aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app/config/config.c25
-rw-r--r--src/app/config/or_options_st.h13
-rw-r--r--src/app/config/or_state_st.h2
-rw-r--r--src/app/config/statefile.c2
-rw-r--r--src/app/config/statefile.h2
-rw-r--r--src/app/main/main.c183
-rw-r--r--src/app/main/main.h3
-rw-r--r--src/app/main/ntmain.c1
-rw-r--r--src/app/main/shutdown.c169
-rw-r--r--src/app/main/shutdown.h18
-rw-r--r--src/app/main/subsysmgr.c52
-rw-r--r--src/app/main/subsysmgr.h7
-rw-r--r--src/app/main/subsystem_list.c16
-rw-r--r--src/config/mmdb-convert.py2
-rw-r--r--src/config/torrc.sample.in6
-rw-r--r--src/core/crypto/hs_ntor.c14
-rw-r--r--src/core/crypto/onion_crypto.h2
-rw-r--r--src/core/crypto/relay_crypto.c48
-rw-r--r--src/core/crypto/relay_crypto.h11
-rw-r--r--src/core/include.am70
-rw-r--r--src/core/mainloop/connection.c17
-rw-r--r--src/core/mainloop/cpuworker.c7
-rw-r--r--src/core/mainloop/mainloop.c562
-rw-r--r--src/core/mainloop/mainloop.h12
-rw-r--r--src/core/mainloop/mainloop_pubsub.c170
-rw-r--r--src/core/mainloop/mainloop_pubsub.h24
-rw-r--r--src/core/mainloop/mainloop_sys.c32
-rw-r--r--src/core/mainloop/mainloop_sys.h12
-rw-r--r--src/core/mainloop/netstatus.h2
-rw-r--r--src/core/mainloop/periodic.c198
-rw-r--r--src/core/mainloop/periodic.h13
-rw-r--r--src/core/or/addr_policy_st.h2
-rw-r--r--src/core/or/address_set.h2
-rw-r--r--src/core/or/cell_queue_st.h2
-rw-r--r--src/core/or/cell_st.h2
-rw-r--r--src/core/or/channel.c11
-rw-r--r--src/core/or/channeltls.c6
-rw-r--r--src/core/or/circuit_st.h46
-rw-r--r--src/core/or/circuitbuild.c131
-rw-r--r--src/core/or/circuitbuild.h13
-rw-r--r--src/core/or/circuitlist.c105
-rw-r--r--src/core/or/circuitlist.h31
-rw-r--r--src/core/or/circuitmux.c29
-rw-r--r--src/core/or/circuitpadding.c1036
-rw-r--r--src/core/or/circuitpadding.h224
-rw-r--r--src/core/or/circuitpadding_machines.c458
-rw-r--r--src/core/or/circuitpadding_machines.h35
-rw-r--r--src/core/or/circuitstats.c4
-rw-r--r--src/core/or/circuituse.c28
-rw-r--r--src/core/or/command.c2
-rw-r--r--src/core/or/connection_edge.c53
-rw-r--r--src/core/or/connection_edge.h1
-rw-r--r--src/core/or/connection_or.c7
-rw-r--r--src/core/or/connection_st.h2
-rw-r--r--src/core/or/cpath_build_state_st.h2
-rw-r--r--src/core/or/crypt_path.c262
-rw-r--r--src/core/or/crypt_path.h46
-rw-r--r--src/core/or/crypt_path_reference_st.h2
-rw-r--r--src/core/or/crypt_path_st.h25
-rw-r--r--src/core/or/destroy_cell_queue_st.h2
-rw-r--r--src/core/or/dos.h4
-rw-r--r--src/core/or/edge_connection_st.h2
-rw-r--r--src/core/or/entry_connection_st.h2
-rw-r--r--src/core/or/entry_port_cfg_st.h2
-rw-r--r--src/core/or/extend_info_st.h2
-rw-r--r--src/core/or/half_edge_st.h2
-rw-r--r--src/core/or/listener_connection_st.h2
-rw-r--r--src/core/or/ocirc_event.h2
-rw-r--r--src/core/or/ocirc_event_sys.h2
-rw-r--r--src/core/or/or.h4
-rw-r--r--src/core/or/or_circuit_st.h7
-rw-r--r--src/core/or/or_connection_st.h2
-rw-r--r--src/core/or/or_handshake_certs_st.h2
-rw-r--r--src/core/or/or_handshake_state_st.h2
-rw-r--r--src/core/or/or_periodic.c65
-rw-r--r--src/core/or/or_periodic.h17
-rw-r--r--src/core/or/or_sys.c43
-rw-r--r--src/core/or/or_sys.h17
-rw-r--r--src/core/or/orconn_event.h2
-rw-r--r--src/core/or/orconn_event_sys.h2
-rw-r--r--src/core/or/origin_circuit_st.h2
-rw-r--r--src/core/or/policies.c98
-rw-r--r--src/core/or/policies.h2
-rw-r--r--src/core/or/port_cfg_st.h2
-rw-r--r--src/core/or/protover.c9
-rw-r--r--src/core/or/protover.h3
-rw-r--r--src/core/or/relay.c474
-rw-r--r--src/core/or/relay.h9
-rw-r--r--src/core/or/relay_crypto_st.h4
-rw-r--r--src/core/or/sendme.c710
-rw-r--r--src/core/or/sendme.h80
-rw-r--r--src/core/or/server_port_cfg_st.h2
-rw-r--r--src/core/or/socks_request_st.h2
-rw-r--r--src/core/or/tor_version_st.h2
-rw-r--r--src/core/or/var_cell_st.h2
-rw-r--r--src/core/or/versions.c5
-rw-r--r--src/core/proto/proto_socks.c2
-rw-r--r--src/ext/include.am2
-rw-r--r--src/ext/timeouts/.may_include5
-rw-r--r--src/ext/timeouts/test-timeout.c2
-rw-r--r--src/ext/timeouts/timeout.c6
-rw-r--r--src/ext/tinytest.c6
-rw-r--r--src/ext/tinytest.h3
-rw-r--r--src/feature/api/tor_api.c4
-rw-r--r--src/feature/api/tor_api.h2
-rw-r--r--src/feature/client/addressmap.c2
-rw-r--r--src/feature/client/bridges.c2
-rw-r--r--src/feature/client/circpathbias.c35
-rw-r--r--src/feature/client/dnsserv.c2
-rw-r--r--src/feature/client/entrynodes.c2
-rw-r--r--src/feature/client/transports.c12
-rw-r--r--src/feature/control/btrack_circuit.h2
-rw-r--r--src/feature/control/btrack_orconn.h4
-rw-r--r--src/feature/control/btrack_orconn_cevent.c2
-rw-r--r--src/feature/control/btrack_orconn_cevent.h3
-rw-r--r--src/feature/control/btrack_orconn_maps.h3
-rw-r--r--src/feature/control/btrack_sys.h2
-rw-r--r--src/feature/control/control.c7128
-rw-r--r--src/feature/control/control.h388
-rw-r--r--src/feature/control/control_auth.c445
-rw-r--r--src/feature/control/control_auth.h32
-rw-r--r--src/feature/control/control_bootstrap.c2
-rw-r--r--src/feature/control/control_cmd.c2408
-rw-r--r--src/feature/control/control_cmd.h112
-rw-r--r--src/feature/control/control_cmd_args_st.h52
-rw-r--r--src/feature/control/control_connection_st.h5
-rw-r--r--src/feature/control/control_events.c2318
-rw-r--r--src/feature/control/control_events.h352
-rw-r--r--src/feature/control/control_fmt.c181
-rw-r--r--src/feature/control/control_fmt.h23
-rw-r--r--src/feature/control/control_getinfo.c1654
-rw-r--r--src/feature/control/control_getinfo.h61
-rw-r--r--src/feature/control/control_proto.c276
-rw-r--r--src/feature/control/control_proto.h48
-rw-r--r--src/feature/control/fmt_serverstatus.c6
-rw-r--r--src/feature/control/fmt_serverstatus.h2
-rw-r--r--src/feature/control/getinfo_geoip.h2
-rw-r--r--src/feature/dirauth/authmode.h6
-rw-r--r--src/feature/dirauth/bridgeauth.c55
-rw-r--r--src/feature/dirauth/bridgeauth.h12
-rw-r--r--src/feature/dirauth/bwauth.c33
-rw-r--r--src/feature/dirauth/bwauth.h2
-rw-r--r--src/feature/dirauth/dirauth_periodic.c161
-rw-r--r--src/feature/dirauth/dirauth_periodic.h25
-rw-r--r--src/feature/dirauth/dirauth_sys.c40
-rw-r--r--src/feature/dirauth/dirauth_sys.h12
-rw-r--r--src/feature/dirauth/dirvote.c11
-rw-r--r--src/feature/dirauth/dirvote.h4
-rw-r--r--src/feature/dirauth/dsigs_parse.c2
-rw-r--r--src/feature/dirauth/dsigs_parse.h2
-rw-r--r--src/feature/dirauth/guardfraction.h2
-rw-r--r--src/feature/dirauth/ns_detached_signatures_st.h2
-rw-r--r--src/feature/dirauth/process_descs.h2
-rw-r--r--src/feature/dirauth/reachability.h2
-rw-r--r--src/feature/dirauth/recommend_pkg.h14
-rw-r--r--src/feature/dirauth/shared_random.c4
-rw-r--r--src/feature/dirauth/shared_random.h4
-rw-r--r--src/feature/dirauth/shared_random_state.c4
-rw-r--r--src/feature/dirauth/vote_microdesc_hash_st.h2
-rw-r--r--src/feature/dirauth/voteflags.c102
-rw-r--r--src/feature/dirauth/voteflags.h18
-rw-r--r--src/feature/dircache/cached_dir_st.h2
-rw-r--r--src/feature/dircache/consdiffmgr.c2
-rw-r--r--src/feature/dircache/dircache.c28
-rw-r--r--src/feature/dircache/dircache.h2
-rw-r--r--src/feature/dircache/dirserv.c20
-rw-r--r--src/feature/dirclient/dir_server_st.h2
-rw-r--r--src/feature/dirclient/dirclient.c4
-rw-r--r--src/feature/dirclient/dirclient.h2
-rw-r--r--src/feature/dirclient/dlstatus.h2
-rw-r--r--src/feature/dirclient/download_status_st.h2
-rw-r--r--src/feature/dircommon/dir_connection_st.h2
-rw-r--r--src/feature/dircommon/vote_timing_st.h2
-rw-r--r--src/feature/dircommon/voting_schedule.c2
-rw-r--r--src/feature/dircommon/voting_schedule.h2
-rw-r--r--src/feature/dirparse/microdesc_parse.h2
-rw-r--r--src/feature/dirparse/ns_parse.c2
-rw-r--r--src/feature/dirparse/ns_parse.h4
-rw-r--r--src/feature/dirparse/sigcommon.h2
-rw-r--r--src/feature/dirparse/signing.h2
-rw-r--r--src/feature/dirparse/unparseable.h2
-rw-r--r--src/feature/hibernate/hibernate.c6
-rw-r--r--src/feature/hibernate/hibernate.h1
-rw-r--r--src/feature/hs/hs_cell.c9
-rw-r--r--src/feature/hs/hs_circuit.c121
-rw-r--r--src/feature/hs/hs_circuit.h3
-rw-r--r--src/feature/hs/hs_client.c64
-rw-r--r--src/feature/hs/hs_client.h4
-rw-r--r--src/feature/hs/hs_common.c189
-rw-r--r--src/feature/hs/hs_common.h7
-rw-r--r--src/feature/hs/hs_config.c9
-rw-r--r--src/feature/hs/hs_control.c39
-rw-r--r--src/feature/hs/hs_control.h4
-rw-r--r--src/feature/hs/hs_descriptor.c226
-rw-r--r--src/feature/hs/hs_descriptor.h29
-rw-r--r--src/feature/hs/hs_intropoint.c8
-rw-r--r--src/feature/hs/hs_service.c189
-rw-r--r--src/feature/hs/hs_service.h7
-rw-r--r--src/feature/hs/hs_stats.h4
-rw-r--r--src/feature/hs/hsdir_index_st.h2
-rw-r--r--src/feature/hs_common/shared_random_client.h2
-rw-r--r--src/feature/keymgt/loadkey.h2
-rw-r--r--src/feature/nodelist/authcert.h2
-rw-r--r--src/feature/nodelist/authority_cert_st.h2
-rw-r--r--src/feature/nodelist/desc_store_st.h2
-rw-r--r--src/feature/nodelist/describe.h2
-rw-r--r--src/feature/nodelist/dirlist.c2
-rw-r--r--src/feature/nodelist/dirlist.h2
-rw-r--r--src/feature/nodelist/document_signature_st.h2
-rw-r--r--src/feature/nodelist/extrainfo_st.h2
-rw-r--r--src/feature/nodelist/fmt_routerstatus.c43
-rw-r--r--src/feature/nodelist/microdesc.c6
-rw-r--r--src/feature/nodelist/microdesc_st.h2
-rw-r--r--src/feature/nodelist/networkstatus.c92
-rw-r--r--src/feature/nodelist/networkstatus.h5
-rw-r--r--src/feature/nodelist/networkstatus_sr_info_st.h2
-rw-r--r--src/feature/nodelist/networkstatus_st.h2
-rw-r--r--src/feature/nodelist/networkstatus_voter_info_st.h2
-rw-r--r--src/feature/nodelist/nickname.h2
-rw-r--r--src/feature/nodelist/node_select.c75
-rw-r--r--src/feature/nodelist/node_select.h4
-rw-r--r--src/feature/nodelist/node_st.h2
-rw-r--r--src/feature/nodelist/nodefamily.h2
-rw-r--r--src/feature/nodelist/nodefamily_st.h2
-rw-r--r--src/feature/nodelist/nodelist.c104
-rw-r--r--src/feature/nodelist/nodelist.h7
-rw-r--r--src/feature/nodelist/routerinfo.h2
-rw-r--r--src/feature/nodelist/routerinfo_st.h2
-rw-r--r--src/feature/nodelist/routerlist.c10
-rw-r--r--src/feature/nodelist/routerlist_st.h2
-rw-r--r--src/feature/nodelist/routerset.c2
-rw-r--r--src/feature/nodelist/routerstatus_st.h2
-rw-r--r--src/feature/nodelist/signed_descriptor_st.h2
-rw-r--r--src/feature/nodelist/torcert.c6
-rw-r--r--src/feature/nodelist/torcert.h2
-rw-r--r--src/feature/nodelist/vote_routerstatus_st.h2
-rw-r--r--src/feature/relay/dns.c4
-rw-r--r--src/feature/relay/ext_orport.c3
-rw-r--r--src/feature/relay/onion_queue.c10
-rw-r--r--src/feature/relay/onion_queue.h2
-rw-r--r--src/feature/relay/relay_periodic.c308
-rw-r--r--src/feature/relay/relay_periodic.h18
-rw-r--r--src/feature/relay/relay_sys.c48
-rw-r--r--src/feature/relay/relay_sys.h17
-rw-r--r--src/feature/relay/router.c403
-rw-r--r--src/feature/relay/router.h23
-rw-r--r--src/feature/relay/routerkeys.c18
-rw-r--r--src/feature/relay/routerkeys.h4
-rw-r--r--src/feature/relay/selftest.c3
-rw-r--r--src/feature/relay/selftest.h2
-rw-r--r--src/feature/rend/rend_authorized_client_st.h2
-rw-r--r--src/feature/rend/rend_encoded_v2_service_descriptor_st.h2
-rw-r--r--src/feature/rend/rend_intro_point_st.h2
-rw-r--r--src/feature/rend/rend_service_descriptor_st.h2
-rw-r--r--src/feature/rend/rendcache.c14
-rw-r--r--src/feature/rend/rendclient.c24
-rw-r--r--src/feature/rend/rendcommon.c7
-rw-r--r--src/feature/rend/rendmid.c3
-rw-r--r--src/feature/rend/rendparse.c17
-rw-r--r--src/feature/rend/rendparse.h2
-rw-r--r--src/feature/rend/rendservice.c53
-rw-r--r--src/feature/stats/geoip_stats.c2
-rw-r--r--src/feature/stats/predict_ports.h2
-rw-r--r--src/feature/stats/rephist.h2
-rw-r--r--src/include.am2
-rw-r--r--src/lib/arch/bytes.h6
-rw-r--r--src/lib/arch/include.am1
-rw-r--r--src/lib/buf/include.am2
-rw-r--r--src/lib/cc/compat_compiler.h14
-rw-r--r--src/lib/cc/ctassert.h6
-rw-r--r--src/lib/cc/include.am1
-rw-r--r--src/lib/cc/torint.h17
-rw-r--r--src/lib/compress/compress_zstd.c16
-rw-r--r--src/lib/compress/include.am2
-rw-r--r--src/lib/container/bitarray.h2
-rw-r--r--src/lib/container/include.am5
-rw-r--r--src/lib/container/map.h2
-rw-r--r--src/lib/container/namemap.c184
-rw-r--r--src/lib/container/namemap.h35
-rw-r--r--src/lib/container/namemap_st.h34
-rw-r--r--src/lib/container/order.h2
-rw-r--r--src/lib/container/smartlist.h2
-rw-r--r--src/lib/crypt_ops/crypto_cipher.h2
-rw-r--r--src/lib/crypt_ops/crypto_curve25519.h4
-rw-r--r--src/lib/crypt_ops/crypto_dh_openssl.c8
-rw-r--r--src/lib/crypt_ops/crypto_digest.c705
-rw-r--r--src/lib/crypt_ops/crypto_digest.h2
-rw-r--r--src/lib/crypt_ops/crypto_digest_nss.c560
-rw-r--r--src/lib/crypt_ops/crypto_digest_openssl.c522
-rw-r--r--src/lib/crypt_ops/crypto_ed25519.c2
-rw-r--r--src/lib/crypt_ops/crypto_format.c90
-rw-r--r--src/lib/crypt_ops/crypto_format.h12
-rw-r--r--src/lib/crypt_ops/crypto_hkdf.c10
-rw-r--r--src/lib/crypt_ops/crypto_init.c15
-rw-r--r--src/lib/crypt_ops/crypto_init.h2
-rw-r--r--src/lib/crypt_ops/crypto_nss_mgt.h4
-rw-r--r--src/lib/crypt_ops/crypto_ope.c4
-rw-r--r--src/lib/crypt_ops/crypto_ope.h4
-rw-r--r--src/lib/crypt_ops/crypto_openssl_mgt.c4
-rw-r--r--src/lib/crypt_ops/crypto_openssl_mgt.h2
-rw-r--r--src/lib/crypt_ops/crypto_rand.c15
-rw-r--r--src/lib/crypt_ops/crypto_rand.h27
-rw-r--r--src/lib/crypt_ops/crypto_rand_fast.c220
-rw-r--r--src/lib/crypt_ops/crypto_rand_numeric.c30
-rw-r--r--src/lib/crypt_ops/crypto_rsa.c2
-rw-r--r--src/lib/crypt_ops/crypto_rsa.h8
-rw-r--r--src/lib/crypt_ops/crypto_rsa_nss.c2
-rw-r--r--src/lib/crypt_ops/crypto_s2k.c4
-rw-r--r--src/lib/crypt_ops/crypto_util.c2
-rw-r--r--src/lib/crypt_ops/digestset.h2
-rw-r--r--src/lib/crypt_ops/include.am4
-rw-r--r--src/lib/ctime/include.am2
-rw-r--r--src/lib/defs/dh_sizes.h2
-rw-r--r--src/lib/defs/digest_sizes.h2
-rw-r--r--src/lib/defs/include.am2
-rw-r--r--src/lib/defs/logging_types.h23
-rw-r--r--src/lib/defs/time.h2
-rw-r--r--src/lib/defs/x25519_sizes.h2
-rw-r--r--src/lib/dispatch/.may_include10
-rw-r--r--src/lib/dispatch/dispatch.h114
-rw-r--r--src/lib/dispatch/dispatch_cfg.c141
-rw-r--r--src/lib/dispatch/dispatch_cfg.h39
-rw-r--r--src/lib/dispatch/dispatch_cfg_st.h25
-rw-r--r--src/lib/dispatch/dispatch_core.c260
-rw-r--r--src/lib/dispatch/dispatch_naming.c63
-rw-r--r--src/lib/dispatch/dispatch_naming.h46
-rw-r--r--src/lib/dispatch/dispatch_new.c174
-rw-r--r--src/lib/dispatch/dispatch_st.h108
-rw-r--r--src/lib/dispatch/include.am27
-rw-r--r--src/lib/dispatch/msgtypes.h80
-rw-r--r--src/lib/encoding/binascii.c10
-rw-r--r--src/lib/encoding/binascii.h2
-rw-r--r--src/lib/encoding/confline.c13
-rw-r--r--src/lib/encoding/confline.h2
-rw-r--r--src/lib/encoding/include.am4
-rw-r--r--src/lib/encoding/keyval.h2
-rw-r--r--src/lib/encoding/kvline.c72
-rw-r--r--src/lib/encoding/kvline.h2
-rw-r--r--src/lib/encoding/pem.h2
-rw-r--r--src/lib/encoding/qstring.c90
-rw-r--r--src/lib/encoding/qstring.h18
-rw-r--r--src/lib/encoding/time_fmt.h2
-rw-r--r--src/lib/err/.may_include3
-rw-r--r--src/lib/err/backtrace.c4
-rw-r--r--src/lib/err/backtrace.h7
-rw-r--r--src/lib/err/include.am2
-rw-r--r--src/lib/err/torerr.h2
-rw-r--r--src/lib/evloop/include.am3
-rw-r--r--src/lib/evloop/token_bucket.h4
-rw-r--r--src/lib/evloop/workqueue.c10
-rw-r--r--src/lib/fdio/include.am2
-rw-r--r--src/lib/fs/conffile.h2
-rw-r--r--src/lib/fs/dir.h2
-rw-r--r--src/lib/fs/files.h14
-rw-r--r--src/lib/fs/include.am2
-rw-r--r--src/lib/fs/lockfile.h2
-rw-r--r--src/lib/fs/mmap.c2
-rw-r--r--src/lib/fs/mmap.h2
-rw-r--r--src/lib/fs/path.h2
-rw-r--r--src/lib/fs/userdb.h4
-rw-r--r--src/lib/fs/winlib.h4
-rw-r--r--src/lib/geoip/country.h2
-rw-r--r--src/lib/geoip/include.am2
-rw-r--r--src/lib/intmath/addsub.h2
-rw-r--r--src/lib/intmath/include.am2
-rw-r--r--src/lib/intmath/logic.h2
-rw-r--r--src/lib/intmath/weakrng.h2
-rw-r--r--src/lib/lock/compat_mutex.h2
-rw-r--r--src/lib/lock/include.am2
-rw-r--r--src/lib/log/.may_include1
-rw-r--r--src/lib/log/escape.h2
-rw-r--r--src/lib/log/include.am2
-rw-r--r--src/lib/log/log.c16
-rw-r--r--src/lib/log/log.h111
-rw-r--r--src/lib/log/ratelim.h2
-rw-r--r--src/lib/log/util_bug.c61
-rw-r--r--src/lib/log/util_bug.h55
-rw-r--r--src/lib/log/win32err.h2
-rw-r--r--src/lib/malloc/include.am2
-rw-r--r--src/lib/malloc/malloc.h4
-rw-r--r--src/lib/malloc/map_anon.c52
-rw-r--r--src/lib/malloc/map_anon.h36
-rw-r--r--src/lib/math/fp.h2
-rw-r--r--src/lib/math/include.am3
-rw-r--r--src/lib/math/laplace.h2
-rw-r--r--src/lib/math/prob_distr.c165
-rw-r--r--src/lib/math/prob_distr.h101
-rw-r--r--src/lib/memarea/include.am2
-rw-r--r--src/lib/meminfo/include.am2
-rw-r--r--src/lib/meminfo/meminfo.h2
-rw-r--r--src/lib/net/address.c10
-rw-r--r--src/lib/net/alertsock.h2
-rw-r--r--src/lib/net/buffers_net.h2
-rw-r--r--src/lib/net/gethostname.h2
-rw-r--r--src/lib/net/inaddr.h2
-rw-r--r--src/lib/net/inaddr_st.h2
-rw-r--r--src/lib/net/include.am2
-rw-r--r--src/lib/net/nettypes.h2
-rw-r--r--src/lib/net/resolve.c4
-rw-r--r--src/lib/net/resolve.h2
-rw-r--r--src/lib/net/socket.c8
-rw-r--r--src/lib/net/socket.h2
-rw-r--r--src/lib/net/socketpair.c4
-rw-r--r--src/lib/net/socketpair.h2
-rw-r--r--src/lib/net/socks5_status.h2
-rw-r--r--src/lib/osinfo/include.am2
-rw-r--r--src/lib/osinfo/uname.h2
-rw-r--r--src/lib/process/daemon.h2
-rw-r--r--src/lib/process/env.h2
-rw-r--r--src/lib/process/include.am2
-rw-r--r--src/lib/process/pidfile.h2
-rw-r--r--src/lib/process/process.c10
-rw-r--r--src/lib/process/process.h6
-rw-r--r--src/lib/process/process_unix.c2
-rw-r--r--src/lib/process/process_unix.h6
-rw-r--r--src/lib/process/process_win32.c4
-rw-r--r--src/lib/process/process_win32.h6
-rw-r--r--src/lib/process/setuid.h2
-rw-r--r--src/lib/process/winprocess_sys.c2
-rw-r--r--src/lib/pubsub/.may_include10
-rw-r--r--src/lib/pubsub/include.am28
-rw-r--r--src/lib/pubsub/pub_binding_st.h38
-rw-r--r--src/lib/pubsub/pubsub.h89
-rw-r--r--src/lib/pubsub/pubsub_build.c307
-rw-r--r--src/lib/pubsub/pubsub_build.h92
-rw-r--r--src/lib/pubsub/pubsub_builder_st.h161
-rw-r--r--src/lib/pubsub/pubsub_check.c428
-rw-r--r--src/lib/pubsub/pubsub_connect.h54
-rw-r--r--src/lib/pubsub/pubsub_flags.h32
-rw-r--r--src/lib/pubsub/pubsub_macros.h373
-rw-r--r--src/lib/pubsub/pubsub_publish.c72
-rw-r--r--src/lib/pubsub/pubsub_publish.h15
-rw-r--r--src/lib/sandbox/include.am2
-rw-r--r--src/lib/smartlist_core/include.am2
-rw-r--r--src/lib/smartlist_core/smartlist_core.c26
-rw-r--r--src/lib/smartlist_core/smartlist_core.h3
-rw-r--r--src/lib/smartlist_core/smartlist_split.h2
-rw-r--r--src/lib/string/compat_string.h8
-rw-r--r--src/lib/string/include.am2
-rw-r--r--src/lib/string/parse_int.h2
-rw-r--r--src/lib/string/printf.c8
-rw-r--r--src/lib/string/printf.h2
-rw-r--r--src/lib/string/scanf.h2
-rw-r--r--src/lib/string/util_string.c9
-rw-r--r--src/lib/string/util_string.h5
-rw-r--r--src/lib/subsys/include.am1
-rw-r--r--src/lib/subsys/subsys.h6
-rw-r--r--src/lib/term/getpass.h2
-rw-r--r--src/lib/term/include.am2
-rw-r--r--src/lib/testsupport/include.am1
-rw-r--r--src/lib/testsupport/testsupport.h2
-rw-r--r--src/lib/thread/include.am2
-rw-r--r--src/lib/thread/numcpus.h2
-rw-r--r--src/lib/time/compat_time.c20
-rw-r--r--src/lib/time/compat_time.h52
-rw-r--r--src/lib/time/include.am2
-rw-r--r--src/lib/time/tvdiff.h2
-rw-r--r--src/lib/tls/include.am2
-rw-r--r--src/lib/tls/nss_countbytes.h2
-rw-r--r--src/lib/tls/tortls.h10
-rw-r--r--src/lib/tls/tortls_internal.h6
-rw-r--r--src/lib/tls/tortls_openssl.c14
-rw-r--r--src/lib/tls/tortls_st.h4
-rw-r--r--src/lib/tls/x509.h6
-rw-r--r--src/lib/tls/x509_internal.h2
-rw-r--r--src/lib/tls/x509_nss.c8
-rw-r--r--src/lib/tls/x509_openssl.c4
-rw-r--r--src/lib/trace/debug.h2
-rw-r--r--src/lib/trace/events.h6
-rw-r--r--src/lib/trace/include.am3
-rw-r--r--src/lib/trace/trace.h2
-rw-r--r--src/lib/version/include.am2
-rw-r--r--src/lib/wallclock/approx_time.h2
-rw-r--r--src/lib/wallclock/include.am2
-rw-r--r--src/lib/wallclock/time_to_tm.h2
-rw-r--r--src/lib/wallclock/timeval.h23
-rw-r--r--src/lib/wallclock/tor_gettimeofday.h2
-rw-r--r--src/rust/protover/ffi.rs1
-rw-r--r--src/rust/protover/protover.rs8
-rw-r--r--src/rust/tor_log/tor_log.rs8
-rw-r--r--src/test/bench.c4
-rwxr-xr-xsrc/test/fuzz/fixup_filenames.sh6
-rwxr-xr-xsrc/test/fuzz/fuzz_multi.sh6
-rw-r--r--src/test/fuzz/fuzz_strops.c20
-rw-r--r--src/test/fuzz/fuzzing.h2
-rw-r--r--src/test/fuzz/fuzzing_common.c4
-rwxr-xr-xsrc/test/fuzz/minimize.sh2
-rwxr-xr-xsrc/test/fuzz_static_testcases.sh2
-rw-r--r--src/test/hs_test_helpers.c86
-rw-r--r--src/test/include.am25
-rw-r--r--src/test/ope_ref.py2
-rw-r--r--src/test/ptr_helpers.c50
-rw-r--r--src/test/ptr_helpers.h23
-rw-r--r--src/test/rng_test_helpers.c259
-rw-r--r--src/test/rng_test_helpers.h25
-rw-r--r--src/test/test-memwipe.c2
-rwxr-xr-xsrc/test/test-network.sh48
-rw-r--r--src/test/test.c27
-rw-r--r--src/test/test.h6
-rw-r--r--src/test/test_addr.c45
-rwxr-xr-xsrc/test/test_bt.sh2
-rw-r--r--src/test/test_bt_cl.c10
-rw-r--r--src/test/test_channel.c6
-rw-r--r--src/test/test_circuitbuild.c2
-rw-r--r--src/test/test_circuitpadding.c1343
-rw-r--r--src/test/test_circuitstats.c16
-rw-r--r--src/test/test_config.c61
-rw-r--r--src/test/test_connection.h4
-rw-r--r--src/test/test_containers.c65
-rw-r--r--src/test/test_controller.c192
-rw-r--r--src/test/test_controller_events.c3
-rw-r--r--src/test/test_crypto.c36
-rw-r--r--src/test/test_crypto_rng.c8
-rw-r--r--src/test/test_crypto_slow.c2
-rw-r--r--src/test/test_dir.c1211
-rw-r--r--src/test/test_dir_common.h4
-rw-r--r--src/test/test_dispatch.c249
-rw-r--r--src/test/test_dns.c4
-rw-r--r--src/test/test_dos.c2
-rw-r--r--src/test/test_entrynodes.c3
-rw-r--r--src/test/test_extorport.c31
-rw-r--r--src/test/test_helpers.c2
-rw-r--r--src/test/test_hs.c16
-rw-r--r--src/test/test_hs_cache.c7
-rw-r--r--src/test/test_hs_cell.c4
-rw-r--r--src/test/test_hs_client.c41
-rw-r--r--src/test/test_hs_common.c6
-rw-r--r--src/test/test_hs_control.c7
-rw-r--r--src/test/test_hs_descriptor.c132
-rw-r--r--src/test/test_hs_intropoint.c4
-rw-r--r--src/test/test_hs_service.c98
-rwxr-xr-xsrc/test/test_key_expiration.sh14
-rwxr-xr-xsrc/test/test_keygen.sh46
-rw-r--r--src/test/test_link_handshake.c8
-rw-r--r--src/test/test_logging.c2
-rw-r--r--src/test/test_namemap.c174
-rw-r--r--src/test/test_options.c5
-rw-r--r--src/test/test_periodic_event.c41
-rw-r--r--src/test/test_policy.c235
-rw-r--r--src/test/test_prob_distr.c102
-rw-r--r--src/test/test_process.c4
-rw-r--r--src/test/test_process_slow.c2
-rw-r--r--src/test/test_protover.c10
-rw-r--r--src/test/test_pt.c3
-rw-r--r--src/test/test_ptr_slow.c106
-rw-r--r--src/test/test_pubsub_build.c621
-rw-r--r--src/test/test_pubsub_msg.c305
-rwxr-xr-xsrc/test/test_rebind.sh11
-rw-r--r--src/test/test_relaycell.c7
-rw-r--r--src/test/test_relaycrypt.c10
-rw-r--r--src/test/test_rng.c2
-rw-r--r--src/test/test_router.c3
-rw-r--r--src/test/test_routerkeys.c12
-rw-r--r--src/test/test_routerlist.c2
-rw-r--r--src/test/test_routerset.c8
-rwxr-xr-xsrc/test/test_rust.sh5
-rw-r--r--src/test/test_sendme.c365
-rw-r--r--src/test/test_shared_random.c140
-rw-r--r--src/test/test_slow.c1
-rw-r--r--src/test/test_status.c24
-rwxr-xr-xsrc/test/test_switch_id.sh4
-rw-r--r--src/test/test_tortls.c6
-rw-r--r--src/test/test_tortls.h2
-rw-r--r--src/test/test_tortls_openssl.c4
-rw-r--r--src/test/test_util.c75
-rw-r--r--src/test/test_util_format.c6
-rw-r--r--src/test/test_voting_flags.c2
-rwxr-xr-xsrc/test/test_workqueue_cancel.sh2
-rwxr-xr-xsrc/test/test_workqueue_efd.sh2
-rwxr-xr-xsrc/test/test_workqueue_efd2.sh2
-rwxr-xr-xsrc/test/test_workqueue_pipe.sh2
-rwxr-xr-xsrc/test/test_workqueue_pipe2.sh2
-rwxr-xr-xsrc/test/test_workqueue_socketpair.sh2
-rw-r--r--src/test/testing_common.c3
-rw-r--r--src/test/testing_rsakeys.c3
-rwxr-xr-xsrc/test/zero_length_keys.sh6
-rw-r--r--src/tools/tor-gencert.c2
-rw-r--r--src/tools/tor-resolve.c1
-rw-r--r--src/trunnel/ed25519_cert.trunnel6
-rw-r--r--src/trunnel/include.am3
-rw-r--r--src/trunnel/sendme.c347
-rw-r--r--src/trunnel/sendme.h101
-rw-r--r--src/trunnel/sendme.trunnel19
-rw-r--r--src/win32/orconfig.h2
584 files changed, 25808 insertions, 12402 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c
index 1676e9349a..a061871748 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -86,6 +86,8 @@
#include "feature/client/entrynodes.h"
#include "feature/client/transports.h"
#include "feature/control/control.h"
+#include "feature/control/control_auth.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/bwauth.h"
#include "feature/dirauth/guardfraction.h"
#include "feature/dircache/consdiffmgr.h"
@@ -154,6 +156,7 @@
#include "lib/evloop/procmon.h"
#include "feature/dirauth/dirvote.h"
+#include "feature/dirauth/dirauth_periodic.h"
#include "feature/dirauth/recommend_pkg.h"
#include "feature/dirauth/authmode.h"
@@ -594,6 +597,8 @@ static config_var_t option_vars_[] = {
V(ReducedConnectionPadding, BOOL, "0"),
V(ConnectionPadding, AUTOBOOL, "auto"),
V(RefuseUnknownExits, AUTOBOOL, "auto"),
+ V(CircuitPadding, BOOL, "1"),
+ V(ReducedCircuitPadding, BOOL, "0"),
V(RejectPlaintextPorts, CSV, ""),
V(RelayBandwidthBurst, MEMUNIT, "0"),
V(RelayBandwidthRate, MEMUNIT, "0"),
@@ -3546,7 +3551,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
tor_free(t);
t = format_recommended_version_list(options->RecommendedServerVersions, 1);
tor_free(t);
-#endif
+#endif /* defined(HAVE_MODULE_DIRAUTH) */
if (options->UseEntryGuards) {
log_info(LD_CONFIG, "Authoritative directory servers can't set "
@@ -3562,6 +3567,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->V3AuthoritativeDir))
REJECT("AuthoritativeDir is set, but none of "
"(Bridge/V3)AuthoritativeDir is set.");
+#ifdef HAVE_MODULE_DIRAUTH
/* If we have a v3bandwidthsfile and it's broken, complain on startup */
if (options->V3BandwidthsFile && !old_options) {
dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL, NULL,
@@ -3571,6 +3577,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->GuardfractionFile && !old_options) {
dirserv_read_guardfraction_file(options->GuardfractionFile, NULL);
}
+#endif /* defined(HAVE_MODULE_DIRAUTH) */
}
if (options->AuthoritativeDir && !options->DirPort_set)
@@ -3748,6 +3755,14 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Relays cannot set ReducedConnectionPadding. ");
}
+ if (server_mode(options) && options->CircuitPadding == 0) {
+ REJECT("Relays cannot set CircuitPadding to 0. ");
+ }
+
+ if (server_mode(options) && options->ReducedCircuitPadding == 1) {
+ REJECT("Relays cannot set ReducedCircuitPadding. ");
+ }
+
if (options->BridgeDistribution) {
if (!options->BridgeRelay) {
REJECT("You set BridgeDistribution, but you didn't set BridgeRelay!");
@@ -4198,6 +4213,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
"You should also make sure you aren't listing this bridge's "
"fingerprint in any other MyFamily.");
}
+ if (options->MyFamily_lines && !options->ContactInfo) {
+ log_warn(LD_CONFIG, "MyFamily is set but ContactInfo is not configured. "
+ "ContactInfo should always be set when MyFamily option is too.");
+ }
if (normalize_nickname_list(&options->MyFamily,
options->MyFamily_lines, "MyFamily", msg))
return -1;
@@ -4586,7 +4605,7 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess)
#else
/* On a 32-bit platform, we can't have 8GB of ram. */
#define RAM_IS_VERY_LARGE(x) (0)
-#endif
+#endif /* SIZEOF_SIZE_T > 4 */
if (RAM_IS_VERY_LARGE(ram)) {
/* If we have 8 GB, or more, RAM available, we set the MaxMemInQueues
@@ -5758,7 +5777,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options,
#else
log_warn(LD_CONFIG, "Android logging is not supported"
" on this system. Sorry.");
-#endif // HAVE_ANDROID_LOG_H.
+#endif /* defined(HAVE_ANDROID_LOG_H) */
goto cleanup;
}
}
diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h
index bd707fd193..2ee2d15674 100644
--- a/src/app/config/or_options_st.h
+++ b/src/app/config/or_options_st.h
@@ -248,6 +248,17 @@ struct or_options_t {
* pad to the server regardless of server support. */
int ConnectionPadding;
+ /** Boolean: if true, then circuit padding will be negotiated by client
+ * and server, subject to consenus limits (default). If 0, it will be fully
+ * disabled. */
+ int CircuitPadding;
+
+ /** Boolean: if true, then this client will only use circuit padding
+ * algorithms that are known to use a low amount of overhead. If false,
+ * we will use all available circuit padding algorithms.
+ */
+ int ReducedCircuitPadding;
+
/** To what authority types do we publish our descriptor? Choices are
* "v1", "v2", "v3", "bridge", or "". */
struct smartlist_t *PublishServerDescriptor;
@@ -1099,4 +1110,4 @@ struct or_options_t {
int DormantCanceledByStartup;
};
-#endif
+#endif /* !defined(TOR_OR_OPTIONS_ST_H) */
diff --git a/src/app/config/or_state_st.h b/src/app/config/or_state_st.h
index cdb9b38287..f45c6196cc 100644
--- a/src/app/config/or_state_st.h
+++ b/src/app/config/or_state_st.h
@@ -96,4 +96,4 @@ struct or_state_t {
int Dormant;
};
-#endif
+#endif /* !defined(TOR_OR_STATE_ST_H) */
diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c
index 9681f6f8b3..fdfd68b244 100644
--- a/src/app/config/statefile.c
+++ b/src/app/config/statefile.c
@@ -36,7 +36,7 @@
#include "core/mainloop/mainloop.h"
#include "core/mainloop/netstatus.h"
#include "core/mainloop/connection.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/client/entrynodes.h"
#include "feature/hibernate/hibernate.h"
#include "feature/stats/rephist.h"
diff --git a/src/app/config/statefile.h b/src/app/config/statefile.h
index 1950078450..515c90a52f 100644
--- a/src/app/config/statefile.h
+++ b/src/app/config/statefile.h
@@ -31,6 +31,6 @@ STATIC struct config_line_t *get_transport_in_state_by_name(
STATIC void or_state_free_(or_state_t *state);
#define or_state_free(st) FREE_AND_NULL(or_state_t, or_state_free_, (st))
STATIC or_state_t *or_state_new(void);
-#endif
+#endif /* defined(STATEFILE_PRIVATE) */
#endif /* !defined(TOR_STATEFILE_H) */
diff --git a/src/app/main/main.c b/src/app/main/main.c
index 4b60763f75..6e325f0b10 100644
--- a/src/app/main/main.c
+++ b/src/app/main/main.c
@@ -15,66 +15,51 @@
#include "app/config/statefile.h"
#include "app/main/main.h"
#include "app/main/ntmain.h"
+#include "app/main/shutdown.h"
#include "app/main/subsysmgr.h"
#include "core/mainloop/connection.h"
#include "core/mainloop/cpuworker.h"
#include "core/mainloop/mainloop.h"
+#include "core/mainloop/mainloop_pubsub.h"
#include "core/mainloop/netstatus.h"
#include "core/or/channel.h"
#include "core/or/channelpadding.h"
#include "core/or/circuitpadding.h"
-#include "core/or/channeltls.h"
#include "core/or/circuitlist.h"
-#include "core/or/circuitmux_ewma.h"
#include "core/or/command.h"
-#include "core/or/connection_edge.h"
#include "core/or/connection_or.h"
-#include "core/or/dos.h"
-#include "core/or/policies.h"
-#include "core/or/protover.h"
#include "core/or/relay.h"
-#include "core/or/scheduler.h"
#include "core/or/status.h"
-#include "core/or/versions.h"
#include "feature/api/tor_api.h"
#include "feature/api/tor_api_internal.h"
#include "feature/client/addressmap.h"
-#include "feature/client/bridges.h"
-#include "feature/client/entrynodes.h"
-#include "feature/client/transports.h"
#include "feature/control/control.h"
-#include "feature/dirauth/bwauth.h"
+#include "feature/control/control_auth.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/keypin.h"
#include "feature/dirauth/process_descs.h"
#include "feature/dircache/consdiffmgr.h"
-#include "feature/dircache/dirserv.h"
#include "feature/dirparse/routerparse.h"
#include "feature/hibernate/hibernate.h"
-#include "feature/hs/hs_cache.h"
#include "feature/nodelist/authcert.h"
-#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
-#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/relay/dns.h"
#include "feature/relay/ext_orport.h"
-#include "feature/relay/onion_queue.h"
#include "feature/relay/routerkeys.h"
#include "feature/relay/routermode.h"
#include "feature/rend/rendcache.h"
-#include "feature/rend/rendclient.h"
#include "feature/rend/rendservice.h"
-#include "feature/stats/geoip_stats.h"
#include "feature/stats/predict_ports.h"
#include "feature/stats/rephist.h"
#include "lib/compress/compress.h"
#include "lib/buf/buffers.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_s2k.h"
-#include "lib/geoip/geoip.h"
#include "lib/net/resolve.h"
#include "lib/process/waitpid.h"
+#include "lib/pubsub/pubsub_build.h"
#include "lib/meminfo/meminfo.h"
#include "lib/osinfo/uname.h"
@@ -90,7 +75,6 @@
#include <event2/event.h>
-#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/authmode.h"
#include "feature/dirauth/shared_random.h"
@@ -111,8 +95,6 @@
#include <systemd/sd-daemon.h>
#endif /* defined(HAVE_SYSTEMD) */
-void evdns_shutdown(int);
-
#ifdef HAVE_RUST
// helper function defined in Rust to output a log message indicating if tor is
// running with Rust enabled. See src/rust/tor_util
@@ -670,7 +652,7 @@ tor_init(int argc, char *argv[])
log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting.");
return -1;
}
- stream_choice_seed_weak_rng();
+
if (tor_init_libevent_rng() < 0) {
log_warn(LD_NET, "Problem initializing libevent RNG.");
}
@@ -743,86 +725,6 @@ release_lockfile(void)
}
}
-/** Free all memory that we might have allocated somewhere.
- * If <b>postfork</b>, we are a worker process and we want to free
- * only the parts of memory that we won't touch. If !<b>postfork</b>,
- * Tor is shutting down and we should free everything.
- *
- * Helps us find the real leaks with sanitizers and the like. Also valgrind
- * should then report 0 reachable in its leak report (in an ideal world --
- * in practice libevent, SSL, libc etc never quite free everything). */
-void
-tor_free_all(int postfork)
-{
- if (!postfork) {
- evdns_shutdown(1);
- }
- geoip_free_all();
- geoip_stats_free_all();
- dirvote_free_all();
- routerlist_free_all();
- networkstatus_free_all();
- addressmap_free_all();
- dirserv_free_fingerprint_list();
- dirserv_free_all();
- dirserv_clear_measured_bw_cache();
- rend_cache_free_all();
- rend_service_authorization_free_all();
- rep_hist_free_all();
- dns_free_all();
- clear_pending_onions();
- circuit_free_all();
- circpad_machines_free();
- entry_guards_free_all();
- pt_free_all();
- channel_tls_free_all();
- channel_free_all();
- connection_free_all();
- connection_edge_free_all();
- scheduler_free_all();
- nodelist_free_all();
- microdesc_free_all();
- routerparse_free_all();
- ext_orport_free_all();
- control_free_all();
- protover_free_all();
- bridges_free_all();
- consdiffmgr_free_all();
- hs_free_all();
- dos_free_all();
- circuitmux_ewma_free_all();
- accounting_free_all();
- protover_summary_cache_free_all();
-
- if (!postfork) {
- config_free_all();
- or_state_free_all();
- router_free_all();
- routerkeys_free_all();
- policies_free_all();
- }
- if (!postfork) {
-#ifndef _WIN32
- tor_getpwnam(NULL);
-#endif
- }
- /* stuff in main.c */
-
- tor_mainloop_free_all();
-
- if (!postfork) {
- release_lockfile();
- }
- tor_libevent_free_all();
-
- subsystems_shutdown();
-
- /* Stuff in util.c and address.c*/
- if (!postfork) {
- esc_router_info(NULL);
- }
-}
-
/**
* Remove the specified file, and log a warning if the operation fails for
* any reason other than the file not existing. Ignores NULL filenames.
@@ -836,50 +738,6 @@ tor_remove_file(const char *filename)
}
}
-/** Do whatever cleanup is necessary before shutting Tor down. */
-void
-tor_cleanup(void)
-{
- const or_options_t *options = get_options();
- if (options->command == CMD_RUN_TOR) {
- time_t now = time(NULL);
- /* Remove our pid file. We don't care if there was an error when we
- * unlink, nothing we could do about it anyways. */
- tor_remove_file(options->PidFile);
- /* Remove control port file */
- tor_remove_file(options->ControlPortWriteToFile);
- /* Remove cookie authentication file */
- {
- char *cookie_fname = get_controller_cookie_file_name();
- tor_remove_file(cookie_fname);
- tor_free(cookie_fname);
- }
- /* Remove Extended ORPort cookie authentication file */
- {
- char *cookie_fname = get_ext_or_auth_cookie_file_name();
- tor_remove_file(cookie_fname);
- tor_free(cookie_fname);
- }
- if (accounting_is_enabled(options))
- 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();
- }
-
- timers_shutdown();
-
- tor_free_all(0); /* We could move tor_free_all back into the ifdef below
- later, if it makes shutdown unacceptably slow. But for
- now, leave it here: it's helped us catch bugs in the
- past. */
-}
-
/** Read/create keys as needed, and echo our fingerprint to stdout. */
static int
do_list_fingerprint(void)
@@ -1377,6 +1235,30 @@ run_tor_main_loop(void)
return do_main_loop();
}
+/** Install the publish/subscribe relationships for all the subsystems. */
+static void
+pubsub_install(void)
+{
+ pubsub_builder_t *builder = pubsub_builder_new();
+ int r = subsystems_add_pubsub(builder);
+ tor_assert(r == 0);
+ r = tor_mainloop_connect_pubsub(builder); // consumes builder
+ tor_assert(r == 0);
+}
+
+/** Connect the mainloop to its publish/subscribe message delivery events if
+ * appropriate, and configure the global channels appropriately. */
+static void
+pubsub_connect(void)
+{
+ if (get_options()->command == CMD_RUN_TOR) {
+ tor_mainloop_connect_pubsub_events();
+ /* XXXX For each pubsub channel, its delivery strategy should be set at
+ * this XXXX point, using tor_mainloop_set_delivery_strategy().
+ */
+ }
+}
+
/* Main entry point for the Tor process. Called from tor_main(), and by
* anybody embedding Tor. */
int
@@ -1408,6 +1290,9 @@ tor_run_main(const tor_main_configuration_t *tor_cfg)
}
}
#endif /* defined(NT_SERVICE) */
+
+ pubsub_install();
+
{
int init_rv = tor_init(argc, argv);
if (init_rv) {
@@ -1417,6 +1302,8 @@ tor_run_main(const tor_main_configuration_t *tor_cfg)
}
}
+ pubsub_connect();
+
if (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) {
sandbox_cfg_t* cfg = sandbox_init_filter();
diff --git a/src/app/main/main.h b/src/app/main/main.h
index bbbbf984fb..9dfaf4b8ef 100644
--- a/src/app/main/main.h
+++ b/src/app/main/main.h
@@ -21,9 +21,6 @@ void release_lockfile(void);
void tor_remove_file(const char *filename);
-void tor_cleanup(void);
-void tor_free_all(int postfork);
-
int tor_init(int argc, char **argv);
int run_tor_main_loop(void);
diff --git a/src/app/main/ntmain.c b/src/app/main/ntmain.c
index 05d203b0be..f00b712702 100644
--- a/src/app/main/ntmain.c
+++ b/src/app/main/ntmain.c
@@ -24,6 +24,7 @@
#include "app/config/config.h"
#include "app/main/main.h"
#include "app/main/ntmain.h"
+#include "app/main/shutdown.h"
#include "core/mainloop/mainloop.h"
#include "lib/evloop/compat_libevent.h"
#include "lib/fs/winlib.h"
diff --git a/src/app/main/shutdown.c b/src/app/main/shutdown.c
new file mode 100644
index 0000000000..cc0091a9ab
--- /dev/null
+++ b/src/app/main/shutdown.c
@@ -0,0 +1,169 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file shutdown.c
+ * @brief Code to free global resources used by Tor.
+ *
+ * In the future, this should all be handled by the subsystem manager. */
+
+#include "core/or/or.h"
+
+#include "app/config/config.h"
+#include "app/config/statefile.h"
+#include "app/main/main.h"
+#include "app/main/shutdown.h"
+#include "app/main/subsysmgr.h"
+#include "core/mainloop/connection.h"
+#include "core/mainloop/mainloop_pubsub.h"
+#include "core/or/channeltls.h"
+#include "core/or/circuitlist.h"
+#include "core/or/circuitmux_ewma.h"
+#include "core/or/circuitpadding.h"
+#include "core/or/connection_edge.h"
+#include "core/or/dos.h"
+#include "core/or/scheduler.h"
+#include "feature/client/addressmap.h"
+#include "feature/client/bridges.h"
+#include "feature/client/entrynodes.h"
+#include "feature/client/transports.h"
+#include "feature/control/control.h"
+#include "feature/control/control_auth.h"
+#include "feature/dirauth/authmode.h"
+#include "feature/dirauth/shared_random.h"
+#include "feature/dircache/consdiffmgr.h"
+#include "feature/dircache/dirserv.h"
+#include "feature/dirparse/routerparse.h"
+#include "feature/hibernate/hibernate.h"
+#include "feature/hs/hs_common.h"
+#include "feature/nodelist/microdesc.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/relay/ext_orport.h"
+#include "feature/rend/rendcache.h"
+#include "feature/rend/rendclient.h"
+#include "feature/stats/geoip_stats.h"
+#include "feature/stats/rephist.h"
+#include "lib/evloop/compat_libevent.h"
+#include "lib/geoip/geoip.h"
+
+void evdns_shutdown(int);
+
+/** Do whatever cleanup is necessary before shutting Tor down. */
+void
+tor_cleanup(void)
+{
+ const or_options_t *options = get_options();
+ if (options->command == CMD_RUN_TOR) {
+ time_t now = time(NULL);
+ /* Remove our pid file. We don't care if there was an error when we
+ * unlink, nothing we could do about it anyways. */
+ tor_remove_file(options->PidFile);
+ /* Remove control port file */
+ tor_remove_file(options->ControlPortWriteToFile);
+ /* Remove cookie authentication file */
+ {
+ char *cookie_fname = get_controller_cookie_file_name();
+ tor_remove_file(cookie_fname);
+ tor_free(cookie_fname);
+ }
+ /* Remove Extended ORPort cookie authentication file */
+ {
+ char *cookie_fname = get_ext_or_auth_cookie_file_name();
+ tor_remove_file(cookie_fname);
+ tor_free(cookie_fname);
+ }
+ if (accounting_is_enabled(options))
+ 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);
+ }
+
+ timers_shutdown();
+
+ tor_free_all(0); /* We could move tor_free_all back into the ifdef below
+ later, if it makes shutdown unacceptably slow. But for
+ now, leave it here: it's helped us catch bugs in the
+ past. */
+}
+
+/** Free all memory that we might have allocated somewhere.
+ * If <b>postfork</b>, we are a worker process and we want to free
+ * only the parts of memory that we won't touch. If !<b>postfork</b>,
+ * Tor is shutting down and we should free everything.
+ *
+ * Helps us find the real leaks with sanitizers and the like. Also valgrind
+ * should then report 0 reachable in its leak report (in an ideal world --
+ * in practice libevent, SSL, libc etc never quite free everything). */
+void
+tor_free_all(int postfork)
+{
+ if (!postfork) {
+ evdns_shutdown(1);
+ }
+ geoip_free_all();
+ geoip_stats_free_all();
+ routerlist_free_all();
+ networkstatus_free_all();
+ addressmap_free_all();
+ dirserv_free_all();
+ rend_cache_free_all();
+ rend_service_authorization_free_all();
+ rep_hist_free_all();
+ circuit_free_all();
+ circpad_machines_free();
+ entry_guards_free_all();
+ pt_free_all();
+ channel_tls_free_all();
+ channel_free_all();
+ connection_free_all();
+ connection_edge_free_all();
+ scheduler_free_all();
+ nodelist_free_all();
+ microdesc_free_all();
+ routerparse_free_all();
+ control_free_all();
+ bridges_free_all();
+ consdiffmgr_free_all();
+ hs_free_all();
+ dos_free_all();
+ circuitmux_ewma_free_all();
+ accounting_free_all();
+ circpad_free_all();
+
+ if (!postfork) {
+ config_free_all();
+ or_state_free_all();
+ }
+ if (!postfork) {
+#ifndef _WIN32
+ tor_getpwnam(NULL);
+#endif
+ }
+ /* stuff in main.c */
+
+ tor_mainloop_disconnect_pubsub();
+
+ if (!postfork) {
+ release_lockfile();
+ }
+
+ subsystems_shutdown();
+
+ tor_libevent_free_all();
+
+ /* Stuff in util.c and address.c*/
+ if (!postfork) {
+ esc_router_info(NULL);
+ }
+}
diff --git a/src/app/main/shutdown.h b/src/app/main/shutdown.h
new file mode 100644
index 0000000000..1bca96a0aa
--- /dev/null
+++ b/src/app/main/shutdown.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shutdown.h
+ * \brief Header file for shutdown.c.
+ **/
+
+#ifndef TOR_SHUTDOWN_H
+#define TOR_SHUTDOWN_H
+
+void tor_cleanup(void);
+void tor_free_all(int postfork);
+
+#endif /* !defined(TOR_SHUTDOWN_H) */
diff --git a/src/app/main/subsysmgr.c b/src/app/main/subsysmgr.c
index e0ca3ce4df..5aa4fd76c9 100644
--- a/src/app/main/subsysmgr.c
+++ b/src/app/main/subsysmgr.c
@@ -5,9 +5,14 @@
#include "orconfig.h"
#include "app/main/subsysmgr.h"
-#include "lib/err/torerr.h"
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/dispatch/msgtypes.h"
+#include "lib/err/torerr.h"
#include "lib/log/log.h"
+#include "lib/malloc/malloc.h"
+#include "lib/pubsub/pubsub_build.h"
+#include "lib/pubsub/pubsub_connect.h"
#include <stdio.h>
#include <stdlib.h>
@@ -106,6 +111,51 @@ subsystems_init_upto(int target_level)
}
/**
+ * Add publish/subscribe relationships to <b>builder</b> for all
+ * initialized subsystems of level no more than <b>target_level</b>.
+ **/
+int
+subsystems_add_pubsub_upto(pubsub_builder_t *builder,
+ int target_level)
+{
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (sys->level > target_level)
+ break;
+ if (! sys_initialized[i])
+ continue;
+ int r = 0;
+ if (sys->add_pubsub) {
+ subsys_id_t sysid = get_subsys_id(sys->name);
+ raw_assert(sysid != ERROR_ID);
+ pubsub_connector_t *connector;
+ connector = pubsub_connector_for_subsystem(builder, sysid);
+ r = sys->add_pubsub(connector);
+ pubsub_connector_free(connector);
+ }
+ if (r < 0) {
+ fprintf(stderr, "BUG: subsystem %s (at %u) could not connect to "
+ "publish/subscribe system.", sys->name, sys->level);
+ raw_assert_unreached_msg("A subsystem couldn't be connected.");
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Add publish/subscribe relationships to <b>builder</b> for all
+ * initialized subsystems.
+ **/
+int
+subsystems_add_pubsub(pubsub_builder_t *builder)
+{
+ return subsystems_add_pubsub_upto(builder, MAX_SUBSYS_LEVEL);
+}
+
+/**
* Shut down all the subsystems.
**/
void
diff --git a/src/app/main/subsysmgr.h b/src/app/main/subsysmgr.h
index a5e62f71d9..d4426614e3 100644
--- a/src/app/main/subsysmgr.h
+++ b/src/app/main/subsysmgr.h
@@ -14,6 +14,11 @@ extern const unsigned n_tor_subsystems;
int subsystems_init(void);
int subsystems_init_upto(int level);
+struct pubsub_builder_t;
+int subsystems_add_pubsub_upto(struct pubsub_builder_t *builder,
+ int target_level);
+int subsystems_add_pubsub(struct pubsub_builder_t *builder);
+
void subsystems_shutdown(void);
void subsystems_shutdown_downto(int level);
@@ -21,4 +26,4 @@ void subsystems_prefork(void);
void subsystems_postfork(void);
void subsystems_thread_cleanup(void);
-#endif
+#endif /* !defined(TOR_SUBSYSMGR_T) */
diff --git a/src/app/main/subsystem_list.c b/src/app/main/subsystem_list.c
index 81b58cf708..c637887153 100644
--- a/src/app/main/subsystem_list.c
+++ b/src/app/main/subsystem_list.c
@@ -8,20 +8,25 @@
#include "lib/cc/compat_compiler.h"
#include "lib/cc/torint.h"
+#include "core/mainloop/mainloop_sys.h"
#include "core/or/ocirc_event_sys.h"
+#include "core/or/or_sys.h"
#include "core/or/orconn_event_sys.h"
#include "feature/control/btrack_sys.h"
+#include "feature/relay/relay_sys.h"
#include "lib/compress/compress_sys.h"
#include "lib/crypt_ops/crypto_sys.h"
#include "lib/err/torerr_sys.h"
#include "lib/log/log_sys.h"
#include "lib/net/network_sys.h"
+#include "lib/process/process_sys.h"
#include "lib/process/winprocess_sys.h"
#include "lib/thread/thread_sys.h"
#include "lib/time/time_sys.h"
#include "lib/tls/tortls_sys.h"
#include "lib/wallclock/wallclock_sys.h"
-#include "lib/process/process_sys.h"
+
+#include "feature/dirauth/dirauth_sys.h"
#include <stddef.h>
@@ -46,6 +51,15 @@ const subsys_fns_t *tor_subsystems[] = {
&sys_orconn_event,
&sys_ocirc_event,
&sys_btrack,
+
+ &sys_mainloop, /* 5 */
+ &sys_or, /* 20 */
+
+ &sys_relay, /* 50 */
+
+#ifdef HAVE_MODULE_DIRAUTH
+ &sys_dirauth, /* 70 */
+#endif
};
const unsigned n_tor_subsystems = ARRAY_LENGTH(tor_subsystems);
diff --git a/src/config/mmdb-convert.py b/src/config/mmdb-convert.py
index 706a8b03cc..b861e9433e 100644
--- a/src/config/mmdb-convert.py
+++ b/src/config/mmdb-convert.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python3
+#!/usr/bin/python
# This software has been dedicated to the public domain under the CC0
# public domain dedication.
diff --git a/src/config/torrc.sample.in b/src/config/torrc.sample.in
index c2ae707e93..9d514e6bda 100644
--- a/src/config/torrc.sample.in
+++ b/src/config/torrc.sample.in
@@ -174,13 +174,11 @@
## Uncomment this if you want your relay to be an exit, with the default
## exit policy (or whatever exit policy you set below).
-## (If ReducedExitPolicy or ExitPolicy are set, relays are exits.
-## If neither exit policy option is set, relays are non-exits.)
+## (If ReducedExitPolicy, ExitPolicy, or IPv6Exit are set, relays are exits.
+## If none of these options are set, relays are non-exits.)
#ExitRelay 1
## Uncomment this if you want your relay to allow IPv6 exit traffic.
-## You must also set ExitRelay, ReducedExitPolicy, or ExitPolicy to make your
-## relay into an exit.
## (Relays do not allow any exit traffic by default.)
#IPv6Exit 1
diff --git a/src/core/crypto/hs_ntor.c b/src/core/crypto/hs_ntor.c
index c34073690e..add8a2b8f2 100644
--- a/src/core/crypto/hs_ntor.c
+++ b/src/core/crypto/hs_ntor.c
@@ -176,7 +176,6 @@ get_introduce1_key_material(const uint8_t *secret_input,
uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN];
uint8_t info_blob[INFO_BLOB_LEN];
uint8_t kdf_input[KDF_INPUT_LEN];
- crypto_xof_t *xof;
uint8_t *ptr;
/* Let's build info */
@@ -193,10 +192,8 @@ get_introduce1_key_material(const uint8_t *secret_input,
tor_assert(ptr == kdf_input + sizeof(kdf_input));
/* Now we need to run kdf_input over SHAKE-256 */
- xof = crypto_xof_new();
- crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input));
- crypto_xof_squeeze_bytes(xof, keystream, sizeof(keystream)) ;
- crypto_xof_free(xof);
+ crypto_xof(keystream, sizeof(keystream),
+ kdf_input, sizeof(kdf_input));
{ /* Get the keys */
memcpy(&hs_ntor_intro_cell_keys_out->enc_key, keystream,CIPHER256_KEY_LEN);
@@ -594,7 +591,6 @@ hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len,
{
uint8_t *ptr;
uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN];
- crypto_xof_t *xof;
/* Sanity checks on lengths to make sure we are good */
if (BUG(seed_len != DIGEST256_LEN)) {
@@ -611,10 +607,8 @@ hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len,
tor_assert(ptr == kdf_input + sizeof(kdf_input));
/* Generate the keys */
- xof = crypto_xof_new();
- crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input));
- crypto_xof_squeeze_bytes(xof, keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN);
- crypto_xof_free(xof);
+ crypto_xof(keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN,
+ kdf_input, sizeof(kdf_input));
return 0;
}
diff --git a/src/core/crypto/onion_crypto.h b/src/core/crypto/onion_crypto.h
index 1cddde3610..7abdd6538e 100644
--- a/src/core/crypto/onion_crypto.h
+++ b/src/core/crypto/onion_crypto.h
@@ -44,4 +44,4 @@ void server_onion_keys_free_(server_onion_keys_t *keys);
#define server_onion_keys_free(keys) \
FREE_AND_NULL(server_onion_keys_t, server_onion_keys_free_, (keys))
-#endif
+#endif /* !defined(TOR_ONION_CRYPTO_H) */
diff --git a/src/core/crypto/relay_crypto.c b/src/core/crypto/relay_crypto.c
index 0b83b2d0a5..8a285131a8 100644
--- a/src/core/crypto/relay_crypto.c
+++ b/src/core/crypto/relay_crypto.c
@@ -6,12 +6,14 @@
#include "core/or/or.h"
#include "core/or/circuitlist.h"
+#include "core/or/crypt_path.h"
#include "app/config/config.h"
#include "lib/crypt_ops/crypto_cipher.h"
#include "lib/crypt_ops/crypto_util.h"
#include "core/crypto/hs_ntor.h" // for HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN
#include "core/or/relay.h"
#include "core/crypto/relay_crypto.h"
+#include "core/or/sendme.h"
#include "core/or/cell_st.h"
#include "core/or/or_circuit_st.h"
@@ -20,7 +22,7 @@
/** Update digest from the payload of cell. Assign integrity part to
* cell.
*/
-static void
+void
relay_set_digest(crypto_digest_t *digest, cell_t *cell)
{
char integrity[4];
@@ -84,12 +86,39 @@ relay_digest_matches(crypto_digest_t *digest, cell_t *cell)
*
* Note that we use the same operation for encrypting and for decrypting.
*/
-static void
+void
relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in)
{
crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE);
}
+/** Return the sendme_digest within the <b>crypto</b> object. */
+uint8_t *
+relay_crypto_get_sendme_digest(relay_crypto_t *crypto)
+{
+ tor_assert(crypto);
+ return crypto->sendme_digest;
+}
+
+/** Record the cell digest, indicated by is_foward_digest or not, as the
+ * SENDME cell digest. */
+void
+relay_crypto_record_sendme_digest(relay_crypto_t *crypto,
+ bool is_foward_digest)
+{
+ struct crypto_digest_t *digest;
+
+ tor_assert(crypto);
+
+ digest = crypto->b_digest;
+ if (is_foward_digest) {
+ digest = crypto->f_digest;
+ }
+
+ crypto_digest_get_digest(digest, (char *) crypto->sendme_digest,
+ sizeof(crypto->sendme_digest));
+}
+
/** Do the appropriate en/decryptions for <b>cell</b> arriving on
* <b>circ</b> in direction <b>cell_direction</b>.
*
@@ -134,12 +163,12 @@ relay_decrypt_cell(circuit_t *circ, cell_t *cell,
tor_assert(thishop);
/* decrypt one layer */
- relay_crypt_one_payload(thishop->crypto.b_crypto, cell->payload);
+ cpath_crypt_cell(thishop, cell->payload, true);
relay_header_unpack(&rh, cell->payload);
if (rh.recognized == 0) {
/* it's possibly recognized. have to check digest to be sure. */
- if (relay_digest_matches(thishop->crypto.b_digest, cell)) {
+ if (relay_digest_matches(cpath_get_incoming_digest(thishop), cell)) {
*recognized = 1;
*layer_hint = thishop;
return 0;
@@ -187,14 +216,17 @@ relay_encrypt_cell_outbound(cell_t *cell,
crypt_path_t *layer_hint)
{
crypt_path_t *thishop; /* counter for repeated crypts */
- relay_set_digest(layer_hint->crypto.f_digest, cell);
+ cpath_set_cell_forward_digest(layer_hint, cell);
+
+ /* Record cell digest as the SENDME digest if need be. */
+ sendme_record_sending_cell_digest(TO_CIRCUIT(circ), layer_hint);
thishop = layer_hint;
/* moving from farthest to nearest hop */
do {
tor_assert(thishop);
log_debug(LD_OR,"encrypting a layer of the relay cell.");
- relay_crypt_one_payload(thishop->crypto.f_crypto, cell->payload);
+ cpath_crypt_cell(thishop, cell->payload, false);
thishop = thishop->prev;
} while (thishop != circ->cpath->prev);
@@ -212,6 +244,10 @@ relay_encrypt_cell_inbound(cell_t *cell,
or_circuit_t *or_circ)
{
relay_set_digest(or_circ->crypto.b_digest, cell);
+
+ /* Record cell digest as the SENDME digest if need be. */
+ sendme_record_sending_cell_digest(TO_CIRCUIT(or_circ), NULL);
+
/* encrypt one layer */
relay_crypt_one_payload(or_circ->crypto.b_crypto, cell->payload);
}
diff --git a/src/core/crypto/relay_crypto.h b/src/core/crypto/relay_crypto.h
index 45a21d14ab..9478f8d359 100644
--- a/src/core/crypto/relay_crypto.h
+++ b/src/core/crypto/relay_crypto.h
@@ -27,5 +27,16 @@ void relay_crypto_clear(relay_crypto_t *crypto);
void relay_crypto_assert_ok(const relay_crypto_t *crypto);
+uint8_t *relay_crypto_get_sendme_digest(relay_crypto_t *crypto);
+
+void relay_crypto_record_sendme_digest(relay_crypto_t *crypto,
+ bool is_foward_digest);
+
+void
+relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in);
+
+void
+relay_set_digest(crypto_digest_t *digest, cell_t *cell);
+
#endif /* !defined(TOR_RELAY_CRYPTO_H) */
diff --git a/src/core/include.am b/src/core/include.am
index ae47c75e09..1a4b9fb8ab 100644
--- a/src/core/include.am
+++ b/src/core/include.am
@@ -6,11 +6,13 @@ noinst_LIBRARIES += \
src/core/libtor-app-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
LIBTOR_APP_A_SOURCES = \
src/app/config/config.c \
src/app/config/confparse.c \
src/app/config/statefile.c \
src/app/main/main.c \
+ src/app/main/shutdown.c \
src/app/main/subsystem_list.c \
src/app/main/subsysmgr.c \
src/core/crypto/hs_ntor.c \
@@ -22,6 +24,8 @@ LIBTOR_APP_A_SOURCES = \
src/core/mainloop/connection.c \
src/core/mainloop/cpuworker.c \
src/core/mainloop/mainloop.c \
+ src/core/mainloop/mainloop_pubsub.c \
+ src/core/mainloop/mainloop_sys.c \
src/core/mainloop/netstatus.c \
src/core/mainloop/periodic.c \
src/core/or/address_set.c \
@@ -33,14 +37,18 @@ LIBTOR_APP_A_SOURCES = \
src/core/or/circuitmux.c \
src/core/or/circuitmux_ewma.c \
src/core/or/circuitpadding.c \
+ src/core/or/circuitpadding_machines.c \
src/core/or/circuitstats.c \
src/core/or/circuituse.c \
+ src/core/or/crypt_path.c \
src/core/or/command.c \
src/core/or/connection_edge.c \
src/core/or/connection_or.c \
src/core/or/dos.c \
src/core/or/onion.c \
src/core/or/ocirc_event.c \
+ src/core/or/or_periodic.c \
+ src/core/or/or_sys.c \
src/core/or/orconn_event.c \
src/core/or/policies.c \
src/core/or/protover.c \
@@ -50,6 +58,7 @@ LIBTOR_APP_A_SOURCES = \
src/core/or/scheduler.c \
src/core/or/scheduler_kist.c \
src/core/or/scheduler_vanilla.c \
+ src/core/or/sendme.c \
src/core/or/status.c \
src/core/or/versions.c \
src/core/proto/proto_cell.c \
@@ -70,10 +79,15 @@ LIBTOR_APP_A_SOURCES = \
src/feature/control/btrack_orconn_cevent.c \
src/feature/control/btrack_orconn_maps.c \
src/feature/control/control.c \
+ src/feature/control/control_auth.c \
src/feature/control/control_bootstrap.c \
+ src/feature/control/control_cmd.c \
+ src/feature/control/control_events.c \
+ src/feature/control/control_fmt.c \
+ src/feature/control/control_getinfo.c \
+ src/feature/control/control_proto.c \
src/feature/control/fmt_serverstatus.c \
src/feature/control/getinfo_geoip.c \
- src/feature/dirauth/keypin.c \
src/feature/dircache/conscache.c \
src/feature/dircache/consdiffmgr.c \
src/feature/dircache/dircache.c \
@@ -110,7 +124,6 @@ LIBTOR_APP_A_SOURCES = \
src/feature/hs_common/replaycache.c \
src/feature/hs_common/shared_random_client.c \
src/feature/keymgt/loadkey.c \
- src/feature/dirauth/keypin.c \
src/feature/nodelist/authcert.c \
src/feature/nodelist/describe.c \
src/feature/nodelist/dirlist.c \
@@ -128,6 +141,8 @@ LIBTOR_APP_A_SOURCES = \
src/feature/relay/dns.c \
src/feature/relay/ext_orport.c \
src/feature/relay/onion_queue.c \
+ src/feature/relay/relay_periodic.c \
+ src/feature/relay/relay_sys.c \
src/feature/relay/router.c \
src/feature/relay/routerkeys.c \
src/feature/relay/routermode.c \
@@ -142,17 +157,6 @@ LIBTOR_APP_A_SOURCES = \
src/feature/stats/rephist.c \
src/feature/stats/predict_ports.c
-# These should eventually move into module_dirauth_sources, but for now
-# the separation is only in the code location.
-LIBTOR_APP_A_SOURCES += \
- src/feature/dirauth/bwauth.c \
- src/feature/dirauth/dsigs_parse.c \
- src/feature/dirauth/guardfraction.c \
- src/feature/dirauth/reachability.c \
- src/feature/dirauth/recommend_pkg.c \
- src/feature/dirauth/process_descs.c \
- src/feature/dirauth/voteflags.c
-
if BUILD_NT_SERVICES
LIBTOR_APP_A_SOURCES += src/app/main/ntmain.c
endif
@@ -168,10 +172,21 @@ LIBTOR_APP_TESTING_A_SOURCES = $(LIBTOR_APP_A_SOURCES)
# The Directory Authority module.
MODULE_DIRAUTH_SOURCES = \
src/feature/dirauth/authmode.c \
+ src/feature/dirauth/bridgeauth.c \
+ src/feature/dirauth/bwauth.c \
+ src/feature/dirauth/dirauth_periodic.c \
+ src/feature/dirauth/dirauth_sys.c \
src/feature/dirauth/dircollate.c \
src/feature/dirauth/dirvote.c \
+ src/feature/dirauth/dsigs_parse.c \
+ src/feature/dirauth/guardfraction.c \
+ src/feature/dirauth/keypin.c \
+ src/feature/dirauth/process_descs.c \
+ src/feature/dirauth/reachability.c \
+ src/feature/dirauth/recommend_pkg.c \
src/feature/dirauth/shared_random.c \
- src/feature/dirauth/shared_random_state.c
+ src/feature/dirauth/shared_random_state.c \
+ src/feature/dirauth/voteflags.c
if BUILD_MODULE_DIRAUTH
LIBTOR_APP_A_SOURCES += $(MODULE_DIRAUTH_SOURCES)
@@ -195,6 +210,7 @@ AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \
src_core_libtor_app_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_core_libtor_app_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/app/config/config.h \
src/app/config/confparse.h \
@@ -203,6 +219,7 @@ noinst_HEADERS += \
src/app/config/statefile.h \
src/app/main/main.h \
src/app/main/ntmain.h \
+ src/app/main/shutdown.h \
src/app/main/subsysmgr.h \
src/core/crypto/hs_ntor.h \
src/core/crypto/onion_crypto.h \
@@ -213,6 +230,8 @@ noinst_HEADERS += \
src/core/mainloop/connection.h \
src/core/mainloop/cpuworker.h \
src/core/mainloop/mainloop.h \
+ src/core/mainloop/mainloop_pubsub.h \
+ src/core/mainloop/mainloop_sys.h \
src/core/mainloop/netstatus.h \
src/core/mainloop/periodic.h \
src/core/or/addr_policy_st.h \
@@ -229,24 +248,28 @@ noinst_HEADERS += \
src/core/or/circuitmux_ewma.h \
src/core/or/circuitstats.h \
src/core/or/circuitpadding.h \
+ src/core/or/circuitpadding_machines.h \
src/core/or/circuituse.h \
src/core/or/command.h \
src/core/or/connection_edge.h \
src/core/or/connection_or.h \
src/core/or/connection_st.h \
+ src/core/or/crypt_path.h \
src/core/or/cpath_build_state_st.h \
src/core/or/crypt_path_reference_st.h \
src/core/or/crypt_path_st.h \
src/core/or/destroy_cell_queue_st.h \
src/core/or/dos.h \
src/core/or/edge_connection_st.h \
- src/core/or/half_edge_st.h \
+ src/core/or/half_edge_st.h \
src/core/or/entry_connection_st.h \
src/core/or/entry_port_cfg_st.h \
src/core/or/extend_info_st.h \
src/core/or/listener_connection_st.h \
src/core/or/onion.h \
src/core/or/or.h \
+ src/core/or/or_periodic.h \
+ src/core/or/or_sys.h \
src/core/or/orconn_event.h \
src/core/or/orconn_event_sys.h \
src/core/or/or_circuit_st.h \
@@ -263,6 +286,7 @@ noinst_HEADERS += \
src/core/or/relay.h \
src/core/or/relay_crypto_st.h \
src/core/or/scheduler.h \
+ src/core/or/sendme.h \
src/core/or/server_port_cfg_st.h \
src/core/or/socks_request_st.h \
src/core/or/status.h \
@@ -287,11 +311,21 @@ noinst_HEADERS += \
src/feature/control/btrack_orconn_maps.h \
src/feature/control/btrack_sys.h \
src/feature/control/control.h \
+ src/feature/control/control_auth.h \
+ src/feature/control/control_cmd.h \
+ src/feature/control/control_cmd_args_st.h \
src/feature/control/control_connection_st.h \
+ src/feature/control/control_events.h \
+ src/feature/control/control_fmt.h \
+ src/feature/control/control_getinfo.h \
+ src/feature/control/control_proto.h \
src/feature/control/fmt_serverstatus.h \
src/feature/control/getinfo_geoip.h \
src/feature/dirauth/authmode.h \
+ src/feature/dirauth/bridgeauth.h \
src/feature/dirauth/bwauth.h \
+ src/feature/dirauth/dirauth_periodic.h \
+ src/feature/dirauth/dirauth_sys.h \
src/feature/dirauth/dircollate.h \
src/feature/dirauth/dirvote.h \
src/feature/dirauth/dsigs_parse.h \
@@ -363,8 +397,8 @@ noinst_HEADERS += \
src/feature/nodelist/networkstatus_voter_info_st.h \
src/feature/nodelist/nickname.h \
src/feature/nodelist/node_st.h \
- src/feature/nodelist/nodefamily.h \
- src/feature/nodelist/nodefamily_st.h \
+ src/feature/nodelist/nodefamily.h \
+ src/feature/nodelist/nodefamily_st.h \
src/feature/nodelist/nodelist.h \
src/feature/nodelist/node_select.h \
src/feature/nodelist/routerinfo.h \
@@ -381,6 +415,8 @@ noinst_HEADERS += \
src/feature/relay/dns_structs.h \
src/feature/relay/ext_orport.h \
src/feature/relay/onion_queue.h \
+ src/feature/relay/relay_periodic.h \
+ src/feature/relay/relay_sys.h \
src/feature/relay/router.h \
src/feature/relay/routerkeys.h \
src/feature/relay/routermode.h \
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 73b8ef4da5..127f08683f 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -82,12 +82,14 @@
#include "core/or/policies.h"
#include "core/or/reasons.h"
#include "core/or/relay.h"
+#include "core/or/crypt_path.h"
#include "core/proto/proto_http.h"
#include "core/proto/proto_socks.h"
#include "feature/client/dnsserv.h"
#include "feature/client/entrynodes.h"
#include "feature/client/transports.h"
#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/authmode.h"
#include "feature/dircache/dirserv.h"
#include "feature/dircommon/directory.h"
@@ -696,6 +698,7 @@ connection_free_minimal(connection_t *conn)
control_connection_t *control_conn = TO_CONTROL_CONN(conn);
tor_free(control_conn->safecookie_client_hash);
tor_free(control_conn->incoming_cmd);
+ tor_free(control_conn->current_cmd);
if (control_conn->ephemeral_onion_services) {
SMARTLIST_FOREACH(control_conn->ephemeral_onion_services, char *, cp, {
memwipe(cp, 0, strlen(cp));
@@ -1473,7 +1476,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
goto err;
}
}
-#endif /* __APPLE__ */
+#endif /* !defined(__APPLE__) */
#endif /* defined(HAVE_SYS_UN_H) */
} else {
log_err(LD_BUG, "Got unexpected address family %d.",
@@ -1652,7 +1655,7 @@ check_sockaddr(const struct sockaddr *sa, int len, int level)
len,(int)sizeof(struct sockaddr_in6));
ok = 0;
}
- if (tor_mem_is_zero((void*)sin6->sin6_addr.s6_addr, 16) ||
+ if (fast_mem_is_zero((void*)sin6->sin6_addr.s6_addr, 16) ||
sin6->sin6_port == 0) {
log_fn(level, LD_NET,
"Address for new connection has address/port equal to zero.");
@@ -2853,7 +2856,7 @@ retry_listener_ports(smartlist_t *old_conns,
SMARTLIST_DEL_CURRENT(old_conns, conn);
break;
}
-#endif
+#endif /* defined(ENABLE_LISTENER_REBIND) */
}
} SMARTLIST_FOREACH_END(wanted);
@@ -2955,7 +2958,7 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol)
conn_type_to_string(old_conn->type), old_conn->address,
old_conn->port, new_conn->address, new_conn->port);
} SMARTLIST_FOREACH_END(r);
-#endif
+#endif /* defined(ENABLE_LISTENER_REBIND) */
/* Any members that were still in 'listeners' don't correspond to
* any configured port. Kill 'em. */
@@ -3954,9 +3957,9 @@ update_send_buffer_size(tor_socket_t sock)
&isb, sizeof(isb), &bytesReturned, NULL, NULL)) {
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&isb, sizeof(isb));
}
-#else
+#else /* !(defined(_WIN32)) */
(void) sock;
-#endif
+#endif /* defined(_WIN32) */
}
/** Try to flush more bytes onto <b>conn</b>-\>s.
@@ -5337,7 +5340,7 @@ assert_connection_ok(connection_t *conn, time_t now)
tor_assert(entry_conn->socks_request->has_finished);
if (!conn->marked_for_close) {
tor_assert(ENTRY_TO_EDGE_CONN(entry_conn)->cpath_layer);
- assert_cpath_layer_ok(ENTRY_TO_EDGE_CONN(entry_conn)->cpath_layer);
+ cpath_assert_layer_ok(ENTRY_TO_EDGE_CONN(entry_conn)->cpath_layer);
}
}
}
diff --git a/src/core/mainloop/cpuworker.c b/src/core/mainloop/cpuworker.c
index e704d55642..436fcd28c3 100644
--- a/src/core/mainloop/cpuworker.c
+++ b/src/core/mainloop/cpuworker.c
@@ -34,7 +34,6 @@
#include "core/crypto/onion_crypto.h"
#include "core/or/or_circuit_st.h"
-#include "lib/intmath/weakrng.h"
static void queue_pending_tasks(void);
@@ -74,8 +73,6 @@ worker_state_free_void(void *arg)
static replyqueue_t *replyqueue = NULL;
static threadpool_t *threadpool = NULL;
-static tor_weak_rng_t request_sample_rng = TOR_WEAK_RNG_INIT;
-
static int total_pending_tasks = 0;
static int max_pending_tasks = 128;
@@ -109,7 +106,6 @@ cpu_init(void)
/* Total voodoo. Can we make this more sensible? */
max_pending_tasks = get_num_cpus(get_options()) * 64;
- crypto_seed_weak_rng(&request_sample_rng);
}
/** Magic numbers to make sure our cpuworker_requests don't grow any
@@ -235,9 +231,10 @@ should_time_request(uint16_t onionskin_type)
* sample */
if (onionskins_n_processed[onionskin_type] < 4096)
return 1;
+
/** Otherwise, measure with P=1/128. We avoid doing this for every
* handshake, since the measurement itself can take a little time. */
- return tor_weak_random_one_in_n(&request_sample_rng, 128);
+ return crypto_fast_rng_one_in_n(get_thread_fast_rng(), 128);
}
/** Return an estimate of how many microseconds we will need for a single
diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c
index 18e87fa87a..c051b11566 100644
--- a/src/core/mainloop/mainloop.c
+++ b/src/core/mainloop/mainloop.c
@@ -73,8 +73,8 @@
#include "feature/client/entrynodes.h"
#include "feature/client/transports.h"
#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/authmode.h"
-#include "feature/dirauth/reachability.h"
#include "feature/dircache/consdiffmgr.h"
#include "feature/dircache/dirserv.h"
#include "feature/dircommon/directory.h"
@@ -105,9 +105,6 @@
#include <event2/event.h>
-#include "feature/dirauth/dirvote.h"
-#include "feature/dirauth/authmode.h"
-
#include "core/or/cell_st.h"
#include "core/or/entry_connection_st.h"
#include "feature/nodelist/networkstatus_st.h"
@@ -757,7 +754,7 @@ tor_shutdown_event_loop_for_restart_cb(
tor_event_free(tor_shutdown_event_loop_for_restart_event);
tor_shutdown_event_loop_and_exit(0);
}
-#endif
+#endif /* defined(ENABLE_RESTART_DEBUGGING) */
/**
* After finishing the current callback (if any), shut down the main loop,
@@ -1345,36 +1342,20 @@ static int periodic_events_initialized = 0;
#define CALLBACK(name) \
static int name ## _callback(time_t, const or_options_t *)
CALLBACK(add_entropy);
-CALLBACK(check_authority_cert);
-CALLBACK(check_canonical_channels);
-CALLBACK(check_descriptor);
-CALLBACK(check_dns_honesty);
-CALLBACK(check_ed_keys);
CALLBACK(check_expired_networkstatus);
-CALLBACK(check_for_reachability_bw);
-CALLBACK(check_onion_keys_expiry_time);
CALLBACK(clean_caches);
CALLBACK(clean_consdiffmgr);
-CALLBACK(dirvote);
-CALLBACK(downrate_stability);
-CALLBACK(expire_old_ciruits_serverside);
CALLBACK(fetch_networkstatus);
CALLBACK(heartbeat);
CALLBACK(hs_service);
CALLBACK(launch_descriptor_fetches);
-CALLBACK(launch_reachability_tests);
CALLBACK(prune_old_routers);
-CALLBACK(reachability_warnings);
CALLBACK(record_bridge_stats);
CALLBACK(rend_cache_failure_clean);
CALLBACK(reset_padding_counts);
-CALLBACK(retry_dns);
CALLBACK(retry_listeners);
-CALLBACK(rotate_onion_key);
CALLBACK(rotate_x509_certificate);
-CALLBACK(save_stability);
CALLBACK(save_state);
-CALLBACK(write_bridge_ns);
CALLBACK(write_stats_file);
CALLBACK(control_per_second_events);
CALLBACK(second_elapsed);
@@ -1386,7 +1367,7 @@ CALLBACK(second_elapsed);
PERIODIC_EVENT(name, PERIODIC_EVENT_ROLE_ ## r, f)
#define FL(name) (PERIODIC_EVENT_FLAG_ ## name)
-STATIC periodic_event_item_t periodic_events[] = {
+STATIC periodic_event_item_t mainloop_periodic_events[] = {
/* Everyone needs to run these. They need to have very long timeouts for
* that to be safe. */
@@ -1417,29 +1398,6 @@ STATIC periodic_event_item_t periodic_events[] = {
CALLBACK(write_stats_file, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
CALLBACK(prune_old_routers, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
- /* Routers (bridge and relay) only. */
- CALLBACK(check_descriptor, ROUTER, FL(NEED_NET)),
- CALLBACK(check_ed_keys, ROUTER, 0),
- CALLBACK(check_for_reachability_bw, ROUTER, FL(NEED_NET)),
- CALLBACK(check_onion_keys_expiry_time, ROUTER, 0),
- CALLBACK(expire_old_ciruits_serverside, ROUTER, FL(NEED_NET)),
- CALLBACK(reachability_warnings, ROUTER, FL(NEED_NET)),
- CALLBACK(retry_dns, ROUTER, 0),
- CALLBACK(rotate_onion_key, ROUTER, 0),
-
- /* Authorities (bridge and directory) only. */
- CALLBACK(downrate_stability, AUTHORITIES, 0),
- CALLBACK(launch_reachability_tests, AUTHORITIES, FL(NEED_NET)),
- CALLBACK(save_stability, AUTHORITIES, 0),
-
- /* Directory authority only. */
- CALLBACK(check_authority_cert, DIRAUTH, 0),
- CALLBACK(dirvote, DIRAUTH, FL(NEED_NET)),
-
- /* Relay only. */
- CALLBACK(check_canonical_channels, RELAY, FL(NEED_NET)),
- CALLBACK(check_dns_honesty, RELAY, FL(NEED_NET)),
-
/* Hidden Service service only. */
CALLBACK(hs_service, HS_SERVICE, FL(NEED_NET)), // XXXX break this down more
@@ -1450,9 +1408,6 @@ STATIC periodic_event_item_t periodic_events[] = {
/* XXXX this could be restricted to CLIENT+NET_PARTICIPANT */
CALLBACK(rend_cache_failure_clean, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
- /* Bridge Authority only. */
- CALLBACK(write_bridge_ns, BRIDGEAUTH, 0),
-
/* Directory server only. */
CALLBACK(clean_consdiffmgr, DIRSERVER, 0),
@@ -1468,8 +1423,6 @@ STATIC periodic_event_item_t periodic_events[] = {
* implement particular callbacks. We keep them separate here so that we
* can access them by name. We also keep them inside periodic_events[]
* so that we can implement "reset all timers" in a reasonable way. */
-static periodic_event_item_t *check_descriptor_event=NULL;
-static periodic_event_item_t *dirvote_event=NULL;
static periodic_event_item_t *fetch_networkstatus_event=NULL;
static periodic_event_item_t *launch_descriptor_fetches_event=NULL;
static periodic_event_item_t *check_dns_honesty_event=NULL;
@@ -1484,24 +1437,7 @@ static periodic_event_item_t *prune_old_routers_event=NULL;
void
reset_all_main_loop_timers(void)
{
- int i;
- for (i = 0; periodic_events[i].name; ++i) {
- periodic_event_reschedule(&periodic_events[i]);
- }
-}
-
-/** Return the member of periodic_events[] whose name is <b>name</b>.
- * Return NULL if no such event is found.
- */
-static periodic_event_item_t *
-find_periodic_event(const char *name)
-{
- int i;
- for (i = 0; periodic_events[i].name; ++i) {
- if (strcmp(name, periodic_events[i].name) == 0)
- return &periodic_events[i];
- }
- return NULL;
+ periodic_events_reset_all();
}
/** Return a bitmask of the roles this tor instance is configured for using
@@ -1564,9 +1500,9 @@ initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data)
rescan_periodic_events(get_options());
}
-/** Set up all the members of periodic_events[], and configure them all to be
- * launched from a callback. */
-STATIC void
+/** Set up all the members of mainloop_periodic_events[], and configure them
+ * all to be launched from a callback. */
+void
initialize_periodic_events(void)
{
if (periodic_events_initialized)
@@ -1574,37 +1510,31 @@ initialize_periodic_events(void)
periodic_events_initialized = 1;
- /* Set up all periodic events. We'll launch them by roles. */
- int i;
- for (i = 0; periodic_events[i].name; ++i) {
- periodic_event_setup(&periodic_events[i]);
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_events_register(&mainloop_periodic_events[i]);
}
+ /* Set up all periodic events. We'll launch them by roles. */
+
#define NAMED_CALLBACK(name) \
- STMT_BEGIN name ## _event = find_periodic_event( #name ); STMT_END
+ STMT_BEGIN name ## _event = periodic_events_find( #name ); STMT_END
- NAMED_CALLBACK(check_descriptor);
NAMED_CALLBACK(prune_old_routers);
- NAMED_CALLBACK(dirvote);
NAMED_CALLBACK(fetch_networkstatus);
NAMED_CALLBACK(launch_descriptor_fetches);
NAMED_CALLBACK(check_dns_honesty);
NAMED_CALLBACK(save_state);
-
- struct timeval one_second = { 1, 0 };
- initialize_periodic_events_event = tor_evtimer_new(
- tor_libevent_get_base(),
- initialize_periodic_events_cb, NULL);
- event_add(initialize_periodic_events_event, &one_second);
}
STATIC void
teardown_periodic_events(void)
{
- int i;
- for (i = 0; periodic_events[i].name; ++i) {
- periodic_event_destroy(&periodic_events[i]);
- }
+ periodic_events_disconnect_all();
+ fetch_networkstatus_event = NULL;
+ launch_descriptor_fetches_event = NULL;
+ check_dns_honesty_event = NULL;
+ save_state_event = NULL;
+ prune_old_routers_event = NULL;
periodic_events_initialized = 0;
}
@@ -1639,40 +1569,7 @@ rescan_periodic_events(const or_options_t *options)
{
tor_assert(options);
- /* Avoid scanning the event list if we haven't initialized it yet. This is
- * particularly useful for unit tests in order to avoid initializing main
- * loop events everytime. */
- if (!periodic_events_initialized) {
- return;
- }
-
- int roles = get_my_roles(options);
-
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
-
- int enable = !!(item->roles & roles);
-
- /* Handle the event flags. */
- if (net_is_disabled() &&
- (item->flags & PERIODIC_EVENT_FLAG_NEED_NET)) {
- enable = 0;
- }
-
- /* Enable the event if needed. It is safe to enable an event that was
- * already enabled. Same goes for disabling it. */
- if (enable) {
- log_debug(LD_GENERAL, "Launching periodic event %s", item->name);
- periodic_event_enable(item);
- } else {
- log_debug(LD_GENERAL, "Disabling periodic event %s", item->name);
- if (item->flags & PERIODIC_EVENT_FLAG_RUN_ON_DISABLE) {
- periodic_event_schedule_and_disable(item);
- } else {
- periodic_event_disable(item);
- }
- }
- }
+ periodic_events_rescan_by_roles(get_my_roles(options), net_is_disabled());
}
/* We just got new options globally set, see if we need to enabled or disable
@@ -1680,26 +1577,7 @@ rescan_periodic_events(const or_options_t *options)
void
periodic_events_on_new_options(const or_options_t *options)
{
- /* Only if we've already initialized the events, rescan the list which will
- * enable or disable events depending on our roles. This will be called at
- * bootup and we don't want this function to initialize the events because
- * they aren't set up at this stage. */
- if (periodic_events_initialized) {
- rescan_periodic_events(options);
- }
-}
-
-/**
- * Update our schedule so that we'll check whether we need to update our
- * descriptor immediately, rather than after up to CHECK_DESCRIPTOR_INTERVAL
- * seconds.
- */
-void
-reschedule_descriptor_update_check(void)
-{
- if (check_descriptor_event) {
- periodic_event_reschedule(check_descriptor_event);
- }
+ rescan_periodic_events(options);
}
/**
@@ -1769,29 +1647,6 @@ mainloop_schedule_shutdown(int delay_sec)
mainloop_event_schedule(scheduled_shutdown_ev, &delay_tv);
}
-#define LONGEST_TIMER_PERIOD (30 * 86400)
-/** Helper: Return the number of seconds between <b>now</b> and <b>next</b>,
- * clipped to the range [1 second, LONGEST_TIMER_PERIOD]. */
-static inline int
-safe_timer_diff(time_t now, time_t next)
-{
- if (next > now) {
- /* There were no computers at signed TIME_MIN (1902 on 32-bit systems),
- * and nothing that could run Tor. It's a bug if 'next' is around then.
- * On 64-bit systems with signed TIME_MIN, TIME_MIN is before the Big
- * Bang. We cannot extrapolate past a singularity, but there was probably
- * nothing that could run Tor then, either.
- **/
- tor_assert(next > TIME_MIN + LONGEST_TIMER_PERIOD);
-
- if (next - LONGEST_TIMER_PERIOD > now)
- return LONGEST_TIMER_PERIOD;
- return (int)(next - now);
- } else {
- return 1;
- }
-}
-
/** Perform regular maintenance tasks. This function gets run once per
* second.
*/
@@ -1867,77 +1722,6 @@ second_elapsed_callback(time_t now, const or_options_t *options)
return 1;
}
-/* Periodic callback: rotate the onion keys after the period defined by the
- * "onion-key-rotation-days" consensus parameter, shut down and restart all
- * cpuworkers, and update our descriptor if necessary.
- */
-static int
-rotate_onion_key_callback(time_t now, const or_options_t *options)
-{
- if (server_mode(options)) {
- int onion_key_lifetime = get_onion_key_lifetime();
- time_t rotation_time = get_onion_key_set_at()+onion_key_lifetime;
- if (rotation_time > now) {
- return ONION_KEY_CONSENSUS_CHECK_INTERVAL;
- }
-
- log_info(LD_GENERAL,"Rotating onion key.");
- rotate_onion_key();
- cpuworkers_rotate_keyinfo();
- if (router_rebuild_descriptor(1)<0) {
- log_info(LD_CONFIG, "Couldn't rebuild router descriptor");
- }
- if (advertised_server_mode() && !net_is_disabled())
- router_upload_dir_desc_to_dirservers(0);
- return ONION_KEY_CONSENSUS_CHECK_INTERVAL;
- }
- return PERIODIC_EVENT_NO_UPDATE;
-}
-
-/* Period callback: Check if our old onion keys are still valid after the
- * period of time defined by the consensus parameter
- * "onion-key-grace-period-days", otherwise expire them by setting them to
- * NULL.
- */
-static int
-check_onion_keys_expiry_time_callback(time_t now, const or_options_t *options)
-{
- if (server_mode(options)) {
- int onion_key_grace_period = get_onion_key_grace_period();
- time_t expiry_time = get_onion_key_set_at()+onion_key_grace_period;
- if (expiry_time > now) {
- return ONION_KEY_CONSENSUS_CHECK_INTERVAL;
- }
-
- log_info(LD_GENERAL, "Expiring old onion keys.");
- expire_old_onion_keys();
- cpuworkers_rotate_keyinfo();
- return ONION_KEY_CONSENSUS_CHECK_INTERVAL;
- }
-
- return PERIODIC_EVENT_NO_UPDATE;
-}
-
-/* Periodic callback: Every 30 seconds, check whether it's time to make new
- * Ed25519 subkeys.
- */
-static int
-check_ed_keys_callback(time_t now, const or_options_t *options)
-{
- if (server_mode(options)) {
- if (should_make_new_ed_keys(options, now)) {
- int new_signing_key = load_ed_keys(options, now);
- if (new_signing_key < 0 ||
- generate_ed_link_cert(options, now, new_signing_key > 0)) {
- log_err(LD_OR, "Unable to update Ed25519 keys! Exiting.");
- tor_shutdown_event_loop_and_exit(1);
- }
- }
- return 30;
- }
- return PERIODIC_EVENT_NO_UPDATE;
-}
-
/**
* Periodic callback: Every {LAZY,GREEDY}_DESCRIPTOR_RETRY_INTERVAL,
* see about fetching descriptors, microdescriptors, and extrainfo
@@ -2061,102 +1845,6 @@ check_network_participation_callback(time_t now, const or_options_t *options)
}
/**
- * Periodic callback: if we're an authority, make sure we test
- * the routers on the network for reachability.
- */
-static int
-launch_reachability_tests_callback(time_t now, const or_options_t *options)
-{
- if (authdir_mode_tests_reachability(options) &&
- !net_is_disabled()) {
- /* try to determine reachability of the other Tor relays */
- dirserv_test_reachability(now);
- }
- return REACHABILITY_TEST_INTERVAL;
-}
-
-/**
- * Periodic callback: if we're an authority, discount the stability
- * information (and other rephist information) that's older.
- */
-static int
-downrate_stability_callback(time_t now, const or_options_t *options)
-{
- (void)options;
- /* 1d. Periodically, we discount older stability information so that new
- * stability info counts more, and save the stability information to disk as
- * appropriate. */
- time_t next = rep_hist_downrate_old_runs(now);
- return safe_timer_diff(now, next);
-}
-
-/**
- * Periodic callback: if we're an authority, record our measured stability
- * information from rephist in an mtbf file.
- */
-static int
-save_stability_callback(time_t now, const or_options_t *options)
-{
- if (authdir_mode_tests_reachability(options)) {
- if (rep_hist_record_mtbf_data(now, 1)<0) {
- log_warn(LD_GENERAL, "Couldn't store mtbf data.");
- }
- }
-#define SAVE_STABILITY_INTERVAL (30*60)
- return SAVE_STABILITY_INTERVAL;
-}
-
-/**
- * Periodic callback: if we're an authority, check on our authority
- * certificate (the one that authenticates our authority signing key).
- */
-static int
-check_authority_cert_callback(time_t now, const or_options_t *options)
-{
- (void)now;
- (void)options;
- /* 1e. Periodically, if we're a v3 authority, we check whether our cert is
- * close to expiring and warn the admin if it is. */
- v3_authority_check_key_expiry();
-#define CHECK_V3_CERTIFICATE_INTERVAL (5*60)
- return CHECK_V3_CERTIFICATE_INTERVAL;
-}
-
-/**
- * Scheduled callback: Run directory-authority voting functionality.
- *
- * The schedule is a bit complicated here, so dirvote_act() manages the
- * schedule itself.
- **/
-static int
-dirvote_callback(time_t now, const or_options_t *options)
-{
- if (!authdir_mode_v3(options)) {
- tor_assert_nonfatal_unreached();
- return 3600;
- }
-
- time_t next = dirvote_act(options, now);
- if (BUG(next == TIME_MAX)) {
- /* This shouldn't be returned unless we called dirvote_act() without
- * being an authority. If it happens, maybe our configuration will
- * fix itself in an hour or so? */
- return 3600;
- }
- return safe_timer_diff(now, next);
-}
-
-/** Reschedule the directory-authority voting event. Run this whenever the
- * schedule has changed. */
-void
-reschedule_dirvote(const or_options_t *options)
-{
- if (periodic_events_initialized && authdir_mode_v3(options)) {
- periodic_event_reschedule(dirvote_event);
- }
-}
-
-/**
* Periodic callback: If our consensus is too old, recalculate whether
* we can actually use it.
*/
@@ -2254,17 +1942,6 @@ write_stats_file_callback(time_t now, const or_options_t *options)
return safe_timer_diff(now, next_time_to_write_stats_files);
}
-#define CHANNEL_CHECK_INTERVAL (60*60)
-static int
-check_canonical_channels_callback(time_t now, const or_options_t *options)
-{
- (void)now;
- if (public_server_mode(options))
- channel_check_for_duplicates();
-
- return CHANNEL_CHECK_INTERVAL;
-}
-
static int
reset_padding_counts_callback(time_t now, const or_options_t *options)
{
@@ -2339,19 +2016,6 @@ rend_cache_failure_clean_callback(time_t now, const or_options_t *options)
}
/**
- * Periodic callback: If we're a server and initializing dns failed, retry.
- */
-static int
-retry_dns_callback(time_t now, const or_options_t *options)
-{
- (void)now;
-#define RETRY_DNS_INTERVAL (10*60)
- if (server_mode(options) && has_dns_init_failed())
- dns_init();
- return RETRY_DNS_INTERVAL;
-}
-
-/**
* Periodic callback: prune routerlist of old information about Tor network.
*/
static int
@@ -2372,71 +2036,6 @@ prune_old_routers_callback(time_t now, const or_options_t *options)
return ROUTERLIST_PRUNING_INTERVAL;
}
-/** Periodic callback: consider rebuilding or and re-uploading our descriptor
- * (if we've passed our internal checks). */
-static int
-check_descriptor_callback(time_t now, const or_options_t *options)
-{
-/** How often do we check whether part of our router info has changed in a
- * way that would require an upload? That includes checking whether our IP
- * address has changed. */
-#define CHECK_DESCRIPTOR_INTERVAL (60)
-
- (void)options;
-
- /* 2b. Once per minute, regenerate and upload the descriptor if the old
- * one is inaccurate. */
- if (!net_is_disabled()) {
- check_descriptor_bandwidth_changed(now);
- check_descriptor_ipaddress_changed(now);
- mark_my_descriptor_dirty_if_too_old(now);
- consider_publishable_server(0);
- }
-
- return CHECK_DESCRIPTOR_INTERVAL;
-}
-
-/**
- * Periodic callback: check whether we're reachable (as a relay), and
- * whether our bandwidth has changed enough that we need to
- * publish a new descriptor.
- */
-static int
-check_for_reachability_bw_callback(time_t now, const or_options_t *options)
-{
- /* XXXX This whole thing was stuck in the middle of what is now
- * XXXX check_descriptor_callback. I'm not sure it's right. */
-
- static int dirport_reachability_count = 0;
- /* also, check religiously for reachability, if it's within the first
- * 20 minutes of our uptime. */
- if (server_mode(options) &&
- (have_completed_a_circuit() || !any_predicted_circuits(now)) &&
- !net_is_disabled()) {
- if (get_uptime() < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
- router_do_reachability_checks(1, dirport_reachability_count==0);
- if (++dirport_reachability_count > 5)
- dirport_reachability_count = 0;
- return 1;
- } else {
- /* If we haven't checked for 12 hours and our bandwidth estimate is
- * low, do another bandwidth test. This is especially important for
- * bridges, since they might go long periods without much use. */
- const routerinfo_t *me = router_get_my_routerinfo();
- static int first_time = 1;
- if (!first_time && me &&
- me->bandwidthcapacity < me->bandwidthrate &&
- me->bandwidthcapacity < 51200) {
- reset_bandwidth_test();
- }
- first_time = 0;
-#define BANDWIDTH_RECHECK_INTERVAL (12*60*60)
- return BANDWIDTH_RECHECK_INTERVAL;
- }
- }
- return CHECK_DESCRIPTOR_INTERVAL;
-}
-
/**
* Periodic event: once a minute, (or every second if TestingTorNetwork, or
* during client bootstrap), check whether we want to download any
@@ -2479,109 +2078,6 @@ retry_listeners_callback(time_t now, const or_options_t *options)
return PERIODIC_EVENT_NO_UPDATE;
}
-/**
- * Periodic callback: as a server, see if we have any old unused circuits
- * that should be expired */
-static int
-expire_old_ciruits_serverside_callback(time_t now, const or_options_t *options)
-{
- (void)options;
- /* every 11 seconds, so not usually the same second as other such events */
- circuit_expire_old_circuits_serverside(now);
- return 11;
-}
-
-/**
- * Callback: Send warnings if Tor doesn't find its ports reachable.
- */
-static int
-reachability_warnings_callback(time_t now, const or_options_t *options)
-{
- (void) now;
-
- if (get_uptime() < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
- return (int)(TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT - get_uptime());
- }
-
- if (server_mode(options) &&
- !net_is_disabled() &&
- have_completed_a_circuit()) {
- /* every 20 minutes, check and complain if necessary */
- const routerinfo_t *me = router_get_my_routerinfo();
- if (me && !check_whether_orport_reachable(options)) {
- char *address = tor_dup_ip(me->addr);
- log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that "
- "its ORPort is reachable. Relays do not publish descriptors "
- "until their ORPort and DirPort are reachable. Please check "
- "your firewalls, ports, address, /etc/hosts file, etc.",
- address, me->or_port);
- control_event_server_status(LOG_WARN,
- "REACHABILITY_FAILED ORADDRESS=%s:%d",
- address, me->or_port);
- tor_free(address);
- }
-
- if (me && !check_whether_dirport_reachable(options)) {
- char *address = tor_dup_ip(me->addr);
- log_warn(LD_CONFIG,
- "Your server (%s:%d) has not managed to confirm that its "
- "DirPort is reachable. Relays do not publish descriptors "
- "until their ORPort and DirPort are reachable. Please check "
- "your firewalls, ports, address, /etc/hosts file, etc.",
- address, me->dir_port);
- control_event_server_status(LOG_WARN,
- "REACHABILITY_FAILED DIRADDRESS=%s:%d",
- address, me->dir_port);
- tor_free(address);
- }
- }
-
- return TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT;
-}
-
-static int dns_honesty_first_time = 1;
-
-/**
- * Periodic event: if we're an exit, see if our DNS server is telling us
- * obvious lies.
- */
-static int
-check_dns_honesty_callback(time_t now, const or_options_t *options)
-{
- (void)now;
- /* 9. and if we're an exit node, check whether our DNS is telling stories
- * to us. */
- if (net_is_disabled() ||
- ! public_server_mode(options) ||
- router_my_exit_policy_is_reject_star())
- return PERIODIC_EVENT_NO_UPDATE;
-
- if (dns_honesty_first_time) {
- /* Don't launch right when we start */
- dns_honesty_first_time = 0;
- return crypto_rand_int_range(60, 180);
- }
-
- dns_launch_correctness_checks();
- return 12*3600 + crypto_rand_int(12*3600);
-}
-
-/**
- * Periodic callback: if we're the bridge authority, write a networkstatus
- * file to disk.
- */
-static int
-write_bridge_ns_callback(time_t now, const or_options_t *options)
-{
- /* 10. write bridge networkstatus file to disk */
- if (options->BridgeAuthoritativeDir) {
- networkstatus_dump_bridge_status_to_file(now);
-#define BRIDGE_STATUSFILE_INTERVAL (30*60)
- return BRIDGE_STATUSFILE_INTERVAL;
- }
- return PERIODIC_EVENT_NO_UPDATE;
-}
-
static int heartbeat_callback_first_time = 1;
/**
@@ -2797,8 +2293,7 @@ dns_servers_relaunch_checks(void)
{
if (server_mode(get_options())) {
dns_reset_correctness_checks();
- if (periodic_events_initialized) {
- tor_assert(check_dns_honesty_event);
+ if (check_dns_honesty_event) {
periodic_event_reschedule(check_dns_honesty_event);
}
}
@@ -2808,8 +2303,6 @@ dns_servers_relaunch_checks(void)
void
initialize_mainloop_events(void)
{
- initialize_periodic_events();
-
if (!schedule_active_linked_connections_event) {
schedule_active_linked_connections_event =
mainloop_event_postloop_new(schedule_active_linked_connections_cb, NULL);
@@ -2827,9 +2320,17 @@ do_main_loop(void)
/* initialize the periodic events first, so that code that depends on the
* events being present does not assert.
*/
- initialize_periodic_events();
+ tor_assert(periodic_events_initialized);
initialize_mainloop_events();
+ periodic_events_connect_all();
+
+ struct timeval one_second = { 1, 0 };
+ initialize_periodic_events_event = tor_evtimer_new(
+ tor_libevent_get_base(),
+ initialize_periodic_events_cb, NULL);
+ event_add(initialize_periodic_events_event, &one_second);
+
#ifdef HAVE_SYSTEMD_209
uint64_t watchdog_delay;
/* set up systemd watchdog notification. */
@@ -2874,7 +2375,7 @@ do_main_loop(void)
event_add(tor_shutdown_event_loop_for_restart_event, &restart_after);
}
}
-#endif
+#endif /* defined(ENABLE_RESTART_DEBUGGING) */
return run_main_loop_until_done();
}
@@ -3042,7 +2543,6 @@ tor_mainloop_free_all(void)
can_complete_circuits = 0;
quiet_level = 0;
should_init_bridge_stats = 1;
- dns_honesty_first_time = 1;
heartbeat_callback_first_time = 1;
current_second = 0;
memset(&current_second_last_changed, 0,
diff --git a/src/core/mainloop/mainloop.h b/src/core/mainloop/mainloop.h
index 6ed93fa900..caef736c15 100644
--- a/src/core/mainloop/mainloop.h
+++ b/src/core/mainloop/mainloop.h
@@ -59,10 +59,8 @@ void directory_info_has_arrived(time_t now, int from_cache, int suppress_logs);
void ip_address_changed(int at_interface);
void dns_servers_relaunch_checks(void);
void reset_all_main_loop_timers(void);
-void reschedule_descriptor_update_check(void);
void reschedule_directory_downloads(void);
void reschedule_or_state_save(void);
-void reschedule_dirvote(const or_options_t *options);
void mainloop_schedule_postloop_cleanup(void);
void rescan_periodic_events(const or_options_t *options);
MOCK_DECL(void, schedule_rescan_periodic_events,(void));
@@ -90,6 +88,7 @@ void mainloop_schedule_shutdown(int delay_sec);
void tor_init_connection_lists(void);
void initialize_mainloop_events(void);
+void initialize_periodic_events(void);
void tor_mainloop_free_all(void);
struct token_bucket_rw_t;
@@ -102,7 +101,6 @@ extern struct token_bucket_rw_t global_relayed_bucket;
#ifdef MAINLOOP_PRIVATE
STATIC int run_main_loop_until_done(void);
STATIC void close_closeable_connections(void);
-STATIC void initialize_periodic_events(void);
STATIC void teardown_periodic_events(void);
STATIC int get_my_roles(const or_options_t *);
STATIC int check_network_participation_callback(time_t now,
@@ -113,8 +111,8 @@ extern smartlist_t *connection_array;
/* We need the periodic_event_item_t definition. */
#include "core/mainloop/periodic.h"
-extern periodic_event_item_t periodic_events[];
-#endif
-#endif /* defined(MAIN_PRIVATE) */
+extern periodic_event_item_t mainloop_periodic_events[];
+#endif /* defined(TOR_UNIT_TESTS) */
+#endif /* defined(MAINLOOP_PRIVATE) */
-#endif
+#endif /* !defined(TOR_MAINLOOP_H) */
diff --git a/src/core/mainloop/mainloop_pubsub.c b/src/core/mainloop/mainloop_pubsub.c
new file mode 100644
index 0000000000..53275d8119
--- /dev/null
+++ b/src/core/mainloop/mainloop_pubsub.c
@@ -0,0 +1,170 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#include "core/or/or.h"
+#include "core/mainloop/mainloop.h"
+#include "core/mainloop/mainloop_pubsub.h"
+
+#include "lib/container/smartlist.h"
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/evloop/compat_libevent.h"
+#include "lib/pubsub/pubsub.h"
+#include "lib/pubsub/pubsub_build.h"
+
+/**
+ * Dispatcher to use for delivering messages.
+ **/
+static dispatch_t *the_dispatcher = NULL;
+static pubsub_items_t *the_pubsub_items = NULL;
+/**
+ * A list of mainloop_event_t, indexed by channel ID, to flush the messages
+ * on a channel.
+ **/
+static smartlist_t *alert_events = NULL;
+
+/**
+ * Mainloop event callback: flush all the messages in a channel.
+ *
+ * The channel is encoded as a pointer, and passed via arg.
+ **/
+static void
+flush_channel_event(mainloop_event_t *ev, void *arg)
+{
+ (void)ev;
+ if (!the_dispatcher)
+ return;
+
+ channel_id_t chan = (channel_id_t)(uintptr_t)(arg);
+ dispatch_flush(the_dispatcher, chan, INT_MAX);
+}
+
+/**
+ * Construct our global pubsub object from <b>builder</b>. Return 0 on
+ * success, -1 on failure. */
+int
+tor_mainloop_connect_pubsub(struct pubsub_builder_t *builder)
+{
+ int rv = -1;
+ tor_mainloop_disconnect_pubsub();
+
+ the_dispatcher = pubsub_builder_finalize(builder, &the_pubsub_items);
+ if (! the_dispatcher)
+ goto err;
+
+ rv = 0;
+ goto done;
+ err:
+ tor_mainloop_disconnect_pubsub();
+ done:
+ return rv;
+}
+
+/**
+ * Install libevent events for all of the pubsub channels.
+ *
+ * Invoke this after tor_mainloop_connect_pubsub, and after libevent has been
+ * initialized.
+ */
+void
+tor_mainloop_connect_pubsub_events(void)
+{
+ tor_assert(the_dispatcher);
+ tor_assert(! alert_events);
+
+ const size_t num_channels = get_num_channel_ids();
+ alert_events = smartlist_new();
+ for (size_t i = 0; i < num_channels; ++i) {
+ smartlist_add(alert_events,
+ mainloop_event_postloop_new(flush_channel_event,
+ (void*)(uintptr_t)(i)));
+ }
+}
+
+/**
+ * Dispatch alertfn callback: do nothing. Implements DELIV_NEVER.
+ **/
+static void
+alertfn_never(dispatch_t *d, channel_id_t chan, void *arg)
+{
+ (void)d;
+ (void)chan;
+ (void)arg;
+}
+
+/**
+ * Dispatch alertfn callback: activate a mainloop event. Implements
+ * DELIV_PROMPT.
+ **/
+static void
+alertfn_prompt(dispatch_t *d, channel_id_t chan, void *arg)
+{
+ (void)d;
+ (void)chan;
+ mainloop_event_t *event = arg;
+ mainloop_event_activate(event);
+}
+
+/**
+ * Dispatch alertfn callback: flush all messages right now. Implements
+ * DELIV_IMMEDIATE.
+ **/
+static void
+alertfn_immediate(dispatch_t *d, channel_id_t chan, void *arg)
+{
+ (void) arg;
+ dispatch_flush(d, chan, INT_MAX);
+}
+
+/**
+ * Set the strategy to be used for delivering messages on the named channel.
+ *
+ * This function needs to be called once globally for each channel, to
+ * set up how messages are delivered.
+ **/
+int
+tor_mainloop_set_delivery_strategy(const char *msg_channel_name,
+ deliv_strategy_t strategy)
+{
+ channel_id_t chan = get_channel_id(msg_channel_name);
+ if (BUG(chan == ERROR_ID) ||
+ BUG(chan >= smartlist_len(alert_events)))
+ return -1;
+
+ switch (strategy) {
+ case DELIV_NEVER:
+ dispatch_set_alert_fn(the_dispatcher, chan, alertfn_never, NULL);
+ break;
+ case DELIV_PROMPT:
+ dispatch_set_alert_fn(the_dispatcher, chan, alertfn_prompt,
+ smartlist_get(alert_events, chan));
+ break;
+ case DELIV_IMMEDIATE:
+ dispatch_set_alert_fn(the_dispatcher, chan, alertfn_immediate, NULL);
+ break;
+ }
+ return 0;
+}
+
+/**
+ * Remove all pubsub dispatchers and events from the mainloop.
+ **/
+void
+tor_mainloop_disconnect_pubsub(void)
+{
+ if (the_pubsub_items) {
+ pubsub_items_clear_bindings(the_pubsub_items);
+ pubsub_items_free(the_pubsub_items);
+ }
+ if (alert_events) {
+ SMARTLIST_FOREACH(alert_events, mainloop_event_t *, ev,
+ mainloop_event_free(ev));
+ smartlist_free(alert_events);
+ }
+ dispatch_free(the_dispatcher);
+}
diff --git a/src/core/mainloop/mainloop_pubsub.h b/src/core/mainloop/mainloop_pubsub.h
new file mode 100644
index 0000000000..365a3dd565
--- /dev/null
+++ b/src/core/mainloop/mainloop_pubsub.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_MAINLOOP_PUBSUB_H
+#define TOR_MAINLOOP_PUBSUB_H
+
+struct pubsub_builder_t;
+
+typedef enum {
+ DELIV_NEVER=0,
+ DELIV_PROMPT,
+ DELIV_IMMEDIATE,
+} deliv_strategy_t;
+
+int tor_mainloop_connect_pubsub(struct pubsub_builder_t *builder);
+void tor_mainloop_connect_pubsub_events(void);
+int tor_mainloop_set_delivery_strategy(const char *msg_channel_name,
+ deliv_strategy_t strategy);
+void tor_mainloop_disconnect_pubsub(void);
+
+#endif /* !defined(TOR_MAINLOOP_PUBSUB_H) */
diff --git a/src/core/mainloop/mainloop_sys.c b/src/core/mainloop/mainloop_sys.c
new file mode 100644
index 0000000000..fbd5a40327
--- /dev/null
+++ b/src/core/mainloop/mainloop_sys.c
@@ -0,0 +1,32 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "core/or/or.h"
+#include "core/mainloop/mainloop_sys.h"
+#include "core/mainloop/mainloop.h"
+
+#include "lib/subsys/subsys.h"
+
+static int
+subsys_mainloop_initialize(void)
+{
+ initialize_periodic_events();
+ return 0;
+}
+
+static void
+subsys_mainloop_shutdown(void)
+{
+ tor_mainloop_free_all();
+}
+
+const struct subsys_fns_t sys_mainloop = {
+ .name = "mainloop",
+ .supported = true,
+ .level = 5,
+ .initialize = subsys_mainloop_initialize,
+ .shutdown = subsys_mainloop_shutdown,
+};
diff --git a/src/core/mainloop/mainloop_sys.h b/src/core/mainloop/mainloop_sys.h
new file mode 100644
index 0000000000..fa74fe5d4b
--- /dev/null
+++ b/src/core/mainloop/mainloop_sys.h
@@ -0,0 +1,12 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef MAINLOOP_SYS_H
+#define MAINLOOP_SYS_H
+
+extern const struct subsys_fns_t sys_mainloop;
+
+#endif /* !defined(MAINLOOP_SYS_H) */
diff --git a/src/core/mainloop/netstatus.h b/src/core/mainloop/netstatus.h
index aba631e2fb..e8469ff558 100644
--- a/src/core/mainloop/netstatus.h
+++ b/src/core/mainloop/netstatus.h
@@ -21,4 +21,4 @@ void netstatus_flush_to_state(or_state_t *state, time_t now);
void netstatus_load_from_state(const or_state_t *state, time_t now);
void netstatus_note_clock_jumped(time_t seconds_diff);
-#endif
+#endif /* !defined(TOR_NETSTATUS_H) */
diff --git a/src/core/mainloop/periodic.c b/src/core/mainloop/periodic.c
index c0363b15ea..dbc4553a73 100644
--- a/src/core/mainloop/periodic.c
+++ b/src/core/mainloop/periodic.c
@@ -6,9 +6,22 @@
*
* \brief Generic backend for handling periodic events.
*
- * The events in this module are used by main.c to track items that need
+ * The events in this module are used 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.
+ * that they fire. See periodic_events[] in mainloop.c for examples.
+ *
+ * This module manages a global list of periodic_event_item_t objects,
+ * each corresponding to a single event. To register an event, pass it to
+ * periodic_events_register() when initializing your subsystem.
+ *
+ * Registering an event makes the periodic event subsystem know about it, but
+ * doesn't cause the event to get created immediately. Before the event can
+ * be started, periodic_event_connect_all() must be called by mainloop.c to
+ * connect all the events to Libevent.
+ *
+ * We expect that periodic_event_item_t objects will be statically allocated;
+ * we set them up and tear them down here, but we don't take ownership of
+ * them.
*/
#include "core/or/or.h"
@@ -24,6 +37,12 @@
*/
static const int MAX_INTERVAL = 10 * 365 * 86400;
+/**
+ * Global list of periodic events that have been registered with
+ * <b>periodic_event_register</a>.
+ **/
+static smartlist_t *the_periodic_events = NULL;
+
/** Set the event <b>event</b> to run in <b>next_interval</b> seconds from
* now. */
static void
@@ -87,15 +106,16 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data)
void
periodic_event_reschedule(periodic_event_item_t *event)
{
- /* Don't reschedule a disabled event. */
- if (periodic_event_is_enabled(event)) {
+ /* Don't reschedule a disabled or uninitialized event. */
+ if (event->ev && periodic_event_is_enabled(event)) {
periodic_event_set_interval(event, 1);
}
}
-/** Initializes the libevent backend for a periodic event. */
+/** Connects a periodic event to the Libevent backend. Does not launch the
+ * event immediately. */
void
-periodic_event_setup(periodic_event_item_t *event)
+periodic_event_connect(periodic_event_item_t *event)
{
if (event->ev) { /* Already setup? This is a bug */
log_err(LD_BUG, "Initial dispatch should only be done once.");
@@ -113,7 +133,7 @@ void
periodic_event_launch(periodic_event_item_t *event)
{
if (! event->ev) { /* Not setup? This is a bug */
- log_err(LD_BUG, "periodic_event_launch without periodic_event_setup");
+ log_err(LD_BUG, "periodic_event_launch without periodic_event_connect");
tor_assert(0);
}
/* Event already enabled? This is a bug */
@@ -127,9 +147,9 @@ periodic_event_launch(periodic_event_item_t *event)
periodic_event_dispatch(event->ev, event);
}
-/** Release all storage associated with <b>event</b> */
-void
-periodic_event_destroy(periodic_event_item_t *event)
+/** Disconnect and unregister the periodic event in <b>event</b> */
+static void
+periodic_event_disconnect(periodic_event_item_t *event)
{
if (!event)
return;
@@ -184,3 +204,161 @@ periodic_event_schedule_and_disable(periodic_event_item_t *event)
mainloop_event_activate(event->ev);
}
+
+/**
+ * Add <b>item</b> to the list of periodic events.
+ *
+ * Note that <b>item</b> should be statically allocated: we do not
+ * take ownership of it.
+ **/
+void
+periodic_events_register(periodic_event_item_t *item)
+{
+ if (!the_periodic_events)
+ the_periodic_events = smartlist_new();
+
+ if (BUG(smartlist_contains(the_periodic_events, item)))
+ return;
+
+ smartlist_add(the_periodic_events, item);
+}
+
+/**
+ * Make all registered periodic events connect to the libevent backend.
+ */
+void
+periodic_events_connect_all(void)
+{
+ if (! the_periodic_events)
+ return;
+
+ SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
+ if (item->ev)
+ continue;
+ periodic_event_connect(item);
+ } SMARTLIST_FOREACH_END(item);
+}
+
+/**
+ * Reset all the registered periodic events so we'll do all our actions again
+ * as if we just started up.
+ *
+ * Useful if our clock just moved back a long time from the future,
+ * so we don't wait until that future arrives again before acting.
+ */
+void
+periodic_events_reset_all(void)
+{
+ if (! the_periodic_events)
+ return;
+
+ SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
+ if (!item->ev)
+ continue;
+
+ periodic_event_reschedule(item);
+ } SMARTLIST_FOREACH_END(item);
+}
+
+/**
+ * Return the registered periodic event whose name is <b>name</b>.
+ * Return NULL if no such event is found.
+ */
+periodic_event_item_t *
+periodic_events_find(const char *name)
+{
+ if (! the_periodic_events)
+ return NULL;
+
+ SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
+ if (strcmp(name, item->name) == 0)
+ return item;
+ } SMARTLIST_FOREACH_END(item);
+ return NULL;
+}
+
+/**
+ * Start or stop registered periodic events, depending on our current set of
+ * roles.
+ *
+ * Invoked when our list of roles, or the net_disabled flag has changed.
+ **/
+void
+periodic_events_rescan_by_roles(int roles, bool net_disabled)
+{
+ if (! the_periodic_events)
+ return;
+
+ SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
+ if (!item->ev)
+ continue;
+
+ int enable = !!(item->roles & roles);
+
+ /* Handle the event flags. */
+ if (net_disabled &&
+ (item->flags & PERIODIC_EVENT_FLAG_NEED_NET)) {
+ enable = 0;
+ }
+
+ /* Enable the event if needed. It is safe to enable an event that was
+ * already enabled. Same goes for disabling it. */
+ if (enable) {
+ log_debug(LD_GENERAL, "Launching periodic event %s", item->name);
+ periodic_event_enable(item);
+ } else {
+ log_debug(LD_GENERAL, "Disabling periodic event %s", item->name);
+ if (item->flags & PERIODIC_EVENT_FLAG_RUN_ON_DISABLE) {
+ periodic_event_schedule_and_disable(item);
+ } else {
+ periodic_event_disable(item);
+ }
+ }
+ } SMARTLIST_FOREACH_END(item);
+}
+
+/**
+ * Invoked at shutdown: disconnect and unregister all periodic events.
+ *
+ * Does not free the periodic_event_item_t object themselves, because we do
+ * not own them.
+ */
+void
+periodic_events_disconnect_all(void)
+{
+ if (! the_periodic_events)
+ return;
+
+ SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
+ periodic_event_disconnect(item);
+ } SMARTLIST_FOREACH_END(item);
+
+ smartlist_free(the_periodic_events);
+}
+
+#define LONGEST_TIMER_PERIOD (30 * 86400)
+/** Helper: Return the number of seconds between <b>now</b> and <b>next</b>,
+ * clipped to the range [1 second, LONGEST_TIMER_PERIOD].
+ *
+ * We use this to answer the question, "how many seconds is it from now until
+ * next" in periodic timer callbacks. Don't use it for other purposes
+ **/
+int
+safe_timer_diff(time_t now, time_t next)
+{
+ if (next > now) {
+ /* There were no computers at signed TIME_MIN (1902 on 32-bit systems),
+ * and nothing that could run Tor. It's a bug if 'next' is around then.
+ * On 64-bit systems with signed TIME_MIN, TIME_MIN is before the Big
+ * Bang. We cannot extrapolate past a singularity, but there was probably
+ * nothing that could run Tor then, either.
+ **/
+ tor_assert(next > TIME_MIN + LONGEST_TIMER_PERIOD);
+
+ if (next - LONGEST_TIMER_PERIOD > now)
+ return LONGEST_TIMER_PERIOD;
+ return (int)(next - now);
+ } else {
+ return 1;
+ }
+}
diff --git a/src/core/mainloop/periodic.h b/src/core/mainloop/periodic.h
index 344fc9ad25..a9aa461969 100644
--- a/src/core/mainloop/periodic.h
+++ b/src/core/mainloop/periodic.h
@@ -83,11 +83,20 @@ periodic_event_is_enabled(const periodic_event_item_t *item)
}
void periodic_event_launch(periodic_event_item_t *event);
-void periodic_event_setup(periodic_event_item_t *event);
-void periodic_event_destroy(periodic_event_item_t *event);
+void periodic_event_connect(periodic_event_item_t *event);
+//void periodic_event_disconnect(periodic_event_item_t *event);
void periodic_event_reschedule(periodic_event_item_t *event);
void periodic_event_enable(periodic_event_item_t *event);
void periodic_event_disable(periodic_event_item_t *event);
void periodic_event_schedule_and_disable(periodic_event_item_t *event);
+void periodic_events_register(periodic_event_item_t *item);
+void periodic_events_connect_all(void);
+void periodic_events_reset_all(void);
+periodic_event_item_t *periodic_events_find(const char *name);
+void periodic_events_rescan_by_roles(int roles, bool net_disabled);
+void periodic_events_disconnect_all(void);
+
+int safe_timer_diff(time_t now, time_t next);
+
#endif /* !defined(TOR_PERIODIC_H) */
diff --git a/src/core/or/addr_policy_st.h b/src/core/or/addr_policy_st.h
index a75f1a731d..11442d29b4 100644
--- a/src/core/or/addr_policy_st.h
+++ b/src/core/or/addr_policy_st.h
@@ -43,4 +43,4 @@ struct addr_policy_t {
uint16_t prt_max; /**< Highest port number to accept/reject. */
};
-#endif
+#endif /* !defined(TOR_ADDR_POLICY_ST_H) */
diff --git a/src/core/or/address_set.h b/src/core/or/address_set.h
index 7a9e71628e..95608a9a53 100644
--- a/src/core/or/address_set.h
+++ b/src/core/or/address_set.h
@@ -28,4 +28,4 @@ void address_set_add_ipv4h(address_set_t *set, uint32_t addr);
int address_set_probably_contains(const address_set_t *set,
const struct tor_addr_t *addr);
-#endif
+#endif /* !defined(TOR_ADDRESS_SET_H) */
diff --git a/src/core/or/cell_queue_st.h b/src/core/or/cell_queue_st.h
index 130b95a011..7ba339b965 100644
--- a/src/core/or/cell_queue_st.h
+++ b/src/core/or/cell_queue_st.h
@@ -26,4 +26,4 @@ struct cell_queue_t {
int n; /**< The number of cells in the queue. */
};
-#endif
+#endif /* !defined(PACKED_CELL_ST_H) */
diff --git a/src/core/or/cell_st.h b/src/core/or/cell_st.h
index 7ab7eceb50..c4eec4f4b5 100644
--- a/src/core/or/cell_st.h
+++ b/src/core/or/cell_st.h
@@ -16,5 +16,5 @@ struct cell_t {
uint8_t payload[CELL_PAYLOAD_SIZE]; /**< Cell body. */
};
-#endif
+#endif /* !defined(CELL_ST_H) */
diff --git a/src/core/or/channel.c b/src/core/or/channel.c
index fd7bf62789..0e190809ba 100644
--- a/src/core/or/channel.c
+++ b/src/core/or/channel.c
@@ -1418,6 +1418,7 @@ write_packed_cell(channel_t *chan, packed_cell_t *cell)
{
int ret = -1;
size_t cell_bytes;
+ uint8_t command = packed_cell_get_command(cell, chan->wide_circ_ids);
tor_assert(chan);
tor_assert(cell);
@@ -1452,6 +1453,16 @@ write_packed_cell(channel_t *chan, packed_cell_t *cell)
/* Successfully sent the cell. */
ret = 0;
+ /* Update padding statistics for the packed codepath.. */
+ rep_hist_padding_count_write(PADDING_TYPE_TOTAL);
+ if (command == CELL_PADDING)
+ rep_hist_padding_count_write(PADDING_TYPE_CELL);
+ if (chan->padding_enabled) {
+ rep_hist_padding_count_write(PADDING_TYPE_ENABLED_TOTAL);
+ if (command == CELL_PADDING)
+ rep_hist_padding_count_write(PADDING_TYPE_ENABLED_CELL);
+ }
+
done:
return ret;
}
diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c
index 5a00a9e00f..2a6edc951c 100644
--- a/src/core/or/channeltls.c
+++ b/src/core/or/channeltls.c
@@ -1094,13 +1094,13 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
entry_guards_note_internet_connectivity(get_guard_selection_info());
rep_hist_padding_count_read(PADDING_TYPE_TOTAL);
- if (TLS_CHAN_TO_BASE(chan)->currently_padding)
+ if (TLS_CHAN_TO_BASE(chan)->padding_enabled)
rep_hist_padding_count_read(PADDING_TYPE_ENABLED_TOTAL);
switch (cell->command) {
case CELL_PADDING:
rep_hist_padding_count_read(PADDING_TYPE_CELL);
- if (TLS_CHAN_TO_BASE(chan)->currently_padding)
+ if (TLS_CHAN_TO_BASE(chan)->padding_enabled)
rep_hist_padding_count_read(PADDING_TYPE_ENABLED_CELL);
++stats_n_padding_cells_processed;
/* do nothing */
@@ -1734,7 +1734,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
tor_assert(tor_digest_is_zero(
(const char*)(chan->conn->handshake_state->
authenticated_rsa_peer_id)));
- tor_assert(tor_mem_is_zero(
+ tor_assert(fast_mem_is_zero(
(const char*)(chan->conn->handshake_state->
authenticated_ed25519_peer_id.pubkey), 32));
/* If the client never authenticated, it's a tor client or bridge
diff --git a/src/core/or/circuit_st.h b/src/core/or/circuit_st.h
index af343f082e..eae3c908d5 100644
--- a/src/core/or/circuit_st.h
+++ b/src/core/or/circuit_st.h
@@ -13,7 +13,7 @@
struct hs_token_t;
struct circpad_machine_spec_t;
-struct circpad_machine_state_t;
+struct circpad_machine_runtime_t;
/** Number of padding state machines on a circuit. */
#define CIRCPAD_MAX_MACHINES (2)
@@ -66,12 +66,6 @@ struct circuit_t {
*/
circid_t n_circ_id;
- /**
- * Circuit mux associated with n_chan to which this circuit is attached;
- * NULL if we have no n_chan.
- */
- circuitmux_t *n_mux;
-
/** Queue of cells waiting to be transmitted on n_chan */
cell_queue_t n_chan_cells;
@@ -98,6 +92,10 @@ struct circuit_t {
/** True iff this circuit has received a DESTROY cell in either direction */
unsigned int received_destroy : 1;
+ /** True iff we have sent a sufficiently random data cell since last
+ * we reset send_randomness_after_n_cells. */
+ unsigned int have_sent_sufficiently_random_cell : 1;
+
uint8_t state; /**< Current status of this circuit. */
uint8_t purpose; /**< Why are we creating this circuit? */
@@ -110,6 +108,32 @@ struct circuit_t {
* circuit-level sendme cells to indicate that we're willing to accept
* more. */
int deliver_window;
+ /**
+ * How many cells do we have until we need to send one that contains
+ * sufficient randomness? Used to ensure that authenticated SENDME cells
+ * will reflect some unpredictable information.
+ **/
+ uint16_t send_randomness_after_n_cells;
+
+ /** FIFO containing the digest of the cells that are just before a SENDME is
+ * sent by the client. It is done at the last cell before our package_window
+ * goes down to 0 which is when we expect a SENDME.
+ *
+ * Our current circuit package window is capped to 1000
+ * (CIRCWINDOW_START_MAX) which is also the start value. The increment is
+ * set to 100 (CIRCWINDOW_INCREMENT) which means we don't allow more than
+ * 1000/100 = 10 outstanding SENDME cells worth of data. Meaning that this
+ * list can not contain more than 10 digests of DIGEST_LEN bytes (20).
+ *
+ * At position i in the list, the digest corresponds to the
+ * (CIRCWINDOW_INCREMENT * i)-nth cell received since we expect a SENDME to
+ * be received containing that cell digest.
+ *
+ * For example, position 2 (starting at 0) means that we've received 300
+ * cells so the 300th cell digest is kept at index 2.
+ *
+ * At maximum, this list contains 200 bytes plus the smartlist overhead. */
+ smartlist_t *sendme_last_digests;
/** Temporary field used during circuits_handle_oom. */
uint32_t age_tmp;
@@ -193,8 +217,8 @@ struct circuit_t {
* and we can have up to CIRCPAD_MAX_MACHINES such machines. */
const struct circpad_machine_spec_t *padding_machine[CIRCPAD_MAX_MACHINES];
- /** Adaptive Padding machine info for above machines. This is the
- * per-circuit mutable information, such as the current state and
+ /** Adaptive Padding machine runtime info for above machines. This is
+ * the per-circuit mutable information, such as the current state and
* histogram token counts. Some of it is optional (aka NULL).
* If a machine is being shut down, these indexes can be NULL
* without the corresponding padding_machine being NULL, while we
@@ -202,7 +226,7 @@ struct circuit_t {
*
* Each element of this array corresponds to a different padding machine,
* and we can have up to CIRCPAD_MAX_MACHINES such machines. */
- struct circpad_machine_state_t *padding_info[CIRCPAD_MAX_MACHINES];
+ struct circpad_machine_runtime_t *padding_info[CIRCPAD_MAX_MACHINES];
};
-#endif
+#endif /* !defined(CIRCUIT_ST_H) */
diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c
index 3ec1e01f11..3a4e729429 100644
--- a/src/core/or/circuitbuild.c
+++ b/src/core/or/circuitbuild.c
@@ -51,11 +51,12 @@
#include "core/or/ocirc_event.h"
#include "core/or/policies.h"
#include "core/or/relay.h"
+#include "core/or/crypt_path.h"
#include "feature/client/bridges.h"
#include "feature/client/circpathbias.h"
#include "feature/client/entrynodes.h"
#include "feature/client/transports.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dircommon/directory.h"
#include "feature/nodelist/describe.h"
#include "feature/nodelist/microdesc.h"
@@ -90,8 +91,6 @@ static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
static int circuit_deliver_create_cell(circuit_t *circ,
const create_cell_t *create_cell,
int relayed);
-static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
-STATIC int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
static int circuit_send_first_onion_skin(origin_circuit_t *circ);
static int circuit_build_no_more_hops(origin_circuit_t *circ);
static int circuit_send_intermediate_onion_skin(origin_circuit_t *circ,
@@ -547,7 +546,7 @@ circuit_handle_first_hop(origin_circuit_t *circ)
int should_launch = 0;
const or_options_t *options = get_options();
- firsthop = onion_next_hop_in_cpath(circ->cpath);
+ firsthop = cpath_get_next_non_open_hop(circ->cpath);
tor_assert(firsthop);
tor_assert(firsthop->extend_info);
@@ -948,7 +947,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
tor_assert(circ->cpath->state == CPATH_STATE_OPEN);
tor_assert(circ->base_.state == CIRCUIT_STATE_BUILDING);
- crypt_path_t *hop = onion_next_hop_in_cpath(circ->cpath);
+ crypt_path_t *hop = cpath_get_next_non_open_hop(circ->cpath);
circuit_build_times_handle_completed_hop(circ);
circpad_machine_event_circ_added_hop(circ);
@@ -1360,34 +1359,6 @@ circuit_extend(cell_t *cell, circuit_t *circ)
return 0;
}
-/** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in key_data.
- *
- * If <b>is_hs_v3</b> is set, this cpath will be used for next gen hidden
- * service circuits and <b>key_data</b> must be at least
- * HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN bytes in length.
- *
- * If <b>is_hs_v3</b> is not set, key_data must contain CPATH_KEY_MATERIAL_LEN
- * bytes, which are used as follows:
- * - 20 to initialize f_digest
- * - 20 to initialize b_digest
- * - 16 to key f_crypto
- * - 16 to key b_crypto
- *
- * (If 'reverse' is true, then f_XX and b_XX are swapped.)
- *
- * Return 0 if init was successful, else -1 if it failed.
- */
-int
-circuit_init_cpath_crypto(crypt_path_t *cpath,
- const char *key_data, size_t key_data_len,
- int reverse, int is_hs_v3)
-{
-
- tor_assert(cpath);
- return relay_crypto_init(&cpath->crypto, key_data, key_data_len, reverse,
- is_hs_v3);
-}
-
/** A "created" cell <b>reply</b> came back to us on circuit <b>circ</b>.
* (The body of <b>reply</b> varies depending on what sort of handshake
* this is.)
@@ -1413,7 +1384,7 @@ circuit_finish_handshake(origin_circuit_t *circ,
if (circ->cpath->state == CPATH_STATE_AWAITING_KEYS) {
hop = circ->cpath;
} else {
- hop = onion_next_hop_in_cpath(circ->cpath);
+ hop = cpath_get_next_non_open_hop(circ->cpath);
if (!hop) { /* got an extended when we're all done? */
log_warn(LD_PROTOCOL,"got extended when circ already built? Closing.");
return - END_CIRC_REASON_TORPROTOCOL;
@@ -1437,7 +1408,7 @@ circuit_finish_handshake(origin_circuit_t *circ,
onion_handshake_state_release(&hop->handshake_state);
- if (circuit_init_cpath_crypto(hop, keys, sizeof(keys), 0, 0)<0) {
+ if (cpath_init_circuit_crypto(hop, keys, sizeof(keys), 0, 0)<0) {
return -END_CIRC_REASON_TORPROTOCOL;
}
@@ -1489,7 +1460,7 @@ circuit_truncated(origin_circuit_t *circ, int reason)
}
layer->next = victim->next;
- circuit_free_cpath_node(victim);
+ cpath_free(victim);
}
log_info(LD_CIRC, "finished");
@@ -1683,7 +1654,8 @@ route_len_for_purpose(uint8_t purpose, extend_info_t *exit_ei)
* to handle the desired path length, return -1.
*/
STATIC int
-new_route_len(uint8_t purpose, extend_info_t *exit_ei, smartlist_t *nodes)
+new_route_len(uint8_t purpose, extend_info_t *exit_ei,
+ const smartlist_t *nodes)
{
int routelen;
@@ -2307,7 +2279,7 @@ circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
state->chosen_exit = extend_info_dup(exit_ei);
++circ->build_state->desired_path_len;
- onion_append_hop(&circ->cpath, exit_ei);
+ cpath_append_hop(&circ->cpath, exit_ei);
return 0;
}
@@ -2345,7 +2317,7 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
* particular router. See bug #25885.)
*/
MOCK_IMPL(STATIC int,
-count_acceptable_nodes, (smartlist_t *nodes, int direct))
+count_acceptable_nodes, (const smartlist_t *nodes, int direct))
{
int num=0;
@@ -2372,47 +2344,6 @@ count_acceptable_nodes, (smartlist_t *nodes, int direct))
return num;
}
-/** Add <b>new_hop</b> to the end of the doubly-linked-list <b>head_ptr</b>.
- * This function is used to extend cpath by another hop.
- */
-void
-onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop)
-{
- if (*head_ptr) {
- new_hop->next = (*head_ptr);
- new_hop->prev = (*head_ptr)->prev;
- (*head_ptr)->prev->next = new_hop;
- (*head_ptr)->prev = new_hop;
- } else {
- *head_ptr = new_hop;
- new_hop->prev = new_hop->next = new_hop;
- }
-}
-
-#ifdef TOR_UNIT_TESTS
-
-/** Unittest helper function: Count number of hops in cpath linked list. */
-unsigned int
-cpath_get_n_hops(crypt_path_t **head_ptr)
-{
- unsigned int n_hops = 0;
- crypt_path_t *tmp;
-
- if (!*head_ptr) {
- return 0;
- }
-
- tmp = *head_ptr;
- do {
- n_hops++;
- tmp = tmp->next;
- } while (tmp != *head_ptr);
-
- return n_hops;
-}
-
-#endif /* defined(TOR_UNIT_TESTS) */
-
/**
* Build the exclude list for vanguard circuits.
*
@@ -2687,20 +2618,6 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state,
return choice;
}
-/** Return the first non-open hop in cpath, or return NULL if all
- * hops are open. */
-static crypt_path_t *
-onion_next_hop_in_cpath(crypt_path_t *cpath)
-{
- crypt_path_t *hop = cpath;
- do {
- if (hop->state != CPATH_STATE_OPEN)
- return hop;
- hop = hop->next;
- } while (hop != cpath);
- return NULL;
-}
-
/** Choose a suitable next hop for the circuit <b>circ</b>.
* Append the hop info to circ->cpath.
*
@@ -2757,33 +2674,11 @@ onion_extend_cpath(origin_circuit_t *circ)
extend_info_describe(info),
cur_len+1, build_state_get_exit_nickname(state));
- onion_append_hop(&circ->cpath, info);
+ cpath_append_hop(&circ->cpath, info);
extend_info_free(info);
return 0;
}
-/** Create a new hop, annotate it with information about its
- * corresponding router <b>choice</b>, and append it to the
- * end of the cpath <b>head_ptr</b>. */
-STATIC int
-onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice)
-{
- crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t));
-
- /* link hop into the cpath, at the end. */
- onion_append_to_cpath(head_ptr, hop);
-
- hop->magic = CRYPT_PATH_MAGIC;
- hop->state = CPATH_STATE_CLOSED;
-
- hop->extend_info = extend_info_dup(choice);
-
- hop->package_window = circuit_initial_package_window();
- hop->deliver_window = CIRCWINDOW_START;
-
- return 0;
-}
-
/** Allocate a new extend_info object based on the various arguments. */
extend_info_t *
extend_info_new(const char *nickname,
@@ -2988,7 +2883,7 @@ 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(
+ return !fast_mem_is_zero(
(const char*)ei->curve25519_onion_key.public_key,
CURVE25519_PUBKEY_LEN);
}
diff --git a/src/core/or/circuitbuild.h b/src/core/or/circuitbuild.h
index b19bc41235..ad7d032cd4 100644
--- a/src/core/or/circuitbuild.h
+++ b/src/core/or/circuitbuild.h
@@ -34,9 +34,6 @@ int circuit_timeout_want_to_count_circ(const origin_circuit_t *circ);
int circuit_send_next_onion_skin(origin_circuit_t *circ);
void circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle);
int circuit_extend(cell_t *cell, circuit_t *circ);
-int circuit_init_cpath_crypto(crypt_path_t *cpath,
- const char *key_data, size_t key_data_len,
- int reverse, int is_hs_v3);
struct created_cell_t;
int circuit_finish_handshake(origin_circuit_t *circ,
const struct created_cell_t *created_cell);
@@ -51,7 +48,6 @@ MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now,
int circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info);
int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info);
-void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
extend_info_t *extend_info_new(const char *nickname,
const char *rsa_id_digest,
const struct ed25519_public_key_t *ed_id,
@@ -83,8 +79,8 @@ void circuit_upgrade_circuits_from_guard_wait(void);
#ifdef CIRCUITBUILD_PRIVATE
STATIC circid_t get_unique_circ_id_by_chan(channel_t *chan);
STATIC int new_route_len(uint8_t purpose, extend_info_t *exit_ei,
- smartlist_t *nodes);
-MOCK_DECL(STATIC int, count_acceptable_nodes, (smartlist_t *nodes,
+ const smartlist_t *nodes);
+MOCK_DECL(STATIC int, count_acceptable_nodes, (const smartlist_t *nodes,
int direct));
STATIC int onion_extend_cpath(origin_circuit_t *circ);
@@ -93,11 +89,6 @@ STATIC int
onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
int is_hs_v3_rp_circuit);
-#if defined(TOR_UNIT_TESTS)
-unsigned int cpath_get_n_hops(crypt_path_t **head_ptr);
-
-#endif /* defined(TOR_UNIT_TESTS) */
-
#endif /* defined(CIRCUITBUILD_PRIVATE) */
#endif /* !defined(TOR_CIRCUITBUILD_H) */
diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c
index 6b5f30e418..ebbe7f0824 100644
--- a/src/core/or/circuitlist.c
+++ b/src/core/or/circuitlist.c
@@ -63,11 +63,12 @@
#include "core/or/circuituse.h"
#include "core/or/circuitstats.h"
#include "core/or/circuitpadding.h"
+#include "core/or/crypt_path.h"
#include "core/mainloop/connection.h"
#include "app/config/config.h"
#include "core/or/connection_edge.h"
#include "core/or/connection_or.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/crypt_ops/crypto_dh.h"
@@ -132,7 +133,6 @@ static smartlist_t *circuits_pending_other_guards = NULL;
* circuit_mark_for_close and which are waiting for circuit_about_to_free. */
static smartlist_t *circuits_pending_close = NULL;
-static void circuit_free_cpath_node(crypt_path_t *victim);
static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
static void circuit_about_to_free_atexit(circuit_t *circ);
static void circuit_about_to_free(circuit_t *circ);
@@ -823,6 +823,8 @@ circuit_purpose_to_controller_string(uint8_t purpose)
return "PATH_BIAS_TESTING";
case CIRCUIT_PURPOSE_HS_VANGUARDS:
return "HS_VANGUARDS";
+ case CIRCUIT_PURPOSE_C_CIRCUIT_PADDING:
+ return "CIRCUIT_PADDING";
default:
tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose);
@@ -852,6 +854,7 @@ circuit_purpose_to_controller_hs_state_string(uint8_t purpose)
case CIRCUIT_PURPOSE_CONTROLLER:
case CIRCUIT_PURPOSE_PATH_BIAS_TESTING:
case CIRCUIT_PURPOSE_HS_VANGUARDS:
+ case CIRCUIT_PURPOSE_C_CIRCUIT_PADDING:
return NULL;
case CIRCUIT_PURPOSE_INTRO_POINT:
@@ -952,6 +955,9 @@ circuit_purpose_to_string(uint8_t purpose)
case CIRCUIT_PURPOSE_HS_VANGUARDS:
return "Hidden service: Pre-built vanguard circuit";
+ case CIRCUIT_PURPOSE_C_CIRCUIT_PADDING:
+ return "Circuit kept open for padding";
+
default:
tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose);
return buf;
@@ -987,6 +993,7 @@ init_circuit_base(circuit_t *circ)
circ->package_window = circuit_initial_package_window();
circ->deliver_window = CIRCWINDOW_START;
+ circuit_reset_sendme_randomness(circ);
cell_queue_init(&circ->n_chan_cells);
smartlist_add(circuit_get_global_list(), circ);
@@ -1148,7 +1155,7 @@ circuit_free_(circuit_t *circ)
if (ocirc->build_state) {
extend_info_free(ocirc->build_state->chosen_exit);
- circuit_free_cpath_node(ocirc->build_state->pending_final_cpath);
+ cpath_free(ocirc->build_state->pending_final_cpath);
cpath_ref_decref(ocirc->build_state->service_pending_final_cpath_ref);
}
tor_free(ocirc->build_state);
@@ -1227,6 +1234,12 @@ circuit_free_(circuit_t *circ)
* "active" checks will be violated. */
cell_queue_clear(&circ->n_chan_cells);
+ /* Cleanup possible SENDME state. */
+ if (circ->sendme_last_digests) {
+ SMARTLIST_FOREACH(circ->sendme_last_digests, uint8_t *, d, tor_free(d));
+ smartlist_free(circ->sendme_last_digests);
+ }
+
log_info(LD_CIRC, "Circuit %u (id: %" PRIu32 ") has been freed.",
n_circ_id,
CIRCUIT_IS_ORIGIN(circ) ?
@@ -1266,10 +1279,10 @@ circuit_clear_cpath(origin_circuit_t *circ)
while (cpath->next && cpath->next != head) {
victim = cpath;
cpath = victim->next;
- circuit_free_cpath_node(victim);
+ cpath_free(victim);
}
- circuit_free_cpath_node(cpath);
+ cpath_free(cpath);
circ->cpath = NULL;
}
@@ -1326,29 +1339,13 @@ circuit_free_all(void)
HT_CLEAR(chan_circid_map, &chan_circid_map);
}
-/** Deallocate space associated with the cpath node <b>victim</b>. */
-static void
-circuit_free_cpath_node(crypt_path_t *victim)
-{
- if (!victim)
- return;
-
- relay_crypto_clear(&victim->crypto);
- onion_handshake_state_release(&victim->handshake_state);
- crypto_dh_free(victim->rend_dh_handshake_state);
- extend_info_free(victim->extend_info);
-
- memwipe(victim, 0xBB, sizeof(crypt_path_t)); /* poison memory */
- tor_free(victim);
-}
-
/** Release a crypt_path_reference_t*, which may be NULL. */
static void
cpath_ref_decref(crypt_path_reference_t *cpath_ref)
{
if (cpath_ref != NULL) {
if (--(cpath_ref->refcount) == 0) {
- circuit_free_cpath_node(cpath_ref->cpath);
+ cpath_free(cpath_ref->cpath);
tor_free(cpath_ref);
}
}
@@ -2199,6 +2196,11 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
tor_assert(line);
tor_assert(file);
+ /* Check whether the circuitpadding subsystem wants to block this close */
+ if (circpad_marked_circuit_for_padding(circ, reason)) {
+ return;
+ }
+
if (circ->marked_for_close) {
log_warn(LD_BUG,
"Duplicate call to circuit_mark_for_close at %s:%d"
@@ -2433,13 +2435,9 @@ marked_circuit_free_cells(circuit_t *circ)
return;
}
cell_queue_clear(&circ->n_chan_cells);
- if (circ->n_mux)
- circuitmux_clear_num_cells(circ->n_mux, circ);
if (! CIRCUIT_IS_ORIGIN(circ)) {
or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
cell_queue_clear(&orcirc->p_chan_cells);
- if (orcirc->p_mux)
- circuitmux_clear_num_cells(orcirc->p_mux, circ);
}
}
@@ -2783,59 +2781,6 @@ circuits_handle_oom(size_t current_allocation)
n_dirconns_killed);
}
-/** Verify that cpath layer <b>cp</b> has all of its invariants
- * correct. Trigger an assert if anything is invalid.
- */
-void
-assert_cpath_layer_ok(const crypt_path_t *cp)
-{
-// tor_assert(cp->addr); /* these are zero for rendezvous extra-hops */
-// tor_assert(cp->port);
- tor_assert(cp);
- tor_assert(cp->magic == CRYPT_PATH_MAGIC);
- switch (cp->state)
- {
- case CPATH_STATE_OPEN:
- relay_crypto_assert_ok(&cp->crypto);
- /* fall through */
- case CPATH_STATE_CLOSED:
- /*XXXX Assert that there's no handshake_state either. */
- tor_assert(!cp->rend_dh_handshake_state);
- break;
- case CPATH_STATE_AWAITING_KEYS:
- /* tor_assert(cp->dh_handshake_state); */
- break;
- default:
- log_fn(LOG_ERR, LD_BUG, "Unexpected state %d", cp->state);
- tor_assert(0);
- }
- tor_assert(cp->package_window >= 0);
- tor_assert(cp->deliver_window >= 0);
-}
-
-/** Verify that cpath <b>cp</b> has all of its invariants
- * correct. Trigger an assert if anything is invalid.
- */
-static void
-assert_cpath_ok(const crypt_path_t *cp)
-{
- const crypt_path_t *start = cp;
-
- do {
- assert_cpath_layer_ok(cp);
- /* layers must be in sequence of: "open* awaiting? closed*" */
- if (cp != start) {
- if (cp->state == CPATH_STATE_AWAITING_KEYS) {
- tor_assert(cp->prev->state == CPATH_STATE_OPEN);
- } else if (cp->state == CPATH_STATE_OPEN) {
- tor_assert(cp->prev->state == CPATH_STATE_OPEN);
- }
- }
- cp = cp->next;
- tor_assert(cp);
- } while (cp != start);
-}
-
/** Verify that circuit <b>c</b> has all of its invariants
* correct. Trigger an assert if anything is invalid.
*/
@@ -2897,7 +2842,7 @@ assert_circuit_ok,(const circuit_t *c))
!smartlist_contains(circuits_pending_chans, c));
}
if (origin_circ && origin_circ->cpath) {
- assert_cpath_ok(origin_circ->cpath);
+ cpath_assert_ok(origin_circ->cpath);
}
if (c->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED) {
tor_assert(or_circ);
diff --git a/src/core/or/circuitlist.h b/src/core/or/circuitlist.h
index f34f4ed6b7..80c1f7ac4e 100644
--- a/src/core/or/circuitlist.h
+++ b/src/core/or/circuitlist.h
@@ -92,31 +92,33 @@
#define CIRCUIT_PURPOSE_C_HS_MAX_ 13
/** This circuit is used for build time measurement only */
#define CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT 14
-#define CIRCUIT_PURPOSE_C_MAX_ 14
+/** This circuit is being held open by circuit padding */
+#define CIRCUIT_PURPOSE_C_CIRCUIT_PADDING 15
+#define CIRCUIT_PURPOSE_C_MAX_ 15
-#define CIRCUIT_PURPOSE_S_HS_MIN_ 15
+#define CIRCUIT_PURPOSE_S_HS_MIN_ 16
/** Hidden-service-side circuit purpose: at the service, waiting for
* introductions. */
-#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 15
+#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 16
/** Hidden-service-side circuit purpose: at the service, successfully
* established intro. */
-#define CIRCUIT_PURPOSE_S_INTRO 16
+#define CIRCUIT_PURPOSE_S_INTRO 17
/** Hidden-service-side circuit purpose: at the service, connecting to rend
* point. */
-#define CIRCUIT_PURPOSE_S_CONNECT_REND 17
+#define CIRCUIT_PURPOSE_S_CONNECT_REND 18
/** Hidden-service-side circuit purpose: at the service, rendezvous
* established. */
-#define CIRCUIT_PURPOSE_S_REND_JOINED 18
+#define CIRCUIT_PURPOSE_S_REND_JOINED 19
/** This circuit is used for uploading hsdirs */
-#define CIRCUIT_PURPOSE_S_HSDIR_POST 19
-#define CIRCUIT_PURPOSE_S_HS_MAX_ 19
+#define CIRCUIT_PURPOSE_S_HSDIR_POST 20
+#define CIRCUIT_PURPOSE_S_HS_MAX_ 20
/** A testing circuit; not meant to be used for actual traffic. */
-#define CIRCUIT_PURPOSE_TESTING 20
+#define CIRCUIT_PURPOSE_TESTING 21
/** A controller made this circuit and Tor should not use it. */
-#define CIRCUIT_PURPOSE_CONTROLLER 21
+#define CIRCUIT_PURPOSE_CONTROLLER 22
/** This circuit is used for path bias probing only */
-#define CIRCUIT_PURPOSE_PATH_BIAS_TESTING 22
+#define CIRCUIT_PURPOSE_PATH_BIAS_TESTING 23
/** This circuit is used for vanguards/restricted paths.
*
@@ -124,9 +126,9 @@
* on-demand. When an HS operation needs to take place (e.g. connect to an
* intro point), these circuits are then cannibalized and repurposed to the
* actual needed HS purpose. */
-#define CIRCUIT_PURPOSE_HS_VANGUARDS 23
+#define CIRCUIT_PURPOSE_HS_VANGUARDS 24
-#define CIRCUIT_PURPOSE_MAX_ 23
+#define CIRCUIT_PURPOSE_MAX_ 24
/** A catch-all for unrecognized purposes. Currently we don't expect
* to make or see any circuits with this purpose. */
#define CIRCUIT_PURPOSE_UNKNOWN 255
@@ -216,7 +218,7 @@ void circuit_mark_all_dirty_circs_as_unusable(void);
void circuit_synchronize_written_or_bandwidth(const circuit_t *c,
circuit_channel_direction_t dir);
MOCK_DECL(void, circuit_mark_for_close_, (circuit_t *circ, int reason,
- int line, const char *file));
+ int line, const char *cfile));
int circuit_get_cpath_len(origin_circuit_t *circ);
int circuit_get_cpath_opened_len(const origin_circuit_t *);
void circuit_clear_cpath(origin_circuit_t *circ);
@@ -228,7 +230,6 @@ int circuit_count_pending_on_channel(channel_t *chan);
#define circuit_mark_for_close(c, reason) \
circuit_mark_for_close_((c), (reason), __LINE__, SHORT_FILE__)
-void assert_cpath_layer_ok(const crypt_path_t *cp);
MOCK_DECL(void, assert_circuit_ok,(const circuit_t *c));
void circuit_free_all(void);
void circuits_handle_oom(size_t current_allocation);
diff --git a/src/core/or/circuitmux.c b/src/core/or/circuitmux.c
index 88f9ac7923..b2628bec3f 100644
--- a/src/core/or/circuitmux.c
+++ b/src/core/or/circuitmux.c
@@ -294,9 +294,6 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux, smartlist_t *detached_out)
circuitmux_make_circuit_inactive(cmux, circ);
}
- /* Clear n_mux */
- circ->n_mux = NULL;
-
if (detached_out)
smartlist_add(detached_out, circ);
} else if (circ->magic == OR_CIRCUIT_MAGIC) {
@@ -309,12 +306,6 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux, smartlist_t *detached_out)
circuitmux_make_circuit_inactive(cmux, circ);
}
- /*
- * It has a sensible p_chan and direction == CELL_DIRECTION_IN,
- * so clear p_mux.
- */
- TO_OR_CIRCUIT(circ)->p_mux = NULL;
-
if (detached_out)
smartlist_add(detached_out, circ);
} else {
@@ -836,18 +827,14 @@ circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ,
*/
log_info(LD_CIRC,
"Circuit %u on channel %"PRIu64 " was already attached to "
- "cmux %p (trying to attach to %p)",
+ "(trying to attach to %p)",
(unsigned)circ_id, (channel_id),
- ((direction == CELL_DIRECTION_OUT) ?
- circ->n_mux : TO_OR_CIRCUIT(circ)->p_mux),
cmux);
/*
* The mux pointer on this circuit and the direction in result should
* match; otherwise assert.
*/
- if (direction == CELL_DIRECTION_OUT) tor_assert(circ->n_mux == cmux);
- else tor_assert(TO_OR_CIRCUIT(circ)->p_mux == cmux);
tor_assert(hashent->muxinfo.direction == direction);
/*
@@ -872,13 +859,6 @@ circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ,
"Attaching circuit %u on channel %"PRIu64 " to cmux %p",
(unsigned)circ_id, (channel_id), cmux);
- /*
- * Assert that the circuit doesn't already have a mux for this
- * direction.
- */
- if (direction == CELL_DIRECTION_OUT) tor_assert(circ->n_mux == NULL);
- else tor_assert(TO_OR_CIRCUIT(circ)->p_mux == NULL);
-
/* Insert it in the map */
hashent = tor_malloc_zero(sizeof(*hashent));
hashent->chan_id = channel_id;
@@ -902,10 +882,6 @@ circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ,
HT_INSERT(chanid_circid_muxinfo_map, cmux->chanid_circid_map,
hashent);
- /* Set the circuit's mux for this direction */
- if (direction == CELL_DIRECTION_OUT) circ->n_mux = cmux;
- else TO_OR_CIRCUIT(circ)->p_mux = cmux;
-
/* Update counters */
++(cmux->n_circuits);
if (cell_count > 0) {
@@ -993,9 +969,6 @@ circuitmux_detach_circuit,(circuitmux_t *cmux, circuit_t *circ))
/* Consistency check: the direction must match the direction searched */
tor_assert(last_searched_direction == hashent->muxinfo.direction);
- /* Clear the circuit's mux for this direction */
- if (last_searched_direction == CELL_DIRECTION_OUT) circ->n_mux = NULL;
- else TO_OR_CIRCUIT(circ)->p_mux = NULL;
/* Now remove it from the map */
HT_REMOVE(chanid_circid_muxinfo_map, cmux->chanid_circid_map, hashent);
diff --git a/src/core/or/circuitpadding.c b/src/core/or/circuitpadding.c
index aa38b0ffc3..a62cdcf9e6 100644
--- a/src/core/or/circuitpadding.c
+++ b/src/core/or/circuitpadding.c
@@ -23,7 +23,7 @@
* As specified by prop#254, clients can negotiate padding with relays by using
* PADDING_NEGOTIATE cells. After successful padding negotiation, padding
* machines are assigned to the circuit in their mutable form as a
- * circpad_machine_state_t.
+ * circpad_machine_runtime_t.
*
* Each state of a padding state machine can be either:
* - A histogram that specifies inter-arrival padding delays.
@@ -37,6 +37,13 @@
* When a padding machine reaches the END state, it gets wiped from the circuit
* so that other padding machines can take over if needed (see
* circpad_machine_spec_transitioned_to_end()).
+ *
+ ****************************
+ * General notes:
+ *
+ * All used machines should be heap allocated and placed into
+ * origin_padding_machines/relay_padding_machines so that they get correctly
+ * cleaned up by the circpad_free_all() function.
**/
#define CIRCUITPADDING_PRIVATE
@@ -46,8 +53,10 @@
#include "lib/math/prob_distr.h"
#include "core/or/or.h"
#include "core/or/circuitpadding.h"
+#include "core/or/circuitpadding_machines.h"
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
+#include "core/mainloop/netstatus.h"
#include "core/or/relay.h"
#include "feature/stats/rephist.h"
#include "feature/nodelist/networkstatus.h"
@@ -71,15 +80,18 @@
#include "app/config/config.h"
-static inline circpad_purpose_mask_t circpad_circ_purpose_to_mask(uint8_t
- circ_purpose);
static inline circpad_circuit_state_t circpad_circuit_state(
origin_circuit_t *circ);
static void circpad_setup_machine_on_circ(circuit_t *on_circ,
const circpad_machine_spec_t *machine);
static double circpad_distribution_sample(circpad_distribution_t dist);
+static inline void circpad_machine_update_state_length_for_nonpadding(
+ circpad_machine_runtime_t *mi);
+
/** Cached consensus params */
+static uint8_t circpad_padding_disabled;
+static uint8_t circpad_padding_reduced;
static uint8_t circpad_global_max_padding_percent;
static uint16_t circpad_global_allowed_cells;
static uint16_t circpad_max_circ_queued_cells;
@@ -120,40 +132,17 @@ STATIC smartlist_t *relay_padding_machines = NULL;
#define FOR_EACH_ACTIVE_CIRCUIT_MACHINE_END } STMT_END ;
/**
- * Return a human-readable description for a circuit padding state.
- */
-static const char *
-circpad_state_to_string(circpad_statenum_t state)
-{
- const char *descr;
-
- switch (state) {
- case CIRCPAD_STATE_START:
- descr = "START";
- break;
- case CIRCPAD_STATE_BURST:
- descr = "BURST";
- break;
- case CIRCPAD_STATE_GAP:
- descr = "GAP";
- break;
- case CIRCPAD_STATE_END:
- descr = "END";
- break;
- default:
- descr = "CUSTOM"; // XXX: Just return # in static char buf?
- }
-
- return descr;
-}
-
-/**
* Free the machineinfo at an index
*/
static void
circpad_circuit_machineinfo_free_idx(circuit_t *circ, int idx)
{
if (circ->padding_info[idx]) {
+ log_fn(LOG_INFO,LD_CIRC, "Freeing padding info idx %d on circuit %u (%d)",
+ idx, CIRCUIT_IS_ORIGIN(circ) ?
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0,
+ circ->purpose);
+
tor_free(circ->padding_info[idx]->histogram);
timer_free(circ->padding_info[idx]->padding_timer);
tor_free(circ->padding_info[idx]);
@@ -161,6 +150,118 @@ circpad_circuit_machineinfo_free_idx(circuit_t *circ, int idx)
}
/**
+ * Return true if circpad has decided to hold the circuit open for additional
+ * padding. This function is used to take and retain ownership of certain
+ * types of circuits that have padding machines on them, that have been passed
+ * to circuit_mark_for_close().
+ *
+ * circuit_mark_for_close() calls this function to ask circpad if any padding
+ * machines want to keep the circuit open longer to pad.
+ *
+ * Any non-measurement circuit that was closed for a normal, non-error reason
+ * code may be held open for up to CIRCPAD_DELAY_INFINITE microseconds between
+ * network-driven cell events.
+ *
+ * After CIRCPAD_DELAY_INFINITE microseconds of silence on a circuit, this
+ * function will no longer hold it open (it will return 0 regardless of
+ * what the machines ask for, and thus circuit_expire_old_circuits_clientside()
+ * will close the circuit after roughly 1.25hr of idle time, maximum,
+ * regardless of the padding machine state.
+ */
+int
+circpad_marked_circuit_for_padding(circuit_t *circ, int reason)
+{
+ /* If the circuit purpose is measurement or path bias, don't
+ * hold it open */
+ if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING ||
+ circ->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ return 0;
+ }
+
+ /* If the circuit is closed for any reason other than these three valid,
+ * client-side close reasons, do not try to keep it open. It is probably
+ * damaged or unusable. Note this is OK with vanguards because
+ * controller-closed circuits have REASON=REQUESTED, so vanguards-closed
+ * circuits will not be held open (we want them to close ASAP). */
+ if (!(reason == END_CIRC_REASON_NONE ||
+ reason == END_CIRC_REASON_FINISHED ||
+ reason == END_CIRC_REASON_IP_NOW_REDUNDANT)) {
+ return 0;
+ }
+
+ FOR_EACH_ACTIVE_CIRCUIT_MACHINE_BEGIN(i, circ) {
+ circpad_machine_runtime_t *mi = circ->padding_info[i];
+ if (!mi) {
+ continue; // No padding runtime info; check next machine
+ }
+
+ const circpad_state_t *state = circpad_machine_current_state(mi);
+
+ /* If we're in END state (NULL here), then check next machine */
+ if (!state) {
+ continue; // check next machine
+ }
+
+ /* If the machine does not want to control the circuit close itself, then
+ * check the next machine */
+ if (!circ->padding_machine[i]->manage_circ_lifetime) {
+ continue; // check next machine
+ }
+
+ /* If the machine has reached the END state, we can close. Check next
+ * machine. */
+ if (mi->current_state == CIRCPAD_STATE_END) {
+ continue; // check next machine
+ }
+
+ log_info(LD_CIRC, "Circuit %d is not marked for close because of a "
+ "pending padding machine in index %d.",
+ CIRCUIT_IS_ORIGIN(circ) ?
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0, i);
+
+ /* If the machine has had no network events at all within the
+ * last circpad_delay_t timespan, it's in some deadlock state.
+ * Tell circuit_mark_for_close() that we don't own it anymore.
+ * This will allow circuit_expire_old_circuits_clientside() to
+ * close it.
+ */
+ if (circ->padding_info[i]->last_cell_time_sec +
+ (time_t)CIRCPAD_DELAY_MAX_SECS < approx_time()) {
+ log_notice(LD_BUG, "Circuit %d was not marked for close because of a "
+ "pending padding machine in index %d for over an hour. "
+ "Circuit is a %s",
+ CIRCUIT_IS_ORIGIN(circ) ?
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0,
+ i, circuit_purpose_to_string(circ->purpose));
+
+ return 0; // abort timer reached; mark the circuit for close now
+ }
+
+ /* If we weren't marked dirty yet, let's pretend we're dirty now.
+ * ("Dirty" means that a circuit has been used for application traffic
+ * by Tor.. Dirty circuits have different expiry times, and are not
+ * considered in counts of built circuits, etc. By claiming that we're
+ * dirty, the rest of Tor will make decisions as if we were actually
+ * used by application data.
+ *
+ * This is most important for circuit_expire_old_circuits_clientside(),
+ * where we want that function to expire us after the padding machine
+ * has shut down, but using the MaxCircuitDirtiness timer instead of
+ * the idle circuit timer (again, we want this because we're not
+ * supposed to look idle to Guard nodes that can see our lifespan). */
+ if (!circ->timestamp_dirty)
+ circ->timestamp_dirty = approx_time();
+
+ /* Take ownership of the circuit */
+ circuit_change_purpose(circ, CIRCUIT_PURPOSE_C_CIRCUIT_PADDING);
+
+ return 1;
+ } FOR_EACH_ACTIVE_CIRCUIT_MACHINE_END;
+
+ return 0; // No machine wanted to keep the circuit open; mark for close
+}
+
+/**
* Free all the machineinfos in <b>circ</b> that match <b>machine_num</b>.
*
* Returns true if any machineinfos with that number were freed.
@@ -195,13 +296,14 @@ circpad_circuit_free_all_machineinfos(circuit_t *circ)
/**
* Allocate a new mutable machineinfo structure.
*/
-STATIC circpad_machine_state_t *
+STATIC circpad_machine_runtime_t *
circpad_circuit_machineinfo_new(circuit_t *on_circ, int machine_index)
{
- circpad_machine_state_t *mi =
- tor_malloc_zero(sizeof(circpad_machine_state_t));
+ circpad_machine_runtime_t *mi =
+ tor_malloc_zero(sizeof(circpad_machine_runtime_t));
mi->machine_index = machine_index;
mi->on_circ = on_circ;
+ mi->last_cell_time_sec = approx_time();
return mi;
}
@@ -214,7 +316,7 @@ circpad_circuit_machineinfo_new(circuit_t *on_circ, int machine_index)
* invalid state.
*/
STATIC const circpad_state_t *
-circpad_machine_current_state(const circpad_machine_state_t *mi)
+circpad_machine_current_state(const circpad_machine_runtime_t *mi)
{
const circpad_machine_spec_t *machine = CIRCPAD_GET_MACHINE(mi);
@@ -232,25 +334,20 @@ circpad_machine_current_state(const circpad_machine_state_t *mi)
}
/**
- * Calculate the lower bound of a histogram bin. The upper bound
- * is obtained by calling this function with bin+1, and subtracting 1.
+ * Get the lower bound of a histogram bin.
*
- * The 0th bin has a special value -- it only represents start_usec.
- * This is so we can specify a probability on 0-delay values.
+ * You can obtain the upper bound using histogram_get_bin_upper_bound().
*
- * After bin 0, bins are exponentially spaced, so that each subsequent
- * bin is twice as large as the previous. This is done so that higher
- * time resolution is given to lower time values.
- *
- * The infinity bin is a the last bin in the array (histogram_len-1).
- * It has a usec value of CIRCPAD_DELAY_INFINITE (UINT32_MAX).
+ * This function can also be called with 'bin' set to a value equal or greater
+ * than histogram_len in which case the infinity bin is chosen and
+ * CIRCPAD_DELAY_INFINITE is returned.
*/
STATIC circpad_delay_t
-circpad_histogram_bin_to_usec(const circpad_machine_state_t *mi,
+circpad_histogram_bin_to_usec(const circpad_machine_runtime_t *mi,
circpad_hist_index_t bin)
{
const circpad_state_t *state = circpad_machine_current_state(mi);
- circpad_delay_t start_usec;
+ circpad_delay_t rtt_add_usec = 0;
/* Our state should have been checked to be non-null by the caller
* (circpad_machine_remove_token()) */
@@ -258,37 +355,38 @@ circpad_histogram_bin_to_usec(const circpad_machine_state_t *mi,
return CIRCPAD_DELAY_INFINITE;
}
- if (state->use_rtt_estimate)
- start_usec = mi->rtt_estimate_usec+state->start_usec;
- else
- start_usec = state->start_usec;
-
- if (bin >= CIRCPAD_INFINITY_BIN(state))
+ /* The infinity bin has an upper bound of infinity, so make sure we return
+ * that if they ask for it. */
+ if (bin > CIRCPAD_INFINITY_BIN(state)) {
return CIRCPAD_DELAY_INFINITE;
+ }
- if (bin == 0)
- return start_usec;
+ /* If we are using an RTT estimate, consider it as well. */
+ if (state->use_rtt_estimate) {
+ rtt_add_usec = mi->rtt_estimate_usec;
+ }
- if (bin == 1)
- return start_usec+1;
+ return state->histogram_edges[bin] + rtt_add_usec;
+}
- /* The bin widths double every index, so that we can have more resolution
- * for lower time values in the histogram. */
- const circpad_time_t bin_width_exponent =
- 1 << (CIRCPAD_INFINITY_BIN(state) - bin);
- return (circpad_delay_t)MIN(start_usec +
- state->range_usec/bin_width_exponent,
- CIRCPAD_DELAY_INFINITE);
+/**
+ * Like circpad_histogram_bin_to_usec() but return the upper bound of bin.
+ * (The upper bound is included in the bin.)
+ */
+STATIC circpad_delay_t
+histogram_get_bin_upper_bound(const circpad_machine_runtime_t *mi,
+ circpad_hist_index_t bin)
+{
+ return circpad_histogram_bin_to_usec(mi, bin+1) - 1;
}
/** Return the midpoint of the histogram bin <b>bin_index</b>. */
static circpad_delay_t
-circpad_get_histogram_bin_midpoint(const circpad_machine_state_t *mi,
+circpad_get_histogram_bin_midpoint(const circpad_machine_runtime_t *mi,
int bin_index)
{
circpad_delay_t left_bound = circpad_histogram_bin_to_usec(mi, bin_index);
- circpad_delay_t right_bound =
- circpad_histogram_bin_to_usec(mi, bin_index+1)-1;
+ circpad_delay_t right_bound = histogram_get_bin_upper_bound(mi, bin_index);
return left_bound + (right_bound - left_bound)/2;
}
@@ -297,19 +395,17 @@ circpad_get_histogram_bin_midpoint(const circpad_machine_state_t *mi,
* Return the bin that contains the usec argument.
* "Contains" is defined as us in [lower, upper).
*
- * This function will never return the infinity bin (histogram_len-1),
- * in order to simplify the rest of the code.
- *
- * This means that technically the last bin (histogram_len-2)
- * has range [start_usec+range_usec, CIRCPAD_DELAY_INFINITE].
+ * This function will never return the infinity bin (histogram_len-1), in order
+ * to simplify the rest of the code, so if a usec is provided that falls above
+ * the highest non-infinity bin, that bin index will be returned.
*/
STATIC circpad_hist_index_t
-circpad_histogram_usec_to_bin(const circpad_machine_state_t *mi,
+circpad_histogram_usec_to_bin(const circpad_machine_runtime_t *mi,
circpad_delay_t usec)
{
const circpad_state_t *state = circpad_machine_current_state(mi);
- circpad_delay_t start_usec;
- int32_t bin; /* Larger than return type to properly clamp overflow */
+ circpad_delay_t rtt_add_usec = 0;
+ circpad_hist_index_t bin;
/* Our state should have been checked to be non-null by the caller
* (circpad_machine_remove_token()) */
@@ -317,34 +413,61 @@ circpad_histogram_usec_to_bin(const circpad_machine_state_t *mi,
return 0;
}
- if (state->use_rtt_estimate)
- start_usec = mi->rtt_estimate_usec+state->start_usec;
- else
- start_usec = state->start_usec;
+ /* If we are using an RTT estimate, consider it as well. */
+ if (state->use_rtt_estimate) {
+ rtt_add_usec = mi->rtt_estimate_usec;
+ }
- /* The first bin (#0) has zero width and starts (and ends) at start_usec. */
- if (usec <= start_usec)
- return 0;
+ /* Walk through the bins and check the upper bound of each bin, if 'usec' is
+ * less-or-equal to that, return that bin. If rtt_estimate is enabled then
+ * add that to the upper bound of each bin.
+ *
+ * We don't want to return the infinity bin here, so don't go there. */
+ for (bin = 0 ; bin < CIRCPAD_INFINITY_BIN(state) ; bin++) {
+ if (usec <= histogram_get_bin_upper_bound(mi, bin) + rtt_add_usec) {
+ return bin;
+ }
+ }
- if (usec == start_usec+1)
- return 1;
+ /* We don't want to return the infinity bin here, so if we still didn't find
+ * the right bin, return the highest non-infinity bin */
+ return CIRCPAD_INFINITY_BIN(state)-1;
+}
- const circpad_time_t histogram_range_usec = state->range_usec;
- /* We need to find the bin corresponding to our position in the range.
- * Since bins are exponentially spaced in powers of two, we need to
- * take the log2 of our position in histogram_range_usec. However,
- * since tor_log2() returns the floor(log2(u64)), we have to adjust
- * it to behave like ceil(log2(u64)). This is verified in our tests
- * to properly invert the operation done in
- * circpad_histogram_bin_to_usec(). */
- bin = CIRCPAD_INFINITY_BIN(state) -
- tor_log2(2*histogram_range_usec/(usec-start_usec+1));
+/**
+ * Return true if the machine supports token removal.
+ *
+ * Token removal is equivalent to having a mutable histogram in the
+ * circpad_machine_runtime_t mutable info. So while we're at it,
+ * let's assert that everything is consistent between the mutable
+ * runtime and the readonly machine spec.
+ */
+static inline int
+circpad_is_token_removal_supported(circpad_machine_runtime_t *mi)
+{
+ /* No runtime histogram == no token removal */
+ if (mi->histogram == NULL) {
+ /* Machines that don't want token removal are trying to avoid
+ * potentially expensive mallocs, extra memory accesses, and/or
+ * potentially expensive monotime calls. Let's minimize checks
+ * and keep this path fast. */
+ tor_assert_nonfatal(mi->histogram_len == 0);
+ return 0;
+ } else {
+ /* Machines that do want token removal are less sensitive to performance.
+ * Let's spend some time to check that our state is consistent and sane */
+ const circpad_state_t *state = circpad_machine_current_state(mi);
+ if (BUG(!state)) {
+ return 1;
+ }
+ tor_assert_nonfatal(state->token_removal != CIRCPAD_TOKEN_REMOVAL_NONE);
+ tor_assert_nonfatal(state->histogram_len == mi->histogram_len);
+ tor_assert_nonfatal(mi->histogram_len != 0);
+ return 1;
+ }
- /* Clamp the return value to account for timevals before the start
- * of bin 0, or after the last bin. Don't return the infinity bin
- * index. */
- bin = MIN(MAX(bin, 1), CIRCPAD_INFINITY_BIN(state)-1);
- return bin;
+ tor_assert_nonfatal_unreached();
+ return 0;
}
/**
@@ -353,12 +476,15 @@ circpad_histogram_usec_to_bin(const circpad_machine_state_t *mi,
* Called after a state transition, or if the bins are empty.
*/
STATIC void
-circpad_machine_setup_tokens(circpad_machine_state_t *mi)
+circpad_machine_setup_tokens(circpad_machine_runtime_t *mi)
{
const circpad_state_t *state = circpad_machine_current_state(mi);
/* If this state doesn't exist, or doesn't have token removal,
- * free any previous state's histogram, and bail */
+ * free any previous state's runtime histogram, and bail.
+ *
+ * If we don't have a token removal strategy, we also don't need a runtime
+ * histogram and we rely on the immutable one in machine_spec_t. */
if (!state || state->token_removal == CIRCPAD_TOKEN_REMOVAL_NONE) {
if (mi->histogram) {
tor_free(mi->histogram);
@@ -385,7 +511,7 @@ circpad_machine_setup_tokens(circpad_machine_state_t *mi)
* Choose a length for this state (in cells), if specified.
*/
static void
-circpad_choose_state_length(circpad_machine_state_t *mi)
+circpad_choose_state_length(circpad_machine_runtime_t *mi)
{
const circpad_state_t *state = circpad_machine_current_state(mi);
double length;
@@ -398,28 +524,40 @@ circpad_choose_state_length(circpad_machine_state_t *mi)
length = circpad_distribution_sample(state->length_dist);
length = MAX(0, length);
length += state->start_length;
- length = MIN(length, state->max_length);
+
+ if (state->max_length) {
+ length = MIN(length, state->max_length);
+ }
mi->state_length = clamp_double_to_int64(length);
+
+ log_info(LD_CIRC, "State length sampled to %"PRIu64" for circuit %u",
+ mi->state_length, CIRCUIT_IS_ORIGIN(mi->on_circ) ?
+ TO_ORIGIN_CIRCUIT(mi->on_circ)->global_identifier : 0);
}
/**
* Sample a value from our iat_dist, and clamp it safely
* to circpad_delay_t.
+ *
+ * Before returning, add <b>delay_shift</b> (can be zero) to the sampled value.
*/
static circpad_delay_t
circpad_distribution_sample_iat_delay(const circpad_state_t *state,
- circpad_delay_t start_usec)
+ circpad_delay_t delay_shift)
{
double val = circpad_distribution_sample(state->iat_dist);
/* These comparisons are safe, because the output is in the range
* [0, 2**32), and double has a precision of 53 bits. */
+ /* We want a positive sample value */
val = MAX(0, val);
- val = MIN(val, state->range_usec);
+ /* Respect the maximum sample setting */
+ val = MIN(val, state->dist_max_sample_usec);
- /* This addition is exact: val is at most 2**32-1, start_usec
- * is at most 2**32-1, and doubles have a precision of 53 bits. */
- val += start_usec;
+ /* Now apply the shift:
+ * This addition is exact: val is at most 2**32-1, delay_shift is at most
+ * 2**32-1, and doubles have a precision of 53 bits. */
+ val += delay_shift;
/* Clamp the distribution at infinite delay val */
return (circpad_delay_t)MIN(tor_llround(val), CIRCPAD_DELAY_INFINITE);
@@ -433,13 +571,12 @@ circpad_distribution_sample_iat_delay(const circpad_state_t *state,
* that bin's [start,end) time range.
*/
STATIC circpad_delay_t
-circpad_machine_sample_delay(circpad_machine_state_t *mi)
+circpad_machine_sample_delay(circpad_machine_runtime_t *mi)
{
const circpad_state_t *state = circpad_machine_current_state(mi);
const circpad_hist_token_t *histogram = NULL;
circpad_hist_index_t curr_bin = 0;
circpad_delay_t bin_start, bin_end;
- circpad_delay_t start_usec;
/* These three must all be larger than circpad_hist_token_t, because
* we sum several circpad_hist_token_t values across the histogram */
uint64_t curr_weight = 0;
@@ -448,21 +585,13 @@ circpad_machine_sample_delay(circpad_machine_state_t *mi)
tor_assert(state);
- if (state->use_rtt_estimate)
- start_usec = mi->rtt_estimate_usec+state->start_usec;
- else
- start_usec = state->start_usec;
-
if (state->iat_dist.type != CIRCPAD_DIST_NONE) {
/* Sample from a fixed IAT distribution and return */
- return circpad_distribution_sample_iat_delay(state, start_usec);
- } else if (state->token_removal != CIRCPAD_TOKEN_REMOVAL_NONE) {
- /* We have a mutable histogram. Do basic sanity check and apply: */
- if (BUG(!mi->histogram) ||
- BUG(mi->histogram_len != state->histogram_len)) {
- return CIRCPAD_DELAY_INFINITE;
- }
-
+ circpad_delay_t iat_delay_shift = state->use_rtt_estimate ?
+ mi->rtt_estimate_usec + state->dist_added_shift_usec :
+ state->dist_added_shift_usec;
+ return circpad_distribution_sample_iat_delay(state, iat_delay_shift);
+ } else if (circpad_is_token_removal_supported(mi)) {
histogram = mi->histogram;
for (circpad_hist_index_t b = 0; b < state->histogram_len; b++)
histogram_total_tokens += histogram[b];
@@ -472,7 +601,13 @@ circpad_machine_sample_delay(circpad_machine_state_t *mi)
histogram_total_tokens = state->histogram_total_tokens;
}
- bin_choice = crypto_rand_uint64(histogram_total_tokens);
+ /* If we are out of tokens, don't schedule padding. */
+ if (!histogram_total_tokens) {
+ return CIRCPAD_DELAY_INFINITE;
+ }
+
+ bin_choice = crypto_fast_rng_get_uint64(get_thread_fast_rng(),
+ histogram_total_tokens);
/* Skip all the initial zero bins */
while (!histogram[curr_bin]) {
@@ -520,16 +655,13 @@ circpad_machine_sample_delay(circpad_machine_state_t *mi)
* function below samples from [bin_start, bin_end) */
bin_end = circpad_histogram_bin_to_usec(mi, curr_bin+1);
- /* Truncate the high bin in case it's the infinity bin:
- * Don't actually schedule an "infinite"-1 delay */
- bin_end = MIN(bin_end, start_usec+state->range_usec);
-
- // Sample uniformly between histogram[i] to histogram[i+1]-1,
- // but no need to sample if they are the same timeval (aka bin 0 or bin 1).
- if (bin_end <= bin_start+1)
+ /* Bin edges are monotonically increasing so this is a bug. Handle it. */
+ if (BUG(bin_start >= bin_end)) {
return bin_start;
- else
- return (circpad_delay_t)crypto_rand_uint64_range(bin_start, bin_end);
+ }
+
+ return (circpad_delay_t)crypto_fast_rng_uint64_range(get_thread_fast_rng(),
+ bin_start, bin_end);
}
/**
@@ -626,7 +758,7 @@ circpad_distribution_sample(circpad_distribution_t dist)
* greater than the target, and that has tokens remaining.
*/
static circpad_hist_index_t
-circpad_machine_first_higher_index(const circpad_machine_state_t *mi,
+circpad_machine_first_higher_index(const circpad_machine_runtime_t *mi,
circpad_delay_t target_bin_usec)
{
circpad_hist_index_t bin = circpad_histogram_usec_to_bin(mi,
@@ -635,7 +767,7 @@ circpad_machine_first_higher_index(const circpad_machine_state_t *mi,
/* Don't remove from the infinity bin */
for (; bin < CIRCPAD_INFINITY_BIN(mi); bin++) {
if (mi->histogram[bin] &&
- circpad_histogram_bin_to_usec(mi, bin+1) > target_bin_usec) {
+ histogram_get_bin_upper_bound(mi, bin) >= target_bin_usec) {
return bin;
}
}
@@ -648,7 +780,7 @@ circpad_machine_first_higher_index(const circpad_machine_state_t *mi,
* <b>target_bin_usec</b>, and that still has tokens remaining.
*/
static circpad_hist_index_t
-circpad_machine_first_lower_index(const circpad_machine_state_t *mi,
+circpad_machine_first_lower_index(const circpad_machine_runtime_t *mi,
circpad_delay_t target_bin_usec)
{
circpad_hist_index_t bin = circpad_histogram_usec_to_bin(mi,
@@ -669,7 +801,7 @@ circpad_machine_first_lower_index(const circpad_machine_state_t *mi,
* greater than the target.
*/
STATIC void
-circpad_machine_remove_higher_token(circpad_machine_state_t *mi,
+circpad_machine_remove_higher_token(circpad_machine_runtime_t *mi,
circpad_delay_t target_bin_usec)
{
/* We need to remove the token from the first bin
@@ -690,7 +822,7 @@ circpad_machine_remove_higher_token(circpad_machine_state_t *mi,
* lower than the target.
*/
STATIC void
-circpad_machine_remove_lower_token(circpad_machine_state_t *mi,
+circpad_machine_remove_lower_token(circpad_machine_runtime_t *mi,
circpad_delay_t target_bin_usec)
{
circpad_hist_index_t bin = circpad_machine_first_lower_index(mi,
@@ -719,7 +851,7 @@ circpad_machine_remove_lower_token(circpad_machine_state_t *mi,
* If it is false, use bin index distance only.
*/
STATIC void
-circpad_machine_remove_closest_token(circpad_machine_state_t *mi,
+circpad_machine_remove_closest_token(circpad_machine_runtime_t *mi,
circpad_delay_t target_bin_usec,
bool use_usec)
{
@@ -776,7 +908,7 @@ circpad_machine_remove_closest_token(circpad_machine_state_t *mi,
bin_to_remove = lower;
}
mi->histogram[bin_to_remove]--;
- log_debug(LD_GENERAL, "Removing token from bin %d", bin_to_remove);
+ log_debug(LD_CIRC, "Removing token from bin %d", bin_to_remove);
return;
} else {
if (current - lower > higher - current) {
@@ -801,7 +933,7 @@ circpad_machine_remove_closest_token(circpad_machine_state_t *mi,
* If it is empty, do nothing.
*/
static void
-circpad_machine_remove_exact(circpad_machine_state_t *mi,
+circpad_machine_remove_exact(circpad_machine_runtime_t *mi,
circpad_delay_t target_bin_usec)
{
circpad_hist_index_t bin = circpad_histogram_usec_to_bin(mi,
@@ -818,7 +950,7 @@ circpad_machine_remove_exact(circpad_machine_state_t *mi,
* otherwise returns 0.
*/
static circpad_decision_t
-check_machine_token_supply(circpad_machine_state_t *mi)
+check_machine_token_supply(circpad_machine_runtime_t *mi)
{
uint32_t histogram_total_tokens = 0;
@@ -829,7 +961,7 @@ check_machine_token_supply(circpad_machine_state_t *mi)
*
* We also do not count infinity bin in histogram totals.
*/
- if (mi->histogram_len && mi->histogram) {
+ if (circpad_is_token_removal_supported(mi)) {
for (circpad_hist_index_t b = 0; b < CIRCPAD_INFINITY_BIN(mi); b++)
histogram_total_tokens += mi->histogram[b];
@@ -848,22 +980,55 @@ check_machine_token_supply(circpad_machine_state_t *mi)
}
/**
- * Remove a token from the bin corresponding to the delta since
- * last packet. If that bin is empty, choose a token based on
- * the specified removal strategy in the state machine.
+ * Count that a padding packet was sent.
*
- * This function also updates and checks rate limit and state
- * limit counters.
- *
- * Returns 1 if we transition states, 0 otherwise.
+ * This updates our state length count, our machine rate limit counts,
+ * and if token removal is used, decrements the histogram.
*/
-STATIC circpad_decision_t
-circpad_machine_remove_token(circpad_machine_state_t *mi)
+static inline void
+circpad_machine_count_padding_sent(circpad_machine_runtime_t *mi)
{
- const circpad_state_t *state = NULL;
- circpad_time_t current_time;
- circpad_delay_t target_bin_usec;
+ /* If we have a valid state length bound, consider it */
+ if (mi->state_length != CIRCPAD_STATE_LENGTH_INFINITE &&
+ !BUG(mi->state_length <= 0)) {
+ mi->state_length--;
+ }
+
+ /*
+ * Update non-padding counts for rate limiting: We scale at UINT16_MAX
+ * because we only use this for a percentile limit of 2 sig figs, and
+ * space is scare in the machineinfo struct.
+ */
+ mi->padding_sent++;
+ if (mi->padding_sent == UINT16_MAX) {
+ mi->padding_sent /= 2;
+ mi->nonpadding_sent /= 2;
+ }
+
+ circpad_global_padding_sent++;
+
+ /* If we have a mutable histogram, reduce the token count from
+ * the chosen padding bin (this assumes we always send padding
+ * when we intended to). */
+ if (circpad_is_token_removal_supported(mi)) {
+ /* Check array bounds and token count before removing */
+ if (!BUG(mi->chosen_bin >= mi->histogram_len) &&
+ !BUG(mi->histogram[mi->chosen_bin] == 0)) {
+ mi->histogram[mi->chosen_bin]--;
+ }
+ }
+}
+/**
+ * Count a nonpadding packet as being sent.
+ *
+ * This function updates our overhead accounting variables, as well
+ * as decrements the state limit packet counter, if the latter was
+ * flagged as applying to non-padding as well.
+ */
+static inline void
+circpad_machine_count_nonpadding_sent(circpad_machine_runtime_t *mi)
+{
/* Update non-padding counts for rate limiting: We scale at UINT16_MAX
* because we only use this for a percentile limit of 2 sig figs, and
* space is scare in the machineinfo struct. */
@@ -873,12 +1038,70 @@ circpad_machine_remove_token(circpad_machine_state_t *mi)
mi->nonpadding_sent /= 2;
}
+ /* Update any state packet length limits that apply */
+ circpad_machine_update_state_length_for_nonpadding(mi);
+
+ /* Remove a token from the histogram, if applicable */
+ circpad_machine_remove_token(mi);
+}
+
+/**
+ * Decrement the state length counter for a non-padding packet.
+ *
+ * Only updates the state length if we're using that feature, we
+ * have a state, and the machine wants to count non-padding packets
+ * towards the state length.
+ */
+static inline void
+circpad_machine_update_state_length_for_nonpadding(
+ circpad_machine_runtime_t *mi)
+{
+ const circpad_state_t *state = NULL;
+
+ if (mi->state_length == CIRCPAD_STATE_LENGTH_INFINITE)
+ return;
+
+ state = circpad_machine_current_state(mi);
+
+ /* If we are not in a padding state (like start or end), we're done */
+ if (!state)
+ return;
+
+ /* If we're enforcing a state length on non-padding packets,
+ * decrement it */
+ if (state->length_includes_nonpadding &&
+ mi->state_length > 0) {
+ mi->state_length--;
+ }
+}
+
+/**
+ * When a non-padding packet arrives, remove a token from the bin
+ * corresponding to the delta since last sent packet. If that bin
+ * is empty, choose a token based on the specified removal strategy
+ * in the state machine.
+ */
+STATIC void
+circpad_machine_remove_token(circpad_machine_runtime_t *mi)
+{
+ const circpad_state_t *state = NULL;
+ circpad_time_t current_time;
+ circpad_delay_t target_bin_usec;
+
/* Dont remove any tokens if there was no padding scheduled */
if (!mi->padding_scheduled_at_usec) {
- return CIRCPAD_STATE_UNCHANGED;
+ return;
}
state = circpad_machine_current_state(mi);
+
+ /* If we are not in a padding state (like start or end), we're done */
+ if (!state)
+ return;
+ /* Don't remove any tokens if we're not doing token removal */
+ if (state->token_removal == CIRCPAD_TOKEN_REMOVAL_NONE)
+ return;
+
current_time = monotime_absolute_usec();
/* If we have scheduled padding some time in the future, we want to see what
@@ -895,22 +1118,8 @@ circpad_machine_remove_token(circpad_machine_state_t *mi)
timer_disable(mi->padding_timer);
}
- /* If we are not in a padding state (like start or end), we're done */
- if (!state)
- return CIRCPAD_STATE_UNCHANGED;
-
- /* If we're enforcing a state length on non-padding packets,
- * decrement it */
- if (mi->state_length != CIRCPAD_STATE_LENGTH_INFINITE &&
- state->length_includes_nonpadding &&
- mi->state_length > 0) {
- mi->state_length--;
- }
-
/* Perform the specified token removal strategy */
switch (state->token_removal) {
- case CIRCPAD_TOKEN_REMOVAL_NONE:
- break;
case CIRCPAD_TOKEN_REMOVAL_CLOSEST_USEC:
circpad_machine_remove_closest_token(mi, target_bin_usec, 1);
break;
@@ -926,10 +1135,13 @@ circpad_machine_remove_token(circpad_machine_state_t *mi)
case CIRCPAD_TOKEN_REMOVAL_EXACT:
circpad_machine_remove_exact(mi, target_bin_usec);
break;
+ case CIRCPAD_TOKEN_REMOVAL_NONE:
+ default:
+ tor_assert_nonfatal_unreached();
+ log_warn(LD_BUG, "Circpad: Unknown token removal strategy %d",
+ state->token_removal);
+ break;
}
-
- /* Check our token and state length limits */
- return check_machine_token_supply(mi);
}
/**
@@ -985,63 +1197,41 @@ circpad_send_command_to_hop,(origin_circuit_t *circ, uint8_t hopnum,
* CIRCPAD_STATE_CHANGED. Otherwise return CIRCPAD_STATE_UNCHANGED.
*/
circpad_decision_t
-circpad_send_padding_cell_for_callback(circpad_machine_state_t *mi)
+circpad_send_padding_cell_for_callback(circpad_machine_runtime_t *mi)
{
circuit_t *circ = mi->on_circ;
int machine_idx = mi->machine_index;
mi->padding_scheduled_at_usec = 0;
circpad_statenum_t state = mi->current_state;
- // Make sure circuit didn't close on us
+ /* Make sure circuit didn't close on us */
if (mi->on_circ->marked_for_close) {
log_fn(LOG_INFO,LD_CIRC,
- "Padding callback on a circuit marked for close. Ignoring.");
+ "Padding callback on circuit marked for close (%u). Ignoring.",
+ CIRCUIT_IS_ORIGIN(mi->on_circ) ?
+ TO_ORIGIN_CIRCUIT(mi->on_circ)->global_identifier : 0);
return CIRCPAD_STATE_CHANGED;
}
- /* If it's a histogram, reduce the token count */
- if (mi->histogram && mi->histogram_len) {
- /* Basic sanity check on the histogram before removing anything */
- if (BUG(mi->chosen_bin >= mi->histogram_len) ||
- BUG(mi->histogram[mi->chosen_bin] == 0)) {
- return CIRCPAD_STATE_CHANGED;
- }
-
- mi->histogram[mi->chosen_bin]--;
- }
-
- /* If we have a valid state length bound, consider it */
- if (mi->state_length != CIRCPAD_STATE_LENGTH_INFINITE &&
- !BUG(mi->state_length <= 0)) {
- mi->state_length--;
- }
-
- /*
- * Update non-padding counts for rate limiting: We scale at UINT16_MAX
- * because we only use this for a percentile limit of 2 sig figs, and
- * space is scare in the machineinfo struct.
- */
- mi->padding_sent++;
- if (mi->padding_sent == UINT16_MAX) {
- mi->padding_sent /= 2;
- mi->nonpadding_sent /= 2;
- }
- circpad_global_padding_sent++;
+ circpad_machine_count_padding_sent(mi);
if (CIRCUIT_IS_ORIGIN(mi->on_circ)) {
circpad_send_command_to_hop(TO_ORIGIN_CIRCUIT(mi->on_circ),
CIRCPAD_GET_MACHINE(mi)->target_hopnum,
RELAY_COMMAND_DROP, NULL, 0);
- log_fn(LOG_INFO,LD_CIRC, "Callback: Sending padding to origin circuit %u.",
- TO_ORIGIN_CIRCUIT(mi->on_circ)->global_identifier);
+ log_info(LD_CIRC, "Callback: Sending padding to origin circuit %u"
+ " (%d) [length: %"PRIu64"]",
+ TO_ORIGIN_CIRCUIT(mi->on_circ)->global_identifier,
+ mi->on_circ->purpose, mi->state_length);
} else {
// If we're a non-origin circ, we can just send from here as if we're the
// edge.
if (TO_OR_CIRCUIT(circ)->p_chan_cells.n <= circpad_max_circ_queued_cells) {
- log_fn(LOG_INFO,LD_CIRC,
- "Callback: Sending padding to non-origin circuit.");
+ log_info(LD_CIRC, "Callback: Sending padding to circuit (%d)"
+ " [length: %"PRIu64"]", mi->on_circ->purpose, mi->state_length);
relay_send_command_from_edge(0, mi->on_circ, RELAY_COMMAND_DROP, NULL,
0, NULL);
+ rep_hist_padding_count_write(PADDING_TYPE_DROP);
} else {
static ratelim_t cell_lim = RATELIM_INIT(600);
log_fn_ratelim(&cell_lim,LOG_NOTICE,LD_CIRC,
@@ -1050,7 +1240,6 @@ circpad_send_padding_cell_for_callback(circpad_machine_state_t *mi)
}
}
- rep_hist_padding_count_write(PADDING_TYPE_DROP);
/* This is a padding cell sent from the client or from the middle node,
* (because it's invoked from circuitpadding.c) */
circpad_cell_event_padding_sent(circ);
@@ -1071,7 +1260,7 @@ circpad_send_padding_cell_for_callback(circpad_machine_state_t *mi)
/**
* Tor-timer compatible callback that tells us to send a padding cell.
*
- * Timers are associated with circpad_machine_state_t's. When the machineinfo
+ * Timers are associated with circpad_machine_runtime_t's. When the machineinfo
* is freed on a circuit, the timers are cancelled. Since the lifetime
* of machineinfo is always longer than the timers, handles are not
* needed.
@@ -1080,7 +1269,7 @@ static void
circpad_send_padding_callback(tor_timer_t *timer, void *args,
const struct monotime_t *time)
{
- circpad_machine_state_t *mi = ((circpad_machine_state_t*)args);
+ circpad_machine_runtime_t *mi = ((circpad_machine_runtime_t*)args);
(void)timer; (void)time;
if (mi && mi->on_circ) {
@@ -1103,6 +1292,14 @@ circpad_send_padding_callback(tor_timer_t *timer, void *args,
void
circpad_new_consensus_params(const networkstatus_t *ns)
{
+ circpad_padding_disabled =
+ networkstatus_get_param(ns, "circpad_padding_disabled",
+ 0, 0, 1);
+
+ circpad_padding_reduced =
+ networkstatus_get_param(ns, "circpad_padding_reduced",
+ 0, 0, 1);
+
circpad_global_allowed_cells =
networkstatus_get_param(ns, "circpad_global_allowed_cells",
0, 0, UINT16_MAX-1);
@@ -1117,6 +1314,24 @@ circpad_new_consensus_params(const networkstatus_t *ns)
}
/**
+ * Return true if padding is allowed by torrc and consensus.
+ */
+static bool
+circpad_is_padding_allowed(void)
+{
+ /* If padding has been disabled in the consensus, don't send any more
+ * padding. Technically the machine should be shut down when the next
+ * machine condition check happens, but machine checks only happen on
+ * certain circuit events, and if padding is disabled due to some
+ * network overload or DoS condition, we really want to stop ASAP. */
+ if (circpad_padding_disabled || !get_options()->CircuitPadding) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
* Check this machine against its padding limits, as well as global
* consensus limits.
*
@@ -1130,14 +1345,14 @@ circpad_new_consensus_params(const networkstatus_t *ns)
* Returns 1 if limits are set and we've hit them. Otherwise returns 0.
*/
STATIC bool
-circpad_machine_reached_padding_limit(circpad_machine_state_t *mi)
+circpad_machine_reached_padding_limit(circpad_machine_runtime_t *mi)
{
const circpad_machine_spec_t *machine = CIRCPAD_GET_MACHINE(mi);
/* If machine_padding_pct is non-zero, and we've sent more
* than the allowed count of padding cells, then check our
* percent limits for this machine. */
- if (machine->max_padding_percent &&
+ if (machine->max_padding_percent &&
mi->padding_sent >= machine->allowed_padding_count) {
uint32_t total_cells = mi->padding_sent + mi->nonpadding_sent;
@@ -1178,16 +1393,36 @@ circpad_machine_reached_padding_limit(circpad_machine_state_t *mi)
* 0 otherwise.
*/
MOCK_IMPL(circpad_decision_t,
-circpad_machine_schedule_padding,(circpad_machine_state_t *mi))
+circpad_machine_schedule_padding,(circpad_machine_runtime_t *mi))
{
circpad_delay_t in_usec = 0;
struct timeval timeout;
tor_assert(mi);
+ /* Don't schedule padding if it is disabled */
+ if (!circpad_is_padding_allowed()) {
+ static ratelim_t padding_lim = RATELIM_INIT(600);
+ log_fn_ratelim(&padding_lim,LOG_INFO,LD_CIRC,
+ "Padding has been disabled, but machine still on circuit %"PRIu64
+ ", %d",
+ mi->on_circ->n_chan ? mi->on_circ->n_chan->global_identifier : 0,
+ mi->on_circ->n_circ_id);
+
+ return CIRCPAD_STATE_UNCHANGED;
+ }
+
+ /* Don't schedule padding if we are currently in dormant mode. */
+ if (!is_participating_on_network()) {
+ log_info(LD_CIRC, "Not scheduling padding because we are dormant.");
+ return CIRCPAD_STATE_UNCHANGED;
+ }
+
// Don't pad in end (but also don't cancel any previously
// scheduled padding either).
if (mi->current_state == CIRCPAD_STATE_END) {
- log_fn(LOG_INFO, LD_CIRC, "Padding end state");
+ log_fn(LOG_INFO, LD_CIRC, "Padding end state on circuit %u",
+ CIRCUIT_IS_ORIGIN(mi->on_circ) ?
+ TO_ORIGIN_CIRCUIT(mi->on_circ)->global_identifier : 0);
return CIRCPAD_STATE_UNCHANGED;
}
@@ -1198,7 +1433,8 @@ circpad_machine_schedule_padding,(circpad_machine_state_t *mi))
"Padding machine has reached padding limit on circuit %u",
TO_ORIGIN_CIRCUIT(mi->on_circ)->global_identifier);
} else {
- log_fn(LOG_INFO, LD_CIRC,
+ static ratelim_t padding_lim = RATELIM_INIT(600);
+ log_fn_ratelim(&padding_lim,LOG_INFO,LD_CIRC,
"Padding machine has reached padding limit on circuit %"PRIu64
", %d",
mi->on_circ->n_chan ? mi->on_circ->n_chan->global_identifier : 0,
@@ -1215,8 +1451,20 @@ circpad_machine_schedule_padding,(circpad_machine_state_t *mi))
/* in_usec = in microseconds */
in_usec = circpad_machine_sample_delay(mi);
- mi->padding_scheduled_at_usec = monotime_absolute_usec();
- log_fn(LOG_INFO,LD_CIRC,"\tPadding in %u usec", in_usec);
+ /* If we're using token removal, we need to know when the padding
+ * was scheduled at, so we can remove the appropriate token if
+ * a non-padding cell is sent before the padding timer expires.
+ *
+ * However, since monotime is unpredictably expensive, let's avoid
+ * using it for machines that don't need token removal. */
+ if (circpad_is_token_removal_supported(mi)) {
+ mi->padding_scheduled_at_usec = monotime_absolute_usec();
+ } else {
+ mi->padding_scheduled_at_usec = 1;
+ }
+ log_fn(LOG_INFO,LD_CIRC,"\tPadding in %u usec on circuit %u", in_usec,
+ CIRCUIT_IS_ORIGIN(mi->on_circ) ?
+ TO_ORIGIN_CIRCUIT(mi->on_circ)->global_identifier : 0);
// Don't schedule if we have infinite delay.
if (in_usec == CIRCPAD_DELAY_INFINITE) {
@@ -1240,7 +1488,9 @@ circpad_machine_schedule_padding,(circpad_machine_state_t *mi))
timeout.tv_sec = in_usec/TOR_USEC_PER_SEC;
timeout.tv_usec = (in_usec%TOR_USEC_PER_SEC);
- log_fn(LOG_INFO, LD_CIRC, "\tPadding in %u sec, %u usec",
+ log_fn(LOG_INFO, LD_CIRC, "\tPadding circuit %u in %u sec, %u usec",
+ CIRCUIT_IS_ORIGIN(mi->on_circ) ?
+ TO_ORIGIN_CIRCUIT(mi->on_circ)->global_identifier : 0,
(unsigned)timeout.tv_sec, (unsigned)timeout.tv_usec);
if (mi->padding_timer) {
@@ -1268,9 +1518,15 @@ circpad_machine_schedule_padding,(circpad_machine_state_t *mi))
* not access it.
*/
static void
-circpad_machine_spec_transitioned_to_end(circpad_machine_state_t *mi)
+circpad_machine_spec_transitioned_to_end(circpad_machine_runtime_t *mi)
{
const circpad_machine_spec_t *machine = CIRCPAD_GET_MACHINE(mi);
+ circuit_t *on_circ = mi->on_circ;
+
+ log_fn(LOG_INFO,LD_CIRC, "Padding machine in end state on circuit %u (%d)",
+ CIRCUIT_IS_ORIGIN(on_circ) ?
+ TO_ORIGIN_CIRCUIT(on_circ)->global_identifier : 0,
+ on_circ->purpose);
/*
* We allow machines to shut down and delete themselves as opposed
@@ -1286,7 +1542,6 @@ circpad_machine_spec_transitioned_to_end(circpad_machine_state_t *mi)
* here does.
*/
if (machine->should_negotiate_end) {
- circuit_t *on_circ = mi->on_circ;
if (machine->is_origin_side) {
/* We free the machine info here so that we can be replaced
* by a different machine. But we must leave the padding_machine
@@ -1318,7 +1573,7 @@ circpad_machine_spec_transitioned_to_end(circpad_machine_state_t *mi)
* Returns 1 if we transition states, 0 otherwise.
*/
MOCK_IMPL(circpad_decision_t,
-circpad_machine_spec_transition,(circpad_machine_state_t *mi,
+circpad_machine_spec_transition,(circpad_machine_runtime_t *mi,
circpad_event_t event))
{
const circpad_state_t *state =
@@ -1352,9 +1607,10 @@ circpad_machine_spec_transition,(circpad_machine_state_t *mi,
* a transition to itself. All non-specified events are ignored.
*/
log_fn(LOG_INFO, LD_CIRC,
- "Circpad machine %d transitioning from %s to %s",
- mi->machine_index, circpad_state_to_string(mi->current_state),
- circpad_state_to_string(s));
+ "Circuit %u circpad machine %d transitioning from %u to %u",
+ CIRCUIT_IS_ORIGIN(mi->on_circ) ?
+ TO_ORIGIN_CIRCUIT(mi->on_circ)->global_identifier : 0,
+ mi->machine_index, mi->current_state, s);
/* If this is not the same state, switch and init tokens,
* otherwise just reschedule padding. */
@@ -1399,7 +1655,7 @@ circpad_machine_spec_transition,(circpad_machine_state_t *mi,
*/
static void
circpad_estimate_circ_rtt_on_received(circuit_t *circ,
- circpad_machine_state_t *mi)
+ circpad_machine_runtime_t *mi)
{
/* Origin circuits don't estimate RTT. They could do it easily enough,
* but they have no reason to use it in any delay calculations. */
@@ -1428,10 +1684,29 @@ circpad_estimate_circ_rtt_on_received(circuit_t *circ,
", %d) after two back to back packets. Current RTT: %d",
circ->n_chan ? circ->n_chan->global_identifier : 0,
circ->n_circ_id, mi->rtt_estimate_usec);
- mi->stop_rtt_update = 1;
+ mi->stop_rtt_update = 1;
+
+ if (!mi->rtt_estimate_usec) {
+ static ratelim_t rtt_lim = RATELIM_INIT(600);
+ log_fn_ratelim(&rtt_lim,LOG_NOTICE,LD_BUG,
+ "Circuit got two cells back to back before estimating RTT.");
+ }
}
} else {
- mi->last_received_time_usec = monotime_absolute_usec();
+ const circpad_state_t *state = circpad_machine_current_state(mi);
+ if (BUG(!state)) {
+ return;
+ }
+
+ /* Since monotime is unpredictably expensive, only update this field
+ * if rtt estimates are needed. Otherwise, stop the rtt update. */
+ if (state->use_rtt_estimate) {
+ mi->last_received_time_usec = monotime_absolute_usec();
+ } else {
+ /* Let's fast-path future decisions not to update rtt if the
+ * feature is not in use. */
+ mi->stop_rtt_update = 1;
+ }
}
}
@@ -1446,7 +1721,7 @@ circpad_estimate_circ_rtt_on_received(circuit_t *circ,
*/
static void
circpad_estimate_circ_rtt_on_send(circuit_t *circ,
- circpad_machine_state_t *mi)
+ circpad_machine_runtime_t *mi)
{
/* Origin circuits don't estimate RTT. They could do it easily enough,
* but they have no reason to use it in any delay calculations. */
@@ -1488,12 +1763,12 @@ circpad_estimate_circ_rtt_on_send(circuit_t *circ,
* to back. Stop estimating RTT in this case. Note that we only
* stop RTT update if the circuit is opened, to allow for RTT estimates
* of var cells during circ setup. */
- mi->stop_rtt_update = 1;
-
- if (!mi->rtt_estimate_usec) {
- log_fn(LOG_NOTICE, LD_CIRC,
- "Got two cells back to back on a circuit before estimating RTT.");
+ if (!mi->rtt_estimate_usec && !mi->stop_rtt_update) {
+ static ratelim_t rtt_lim = RATELIM_INIT(600);
+ log_fn_ratelim(&rtt_lim,LOG_NOTICE,LD_BUG,
+ "Circuit sent two cells back to back before estimating RTT.");
}
+ mi->stop_rtt_update = 1;
}
}
@@ -1513,12 +1788,17 @@ circpad_cell_event_nonpadding_sent(circuit_t *on_circ)
/* If there are no machines then this loop should not iterate */
FOR_EACH_ACTIVE_CIRCUIT_MACHINE_BEGIN(i, on_circ) {
- /* First, update any RTT estimate */
+ /* First, update any timestamps */
+ on_circ->padding_info[i]->last_cell_time_sec = approx_time();
circpad_estimate_circ_rtt_on_send(on_circ, on_circ->padding_info[i]);
- /* Remove a token: this is the idea of adaptive padding, since we have an
- * ideal distribution that we want our distribution to look like. */
- if (!circpad_machine_remove_token(on_circ->padding_info[i])) {
+ /* Then, do accounting */
+ circpad_machine_count_nonpadding_sent(on_circ->padding_info[i]);
+
+ /* Check to see if we've run out of tokens for this state already,
+ * and if not, check for other state transitions */
+ if (check_machine_token_supply(on_circ->padding_info[i])
+ == CIRCPAD_STATE_UNCHANGED) {
/* If removing a token did not cause a transition, check if
* non-padding sent event should */
circpad_machine_spec_transition(on_circ->padding_info[i],
@@ -1539,7 +1819,8 @@ void
circpad_cell_event_nonpadding_received(circuit_t *on_circ)
{
FOR_EACH_ACTIVE_CIRCUIT_MACHINE_BEGIN(i, on_circ) {
- /* First, update any RTT estimate */
+ /* First, update any timestamps */
+ on_circ->padding_info[i]->last_cell_time_sec = approx_time();
circpad_estimate_circ_rtt_on_received(on_circ, on_circ->padding_info[i]);
circpad_machine_spec_transition(on_circ->padding_info[i],
@@ -1559,8 +1840,17 @@ void
circpad_cell_event_padding_sent(circuit_t *on_circ)
{
FOR_EACH_ACTIVE_CIRCUIT_MACHINE_BEGIN(i, on_circ) {
- circpad_machine_spec_transition(on_circ->padding_info[i],
+ /* Check to see if we've run out of tokens for this state already,
+ * and if not, check for other state transitions */
+ if (check_machine_token_supply(on_circ->padding_info[i])
+ == CIRCPAD_STATE_UNCHANGED) {
+ /* If removing a token did not cause a transition, check if
+ * non-padding sent event should */
+
+ on_circ->padding_info[i]->last_cell_time_sec = approx_time();
+ circpad_machine_spec_transition(on_circ->padding_info[i],
CIRCPAD_EVENT_PADDING_SENT);
+ }
} FOR_EACH_ACTIVE_CIRCUIT_MACHINE_END;
}
@@ -1577,6 +1867,7 @@ circpad_cell_event_padding_received(circuit_t *on_circ)
{
/* identical to padding sent */
FOR_EACH_ACTIVE_CIRCUIT_MACHINE_BEGIN(i, on_circ) {
+ on_circ->padding_info[i]->last_cell_time_sec = approx_time();
circpad_machine_spec_transition(on_circ->padding_info[i],
CIRCPAD_EVENT_PADDING_RECV);
} FOR_EACH_ACTIVE_CIRCUIT_MACHINE_END;
@@ -1592,7 +1883,7 @@ circpad_cell_event_padding_received(circuit_t *on_circ)
* Return 1 if we decide to transition, 0 otherwise.
*/
circpad_decision_t
-circpad_internal_event_infinity(circpad_machine_state_t *mi)
+circpad_internal_event_infinity(circpad_machine_runtime_t *mi)
{
return circpad_machine_spec_transition(mi, CIRCPAD_EVENT_INFINITY);
}
@@ -1606,7 +1897,7 @@ circpad_internal_event_infinity(circpad_machine_state_t *mi)
* Return 1 if we decide to transition, 0 otherwise.
*/
circpad_decision_t
-circpad_internal_event_bins_empty(circpad_machine_state_t *mi)
+circpad_internal_event_bins_empty(circpad_machine_runtime_t *mi)
{
if (circpad_machine_spec_transition(mi, CIRCPAD_EVENT_BINS_EMPTY)
== CIRCPAD_STATE_CHANGED) {
@@ -1625,7 +1916,7 @@ circpad_internal_event_bins_empty(circpad_machine_state_t *mi)
* Return 1 if we decide to transition, 0 otherwise.
*/
circpad_decision_t
-circpad_internal_event_state_length_up(circpad_machine_state_t *mi)
+circpad_internal_event_state_length_up(circpad_machine_runtime_t *mi)
{
return circpad_machine_spec_transition(mi, CIRCPAD_EVENT_LENGTH_COUNT);
}
@@ -1637,6 +1928,19 @@ static inline bool
circpad_machine_conditions_met(origin_circuit_t *circ,
const circpad_machine_spec_t *machine)
{
+ /* If padding is disabled, no machines should match/apply. This has
+ * the effect of shutting down all machines, and not adding any more. */
+ if (circpad_padding_disabled || !get_options()->CircuitPadding)
+ return 0;
+
+ /* If the consensus or our torrc has selected reduced connection padding,
+ * then only allow this machine if it is flagged as acceptable under
+ * reduced padding conditions */
+ if (circpad_padding_reduced || get_options()->ReducedCircuitPadding) {
+ if (!machine->conditions.reduced_padding_ok)
+ return 0;
+ }
+
if (!(circpad_circ_purpose_to_mask(TO_CIRCUIT(circ)->purpose)
& machine->conditions.purpose_mask))
return 0;
@@ -1700,7 +2004,6 @@ circpad_circuit_state(origin_circuit_t *circ)
* Convert a normal circuit purpose into a bitmask that we can
* use for determining matching circuits.
*/
-static inline
circpad_purpose_mask_t
circpad_circ_purpose_to_mask(uint8_t circ_purpose)
{
@@ -1743,7 +2046,8 @@ circpad_shutdown_old_machines(origin_circuit_t *on_circ)
}
/**
- * Negotiate new machines that would apply to this circuit.
+ * Negotiate new machines that would apply to this circuit, given the machines
+ * inside <b>machines_sl</b>.
*
* This function checks to see if we have any free machine indexes,
* and for each free machine index, it initializes the most recently
@@ -1751,14 +2055,15 @@ circpad_shutdown_old_machines(origin_circuit_t *on_circ)
* index and circuit conditions, and negotiates it with the appropriate
* middle relay.
*/
-static void
-circpad_add_matching_machines(origin_circuit_t *on_circ)
+STATIC void
+circpad_add_matching_machines(origin_circuit_t *on_circ,
+ smartlist_t *machines_sl)
{
circuit_t *circ = TO_CIRCUIT(on_circ);
#ifdef TOR_UNIT_TESTS
/* Tests don't have to init our padding machines */
- if (!origin_padding_machines)
+ if (!machines_sl)
return;
#endif
@@ -1775,7 +2080,7 @@ circpad_add_matching_machines(origin_circuit_t *on_circ)
/* We have a free machine index. Check the origin padding
* machines in reverse order, so that more recently added
* machines take priority over older ones. */
- SMARTLIST_FOREACH_REVERSE_BEGIN(origin_padding_machines,
+ SMARTLIST_FOREACH_REVERSE_BEGIN(machines_sl,
circpad_machine_spec_t *,
machine) {
/* Machine definitions have a specific target machine index.
@@ -1803,6 +2108,10 @@ circpad_add_matching_machines(origin_circuit_t *on_circ)
if (circpad_negotiate_padding(on_circ, machine->machine_num,
machine->target_hopnum,
CIRCPAD_COMMAND_START) < 0) {
+ log_info(LD_CIRC,
+ "Padding not negotiated. Cleaning machine from circuit %u",
+ CIRCUIT_IS_ORIGIN(circ) ?
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0);
circpad_circuit_machineinfo_free_idx(circ, i);
circ->padding_machine[i] = NULL;
on_circ->padding_negotiation_failed = 1;
@@ -1826,7 +2135,7 @@ circpad_machine_event_circ_added_hop(origin_circuit_t *on_circ)
{
/* Since our padding conditions do not specify a max_hops,
* all we can do is add machines here */
- circpad_add_matching_machines(on_circ);
+ circpad_add_matching_machines(on_circ, origin_padding_machines);
}
/**
@@ -1839,7 +2148,7 @@ void
circpad_machine_event_circ_built(origin_circuit_t *circ)
{
circpad_shutdown_old_machines(circ);
- circpad_add_matching_machines(circ);
+ circpad_add_matching_machines(circ, origin_padding_machines);
}
/**
@@ -1852,7 +2161,7 @@ void
circpad_machine_event_circ_purpose_changed(origin_circuit_t *circ)
{
circpad_shutdown_old_machines(circ);
- circpad_add_matching_machines(circ);
+ circpad_add_matching_machines(circ, origin_padding_machines);
}
/**
@@ -1866,7 +2175,7 @@ void
circpad_machine_event_circ_has_no_relay_early(origin_circuit_t *circ)
{
circpad_shutdown_old_machines(circ);
- circpad_add_matching_machines(circ);
+ circpad_add_matching_machines(circ, origin_padding_machines);
}
/**
@@ -1881,7 +2190,7 @@ void
circpad_machine_event_circ_has_streams(origin_circuit_t *circ)
{
circpad_shutdown_old_machines(circ);
- circpad_add_matching_machines(circ);
+ circpad_add_matching_machines(circ, origin_padding_machines);
}
/**
@@ -1896,7 +2205,7 @@ void
circpad_machine_event_circ_has_no_streams(origin_circuit_t *circ)
{
circpad_shutdown_old_machines(circ);
- circpad_add_matching_machines(circ);
+ circpad_add_matching_machines(circ, origin_padding_machines);
}
/**
@@ -1977,13 +2286,6 @@ circpad_deliver_recognized_relay_cell_events(circuit_t *circ,
uint8_t relay_command,
crypt_path_t *layer_hint)
{
- /* Padding negotiate cells are ignored by the state machines
- * for simplicity. */
- if (relay_command == RELAY_COMMAND_PADDING_NEGOTIATE ||
- relay_command == RELAY_COMMAND_PADDING_NEGOTIATED) {
- return;
- }
-
if (relay_command == RELAY_COMMAND_DROP) {
rep_hist_padding_count_read(PADDING_TYPE_DROP);
@@ -2020,16 +2322,12 @@ void
circpad_deliver_sent_relay_cell_events(circuit_t *circ,
uint8_t relay_command)
{
- /* Padding negotiate cells are ignored by the state machines
- * for simplicity. */
- if (relay_command == RELAY_COMMAND_PADDING_NEGOTIATE ||
- relay_command == RELAY_COMMAND_PADDING_NEGOTIATED) {
- return;
- }
-
/* RELAY_COMMAND_DROP is the multi-hop (aka circuit-level) padding cell in
* tor. (CELL_PADDING is a channel-level padding cell, which is not relayed
- * or processed here) */
+ * or processed here).
+ *
+ * We do generate events for PADDING_NEGOTIATE and PADDING_NEGOTIATED cells.
+ */
if (relay_command == RELAY_COMMAND_DROP) {
/* Optimization: The event for RELAY_COMMAND_DROP is sent directly
* from circpad_send_padding_cell_for_callback(). This is to avoid
@@ -2087,25 +2385,112 @@ circpad_setup_machine_on_circ(circuit_t *on_circ,
== NULL);
tor_assert_nonfatal(on_circ->padding_info[machine->machine_index] == NULL);
+ /* Log message */
+ if (CIRCUIT_IS_ORIGIN(on_circ)) {
+ log_info(LD_CIRC, "Registering machine %s to origin circ %u (%d)",
+ machine->name,
+ TO_ORIGIN_CIRCUIT(on_circ)->global_identifier, on_circ->purpose);
+ } else {
+ log_info(LD_CIRC, "Registering machine %s to non-origin circ (%d)",
+ machine->name, on_circ->purpose);
+ }
+
on_circ->padding_info[machine->machine_index] =
circpad_circuit_machineinfo_new(on_circ, machine->machine_index);
on_circ->padding_machine[machine->machine_index] = machine;
}
-/* These padding machines are only used for tests pending #28634. */
+/** Validate a single state of a padding machine */
+static bool
+padding_machine_state_is_valid(const circpad_state_t *state)
+{
+ int b;
+ uint32_t tokens_count = 0;
+ circpad_delay_t prev_bin_edge = 0;
+
+ /* We only validate histograms */
+ if (!state->histogram_len) {
+ return true;
+ }
+
+ /* We need at least two bins in a histogram */
+ if (state->histogram_len < 2) {
+ log_warn(LD_CIRC, "You can't have a histogram with less than 2 bins");
+ return false;
+ }
+
+ /* For each machine state, if it's a histogram, make sure all the
+ * histogram edges are well defined (i.e. are strictly monotonic). */
+ for (b = 0 ; b < state->histogram_len ; b++) {
+ /* Check that histogram edges are strictly increasing. Ignore the first
+ * edge since it can be zero. */
+ if (prev_bin_edge >= state->histogram_edges[b] && b > 0) {
+ log_warn(LD_CIRC, "Histogram edges are not increasing [%u/%u]",
+ prev_bin_edge, state->histogram_edges[b]);
+ return false;
+ }
+
+ prev_bin_edge = state->histogram_edges[b];
+
+ /* Also count the number of tokens as we go through the histogram states */
+ tokens_count += state->histogram[b];
+ }
+ /* Verify that the total number of tokens is correct */
+ if (tokens_count != state->histogram_total_tokens) {
+ log_warn(LD_CIRC, "Histogram token count is wrong [%u/%u]",
+ tokens_count, state->histogram_total_tokens);
+ return false;
+ }
+
+ return true;
+}
+
+/** Basic validation of padding machine */
+static bool
+padding_machine_is_valid(const circpad_machine_spec_t *machine)
+{
+ int i;
+
+ /* Validate the histograms of the padding machine */
+ for (i = 0 ; i < machine->num_states ; i++) {
+ if (!padding_machine_state_is_valid(&machine->states[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Validate and register <b>machine</b> into <b>machine_list</b>. If
+ * <b>machine_list</b> is NULL, then just validate. */
+void
+circpad_register_padding_machine(circpad_machine_spec_t *machine,
+ smartlist_t *machine_list)
+{
+ if (!padding_machine_is_valid(machine)) {
+ log_warn(LD_CIRC, "Machine #%u is invalid. Ignoring.",
+ machine->machine_num);
+ return;
+ }
+
+ if (machine_list) {
+ smartlist_add(machine_list, machine);
+ }
+}
+
#ifdef TOR_UNIT_TESTS
+/* These padding machines are only used for tests pending #28634. */
static void
circpad_circ_client_machine_init(void)
{
circpad_machine_spec_t *circ_client_machine
= tor_malloc_zero(sizeof(circpad_machine_spec_t));
- // XXX: Better conditions for merge.. Or disable this machine in
- // merge?
circ_client_machine->conditions.min_hops = 2;
circ_client_machine->conditions.state_mask =
CIRCPAD_CIRC_BUILDING|CIRCPAD_CIRC_OPENED|CIRCPAD_CIRC_HAS_RELAY_EARLY;
circ_client_machine->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL;
+ circ_client_machine->conditions.reduced_padding_ok = 1;
circ_client_machine->target_hopnum = 2;
circ_client_machine->is_origin_side = 1;
@@ -2133,19 +2518,21 @@ circpad_circ_client_machine_init(void)
circ_client_machine->states[CIRCPAD_STATE_BURST].token_removal =
CIRCPAD_TOKEN_REMOVAL_CLOSEST;
- // FIXME: Tune this histogram
circ_client_machine->states[CIRCPAD_STATE_BURST].histogram_len = 2;
- circ_client_machine->states[CIRCPAD_STATE_BURST].start_usec = 500;
- circ_client_machine->states[CIRCPAD_STATE_BURST].range_usec = 1000000;
+ circ_client_machine->states[CIRCPAD_STATE_BURST].histogram_edges[0]= 500;
+ circ_client_machine->states[CIRCPAD_STATE_BURST].histogram_edges[1]= 1000000;
+
/* We have 5 tokens in the histogram, which means that all circuits will look
* like they have 7 hops (since we start this machine after the second hop,
* and tokens are decremented for any valid hops, and fake extends are
* used after that -- 2+5==7). */
circ_client_machine->states[CIRCPAD_STATE_BURST].histogram[0] = 5;
+
circ_client_machine->states[CIRCPAD_STATE_BURST].histogram_total_tokens = 5;
circ_client_machine->machine_num = smartlist_len(origin_padding_machines);
- smartlist_add(origin_padding_machines, circ_client_machine);
+ circpad_register_padding_machine(circ_client_machine,
+ origin_padding_machines);
}
static void
@@ -2192,8 +2579,9 @@ circpad_circ_responder_machine_init(void)
circ_responder_machine->states[CIRCPAD_STATE_BURST].use_rtt_estimate = 1;
/* The histogram is 2 bins: an empty one, and infinity */
circ_responder_machine->states[CIRCPAD_STATE_BURST].histogram_len = 2;
- circ_responder_machine->states[CIRCPAD_STATE_BURST].start_usec = 5000;
- circ_responder_machine->states[CIRCPAD_STATE_BURST].range_usec = 1000000;
+ circ_responder_machine->states[CIRCPAD_STATE_BURST].histogram_edges[0]= 500;
+ circ_responder_machine->states[CIRCPAD_STATE_BURST].histogram_edges[1] =
+ 1000000;
/* During burst state we wait forever for padding to arrive.
We are waiting for a padding cell from the client to come in, so that we
@@ -2224,8 +2612,14 @@ circpad_circ_responder_machine_init(void)
before you send a padding response */
circ_responder_machine->states[CIRCPAD_STATE_GAP].use_rtt_estimate = 1;
circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram_len = 6;
- circ_responder_machine->states[CIRCPAD_STATE_GAP].start_usec = 5000;
- circ_responder_machine->states[CIRCPAD_STATE_GAP].range_usec = 1000000;
+ /* Specify histogram bins */
+ circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram_edges[0]= 500;
+ circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram_edges[1]= 1000;
+ circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram_edges[2]= 2000;
+ circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram_edges[3]= 4000;
+ circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram_edges[4]= 8000;
+ circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram_edges[5]= 16000;
+ /* Specify histogram tokens */
circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram[0] = 0;
circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram[1] = 1;
circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram[2] = 2;
@@ -2233,13 +2627,15 @@ circpad_circ_responder_machine_init(void)
circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram[4] = 1;
/* Total number of tokens */
circ_responder_machine->states[CIRCPAD_STATE_GAP].histogram_total_tokens = 6;
+
circ_responder_machine->states[CIRCPAD_STATE_GAP].token_removal =
CIRCPAD_TOKEN_REMOVAL_CLOSEST_USEC;
circ_responder_machine->machine_num = smartlist_len(relay_padding_machines);
- smartlist_add(relay_padding_machines, circ_responder_machine);
+ circpad_register_padding_machine(circ_responder_machine,
+ relay_padding_machines);
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/**
* Initialize all of our padding machines.
@@ -2256,6 +2652,14 @@ circpad_machines_init(void)
origin_padding_machines = smartlist_new();
relay_padding_machines = smartlist_new();
+ /* Register machines for hiding client-side intro circuits */
+ circpad_machine_client_hide_intro_circuits(origin_padding_machines);
+ circpad_machine_relay_hide_intro_circuits(relay_padding_machines);
+
+ /* Register machines for hiding client-side rendezvous circuits */
+ circpad_machine_client_hide_rend_circuits(origin_padding_machines);
+ circpad_machine_relay_hide_rend_circuits(relay_padding_machines);
+
// TODO: Parse machines from consensus and torrc
#ifdef TOR_UNIT_TESTS
circpad_circ_client_machine_init();
@@ -2292,8 +2696,9 @@ circpad_node_supports_padding(const node_t *node)
{
if (node->rs) {
log_fn(LOG_INFO, LD_CIRC, "Checking padding: %s",
- node->rs->pv.supports_padding ? "supported" : "unsupported");
- return node->rs->pv.supports_padding;
+ node->rs->pv.supports_hs_setup_padding ?
+ "supported" : "unsupported");
+ return node->rs->pv.supports_hs_setup_padding;
}
log_fn(LOG_INFO, LD_CIRC, "Empty routerstatus in padding check");
@@ -2306,8 +2711,8 @@ circpad_node_supports_padding(const node_t *node)
* Returns node_t from the consensus for that hop, if it is opened.
* Otherwise returns NULL.
*/
-static const node_t *
-circuit_get_nth_node(origin_circuit_t *circ, int hop)
+MOCK_IMPL(STATIC const node_t *,
+circuit_get_nth_node,(origin_circuit_t *circ, int hop))
{
crypt_path_t *iter = circuit_get_cpath_hop(circ, hop);
@@ -2370,8 +2775,9 @@ circpad_negotiate_padding(origin_circuit_t *circ,
&type)) < 0)
return -1;
- log_fn(LOG_INFO,LD_CIRC, "Negotiating padding on circuit %u",
- circ->global_identifier);
+ log_fn(LOG_INFO,LD_CIRC,
+ "Negotiating padding on circuit %u (%d), command %d",
+ circ->global_identifier, TO_CIRCUIT(circ)->purpose, command);
return circpad_send_command_to_hop(circ, target_hopnum,
RELAY_COMMAND_PADDING_NEGOTIATE,
@@ -2434,7 +2840,8 @@ circpad_handle_padding_negotiate(circuit_t *circ, cell_t *cell)
if (CIRCUIT_IS_ORIGIN(circ)) {
log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
- "Padding negotiate cell unsupported at origin.");
+ "Padding negotiate cell unsupported at origin (circuit %u)",
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier);
return -1;
}
@@ -2461,6 +2868,7 @@ circpad_handle_padding_negotiate(circuit_t *circ, cell_t *cell)
const circpad_machine_spec_t *, m) {
if (m->machine_num == negotiate->machine_type) {
circpad_setup_machine_on_circ(circ, m);
+ circpad_cell_event_nonpadding_received(circ);
goto done;
}
} SMARTLIST_FOREACH_END(m);
@@ -2500,20 +2908,24 @@ circpad_handle_padding_negotiated(circuit_t *circ, cell_t *cell,
/* Verify this came from the expected hop */
if (!circpad_padding_is_from_expected_hop(circ, layer_hint)) {
- log_fn(LOG_WARN, LD_CIRC,
- "Padding negotiated cell from wrong hop!");
+ log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
+ "Padding negotiated cell from wrong hop on circuit %u",
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier);
return -1;
}
if (circpad_negotiated_parse(&negotiated, cell->payload+RELAY_HEADER_SIZE,
CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE) < 0) {
log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
- "Received malformed PADDING_NEGOTIATED cell; "
- "dropping.");
+ "Received malformed PADDING_NEGOTIATED cell on circuit %u; "
+ "dropping.", TO_ORIGIN_CIRCUIT(circ)->global_identifier);
return -1;
}
if (negotiated->command == CIRCPAD_COMMAND_STOP) {
+ log_info(LD_CIRC,
+ "Received STOP command on PADDING_NEGOTIATED for circuit %u",
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier);
/* There may not be a padding_info here if we shut down the
* machine in circpad_shutdown_old_machines(). Or, if
* circpad_add_matching_matchines() added a new machine,
@@ -2527,13 +2939,45 @@ circpad_handle_padding_negotiated(circuit_t *circ, cell_t *cell,
free_circ_machineinfos_with_machine_num(circ, negotiated->machine_type);
TO_ORIGIN_CIRCUIT(circ)->padding_negotiation_failed = 1;
log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
- "Middle node did not accept our padding request.");
+ "Middle node did not accept our padding request on circuit %u (%d)",
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier,
+ circ->purpose);
}
circpad_negotiated_free(negotiated);
return 0;
}
+/** Free memory allocated by this machine spec. */
+STATIC void
+machine_spec_free_(circpad_machine_spec_t *m)
+{
+ if (!m) return;
+
+ tor_free(m->states);
+ tor_free(m);
+}
+
+/** Free all memory allocated by the circuitpadding subsystem. */
+void
+circpad_free_all(void)
+{
+ if (origin_padding_machines) {
+ SMARTLIST_FOREACH_BEGIN(origin_padding_machines,
+ circpad_machine_spec_t *, m) {
+ machine_spec_free(m);
+ } SMARTLIST_FOREACH_END(m);
+ smartlist_free(origin_padding_machines);
+ }
+ if (relay_padding_machines) {
+ SMARTLIST_FOREACH_BEGIN(relay_padding_machines,
+ circpad_machine_spec_t *, m) {
+ machine_spec_free(m);
+ } SMARTLIST_FOREACH_END(m);
+ smartlist_free(relay_padding_machines);
+ }
+}
+
/* Serialization */
// TODO: Should we use keyword=value here? Are there helpers for that?
#if 0
@@ -2586,4 +3030,4 @@ circpad_string_to_machine(const char *str)
return NULL;
}
-#endif
+#endif /* 0 */
diff --git a/src/core/or/circuitpadding.h b/src/core/or/circuitpadding.h
index 92fd4fc2d5..3cf40e11db 100644
--- a/src/core/or/circuitpadding.h
+++ b/src/core/or/circuitpadding.h
@@ -10,7 +10,7 @@
#ifndef TOR_CIRCUITPADDING_H
#define TOR_CIRCUITPADDING_H
-#include "src/trunnel/circpad_negotiation.h"
+#include "trunnel/circpad_negotiation.h"
#include "lib/evloop/timers.h"
struct circuit_t;
@@ -73,6 +73,7 @@ typedef uint64_t circpad_time_t;
/** The type for timer delays, in microseconds */
typedef uint32_t circpad_delay_t;
+#define CIRCPAD_DELAY_UNITS_PER_SECOND (1000*1000)
/**
* An infinite padding cell delay means don't schedule any padding --
@@ -86,9 +87,16 @@ typedef uint32_t circpad_delay_t;
#define CIRCPAD_DELAY_INFINITE (UINT32_MAX)
/**
+ * This is the maximum delay that the circuit padding system can have, in
+ * seconds.
+ */
+#define CIRCPAD_DELAY_MAX_SECS \
+ ((CIRCPAD_DELAY_INFINITE/CIRCPAD_DELAY_UNITS_PER_SECOND)+1)
+
+/**
* Macro to clarify when we're checking the infinity bin.
*
- * Works with either circpad_state_t or circpad_machine_state_t
+ * Works with either circpad_state_t or circpad_machine_runtime_t
*/
#define CIRCPAD_INFINITY_BIN(mi) ((mi)->histogram_len-1)
@@ -152,6 +160,17 @@ typedef struct circpad_machine_conditions_t {
/** Only apply the machine *if* vanguards are enabled */
unsigned requires_vanguards : 1;
+ /**
+ * This machine is ok to use if reduced padding is set in consensus
+ * or torrc. This machine will still be applied even if reduced padding
+ * is not set; this flag only acts to exclude machines that don't have
+ * it set when reduced padding is requested. Therefore, reduced padding
+ * machines should appear at the lowest priority in the padding machine
+ * lists (aka first in the list), so that non-reduced padding machines
+ * for the same purpose are given a chance to apply when reduced padding
+ * is not requested. */
+ unsigned reduced_padding_ok : 1;
+
/** Only apply the machine *if* the circuit's state matches any of
* the bits set in this bitmask. */
circpad_circuit_state_t state_mask;
@@ -198,14 +217,23 @@ typedef enum {
* These can be used instead of histograms for the inter-packet
* timing distribution, or to specify a distribution on the number
* of cells that can be sent while in a specific state of the state
- * machine. */
+ * machine.
+ *
+ * Each distribution takes up to two parameters which are described below. */
typedef enum {
+ /* No probability distribution is used */
CIRCPAD_DIST_NONE = 0,
+ /* Uniform distribution: param1 is lower bound and param2 is upper bound */
CIRCPAD_DIST_UNIFORM = 1,
+ /* Logistic distribution: param1 is Mu, param2 is sigma. */
CIRCPAD_DIST_LOGISTIC = 2,
+ /* Log-logistic distribution: param1 is Alpha, param2 is 1.0/Beta */
CIRCPAD_DIST_LOG_LOGISTIC = 3,
+ /* Geometric distribution: param1 is 'p' (success probability) */
CIRCPAD_DIST_GEOMETRIC = 4,
+ /* Weibull distribution: param1 is k, param2 is Lambda */
CIRCPAD_DIST_WEIBULL = 5,
+ /* Generalized Pareto distribution: param1 is sigma, param2 is xi */
CIRCPAD_DIST_PARETO = 6
} circpad_distribution_type_t;
@@ -228,19 +256,24 @@ typedef uint16_t circpad_statenum_t;
#define CIRCPAD_STATENUM_MAX (UINT16_MAX)
/** A histogram is used to sample padding delays given a machine state. This
- * constant defines the maximum histogram width (i.e. the max number of bins)
+ * constant defines the maximum histogram width (i.e. the max number of bins).
*
- * Each histogram bin is twice as large as the previous. Two exceptions: The
- * first bin has zero width (which means that minimum delay is applied to the
- * next padding cell), and the last bin (infinity bin) has infinite width
- * (which means that the next padding cell will be delayed infinitely). */
-#define CIRCPAD_MAX_HISTOGRAM_LEN (sizeof(circpad_delay_t)*8 + 1)
+ * The current limit is arbitrary and could be raised if there is a need,
+ * however too many bins will be hard to serialize in the future.
+ *
+ * Memory concerns are not so great here since the corresponding histogram and
+ * histogram_edges arrays are global and not per-circuit.
+ *
+ * If we ever upgrade this to a value that can't be represented by 8-bits we
+ * also need to upgrade circpad_hist_index_t.
+ */
+#define CIRCPAD_MAX_HISTOGRAM_LEN (100)
/**
* A state of a padding state machine. The information here are immutable and
* represent the initial form of the state; it does not get updated as things
* happen. The mutable information that gets updated in runtime are carried in
- * a circpad_machine_state_t.
+ * a circpad_machine_runtime_t.
*
* This struct describes the histograms and parameters of a single
* state in the adaptive padding machine. Instances of this struct
@@ -248,38 +281,60 @@ typedef uint16_t circpad_statenum_t;
* or the consensus.
*/
typedef struct circpad_state_t {
- /** If a histogram is used for this state, this specifies the number of bins
- * of this histogram. Histograms must have at least 2 bins.
+ /**
+ * If a histogram is used for this state, this specifies the number of bins
+ * of this histogram. Histograms must have at least 2 bins.
+ *
+ * In particular, the following histogram:
+ *
+ * Tokens
+ * +
+ * 10 | +----+
+ * 9 | | | +---------+
+ * 8 | | | | |
+ * 7 | | | +-----+ |
+ * 6 +----+ Bin+-----+ | +---------------+
+ * 5 | | #1 | | | | |
+ * | Bin| | Bin | Bin | Bin #4 | Bin #5 |
+ * | #0 | | #2 | #3 | | (infinity bin)|
+ * | | | | | | |
+ * | | | | | | |
+ * 0 +----+----+-----+-----+---------+---------------+
+ * 0 100 200 350 500 1000 ∞ microseconds
*
- * If a delay probability distribution is used for this state, this is set
- * to 0. */
+ * would be specified the following way:
+ * histogram_len = 6;
+ * histogram[] = { 6, 10, 6, 7, 9, 6 }
+ * histogram_edges[] = { 0, 100, 200, 350, 500, 1000 }
+ *
+ * The final bin is called the "infinity bin" and if it's chosen we don't
+ * schedule any padding. The infinity bin is strange because its lower edge
+ * is the max value of possible non-infinite delay allowed by this histogram,
+ * and its upper edge is CIRCPAD_DELAY_INFINITE. You can tell if the infinity
+ * bin is chosen by inspecting its bin index or inspecting its upper edge.
+ *
+ * If a delay probability distribution is used for this state, this is set
+ * to 0. */
circpad_hist_index_t histogram_len;
/** The histogram itself: an array of uint16s of tokens, whose
- * widths are exponentially spaced, in microseconds */
+ * widths are exponentially spaced, in microseconds.
+ *
+ * This array must have histogram_len elements that are strictly
+ * monotonically increasing. */
circpad_hist_token_t histogram[CIRCPAD_MAX_HISTOGRAM_LEN];
+ /* The histogram bin edges in usec.
+ *
+ * Each element of this array specifies the left edge of the corresponding
+ * bin. The rightmost edge is always infinity and is not specified in this
+ * array.
+ *
+ * This array must have histogram_len elements. */
+ circpad_delay_t histogram_edges[CIRCPAD_MAX_HISTOGRAM_LEN+1];
/** Total number of tokens in this histogram. This is a constant and is *not*
* decremented every time we spend a token. It's used for initializing and
* refilling the histogram. */
uint32_t histogram_total_tokens;
- /** Minimum padding delay of this state in microseconds.
- *
- * If histograms are used, this is the left (and right) bound of the first
- * bin (since it has zero width).
- *
- * If a delay probability distribution is used, this represents the minimum
- * delay we can sample from the distribution.
- */
- circpad_delay_t start_usec;
-
- /** If histograms are used, this is the width of the whole histogram in
- * microseconds, and it's used to calculate individual bin width.
- *
- * If a delay probability distribution is used, this is used as the max
- * delay we can sample from the distribution.
- */
- circpad_delay_t range_usec;
-
/**
* Represents a delay probability distribution (aka IAT distribution). It's a
* parametrized way of encoding inter-packet delay information in
@@ -292,6 +347,16 @@ typedef struct circpad_state_t {
* results of sampling from this distribution (range_sec is used as a max).
*/
circpad_distribution_t iat_dist;
+ /* If a delay probability distribution is used, this is used as the max
+ * value we can sample from the distribution. However, RTT measurements and
+ * dist_added_shift gets applied on top of this value to derive the final
+ * padding delay. */
+ circpad_delay_t dist_max_sample_usec;
+ /* If a delay probability distribution is used and this is set, we will add
+ * this value on top of the value sampled from the IAT distribution to
+ * derive the final padding delay (We also add the RTT measurement if it's
+ * enabled.). */
+ circpad_delay_t dist_added_shift_usec;
/**
* The length dist is a parameterized way of encoding how long this
@@ -430,7 +495,7 @@ typedef struct circpad_state_t {
*
* XXX: Play with layout to minimize space on x64 Linux (most common relay).
*/
-typedef struct circpad_machine_state_t {
+typedef struct circpad_machine_runtime_t {
/** The callback pointer for the padding callbacks.
*
* These timers stick around the machineinfo until the machineinfo's circuit
@@ -468,6 +533,14 @@ typedef struct circpad_machine_state_t {
uint16_t nonpadding_sent;
/**
+ * Timestamp of the most recent cell event (sent, received, padding,
+ * non-padding), in seconds from approx_time().
+ *
+ * Used as an emergency break to stop holding padding circuits open.
+ */
+ time_t last_cell_time_sec;
+
+ /**
* EWMA estimate of the RTT of the circuit from this hop
* to the exit end, in microseconds. */
circpad_delay_t rtt_estimate_usec;
@@ -514,7 +587,7 @@ typedef struct circpad_machine_state_t {
* CIRCPAD_MAX_MACHINES define). */
unsigned machine_index : 1;
-} circpad_machine_state_t;
+} circpad_machine_runtime_t;
/** Helper macro to get an actual state machine from a machineinfo */
#define CIRCPAD_GET_MACHINE(machineinfo) \
@@ -530,6 +603,9 @@ typedef uint8_t circpad_machine_num_t;
/** Global state machine structure from the consensus */
typedef struct circpad_machine_spec_t {
+ /* Just a user-friendly machine name for logs */
+ const char *name;
+
/** Global machine number */
circpad_machine_num_t machine_num;
@@ -548,6 +624,19 @@ typedef struct circpad_machine_spec_t {
* 1-indexed (ie: hop #1 is guard, #2 middle, #3 exit). */
unsigned target_hopnum : 3;
+ /** If this flag is enabled, don't close circuits that use this machine even
+ * if another part of Tor wants to close this circuit.
+ *
+ * If this flag is set, the circuitpadding subsystem will close circuits the
+ * moment the machine transitions to the END state, and only if the circuit
+ * has already been asked to be closed by another part of Tor.
+ *
+ * Circuits that should have been closed but were kept open by a padding
+ * machine are re-purposed to CIRCUIT_PURPOSE_C_CIRCUIT_PADDING, hence
+ * machines should take that purpose into account if they are filtering
+ * circuits by purpose. */
+ unsigned manage_circ_lifetime : 1;
+
/** This machine only kills fascists if the following conditions are met. */
circpad_machine_conditions_t conditions;
@@ -573,6 +662,8 @@ typedef struct circpad_machine_spec_t {
void circpad_new_consensus_params(const networkstatus_t *ns);
+int circpad_marked_circuit_for_padding(circuit_t *circ, int reason);
+
/**
* The following are event call-in points that are of interest to
* the state machines. They are called during cell processing. */
@@ -592,11 +683,11 @@ void circpad_cell_event_padding_received(struct circuit_t *on_circ);
/** Internal events are events the machines send to themselves */
circpad_decision_t
-circpad_internal_event_infinity(circpad_machine_state_t *mi);
+circpad_internal_event_infinity(circpad_machine_runtime_t *mi);
circpad_decision_t
-circpad_internal_event_bins_empty(circpad_machine_state_t *);
+circpad_internal_event_bins_empty(circpad_machine_runtime_t *);
circpad_decision_t circpad_internal_event_state_length_up(
- circpad_machine_state_t *);
+ circpad_machine_runtime_t *);
/** Machine creation events are events that cause us to set up or
* tear down padding state machines. */
@@ -610,6 +701,8 @@ circpad_machine_event_circ_has_no_relay_early(struct origin_circuit_t *circ);
void circpad_machines_init(void);
void circpad_machines_free(void);
+void circpad_register_padding_machine(circpad_machine_spec_t *machine,
+ smartlist_t *machine_list);
void circpad_machine_states_init(circpad_machine_spec_t *machine,
circpad_statenum_t num_states);
@@ -638,59 +731,78 @@ bool circpad_padding_negotiated(struct circuit_t *circ,
uint8_t command,
uint8_t response);
+circpad_purpose_mask_t circpad_circ_purpose_to_mask(uint8_t circ_purpose);
+
MOCK_DECL(circpad_decision_t,
-circpad_machine_schedule_padding,(circpad_machine_state_t *));
+circpad_machine_schedule_padding,(circpad_machine_runtime_t *));
MOCK_DECL(circpad_decision_t,
-circpad_machine_spec_transition, (circpad_machine_state_t *mi,
+circpad_machine_spec_transition, (circpad_machine_runtime_t *mi,
circpad_event_t event));
circpad_decision_t circpad_send_padding_cell_for_callback(
- circpad_machine_state_t *mi);
+ circpad_machine_runtime_t *mi);
+
+void circpad_free_all(void);
#ifdef CIRCUITPADDING_PRIVATE
+STATIC void machine_spec_free_(circpad_machine_spec_t *m);
+#define machine_spec_free(chan) \
+ FREE_AND_NULL(circpad_machine_spec_t,machine_spec_free_, (m))
+
STATIC circpad_delay_t
-circpad_machine_sample_delay(circpad_machine_state_t *mi);
+circpad_machine_sample_delay(circpad_machine_runtime_t *mi);
STATIC bool
-circpad_machine_reached_padding_limit(circpad_machine_state_t *mi);
-
-STATIC
-circpad_decision_t circpad_machine_remove_token(circpad_machine_state_t *mi);
+circpad_machine_reached_padding_limit(circpad_machine_runtime_t *mi);
STATIC circpad_delay_t
-circpad_histogram_bin_to_usec(const circpad_machine_state_t *mi,
+circpad_histogram_bin_to_usec(const circpad_machine_runtime_t *mi,
circpad_hist_index_t bin);
STATIC const circpad_state_t *
-circpad_machine_current_state(const circpad_machine_state_t *mi);
+circpad_machine_current_state(const circpad_machine_runtime_t *mi);
+
+STATIC void circpad_machine_remove_token(circpad_machine_runtime_t *mi);
STATIC circpad_hist_index_t circpad_histogram_usec_to_bin(
- const circpad_machine_state_t *mi,
+ const circpad_machine_runtime_t *mi,
circpad_delay_t us);
-STATIC circpad_machine_state_t *circpad_circuit_machineinfo_new(
+STATIC circpad_machine_runtime_t *circpad_circuit_machineinfo_new(
struct circuit_t *on_circ,
int machine_index);
-STATIC void circpad_machine_remove_higher_token(circpad_machine_state_t *mi,
+STATIC void circpad_machine_remove_higher_token(circpad_machine_runtime_t *mi,
circpad_delay_t target_bin_us);
-STATIC void circpad_machine_remove_lower_token(circpad_machine_state_t *mi,
+STATIC void circpad_machine_remove_lower_token(circpad_machine_runtime_t *mi,
circpad_delay_t target_bin_us);
-STATIC void circpad_machine_remove_closest_token(circpad_machine_state_t *mi,
+STATIC void circpad_machine_remove_closest_token(circpad_machine_runtime_t *mi,
circpad_delay_t target_bin_us,
bool use_usec);
-STATIC void circpad_machine_setup_tokens(circpad_machine_state_t *mi);
+STATIC void circpad_machine_setup_tokens(circpad_machine_runtime_t *mi);
MOCK_DECL(STATIC signed_error_t,
circpad_send_command_to_hop,(struct origin_circuit_t *circ, uint8_t hopnum,
uint8_t relay_command, const uint8_t *payload,
ssize_t payload_len));
+MOCK_DECL(STATIC const node_t *,
+circuit_get_nth_node,(origin_circuit_t *circ, int hop));
+
+STATIC circpad_delay_t
+histogram_get_bin_upper_bound(const circpad_machine_runtime_t *mi,
+ circpad_hist_index_t bin);
+
+STATIC void
+circpad_add_matching_machines(origin_circuit_t *on_circ,
+ smartlist_t *machines_sl);
+
#ifdef TOR_UNIT_TESTS
extern smartlist_t *origin_padding_machines;
extern smartlist_t *relay_padding_machines;
-#endif
#endif
-#endif
+#endif /* defined(CIRCUITPADDING_PRIVATE) */
+
+#endif /* !defined(TOR_CIRCUITPADDING_H) */
diff --git a/src/core/or/circuitpadding_machines.c b/src/core/or/circuitpadding_machines.c
new file mode 100644
index 0000000000..75d2614aca
--- /dev/null
+++ b/src/core/or/circuitpadding_machines.c
@@ -0,0 +1,458 @@
+/* Copyright (c) 2019 The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file circuitpadding_machines.c
+ * \brief Circuit padding state machines
+ *
+ * \detail
+ *
+ * Introduce circuit padding machines that will be used by Tor circuits, as
+ * specified by proposal 302 "Hiding onion service clients using padding".
+ *
+ * Right now this file introduces two machines that aim to hide the client-side
+ * of onion service circuits against naive classifiers like the ones from the
+ * "Circuit Fingerprinting Attacks: Passive Deanonymization of Tor Hidden
+ * Services" paper from USENIX. By naive classifiers we mean classifiers that
+ * use basic features like "circuit construction circuits" and "incoming and
+ * outgoing cell counts" and "duration of activity".
+ *
+ * In particular, these machines aim to be lightweight and protect against
+ * these basic classifiers. They don't aim to protect against more advanced
+ * attacks that use deep learning or even correlate various circuit
+ * construction events together. Machines that fool such advanced classifiers
+ * are also possible, but they can't be so lightweight and might require more
+ * WTF-PAD features. So for now we opt for the following two machines:
+ *
+ * Client-side introduction circuit hiding machine:
+ *
+ * This machine hides client-side introduction circuits by making their
+ * circuit consruction sequence look like normal general circuits that
+ * download directory information. Furthermore, the circuits are kept open
+ * until all the padding has been sent, since intro circuits are usually
+ * very short lived and this act as a distinguisher. For more info see
+ * circpad_machine_client_hide_intro_circuits() and the sec.
+ *
+ * Client-side rendezvous circuit hiding machine:
+ *
+ * This machine hides client-side rendezvous circuits by making their
+ * circuit construction sequence look like normal general circuits. For more
+ * details see circpad_machine_client_hide_rend_circuits() and the spec.
+ *
+ * TODO: These are simple machines that carefully manipulate the cells of the
+ * initial circuit setup procedure to make them look like general
+ * circuits. In the future, more states can be baked into their state machine
+ * to do more advanced obfuscation.
+ **/
+
+#define CIRCUITPADDING_MACHINES_PRIVATE
+
+#include "core/or/or.h"
+#include "feature/nodelist/networkstatus.h"
+
+#include "lib/crypt_ops/crypto_rand.h"
+
+#include "core/or/circuitlist.h"
+
+#include "core/or/circuitpadding_machines.h"
+#include "core/or/circuitpadding.h"
+
+/** Create a client-side padding machine that aims to hide IP circuits. In
+ * particular, it keeps intro circuits alive until a bunch of fake traffic has
+ * been pushed through.
+ */
+void
+circpad_machine_client_hide_intro_circuits(smartlist_t *machines_sl)
+{
+ circpad_machine_spec_t *client_machine
+ = tor_malloc_zero(sizeof(circpad_machine_spec_t));
+
+ client_machine->name = "client_ip_circ";
+
+ client_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED;
+ client_machine->target_hopnum = 2;
+
+ /* This is a client machine */
+ client_machine->is_origin_side = 1;
+
+ /* We only want to pad introduction circuits, and we want to start padding
+ * only after the INTRODUCE1 cell has been sent, so set the purposes
+ * appropriately.
+ *
+ * In particular we want introduction circuits to blend as much as possible
+ * with general circuits. Most general circuits have the following initial
+ * relay cell sequence (outgoing cells marked in [brackets]):
+ *
+ * [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2 -> [BEGIN] -> CONNECTED
+ * -> [DATA] -> [DATA] -> DATA -> DATA...(inbound data cells continue)
+ *
+ * Whereas normal introduction circuits usually look like:
+ *
+ * [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2
+ * -> [INTRO1] -> INTRODUCE_ACK
+ *
+ * This means that up to the sixth cell (first line of each sequence above),
+ * both general and intro circuits have identical cell sequences. After that
+ * we want to mimic the second line sequence of
+ * -> [DATA] -> [DATA] -> DATA -> DATA...(inbound data cells continue)
+ *
+ * We achieve this by starting padding INTRODUCE1 has been sent. With padding
+ * negotiation cells, in the common case of the second line looks like:
+ * -> [INTRO1] -> [PADDING_NEGOTIATE] -> PADDING_NEGOTIATED -> INTRO_ACK
+ *
+ * Then, the middle node will send between INTRO_MACHINE_MINIMUM_PADDING and
+ * INTRO_MACHINE_MAXIMUM_PADDING cells, to match the "...(inbound data cells
+ * continue)" portion of the trace (aka the rest of an HTTPS response body).
+ */
+ client_machine->conditions.purpose_mask =
+ circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)|
+ circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACKED)|
+ circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_CIRCUIT_PADDING);
+
+ /* Keep the circuit alive even after the introduction has been finished,
+ * otherwise the short-term lifetime of the circuit will blow our cover */
+ client_machine->manage_circ_lifetime = 1;
+
+ /* Set padding machine limits to help guard against excessive padding */
+ client_machine->allowed_padding_count = INTRO_MACHINE_MAXIMUM_PADDING;
+ client_machine->max_padding_percent = 1;
+
+ /* Two states: START, OBFUSCATE_CIRC_SETUP (and END) */
+ circpad_machine_states_init(client_machine, 2);
+
+ /* For the origin-side machine, we transition to OBFUSCATE_CIRC_SETUP after
+ * sending PADDING_NEGOTIATE, and we stay there (without sending any padding)
+ * until we receive a STOP from the other side. */
+ client_machine->states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_NONPADDING_SENT] =
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP;
+
+ /* origin-side machine has no event reactions while in
+ * CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP, so no more state transitions here. */
+
+ /* The client side should never send padding, so it does not need
+ * to specify token removal, or a histogram definition or state lengths.
+ * That is all controlled by the middle node. */
+
+ /* Register the machine */
+ client_machine->machine_num = smartlist_len(machines_sl);
+ circpad_register_padding_machine(client_machine, machines_sl);
+
+ log_info(LD_CIRC,
+ "Registered client intro point hiding padding machine (%u)",
+ client_machine->machine_num);
+}
+
+/** Create a relay-side padding machine that aims to hide IP circuits. See
+ * comments on the function above for more details on the workings of the
+ * machine. */
+void
+circpad_machine_relay_hide_intro_circuits(smartlist_t *machines_sl)
+{
+ circpad_machine_spec_t *relay_machine
+ = tor_malloc_zero(sizeof(circpad_machine_spec_t));
+
+ relay_machine->name = "relay_ip_circ";
+
+ relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED;
+ relay_machine->target_hopnum = 2;
+
+ /* This is a relay-side machine */
+ relay_machine->is_origin_side = 0;
+
+ /* We want to negotiate END from this side after all our padding is done, so
+ * that the origin-side machine goes into END state, and eventually closes
+ * the circuit. */
+ relay_machine->should_negotiate_end = 1;
+
+ /* Set padding machine limits to help guard against excessive padding */
+ relay_machine->allowed_padding_count = INTRO_MACHINE_MAXIMUM_PADDING;
+ relay_machine->max_padding_percent = 1;
+
+ /* Two states: START, OBFUSCATE_CIRC_SETUP (and END) */
+ circpad_machine_states_init(relay_machine, 2);
+
+ /* For the relay-side machine, we want to transition
+ * START -> OBFUSCATE_CIRC_SETUP upon first non-padding
+ * cell sent (PADDING_NEGOTIATED in this case). */
+ relay_machine->states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_NONPADDING_SENT] =
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP;
+
+ /* For the relay-side, we want to transition from OBFUSCATE_CIRC_SETUP to END
+ * state when the length finishes. */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END;
+
+ /* Now let's define the OBF -> OBF transitions that maintain our padding
+ * flow:
+ *
+ * For the relay-side machine, we want to keep on sending padding bytes even
+ * when nothing else happens on this circuit. */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ next_state[CIRCPAD_EVENT_PADDING_SENT] =
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP;
+ /* For the relay-side machine, we need this transition so that we re-enter
+ the state, after PADDING_NEGOTIATED is sent. Otherwise, the remove token
+ function will disable the timer, and nothing will restart it since there
+ is no other motion on an intro circuit. */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ next_state[CIRCPAD_EVENT_NONPADDING_SENT] =
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP;
+
+ /* Token removal strategy for OBFUSCATE_CIRC_SETUP state: Don't
+ * remove any tokens.
+ *
+ * We rely on the state length sampling and not token removal, to avoid
+ * the mallocs required to copy the histograms for token removal,
+ * and to avoid monotime calls needed to determine histogram
+ * bins for token removal. */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ token_removal = CIRCPAD_TOKEN_REMOVAL_NONE;
+
+ /* Figure out the length of the OBFUSCATE_CIRC_SETUP state so that it's
+ * randomized. The relay side will send between INTRO_MACHINE_MINIMUM_PADDING
+ * and INTRO_MACHINE_MAXIMUM_PADDING padding cells towards the client. */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ length_dist.type = CIRCPAD_DIST_UNIFORM;
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ length_dist.param1 = INTRO_MACHINE_MINIMUM_PADDING;
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ length_dist.param2 = INTRO_MACHINE_MAXIMUM_PADDING;
+
+ /* Configure histogram */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_len = 2;
+
+ /* For the relay-side machine we want to batch padding instantly to pretend
+ * its an incoming directory download. So set the histogram edges tight:
+ * (1, 10ms, infinity). */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_edges[0] = 1000;
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_edges[1] = 10000;
+
+ /* We put all our tokens in bin 0, which means we want 100% probability
+ * for choosing a inter-packet delay of between 1000 and 10000 microseconds
+ * (1 to 10ms). Since we only have 1 bin, it doesn't matter how many tokens
+ * there are, 1000 out of 1000 is 100% */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram[0] = 1000;
+
+ /* just one bin, so setup the total tokens */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_total_tokens =
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].histogram[0];
+
+ /* Register the machine */
+ relay_machine->machine_num = smartlist_len(machines_sl);
+ circpad_register_padding_machine(relay_machine, machines_sl);
+
+ log_info(LD_CIRC,
+ "Registered relay intro circuit hiding padding machine (%u)",
+ relay_machine->machine_num);
+}
+
+/************************** Rendezvous-circuit machine ***********************/
+
+/** Create a client-side padding machine that aims to hide rendezvous
+ * circuits.*/
+void
+circpad_machine_client_hide_rend_circuits(smartlist_t *machines_sl)
+{
+ circpad_machine_spec_t *client_machine
+ = tor_malloc_zero(sizeof(circpad_machine_spec_t));
+
+ client_machine->name = "client_rp_circ";
+
+ /* Only pad after the circuit has been built and pad to the middle */
+ client_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED;
+ client_machine->target_hopnum = 2;
+
+ /* This is a client machine */
+ client_machine->is_origin_side = 1;
+
+ /* We only want to pad rendezvous circuits, and we want to start padding only
+ * after the rendezvous circuit has been established.
+ *
+ * Following a similar argument as for intro circuits, we are aiming for
+ * padded rendezvous circuits to blend in with the initial cell sequence of
+ * general circuits which usually look like this:
+ *
+ * [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2 -> [BEGIN] -> CONNECTED
+ * -> [DATA] -> [DATA] -> DATA -> DATA...(incoming cells continue)
+ *
+ * Whereas normal rendezvous circuits usually look like:
+ *
+ * [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2 -> [EST_REND] -> REND_EST
+ * -> REND2 -> [BEGIN]
+ *
+ * This means that up to the sixth cell (in the first line), both general and
+ * rend circuits have identical cell sequences.
+ *
+ * After that we want to mimic a [DATA] -> [DATA] -> DATA -> DATA sequence.
+ *
+ * With padding negotiation right after the REND_ESTABLISHED, the sequence
+ * becomes:
+ *
+ * [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2 -> [EST_REND] -> REND_EST
+ * -> [PADDING_NEGOTIATE] -> [DROP] -> PADDING_NEGOTIATED -> DROP...
+ *
+ * After which normal application DATA cells continue on the circuit.
+ *
+ * Hence this way we make rendezvous circuits look like general circuits up
+ * till the end of the circuit setup. */
+ client_machine->conditions.purpose_mask =
+ circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_JOINED)|
+ circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_READY)|
+ circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
+
+ /* Set padding machine limits to help guard against excessive padding */
+ client_machine->allowed_padding_count = 1;
+ client_machine->max_padding_percent = 1;
+
+ /* Two states: START, OBFUSCATE_CIRC_SETUP (and END) */
+ circpad_machine_states_init(client_machine, 2);
+
+ /* START -> OBFUSCATE_CIRC_SETUP transition upon sending the first
+ * non-padding cell (which is PADDING_NEGOTIATE) */
+ client_machine->states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_NONPADDING_SENT] =
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP;
+
+ /* OBFUSCATE_CIRC_SETUP -> END transition when we send our first
+ * padding packet and/or hit the state length (the state length is 1). */
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ next_state[CIRCPAD_EVENT_PADDING_RECV] = CIRCPAD_STATE_END;
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END;
+
+ /* Don't use a token removal strategy since we don't want to use monotime
+ * functions and we want to avoid mallocing histogram copies. We want
+ * this machine to be light. */
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ token_removal = CIRCPAD_TOKEN_REMOVAL_NONE;
+
+ /* Instead, to control the volume of padding (we just want to send a single
+ * padding cell) we will use a static state length. We just want one token,
+ * since we want to make the following pattern:
+ * [PADDING_NEGOTIATE] -> [DROP] -> PADDING_NEGOTIATED -> DROP */
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ length_dist.type = CIRCPAD_DIST_UNIFORM;
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ length_dist.param1 = 1;
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ length_dist.param2 = 2; // rand(1,2) is always 1
+
+ /* Histogram is: (0 msecs, 1 msec, infinity). We want this to be fast so
+ * that we send our outgoing [DROP] before the PADDING_NEGOTIATED comes
+ * back from the relay side. */
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_len = 2;
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_edges[0] = 0;
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_edges[1] = 1000;
+
+ /* We want a 100% probability of choosing an inter-packet delay of
+ * between 0 and 1ms. Since we don't use token removal,
+ * the number of tokens does not matter. (And also, state_length
+ * governs how many packets we send). */
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram[0] = 1;
+ client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_total_tokens = 1;
+
+ /* Register the machine */
+ client_machine->machine_num = smartlist_len(machines_sl);
+ circpad_register_padding_machine(client_machine, machines_sl);
+
+ log_info(LD_CIRC,
+ "Registered client rendezvous circuit hiding padding machine (%u)",
+ client_machine->machine_num);
+}
+
+/** Create a relay-side padding machine that aims to hide IP circuits.
+ *
+ * This is meant to follow the client-side machine.
+ */
+void
+circpad_machine_relay_hide_rend_circuits(smartlist_t *machines_sl)
+{
+ circpad_machine_spec_t *relay_machine
+ = tor_malloc_zero(sizeof(circpad_machine_spec_t));
+
+ relay_machine->name = "relay_rp_circ";
+
+ /* Only pad after the circuit has been built and pad to the middle */
+ relay_machine->conditions.min_hops = 2;
+ relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED;
+ relay_machine->target_hopnum = 2;
+
+ /* This is a relay-side machine */
+ relay_machine->is_origin_side = 0;
+
+ /* Set padding machine limits to help guard against excessive padding */
+ relay_machine->allowed_padding_count = 1;
+ relay_machine->max_padding_percent = 1;
+
+ /* Two states: START, OBFUSCATE_CIRC_SETUP (and END) */
+ circpad_machine_states_init(relay_machine, 2);
+
+ /* START -> OBFUSCATE_CIRC_SETUP transition upon sending the first
+ * non-padding cell (which is PADDING_NEGOTIATED) */
+ relay_machine->states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_NONPADDING_SENT] =
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP;
+
+ /* OBFUSCATE_CIRC_SETUP -> END transition when we send our first
+ * padding packet and/or hit the state length (the state length is 1). */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ next_state[CIRCPAD_EVENT_PADDING_RECV] = CIRCPAD_STATE_END;
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END;
+
+ /* Don't use a token removal strategy since we don't want to use monotime
+ * functions and we want to avoid mallocing histogram copies. We want
+ * this machine to be light. */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ token_removal = CIRCPAD_TOKEN_REMOVAL_NONE;
+
+ /* Instead, to control the volume of padding (we just want to send a single
+ * padding cell) we will use a static state length. We just want one token,
+ * since we want to make the following pattern:
+ * [PADDING_NEGOTIATE] -> [DROP] -> PADDING_NEGOTIATED -> DROP */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ length_dist.type = CIRCPAD_DIST_UNIFORM;
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ length_dist.param1 = 1;
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ length_dist.param2 = 2; // rand(1,2) is always 1
+
+ /* Histogram is: (0 msecs, 1 msec, infinity). We want this to be fast so
+ * that the outgoing DROP cell is sent immediately after the
+ * PADDING_NEGOTIATED. */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_len = 2;
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_edges[0] = 0;
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_edges[1] = 1000;
+
+ /* We want a 100% probability of choosing an inter-packet delay of
+ * between 0 and 1ms. Since we don't use token removal,
+ * the number of tokens does not matter. (And also, state_length
+ * governs how many packets we send). */
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram[0] = 1;
+ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
+ histogram_total_tokens = 1;
+
+ /* Register the machine */
+ relay_machine->machine_num = smartlist_len(machines_sl);
+ circpad_register_padding_machine(relay_machine, machines_sl);
+
+ log_info(LD_CIRC,
+ "Registered relay rendezvous circuit hiding padding machine (%u)",
+ relay_machine->machine_num);
+}
diff --git a/src/core/or/circuitpadding_machines.h b/src/core/or/circuitpadding_machines.h
new file mode 100644
index 0000000000..3c9798d42d
--- /dev/null
+++ b/src/core/or/circuitpadding_machines.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2018 The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file circuitpadding_machines.h
+ * \brief Header file for circuitpadding_machines.c.
+ **/
+
+#ifndef TOR_CIRCUITPADDING_MACHINES_H
+#define TOR_CIRCUITPADDING_MACHINES_H
+
+void circpad_machine_relay_hide_intro_circuits(smartlist_t *machines_sl);
+void circpad_machine_client_hide_intro_circuits(smartlist_t *machines_sl);
+void circpad_machine_relay_hide_rend_circuits(smartlist_t *machines_sl);
+void circpad_machine_client_hide_rend_circuits(smartlist_t *machines_sl);
+
+#ifdef CIRCUITPADDING_MACHINES_PRIVATE
+
+/** State of the padding machines that actually sends padding */
+#define CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP CIRCPAD_STATE_BURST
+
+/** Constants defining the amount of padding that a machine will send to hide
+ * HS circuits. The actual value is sampled uniformly random between the
+ * min/max values.
+ */
+
+/** Minimum number of relay-side padding cells to be sent by this machine */
+#define INTRO_MACHINE_MINIMUM_PADDING 7
+/** Maximum number of relay-side padding cells to be sent by this machine.
+ * The actual value will be sampled between the min and max.*/
+#define INTRO_MACHINE_MAXIMUM_PADDING 10
+
+#endif /* defined(CIRCUITPADDING_MACHINES_PRIVATE) */
+
+#endif /* !defined(TOR_CIRCUITPADDING_MACHINES_H) */
diff --git a/src/core/or/circuitstats.c b/src/core/or/circuitstats.c
index c6ea2fff97..03eea1d779 100644
--- a/src/core/or/circuitstats.c
+++ b/src/core/or/circuitstats.c
@@ -30,7 +30,7 @@
#include "core/or/circuitstats.h"
#include "app/config/config.h"
#include "app/config/confparse.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
@@ -44,6 +44,7 @@
#include "lib/time/tvdiff.h"
#include "lib/encoding/confline.h"
#include "feature/dirauth/authmode.h"
+#include "feature/relay/relay_periodic.h"
#include "core/or/crypt_path_st.h"
#include "core/or/origin_circuit_st.h"
@@ -1420,6 +1421,7 @@ void
circuit_build_times_network_is_live(circuit_build_times_t *cbt)
{
time_t now = approx_time();
+ // XXXX this should use pubsub
if (cbt->liveness.nonlive_timeouts > 0) {
time_t time_since_live = now - cbt->liveness.network_last_live;
log_notice(LD_CIRC,
diff --git a/src/core/or/circuituse.c b/src/core/or/circuituse.c
index 3e604adcfc..18b419e99d 100644
--- a/src/core/or/circuituse.c
+++ b/src/core/or/circuituse.c
@@ -42,7 +42,7 @@
#include "feature/client/bridges.h"
#include "feature/client/circpathbias.h"
#include "feature/client/entrynodes.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dircommon/directory.h"
#include "feature/hs/hs_circuit.h"
#include "feature/hs/hs_client.h"
@@ -70,7 +70,7 @@
#include "core/or/origin_circuit_st.h"
#include "core/or/socks_request_st.h"
-static void circuit_expire_old_circuits_clientside(void);
+STATIC void circuit_expire_old_circuits_clientside(void);
static void circuit_increment_failure_count(void);
/** Check whether the hidden service destination of the stream at
@@ -178,7 +178,6 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
purpose == CIRCUIT_PURPOSE_C_HSDIR_GET) {
tor_addr_t addr;
- const int family = tor_addr_parse(&addr, conn->socks_request->address);
if (!exitnode && !build_state->onehop_tunnel) {
log_debug(LD_CIRC,"Not considering circuit with unknown router.");
return 0; /* this circuit is screwed and doesn't know it yet,
@@ -199,6 +198,8 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
return 0; /* this is a circuit to somewhere else */
if (tor_digest_is_zero(digest)) {
/* we don't know the digest; have to compare addr:port */
+ const int family = tor_addr_parse(&addr,
+ conn->socks_request->address);
if (family < 0 ||
!tor_addr_eq(&build_state->chosen_exit->addr, &addr) ||
build_state->chosen_exit->port != conn->socks_request->port)
@@ -211,12 +212,14 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
return 0;
}
}
- if (origin_circ->prepend_policy && family != -1) {
- int r = compare_tor_addr_to_addr_policy(&addr,
- conn->socks_request->port,
- origin_circ->prepend_policy);
- if (r == ADDR_POLICY_REJECTED)
- return 0;
+ if (origin_circ->prepend_policy) {
+ if (tor_addr_parse(&addr, conn->socks_request->address) != -1) {
+ int r = compare_tor_addr_to_addr_policy(&addr,
+ conn->socks_request->port,
+ origin_circ->prepend_policy);
+ if (r == ADDR_POLICY_REJECTED)
+ return 0;
+ }
}
if (exitnode && !connection_ap_can_use_exit(conn, exitnode)) {
/* can't exit from this router */
@@ -1471,7 +1474,7 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
/** Find each circuit that has been unused for too long, or dirty
* for too long and has no streams on it: mark it for close.
*/
-static void
+STATIC void
circuit_expire_old_circuits_clientside(void)
{
struct timeval cutoff, now;
@@ -1511,6 +1514,7 @@ circuit_expire_old_circuits_clientside(void)
circ->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT ||
circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
circ->purpose == CIRCUIT_PURPOSE_TESTING ||
+ circ->purpose == CIRCUIT_PURPOSE_C_CIRCUIT_PADDING ||
(circ->purpose >= CIRCUIT_PURPOSE_C_INTRODUCING &&
circ->purpose <= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) ||
circ->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) {
@@ -3127,7 +3131,9 @@ circuit_sent_valid_data(origin_circuit_t *circ, uint16_t relay_body_len)
{
if (!circ) return;
- tor_assert_nonfatal(relay_body_len <= RELAY_PAYLOAD_SIZE);
+ tor_assertf_nonfatal(relay_body_len <= RELAY_PAYLOAD_SIZE,
+ "Wrong relay_body_len: %d (should be at most %d)",
+ relay_body_len, RELAY_PAYLOAD_SIZE);
circ->n_delivered_written_circ_bw =
tor_add_u32_nowrap(circ->n_delivered_written_circ_bw, relay_body_len);
diff --git a/src/core/or/command.c b/src/core/or/command.c
index 5fb6640c22..77e5447ce3 100644
--- a/src/core/or/command.c
+++ b/src/core/or/command.c
@@ -49,7 +49,7 @@
#include "core/or/dos.h"
#include "core/or/onion.h"
#include "core/or/relay.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/hibernate/hibernate.h"
#include "feature/nodelist/describe.h"
#include "feature/nodelist/nodelist.h"
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
index cc240bdc98..c08d2a9ff5 100644
--- a/src/core/or/connection_edge.c
+++ b/src/core/or/connection_edge.c
@@ -73,12 +73,13 @@
#include "core/or/policies.h"
#include "core/or/reasons.h"
#include "core/or/relay.h"
+#include "core/or/sendme.h"
#include "core/proto/proto_http.h"
#include "core/proto/proto_socks.h"
#include "feature/client/addressmap.h"
#include "feature/client/circpathbias.h"
#include "feature/client/dnsserv.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dircache/dirserv.h"
#include "feature/dircommon/directory.h"
#include "feature/hibernate/hibernate.h"
@@ -767,7 +768,7 @@ connection_edge_flushed_some(edge_connection_t *conn)
/* falls through. */
case EXIT_CONN_STATE_OPEN:
- connection_edge_consider_sending_sendme(conn);
+ sendme_connection_edge_consider_sending(conn);
break;
}
return 0;
@@ -791,7 +792,7 @@ connection_edge_finished_flushing(edge_connection_t *conn)
switch (conn->base_.state) {
case AP_CONN_STATE_OPEN:
case EXIT_CONN_STATE_OPEN:
- connection_edge_consider_sending_sendme(conn);
+ sendme_connection_edge_consider_sending(conn);
return 0;
case AP_CONN_STATE_SOCKS_WAIT:
case AP_CONN_STATE_NATD_WAIT:
@@ -2810,6 +2811,31 @@ connection_ap_process_natd(entry_connection_t *conn)
return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
}
+static const char HTTP_CONNECT_IS_NOT_AN_HTTP_PROXY_MSG[] =
+ "HTTP/1.0 405 Method Not Allowed\r\n"
+ "Content-Type: text/html; charset=iso-8859-1\r\n\r\n"
+ "<html>\n"
+ "<head>\n"
+ "<title>This is an HTTP CONNECT tunnel, not a full HTTP Proxy</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "<h1>This is an HTTP CONNECT tunnel, not an HTTP proxy.</h1>\n"
+ "<p>\n"
+ "It appears you have configured your web browser to use this Tor port as\n"
+ "an HTTP proxy.\n"
+ "</p><p>\n"
+ "This is not correct: This port is configured as a CONNECT tunnel, not\n"
+ "an HTTP proxy. Please configure your client accordingly. You can also\n"
+ "use HTTPS; then the client should automatically use HTTP CONNECT."
+ "</p>\n"
+ "<p>\n"
+ "See <a href=\"https://www.torproject.org/documentation.html\">"
+ "https://www.torproject.org/documentation.html</a> for more "
+ "information.\n"
+ "</p>\n"
+ "</body>\n"
+ "</html>\n";
+
/** Called on an HTTP CONNECT entry connection when some bytes have arrived,
* but we have not yet received a full HTTP CONNECT request. Try to parse an
* HTTP CONNECT request from the connection's inbuf. On success, set up the
@@ -2850,7 +2876,7 @@ connection_ap_process_http_connect(entry_connection_t *conn)
tor_assert(command);
tor_assert(addrport);
if (strcasecmp(command, "connect")) {
- errmsg = "HTTP/1.0 405 Method Not Allowed\r\n\r\n";
+ errmsg = HTTP_CONNECT_IS_NOT_AN_HTTP_PROXY_MSG;
goto err;
}
@@ -4539,6 +4565,25 @@ circuit_clear_isolation(origin_circuit_t *circ)
circ->socks_username_len = circ->socks_password_len = 0;
}
+/** Send an END and mark for close the given edge connection conn using the
+ * given reason that has to be a stream reason.
+ *
+ * Note: We don't unattached the AP connection (if applicable) because we
+ * don't want to flush the remaining data. This function aims at ending
+ * everything quickly regardless of the connection state.
+ *
+ * This function can't fail and does nothing if conn is NULL. */
+void
+connection_edge_end_close(edge_connection_t *conn, uint8_t reason)
+{
+ if (!conn) {
+ return;
+ }
+
+ connection_edge_end(conn, reason);
+ connection_mark_for_close(TO_CONN(conn));
+}
+
/** Free all storage held in module-scoped variables for connection_edge.c */
void
connection_edge_free_all(void)
diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h
index 68d8b19a11..e82b6bd765 100644
--- a/src/core/or/connection_edge.h
+++ b/src/core/or/connection_edge.h
@@ -80,6 +80,7 @@ int connection_edge_process_inbuf(edge_connection_t *conn,
int connection_edge_destroy(circid_t circ_id, edge_connection_t *conn);
int connection_edge_end(edge_connection_t *conn, uint8_t reason);
int connection_edge_end_errno(edge_connection_t *conn);
+void connection_edge_end_close(edge_connection_t *conn, uint8_t reason);
int connection_edge_flushed_some(edge_connection_t *conn);
int connection_edge_finished_flushing(edge_connection_t *conn);
int connection_edge_finished_connecting(edge_connection_t *conn);
diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c
index debf482cb3..830e09fd54 100644
--- a/src/core/or/connection_or.c
+++ b/src/core/or/connection_or.c
@@ -39,7 +39,7 @@
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_or.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "feature/dirauth/reachability.h"
@@ -2308,6 +2308,8 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn)
cell_pack(&networkcell, cell, conn->wide_circ_ids);
+ /* We need to count padding cells from this non-packed code path
+ * since they are sent via chan->write_cell() (which is not packed) */
rep_hist_padding_count_write(PADDING_TYPE_TOTAL);
if (cell->command == CELL_PADDING)
rep_hist_padding_count_write(PADDING_TYPE_CELL);
@@ -2318,7 +2320,7 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn)
if (conn->chan) {
channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan));
- if (TLS_CHAN_TO_BASE(conn->chan)->currently_padding) {
+ if (TLS_CHAN_TO_BASE(conn->chan)->padding_enabled) {
rep_hist_padding_count_write(PADDING_TYPE_ENABLED_TOTAL);
if (cell->command == CELL_PADDING)
rep_hist_padding_count_write(PADDING_TYPE_ENABLED_CELL);
@@ -2348,6 +2350,7 @@ connection_or_write_var_cell_to_buf,(const var_cell_t *cell,
if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
or_handshake_state_record_var_cell(conn, conn->handshake_state, cell, 0);
+ rep_hist_padding_count_write(PADDING_TYPE_TOTAL);
/* Touch the channel's active timestamp if there is one */
if (conn->chan)
channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan));
diff --git a/src/core/or/connection_st.h b/src/core/or/connection_st.h
index d1430eda14..1c42a56d6b 100644
--- a/src/core/or/connection_st.h
+++ b/src/core/or/connection_st.h
@@ -146,4 +146,4 @@ struct connection_t {
* directory connection. */
#define DIR_CONN_IS_SERVER(conn) ((conn)->purpose == DIR_PURPOSE_SERVER)
-#endif
+#endif /* !defined(CONNECTION_ST_H) */
diff --git a/src/core/or/cpath_build_state_st.h b/src/core/or/cpath_build_state_st.h
index dbe596d851..4572a10430 100644
--- a/src/core/or/cpath_build_state_st.h
+++ b/src/core/or/cpath_build_state_st.h
@@ -34,5 +34,5 @@ struct cpath_build_state_t {
time_t expiry_time;
};
-#endif
+#endif /* !defined(CIRCUIT_BUILD_STATE_ST_ST_H) */
diff --git a/src/core/or/crypt_path.c b/src/core/or/crypt_path.c
new file mode 100644
index 0000000000..6d5245510f
--- /dev/null
+++ b/src/core/or/crypt_path.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypt_path.c
+ *
+ * \brief Functions dealing with layered circuit encryption. This file aims to
+ * provide an API around the crypt_path_t structure which holds crypto
+ * information about a specific hop of a circuit.
+ *
+ * TODO: We should eventually move all functions dealing and manipulating
+ * crypt_path_t to this file, so that eventually we encapsulate more and more
+ * of crypt_path_t. Here are some more functions that can be moved here with
+ * some more effort:
+ *
+ * - circuit_list_path_impl()
+ * - Functions dealing with cpaths in HSv2 create_rend_cpath() and
+ * create_rend_cpath_legacy()
+ * - The cpath related parts of rend_service_receive_introduction() and
+ * rend_client_send_introduction().
+ **/
+
+#define CRYPT_PATH_PRIVATE
+
+#include "core/or/or.h"
+#include "core/or/crypt_path.h"
+
+#include "core/crypto/relay_crypto.h"
+#include "core/crypto/onion_crypto.h"
+#include "core/or/circuitbuild.h"
+#include "core/or/circuitlist.h"
+
+#include "lib/crypt_ops/crypto_dh.h"
+#include "lib/crypt_ops/crypto_util.h"
+
+#include "core/or/crypt_path_st.h"
+#include "core/or/cell_st.h"
+
+/** Add <b>new_hop</b> to the end of the doubly-linked-list <b>head_ptr</b>.
+ * This function is used to extend cpath by another hop.
+ */
+void
+cpath_extend_linked_list(crypt_path_t **head_ptr, crypt_path_t *new_hop)
+{
+ if (*head_ptr) {
+ new_hop->next = (*head_ptr);
+ new_hop->prev = (*head_ptr)->prev;
+ (*head_ptr)->prev->next = new_hop;
+ (*head_ptr)->prev = new_hop;
+ } else {
+ *head_ptr = new_hop;
+ new_hop->prev = new_hop->next = new_hop;
+ }
+}
+
+/** Create a new hop, annotate it with information about its
+ * corresponding router <b>choice</b>, and append it to the
+ * end of the cpath <b>head_ptr</b>. */
+int
+cpath_append_hop(crypt_path_t **head_ptr, extend_info_t *choice)
+{
+ crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t));
+
+ /* link hop into the cpath, at the end. */
+ cpath_extend_linked_list(head_ptr, hop);
+
+ hop->magic = CRYPT_PATH_MAGIC;
+ hop->state = CPATH_STATE_CLOSED;
+
+ hop->extend_info = extend_info_dup(choice);
+
+ hop->package_window = circuit_initial_package_window();
+ hop->deliver_window = CIRCWINDOW_START;
+
+ return 0;
+}
+
+/** Verify that cpath <b>cp</b> has all of its invariants
+ * correct. Trigger an assert if anything is invalid.
+ */
+void
+cpath_assert_ok(const crypt_path_t *cp)
+{
+ const crypt_path_t *start = cp;
+
+ do {
+ cpath_assert_layer_ok(cp);
+ /* layers must be in sequence of: "open* awaiting? closed*" */
+ if (cp != start) {
+ if (cp->state == CPATH_STATE_AWAITING_KEYS) {
+ tor_assert(cp->prev->state == CPATH_STATE_OPEN);
+ } else if (cp->state == CPATH_STATE_OPEN) {
+ tor_assert(cp->prev->state == CPATH_STATE_OPEN);
+ }
+ }
+ cp = cp->next;
+ tor_assert(cp);
+ } while (cp != start);
+}
+
+/** Verify that cpath layer <b>cp</b> has all of its invariants
+ * correct. Trigger an assert if anything is invalid.
+ */
+void
+cpath_assert_layer_ok(const crypt_path_t *cp)
+{
+// tor_assert(cp->addr); /* these are zero for rendezvous extra-hops */
+// tor_assert(cp->port);
+ tor_assert(cp);
+ tor_assert(cp->magic == CRYPT_PATH_MAGIC);
+ switch (cp->state)
+ {
+ case CPATH_STATE_OPEN:
+ relay_crypto_assert_ok(&cp->pvt_crypto);
+ /* fall through */
+ case CPATH_STATE_CLOSED:
+ /*XXXX Assert that there's no handshake_state either. */
+ tor_assert(!cp->rend_dh_handshake_state);
+ break;
+ case CPATH_STATE_AWAITING_KEYS:
+ /* tor_assert(cp->dh_handshake_state); */
+ break;
+ default:
+ log_fn(LOG_ERR, LD_BUG, "Unexpected state %d", cp->state);
+ tor_assert(0);
+ }
+ tor_assert(cp->package_window >= 0);
+ tor_assert(cp->deliver_window >= 0);
+}
+
+/** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in key_data.
+ *
+ * If <b>is_hs_v3</b> is set, this cpath will be used for next gen hidden
+ * service circuits and <b>key_data</b> must be at least
+ * HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN bytes in length.
+ *
+ * If <b>is_hs_v3</b> is not set, key_data must contain CPATH_KEY_MATERIAL_LEN
+ * bytes, which are used as follows:
+ * - 20 to initialize f_digest
+ * - 20 to initialize b_digest
+ * - 16 to key f_crypto
+ * - 16 to key b_crypto
+ *
+ * (If 'reverse' is true, then f_XX and b_XX are swapped.)
+ *
+ * Return 0 if init was successful, else -1 if it failed.
+ */
+int
+cpath_init_circuit_crypto(crypt_path_t *cpath,
+ const char *key_data, size_t key_data_len,
+ int reverse, int is_hs_v3)
+{
+
+ tor_assert(cpath);
+ return relay_crypto_init(&cpath->pvt_crypto, key_data, key_data_len,
+ reverse, is_hs_v3);
+}
+
+/** Deallocate space associated with the cpath node <b>victim</b>. */
+void
+cpath_free(crypt_path_t *victim)
+{
+ if (!victim)
+ return;
+
+ relay_crypto_clear(&victim->pvt_crypto);
+ onion_handshake_state_release(&victim->handshake_state);
+ crypto_dh_free(victim->rend_dh_handshake_state);
+ extend_info_free(victim->extend_info);
+
+ memwipe(victim, 0xBB, sizeof(crypt_path_t)); /* poison memory */
+ tor_free(victim);
+}
+
+/********************** cpath crypto API *******************************/
+
+/** Encrypt or decrypt <b>payload</b> using the crypto of <b>cpath</b>. Actual
+ * operation decided by <b>is_decrypt</b>. */
+void
+cpath_crypt_cell(const crypt_path_t *cpath, uint8_t *payload, bool is_decrypt)
+{
+ if (is_decrypt) {
+ relay_crypt_one_payload(cpath->pvt_crypto.b_crypto, payload);
+ } else {
+ relay_crypt_one_payload(cpath->pvt_crypto.f_crypto, payload);
+ }
+}
+
+/** Getter for the incoming digest of <b>cpath</b>. */
+struct crypto_digest_t *
+cpath_get_incoming_digest(const crypt_path_t *cpath)
+{
+ return cpath->pvt_crypto.b_digest;
+}
+
+/** Set the right integrity digest on the outgoing <b>cell</b> based on the
+ * cell payload and update the forward digest of <b>cpath</b>. */
+void
+cpath_set_cell_forward_digest(crypt_path_t *cpath, cell_t *cell)
+{
+ relay_set_digest(cpath->pvt_crypto.f_digest, cell);
+}
+
+/************ cpath sendme API ***************************/
+
+/** Return the sendme_digest of this <b>cpath</b>. */
+uint8_t *
+cpath_get_sendme_digest(crypt_path_t *cpath)
+{
+ return relay_crypto_get_sendme_digest(&cpath->pvt_crypto);
+}
+
+/** Record the cell digest, indicated by is_foward_digest or not, as the
+ * SENDME cell digest. */
+void
+cpath_sendme_record_cell_digest(crypt_path_t *cpath, bool is_foward_digest)
+{
+ tor_assert(cpath);
+ relay_crypto_record_sendme_digest(&cpath->pvt_crypto, is_foward_digest);
+}
+
+/************ other cpath functions ***************************/
+
+/** Return the first non-open hop in cpath, or return NULL if all
+ * hops are open. */
+crypt_path_t *
+cpath_get_next_non_open_hop(crypt_path_t *cpath)
+{
+ crypt_path_t *hop = cpath;
+ do {
+ if (hop->state != CPATH_STATE_OPEN)
+ return hop;
+ hop = hop->next;
+ } while (hop != cpath);
+ return NULL;
+}
+
+#ifdef TOR_UNIT_TESTS
+
+/** Unittest helper function: Count number of hops in cpath linked list. */
+unsigned int
+cpath_get_n_hops(crypt_path_t **head_ptr)
+{
+ unsigned int n_hops = 0;
+ crypt_path_t *tmp;
+
+ if (!*head_ptr) {
+ return 0;
+ }
+
+ tmp = *head_ptr;
+ do {
+ n_hops++;
+ tmp = tmp->next;
+ } while (tmp != *head_ptr);
+
+ return n_hops;
+}
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
diff --git a/src/core/or/crypt_path.h b/src/core/or/crypt_path.h
new file mode 100644
index 0000000000..7a95fec2b4
--- /dev/null
+++ b/src/core/or/crypt_path.h
@@ -0,0 +1,46 @@
+/**
+ * \file crypt_path.h
+ * \brief Header file for crypt_path.c.
+ **/
+
+#ifndef CRYPT_PATH_H
+#define CRYPT_PATH_H
+
+void cpath_assert_layer_ok(const crypt_path_t *cp);
+
+void cpath_assert_ok(const crypt_path_t *cp);
+
+int cpath_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
+
+int cpath_init_circuit_crypto(crypt_path_t *cpath,
+ const char *key_data, size_t key_data_len,
+ int reverse, int is_hs_v3);
+
+void
+cpath_free(crypt_path_t *victim);
+
+void cpath_extend_linked_list(crypt_path_t **head_ptr, crypt_path_t *new_hop);
+
+void
+cpath_crypt_cell(const crypt_path_t *cpath, uint8_t *payload, bool is_decrypt);
+
+struct crypto_digest_t *
+cpath_get_incoming_digest(const crypt_path_t *cpath);
+
+void cpath_sendme_record_cell_digest(crypt_path_t *cpath,
+ bool is_foward_digest);
+
+void
+cpath_set_cell_forward_digest(crypt_path_t *cpath, cell_t *cell);
+
+crypt_path_t *cpath_get_next_non_open_hop(crypt_path_t *cpath);
+
+void cpath_sendme_circuit_record_inbound_cell(crypt_path_t *cpath);
+
+uint8_t *cpath_get_sendme_digest(crypt_path_t *cpath);
+
+#if defined(TOR_UNIT_TESTS)
+unsigned int cpath_get_n_hops(crypt_path_t **head_ptr);
+#endif /* defined(TOR_UNIT_TESTS) */
+
+#endif /* !defined(CRYPT_PATH_H) */
diff --git a/src/core/or/crypt_path_reference_st.h b/src/core/or/crypt_path_reference_st.h
index 3d79f26c1c..1827022b4e 100644
--- a/src/core/or/crypt_path_reference_st.h
+++ b/src/core/or/crypt_path_reference_st.h
@@ -19,5 +19,5 @@ struct crypt_path_reference_t {
crypt_path_t *cpath;
};
-#endif
+#endif /* !defined(CRYPT_PATH_REFERENCE_ST_H) */
diff --git a/src/core/or/crypt_path_st.h b/src/core/or/crypt_path_st.h
index 429480f8ab..249ac6aaa3 100644
--- a/src/core/or/crypt_path_st.h
+++ b/src/core/or/crypt_path_st.h
@@ -24,15 +24,24 @@ struct onion_handshake_state_t {
} u;
};
+/** Macro to encapsulate private members of a struct.
+ *
+ * Renames 'x' to 'x_crypt_path_private_field'.
+ */
+#define CRYPT_PATH_PRIV_FIELD(x) x ## _crypt_path_private_field
+
+#ifdef CRYPT_PATH_PRIVATE
+
+/* Helper macro to access private members of a struct. */
+#define pvt_crypto CRYPT_PATH_PRIV_FIELD(crypto)
+
+#endif /* defined(CRYPT_PATH_PRIVATE) */
+
/** Holds accounting information for a single step in the layered encryption
* performed by a circuit. Used only at the client edge of a circuit. */
struct crypt_path_t {
uint32_t magic;
- /** Cryptographic state used for encrypting and authenticating relay
- * cells to and from this hop. */
- relay_crypto_t crypto;
-
/** Current state of the handshake as performed with the OR at this
* step. */
onion_handshake_state_t handshake_state;
@@ -65,6 +74,12 @@ struct crypt_path_t {
* at this step? */
int deliver_window; /**< How many cells are we willing to deliver originating
* at this step? */
+
+ /*********************** Private members ****************************/
+
+ /** Private member: Cryptographic state used for encrypting and
+ * authenticating relay cells to and from this hop. */
+ relay_crypto_t CRYPT_PATH_PRIV_FIELD(crypto);
};
-#endif
+#endif /* !defined(CRYPT_PATH_ST_H) */
diff --git a/src/core/or/destroy_cell_queue_st.h b/src/core/or/destroy_cell_queue_st.h
index 56630670ba..e917afc700 100644
--- a/src/core/or/destroy_cell_queue_st.h
+++ b/src/core/or/destroy_cell_queue_st.h
@@ -23,5 +23,5 @@ struct destroy_cell_queue_t {
int n; /**< The number of cells in the queue. */
};
-#endif
+#endif /* !defined(DESTROY_CELL_QUEUE_ST_H) */
diff --git a/src/core/or/dos.h b/src/core/or/dos.h
index 95448d0530..b5154a7cd2 100644
--- a/src/core/or/dos.h
+++ b/src/core/or/dos.h
@@ -134,7 +134,7 @@ MOCK_DECL(STATIC unsigned int, get_param_cc_enabled,
MOCK_DECL(STATIC unsigned int, get_param_conn_enabled,
(const networkstatus_t *ns));
-#endif /* TOR_DOS_PRIVATE */
+#endif /* defined(DOS_PRIVATE) */
-#endif /* TOR_DOS_H */
+#endif /* !defined(TOR_DOS_H) */
diff --git a/src/core/or/edge_connection_st.h b/src/core/or/edge_connection_st.h
index 1665b8589f..8922a3a9cf 100644
--- a/src/core/or/edge_connection_st.h
+++ b/src/core/or/edge_connection_st.h
@@ -73,5 +73,5 @@ struct edge_connection_t {
uint64_t dirreq_id;
};
-#endif
+#endif /* !defined(EDGE_CONNECTION_ST_H) */
diff --git a/src/core/or/entry_connection_st.h b/src/core/or/entry_connection_st.h
index 45621fadbf..e65c545d17 100644
--- a/src/core/or/entry_connection_st.h
+++ b/src/core/or/entry_connection_st.h
@@ -96,5 +96,5 @@ struct entry_connection_t {
/** Cast a entry_connection_t subtype pointer to a edge_connection_t **/
#define ENTRY_TO_EDGE_CONN(c) (&(((c))->edge_))
-#endif
+#endif /* !defined(ENTRY_CONNECTION_ST_H) */
diff --git a/src/core/or/entry_port_cfg_st.h b/src/core/or/entry_port_cfg_st.h
index 87dfb331e5..b84838d44f 100644
--- a/src/core/or/entry_port_cfg_st.h
+++ b/src/core/or/entry_port_cfg_st.h
@@ -50,5 +50,5 @@ struct entry_port_cfg_t {
};
-#endif
+#endif /* !defined(ENTRY_PORT_CFG_ST_H) */
diff --git a/src/core/or/extend_info_st.h b/src/core/or/extend_info_st.h
index bc7a77b1b2..7704ff16b5 100644
--- a/src/core/or/extend_info_st.h
+++ b/src/core/or/extend_info_st.h
@@ -27,4 +27,4 @@ struct extend_info_t {
curve25519_public_key_t curve25519_onion_key;
};
-#endif
+#endif /* !defined(EXTEND_INFO_ST_H) */
diff --git a/src/core/or/half_edge_st.h b/src/core/or/half_edge_st.h
index d4617be108..1fe47ad3f1 100644
--- a/src/core/or/half_edge_st.h
+++ b/src/core/or/half_edge_st.h
@@ -30,5 +30,5 @@ typedef struct half_edge_t {
int connected_pending : 1;
} half_edge_t;
-#endif
+#endif /* !defined(HALF_EDGE_ST_H) */
diff --git a/src/core/or/listener_connection_st.h b/src/core/or/listener_connection_st.h
index 8989a39dc8..1250d9c9b4 100644
--- a/src/core/or/listener_connection_st.h
+++ b/src/core/or/listener_connection_st.h
@@ -21,5 +21,5 @@ struct listener_connection_t {
};
-#endif
+#endif /* !defined(LISTENER_CONNECTION_ST_H) */
diff --git a/src/core/or/ocirc_event.h b/src/core/or/ocirc_event.h
index 0b125c2898..59ec9e27cb 100644
--- a/src/core/or/ocirc_event.h
+++ b/src/core/or/ocirc_event.h
@@ -86,4 +86,4 @@ void ocirc_event_subscribe(ocirc_event_rcvr_t fn);
void ocirc_event_publish(const ocirc_event_msg_t *msg);
#endif
-#endif /* defined(TOR_OCIRC_EVENT_STATE_H) */
+#endif /* !defined(TOR_OCIRC_EVENT_H) */
diff --git a/src/core/or/ocirc_event_sys.h b/src/core/or/ocirc_event_sys.h
index 9d4bfe5333..61180496da 100644
--- a/src/core/or/ocirc_event_sys.h
+++ b/src/core/or/ocirc_event_sys.h
@@ -10,4 +10,4 @@
extern const struct subsys_fns_t sys_ocirc_event;
-#endif /* defined(TOR_OCIRC_EVENT_H) */
+#endif /* !defined(TOR_OCIRC_EVENT_SYS_H) */
diff --git a/src/core/or/or.h b/src/core/or/or.h
index db6d089582..ab258629a6 100644
--- a/src/core/or/or.h
+++ b/src/core/or/or.h
@@ -841,8 +841,8 @@ typedef struct protover_summary_flags_t {
unsigned int supports_v3_rendezvous_point: 1;
/** True iff this router has a protocol list that allows clients to
- * negotiate link-level padding. Requires Padding>=1. */
- unsigned int supports_padding : 1;
+ * negotiate hs circuit setup padding. Requires Padding>=2. */
+ unsigned int supports_hs_setup_padding : 1;
} protover_summary_flags_t;
typedef struct routerinfo_t routerinfo_t;
diff --git a/src/core/or/or_circuit_st.h b/src/core/or/or_circuit_st.h
index 6b6feb9d89..6789668224 100644
--- a/src/core/or/or_circuit_st.h
+++ b/src/core/or/or_circuit_st.h
@@ -33,11 +33,6 @@ struct or_circuit_t {
cell_queue_t p_chan_cells;
/** The channel that is previous in this circuit. */
channel_t *p_chan;
- /**
- * Circuit mux associated with p_chan to which this circuit is attached;
- * NULL if we have no p_chan.
- */
- circuitmux_t *p_mux;
/** Linked list of Exit streams associated with this circuit. */
edge_connection_t *n_streams;
/** Linked list of Exit streams associated with this circuit that are
@@ -76,5 +71,5 @@ struct or_circuit_t {
uint64_t total_cell_waiting_time;
};
-#endif
+#endif /* !defined(OR_CIRCUIT_ST_H) */
diff --git a/src/core/or/or_connection_st.h b/src/core/or/or_connection_st.h
index a5ce844bff..051fcd00d3 100644
--- a/src/core/or/or_connection_st.h
+++ b/src/core/or/or_connection_st.h
@@ -91,4 +91,4 @@ struct or_connection_t {
uint64_t bytes_xmitted, bytes_xmitted_by_tls;
};
-#endif
+#endif /* !defined(OR_CONNECTION_ST_H) */
diff --git a/src/core/or/or_handshake_certs_st.h b/src/core/or/or_handshake_certs_st.h
index a93b7104aa..9deb6d6d59 100644
--- a/src/core/or/or_handshake_certs_st.h
+++ b/src/core/or/or_handshake_certs_st.h
@@ -37,4 +37,4 @@ struct or_handshake_certs_t {
size_t ed_rsa_crosscert_len;
};
-#endif
+#endif /* !defined(OR_HANDSHAKE_CERTS_ST) */
diff --git a/src/core/or/or_handshake_state_st.h b/src/core/or/or_handshake_state_st.h
index 09a8a34179..472ce8a302 100644
--- a/src/core/or/or_handshake_state_st.h
+++ b/src/core/or/or_handshake_state_st.h
@@ -74,5 +74,5 @@ struct or_handshake_state_t {
or_handshake_certs_t *certs;
};
-#endif
+#endif /* !defined(OR_HANDSHAKE_STATE_ST) */
diff --git a/src/core/or/or_periodic.c b/src/core/or/or_periodic.c
new file mode 100644
index 0000000000..fe28c99192
--- /dev/null
+++ b/src/core/or/or_periodic.c
@@ -0,0 +1,65 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file or_periodic.c
+ * @brief Periodic callbacks for the onion routing subsystem
+ **/
+
+#include "orconfig.h"
+#include "core/or/or.h"
+
+#include "core/mainloop/periodic.h"
+
+#include "core/or/channel.h"
+#include "core/or/circuituse.h"
+#include "core/or/or_periodic.h"
+
+#include "feature/relay/routermode.h"
+
+#define DECLARE_EVENT(name, roles, flags) \
+ static periodic_event_item_t name ## _event = \
+ PERIODIC_EVENT(name, \
+ PERIODIC_EVENT_ROLE_##roles, \
+ flags)
+
+#define FL(name) (PERIODIC_EVENT_FLAG_ ## name)
+
+#define CHANNEL_CHECK_INTERVAL (60*60)
+static int
+check_canonical_channels_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ if (public_server_mode(options))
+ channel_check_for_duplicates();
+
+ return CHANNEL_CHECK_INTERVAL;
+}
+
+DECLARE_EVENT(check_canonical_channels, RELAY, FL(NEED_NET));
+
+/**
+ * Periodic callback: as a server, see if we have any old unused circuits
+ * that should be expired */
+static int
+expire_old_circuits_serverside_callback(time_t now,
+ const or_options_t *options)
+{
+ (void)options;
+ /* every 11 seconds, so not usually the same second as other such events */
+ circuit_expire_old_circuits_serverside(now);
+ return 11;
+}
+
+DECLARE_EVENT(expire_old_circuits_serverside, ROUTER, FL(NEED_NET));
+
+void
+or_register_periodic_events(void)
+{
+ // These are router-only events, but they're owned by the OR subsystem. */
+ periodic_events_register(&check_canonical_channels_event);
+ periodic_events_register(&expire_old_circuits_serverside_event);
+}
diff --git a/src/core/or/or_periodic.h b/src/core/or/or_periodic.h
new file mode 100644
index 0000000000..c2f47cf5ef
--- /dev/null
+++ b/src/core/or/or_periodic.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file or_periodic.h
+ * @brief Header for core/or/or_periodic.c
+ **/
+
+#ifndef TOR_CORE_OR_OR_PERIODIC_H
+#define TOR_CORE_OR_OR_PERIODIC_H
+
+void or_register_periodic_events(void);
+
+#endif /* !defined(TOR_CORE_OR_OR_PERIODIC_H) */
diff --git a/src/core/or/or_sys.c b/src/core/or/or_sys.c
new file mode 100644
index 0000000000..6f8c81a4df
--- /dev/null
+++ b/src/core/or/or_sys.c
@@ -0,0 +1,43 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file or_sys.c
+ * @brief Subsystem definitions for OR module.
+ **/
+
+#include "orconfig.h"
+#include "core/or/or.h"
+#include "core/or/or_periodic.h"
+#include "core/or/or_sys.h"
+#include "core/or/policies.h"
+#include "core/or/protover.h"
+#include "core/or/versions.h"
+
+#include "lib/subsys/subsys.h"
+
+static int
+subsys_or_initialize(void)
+{
+ or_register_periodic_events();
+ return 0;
+}
+
+static void
+subsys_or_shutdown(void)
+{
+ protover_free_all();
+ protover_summary_cache_free_all();
+ policies_free_all();
+}
+
+const struct subsys_fns_t sys_or = {
+ .name = "or",
+ .supported = true,
+ .level = 20,
+ .initialize = subsys_or_initialize,
+ .shutdown = subsys_or_shutdown,
+};
diff --git a/src/core/or/or_sys.h b/src/core/or/or_sys.h
new file mode 100644
index 0000000000..c37ef01858
--- /dev/null
+++ b/src/core/or/or_sys.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file or_sys.h
+ * @brief Header for core/or/or_sys.c
+ **/
+
+#ifndef TOR_CORE_OR_OR_SYS_H
+#define TOR_CORE_OR_OR_SYS_H
+
+extern const struct subsys_fns_t sys_or;
+
+#endif /* !defined(TOR_CORE_OR_OR_SYS_H) */
diff --git a/src/core/or/orconn_event.h b/src/core/or/orconn_event.h
index 80289d53e6..d6635793db 100644
--- a/src/core/or/orconn_event.h
+++ b/src/core/or/orconn_event.h
@@ -117,4 +117,4 @@ void orconn_event_subscribe(orconn_event_rcvr_t);
void orconn_event_publish(const orconn_event_msg_t *);
#endif
-#endif /* defined(TOR_ORCONN_EVENT_H) */
+#endif /* !defined(TOR_ORCONN_EVENT_H) */
diff --git a/src/core/or/orconn_event_sys.h b/src/core/or/orconn_event_sys.h
index bfb0a3ac4a..9703b2e3d1 100644
--- a/src/core/or/orconn_event_sys.h
+++ b/src/core/or/orconn_event_sys.h
@@ -9,4 +9,4 @@
extern const struct subsys_fns_t sys_orconn_event;
-#endif /* defined(TOR_ORCONN_SYS_H) */
+#endif /* !defined(TOR_ORCONN_EVENT_SYS_H) */
diff --git a/src/core/or/origin_circuit_st.h b/src/core/or/origin_circuit_st.h
index daa5f41dad..01bbc84ae2 100644
--- a/src/core/or/origin_circuit_st.h
+++ b/src/core/or/origin_circuit_st.h
@@ -295,4 +295,4 @@ struct origin_circuit_t {
};
-#endif
+#endif /* !defined(ORIGIN_CIRCUIT_ST_H) */
diff --git a/src/core/or/policies.c b/src/core/or/policies.c
index a6d66d36de..83d9a53fc0 100644
--- a/src/core/or/policies.c
+++ b/src/core/or/policies.c
@@ -31,6 +31,7 @@
#include "ht.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/encoding/confline.h"
+#include "trunnel/ed25519_cert.h"
#include "core/or/addr_policy_st.h"
#include "feature/dirclient/dir_server_st.h"
@@ -1015,6 +1016,83 @@ fascist_firewall_choose_address_rs(const routerstatus_t *rs,
}
}
+/** Like fascist_firewall_choose_address_base(), but takes in a smartlist
+ * <b>lspecs</b> consisting of one or more link specifiers. We assume
+ * fw_connection is FIREWALL_OR_CONNECTION as link specifiers cannot
+ * contain DirPorts.
+ */
+void
+fascist_firewall_choose_address_ls(const smartlist_t *lspecs,
+ int pref_only, tor_addr_port_t* ap)
+{
+ int have_v4 = 0, have_v6 = 0;
+ uint16_t port_v4 = 0, port_v6 = 0;
+ tor_addr_t addr_v4, addr_v6;
+
+ tor_assert(ap);
+
+ if (lspecs == NULL) {
+ log_warn(LD_BUG, "Unknown or missing link specifiers");
+ return;
+ }
+ if (smartlist_len(lspecs) == 0) {
+ log_warn(LD_PROTOCOL, "Link specifiers are empty");
+ return;
+ }
+
+ tor_addr_make_null(&ap->addr, AF_UNSPEC);
+ ap->port = 0;
+
+ tor_addr_make_null(&addr_v4, AF_INET);
+ tor_addr_make_null(&addr_v6, AF_INET6);
+
+ SMARTLIST_FOREACH_BEGIN(lspecs, const link_specifier_t *, ls) {
+ switch (link_specifier_get_ls_type(ls)) {
+ case LS_IPV4:
+ /* Skip if we already seen a v4. */
+ if (have_v4) continue;
+ tor_addr_from_ipv4h(&addr_v4,
+ link_specifier_get_un_ipv4_addr(ls));
+ port_v4 = link_specifier_get_un_ipv4_port(ls);
+ have_v4 = 1;
+ break;
+ case LS_IPV6:
+ /* Skip if we already seen a v6, or deliberately skip it if we're not a
+ * direct connection. */
+ if (have_v6) continue;
+ tor_addr_from_ipv6_bytes(&addr_v6,
+ (const char *) link_specifier_getconstarray_un_ipv6_addr(ls));
+ port_v6 = link_specifier_get_un_ipv6_port(ls);
+ have_v6 = 1;
+ break;
+ default:
+ /* Ignore unknown. */
+ break;
+ }
+ } SMARTLIST_FOREACH_END(ls);
+
+ /* If we don't have IPv4 or IPv6 in link specifiers, log a bug and return. */
+ if (!have_v4 && !have_v6) {
+ if (!have_v6) {
+ log_warn(LD_PROTOCOL, "None of our link specifiers have IPv4 or IPv6");
+ } else {
+ log_warn(LD_PROTOCOL, "None of our link specifiers have IPv4");
+ }
+ return;
+ }
+
+ /* Here, don't check for DirPorts as link specifiers are only used for
+ * ORPorts. */
+ const or_options_t *options = get_options();
+ int pref_ipv6 = fascist_firewall_prefer_ipv6_orport(options);
+ /* Assume that the DirPorts are zero as link specifiers only use ORPorts. */
+ fascist_firewall_choose_address_base(&addr_v4, port_v4, 0,
+ &addr_v6, port_v6, 0,
+ FIREWALL_OR_CONNECTION,
+ pref_only, pref_ipv6,
+ ap);
+}
+
/** Like fascist_firewall_choose_address_base(), but takes <b>node</b>, and
* looks up the node's IPv6 preference rather than taking an argument
* for pref_ipv6. */
@@ -1164,6 +1242,15 @@ authdir_policy_badexit_address(uint32_t addr, uint16_t port)
#define REJECT(arg) \
STMT_BEGIN *msg = tor_strdup(arg); goto err; STMT_END
+/** Check <b>or_options</b> to determine whether or not we are using the
+ * default options for exit policy. Return true if so, false otherwise. */
+static int
+policy_using_default_exit_options(const or_options_t *or_options)
+{
+ return (or_options->ExitPolicy == NULL && or_options->ExitRelay == -1 &&
+ or_options->ReducedExitPolicy == 0 && or_options->IPv6Exit == 0);
+}
+
/** Config helper: If there's any problem with the policy configuration
* options in <b>options</b>, return -1 and set <b>msg</b> to a newly
* allocated description of the error. Else return 0. */
@@ -1182,9 +1269,8 @@ validate_addr_policies(const or_options_t *options, char **msg)
static int warned_about_nonexit = 0;
- if (public_server_mode(options) &&
- !warned_about_nonexit && options->ExitPolicy == NULL &&
- options->ExitRelay == -1 && options->ReducedExitPolicy == 0) {
+ if (public_server_mode(options) && !warned_about_nonexit &&
+ policy_using_default_exit_options(options)) {
warned_about_nonexit = 1;
log_notice(LD_CONFIG, "By default, Tor does not run as an exit relay. "
"If you want to be an exit relay, "
@@ -2141,9 +2227,9 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
int rv = 0;
/* Short-circuit for non-exit relays, or for relays where we didn't specify
- * ExitPolicy or ReducedExitPolicy and ExitRelay is auto. */
- if (or_options->ExitRelay == 0 || (or_options->ExitPolicy == NULL &&
- or_options->ExitRelay == -1 && or_options->ReducedExitPolicy == 0)) {
+ * ExitPolicy or ReducedExitPolicy or IPv6Exit and ExitRelay is auto. */
+ if (or_options->ExitRelay == 0 ||
+ policy_using_default_exit_options(or_options)) {
append_exit_policy_string(result, "reject *4:*");
append_exit_policy_string(result, "reject *6:*");
return 0;
diff --git a/src/core/or/policies.h b/src/core/or/policies.h
index 324c1c2dd1..3c46363c04 100644
--- a/src/core/or/policies.h
+++ b/src/core/or/policies.h
@@ -92,6 +92,8 @@ int fascist_firewall_allows_dir_server(const dir_server_t *ds,
void fascist_firewall_choose_address_rs(const routerstatus_t *rs,
firewall_connection_t fw_connection,
int pref_only, tor_addr_port_t* ap);
+void fascist_firewall_choose_address_ls(const smartlist_t *lspecs,
+ int pref_only, tor_addr_port_t* ap);
void fascist_firewall_choose_address_node(const node_t *node,
firewall_connection_t fw_connection,
int pref_only, tor_addr_port_t* ap);
diff --git a/src/core/or/port_cfg_st.h b/src/core/or/port_cfg_st.h
index b67091ce32..e9e82bb1de 100644
--- a/src/core/or/port_cfg_st.h
+++ b/src/core/or/port_cfg_st.h
@@ -31,5 +31,5 @@ struct port_cfg_t {
char unix_addr[FLEXIBLE_ARRAY_MEMBER];
};
-#endif
+#endif /* !defined(PORT_CFG_ST_H) */
diff --git a/src/core/or/protover.c b/src/core/or/protover.c
index 53709ad002..ccd33fabf7 100644
--- a/src/core/or/protover.c
+++ b/src/core/or/protover.c
@@ -53,7 +53,8 @@ static const struct {
{ PRT_DESC, "Desc" },
{ PRT_MICRODESC, "Microdesc"},
{ PRT_PADDING, "Padding"},
- { PRT_CONS, "Cons" }
+ { PRT_CONS, "Cons" },
+ { PRT_FLOWCTRL, "FlowCtrl"},
};
#define N_PROTOCOL_NAMES ARRAY_LENGTH(PROTOCOL_NAMES)
@@ -401,7 +402,8 @@ protover_get_supported_protocols(void)
#endif
"Microdesc=1-2 "
"Relay=1-2 "
- "Padding=1";
+ "Padding=2 "
+ "FlowCtrl=1";
}
/** The protocols from protover_get_supported_protocols(), as parsed into a
@@ -820,6 +822,8 @@ protover_all_supported(const char *s, char **missing_out)
* ones and, if so, add them to unsupported->ranges. */
if (versions->low != 0 && versions->high != 0) {
smartlist_add(unsupported->ranges, versions);
+ } else {
+ tor_free(versions);
}
/* Finally, if we had something unsupported, add it to the list of
* missing_some things and mark that there was something missing. */
@@ -828,7 +832,6 @@ protover_all_supported(const char *s, char **missing_out)
all_supported = 0;
} else {
proto_entry_free(unsupported);
- tor_free(versions);
}
} SMARTLIST_FOREACH_END(range);
diff --git a/src/core/or/protover.h b/src/core/or/protover.h
index 567b94a168..af45a31aeb 100644
--- a/src/core/or/protover.h
+++ b/src/core/or/protover.h
@@ -28,6 +28,8 @@ struct smartlist_t;
#define PROTOVER_HS_INTRO_V3 4
/** The protover version number that signifies HSv3 rendezvous point support */
#define PROTOVER_HS_RENDEZVOUS_POINT_V3 2
+/** The protover that signals support for HS circuit setup padding machines */
+#define PROTOVER_HS_SETUP_PADDING 2
/** List of recognized subprotocols. */
/// C_RUST_COUPLED: src/rust/protover/ffi.rs `translate_to_rust`
@@ -44,6 +46,7 @@ typedef enum protocol_type_t {
PRT_MICRODESC = 8,
PRT_CONS = 9,
PRT_PADDING = 10,
+ PRT_FLOWCTRL = 11,
} protocol_type_t;
bool protover_contains_long_protocol_names(const char *s);
diff --git a/src/core/or/relay.c b/src/core/or/relay.c
index 706a6e05cb..9e691a02b5 100644
--- a/src/core/or/relay.c
+++ b/src/core/or/relay.c
@@ -61,7 +61,7 @@
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
#include "core/or/connection_or.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "feature/dircommon/directory.h"
@@ -93,15 +93,12 @@
#include "core/or/origin_circuit_st.h"
#include "feature/nodelist/routerinfo_st.h"
#include "core/or/socks_request_st.h"
-
-#include "lib/intmath/weakrng.h"
+#include "core/or/sendme.h"
static edge_connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell,
cell_direction_t cell_direction,
crypt_path_t *layer_hint);
-static void circuit_consider_sending_sendme(circuit_t *circ,
- crypt_path_t *layer_hint);
static void circuit_resume_edge_reading(circuit_t *circ,
crypt_path_t *layer_hint);
static int circuit_resume_edge_reading_helper(edge_connection_t *conn,
@@ -134,9 +131,6 @@ uint64_t stats_n_relay_cells_delivered = 0;
* reached (see append_cell_to_circuit_queue()) */
uint64_t stats_n_circ_max_cell_reached = 0;
-/** Used to tell which stream to read from first on a circuit. */
-static tor_weak_rng_t stream_choice_rng = TOR_WEAK_RNG_INIT;
-
/**
* Update channel usage state based on the type of relay cell and
* circuit properties.
@@ -253,6 +247,10 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
if (recognized) {
edge_connection_t *conn = NULL;
+ /* Recognized cell, the cell digest has been updated, we'll record it for
+ * the SENDME if need be. */
+ sendme_record_received_cell_digest(circ, layer_hint);
+
if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING) {
if (pathbias_check_probe_response(circ, cell) == -1) {
pathbias_count_valid_cells(circ, cell);
@@ -535,6 +533,64 @@ relay_command_to_string(uint8_t command)
}
}
+/** When padding a cell with randomness, leave this many zeros after the
+ * payload. */
+#define CELL_PADDING_GAP 4
+
+/** Return the offset where the padding should start. The <b>data_len</b> is
+ * the relay payload length expected to be put in the cell. It can not be
+ * bigger than RELAY_PAYLOAD_SIZE else this function assert().
+ *
+ * Value will always be smaller than CELL_PAYLOAD_SIZE because this offset is
+ * for the entire cell length not just the data payload length. Zero is
+ * returned if there is no room for padding.
+ *
+ * This function always skips the first 4 bytes after the payload because
+ * having some unused zero bytes has saved us a lot of times in the past. */
+
+STATIC size_t
+get_pad_cell_offset(size_t data_len)
+{
+ /* This is never supposed to happen but in case it does, stop right away
+ * because if tor is tricked somehow into not adding random bytes to the
+ * payload with this function returning 0 for a bad data_len, the entire
+ * authenticated SENDME design can be bypassed leading to bad denial of
+ * service attacks. */
+ tor_assert(data_len <= RELAY_PAYLOAD_SIZE);
+
+ /* If the offset is larger than the cell payload size, we return an offset
+ * of zero indicating that no padding needs to be added. */
+ size_t offset = RELAY_HEADER_SIZE + data_len + CELL_PADDING_GAP;
+ if (offset >= CELL_PAYLOAD_SIZE) {
+ return 0;
+ }
+ return offset;
+}
+
+/* Add random bytes to the unused portion of the payload, to foil attacks
+ * where the other side can predict all of the bytes in the payload and thus
+ * compute the authenticated SENDME cells without seeing the traffic. See
+ * proposal 289. */
+static void
+pad_cell_payload(uint8_t *cell_payload, size_t data_len)
+{
+ size_t pad_offset, pad_len;
+
+ tor_assert(cell_payload);
+
+ pad_offset = get_pad_cell_offset(data_len);
+ if (pad_offset == 0) {
+ /* We can't add padding so we are done. */
+ return;
+ }
+
+ /* Remember here that the cell_payload is the length of the header and
+ * payload size so we offset it using the full length of the cell. */
+ pad_len = CELL_PAYLOAD_SIZE - pad_offset;
+ crypto_fast_rng_getbytes(get_thread_fast_rng(),
+ cell_payload + pad_offset, pad_len);
+}
+
/** Make a relay cell out of <b>relay_command</b> and <b>payload</b>, and send
* it onto the open circuit <b>circ</b>. <b>stream_id</b> is the ID on
* <b>circ</b> for the stream that's sending the relay cell, or 0 if it's a
@@ -578,6 +634,9 @@ relay_send_command_from_edge_,(streamid_t stream_id, circuit_t *circ,
if (payload_len)
memcpy(cell.payload+RELAY_HEADER_SIZE, payload, payload_len);
+ /* Add random padding to the cell if we can. */
+ pad_cell_payload(cell.payload, payload_len);
+
log_debug(LD_OR,"delivering %d cell %s.", relay_command,
cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
@@ -645,6 +704,14 @@ relay_send_command_from_edge_,(streamid_t stream_id, circuit_t *circ,
circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
return -1;
}
+
+ /* If applicable, note the cell digest for the SENDME version 1 purpose if
+ * we need to. This call needs to be after the circuit_package_relay_cell()
+ * because the cell digest is set within that function. */
+ if (relay_command == RELAY_COMMAND_DATA) {
+ sendme_record_cell_digest_on_circ(circ, cpath_layer);
+ }
+
return 0;
}
@@ -1434,6 +1501,81 @@ connection_edge_process_relay_cell_not_open(
// return -1;
}
+/** Process a SENDME cell that arrived on <b>circ</b>. If it is a stream level
+ * cell, it is destined for the given <b>conn</b>. If it is a circuit level
+ * cell, it is destined for the <b>layer_hint</b>. The <b>domain</b> is the
+ * logging domain that should be used.
+ *
+ * Return 0 if everything went well or a negative value representing a circuit
+ * end reason on error for which the caller is responsible for closing it. */
+static int
+process_sendme_cell(const relay_header_t *rh, const cell_t *cell,
+ circuit_t *circ, edge_connection_t *conn,
+ crypt_path_t *layer_hint, int domain)
+{
+ int ret;
+
+ tor_assert(rh);
+
+ if (!rh->stream_id) {
+ /* Circuit level SENDME cell. */
+ ret = sendme_process_circuit_level(layer_hint, circ,
+ cell->payload + RELAY_HEADER_SIZE,
+ rh->length);
+ if (ret < 0) {
+ return ret;
+ }
+ /* Resume reading on any streams now that we've processed a valid
+ * SENDME cell that updated our package window. */
+ circuit_resume_edge_reading(circ, layer_hint);
+ /* We are done, the rest of the code is for the stream level. */
+ return 0;
+ }
+
+ /* No connection, might be half edge state. We are done if so. */
+ if (!conn) {
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ if (connection_half_edge_is_valid_sendme(ocirc->half_streams,
+ rh->stream_id)) {
+ circuit_read_valid_data(ocirc, rh->length);
+ log_info(domain, "Sendme cell on circ %u valid on half-closed "
+ "stream id %d",
+ ocirc->global_identifier, rh->stream_id);
+ }
+ }
+
+ log_info(domain, "SENDME cell dropped, unknown stream (streamid %d).",
+ rh->stream_id);
+ return 0;
+ }
+
+ /* Stream level SENDME cell. */
+ ret = sendme_process_stream_level(conn, circ, rh->length);
+ if (ret < 0) {
+ /* Means we need to close the circuit with reason ret. */
+ return ret;
+ }
+
+ /* We've now processed properly a SENDME cell, all windows have been
+ * properly updated, we'll read on the edge connection to see if we can
+ * get data out towards the end point (Exit or client) since we are now
+ * allowed to deliver more cells. */
+
+ if (circuit_queue_streams_are_blocked(circ)) {
+ /* Still waiting for queue to flush; don't touch conn */
+ return 0;
+ }
+ connection_start_reading(TO_CONN(conn));
+ /* handle whatever might still be on the inbuf */
+ if (connection_edge_package_raw_inbuf(conn, 1, NULL) < 0) {
+ /* (We already sent an end cell if possible) */
+ connection_mark_for_close(TO_CONN(conn));
+ return 0;
+ }
+ return 0;
+}
+
/** An incoming relay cell has arrived on circuit <b>circ</b>. If
* <b>conn</b> is NULL this is a control cell, else <b>cell</b> is
* destined for <b>conn</b>.
@@ -1487,7 +1629,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
}
}
- /* Tell circpad that we've recieved a recognized cell */
+ /* Tell circpad that we've received a recognized cell */
circpad_deliver_recognized_relay_cell_events(circ, rh.command, layer_hint);
/* either conn is NULL, in which case we've got a control cell, or else
@@ -1521,6 +1663,17 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
if (circpad_handle_padding_negotiated(circ, cell, layer_hint) == 0)
circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
return 0;
+ }
+
+ /* If this is a padding circuit we don't need to parse any other commands
+ * than the padding ones. Just drop them to the floor. */
+ if (circ->purpose == CIRCUIT_PURPOSE_C_CIRCUIT_PADDING) {
+ log_info(domain, "Ignored cell (%d) that arrived in padding circuit.",
+ rh.command);
+ return 0;
+ }
+
+ switch (rh.command) {
case RELAY_COMMAND_BEGIN:
case RELAY_COMMAND_BEGIN_DIR:
if (layer_hint &&
@@ -1554,22 +1707,19 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
return connection_exit_begin_conn(cell, circ);
case RELAY_COMMAND_DATA:
++stats_n_data_cells_received;
- if (( layer_hint && --layer_hint->deliver_window < 0) ||
- (!layer_hint && --circ->deliver_window < 0)) {
+
+ /* Update our circuit-level deliver window that we received a DATA cell.
+ * If the deliver window goes below 0, we end the circuit and stream due
+ * to a protocol failure. */
+ if (sendme_circuit_data_received(circ, layer_hint) < 0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"(relay data) circ deliver_window below 0. Killing.");
- if (conn) {
- /* XXXX Do we actually need to do this? Will killing the circuit
- * not send an END and mark the stream for close as appropriate? */
- connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
- connection_mark_for_close(TO_CONN(conn));
- }
+ connection_edge_end_close(conn, END_STREAM_REASON_TORPROTOCOL);
return -END_CIRC_REASON_TORPROTOCOL;
}
- log_debug(domain,"circ deliver_window now %d.", layer_hint ?
- layer_hint->deliver_window : circ->deliver_window);
- circuit_consider_sending_sendme(circ, layer_hint);
+ /* Consider sending a circuit-level SENDME cell. */
+ sendme_circuit_consider_sending(circ, layer_hint);
if (rh.stream_id == 0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Relay data cell with zero "
@@ -1592,9 +1742,14 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
return 0;
}
- if (--conn->deliver_window < 0) { /* is it below 0 after decrement? */
+ /* Update our stream-level deliver window that we just received a DATA
+ * cell. Going below 0 means we have a protocol level error so the
+ * stream and circuit are closed. */
+
+ if (sendme_stream_data_received(conn) < 0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"(relay data) conn deliver_window below 0. Killing.");
+ connection_edge_end_close(conn, END_STREAM_REASON_TORPROTOCOL);
return -END_CIRC_REASON_TORPROTOCOL;
}
/* Total all valid application bytes delivered */
@@ -1620,7 +1775,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
/* Only send a SENDME if we're not getting optimistic data; otherwise
* a SENDME could arrive before the CONNECTED.
*/
- connection_edge_consider_sending_sendme(conn);
+ sendme_connection_edge_consider_sending(conn);
}
return 0;
@@ -1813,99 +1968,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
(unsigned)circ->n_circ_id, rh.stream_id);
return 0;
case RELAY_COMMAND_SENDME:
- if (!rh.stream_id) {
- if (layer_hint) {
- if (layer_hint->package_window + CIRCWINDOW_INCREMENT >
- CIRCWINDOW_START_MAX) {
- static struct ratelim_t exit_warn_ratelim = RATELIM_INIT(600);
- log_fn_ratelim(&exit_warn_ratelim, LOG_WARN, LD_PROTOCOL,
- "Unexpected sendme cell from exit relay. "
- "Closing circ.");
- return -END_CIRC_REASON_TORPROTOCOL;
- }
- layer_hint->package_window += CIRCWINDOW_INCREMENT;
- log_debug(LD_APP,"circ-level sendme at origin, packagewindow %d.",
- layer_hint->package_window);
- circuit_resume_edge_reading(circ, layer_hint);
-
- /* We count circuit-level sendme's as valid delivered data because
- * they are rate limited.
- */
- if (CIRCUIT_IS_ORIGIN(circ)) {
- circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ),
- rh.length);
- }
-
- } else {
- if (circ->package_window + CIRCWINDOW_INCREMENT >
- CIRCWINDOW_START_MAX) {
- static struct ratelim_t client_warn_ratelim = RATELIM_INIT(600);
- log_fn_ratelim(&client_warn_ratelim,LOG_PROTOCOL_WARN, LD_PROTOCOL,
- "Unexpected sendme cell from client. "
- "Closing circ (window %d).",
- circ->package_window);
- return -END_CIRC_REASON_TORPROTOCOL;
- }
- circ->package_window += CIRCWINDOW_INCREMENT;
- log_debug(LD_APP,
- "circ-level sendme at non-origin, packagewindow %d.",
- circ->package_window);
- circuit_resume_edge_reading(circ, layer_hint);
- }
- return 0;
- }
- if (!conn) {
- if (CIRCUIT_IS_ORIGIN(circ)) {
- origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
- if (connection_half_edge_is_valid_sendme(ocirc->half_streams,
- rh.stream_id)) {
- circuit_read_valid_data(ocirc, rh.length);
- log_info(domain,
- "sendme cell on circ %u valid on half-closed "
- "stream id %d", ocirc->global_identifier, rh.stream_id);
- }
- }
-
- log_info(domain,"sendme cell dropped, unknown stream (streamid %d).",
- rh.stream_id);
- return 0;
- }
-
- /* Don't allow the other endpoint to request more than our maximum
- * (i.e. initial) stream SENDME window worth of data. Well-behaved
- * stock clients will not request more than this max (as per the check
- * in the while loop of connection_edge_consider_sending_sendme()).
- */
- if (conn->package_window + STREAMWINDOW_INCREMENT >
- STREAMWINDOW_START_MAX) {
- static struct ratelim_t stream_warn_ratelim = RATELIM_INIT(600);
- log_fn_ratelim(&stream_warn_ratelim, LOG_PROTOCOL_WARN, LD_PROTOCOL,
- "Unexpected stream sendme cell. Closing circ (window %d).",
- conn->package_window);
- return -END_CIRC_REASON_TORPROTOCOL;
- }
-
- /* At this point, the stream sendme is valid */
- if (CIRCUIT_IS_ORIGIN(circ)) {
- circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ),
- rh.length);
- }
-
- conn->package_window += STREAMWINDOW_INCREMENT;
- log_debug(domain,"stream-level sendme, packagewindow now %d.",
- conn->package_window);
- if (circuit_queue_streams_are_blocked(circ)) {
- /* Still waiting for queue to flush; don't touch conn */
- return 0;
- }
- connection_start_reading(TO_CONN(conn));
- /* handle whatever might still be on the inbuf */
- if (connection_edge_package_raw_inbuf(conn, 1, NULL) < 0) {
- /* (We already sent an end cell if possible) */
- connection_mark_for_close(TO_CONN(conn));
- return 0;
- }
- return 0;
+ return process_sendme_cell(&rh, cell, circ, conn, layer_hint, domain);
case RELAY_COMMAND_RESOLVE:
if (layer_hint) {
log_fn(LOG_PROTOCOL_WARN, LD_APP,
@@ -1979,6 +2042,84 @@ uint64_t stats_n_data_cells_received = 0;
* ever received were completely full of data. */
uint64_t stats_n_data_bytes_received = 0;
+/**
+ * Called when initializing a circuit, or when we have reached the end of the
+ * window in which we need to send some randomness so that incoming sendme
+ * cells will be unpredictable. Resets the flags and picks a new window.
+ */
+void
+circuit_reset_sendme_randomness(circuit_t *circ)
+{
+ circ->have_sent_sufficiently_random_cell = 0;
+ circ->send_randomness_after_n_cells = CIRCWINDOW_INCREMENT / 2 +
+ crypto_fast_rng_get_uint(get_thread_fast_rng(), CIRCWINDOW_INCREMENT / 2);
+}
+
+/**
+ * Any relay data payload containing fewer than this many real bytes is
+ * considered to have enough randomness to.
+ **/
+#define RELAY_PAYLOAD_LENGTH_FOR_RANDOM_SENDMES \
+ (RELAY_PAYLOAD_SIZE - CELL_PADDING_GAP - 16)
+
+/**
+ * Helper. Return the number of bytes that should be put into a cell from a
+ * given edge connection on which <b>n_available</b> bytes are available.
+ */
+STATIC size_t
+connection_edge_get_inbuf_bytes_to_package(size_t n_available,
+ int package_partial,
+ circuit_t *on_circuit)
+{
+ if (!n_available)
+ return 0;
+
+ /* Do we need to force this payload to have space for randomness? */
+ const bool force_random_bytes =
+ (on_circuit->send_randomness_after_n_cells == 0) &&
+ (! on_circuit->have_sent_sufficiently_random_cell);
+
+ /* At most how much would we like to send in this cell? */
+ size_t target_length;
+ if (force_random_bytes) {
+ target_length = RELAY_PAYLOAD_LENGTH_FOR_RANDOM_SENDMES;
+ } else {
+ target_length = RELAY_PAYLOAD_SIZE;
+ }
+
+ /* Decide how many bytes we will actually put into this cell. */
+ size_t package_length;
+ if (n_available >= target_length) { /* A full payload is available. */
+ package_length = target_length;
+ } else { /* not a full payload available */
+ if (package_partial)
+ package_length = n_available; /* just take whatever's available now */
+ else
+ return 0; /* nothing to do until we have a full payload */
+ }
+
+ /* If we reach this point, we will be definitely sending the cell. */
+ tor_assert_nonfatal(package_length > 0);
+
+ if (package_length <= RELAY_PAYLOAD_LENGTH_FOR_RANDOM_SENDMES) {
+ /* This cell will have enough randomness in the padding to make a future
+ * sendme cell unpredictable. */
+ on_circuit->have_sent_sufficiently_random_cell = 1;
+ }
+
+ if (on_circuit->send_randomness_after_n_cells == 0) {
+ /* Either this cell, or some previous cell, had enough padding to
+ * ensure sendme unpredictability. */
+ tor_assert_nonfatal(on_circuit->have_sent_sufficiently_random_cell);
+ /* Pick a new interval in which we need to send randomness. */
+ circuit_reset_sendme_randomness(on_circuit);
+ }
+
+ --on_circuit->send_randomness_after_n_cells;
+
+ return package_length;
+}
+
/** If <b>conn</b> has an entire relay payload of bytes on its inbuf (or
* <b>package_partial</b> is true), and the appropriate package windows aren't
* empty, grab a cell and send it down the circuit.
@@ -2051,17 +2192,14 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
bytes_to_process = connection_get_inbuf_len(TO_CONN(conn));
}
- if (!bytes_to_process)
+ length = connection_edge_get_inbuf_bytes_to_package(bytes_to_process,
+ package_partial, circ);
+ if (!length)
return 0;
- if (!package_partial && bytes_to_process < RELAY_PAYLOAD_SIZE)
- return 0;
+ /* If we reach this point, we will definitely be packaging bytes into
+ * a cell. */
- if (bytes_to_process > RELAY_PAYLOAD_SIZE) {
- length = RELAY_PAYLOAD_SIZE;
- } else {
- length = bytes_to_process;
- }
stats_n_data_bytes_packaged += length;
stats_n_data_cells_packaged += 1;
@@ -2096,15 +2234,17 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
return 0;
}
- if (!cpath_layer) { /* non-rendezvous exit */
- tor_assert(circ->package_window > 0);
- circ->package_window--;
- } else { /* we're an AP, or an exit on a rendezvous circ */
- tor_assert(cpath_layer->package_window > 0);
- cpath_layer->package_window--;
+ /* Handle the circuit-level SENDME package window. */
+ if (sendme_note_circuit_data_packaged(circ, cpath_layer) < 0) {
+ /* Package window has gone under 0. Protocol issue. */
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Circuit package window is below 0. Closing circuit.");
+ conn->end_reason = END_STREAM_REASON_TORPROTOCOL;
+ return -1;
}
- if (--conn->package_window <= 0) { /* is it 0 after decrement? */
+ /* Handle the stream-level SENDME package window. */
+ if (sendme_note_stream_data_packaged(conn) < 0) {
connection_stop_reading(TO_CONN(conn));
log_debug(domain,"conn->package_window reached 0.");
circuit_consider_stop_edge_reading(circ, cpath_layer);
@@ -2122,42 +2262,6 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
goto repeat_connection_edge_package_raw_inbuf;
}
-/** Called when we've just received a relay data cell, when
- * we've just finished flushing all bytes to stream <b>conn</b>,
- * or when we've flushed *some* bytes to the stream <b>conn</b>.
- *
- * If conn->outbuf is not too full, and our deliver window is
- * low, send back a suitable number of stream-level sendme cells.
- */
-void
-connection_edge_consider_sending_sendme(edge_connection_t *conn)
-{
- circuit_t *circ;
-
- if (connection_outbuf_too_full(TO_CONN(conn)))
- return;
-
- circ = circuit_get_by_edge_conn(conn);
- if (!circ) {
- /* this can legitimately happen if the destroy has already
- * arrived and torn down the circuit */
- log_info(LD_APP,"No circuit associated with conn. Skipping.");
- return;
- }
-
- while (conn->deliver_window <= STREAMWINDOW_START - STREAMWINDOW_INCREMENT) {
- log_debug(conn->base_.type == CONN_TYPE_AP ?LD_APP:LD_EXIT,
- "Outbuf %d, Queuing stream sendme.",
- (int)conn->base_.outbuf_flushlen);
- conn->deliver_window += STREAMWINDOW_INCREMENT;
- if (connection_edge_send_command(conn, RELAY_COMMAND_SENDME,
- NULL, 0) < 0) {
- log_warn(LD_APP,"connection_edge_send_command failed. Skipping.");
- return; /* the circuit's closed, don't continue */
- }
- }
-}
-
/** The circuit <b>circ</b> has received a circuit-level sendme
* (on hop <b>layer_hint</b>, if we're the OP). Go through all the
* attached streams and let them resume reading and packaging, if
@@ -2180,12 +2284,6 @@ circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
circ, layer_hint);
}
-void
-stream_choice_seed_weak_rng(void)
-{
- crypto_seed_weak_rng(&stream_choice_rng);
-}
-
/** A helper function for circuit_resume_edge_reading() above.
* The arguments are the same, except that <b>conn</b> is the head
* of a linked list of edge streams that should each be considered.
@@ -2237,7 +2335,8 @@ circuit_resume_edge_reading_helper(edge_connection_t *first_conn,
int num_streams = 0;
for (conn = first_conn; conn; conn = conn->next_stream) {
num_streams++;
- if (tor_weak_random_one_in_n(&stream_choice_rng, num_streams)) {
+
+ if (crypto_fast_rng_one_in_n(get_thread_fast_rng(), num_streams)) {
chosen_stream = conn;
}
/* Invariant: chosen_stream has been chosen uniformly at random from
@@ -2379,33 +2478,6 @@ circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
return 0;
}
-/** Check if the deliver_window for circuit <b>circ</b> (at hop
- * <b>layer_hint</b> if it's defined) is low enough that we should
- * send a circuit-level sendme back down the circuit. If so, send
- * enough sendmes that the window would be overfull if we sent any
- * more.
- */
-static void
-circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint)
-{
-// log_fn(LOG_INFO,"Considering: layer_hint is %s",
-// layer_hint ? "defined" : "null");
- while ((layer_hint ? layer_hint->deliver_window : circ->deliver_window) <=
- CIRCWINDOW_START - CIRCWINDOW_INCREMENT) {
- log_debug(LD_CIRC,"Queuing circuit sendme.");
- if (layer_hint)
- layer_hint->deliver_window += CIRCWINDOW_INCREMENT;
- else
- circ->deliver_window += CIRCWINDOW_INCREMENT;
- if (relay_send_command_from_edge(0, circ, RELAY_COMMAND_SENDME,
- NULL, 0, layer_hint) < 0) {
- log_warn(LD_CIRC,
- "relay_send_command_from_edge failed. Circuit's closed.");
- return; /* the circuit's closed, don't continue */
- }
- }
-}
-
/** The total number of cells we have allocated. */
static size_t total_cells_allocated = 0;
@@ -2780,7 +2852,7 @@ set_streams_blocked_on_circ(circuit_t *circ, channel_t *chan,
}
/** Extract the command from a packed cell. */
-static uint8_t
+uint8_t
packed_cell_get_command(const packed_cell_t *cell, int wide_circ_ids)
{
if (wide_circ_ids) {
diff --git a/src/core/or/relay.h b/src/core/or/relay.h
index 044f6be156..79036f97bd 100644
--- a/src/core/or/relay.h
+++ b/src/core/or/relay.h
@@ -42,6 +42,7 @@ int connection_edge_package_raw_inbuf(edge_connection_t *conn,
int package_partial,
int *max_cells);
void connection_edge_consider_sending_sendme(edge_connection_t *conn);
+void circuit_reset_sendme_randomness(circuit_t *circ);
extern uint64_t stats_n_data_cells_packaged;
extern uint64_t stats_n_data_bytes_packaged;
@@ -94,9 +95,8 @@ const uint8_t *decode_address_from_payload(tor_addr_t *addr_out,
int payload_len);
void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan);
-void stream_choice_seed_weak_rng(void);
-
circid_t packed_cell_get_circid(const packed_cell_t *cell, int wide_circ_ids);
+uint8_t packed_cell_get_command(const packed_cell_t *cell, int wide_circ_ids);
#ifdef RELAY_PRIVATE
STATIC int connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
@@ -122,8 +122,11 @@ STATIC int cell_queues_check_size(void);
STATIC int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
edge_connection_t *conn,
crypt_path_t *layer_hint);
+STATIC size_t get_pad_cell_offset(size_t payload_len);
+STATIC size_t connection_edge_get_inbuf_bytes_to_package(size_t n_available,
+ int package_partial,
+ circuit_t *on_circuit);
#endif /* defined(RELAY_PRIVATE) */
#endif /* !defined(TOR_RELAY_H) */
-
diff --git a/src/core/or/relay_crypto_st.h b/src/core/or/relay_crypto_st.h
index dafce257c7..83bbd329a6 100644
--- a/src/core/or/relay_crypto_st.h
+++ b/src/core/or/relay_crypto_st.h
@@ -25,7 +25,9 @@ struct relay_crypto_t {
/** Digest state for cells heading away from the OR at this step. */
struct crypto_digest_t *b_digest;
+ /** Digest used for the next SENDME cell if any. */
+ uint8_t sendme_digest[DIGEST_LEN];
};
#undef crypto_cipher_t
-#endif
+#endif /* !defined(RELAY_CRYPTO_ST_H) */
diff --git a/src/core/or/sendme.c b/src/core/or/sendme.c
new file mode 100644
index 0000000000..47ac95f3cf
--- /dev/null
+++ b/src/core/or/sendme.c
@@ -0,0 +1,710 @@
+/* Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file sendme.c
+ * \brief Code that is related to SENDME cells both in terms of
+ * creating/parsing cells and handling the content.
+ */
+
+#define SENDME_PRIVATE
+
+#include "core/or/or.h"
+
+#include "app/config/config.h"
+#include "core/crypto/relay_crypto.h"
+#include "core/mainloop/connection.h"
+#include "core/or/cell_st.h"
+#include "core/or/crypt_path.h"
+#include "core/or/circuitlist.h"
+#include "core/or/circuituse.h"
+#include "core/or/or_circuit_st.h"
+#include "core/or/relay.h"
+#include "core/or/sendme.h"
+#include "feature/nodelist/networkstatus.h"
+#include "lib/ctime/di_ops.h"
+#include "trunnel/sendme.h"
+
+/* Return the minimum version given by the consensus (if any) that should be
+ * used when emitting a SENDME cell. */
+STATIC int
+get_emit_min_version(void)
+{
+ return networkstatus_get_param(NULL, "sendme_emit_min_version",
+ SENDME_EMIT_MIN_VERSION_DEFAULT,
+ SENDME_EMIT_MIN_VERSION_MIN,
+ SENDME_EMIT_MIN_VERSION_MAX);
+}
+
+/* Return the minimum version given by the consensus (if any) that should be
+ * accepted when receiving a SENDME cell. */
+STATIC int
+get_accept_min_version(void)
+{
+ return networkstatus_get_param(NULL, "sendme_accept_min_version",
+ SENDME_ACCEPT_MIN_VERSION_DEFAULT,
+ SENDME_ACCEPT_MIN_VERSION_MIN,
+ SENDME_ACCEPT_MIN_VERSION_MAX);
+}
+
+/* Pop the first cell digset on the given circuit from the SENDME last digests
+ * list. NULL is returned if the list is uninitialized or empty.
+ *
+ * The caller gets ownership of the returned digest thus is responsible for
+ * freeing the memory. */
+static uint8_t *
+pop_first_cell_digest(const circuit_t *circ)
+{
+ uint8_t *circ_digest;
+
+ tor_assert(circ);
+
+ if (circ->sendme_last_digests == NULL ||
+ smartlist_len(circ->sendme_last_digests) == 0) {
+ return NULL;
+ }
+
+ /* More cell digest than the SENDME window is never suppose to happen. The
+ * cell should have been rejected before reaching this point due to its
+ * package_window down to 0 leading to a circuit close. Scream loudly but
+ * still pop the element so we don't memory leak. */
+ tor_assert_nonfatal(smartlist_len(circ->sendme_last_digests) <=
+ CIRCWINDOW_START_MAX / CIRCWINDOW_INCREMENT);
+
+ circ_digest = smartlist_get(circ->sendme_last_digests, 0);
+ smartlist_del_keeporder(circ->sendme_last_digests, 0);
+ return circ_digest;
+}
+
+/* Return true iff the given cell digest matches the first digest in the
+ * circuit sendme list. */
+static bool
+v1_digest_matches(const uint8_t *circ_digest, const uint8_t *cell_digest)
+{
+ tor_assert(circ_digest);
+ tor_assert(cell_digest);
+
+ /* Compare the digest with the one in the SENDME. This cell is invalid
+ * without a perfect match. */
+ if (tor_memneq(circ_digest, cell_digest, TRUNNEL_SENDME_V1_DIGEST_LEN)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "SENDME v1 cell digest do not match.");
+ return false;
+ }
+
+ /* Digests matches! */
+ return true;
+}
+
+/* Return true iff the given decoded SENDME version 1 cell is valid and
+ * matches the expected digest on the circuit.
+ *
+ * Validation is done by comparing the digest in the cell from the previous
+ * cell we saw which tells us that the other side has in fact seen that cell.
+ * See proposal 289 for more details. */
+static bool
+cell_v1_is_valid(const sendme_cell_t *cell, const uint8_t *circ_digest)
+{
+ tor_assert(cell);
+ tor_assert(circ_digest);
+
+ const uint8_t *cell_digest = sendme_cell_getconstarray_data_v1_digest(cell);
+ return v1_digest_matches(circ_digest, cell_digest);
+}
+
+/* Return true iff the given cell version can be handled or if the minimum
+ * accepted version from the consensus is known to us. */
+STATIC bool
+cell_version_can_be_handled(uint8_t cell_version)
+{
+ int accept_version = get_accept_min_version();
+
+ /* We will first check if the consensus minimum accepted version can be
+ * handled by us and if not, regardless of the cell version we got, we can't
+ * continue. */
+ if (accept_version > SENDME_MAX_SUPPORTED_VERSION) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Unable to accept SENDME version %u (from consensus). "
+ "We only support <= %u. Probably your tor is too old?",
+ accept_version, SENDME_MAX_SUPPORTED_VERSION);
+ goto invalid;
+ }
+
+ /* Then, is this version below the accepted version from the consensus? If
+ * yes, we must not handle it. */
+ if (cell_version < accept_version) {
+ log_info(LD_PROTOCOL, "Unacceptable SENDME version %u. Only "
+ "accepting %u (from consensus). Closing circuit.",
+ cell_version, accept_version);
+ goto invalid;
+ }
+
+ /* Is this cell version supported by us? */
+ if (cell_version > SENDME_MAX_SUPPORTED_VERSION) {
+ log_info(LD_PROTOCOL, "SENDME cell version %u is not supported by us. "
+ "We only support <= %u",
+ cell_version, SENDME_MAX_SUPPORTED_VERSION);
+ goto invalid;
+ }
+
+ return true;
+ invalid:
+ return false;
+}
+
+/* Return true iff the encoded SENDME cell in cell_payload of length
+ * cell_payload_len is valid. For each version:
+ *
+ * 0: No validation
+ * 1: Authenticated with last cell digest.
+ *
+ * This is the main critical function to make sure we can continue to
+ * send/recv cells on a circuit. If the SENDME is invalid, the circuit should
+ * be marked for close by the caller. */
+STATIC bool
+sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload,
+ size_t cell_payload_len)
+{
+ uint8_t cell_version;
+ uint8_t *circ_digest = NULL;
+ sendme_cell_t *cell = NULL;
+
+ tor_assert(circ);
+ tor_assert(cell_payload);
+
+ /* An empty payload means version 0 so skip trunnel parsing. We won't be
+ * able to parse a 0 length buffer into a valid SENDME cell. */
+ if (cell_payload_len == 0) {
+ cell_version = 0;
+ } else {
+ /* First we'll decode the cell so we can get the version. */
+ if (sendme_cell_parse(&cell, cell_payload, cell_payload_len) < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Unparseable SENDME cell received. Closing circuit.");
+ goto invalid;
+ }
+ cell_version = sendme_cell_get_version(cell);
+ }
+
+ /* Validate that we can handle this cell version. */
+ if (!cell_version_can_be_handled(cell_version)) {
+ goto invalid;
+ }
+
+ /* Pop the first element that was added (FIFO). We do that regardless of the
+ * version so we don't accumulate on the circuit if v0 is used by the other
+ * end point. */
+ circ_digest = pop_first_cell_digest(circ);
+ if (circ_digest == NULL) {
+ /* We shouldn't have received a SENDME if we have no digests. Log at
+ * protocol warning because it can be tricked by sending many SENDMEs
+ * without prior data cell. */
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "We received a SENDME but we have no cell digests to match. "
+ "Closing circuit.");
+ goto invalid;
+ }
+
+ /* Validate depending on the version now. */
+ switch (cell_version) {
+ case 0x01:
+ if (!cell_v1_is_valid(cell, circ_digest)) {
+ goto invalid;
+ }
+ break;
+ case 0x00:
+ /* Version 0, there is no work to be done on the payload so it is
+ * necessarily valid if we pass the version validation. */
+ break;
+ default:
+ log_warn(LD_PROTOCOL, "Unknown SENDME cell version %d received.",
+ cell_version);
+ tor_assert_nonfatal_unreached();
+ break;
+ }
+
+ /* Valid cell. */
+ sendme_cell_free(cell);
+ tor_free(circ_digest);
+ return true;
+ invalid:
+ sendme_cell_free(cell);
+ tor_free(circ_digest);
+ return false;
+}
+
+/* Build and encode a version 1 SENDME cell into payload, which must be at
+ * least of RELAY_PAYLOAD_SIZE bytes, using the digest for the cell data.
+ *
+ * Return the size in bytes of the encoded cell in payload. A negative value
+ * is returned on encoding failure. */
+STATIC ssize_t
+build_cell_payload_v1(const uint8_t *cell_digest, uint8_t *payload)
+{
+ ssize_t len = -1;
+ sendme_cell_t *cell = NULL;
+
+ tor_assert(cell_digest);
+ tor_assert(payload);
+
+ cell = sendme_cell_new();
+
+ /* Building a payload for version 1. */
+ sendme_cell_set_version(cell, 0x01);
+ /* Set the data length field for v1. */
+ sendme_cell_set_data_len(cell, TRUNNEL_SENDME_V1_DIGEST_LEN);
+
+ /* Copy the digest into the data payload. */
+ memcpy(sendme_cell_getarray_data_v1_digest(cell), cell_digest,
+ sendme_cell_get_data_len(cell));
+
+ /* Finally, encode the cell into the payload. */
+ len = sendme_cell_encode(payload, RELAY_PAYLOAD_SIZE, cell);
+
+ sendme_cell_free(cell);
+ return len;
+}
+
+/* Send a circuit-level SENDME on the given circuit using the layer_hint if
+ * not NULL. The digest is only used for version 1.
+ *
+ * Return 0 on success else a negative value and the circuit will be closed
+ * because we failed to send the cell on it. */
+static int
+send_circuit_level_sendme(circuit_t *circ, crypt_path_t *layer_hint,
+ const uint8_t *cell_digest)
+{
+ uint8_t emit_version;
+ uint8_t payload[RELAY_PAYLOAD_SIZE];
+ ssize_t payload_len;
+
+ tor_assert(circ);
+ tor_assert(cell_digest);
+
+ emit_version = get_emit_min_version();
+ switch (emit_version) {
+ case 0x01:
+ payload_len = build_cell_payload_v1(cell_digest, payload);
+ if (BUG(payload_len < 0)) {
+ /* Unable to encode the cell, abort. We can recover from this by closing
+ * the circuit but in theory it should never happen. */
+ return -1;
+ }
+ log_debug(LD_PROTOCOL, "Emitting SENDME version 1 cell.");
+ break;
+ case 0x00:
+ /* Fallthrough because default is to use v0. */
+ default:
+ /* Unknown version, fallback to version 0 meaning no payload. */
+ payload_len = 0;
+ log_debug(LD_PROTOCOL, "Emitting SENDME version 0 cell. "
+ "Consensus emit version is %d", emit_version);
+ break;
+ }
+
+ if (relay_send_command_from_edge(0, circ, RELAY_COMMAND_SENDME,
+ (char *) payload, payload_len,
+ layer_hint) < 0) {
+ log_warn(LD_CIRC,
+ "SENDME relay_send_command_from_edge failed. Circuit's closed.");
+ return -1; /* the circuit's closed, don't continue */
+ }
+ return 0;
+}
+
+/* Record the cell digest only if the next cell is expected to be a SENDME. */
+static void
+record_cell_digest_on_circ(circuit_t *circ, const uint8_t *sendme_digest)
+{
+ tor_assert(circ);
+ tor_assert(sendme_digest);
+
+ /* Add the digest to the last seen list in the circuit. */
+ if (circ->sendme_last_digests == NULL) {
+ circ->sendme_last_digests = smartlist_new();
+ }
+ smartlist_add(circ->sendme_last_digests,
+ tor_memdup(sendme_digest, DIGEST_LEN));
+}
+
+/*
+ * Public API
+ */
+
+/** Return true iff the next cell for the given cell window is expected to be
+ * a SENDME.
+ *
+ * We are able to know that because the package or deliver window value minus
+ * one cell (the possible SENDME cell) should be a multiple of the increment
+ * window value. */
+static bool
+circuit_sendme_cell_is_next(int window)
+{
+ /* At the start of the window, no SENDME will be expected. */
+ if (window == CIRCWINDOW_START) {
+ return false;
+ }
+
+ /* Are we at the limit of the increment and if not, we don't expect next
+ * cell is a SENDME.
+ *
+ * We test against the window minus 1 because when we are looking if the
+ * next cell is a SENDME, the window (either package or deliver) hasn't been
+ * decremented just yet so when this is called, we are currently processing
+ * the "window - 1" cell.
+ *
+ * This function is used when recording a cell digest and this is done quite
+ * low in the stack when decrypting or encrypting a cell. The window is only
+ * updated once the cell is actually put in the outbuf. */
+ if (((window - 1) % CIRCWINDOW_INCREMENT) != 0) {
+ return false;
+ }
+
+ /* Next cell is expected to be a SENDME. */
+ return true;
+}
+
+/** Called when we've just received a relay data cell, when we've just
+ * finished flushing all bytes to stream <b>conn</b>, or when we've flushed
+ * *some* bytes to the stream <b>conn</b>.
+ *
+ * If conn->outbuf is not too full, and our deliver window is low, send back a
+ * suitable number of stream-level sendme cells.
+ */
+void
+sendme_connection_edge_consider_sending(edge_connection_t *conn)
+{
+ tor_assert(conn);
+
+ int log_domain = TO_CONN(conn)->type == CONN_TYPE_AP ? LD_APP : LD_EXIT;
+
+ /* Don't send it if we still have data to deliver. */
+ if (connection_outbuf_too_full(TO_CONN(conn))) {
+ goto end;
+ }
+
+ if (circuit_get_by_edge_conn(conn) == NULL) {
+ /* This can legitimately happen if the destroy has already arrived and
+ * torn down the circuit. */
+ log_info(log_domain, "No circuit associated with edge connection. "
+ "Skipping sending SENDME.");
+ goto end;
+ }
+
+ while (conn->deliver_window <=
+ (STREAMWINDOW_START - STREAMWINDOW_INCREMENT)) {
+ log_debug(log_domain, "Outbuf %" TOR_PRIuSZ ", queuing stream SENDME.",
+ TO_CONN(conn)->outbuf_flushlen);
+ conn->deliver_window += STREAMWINDOW_INCREMENT;
+ if (connection_edge_send_command(conn, RELAY_COMMAND_SENDME,
+ NULL, 0) < 0) {
+ log_warn(LD_BUG, "connection_edge_send_command failed while sending "
+ "a SENDME. Circuit probably closed, skipping.");
+ goto end; /* The circuit's closed, don't continue */
+ }
+ }
+
+ end:
+ return;
+}
+
+/** Check if the deliver_window for circuit <b>circ</b> (at hop
+ * <b>layer_hint</b> if it's defined) is low enough that we should
+ * send a circuit-level sendme back down the circuit. If so, send
+ * enough sendmes that the window would be overfull if we sent any
+ * more.
+ */
+void
+sendme_circuit_consider_sending(circuit_t *circ, crypt_path_t *layer_hint)
+{
+ bool sent_one_sendme = false;
+ const uint8_t *digest;
+
+ while ((layer_hint ? layer_hint->deliver_window : circ->deliver_window) <=
+ CIRCWINDOW_START - CIRCWINDOW_INCREMENT) {
+ log_debug(LD_CIRC,"Queuing circuit sendme.");
+ if (layer_hint) {
+ layer_hint->deliver_window += CIRCWINDOW_INCREMENT;
+ digest = cpath_get_sendme_digest(layer_hint);
+ } else {
+ circ->deliver_window += CIRCWINDOW_INCREMENT;
+ digest = relay_crypto_get_sendme_digest(&TO_OR_CIRCUIT(circ)->crypto);
+ }
+ if (send_circuit_level_sendme(circ, layer_hint, digest) < 0) {
+ return; /* The circuit's closed, don't continue */
+ }
+ /* Current implementation is not suppose to send multiple SENDME at once
+ * because this means we would use the same relay crypto digest for each
+ * SENDME leading to a mismatch on the other side and the circuit to
+ * collapse. Scream loudly if it ever happens so we can address it. */
+ tor_assert_nonfatal(!sent_one_sendme);
+ sent_one_sendme = true;
+ }
+}
+
+/* Process a circuit-level SENDME cell that we just received. The layer_hint,
+ * if not NULL, is the Exit hop of the connection which means that we are a
+ * client. In that case, circ must be an origin circuit. The cell_body_len is
+ * the length of the SENDME cell payload (excluding the header). The
+ * cell_payload is the payload.
+ *
+ * Return 0 on success (the SENDME is valid and the package window has
+ * been updated properly).
+ *
+ * On error, a negative value is returned, which indicates that the
+ * circuit must be closed using the value as the reason for it. */
+int
+sendme_process_circuit_level(crypt_path_t *layer_hint,
+ circuit_t *circ, const uint8_t *cell_payload,
+ uint16_t cell_payload_len)
+{
+ tor_assert(circ);
+ tor_assert(cell_payload);
+
+ /* Validate the SENDME cell. Depending on the version, different validation
+ * can be done. An invalid SENDME requires us to close the circuit. */
+ if (!sendme_is_valid(circ, cell_payload, cell_payload_len)) {
+ return -END_CIRC_REASON_TORPROTOCOL;
+ }
+
+ /* If we are the origin of the circuit, we are the Client so we use the
+ * layer hint (the Exit hop) for the package window tracking. */
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ /* If we are the origin of the circuit, it is impossible to not have a
+ * cpath. Just in case, bug on it and close the circuit. */
+ if (BUG(layer_hint == NULL)) {
+ return -END_CIRC_REASON_TORPROTOCOL;
+ }
+ if ((layer_hint->package_window + CIRCWINDOW_INCREMENT) >
+ CIRCWINDOW_START_MAX) {
+ static struct ratelim_t exit_warn_ratelim = RATELIM_INIT(600);
+ log_fn_ratelim(&exit_warn_ratelim, LOG_WARN, LD_PROTOCOL,
+ "Unexpected sendme cell from exit relay. "
+ "Closing circ.");
+ return -END_CIRC_REASON_TORPROTOCOL;
+ }
+ layer_hint->package_window += CIRCWINDOW_INCREMENT;
+ log_debug(LD_APP, "circ-level sendme at origin, packagewindow %d.",
+ layer_hint->package_window);
+
+ /* We count circuit-level sendme's as valid delivered data because they
+ * are rate limited. */
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), cell_payload_len);
+ } else {
+ /* We aren't the origin of this circuit so we are the Exit and thus we
+ * track the package window with the circuit object. */
+ if ((circ->package_window + CIRCWINDOW_INCREMENT) >
+ CIRCWINDOW_START_MAX) {
+ static struct ratelim_t client_warn_ratelim = RATELIM_INIT(600);
+ log_fn_ratelim(&client_warn_ratelim, LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Unexpected sendme cell from client. "
+ "Closing circ (window %d).", circ->package_window);
+ return -END_CIRC_REASON_TORPROTOCOL;
+ }
+ circ->package_window += CIRCWINDOW_INCREMENT;
+ log_debug(LD_EXIT, "circ-level sendme at non-origin, packagewindow %d.",
+ circ->package_window);
+ }
+
+ return 0;
+}
+
+/* Process a stream-level SENDME cell that we just received. The conn is the
+ * edge connection (stream) that the circuit circ is associated with. The
+ * cell_body_len is the length of the payload (excluding the header).
+ *
+ * Return 0 on success (the SENDME is valid and the package window has
+ * been updated properly).
+ *
+ * On error, a negative value is returned, which indicates that the
+ * circuit must be closed using the value as the reason for it. */
+int
+sendme_process_stream_level(edge_connection_t *conn, circuit_t *circ,
+ uint16_t cell_body_len)
+{
+ tor_assert(conn);
+ tor_assert(circ);
+
+ /* Don't allow the other endpoint to request more than our maximum (i.e.
+ * initial) stream SENDME window worth of data. Well-behaved stock clients
+ * will not request more than this max (as per the check in the while loop
+ * of sendme_connection_edge_consider_sending()). */
+ if ((conn->package_window + STREAMWINDOW_INCREMENT) >
+ STREAMWINDOW_START_MAX) {
+ static struct ratelim_t stream_warn_ratelim = RATELIM_INIT(600);
+ log_fn_ratelim(&stream_warn_ratelim, LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Unexpected stream sendme cell. Closing circ (window %d).",
+ conn->package_window);
+ return -END_CIRC_REASON_TORPROTOCOL;
+ }
+ /* At this point, the stream sendme is valid */
+ conn->package_window += STREAMWINDOW_INCREMENT;
+
+ /* We count circuit-level sendme's as valid delivered data because they are
+ * rate limited. */
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), cell_body_len);
+ }
+
+ log_debug(CIRCUIT_IS_ORIGIN(circ) ? LD_APP : LD_EXIT,
+ "stream-level sendme, package_window now %d.",
+ conn->package_window);
+ return 0;
+}
+
+/* Called when a relay DATA cell is received on the given circuit. If
+ * layer_hint is NULL, this means we are the Exit end point else we are the
+ * Client. Update the deliver window and return its new value. */
+int
+sendme_circuit_data_received(circuit_t *circ, crypt_path_t *layer_hint)
+{
+ int deliver_window, domain;
+
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ tor_assert(layer_hint);
+ --layer_hint->deliver_window;
+ deliver_window = layer_hint->deliver_window;
+ domain = LD_APP;
+ } else {
+ tor_assert(!layer_hint);
+ --circ->deliver_window;
+ deliver_window = circ->deliver_window;
+ domain = LD_EXIT;
+ }
+
+ log_debug(domain, "Circuit deliver_window now %d.", deliver_window);
+ return deliver_window;
+}
+
+/* Called when a relay DATA cell is received for the given edge connection
+ * conn. Update the deliver window and return its new value. */
+int
+sendme_stream_data_received(edge_connection_t *conn)
+{
+ tor_assert(conn);
+ return --conn->deliver_window;
+}
+
+/* Called when a relay DATA cell is packaged on the given circuit. If
+ * layer_hint is NULL, this means we are the Exit end point else we are the
+ * Client. Update the package window and return its new value. */
+int
+sendme_note_circuit_data_packaged(circuit_t *circ, crypt_path_t *layer_hint)
+{
+ int package_window, domain;
+
+ tor_assert(circ);
+
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ /* Client side. */
+ tor_assert(layer_hint);
+ --layer_hint->package_window;
+ package_window = layer_hint->package_window;
+ domain = LD_APP;
+ } else {
+ /* Exit side. */
+ tor_assert(!layer_hint);
+ --circ->package_window;
+ package_window = circ->package_window;
+ domain = LD_EXIT;
+ }
+
+ log_debug(domain, "Circuit package_window now %d.", package_window);
+ return package_window;
+}
+
+/* Called when a relay DATA cell is packaged for the given edge connection
+ * conn. Update the package window and return its new value. */
+int
+sendme_note_stream_data_packaged(edge_connection_t *conn)
+{
+ tor_assert(conn);
+
+ --conn->package_window;
+ log_debug(LD_APP, "Stream package_window now %d.", conn->package_window);
+ return conn->package_window;
+}
+
+/* Record the cell digest into the circuit sendme digest list depending on
+ * which edge we are. The digest is recorded only if we expect the next cell
+ * that we will receive is a SENDME so we can match the digest. */
+void
+sendme_record_cell_digest_on_circ(circuit_t *circ, crypt_path_t *cpath)
+{
+ int package_window;
+ uint8_t *sendme_digest;
+
+ tor_assert(circ);
+
+ package_window = circ->package_window;
+ if (cpath) {
+ package_window = cpath->package_window;
+ }
+
+ /* Is this the last cell before a SENDME? The idea is that if the
+ * package_window reaches a multiple of the increment, after this cell, we
+ * should expect a SENDME. */
+ if (!circuit_sendme_cell_is_next(package_window)) {
+ return;
+ }
+
+ /* Getting the digest is expensive so we only do it once we are certain to
+ * record it on the circuit. */
+ if (cpath) {
+ sendme_digest = cpath_get_sendme_digest(cpath);
+ } else {
+ sendme_digest =
+ relay_crypto_get_sendme_digest(&TO_OR_CIRCUIT(circ)->crypto);
+ }
+
+ record_cell_digest_on_circ(circ, sendme_digest);
+}
+
+/* Called once we decrypted a cell and recognized it. Record the cell digest
+ * as the next sendme digest only if the next cell we'll send on the circuit
+ * is expected to be a SENDME. */
+void
+sendme_record_received_cell_digest(circuit_t *circ, crypt_path_t *cpath)
+{
+ tor_assert(circ);
+
+ /* Only record if the next cell is expected to be a SENDME. */
+ if (!circuit_sendme_cell_is_next(cpath ? cpath->deliver_window :
+ circ->deliver_window)) {
+ return;
+ }
+
+ if (cpath) {
+ /* Record incoming digest. */
+ cpath_sendme_record_cell_digest(cpath, false);
+ } else {
+ /* Record foward digest. */
+ relay_crypto_record_sendme_digest(&TO_OR_CIRCUIT(circ)->crypto, true);
+ }
+}
+
+/* Called once we encrypted a cell. Record the cell digest as the next sendme
+ * digest only if the next cell we expect to receive is a SENDME so we can
+ * match the digests. */
+void
+sendme_record_sending_cell_digest(circuit_t *circ, crypt_path_t *cpath)
+{
+ tor_assert(circ);
+
+ /* Only record if the next cell is expected to be a SENDME. */
+ if (!circuit_sendme_cell_is_next(cpath ? cpath->package_window :
+ circ->package_window)) {
+ goto end;
+ }
+
+ if (cpath) {
+ /* Record the forward digest. */
+ cpath_sendme_record_cell_digest(cpath, true);
+ } else {
+ /* Record the incoming digest. */
+ relay_crypto_record_sendme_digest(&TO_OR_CIRCUIT(circ)->crypto, false);
+ }
+
+ end:
+ return;
+}
diff --git a/src/core/or/sendme.h b/src/core/or/sendme.h
new file mode 100644
index 0000000000..20477103fd
--- /dev/null
+++ b/src/core/or/sendme.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file sendme.h
+ * \brief Header file for sendme.c.
+ **/
+
+#ifndef TOR_SENDME_H
+#define TOR_SENDME_H
+
+#include "core/or/edge_connection_st.h"
+#include "core/or/crypt_path_st.h"
+#include "core/or/circuit_st.h"
+
+/* Sending SENDME cell. */
+void sendme_connection_edge_consider_sending(edge_connection_t *edge_conn);
+void sendme_circuit_consider_sending(circuit_t *circ,
+ crypt_path_t *layer_hint);
+
+/* Processing SENDME cell. */
+int sendme_process_circuit_level(crypt_path_t *layer_hint,
+ circuit_t *circ, const uint8_t *cell_payload,
+ uint16_t cell_payload_len);
+int sendme_process_stream_level(edge_connection_t *conn, circuit_t *circ,
+ uint16_t cell_body_len);
+
+/* Update deliver window functions. */
+int sendme_stream_data_received(edge_connection_t *conn);
+int sendme_circuit_data_received(circuit_t *circ, crypt_path_t *layer_hint);
+
+/* Update package window functions. */
+int sendme_note_circuit_data_packaged(circuit_t *circ,
+ crypt_path_t *layer_hint);
+int sendme_note_stream_data_packaged(edge_connection_t *conn);
+
+/* Record cell digest on circuit. */
+void sendme_record_cell_digest_on_circ(circuit_t *circ, crypt_path_t *cpath);
+/* Record cell digest as the SENDME digest. */
+void sendme_record_received_cell_digest(circuit_t *circ, crypt_path_t *cpath);
+void sendme_record_sending_cell_digest(circuit_t *circ, crypt_path_t *cpath);
+
+/* Private section starts. */
+#ifdef SENDME_PRIVATE
+
+/* The maximum supported version. Above that value, the cell can't be
+ * recognized as a valid SENDME. */
+#define SENDME_MAX_SUPPORTED_VERSION 1
+
+/* The cell version constants for when emitting a cell. */
+#define SENDME_EMIT_MIN_VERSION_DEFAULT 0
+#define SENDME_EMIT_MIN_VERSION_MIN 0
+#define SENDME_EMIT_MIN_VERSION_MAX UINT8_MAX
+
+/* The cell version constants for when accepting a cell. */
+#define SENDME_ACCEPT_MIN_VERSION_DEFAULT 0
+#define SENDME_ACCEPT_MIN_VERSION_MIN 0
+#define SENDME_ACCEPT_MIN_VERSION_MAX UINT8_MAX
+
+/*
+ * Unit tests declaractions.
+ */
+#ifdef TOR_UNIT_TESTS
+
+STATIC int get_emit_min_version(void);
+STATIC int get_accept_min_version(void);
+
+STATIC bool cell_version_can_be_handled(uint8_t cell_version);
+
+STATIC ssize_t build_cell_payload_v1(const uint8_t *cell_digest,
+ uint8_t *payload);
+STATIC bool sendme_is_valid(const circuit_t *circ,
+ const uint8_t *cell_payload,
+ size_t cell_payload_len);
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
+#endif /* defined(SENDME_PRIVATE) */
+
+#endif /* !defined(TOR_SENDME_H) */
diff --git a/src/core/or/server_port_cfg_st.h b/src/core/or/server_port_cfg_st.h
index bd026af7ee..0738735c61 100644
--- a/src/core/or/server_port_cfg_st.h
+++ b/src/core/or/server_port_cfg_st.h
@@ -16,5 +16,5 @@ struct server_port_cfg_t {
unsigned int bind_ipv6_only : 1;
};
-#endif
+#endif /* !defined(SERVER_PORT_CFG_ST_H) */
diff --git a/src/core/or/socks_request_st.h b/src/core/or/socks_request_st.h
index 5922870c61..9fb941ff7e 100644
--- a/src/core/or/socks_request_st.h
+++ b/src/core/or/socks_request_st.h
@@ -74,4 +74,4 @@ struct socks_request_t {
uint8_t socks5_atyp; /* SOCKS5 address type */
};
-#endif
+#endif /* !defined(SOCKS_REQUEST_ST_H) */
diff --git a/src/core/or/tor_version_st.h b/src/core/or/tor_version_st.h
index 716429bd32..c5bdcaf07b 100644
--- a/src/core/or/tor_version_st.h
+++ b/src/core/or/tor_version_st.h
@@ -28,5 +28,5 @@ struct tor_version_t {
char git_tag[DIGEST_LEN];
};
-#endif
+#endif /* !defined(TOR_VERSION_ST_H) */
diff --git a/src/core/or/var_cell_st.h b/src/core/or/var_cell_st.h
index 4287c83f6d..607c0d6c83 100644
--- a/src/core/or/var_cell_st.h
+++ b/src/core/or/var_cell_st.h
@@ -19,5 +19,5 @@ struct var_cell_t {
uint8_t payload[FLEXIBLE_ARRAY_MEMBER];
};
-#endif
+#endif /* !defined(VAR_CELL_ST_H) */
diff --git a/src/core/or/versions.c b/src/core/or/versions.c
index 2a572d4704..06417bb4eb 100644
--- a/src/core/or/versions.c
+++ b/src/core/or/versions.c
@@ -448,8 +448,9 @@ memoize_protover_summary(protover_summary_flags_t *out,
out->supports_v3_rendezvous_point =
protocol_list_supports_protocol(protocols, PRT_HSREND,
PROTOVER_HS_RENDEZVOUS_POINT_V3);
- out->supports_padding =
- protocol_list_supports_protocol(protocols, PRT_PADDING, 1);
+ out->supports_hs_setup_padding =
+ protocol_list_supports_protocol(protocols, PRT_PADDING,
+ PROTOVER_HS_SETUP_PADDING);
protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out));
cached = strmap_set(protover_summary_map, protocols, new_cached);
diff --git a/src/core/proto/proto_socks.c b/src/core/proto/proto_socks.c
index ac0c9e911b..b657a7b758 100644
--- a/src/core/proto/proto_socks.c
+++ b/src/core/proto/proto_socks.c
@@ -8,7 +8,7 @@
#include "feature/client/addressmap.h"
#include "lib/buf/buffers.h"
#include "core/mainloop/connection.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "app/config/config.h"
#include "lib/crypt_ops/crypto_util.h"
#include "feature/relay/ext_orport.h"
diff --git a/src/ext/include.am b/src/ext/include.am
index 6bdce2d79e..317e25d78e 100644
--- a/src/ext/include.am
+++ b/src/ext/include.am
@@ -143,6 +143,7 @@ noinst_HEADERS += $(ED25519_DONNA_HDRS)
LIBED25519_DONNA=src/ext/ed25519/donna/libed25519_donna.a
noinst_LIBRARIES += $(LIBED25519_DONNA)
+if BUILD_KECCAK_TINY
src_ext_keccak_tiny_libkeccak_tiny_a_CFLAGS=\
@CFLAGS_CONSTTIME@
@@ -156,6 +157,7 @@ noinst_HEADERS += $(LIBKECCAK_TINY_HDRS)
LIBKECCAK_TINY=src/ext/keccak-tiny/libkeccak-tiny.a
noinst_LIBRARIES += $(LIBKECCAK_TINY)
+endif
EXTRA_DIST += \
src/ext/timeouts/bench/bench-add.lua \
diff --git a/src/ext/timeouts/.may_include b/src/ext/timeouts/.may_include
index 42f44befd4..92c7116555 100644
--- a/src/ext/timeouts/.may_include
+++ b/src/ext/timeouts/.may_include
@@ -1,6 +1,5 @@
orconfig.h
ext/tor_queue.h
-timeout-bitops.c
-timeout-debug.h
-timeout.h
+ext/timeouts/*.h
+ext/timeouts/timeout-bitops.c
diff --git a/src/ext/timeouts/test-timeout.c b/src/ext/timeouts/test-timeout.c
index 8077129376..52d2e31e0c 100644
--- a/src/ext/timeouts/test-timeout.c
+++ b/src/ext/timeouts/test-timeout.c
@@ -4,7 +4,7 @@
#include <assert.h>
#include <limits.h>
-#include "timeout.h"
+#include "ext/timeouts/timeout.h"
#define THE_END_OF_TIME ((timeout_t)-1)
diff --git a/src/ext/timeouts/timeout.c b/src/ext/timeouts/timeout.c
index 07d06772c5..79fcc168ed 100644
--- a/src/ext/timeouts/timeout.c
+++ b/src/ext/timeouts/timeout.c
@@ -40,14 +40,14 @@
#include "ext/tor_queue.h" /* TAILQ(3) */
-#include "timeout.h"
+#include "ext/timeouts/timeout.h"
#ifndef TIMEOUT_DEBUG
#define TIMEOUT_DEBUG 0
#endif
#if TIMEOUT_DEBUG - 0
-#include "timeout-debug.h"
+#include "ext/timeouts/timeout-debug.h"
#endif
#ifdef TIMEOUT_DISABLE_RELATIVE_ACCESS
@@ -141,7 +141,7 @@
#define WHEEL_MASK (WHEEL_LEN - 1)
#define TIMEOUT_MAX ((TIMEOUT_C(1) << (WHEEL_BIT * WHEEL_NUM)) - 1)
-#include "timeout-bitops.c"
+#include "ext/timeouts/timeout-bitops.c"
#if WHEEL_BIT == 6
#define ctz(n) ctz64(n)
diff --git a/src/ext/tinytest.c b/src/ext/tinytest.c
index 16f11e4639..239fdd0a38 100644
--- a/src/ext/tinytest.c
+++ b/src/ext/tinytest.c
@@ -492,6 +492,12 @@ tinytest_set_test_skipped_(void)
cur_test_outcome = SKIP;
}
+int
+tinytest_cur_test_has_failed(void)
+{
+ return (cur_test_outcome == FAIL);
+}
+
char *
tinytest_format_hex_(const void *val_, unsigned long len)
{
diff --git a/src/ext/tinytest.h b/src/ext/tinytest.h
index ed07b26bc0..05c2fda052 100644
--- a/src/ext/tinytest.h
+++ b/src/ext/tinytest.h
@@ -72,6 +72,9 @@ struct testlist_alias_t {
};
#define END_OF_ALIASES { NULL, NULL }
+/** Return true iff the current test has failed. */
+int tinytest_cur_test_has_failed(void);
+
/** Implementation: called from a test to indicate failure, before logging. */
void tinytest_set_test_failed_(void);
/** Implementation: called from a test to indicate that we're skipping. */
diff --git a/src/feature/api/tor_api.c b/src/feature/api/tor_api.c
index 697397d46b..fd9d241353 100644
--- a/src/feature/api/tor_api.c
+++ b/src/feature/api/tor_api.c
@@ -40,10 +40,10 @@
#define raw_socketpair tor_ersatz_socketpair
#define raw_closesocket closesocket
#define snprintf _snprintf
-#else
+#else /* !(defined(_WIN32)) */
#define raw_socketpair socketpair
#define raw_closesocket close
-#endif
+#endif /* defined(_WIN32) */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
diff --git a/src/feature/api/tor_api.h b/src/feature/api/tor_api.h
index 2bf130c376..cb84853a52 100644
--- a/src/feature/api/tor_api.h
+++ b/src/feature/api/tor_api.h
@@ -55,7 +55,7 @@ typedef SOCKET tor_control_socket_t;
#else
typedef int tor_control_socket_t;
#define INVALID_TOR_CONTROL_SOCKET (-1)
-#endif
+#endif /* defined(_WIN32) */
/** DOCDOC */
tor_control_socket_t tor_main_configuration_setup_control_socket(
diff --git a/src/feature/client/addressmap.c b/src/feature/client/addressmap.c
index bbe786a6a2..c5a27ce8c6 100644
--- a/src/feature/client/addressmap.c
+++ b/src/feature/client/addressmap.c
@@ -22,7 +22,7 @@
#include "core/or/circuituse.h"
#include "app/config/config.h"
#include "core/or/connection_edge.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/relay/dns.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerset.h"
diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c
index 05f89ad36c..f517876609 100644
--- a/src/feature/client/bridges.c
+++ b/src/feature/client/bridges.c
@@ -348,7 +348,7 @@ int
node_is_a_configured_bridge(const node_t *node)
{
/* First, let's try searching for a bridge with matching identity. */
- if (BUG(tor_digest_is_zero(node->identity)))
+ if (BUG(fast_mem_is_zero(node->identity, DIGEST_LEN)))
return 0;
if (find_bridge_by_digest(node->identity) != NULL)
diff --git a/src/feature/client/circpathbias.c b/src/feature/client/circpathbias.c
index 1743ab5a81..3544dbb859 100644
--- a/src/feature/client/circpathbias.c
+++ b/src/feature/client/circpathbias.c
@@ -176,6 +176,7 @@ pathbias_get_scale_threshold(const or_options_t *options)
static double
pathbias_get_scale_ratio(const or_options_t *options)
{
+ (void) options;
/*
* The scale factor is the denominator for our scaling
* of circuit counts for our path bias window.
@@ -185,7 +186,8 @@ pathbias_get_scale_ratio(const or_options_t *options)
*/
int denominator = networkstatus_get_param(NULL, "pb_scalefactor",
2, 2, INT32_MAX);
- (void) options;
+ tor_assert(denominator > 0);
+
/**
* The mult factor is the numerator for our scaling
* of circuit counts for our path bias window. It
@@ -369,8 +371,9 @@ pathbias_should_count(origin_circuit_t *circ)
!circ->build_state->onehop_tunnel) {
if ((rate_msg = rate_limit_log(&count_limit, approx_time()))) {
log_info(LD_BUG,
- "One-hop circuit has length %d. Path state is %s. "
+ "One-hop circuit %d has length %d. Path state is %s. "
"Circuit is a %s currently %s.%s",
+ circ->global_identifier,
circ->build_state->desired_path_len,
pathbias_state_to_string(circ->path_state),
circuit_purpose_to_string(circ->base_.purpose),
@@ -398,12 +401,13 @@ pathbias_should_count(origin_circuit_t *circ)
/* Check to see if the shouldcount result has changed due to a
* unexpected purpose change that would affect our results */
if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_IGNORED) {
- log_info(LD_BUG,
- "Circuit %d is now being counted despite being ignored "
- "in the past. Purpose is %s, path state is %s",
- circ->global_identifier,
- circuit_purpose_to_string(circ->base_.purpose),
- pathbias_state_to_string(circ->path_state));
+ log_info(LD_CIRC,
+ "Circuit %d is not being counted by pathbias because it was "
+ "ignored in the past. Purpose is %s, path state is %s",
+ circ->global_identifier,
+ circuit_purpose_to_string(circ->base_.purpose),
+ pathbias_state_to_string(circ->path_state));
+ return 0;
}
circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_COUNTED;
@@ -434,8 +438,9 @@ pathbias_count_build_attempt(origin_circuit_t *circ)
if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
approx_time()))) {
log_info(LD_BUG,
- "Opened circuit is in strange path state %s. "
+ "Opened circuit %d is in strange path state %s. "
"Circuit is a %s currently %s.%s",
+ circ->global_identifier,
pathbias_state_to_string(circ->path_state),
circuit_purpose_to_string(circ->base_.purpose),
circuit_state_to_string(circ->base_.state),
@@ -468,8 +473,9 @@ pathbias_count_build_attempt(origin_circuit_t *circ)
if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
approx_time()))) {
log_info(LD_BUG,
- "Unopened circuit has strange path state %s. "
+ "Unopened circuit %d has strange path state %s. "
"Circuit is a %s currently %s.%s",
+ circ->global_identifier,
pathbias_state_to_string(circ->path_state),
circuit_purpose_to_string(circ->base_.purpose),
circuit_state_to_string(circ->base_.state),
@@ -538,8 +544,9 @@ pathbias_count_build_success(origin_circuit_t *circ)
if ((rate_msg = rate_limit_log(&success_notice_limit,
approx_time()))) {
log_info(LD_BUG,
- "Succeeded circuit is in strange path state %s. "
+ "Succeeded circuit %d is in strange path state %s. "
"Circuit is a %s currently %s.%s",
+ circ->global_identifier,
pathbias_state_to_string(circ->path_state),
circuit_purpose_to_string(circ->base_.purpose),
circuit_state_to_string(circ->base_.state),
@@ -574,8 +581,9 @@ pathbias_count_build_success(origin_circuit_t *circ)
if ((rate_msg = rate_limit_log(&success_notice_limit,
approx_time()))) {
log_info(LD_BUG,
- "Opened circuit is in strange path state %s. "
+ "Opened circuit %d is in strange path state %s. "
"Circuit is a %s currently %s.%s",
+ circ->global_identifier,
pathbias_state_to_string(circ->path_state),
circuit_purpose_to_string(circ->base_.purpose),
circuit_state_to_string(circ->base_.state),
@@ -601,8 +609,9 @@ pathbias_count_use_attempt(origin_circuit_t *circ)
if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) {
log_notice(LD_BUG,
- "Used circuit is in strange path state %s. "
+ "Used circuit %d is in strange path state %s. "
"Circuit is a %s currently %s.",
+ circ->global_identifier,
pathbias_state_to_string(circ->path_state),
circuit_purpose_to_string(circ->base_.purpose),
circuit_state_to_string(circ->base_.state));
diff --git a/src/feature/client/dnsserv.c b/src/feature/client/dnsserv.c
index 44e0caaafa..7fb3fff6c1 100644
--- a/src/feature/client/dnsserv.c
+++ b/src/feature/client/dnsserv.c
@@ -26,7 +26,7 @@
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "core/mainloop/mainloop.h"
#include "core/mainloop/netstatus.h"
#include "core/or/policies.h"
diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c
index 819f90a6d9..54a9238d8f 100644
--- a/src/feature/client/entrynodes.c
+++ b/src/feature/client/entrynodes.c
@@ -128,7 +128,7 @@
#include "feature/client/circpathbias.h"
#include "feature/client/entrynodes.h"
#include "feature/client/transports.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dircommon/directory.h"
#include "feature/nodelist/describe.h"
#include "feature/nodelist/microdesc.h"
diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c
index e7ff3bf34a..97bfc8ae30 100644
--- a/src/feature/client/transports.c
+++ b/src/feature/client/transports.c
@@ -100,7 +100,7 @@
#include "app/config/statefile.h"
#include "core/or/connection_or.h"
#include "feature/relay/ext_orport.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "lib/encoding/confline.h"
#include "lib/encoding/kvline.h"
@@ -1424,11 +1424,6 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
} else {
smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=");
}
-
- /* All new versions of tor will keep stdin open, so PTs can use it
- * as a reliable termination detection mechanism.
- */
- smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1");
} else {
/* If ClientTransportPlugin has a HTTPS/SOCKS proxy configured, set the
* TOR_PT_PROXY line.
@@ -1439,6 +1434,11 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
}
}
+ /* All new versions of tor will keep stdin open, so PTs can use it
+ * as a reliable termination detection mechanism.
+ */
+ smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1");
+
SMARTLIST_FOREACH_BEGIN(envs, const char *, env_var) {
set_environment_variable_in_smartlist(merged_env_vars, env_var,
tor_free_, 1);
diff --git a/src/feature/control/btrack_circuit.h b/src/feature/control/btrack_circuit.h
index c40822f1f1..9e06fefb07 100644
--- a/src/feature/control/btrack_circuit.h
+++ b/src/feature/control/btrack_circuit.h
@@ -12,4 +12,4 @@
int btrack_circ_init(void);
void btrack_circ_fini(void);
-#endif /* defined(TOR_BTRACK_CIRCUIT_H) */
+#endif /* !defined(TOR_BTRACK_CIRCUIT_H) */
diff --git a/src/feature/control/btrack_orconn.h b/src/feature/control/btrack_orconn.h
index 6ab4892a78..f8f5c1096c 100644
--- a/src/feature/control/btrack_orconn.h
+++ b/src/feature/control/btrack_orconn.h
@@ -30,9 +30,9 @@ typedef struct bt_orconn_t {
bool is_onehop; /**< Is this for a one-hop circuit? */
} bt_orconn_t;
-#endif /* defined(BTRACK_ORCONN_PRIVATE) */
+#endif /* defined(BTRACK_ORCONN_PRIVATE) */
int btrack_orconn_init(void);
void btrack_orconn_fini(void);
-#endif /* defined(TOR_BTRACK_ORCONN_H) */
+#endif /* !defined(TOR_BTRACK_ORCONN_H) */
diff --git a/src/feature/control/btrack_orconn_cevent.c b/src/feature/control/btrack_orconn_cevent.c
index ee142f2873..535aa8f614 100644
--- a/src/feature/control/btrack_orconn_cevent.c
+++ b/src/feature/control/btrack_orconn_cevent.c
@@ -20,7 +20,7 @@
#include "core/or/orconn_event.h"
#include "feature/control/btrack_orconn.h"
#include "feature/control/btrack_orconn_cevent.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
/**
* Have we completed our first OR connection?
diff --git a/src/feature/control/btrack_orconn_cevent.h b/src/feature/control/btrack_orconn_cevent.h
index f9d24633aa..afec55581e 100644
--- a/src/feature/control/btrack_orconn_cevent.h
+++ b/src/feature/control/btrack_orconn_cevent.h
@@ -7,6 +7,7 @@
**/
#ifndef TOR_BTRACK_ORCONN_CEVENT_H
+#define TOR_BTRACK_ORCONN_CEVENT_H
#include "feature/control/btrack_orconn.h"
@@ -14,4 +15,4 @@ void bto_cevent_anyconn(const bt_orconn_t *);
void bto_cevent_apconn(const bt_orconn_t *);
void bto_cevent_reset(void);
-#endif /* defined(TOR_BTRACK_ORCONN_CEVENT_H) */
+#endif /* !defined(TOR_BTRACK_ORCONN_CEVENT_H) */
diff --git a/src/feature/control/btrack_orconn_maps.h b/src/feature/control/btrack_orconn_maps.h
index 3ead40984c..c2043fa153 100644
--- a/src/feature/control/btrack_orconn_maps.h
+++ b/src/feature/control/btrack_orconn_maps.h
@@ -7,6 +7,7 @@
**/
#ifndef TOR_BTRACK_ORCONN_MAPS_H
+#define TOR_BTRACK_ORCONN_MAPS_H
void bto_delete(uint64_t);
bt_orconn_t *bto_find_or_new(uint64_t, uint64_t);
@@ -14,4 +15,4 @@ bt_orconn_t *bto_find_or_new(uint64_t, uint64_t);
void bto_init_maps(void);
void bto_clear_maps(void);
-#endif /* defined(TOR_BTRACK_ORCONN_MAPS_H) */
+#endif /* !defined(TOR_BTRACK_ORCONN_MAPS_H) */
diff --git a/src/feature/control/btrack_sys.h b/src/feature/control/btrack_sys.h
index fad35b41db..3f831d0640 100644
--- a/src/feature/control/btrack_sys.h
+++ b/src/feature/control/btrack_sys.h
@@ -11,4 +11,4 @@
extern const struct subsys_fns_t sys_btrack;
-#endif /* defined(TOR_BTRACK_SYS_H) */
+#endif /* !defined(TOR_BTRACK_SYS_H) */
diff --git a/src/feature/control/control.c b/src/feature/control/control.c
index 6f8cd8f0aa..436bf423cf 100644
--- a/src/feature/control/control.c
+++ b/src/feature/control/control.c
@@ -1,4 +1,3 @@
-
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
@@ -33,86 +32,27 @@
* stack.
**/
+#define CONTROL_MODULE_PRIVATE
#define CONTROL_PRIVATE
-#define OCIRC_EVENT_PRIVATE
#include "core/or/or.h"
#include "app/config/config.h"
-#include "app/config/confparse.h"
#include "app/main/main.h"
#include "core/mainloop/connection.h"
#include "core/mainloop/mainloop.h"
-#include "core/or/channel.h"
-#include "core/or/channeltls.h"
-#include "core/or/circuitbuild.h"
-#include "core/or/circuitlist.h"
-#include "core/or/circuitstats.h"
-#include "core/or/circuituse.h"
-#include "core/or/command.h"
-#include "core/or/connection_edge.h"
#include "core/or/connection_or.h"
-#include "core/or/ocirc_event.h"
-#include "core/or/policies.h"
-#include "core/or/reasons.h"
-#include "core/or/versions.h"
#include "core/proto/proto_control0.h"
#include "core/proto/proto_http.h"
-#include "feature/client/addressmap.h"
-#include "feature/client/bridges.h"
-#include "feature/client/dnsserv.h"
-#include "feature/client/entrynodes.h"
#include "feature/control/control.h"
-#include "feature/control/fmt_serverstatus.h"
-#include "feature/control/getinfo_geoip.h"
-#include "feature/dircache/dirserv.h"
-#include "feature/dirclient/dirclient.h"
-#include "feature/dirclient/dlstatus.h"
-#include "feature/dircommon/directory.h"
-#include "feature/hibernate/hibernate.h"
-#include "feature/hs/hs_cache.h"
-#include "feature/hs/hs_common.h"
-#include "feature/hs/hs_control.h"
-#include "feature/hs_common/shared_random_client.h"
-#include "feature/nodelist/authcert.h"
-#include "feature/nodelist/dirlist.h"
-#include "feature/nodelist/microdesc.h"
-#include "feature/nodelist/networkstatus.h"
-#include "feature/nodelist/nodelist.h"
-#include "feature/nodelist/routerinfo.h"
-#include "feature/nodelist/routerlist.h"
-#include "feature/relay/router.h"
-#include "feature/relay/routermode.h"
-#include "feature/relay/selftest.h"
-#include "feature/rend/rendclient.h"
+#include "feature/control/control_auth.h"
+#include "feature/control/control_cmd.h"
+#include "feature/control/control_events.h"
+#include "feature/control/control_proto.h"
#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendparse.h"
#include "feature/rend/rendservice.h"
-#include "feature/stats/geoip_stats.h"
-#include "feature/stats/predict_ports.h"
-#include "lib/buf/buffers.h"
-#include "lib/crypt_ops/crypto_rand.h"
-#include "lib/crypt_ops/crypto_util.h"
-#include "lib/encoding/confline.h"
-#include "lib/evloop/compat_libevent.h"
-#include "lib/version/torversion.h"
+#include "lib/evloop/procmon.h"
-#include "feature/dircache/cached_dir_st.h"
#include "feature/control/control_connection_st.h"
-#include "core/or/cpath_build_state_st.h"
-#include "core/or/entry_connection_st.h"
-#include "feature/nodelist/extrainfo_st.h"
-#include "feature/nodelist/networkstatus_st.h"
-#include "feature/nodelist/node_st.h"
-#include "core/or/or_connection_st.h"
-#include "core/or/or_circuit_st.h"
-#include "core/or/origin_circuit_st.h"
-#include "feature/nodelist/microdesc_st.h"
-#include "feature/rend/rend_authorized_client_st.h"
-#include "feature/rend/rend_encoded_v2_service_descriptor_st.h"
-#include "feature/rend/rend_service_descriptor_st.h"
-#include "feature/nodelist/routerinfo_st.h"
-#include "feature/nodelist/routerlist_st.h"
-#include "core/or/socks_request_st.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@@ -121,145 +61,6 @@
#include <sys/stat.h>
#endif
-#ifndef _WIN32
-#include <pwd.h>
-#include <sys/resource.h>
-#endif
-
-#include "lib/crypt_ops/crypto_s2k.h"
-#include "lib/evloop/procmon.h"
-#include "lib/evloop/compat_libevent.h"
-
-/** Yield true iff <b>s</b> is the state of a control_connection_t that has
- * finished authentication and is accepting commands. */
-#define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN)
-
-/** Bitfield: The bit 1&lt;&lt;e is set if <b>any</b> open control
- * connection is interested in events of type <b>e</b>. We use this
- * so that we can decide to skip generating event messages that nobody
- * has interest in without having to walk over the global connection
- * list to find out.
- **/
-typedef uint64_t event_mask_t;
-
-/** An event mask of all the events that any controller is interested in
- * receiving. */
-static event_mask_t global_event_mask = 0;
-
-/** True iff we have disabled log messages from being sent to the controller */
-static int disable_log_messages = 0;
-
-/** Macro: true if any control connection is interested in events of type
- * <b>e</b>. */
-#define EVENT_IS_INTERESTING(e) \
- (!! (global_event_mask & EVENT_MASK_(e)))
-
-/** Macro: true if any event from the bitfield 'e' is interesting. */
-#define ANY_EVENT_IS_INTERESTING(e) \
- (!! (global_event_mask & (e)))
-
-/** If we're using cookie-type authentication, how long should our cookies be?
- */
-#define AUTHENTICATION_COOKIE_LEN 32
-
-/** If true, we've set authentication_cookie to a secret code and
- * stored it to disk. */
-static int authentication_cookie_is_set = 0;
-/** If authentication_cookie_is_set, a secret cookie that we've stored to disk
- * and which we're using to authenticate controllers. (If the controller can
- * read it off disk, it has permission to connect.) */
-static uint8_t *authentication_cookie = NULL;
-
-#define SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT \
- "Tor safe cookie authentication server-to-controller hash"
-#define SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT \
- "Tor safe cookie authentication controller-to-server hash"
-#define SAFECOOKIE_SERVER_NONCE_LEN DIGEST256_LEN
-
-/** The list of onion services that have been added via ADD_ONION that do not
- * belong to any particular control connection.
- */
-static smartlist_t *detached_onion_services = NULL;
-
-static void connection_printf_to_buf(control_connection_t *conn,
- const char *format, ...)
- CHECK_PRINTF(2,3);
-static void send_control_event_impl(uint16_t event,
- const char *format, va_list ap)
- CHECK_PRINTF(2,0);
-static int control_event_status(int type, int severity, const char *format,
- va_list args)
- CHECK_PRINTF(3,0);
-
-static void send_control_done(control_connection_t *conn);
-static void send_control_event(uint16_t event,
- const char *format, ...)
- CHECK_PRINTF(2,3);
-static int handle_control_setconf(control_connection_t *conn, uint32_t len,
- char *body);
-static int handle_control_resetconf(control_connection_t *conn, uint32_t len,
- char *body);
-static int handle_control_getconf(control_connection_t *conn, uint32_t len,
- const char *body);
-static int handle_control_loadconf(control_connection_t *conn, uint32_t len,
- const char *body);
-static int handle_control_setevents(control_connection_t *conn, uint32_t len,
- const char *body);
-static int handle_control_authenticate(control_connection_t *conn,
- uint32_t len,
- const char *body);
-static int handle_control_signal(control_connection_t *conn, uint32_t len,
- const char *body);
-static int handle_control_mapaddress(control_connection_t *conn, uint32_t len,
- const char *body);
-static char *list_getinfo_options(void);
-static int handle_control_getinfo(control_connection_t *conn, uint32_t len,
- const char *body);
-static int handle_control_extendcircuit(control_connection_t *conn,
- uint32_t len,
- const char *body);
-static int handle_control_setcircuitpurpose(control_connection_t *conn,
- uint32_t len, const char *body);
-static int handle_control_attachstream(control_connection_t *conn,
- uint32_t len,
- const char *body);
-static int handle_control_postdescriptor(control_connection_t *conn,
- uint32_t len,
- const char *body);
-static int handle_control_redirectstream(control_connection_t *conn,
- uint32_t len,
- const char *body);
-static int handle_control_closestream(control_connection_t *conn, uint32_t len,
- const char *body);
-static int handle_control_closecircuit(control_connection_t *conn,
- uint32_t len,
- const char *body);
-static int handle_control_resolve(control_connection_t *conn, uint32_t len,
- const char *body);
-static int handle_control_usefeature(control_connection_t *conn,
- uint32_t len,
- const char *body);
-static int handle_control_hsfetch(control_connection_t *conn, uint32_t len,
- const char *body);
-static int handle_control_hspost(control_connection_t *conn, uint32_t len,
- const char *body);
-static int handle_control_add_onion(control_connection_t *conn, uint32_t len,
- const char *body);
-static int handle_control_del_onion(control_connection_t *conn, uint32_t len,
- const char *body);
-static int write_stream_target_to_buf(entry_connection_t *conn, char *buf,
- size_t len);
-static void orconn_target_get_name(char *buf, size_t len,
- or_connection_t *conn);
-
-static int get_cached_network_liveness(void);
-static void set_cached_network_liveness(int liveness);
-
-static void flush_queued_events_cb(mainloop_event_t *event, void *arg);
-
-static char * download_status_to_string(const download_status_t *dl);
-static void control_get_bytes_rw_last_sec(uint64_t *r, uint64_t *w);
-
/** Convert a connection_t* to an control_connection_t*; assert if the cast is
* invalid. */
control_connection_t *
@@ -269,410 +70,6 @@ TO_CONTROL_CONN(connection_t *c)
return DOWNCAST(control_connection_t, c);
}
-/** Given a control event code for a message event, return the corresponding
- * log severity. */
-static inline int
-event_to_log_severity(int event)
-{
- switch (event) {
- case EVENT_DEBUG_MSG: return LOG_DEBUG;
- case EVENT_INFO_MSG: return LOG_INFO;
- case EVENT_NOTICE_MSG: return LOG_NOTICE;
- case EVENT_WARN_MSG: return LOG_WARN;
- case EVENT_ERR_MSG: return LOG_ERR;
- default: return -1;
- }
-}
-
-/** Given a log severity, return the corresponding control event code. */
-static inline int
-log_severity_to_event(int severity)
-{
- switch (severity) {
- case LOG_DEBUG: return EVENT_DEBUG_MSG;
- case LOG_INFO: return EVENT_INFO_MSG;
- case LOG_NOTICE: return EVENT_NOTICE_MSG;
- case LOG_WARN: return EVENT_WARN_MSG;
- case LOG_ERR: return EVENT_ERR_MSG;
- default: return -1;
- }
-}
-
-/** Helper: clear bandwidth counters of all origin circuits. */
-static void
-clear_circ_bw_fields(void)
-{
- origin_circuit_t *ocirc;
- SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
- if (!CIRCUIT_IS_ORIGIN(circ))
- continue;
- ocirc = TO_ORIGIN_CIRCUIT(circ);
- ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
- ocirc->n_overhead_written_circ_bw = ocirc->n_overhead_read_circ_bw = 0;
- ocirc->n_delivered_written_circ_bw = ocirc->n_delivered_read_circ_bw = 0;
- }
- SMARTLIST_FOREACH_END(circ);
-}
-
-/** Set <b>global_event_mask*</b> to the bitwise OR of each live control
- * connection's event_mask field. */
-void
-control_update_global_event_mask(void)
-{
- smartlist_t *conns = get_connection_array();
- event_mask_t old_mask, new_mask;
- old_mask = global_event_mask;
- int any_old_per_sec_events = control_any_per_second_event_enabled();
-
- global_event_mask = 0;
- SMARTLIST_FOREACH(conns, connection_t *, _conn,
- {
- if (_conn->type == CONN_TYPE_CONTROL &&
- STATE_IS_OPEN(_conn->state)) {
- control_connection_t *conn = TO_CONTROL_CONN(_conn);
- global_event_mask |= conn->event_mask;
- }
- });
-
- new_mask = global_event_mask;
-
- /* Handle the aftermath. Set up the log callback to tell us only what
- * we want to hear...*/
- control_adjust_event_log_severity();
-
- /* Macro: true if ev was false before and is true now. */
-#define NEWLY_ENABLED(ev) \
- (! (old_mask & (ev)) && (new_mask & (ev)))
-
- /* ...then, if we've started logging stream or circ bw, clear the
- * appropriate fields. */
- if (NEWLY_ENABLED(EVENT_STREAM_BANDWIDTH_USED)) {
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->type == CONN_TYPE_AP) {
- edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
- edge_conn->n_written = edge_conn->n_read = 0;
- }
- });
- }
- if (NEWLY_ENABLED(EVENT_CIRC_BANDWIDTH_USED)) {
- clear_circ_bw_fields();
- }
- if (NEWLY_ENABLED(EVENT_BANDWIDTH_USED)) {
- uint64_t r, w;
- control_get_bytes_rw_last_sec(&r, &w);
- }
- if (any_old_per_sec_events != control_any_per_second_event_enabled()) {
- rescan_periodic_events(get_options());
- }
-
-#undef NEWLY_ENABLED
-}
-
-/** Adjust the log severities that result in control_event_logmsg being called
- * to match the severity of log messages that any controllers are interested
- * in. */
-void
-control_adjust_event_log_severity(void)
-{
- int i;
- int min_log_event=EVENT_ERR_MSG, max_log_event=EVENT_DEBUG_MSG;
-
- for (i = EVENT_DEBUG_MSG; i <= EVENT_ERR_MSG; ++i) {
- if (EVENT_IS_INTERESTING(i)) {
- min_log_event = i;
- break;
- }
- }
- for (i = EVENT_ERR_MSG; i >= EVENT_DEBUG_MSG; --i) {
- if (EVENT_IS_INTERESTING(i)) {
- max_log_event = i;
- break;
- }
- }
- if (EVENT_IS_INTERESTING(EVENT_STATUS_GENERAL)) {
- if (min_log_event > EVENT_NOTICE_MSG)
- min_log_event = EVENT_NOTICE_MSG;
- if (max_log_event < EVENT_ERR_MSG)
- max_log_event = EVENT_ERR_MSG;
- }
- if (min_log_event <= max_log_event)
- change_callback_log_severity(event_to_log_severity(min_log_event),
- event_to_log_severity(max_log_event),
- control_event_logmsg);
- else
- change_callback_log_severity(LOG_ERR, LOG_ERR,
- control_event_logmsg);
-}
-
-/** Return true iff the event with code <b>c</b> is being sent to any current
- * control connection. This is useful if the amount of work needed to prepare
- * to call the appropriate control_event_...() function is high.
- */
-int
-control_event_is_interesting(int event)
-{
- return EVENT_IS_INTERESTING(event);
-}
-
-/** Return true if any event that needs to fire once a second is enabled. */
-int
-control_any_per_second_event_enabled(void)
-{
- return ANY_EVENT_IS_INTERESTING(
- EVENT_MASK_(EVENT_BANDWIDTH_USED) |
- EVENT_MASK_(EVENT_CELL_STATS) |
- EVENT_MASK_(EVENT_CIRC_BANDWIDTH_USED) |
- EVENT_MASK_(EVENT_CONN_BW) |
- EVENT_MASK_(EVENT_STREAM_BANDWIDTH_USED)
- );
-}
-
-/* The value of 'get_bytes_read()' the previous time that
- * control_get_bytes_rw_last_sec() as called. */
-static uint64_t stats_prev_n_read = 0;
-/* The value of 'get_bytes_written()' the previous time that
- * control_get_bytes_rw_last_sec() as called. */
-static uint64_t stats_prev_n_written = 0;
-
-/**
- * Set <b>n_read</b> and <b>n_written</b> to the total number of bytes read
- * and written by Tor since the last call to this function.
- *
- * Call this only from the main thread.
- */
-static void
-control_get_bytes_rw_last_sec(uint64_t *n_read,
- uint64_t *n_written)
-{
- const uint64_t stats_n_bytes_read = get_bytes_read();
- const uint64_t stats_n_bytes_written = get_bytes_written();
-
- *n_read = stats_n_bytes_read - stats_prev_n_read;
- *n_written = stats_n_bytes_written - stats_prev_n_written;
- stats_prev_n_read = stats_n_bytes_read;
- stats_prev_n_written = stats_n_bytes_written;
-}
-
-/**
- * Run all the controller events (if any) that are scheduled to trigger once
- * per second.
- */
-void
-control_per_second_events(void)
-{
- if (!control_any_per_second_event_enabled())
- return;
-
- uint64_t bytes_read, bytes_written;
- control_get_bytes_rw_last_sec(&bytes_read, &bytes_written);
- control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
-
- control_event_stream_bandwidth_used();
- control_event_conn_bandwidth_used();
- control_event_circ_bandwidth_used();
- control_event_circuit_cell_stats();
-}
-
-/** Append a NUL-terminated string <b>s</b> to the end of
- * <b>conn</b>-\>outbuf.
- */
-static inline void
-connection_write_str_to_buf(const char *s, control_connection_t *conn)
-{
- size_t len = strlen(s);
- connection_buf_add(s, len, TO_CONN(conn));
-}
-
-/** Given a <b>len</b>-character string in <b>data</b>, made of lines
- * terminated by CRLF, allocate a new string in *<b>out</b>, and copy the
- * contents of <b>data</b> into *<b>out</b>, adding a period before any period
- * that appears at the start of a line, and adding a period-CRLF line at
- * the end. Replace all LF characters sequences with CRLF. Return the number
- * of bytes in *<b>out</b>.
- */
-STATIC size_t
-write_escaped_data(const char *data, size_t len, char **out)
-{
- tor_assert(len < SIZE_MAX - 9);
- size_t sz_out = len+8+1;
- char *outp;
- const char *start = data, *end;
- size_t i;
- int start_of_line;
- for (i=0; i < len; ++i) {
- if (data[i] == '\n') {
- sz_out += 2; /* Maybe add a CR; maybe add a dot. */
- if (sz_out >= SIZE_T_CEILING) {
- log_warn(LD_BUG, "Input to write_escaped_data was too long");
- *out = tor_strdup(".\r\n");
- return 3;
- }
- }
- }
- *out = outp = tor_malloc(sz_out);
- end = data+len;
- start_of_line = 1;
- while (data < end) {
- if (*data == '\n') {
- if (data > start && data[-1] != '\r')
- *outp++ = '\r';
- start_of_line = 1;
- } else if (*data == '.') {
- if (start_of_line) {
- start_of_line = 0;
- *outp++ = '.';
- }
- } else {
- start_of_line = 0;
- }
- *outp++ = *data++;
- }
- if (outp < *out+2 || fast_memcmp(outp-2, "\r\n", 2)) {
- *outp++ = '\r';
- *outp++ = '\n';
- }
- *outp++ = '.';
- *outp++ = '\r';
- *outp++ = '\n';
- *outp = '\0'; /* NUL-terminate just in case. */
- tor_assert(outp >= *out);
- tor_assert((size_t)(outp - *out) <= sz_out);
- return outp - *out;
-}
-
-/** Given a <b>len</b>-character string in <b>data</b>, made of lines
- * terminated by CRLF, allocate a new string in *<b>out</b>, and copy
- * the contents of <b>data</b> into *<b>out</b>, removing any period
- * that appears at the start of a line, and replacing all CRLF sequences
- * with LF. Return the number of
- * bytes in *<b>out</b>. */
-STATIC size_t
-read_escaped_data(const char *data, size_t len, char **out)
-{
- char *outp;
- const char *next;
- const char *end;
-
- *out = outp = tor_malloc(len+1);
-
- end = data+len;
-
- while (data < end) {
- /* we're at the start of a line. */
- if (*data == '.')
- ++data;
- next = memchr(data, '\n', end-data);
- if (next) {
- size_t n_to_copy = next-data;
- /* Don't copy a CR that precedes this LF. */
- if (n_to_copy && *(next-1) == '\r')
- --n_to_copy;
- memcpy(outp, data, n_to_copy);
- outp += n_to_copy;
- data = next+1; /* This will point at the start of the next line,
- * or the end of the string, or a period. */
- } else {
- memcpy(outp, data, end-data);
- outp += (end-data);
- *outp = '\0';
- return outp - *out;
- }
- *outp++ = '\n';
- }
-
- *outp = '\0';
- return outp - *out;
-}
-
-/** If the first <b>in_len_max</b> characters in <b>start</b> contain a
- * double-quoted string with escaped characters, return the length of that
- * string (as encoded, including quotes). Otherwise return -1. */
-static inline int
-get_escaped_string_length(const char *start, size_t in_len_max,
- int *chars_out)
-{
- const char *cp, *end;
- int chars = 0;
-
- if (*start != '\"')
- return -1;
-
- cp = start+1;
- end = start+in_len_max;
-
- /* Calculate length. */
- while (1) {
- if (cp >= end) {
- return -1; /* Too long. */
- } else if (*cp == '\\') {
- if (++cp == end)
- return -1; /* Can't escape EOS. */
- ++cp;
- ++chars;
- } else if (*cp == '\"') {
- break;
- } else {
- ++cp;
- ++chars;
- }
- }
- if (chars_out)
- *chars_out = chars;
- return (int)(cp - start+1);
-}
-
-/** As decode_escaped_string, but does not decode the string: copies the
- * entire thing, including quotation marks. */
-static const char *
-extract_escaped_string(const char *start, size_t in_len_max,
- char **out, size_t *out_len)
-{
- int length = get_escaped_string_length(start, in_len_max, NULL);
- if (length<0)
- return NULL;
- *out_len = length;
- *out = tor_strndup(start, *out_len);
- return start+length;
-}
-
-/** Given a pointer to a string starting at <b>start</b> containing
- * <b>in_len_max</b> characters, decode a string beginning with one double
- * quote, containing any number of non-quote characters or characters escaped
- * with a backslash, and ending with a final double quote. Place the resulting
- * string (unquoted, unescaped) into a newly allocated string in *<b>out</b>;
- * store its length in <b>out_len</b>. On success, return a pointer to the
- * character immediately following the escaped string. On failure, return
- * NULL. */
-static const char *
-decode_escaped_string(const char *start, size_t in_len_max,
- char **out, size_t *out_len)
-{
- const char *cp, *end;
- char *outp;
- int len, n_chars = 0;
-
- len = get_escaped_string_length(start, in_len_max, &n_chars);
- if (len<0)
- return NULL;
-
- end = start+len-1; /* Index of last quote. */
- tor_assert(*end == '\"');
- outp = *out = tor_malloc(len+1);
- *out_len = n_chars;
-
- cp = start+1;
- while (cp < end) {
- if (*cp == '\\')
- ++cp;
- *outp++ = *cp++;
- }
- *outp = '\0';
- tor_assert((outp - *out) == (int)*out_len);
-
- return end+1;
-}
-
/** Create and add a new controller connection on <b>sock</b>. If
* <b>CC_LOCAL_FD_IS_OWNER</b> is set in <b>flags</b>, this Tor process should
* exit when the connection closes. If <b>CC_LOCAL_FD_IS_AUTHENTICATED</b>
@@ -716,29 +113,6 @@ control_connection_add_local_fd(tor_socket_t sock, unsigned flags)
return 0;
}
-/** Acts like sprintf, but writes its formatted string to the end of
- * <b>conn</b>-\>outbuf. */
-static void
-connection_printf_to_buf(control_connection_t *conn, const char *format, ...)
-{
- va_list ap;
- char *buf = NULL;
- int len;
-
- va_start(ap,format);
- len = tor_vasprintf(&buf, format, ap);
- va_end(ap);
-
- if (len < 0) {
- log_err(LD_BUG, "Unable to format string for controller.");
- tor_assert(0);
- }
-
- connection_buf_add(buf, (size_t)len, TO_CONN(conn));
-
- tor_free(buf);
-}
-
/** Write all of the open control ports to ControlPortWriteToFile */
void
control_ports_write_to_file(void)
@@ -783,886 +157,7 @@ control_ports_write_to_file(void)
smartlist_free(lines);
}
-/** Send a "DONE" message down the control connection <b>conn</b>. */
-static void
-send_control_done(control_connection_t *conn)
-{
- connection_write_str_to_buf("250 OK\r\n", conn);
-}
-
-/** Represents an event that's queued to be sent to one or more
- * controllers. */
-typedef struct queued_event_s {
- uint16_t event;
- char *msg;
-} queued_event_t;
-
-/** 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_flag;
-
-/** Holds a smartlist of queued_event_t objects that may need to be sent
- * to one or more controllers */
-static smartlist_t *queued_control_events = NULL;
-
-/** True if the flush_queued_events_event is pending. */
-static int flush_queued_event_pending = 0;
-
-/** Lock to protect the above fields. */
-static tor_mutex_t *queued_control_events_lock = NULL;
-
-/** An event that should fire in order to flush the contents of
- * queued_control_events. */
-static mainloop_event_t *flush_queued_events_event = NULL;
-
-void
-control_initialize_event_queue(void)
-{
- if (queued_control_events == NULL) {
- queued_control_events = smartlist_new();
- }
-
- if (flush_queued_events_event == NULL) {
- struct event_base *b = tor_libevent_get_base();
- if (b) {
- flush_queued_events_event =
- mainloop_event_new(flush_queued_events_cb, NULL);
- tor_assert(flush_queued_events_event);
- }
- }
-
- if (queued_control_events_lock == NULL) {
- queued_control_events_lock = tor_mutex_new();
- tor_threadlocal_init(&block_event_queue_flag);
- }
-}
-
-static int *
-get_block_event_queue(void)
-{
- 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_flag, val);
- }
- return val;
-}
-
-/** Helper: inserts an event on the list of events queued to be sent to
- * one or more controllers, and schedules the events to be flushed if needed.
- *
- * This function takes ownership of <b>msg</b>, and may free it.
- *
- * We queue these events rather than send them immediately in order to break
- * the dependency in our callgraph from code that generates events for the
- * controller, and the network layer at large. Otherwise, nearly every
- * interesting part of Tor would potentially call every other interesting part
- * of Tor.
- */
-MOCK_IMPL(STATIC void,
-queue_control_event_string,(uint16_t event, char *msg))
-{
- /* This is redundant with checks done elsewhere, but it's a last-ditch
- * attempt to avoid queueing something we shouldn't have to queue. */
- if (PREDICT_UNLIKELY( ! EVENT_IS_INTERESTING(event) )) {
- tor_free(msg);
- return;
- }
-
- int *block_event_queue = get_block_event_queue();
- if (*block_event_queue) {
- tor_free(msg);
- return;
- }
-
- queued_event_t *ev = tor_malloc(sizeof(*ev));
- ev->event = event;
- ev->msg = msg;
-
- /* No queueing an event while queueing an event */
- ++*block_event_queue;
-
- tor_mutex_acquire(queued_control_events_lock);
- tor_assert(queued_control_events);
- smartlist_add(queued_control_events, ev);
-
- int activate_event = 0;
- if (! flush_queued_event_pending && in_main_thread()) {
- activate_event = 1;
- flush_queued_event_pending = 1;
- }
-
- tor_mutex_release(queued_control_events_lock);
-
- --*block_event_queue;
-
- /* We just put an event on the queue; mark the queue to be
- * flushed. We only do this from the main thread for now; otherwise,
- * we'd need to incur locking overhead in Libevent or use a socket.
- */
- if (activate_event) {
- tor_assert(flush_queued_events_event);
- mainloop_event_activate(flush_queued_events_event);
- }
-}
-
-#define queued_event_free(ev) \
- FREE_AND_NULL(queued_event_t, queued_event_free_, (ev))
-
-/** Release all storage held by <b>ev</b>. */
-static void
-queued_event_free_(queued_event_t *ev)
-{
- if (ev == NULL)
- return;
-
- tor_free(ev->msg);
- tor_free(ev);
-}
-
-/** Send every queued event to every controller that's interested in it,
- * and remove the events from the queue. If <b>force</b> is true,
- * then make all controllers send their data out immediately, since we
- * may be about to shut down. */
-static void
-queued_events_flush_all(int force)
-{
- /* Make sure that we get all the pending log events, if there are any. */
- flush_pending_log_callbacks();
-
- if (PREDICT_UNLIKELY(queued_control_events == NULL)) {
- return;
- }
- smartlist_t *all_conns = get_connection_array();
- smartlist_t *controllers = smartlist_new();
- smartlist_t *queued_events;
-
- int *block_event_queue = get_block_event_queue();
- ++*block_event_queue;
-
- tor_mutex_acquire(queued_control_events_lock);
- /* No queueing an event while flushing events. */
- flush_queued_event_pending = 0;
- queued_events = queued_control_events;
- queued_control_events = smartlist_new();
- tor_mutex_release(queued_control_events_lock);
-
- /* Gather all the controllers that will care... */
- SMARTLIST_FOREACH_BEGIN(all_conns, connection_t *, conn) {
- if (conn->type == CONN_TYPE_CONTROL &&
- !conn->marked_for_close &&
- conn->state == CONTROL_CONN_STATE_OPEN) {
- control_connection_t *control_conn = TO_CONTROL_CONN(conn);
-
- smartlist_add(controllers, control_conn);
- }
- } SMARTLIST_FOREACH_END(conn);
-
- SMARTLIST_FOREACH_BEGIN(queued_events, queued_event_t *, ev) {
- const event_mask_t bit = ((event_mask_t)1) << ev->event;
- const size_t msg_len = strlen(ev->msg);
- SMARTLIST_FOREACH_BEGIN(controllers, control_connection_t *,
- control_conn) {
- if (control_conn->event_mask & bit) {
- connection_buf_add(ev->msg, msg_len, TO_CONN(control_conn));
- }
- } SMARTLIST_FOREACH_END(control_conn);
-
- queued_event_free(ev);
- } SMARTLIST_FOREACH_END(ev);
-
- if (force) {
- SMARTLIST_FOREACH_BEGIN(controllers, control_connection_t *,
- control_conn) {
- connection_flush(TO_CONN(control_conn));
- } SMARTLIST_FOREACH_END(control_conn);
- }
-
- smartlist_free(queued_events);
- smartlist_free(controllers);
-
- --*block_event_queue;
-}
-
-/** Libevent callback: Flushes pending events to controllers that are
- * interested in them. */
-static void
-flush_queued_events_cb(mainloop_event_t *event, void *arg)
-{
- (void) event;
- (void) arg;
- queued_events_flush_all(0);
-}
-
-/** Send an event to all v1 controllers that are listening for code
- * <b>event</b>. The event's body is given by <b>msg</b>.
- *
- * The EXTENDED_FORMAT and NONEXTENDED_FORMAT flags behave similarly with
- * respect to the EXTENDED_EVENTS feature. */
-MOCK_IMPL(STATIC void,
-send_control_event_string,(uint16_t event,
- const char *msg))
-{
- tor_assert(event >= EVENT_MIN_ && event <= EVENT_MAX_);
- queue_control_event_string(event, tor_strdup(msg));
-}
-
-/** Helper for send_control_event and control_event_status:
- * Send an event to all v1 controllers that are listening for code
- * <b>event</b>. The event's body is created by the printf-style format in
- * <b>format</b>, and other arguments as provided. */
-static void
-send_control_event_impl(uint16_t event,
- const char *format, va_list ap)
-{
- char *buf = NULL;
- int len;
-
- len = tor_vasprintf(&buf, format, ap);
- if (len < 0) {
- log_warn(LD_BUG, "Unable to format event for controller.");
- return;
- }
-
- queue_control_event_string(event, buf);
-}
-
-/** Send an event to all v1 controllers that are listening for code
- * <b>event</b>. The event's body is created by the printf-style format in
- * <b>format</b>, and other arguments as provided. */
-static void
-send_control_event(uint16_t event,
- const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- send_control_event_impl(event, format, ap);
- va_end(ap);
-}
-
-/** Given a text circuit <b>id</b>, return the corresponding circuit. */
-static origin_circuit_t *
-get_circ(const char *id)
-{
- uint32_t n_id;
- int ok;
- n_id = (uint32_t) tor_parse_ulong(id, 10, 0, UINT32_MAX, &ok, NULL);
- if (!ok)
- return NULL;
- return circuit_get_by_global_id(n_id);
-}
-
-/** Given a text stream <b>id</b>, return the corresponding AP connection. */
-static entry_connection_t *
-get_stream(const char *id)
-{
- uint64_t n_id;
- int ok;
- connection_t *conn;
- n_id = tor_parse_uint64(id, 10, 0, UINT64_MAX, &ok, NULL);
- if (!ok)
- return NULL;
- conn = connection_get_by_global_id(n_id);
- if (!conn || conn->type != CONN_TYPE_AP || conn->marked_for_close)
- return NULL;
- return TO_ENTRY_CONN(conn);
-}
-
-/** Helper for setconf and resetconf. Acts like setconf, except
- * it passes <b>use_defaults</b> on to options_trial_assign(). Modifies the
- * contents of body.
- */
-static int
-control_setconf_helper(control_connection_t *conn, uint32_t len, char *body,
- int use_defaults)
-{
- setopt_err_t opt_err;
- config_line_t *lines=NULL;
- char *start = body;
- char *errstring = NULL;
- const unsigned flags =
- CAL_CLEAR_FIRST | (use_defaults ? CAL_USE_DEFAULTS : 0);
-
- char *config;
- smartlist_t *entries = smartlist_new();
-
- /* We have a string, "body", of the format '(key(=val|="val")?)' entries
- * separated by space. break it into a list of configuration entries. */
- while (*body) {
- char *eq = body;
- char *key;
- char *entry;
- while (!TOR_ISSPACE(*eq) && *eq != '=')
- ++eq;
- key = tor_strndup(body, eq-body);
- body = eq+1;
- if (*eq == '=') {
- char *val=NULL;
- size_t val_len=0;
- if (*body != '\"') {
- char *val_start = body;
- while (!TOR_ISSPACE(*body))
- body++;
- val = tor_strndup(val_start, body-val_start);
- val_len = strlen(val);
- } else {
- body = (char*)extract_escaped_string(body, (len - (body-start)),
- &val, &val_len);
- if (!body) {
- connection_write_str_to_buf("551 Couldn't parse string\r\n", conn);
- SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp));
- smartlist_free(entries);
- tor_free(key);
- return 0;
- }
- }
- tor_asprintf(&entry, "%s %s", key, val);
- tor_free(key);
- tor_free(val);
- } else {
- entry = key;
- }
- smartlist_add(entries, entry);
- while (TOR_ISSPACE(*body))
- ++body;
- }
-
- smartlist_add_strdup(entries, "");
- config = smartlist_join_strings(entries, "\n", 0, NULL);
- SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp));
- smartlist_free(entries);
-
- if (config_get_lines(config, &lines, 0) < 0) {
- log_warn(LD_CONTROL,"Controller gave us config lines we can't parse.");
- connection_write_str_to_buf("551 Couldn't parse configuration\r\n",
- conn);
- tor_free(config);
- return 0;
- }
- tor_free(config);
-
- opt_err = options_trial_assign(lines, flags, &errstring);
- {
- const char *msg;
- switch (opt_err) {
- case SETOPT_ERR_MISC:
- msg = "552 Unrecognized option";
- break;
- case SETOPT_ERR_PARSE:
- msg = "513 Unacceptable option value";
- break;
- case SETOPT_ERR_TRANSITION:
- msg = "553 Transition not allowed";
- break;
- case SETOPT_ERR_SETTING:
- default:
- msg = "553 Unable to set option";
- break;
- case SETOPT_OK:
- config_free_lines(lines);
- send_control_done(conn);
- return 0;
- }
- log_warn(LD_CONTROL,
- "Controller gave us config lines that didn't validate: %s",
- errstring);
- connection_printf_to_buf(conn, "%s: %s\r\n", msg, errstring);
- config_free_lines(lines);
- tor_free(errstring);
- return 0;
- }
-}
-
-/** Called when we receive a SETCONF message: parse the body and try
- * to update our configuration. Reply with a DONE or ERROR message.
- * Modifies the contents of body.*/
-static int
-handle_control_setconf(control_connection_t *conn, uint32_t len, char *body)
-{
- return control_setconf_helper(conn, len, body, 0);
-}
-
-/** Called when we receive a RESETCONF message: parse the body and try
- * to update our configuration. Reply with a DONE or ERROR message.
- * Modifies the contents of body. */
-static int
-handle_control_resetconf(control_connection_t *conn, uint32_t len, char *body)
-{
- return control_setconf_helper(conn, len, body, 1);
-}
-
-/** Called when we receive a GETCONF message. Parse the request, and
- * reply with a CONFVALUE or an ERROR message */
-static int
-handle_control_getconf(control_connection_t *conn, uint32_t body_len,
- const char *body)
-{
- smartlist_t *questions = smartlist_new();
- smartlist_t *answers = smartlist_new();
- smartlist_t *unrecognized = smartlist_new();
- char *msg = NULL;
- size_t msg_len;
- const or_options_t *options = get_options();
- int i, len;
-
- (void) body_len; /* body is NUL-terminated; so we can ignore len. */
- smartlist_split_string(questions, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
- if (!option_is_recognized(q)) {
- smartlist_add(unrecognized, (char*) q);
- } else {
- config_line_t *answer = option_get_assignment(options,q);
- if (!answer) {
- const char *name = option_get_canonical_name(q);
- smartlist_add_asprintf(answers, "250-%s\r\n", name);
- }
-
- while (answer) {
- config_line_t *next;
- smartlist_add_asprintf(answers, "250-%s=%s\r\n",
- answer->key, answer->value);
-
- next = answer->next;
- tor_free(answer->key);
- tor_free(answer->value);
- tor_free(answer);
- answer = next;
- }
- }
- } SMARTLIST_FOREACH_END(q);
-
- if ((len = smartlist_len(unrecognized))) {
- for (i=0; i < len-1; ++i)
- connection_printf_to_buf(conn,
- "552-Unrecognized configuration key \"%s\"\r\n",
- (char*)smartlist_get(unrecognized, i));
- connection_printf_to_buf(conn,
- "552 Unrecognized configuration key \"%s\"\r\n",
- (char*)smartlist_get(unrecognized, len-1));
- } else if ((len = smartlist_len(answers))) {
- char *tmp = smartlist_get(answers, len-1);
- tor_assert(strlen(tmp)>4);
- tmp[3] = ' ';
- msg = smartlist_join_strings(answers, "", 0, &msg_len);
- connection_buf_add(msg, msg_len, TO_CONN(conn));
- } else {
- connection_write_str_to_buf("250 OK\r\n", conn);
- }
-
- SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp));
- smartlist_free(answers);
- SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp));
- smartlist_free(questions);
- smartlist_free(unrecognized);
-
- tor_free(msg);
-
- return 0;
-}
-
-/** Called when we get a +LOADCONF message. */
-static int
-handle_control_loadconf(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- setopt_err_t retval;
- char *errstring = NULL;
- const char *msg = NULL;
- (void) len;
-
- retval = options_init_from_string(NULL, body, CMD_RUN_TOR, NULL, &errstring);
-
- if (retval != SETOPT_OK)
- log_warn(LD_CONTROL,
- "Controller gave us config file that didn't validate: %s",
- errstring);
-
- switch (retval) {
- case SETOPT_ERR_PARSE:
- msg = "552 Invalid config file";
- break;
- case SETOPT_ERR_TRANSITION:
- msg = "553 Transition not allowed";
- break;
- case SETOPT_ERR_SETTING:
- msg = "553 Unable to set option";
- break;
- case SETOPT_ERR_MISC:
- default:
- msg = "550 Unable to load config";
- break;
- case SETOPT_OK:
- break;
- }
- if (msg) {
- if (errstring)
- connection_printf_to_buf(conn, "%s: %s\r\n", msg, errstring);
- else
- connection_printf_to_buf(conn, "%s\r\n", msg);
- } else {
- send_control_done(conn);
- }
- tor_free(errstring);
- return 0;
-}
-
-/** Helper structure: maps event values to their names. */
-struct control_event_t {
- uint16_t event_code;
- const char *event_name;
-};
-/** Table mapping event values to their names. Used to implement SETEVENTS
- * and GETINFO events/names, and to keep they in sync. */
-static const struct control_event_t control_event_table[] = {
- { EVENT_CIRCUIT_STATUS, "CIRC" },
- { EVENT_CIRCUIT_STATUS_MINOR, "CIRC_MINOR" },
- { EVENT_STREAM_STATUS, "STREAM" },
- { EVENT_OR_CONN_STATUS, "ORCONN" },
- { EVENT_BANDWIDTH_USED, "BW" },
- { EVENT_DEBUG_MSG, "DEBUG" },
- { EVENT_INFO_MSG, "INFO" },
- { EVENT_NOTICE_MSG, "NOTICE" },
- { EVENT_WARN_MSG, "WARN" },
- { EVENT_ERR_MSG, "ERR" },
- { EVENT_NEW_DESC, "NEWDESC" },
- { EVENT_ADDRMAP, "ADDRMAP" },
- { EVENT_DESCCHANGED, "DESCCHANGED" },
- { EVENT_NS, "NS" },
- { EVENT_STATUS_GENERAL, "STATUS_GENERAL" },
- { EVENT_STATUS_CLIENT, "STATUS_CLIENT" },
- { EVENT_STATUS_SERVER, "STATUS_SERVER" },
- { EVENT_GUARD, "GUARD" },
- { EVENT_STREAM_BANDWIDTH_USED, "STREAM_BW" },
- { EVENT_CLIENTS_SEEN, "CLIENTS_SEEN" },
- { EVENT_NEWCONSENSUS, "NEWCONSENSUS" },
- { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" },
- { EVENT_GOT_SIGNAL, "SIGNAL" },
- { EVENT_CONF_CHANGED, "CONF_CHANGED"},
- { EVENT_CONN_BW, "CONN_BW" },
- { EVENT_CELL_STATS, "CELL_STATS" },
- { EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" },
- { EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" },
- { EVENT_HS_DESC, "HS_DESC" },
- { EVENT_HS_DESC_CONTENT, "HS_DESC_CONTENT" },
- { EVENT_NETWORK_LIVENESS, "NETWORK_LIVENESS" },
- { 0, NULL },
-};
-
-/** Called when we get a SETEVENTS message: update conn->event_mask,
- * and reply with DONE or ERROR. */
-static int
-handle_control_setevents(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- int event_code;
- event_mask_t event_mask = 0;
- smartlist_t *events = smartlist_new();
-
- (void) len;
-
- smartlist_split_string(events, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- SMARTLIST_FOREACH_BEGIN(events, const char *, ev)
- {
- if (!strcasecmp(ev, "EXTENDED") ||
- !strcasecmp(ev, "AUTHDIR_NEWDESCS")) {
- log_warn(LD_CONTROL, "The \"%s\" SETEVENTS argument is no longer "
- "supported.", ev);
- continue;
- } else {
- int i;
- event_code = -1;
-
- for (i = 0; control_event_table[i].event_name != NULL; ++i) {
- if (!strcasecmp(ev, control_event_table[i].event_name)) {
- event_code = control_event_table[i].event_code;
- break;
- }
- }
-
- if (event_code == -1) {
- connection_printf_to_buf(conn, "552 Unrecognized event \"%s\"\r\n",
- ev);
- SMARTLIST_FOREACH(events, char *, e, tor_free(e));
- smartlist_free(events);
- return 0;
- }
- }
- event_mask |= (((event_mask_t)1) << event_code);
- }
- SMARTLIST_FOREACH_END(ev);
- SMARTLIST_FOREACH(events, char *, e, tor_free(e));
- smartlist_free(events);
-
- conn->event_mask = event_mask;
-
- control_update_global_event_mask();
- send_control_done(conn);
- return 0;
-}
-
-/** Decode the hashed, base64'd passwords stored in <b>passwords</b>.
- * Return a smartlist of acceptable passwords (unterminated strings of
- * length S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN) on success, or NULL on
- * failure.
- */
-smartlist_t *
-decode_hashed_passwords(config_line_t *passwords)
-{
- char decoded[64];
- config_line_t *cl;
- smartlist_t *sl = smartlist_new();
-
- tor_assert(passwords);
-
- for (cl = passwords; cl; cl = cl->next) {
- const char *hashed = cl->value;
-
- if (!strcmpstart(hashed, "16:")) {
- 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;
- }
- } else {
- if (base64_decode(decoded, sizeof(decoded), hashed, strlen(hashed))
- != S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN) {
- goto err;
- }
- }
- smartlist_add(sl,
- tor_memdup(decoded, S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN));
- }
-
- return sl;
-
- err:
- SMARTLIST_FOREACH(sl, char*, cp, tor_free(cp));
- smartlist_free(sl);
- return NULL;
-}
-
-/** Called when we get an AUTHENTICATE message. Check whether the
- * authentication is valid, and if so, update the connection's state to
- * OPEN. Reply with DONE or ERROR.
- */
-static int
-handle_control_authenticate(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- int used_quoted_string = 0;
- const or_options_t *options = get_options();
- const char *errstr = "Unknown error";
- char *password;
- size_t password_len;
- const char *cp;
- int i;
- int bad_cookie=0, bad_password=0;
- smartlist_t *sl = NULL;
-
- if (!len) {
- password = tor_strdup("");
- password_len = 0;
- } else if (TOR_ISXDIGIT(body[0])) {
- cp = body;
- while (TOR_ISXDIGIT(*cp))
- ++cp;
- i = (int)(cp - body);
- tor_assert(i>0);
- password_len = i/2;
- password = tor_malloc(password_len + 1);
- 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 "
- "double quotes.\r\n", conn);
- connection_mark_for_close(TO_CONN(conn));
- tor_free(password);
- return 0;
- }
- } else {
- if (!decode_escaped_string(body, len, &password, &password_len)) {
- connection_write_str_to_buf("551 Invalid quoted string. You need "
- "to put the password in double quotes.\r\n", conn);
- connection_mark_for_close(TO_CONN(conn));
- return 0;
- }
- used_quoted_string = 1;
- }
-
- if (conn->safecookie_client_hash != NULL) {
- /* The controller has chosen safe cookie authentication; the only
- * acceptable authentication value is the controller-to-server
- * response. */
-
- tor_assert(authentication_cookie_is_set);
-
- if (password_len != DIGEST256_LEN) {
- log_warn(LD_CONTROL,
- "Got safe cookie authentication response with wrong length "
- "(%d)", (int)password_len);
- errstr = "Wrong length for safe cookie response.";
- goto err;
- }
-
- if (tor_memneq(conn->safecookie_client_hash, password, DIGEST256_LEN)) {
- log_warn(LD_CONTROL,
- "Got incorrect safe cookie authentication response");
- errstr = "Safe cookie response did not match expected value.";
- goto err;
- }
-
- tor_free(conn->safecookie_client_hash);
- goto ok;
- }
-
- if (!options->CookieAuthentication && !options->HashedControlPassword &&
- !options->HashedControlSessionPassword) {
- /* if Tor doesn't demand any stronger authentication, then
- * the controller can get in with anything. */
- goto ok;
- }
-
- if (options->CookieAuthentication) {
- int also_password = options->HashedControlPassword != NULL ||
- options->HashedControlSessionPassword != NULL;
- if (password_len != AUTHENTICATION_COOKIE_LEN) {
- if (!also_password) {
- log_warn(LD_CONTROL, "Got authentication cookie with wrong length "
- "(%d)", (int)password_len);
- errstr = "Wrong length on authentication cookie.";
- goto err;
- }
- bad_cookie = 1;
- } else if (tor_memneq(authentication_cookie, password, password_len)) {
- if (!also_password) {
- log_warn(LD_CONTROL, "Got mismatched authentication cookie");
- errstr = "Authentication cookie did not match expected value.";
- goto err;
- }
- bad_cookie = 1;
- } else {
- goto ok;
- }
- }
-
- if (options->HashedControlPassword ||
- options->HashedControlSessionPassword) {
- int bad = 0;
- smartlist_t *sl_tmp;
- char received[DIGEST_LEN];
- int also_cookie = options->CookieAuthentication;
- sl = smartlist_new();
- if (options->HashedControlPassword) {
- sl_tmp = decode_hashed_passwords(options->HashedControlPassword);
- if (!sl_tmp)
- bad = 1;
- else {
- smartlist_add_all(sl, sl_tmp);
- smartlist_free(sl_tmp);
- }
- }
- if (options->HashedControlSessionPassword) {
- sl_tmp = decode_hashed_passwords(options->HashedControlSessionPassword);
- if (!sl_tmp)
- bad = 1;
- else {
- smartlist_add_all(sl, sl_tmp);
- smartlist_free(sl_tmp);
- }
- }
- if (bad) {
- if (!also_cookie) {
- log_warn(LD_BUG,
- "Couldn't decode HashedControlPassword: invalid base16");
- errstr="Couldn't decode HashedControlPassword value in configuration.";
- goto err;
- }
- bad_password = 1;
- SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
- smartlist_free(sl);
- sl = NULL;
- } else {
- SMARTLIST_FOREACH(sl, char *, expected,
- {
- secret_to_key_rfc2440(received,DIGEST_LEN,
- password,password_len,expected);
- if (tor_memeq(expected + S2K_RFC2440_SPECIFIER_LEN,
- received, DIGEST_LEN))
- goto ok;
- });
- SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
- smartlist_free(sl);
- sl = NULL;
-
- if (used_quoted_string)
- errstr = "Password did not match HashedControlPassword value from "
- "configuration";
- else
- errstr = "Password did not match HashedControlPassword value from "
- "configuration. Maybe you tried a plain text password? "
- "If so, the standard requires that you put it in double quotes.";
- bad_password = 1;
- if (!also_cookie)
- goto err;
- }
- }
-
- /** We only get here if both kinds of authentication failed. */
- tor_assert(bad_password && bad_cookie);
- log_warn(LD_CONTROL, "Bad password or authentication cookie on controller.");
- errstr = "Password did not match HashedControlPassword *or* authentication "
- "cookie.";
-
- err:
- tor_free(password);
- 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 *, str, tor_free(str));
- smartlist_free(sl);
- }
- return 0;
- ok:
- log_info(LD_CONTROL, "Authenticated control connection ("TOR_SOCKET_T_FORMAT
- ")", conn->base_.s);
- send_control_done(conn);
- conn->base_.state = CONTROL_CONN_STATE_OPEN;
- tor_free(password);
- if (sl) { /* clean up */
- SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
- smartlist_free(sl);
- }
- return 0;
-}
-
-/** Called when we get a SAVECONF command. Try to flush the current options to
- * disk, and report success or failure. */
-static int
-handle_control_saveconf(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- (void) len;
-
- int force = !strcmpstart(body, "FORCE");
- const or_options_t *options = get_options();
- if ((!force && options->IncludeUsed) || options_save_current() < 0) {
- connection_write_str_to_buf(
- "551 Unable to write configuration to disk.\r\n", conn);
- } else {
- send_control_done(conn);
- }
- return 0;
-}
-
-struct signal_t {
- int sig;
- const char *signal_name;
-};
-
-static const struct signal_t signal_table[] = {
+const struct signal_name_t signal_table[] = {
{ SIGHUP, "RELOAD" },
{ SIGHUP, "HUP" },
{ SIGINT, "SHUTDOWN" },
@@ -1681,3588 +176,6 @@ static const struct signal_t signal_table[] = {
{ 0, NULL },
};
-/** Called when we get a SIGNAL command. React to the provided signal, and
- * report success or failure. (If the signal results in a shutdown, success
- * may not be reported.) */
-static int
-handle_control_signal(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- int sig = -1;
- int i;
- int n = 0;
- char *s;
-
- (void) len;
-
- while (body[n] && ! TOR_ISSPACE(body[n]))
- ++n;
- s = tor_strndup(body, n);
-
- for (i = 0; signal_table[i].signal_name != NULL; ++i) {
- if (!strcasecmp(s, signal_table[i].signal_name)) {
- sig = signal_table[i].sig;
- break;
- }
- }
-
- if (sig < 0)
- connection_printf_to_buf(conn, "552 Unrecognized signal code \"%s\"\r\n",
- s);
- tor_free(s);
- if (sig < 0)
- return 0;
-
- send_control_done(conn);
- /* Flush the "done" first if the signal might make us shut down. */
- if (sig == SIGTERM || sig == SIGINT)
- connection_flush(TO_CONN(conn));
-
- activate_signal(sig);
-
- return 0;
-}
-
-/** Called when we get a TAKEOWNERSHIP command. Mark this connection
- * as an owning connection, so that we will exit if the connection
- * closes. */
-static int
-handle_control_takeownership(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- (void)len;
- (void)body;
-
- conn->is_owning_control_connection = 1;
-
- log_info(LD_CONTROL, "Control connection %d has taken ownership of this "
- "Tor instance.",
- (int)(conn->base_.s));
-
- send_control_done(conn);
- return 0;
-}
-
-/** Called when we get a DROPOWNERSHIP command. Mark this connection
- * as a non-owning connection, so that we will not exit if the connection
- * closes. */
-static int
-handle_control_dropownership(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- (void)len;
- (void)body;
-
- conn->is_owning_control_connection = 0;
-
- log_info(LD_CONTROL, "Control connection %d has dropped ownership of this "
- "Tor instance.",
- (int)(conn->base_.s));
-
- send_control_done(conn);
- return 0;
-}
-
-/** Return true iff <b>addr</b> is unusable as a mapaddress target because of
- * containing funny characters. */
-static int
-address_is_invalid_mapaddress_target(const char *addr)
-{
- if (!strcmpstart(addr, "*."))
- return address_is_invalid_destination(addr+2, 1);
- else
- return address_is_invalid_destination(addr, 1);
-}
-
-/** Called when we get a MAPADDRESS command; try to bind all listed addresses,
- * and report success or failure. */
-static int
-handle_control_mapaddress(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- smartlist_t *elts;
- smartlist_t *lines;
- smartlist_t *reply;
- char *r;
- size_t sz;
- (void) len; /* body is NUL-terminated, so it's safe to ignore the length. */
-
- lines = smartlist_new();
- elts = smartlist_new();
- reply = smartlist_new();
- smartlist_split_string(lines, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- SMARTLIST_FOREACH_BEGIN(lines, char *, line) {
- tor_strlower(line);
- smartlist_split_string(elts, line, "=", 0, 2);
- if (smartlist_len(elts) == 2) {
- const char *from = smartlist_get(elts,0);
- const char *to = smartlist_get(elts,1);
- if (address_is_invalid_mapaddress_target(to)) {
- smartlist_add_asprintf(reply,
- "512-syntax error: invalid address '%s'", to);
- log_warn(LD_CONTROL,
- "Skipping invalid argument '%s' in MapAddress msg", to);
- } else if (!strcmp(from, ".") || !strcmp(from, "0.0.0.0") ||
- !strcmp(from, "::")) {
- const char type =
- !strcmp(from,".") ? RESOLVED_TYPE_HOSTNAME :
- (!strcmp(from, "0.0.0.0") ? RESOLVED_TYPE_IPV4 : RESOLVED_TYPE_IPV6);
- const char *address = addressmap_register_virtual_address(
- type, tor_strdup(to));
- if (!address) {
- smartlist_add_asprintf(reply,
- "451-resource exhausted: skipping '%s'", line);
- log_warn(LD_CONTROL,
- "Unable to allocate address for '%s' in MapAddress msg",
- safe_str_client(line));
- } else {
- smartlist_add_asprintf(reply, "250-%s=%s", address, to);
- }
- } else {
- const char *msg;
- if (addressmap_register_auto(from, to, 1,
- ADDRMAPSRC_CONTROLLER, &msg) < 0) {
- smartlist_add_asprintf(reply,
- "512-syntax error: invalid address mapping "
- " '%s': %s", line, msg);
- log_warn(LD_CONTROL,
- "Skipping invalid argument '%s' in MapAddress msg: %s",
- line, msg);
- } else {
- smartlist_add_asprintf(reply, "250-%s", line);
- }
- }
- } else {
- smartlist_add_asprintf(reply, "512-syntax error: mapping '%s' is "
- "not of expected form 'foo=bar'.", line);
- log_info(LD_CONTROL, "Skipping MapAddress '%s': wrong "
- "number of items.",
- safe_str_client(line));
- }
- SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
- smartlist_clear(elts);
- } SMARTLIST_FOREACH_END(line);
- SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
- smartlist_free(lines);
- smartlist_free(elts);
-
- if (smartlist_len(reply)) {
- ((char*)smartlist_get(reply,smartlist_len(reply)-1))[3] = ' ';
- r = smartlist_join_strings(reply, "\r\n", 1, &sz);
- connection_buf_add(r, sz, TO_CONN(conn));
- tor_free(r);
- } else {
- const char *response =
- "512 syntax error: not enough arguments to mapaddress.\r\n";
- connection_buf_add(response, strlen(response), TO_CONN(conn));
- }
-
- SMARTLIST_FOREACH(reply, char *, cp, tor_free(cp));
- smartlist_free(reply);
- return 0;
-}
-
-/** Implementation helper for GETINFO: knows the answers for various
- * trivial-to-implement questions. */
-static int
-getinfo_helper_misc(control_connection_t *conn, const char *question,
- char **answer, const char **errmsg)
-{
- (void) conn;
- if (!strcmp(question, "version")) {
- *answer = tor_strdup(get_version());
- } else if (!strcmp(question, "bw-event-cache")) {
- *answer = get_bw_samples();
- } else if (!strcmp(question, "config-file")) {
- const char *a = get_torrc_fname(0);
- if (a)
- *answer = tor_strdup(a);
- } else if (!strcmp(question, "config-defaults-file")) {
- const char *a = get_torrc_fname(1);
- if (a)
- *answer = tor_strdup(a);
- } else if (!strcmp(question, "config-text")) {
- *answer = options_dump(get_options(), OPTIONS_DUMP_MINIMAL);
- } else if (!strcmp(question, "config-can-saveconf")) {
- *answer = tor_strdup(get_options()->IncludeUsed ? "0" : "1");
- } else if (!strcmp(question, "info/names")) {
- *answer = list_getinfo_options();
- } else if (!strcmp(question, "dormant")) {
- int dormant = rep_hist_circbuilding_dormant(time(NULL));
- *answer = tor_strdup(dormant ? "1" : "0");
- } else if (!strcmp(question, "events/names")) {
- int i;
- smartlist_t *event_names = smartlist_new();
-
- for (i = 0; control_event_table[i].event_name != NULL; ++i) {
- smartlist_add(event_names, (char *)control_event_table[i].event_name);
- }
-
- *answer = smartlist_join_strings(event_names, " ", 0, NULL);
-
- smartlist_free(event_names);
- } else if (!strcmp(question, "signal/names")) {
- smartlist_t *signal_names = smartlist_new();
- int j;
- for (j = 0; signal_table[j].signal_name != NULL; ++j) {
- smartlist_add(signal_names, (char*)signal_table[j].signal_name);
- }
-
- *answer = smartlist_join_strings(signal_names, " ", 0, NULL);
-
- smartlist_free(signal_names);
- } else if (!strcmp(question, "features/names")) {
- *answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS");
- } else if (!strcmp(question, "address")) {
- uint32_t addr;
- if (router_pick_published_address(get_options(), &addr, 0) < 0) {
- *errmsg = "Address unknown";
- return -1;
- }
- *answer = tor_dup_ip(addr);
- } else if (!strcmp(question, "traffic/read")) {
- tor_asprintf(answer, "%"PRIu64, (get_bytes_read()));
- } else if (!strcmp(question, "traffic/written")) {
- tor_asprintf(answer, "%"PRIu64, (get_bytes_written()));
- } else if (!strcmp(question, "uptime")) {
- long uptime_secs = get_uptime();
- tor_asprintf(answer, "%ld", uptime_secs);
- } else if (!strcmp(question, "process/pid")) {
- int myPid = -1;
-
-#ifdef _WIN32
- myPid = _getpid();
-#else
- myPid = getpid();
-#endif
-
- tor_asprintf(answer, "%d", myPid);
- } else if (!strcmp(question, "process/uid")) {
-#ifdef _WIN32
- *answer = tor_strdup("-1");
-#else
- int myUid = geteuid();
- tor_asprintf(answer, "%d", myUid);
-#endif /* defined(_WIN32) */
- } else if (!strcmp(question, "process/user")) {
-#ifdef _WIN32
- *answer = tor_strdup("");
-#else
- int myUid = geteuid();
- const struct passwd *myPwEntry = tor_getpwuid(myUid);
-
- if (myPwEntry) {
- *answer = tor_strdup(myPwEntry->pw_name);
- } else {
- *answer = tor_strdup("");
- }
-#endif /* defined(_WIN32) */
- } else if (!strcmp(question, "process/descriptor-limit")) {
- int max_fds = get_max_sockets();
- tor_asprintf(answer, "%d", max_fds);
- } else if (!strcmp(question, "limits/max-mem-in-queues")) {
- tor_asprintf(answer, "%"PRIu64,
- (get_options()->MaxMemInQueues));
- } else if (!strcmp(question, "fingerprint")) {
- crypto_pk_t *server_key;
- if (!server_mode(get_options())) {
- *errmsg = "Not running in server mode";
- return -1;
- }
- server_key = get_server_identity_key();
- *answer = tor_malloc(HEX_DIGEST_LEN+1);
- crypto_pk_get_fingerprint(server_key, *answer, 0);
- }
- return 0;
-}
-
-/** Awful hack: return a newly allocated string based on a routerinfo and
- * (possibly) an extrainfo, sticking the read-history and write-history from
- * <b>ei</b> into the resulting string. The thing you get back won't
- * necessarily have a valid signature.
- *
- * New code should never use this; it's for backward compatibility.
- *
- * NOTE: <b>ri_body</b> is as returned by signed_descriptor_get_body: it might
- * not be NUL-terminated. */
-static char *
-munge_extrainfo_into_routerinfo(const char *ri_body,
- const signed_descriptor_t *ri,
- const signed_descriptor_t *ei)
-{
- char *out = NULL, *outp;
- int i;
- const char *router_sig;
- const char *ei_body = signed_descriptor_get_body(ei);
- size_t ri_len = ri->signed_descriptor_len;
- size_t ei_len = ei->signed_descriptor_len;
- if (!ei_body)
- goto bail;
-
- outp = out = tor_malloc(ri_len+ei_len+1);
- if (!(router_sig = tor_memstr(ri_body, ri_len, "\nrouter-signature")))
- goto bail;
- ++router_sig;
- memcpy(out, ri_body, router_sig-ri_body);
- outp += router_sig-ri_body;
-
- for (i=0; i < 2; ++i) {
- const char *kwd = i ? "\nwrite-history " : "\nread-history ";
- const char *cp, *eol;
- if (!(cp = tor_memstr(ei_body, ei_len, kwd)))
- continue;
- ++cp;
- if (!(eol = memchr(cp, '\n', ei_len - (cp-ei_body))))
- continue;
- memcpy(outp, cp, eol-cp+1);
- outp += eol-cp+1;
- }
- memcpy(outp, router_sig, ri_len - (router_sig-ri_body));
- *outp++ = '\0';
- tor_assert(outp-out < (int)(ri_len+ei_len+1));
-
- return out;
- bail:
- tor_free(out);
- return tor_strndup(ri_body, ri->signed_descriptor_len);
-}
-
-/** Implementation helper for GETINFO: answers requests for information about
- * which ports are bound. */
-static int
-getinfo_helper_listeners(control_connection_t *control_conn,
- const char *question,
- char **answer, const char **errmsg)
-{
- int type;
- smartlist_t *res;
-
- (void)control_conn;
- (void)errmsg;
-
- if (!strcmp(question, "net/listeners/or"))
- type = CONN_TYPE_OR_LISTENER;
- else if (!strcmp(question, "net/listeners/extor"))
- type = CONN_TYPE_EXT_OR_LISTENER;
- else if (!strcmp(question, "net/listeners/dir"))
- type = CONN_TYPE_DIR_LISTENER;
- else if (!strcmp(question, "net/listeners/socks"))
- type = CONN_TYPE_AP_LISTENER;
- else if (!strcmp(question, "net/listeners/trans"))
- type = CONN_TYPE_AP_TRANS_LISTENER;
- else if (!strcmp(question, "net/listeners/natd"))
- type = CONN_TYPE_AP_NATD_LISTENER;
- else if (!strcmp(question, "net/listeners/httptunnel"))
- type = CONN_TYPE_AP_HTTP_CONNECT_LISTENER;
- else if (!strcmp(question, "net/listeners/dns"))
- type = CONN_TYPE_AP_DNS_LISTENER;
- else if (!strcmp(question, "net/listeners/control"))
- type = CONN_TYPE_CONTROL_LISTENER;
- else
- return 0; /* unknown key */
-
- res = smartlist_new();
- SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
- struct sockaddr_storage ss;
- socklen_t ss_len = sizeof(ss);
-
- if (conn->type != type || conn->marked_for_close || !SOCKET_OK(conn->s))
- continue;
-
- if (getsockname(conn->s, (struct sockaddr *)&ss, &ss_len) < 0) {
- smartlist_add_asprintf(res, "%s:%d", conn->address, (int)conn->port);
- } else {
- char *tmp = tor_sockaddr_to_str((struct sockaddr *)&ss);
- smartlist_add(res, esc_for_log(tmp));
- tor_free(tmp);
- }
-
- } SMARTLIST_FOREACH_END(conn);
-
- *answer = smartlist_join_strings(res, " ", 0, NULL);
-
- SMARTLIST_FOREACH(res, char *, cp, tor_free(cp));
- smartlist_free(res);
- return 0;
-}
-
-/** Implementation helper for GETINFO: answers requests for information about
- * the current time in both local and UTC forms. */
-STATIC int
-getinfo_helper_current_time(control_connection_t *control_conn,
- const char *question,
- char **answer, const char **errmsg)
-{
- (void)control_conn;
- (void)errmsg;
-
- struct timeval now;
- tor_gettimeofday(&now);
- char timebuf[ISO_TIME_LEN+1];
-
- if (!strcmp(question, "current-time/local"))
- format_local_iso_time_nospace(timebuf, (time_t)now.tv_sec);
- else if (!strcmp(question, "current-time/utc"))
- format_iso_time_nospace(timebuf, (time_t)now.tv_sec);
- else
- return 0;
-
- *answer = tor_strdup(timebuf);
- return 0;
-}
-
-/** Implementation helper for GETINFO: knows the answers for questions about
- * directory information. */
-STATIC int
-getinfo_helper_dir(control_connection_t *control_conn,
- const char *question, char **answer,
- const char **errmsg)
-{
- (void) control_conn;
- if (!strcmpstart(question, "desc/id/")) {
- const routerinfo_t *ri = NULL;
- const node_t *node = node_get_by_hex_id(question+strlen("desc/id/"), 0);
- if (node)
- ri = node->ri;
- if (ri) {
- const char *body = signed_descriptor_get_body(&ri->cache_info);
- if (body)
- *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
- } else if (! we_fetch_router_descriptors(get_options())) {
- /* Descriptors won't be available, provide proper error */
- *errmsg = "We fetch microdescriptors, not router "
- "descriptors. You'll need to use md/id/* "
- "instead of desc/id/*.";
- return 0;
- }
- } else if (!strcmpstart(question, "desc/name/")) {
- const routerinfo_t *ri = NULL;
- /* XXX Setting 'warn_if_unnamed' here is a bit silly -- the
- * warning goes to the user, not to the controller. */
- const node_t *node =
- node_get_by_nickname(question+strlen("desc/name/"), 0);
- if (node)
- ri = node->ri;
- if (ri) {
- const char *body = signed_descriptor_get_body(&ri->cache_info);
- if (body)
- *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
- } else if (! we_fetch_router_descriptors(get_options())) {
- /* Descriptors won't be available, provide proper error */
- *errmsg = "We fetch microdescriptors, not router "
- "descriptors. You'll need to use md/name/* "
- "instead of desc/name/*.";
- return 0;
- }
- } else if (!strcmp(question, "desc/download-enabled")) {
- int r = we_fetch_router_descriptors(get_options());
- tor_asprintf(answer, "%d", !!r);
- } else if (!strcmp(question, "desc/all-recent")) {
- routerlist_t *routerlist = router_get_routerlist();
- smartlist_t *sl = smartlist_new();
- if (routerlist && routerlist->routers) {
- SMARTLIST_FOREACH(routerlist->routers, const routerinfo_t *, ri,
- {
- const char *body = signed_descriptor_get_body(&ri->cache_info);
- if (body)
- smartlist_add(sl,
- tor_strndup(body, ri->cache_info.signed_descriptor_len));
- });
- }
- *answer = smartlist_join_strings(sl, "", 0, NULL);
- SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
- smartlist_free(sl);
- } else if (!strcmp(question, "desc/all-recent-extrainfo-hack")) {
- /* XXXX Remove this once Torstat asks for extrainfos. */
- routerlist_t *routerlist = router_get_routerlist();
- smartlist_t *sl = smartlist_new();
- if (routerlist && routerlist->routers) {
- SMARTLIST_FOREACH_BEGIN(routerlist->routers, const routerinfo_t *, ri) {
- const char *body = signed_descriptor_get_body(&ri->cache_info);
- signed_descriptor_t *ei = extrainfo_get_by_descriptor_digest(
- ri->cache_info.extra_info_digest);
- if (ei && body) {
- smartlist_add(sl, munge_extrainfo_into_routerinfo(body,
- &ri->cache_info, ei));
- } else if (body) {
- smartlist_add(sl,
- tor_strndup(body, ri->cache_info.signed_descriptor_len));
- }
- } SMARTLIST_FOREACH_END(ri);
- }
- *answer = smartlist_join_strings(sl, "", 0, NULL);
- SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
- smartlist_free(sl);
- } else if (!strcmpstart(question, "hs/client/desc/id/")) {
- hostname_type_t addr_type;
-
- question += strlen("hs/client/desc/id/");
- if (rend_valid_v2_service_id(question)) {
- addr_type = ONION_V2_HOSTNAME;
- } else if (hs_address_is_valid(question)) {
- addr_type = ONION_V3_HOSTNAME;
- } else {
- *errmsg = "Invalid address";
- return -1;
- }
-
- if (addr_type == ONION_V2_HOSTNAME) {
- rend_cache_entry_t *e = NULL;
- if (!rend_cache_lookup_entry(question, -1, &e)) {
- /* Descriptor found in cache */
- *answer = tor_strdup(e->desc);
- } else {
- *errmsg = "Not found in cache";
- return -1;
- }
- } else {
- ed25519_public_key_t service_pk;
- const char *desc;
-
- /* The check before this if/else makes sure of this. */
- tor_assert(addr_type == ONION_V3_HOSTNAME);
-
- if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) {
- *errmsg = "Invalid v3 address";
- return -1;
- }
-
- desc = hs_cache_lookup_encoded_as_client(&service_pk);
- if (desc) {
- *answer = tor_strdup(desc);
- } else {
- *errmsg = "Not found in cache";
- return -1;
- }
- }
- } else if (!strcmpstart(question, "hs/service/desc/id/")) {
- hostname_type_t addr_type;
-
- question += strlen("hs/service/desc/id/");
- if (rend_valid_v2_service_id(question)) {
- addr_type = ONION_V2_HOSTNAME;
- } else if (hs_address_is_valid(question)) {
- addr_type = ONION_V3_HOSTNAME;
- } else {
- *errmsg = "Invalid address";
- return -1;
- }
- rend_cache_entry_t *e = NULL;
-
- if (addr_type == ONION_V2_HOSTNAME) {
- if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
- /* Descriptor found in cache */
- *answer = tor_strdup(e->desc);
- } else {
- *errmsg = "Not found in cache";
- return -1;
- }
- } else {
- ed25519_public_key_t service_pk;
- char *desc;
-
- /* The check before this if/else makes sure of this. */
- tor_assert(addr_type == ONION_V3_HOSTNAME);
-
- if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) {
- *errmsg = "Invalid v3 address";
- return -1;
- }
-
- desc = hs_service_lookup_current_desc(&service_pk);
- if (desc) {
- /* Newly allocated string, we have ownership. */
- *answer = desc;
- } else {
- *errmsg = "Not found in cache";
- return -1;
- }
- }
- } else if (!strcmp(question, "md/all")) {
- const smartlist_t *nodes = nodelist_get_list();
- tor_assert(nodes);
-
- if (smartlist_len(nodes) == 0) {
- *answer = tor_strdup("");
- return 0;
- }
-
- smartlist_t *microdescs = smartlist_new();
-
- SMARTLIST_FOREACH_BEGIN(nodes, node_t *, n) {
- if (n->md && n->md->body) {
- char *copy = tor_strndup(n->md->body, n->md->bodylen);
- smartlist_add(microdescs, copy);
- }
- } SMARTLIST_FOREACH_END(n);
-
- *answer = smartlist_join_strings(microdescs, "", 0, NULL);
- SMARTLIST_FOREACH(microdescs, char *, md, tor_free(md));
- smartlist_free(microdescs);
- } else if (!strcmpstart(question, "md/id/")) {
- const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0);
- const microdesc_t *md = NULL;
- if (node) md = node->md;
- if (md && md->body) {
- *answer = tor_strndup(md->body, md->bodylen);
- }
- } else if (!strcmpstart(question, "md/name/")) {
- /* XXX Setting 'warn_if_unnamed' here is a bit silly -- the
- * warning goes to the user, not to the controller. */
- const node_t *node = node_get_by_nickname(question+strlen("md/name/"), 0);
- /* XXXX duplicated code */
- const microdesc_t *md = NULL;
- if (node) md = node->md;
- if (md && md->body) {
- *answer = tor_strndup(md->body, md->bodylen);
- }
- } else if (!strcmp(question, "md/download-enabled")) {
- int r = we_fetch_microdescriptors(get_options());
- tor_asprintf(answer, "%d", !!r);
- } else if (!strcmpstart(question, "desc-annotations/id/")) {
- const routerinfo_t *ri = NULL;
- const node_t *node =
- node_get_by_hex_id(question+strlen("desc-annotations/id/"), 0);
- if (node)
- ri = node->ri;
- if (ri) {
- const char *annotations =
- signed_descriptor_get_annotations(&ri->cache_info);
- if (annotations)
- *answer = tor_strndup(annotations,
- ri->cache_info.annotations_len);
- }
- } else if (!strcmpstart(question, "dir/server/")) {
- size_t answer_len = 0;
- char *url = NULL;
- smartlist_t *descs = smartlist_new();
- const char *msg;
- int res;
- char *cp;
- tor_asprintf(&url, "/tor/%s", question+4);
- res = dirserv_get_routerdescs(descs, url, &msg);
- if (res) {
- log_warn(LD_CONTROL, "getinfo '%s': %s", question, msg);
- smartlist_free(descs);
- tor_free(url);
- *errmsg = msg;
- return -1;
- }
- SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
- answer_len += sd->signed_descriptor_len);
- cp = *answer = tor_malloc(answer_len+1);
- SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
- {
- memcpy(cp, signed_descriptor_get_body(sd),
- sd->signed_descriptor_len);
- cp += sd->signed_descriptor_len;
- });
- *cp = '\0';
- tor_free(url);
- smartlist_free(descs);
- } else if (!strcmpstart(question, "dir/status/")) {
- *answer = tor_strdup("");
- } else if (!strcmp(question, "dir/status-vote/current/consensus")) { /* v3 */
- if (we_want_to_fetch_flavor(get_options(), FLAV_NS)) {
- const cached_dir_t *consensus = dirserv_get_consensus("ns");
- if (consensus)
- *answer = tor_strdup(consensus->dir);
- }
- if (!*answer) { /* try loading it from disk */
- tor_mmap_t *mapped = networkstatus_map_cached_consensus("ns");
- if (mapped) {
- *answer = tor_memdup_nulterm(mapped->data, mapped->size);
- tor_munmap_file(mapped);
- }
- if (!*answer) { /* generate an error */
- *errmsg = "Could not open cached consensus. "
- "Make sure FetchUselessDescriptors is set to 1.";
- return -1;
- }
- }
- } else if (!strcmp(question, "network-status")) { /* v1 */
- static int network_status_warned = 0;
- if (!network_status_warned) {
- log_warn(LD_CONTROL, "GETINFO network-status is deprecated; it will "
- "go away in a future version of Tor.");
- network_status_warned = 1;
- }
- routerlist_t *routerlist = router_get_routerlist();
- if (!routerlist || !routerlist->routers ||
- list_server_status_v1(routerlist->routers, answer, 1) < 0) {
- return -1;
- }
- } else if (!strcmpstart(question, "extra-info/digest/")) {
- question += strlen("extra-info/digest/");
- if (strlen(question) == HEX_DIGEST_LEN) {
- char d[DIGEST_LEN];
- signed_descriptor_t *sd = NULL;
- 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
- * when it might be freed) are different from those for using one
- * we have downloaded. */
- if (router_extrainfo_digest_is_me(d))
- sd = &(router_get_my_extrainfo()->cache_info);
- else
- sd = extrainfo_get_by_descriptor_digest(d);
- }
- if (sd) {
- const char *body = signed_descriptor_get_body(sd);
- if (body)
- *answer = tor_strndup(body, sd->signed_descriptor_len);
- }
- }
- }
-
- 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;
- 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, download_status_get_next_attempt_at(dl));
-
- 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;
- }
-
- backoff_str = "DL_SCHED_RANDOM_EXPONENTIAL";
-
- /* Now assemble them */
- tor_asprintf(&rv,
- "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"
- "last-backoff-position %u\n"
- "last-delay-used %d\n",
- tbuf,
- dl->n_download_failures,
- dl->n_download_attempts,
- schedule_str,
- want_authority_str,
- increment_on_str,
- backoff_str,
- dl->last_backoff_position,
- dl->last_delay_used);
- }
-
- 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 *
-circuit_describe_status_for_controller(origin_circuit_t *circ)
-{
- char *rv;
- smartlist_t *descparts = smartlist_new();
-
- {
- char *vpath = circuit_list_path_for_controller(circ);
- if (*vpath) {
- smartlist_add(descparts, vpath);
- } else {
- tor_free(vpath); /* empty path; don't put an extra space in the result */
- }
- }
-
- {
- cpath_build_state_t *build_state = circ->build_state;
- smartlist_t *flaglist = smartlist_new();
- char *flaglist_joined;
-
- if (build_state->onehop_tunnel)
- smartlist_add(flaglist, (void *)"ONEHOP_TUNNEL");
- if (build_state->is_internal)
- smartlist_add(flaglist, (void *)"IS_INTERNAL");
- if (build_state->need_capacity)
- smartlist_add(flaglist, (void *)"NEED_CAPACITY");
- if (build_state->need_uptime)
- smartlist_add(flaglist, (void *)"NEED_UPTIME");
-
- /* Only emit a BUILD_FLAGS argument if it will have a non-empty value. */
- if (smartlist_len(flaglist)) {
- flaglist_joined = smartlist_join_strings(flaglist, ",", 0, NULL);
-
- smartlist_add_asprintf(descparts, "BUILD_FLAGS=%s", flaglist_joined);
-
- tor_free(flaglist_joined);
- }
-
- smartlist_free(flaglist);
- }
-
- smartlist_add_asprintf(descparts, "PURPOSE=%s",
- circuit_purpose_to_controller_string(circ->base_.purpose));
-
- {
- const char *hs_state =
- circuit_purpose_to_controller_hs_state_string(circ->base_.purpose);
-
- if (hs_state != NULL) {
- smartlist_add_asprintf(descparts, "HS_STATE=%s", hs_state);
- }
- }
-
- if (circ->rend_data != NULL || circ->hs_ident != NULL) {
- char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
- const char *onion_address;
- if (circ->rend_data) {
- onion_address = rend_data_get_address(circ->rend_data);
- } else {
- hs_build_address(&circ->hs_ident->identity_pk, HS_VERSION_THREE, addr);
- onion_address = addr;
- }
- smartlist_add_asprintf(descparts, "REND_QUERY=%s", onion_address);
- }
-
- {
- char tbuf[ISO_TIME_USEC_LEN+1];
- format_iso_time_nospace_usec(tbuf, &circ->base_.timestamp_created);
-
- smartlist_add_asprintf(descparts, "TIME_CREATED=%s", tbuf);
- }
-
- // Show username and/or password if available.
- if (circ->socks_username_len > 0) {
- char* socks_username_escaped = esc_for_log_len(circ->socks_username,
- (size_t) circ->socks_username_len);
- smartlist_add_asprintf(descparts, "SOCKS_USERNAME=%s",
- socks_username_escaped);
- tor_free(socks_username_escaped);
- }
- if (circ->socks_password_len > 0) {
- char* socks_password_escaped = esc_for_log_len(circ->socks_password,
- (size_t) circ->socks_password_len);
- smartlist_add_asprintf(descparts, "SOCKS_PASSWORD=%s",
- socks_password_escaped);
- tor_free(socks_password_escaped);
- }
-
- rv = smartlist_join_strings(descparts, " ", 0, NULL);
-
- SMARTLIST_FOREACH(descparts, char *, cp, tor_free(cp));
- smartlist_free(descparts);
-
- return rv;
-}
-
-/** Implementation helper for GETINFO: knows how to generate summaries of the
- * current states of things we send events about. */
-static int
-getinfo_helper_events(control_connection_t *control_conn,
- const char *question, char **answer,
- const char **errmsg)
-{
- const or_options_t *options = get_options();
- (void) control_conn;
- if (!strcmp(question, "circuit-status")) {
- smartlist_t *status = smartlist_new();
- SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ_) {
- origin_circuit_t *circ;
- char *circdesc;
- const char *state;
- if (! CIRCUIT_IS_ORIGIN(circ_) || circ_->marked_for_close)
- continue;
- circ = TO_ORIGIN_CIRCUIT(circ_);
-
- if (circ->base_.state == CIRCUIT_STATE_OPEN)
- state = "BUILT";
- else if (circ->base_.state == CIRCUIT_STATE_GUARD_WAIT)
- state = "GUARD_WAIT";
- else if (circ->cpath)
- state = "EXTENDED";
- else
- state = "LAUNCHED";
-
- circdesc = circuit_describe_status_for_controller(circ);
-
- smartlist_add_asprintf(status, "%lu %s%s%s",
- (unsigned long)circ->global_identifier,
- state, *circdesc ? " " : "", circdesc);
- tor_free(circdesc);
- }
- SMARTLIST_FOREACH_END(circ_);
- *answer = smartlist_join_strings(status, "\r\n", 0, NULL);
- SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
- smartlist_free(status);
- } else if (!strcmp(question, "stream-status")) {
- smartlist_t *conns = get_connection_array();
- smartlist_t *status = smartlist_new();
- char buf[256];
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
- const char *state;
- entry_connection_t *conn;
- circuit_t *circ;
- origin_circuit_t *origin_circ = NULL;
- if (base_conn->type != CONN_TYPE_AP ||
- base_conn->marked_for_close ||
- base_conn->state == AP_CONN_STATE_SOCKS_WAIT ||
- base_conn->state == AP_CONN_STATE_NATD_WAIT)
- continue;
- conn = TO_ENTRY_CONN(base_conn);
- switch (base_conn->state)
- {
- case AP_CONN_STATE_CONTROLLER_WAIT:
- case AP_CONN_STATE_CIRCUIT_WAIT:
- if (conn->socks_request &&
- SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command))
- state = "NEWRESOLVE";
- else
- state = "NEW";
- break;
- case AP_CONN_STATE_RENDDESC_WAIT:
- case AP_CONN_STATE_CONNECT_WAIT:
- state = "SENTCONNECT"; break;
- case AP_CONN_STATE_RESOLVE_WAIT:
- state = "SENTRESOLVE"; break;
- case AP_CONN_STATE_OPEN:
- state = "SUCCEEDED"; break;
- default:
- log_warn(LD_BUG, "Asked for stream in unknown state %d",
- base_conn->state);
- continue;
- }
- circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn));
- if (circ && CIRCUIT_IS_ORIGIN(circ))
- origin_circ = TO_ORIGIN_CIRCUIT(circ);
- write_stream_target_to_buf(conn, buf, sizeof(buf));
- smartlist_add_asprintf(status, "%lu %s %lu %s",
- (unsigned long) base_conn->global_identifier,state,
- origin_circ?
- (unsigned long)origin_circ->global_identifier : 0ul,
- buf);
- } SMARTLIST_FOREACH_END(base_conn);
- *answer = smartlist_join_strings(status, "\r\n", 0, NULL);
- SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
- smartlist_free(status);
- } else if (!strcmp(question, "orconn-status")) {
- smartlist_t *conns = get_connection_array();
- smartlist_t *status = smartlist_new();
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
- const char *state;
- char name[128];
- or_connection_t *conn;
- if (base_conn->type != CONN_TYPE_OR || base_conn->marked_for_close)
- continue;
- conn = TO_OR_CONN(base_conn);
- if (conn->base_.state == OR_CONN_STATE_OPEN)
- state = "CONNECTED";
- else if (conn->nickname)
- state = "LAUNCHED";
- else
- state = "NEW";
- orconn_target_get_name(name, sizeof(name), conn);
- smartlist_add_asprintf(status, "%s %s", name, state);
- } SMARTLIST_FOREACH_END(base_conn);
- *answer = smartlist_join_strings(status, "\r\n", 0, NULL);
- SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
- smartlist_free(status);
- } else if (!strcmpstart(question, "address-mappings/")) {
- time_t min_e, max_e;
- smartlist_t *mappings;
- question += strlen("address-mappings/");
- if (!strcmp(question, "all")) {
- min_e = 0; max_e = TIME_MAX;
- } else if (!strcmp(question, "cache")) {
- min_e = 2; max_e = TIME_MAX;
- } else if (!strcmp(question, "config")) {
- min_e = 0; max_e = 0;
- } else if (!strcmp(question, "control")) {
- min_e = 1; max_e = 1;
- } else {
- return 0;
- }
- mappings = smartlist_new();
- addressmap_get_mappings(mappings, min_e, max_e, 1);
- *answer = smartlist_join_strings(mappings, "\r\n", 0, NULL);
- SMARTLIST_FOREACH(mappings, char *, cp, tor_free(cp));
- smartlist_free(mappings);
- } else if (!strcmpstart(question, "status/")) {
- /* Note that status/ is not a catch-all for events; there's only supposed
- * to be a status GETINFO if there's a corresponding STATUS event. */
- if (!strcmp(question, "status/circuit-established")) {
- *answer = tor_strdup(have_completed_a_circuit() ? "1" : "0");
- } else if (!strcmp(question, "status/enough-dir-info")) {
- *answer = tor_strdup(router_have_minimum_dir_info() ? "1" : "0");
- } else if (!strcmp(question, "status/good-server-descriptor") ||
- !strcmp(question, "status/accepted-server-descriptor")) {
- /* They're equivalent for now, until we can figure out how to make
- * good-server-descriptor be what we want. See comment in
- * control-spec.txt. */
- *answer = tor_strdup(directories_have_accepted_server_descriptor()
- ? "1" : "0");
- } else if (!strcmp(question, "status/reachability-succeeded/or")) {
- *answer = tor_strdup(check_whether_orport_reachable(options) ?
- "1" : "0");
- } else if (!strcmp(question, "status/reachability-succeeded/dir")) {
- *answer = tor_strdup(check_whether_dirport_reachable(options) ?
- "1" : "0");
- } else if (!strcmp(question, "status/reachability-succeeded")) {
- tor_asprintf(answer, "OR=%d DIR=%d",
- check_whether_orport_reachable(options) ? 1 : 0,
- check_whether_dirport_reachable(options) ? 1 : 0);
- } else if (!strcmp(question, "status/bootstrap-phase")) {
- *answer = control_event_boot_last_msg();
- } else if (!strcmpstart(question, "status/version/")) {
- int is_server = server_mode(options);
- networkstatus_t *c = networkstatus_get_latest_consensus();
- version_status_t status;
- const char *recommended;
- if (c) {
- recommended = is_server ? c->server_versions : c->client_versions;
- status = tor_version_is_obsolete(VERSION, recommended);
- } else {
- recommended = "?";
- status = VS_UNKNOWN;
- }
-
- if (!strcmp(question, "status/version/recommended")) {
- *answer = tor_strdup(recommended);
- return 0;
- }
- if (!strcmp(question, "status/version/current")) {
- switch (status)
- {
- case VS_RECOMMENDED: *answer = tor_strdup("recommended"); break;
- case VS_OLD: *answer = tor_strdup("obsolete"); break;
- case VS_NEW: *answer = tor_strdup("new"); break;
- case VS_NEW_IN_SERIES: *answer = tor_strdup("new in series"); break;
- case VS_UNRECOMMENDED: *answer = tor_strdup("unrecommended"); break;
- case VS_EMPTY: *answer = tor_strdup("none recommended"); break;
- case VS_UNKNOWN: *answer = tor_strdup("unknown"); break;
- default: tor_fragile_assert();
- }
- }
- } else if (!strcmp(question, "status/clients-seen")) {
- char *bridge_stats = geoip_get_bridge_stats_controller(time(NULL));
- if (!bridge_stats) {
- *errmsg = "No bridge-client stats available";
- return -1;
- }
- *answer = bridge_stats;
- } else if (!strcmp(question, "status/fresh-relay-descs")) {
- if (!server_mode(options)) {
- *errmsg = "Only relays have descriptors";
- return -1;
- }
- routerinfo_t *r;
- extrainfo_t *e;
- if (router_build_fresh_descriptor(&r, &e) < 0) {
- *errmsg = "Error generating descriptor";
- return -1;
- }
- size_t size = r->cache_info.signed_descriptor_len + 1;
- if (e) {
- size += e->cache_info.signed_descriptor_len + 1;
- }
- tor_assert(r->cache_info.signed_descriptor_len);
- char *descs = tor_malloc(size);
- char *cp = descs;
- memcpy(cp, signed_descriptor_get_body(&r->cache_info),
- r->cache_info.signed_descriptor_len);
- cp += r->cache_info.signed_descriptor_len - 1;
- if (e) {
- if (cp[0] == '\0') {
- cp[0] = '\n';
- } else if (cp[0] != '\n') {
- cp[1] = '\n';
- cp++;
- }
- memcpy(cp, signed_descriptor_get_body(&e->cache_info),
- e->cache_info.signed_descriptor_len);
- cp += e->cache_info.signed_descriptor_len - 1;
- }
- if (cp[0] == '\n') {
- cp[0] = '\0';
- } else if (cp[0] != '\0') {
- cp[1] = '\0';
- }
- *answer = descs;
- routerinfo_free(r);
- extrainfo_free(e);
- } else {
- return 0;
- }
- }
- return 0;
-}
-
-/** Implementation helper for GETINFO: knows how to enumerate hidden services
- * created via the control port. */
-STATIC int
-getinfo_helper_onions(control_connection_t *control_conn,
- const char *question, char **answer,
- const char **errmsg)
-{
- smartlist_t *onion_list = NULL;
- (void) errmsg; /* no errors from this method */
-
- if (control_conn && !strcmp(question, "onions/current")) {
- onion_list = control_conn->ephemeral_onion_services;
- } else if (!strcmp(question, "onions/detached")) {
- onion_list = detached_onion_services;
- } else {
- return 0;
- }
- if (!onion_list || smartlist_len(onion_list) == 0) {
- if (answer) {
- *answer = tor_strdup("");
- }
- } else {
- if (answer) {
- *answer = smartlist_join_strings(onion_list, "\r\n", 0, NULL);
- }
- }
-
- return 0;
-}
-
-/** Implementation helper for GETINFO: answers queries about network
- * liveness. */
-static int
-getinfo_helper_liveness(control_connection_t *control_conn,
- const char *question, char **answer,
- const char **errmsg)
-{
- (void)control_conn;
- (void)errmsg;
- if (strcmp(question, "network-liveness") == 0) {
- if (get_cached_network_liveness()) {
- *answer = tor_strdup("up");
- } else {
- *answer = tor_strdup("down");
- }
- }
-
- return 0;
-}
-
-/** Implementation helper for GETINFO: answers queries about shared random
- * value. */
-static int
-getinfo_helper_sr(control_connection_t *control_conn,
- const char *question, char **answer,
- const char **errmsg)
-{
- (void) control_conn;
- (void) errmsg;
-
- if (!strcmp(question, "sr/current")) {
- *answer = sr_get_current_for_control();
- } else if (!strcmp(question, "sr/previous")) {
- *answer = sr_get_previous_for_control();
- }
- /* Else statement here is unrecognized key so do nothing. */
-
- return 0;
-}
-
-/** Callback function for GETINFO: on a given control connection, try to
- * answer the question <b>q</b> and store the newly-allocated answer in
- * *<b>a</b>. If an internal error occurs, return -1 and optionally set
- * *<b>error_out</b> to point to an error message to be delivered to the
- * controller. On success, _or if the key is not recognized_, return 0. Do not
- * set <b>a</b> if the key is not recognized but you may set <b>error_out</b>
- * to improve the error message.
- */
-typedef int (*getinfo_helper_t)(control_connection_t *,
- const char *q, char **a,
- const char **error_out);
-
-/** A single item for the GETINFO question-to-answer-function table. */
-typedef struct getinfo_item_t {
- const char *varname; /**< The value (or prefix) of the question. */
- getinfo_helper_t fn; /**< The function that knows the answer: NULL if
- * this entry is documentation-only. */
- const char *desc; /**< Description of the variable. */
- int is_prefix; /** Must varname match exactly, or must it be a prefix? */
-} getinfo_item_t;
-
-#define ITEM(name, fn, desc) { name, getinfo_helper_##fn, desc, 0 }
-#define PREFIX(name, fn, desc) { name, getinfo_helper_##fn, desc, 1 }
-#define DOC(name, desc) { name, NULL, desc, 0 }
-
-/** Table mapping questions accepted by GETINFO to the functions that know how
- * to answer them. */
-static const getinfo_item_t getinfo_items[] = {
- ITEM("version", misc, "The current version of Tor."),
- ITEM("bw-event-cache", misc, "Cached BW events for a short interval."),
- ITEM("config-file", misc, "Current location of the \"torrc\" file."),
- ITEM("config-defaults-file", misc, "Current location of the defaults file."),
- ITEM("config-text", misc,
- "Return the string that would be written by a saveconf command."),
- ITEM("config-can-saveconf", misc,
- "Is it possible to save the configuration to the \"torrc\" file?"),
- ITEM("accounting/bytes", accounting,
- "Number of bytes read/written so far in the accounting interval."),
- ITEM("accounting/bytes-left", accounting,
- "Number of bytes left to write/read so far in the accounting interval."),
- ITEM("accounting/enabled", accounting, "Is accounting currently enabled?"),
- ITEM("accounting/hibernating", accounting, "Are we hibernating or awake?"),
- ITEM("accounting/interval-start", accounting,
- "Time when the accounting period starts."),
- ITEM("accounting/interval-end", accounting,
- "Time when the accounting period ends."),
- ITEM("accounting/interval-wake", accounting,
- "Time to wake up in this accounting period."),
- ITEM("helper-nodes", entry_guards, NULL), /* deprecated */
- ITEM("entry-guards", entry_guards,
- "Which nodes are we using as entry guards?"),
- ITEM("fingerprint", misc, NULL),
- PREFIX("config/", config, "Current configuration values."),
- DOC("config/names",
- "List of configuration options, types, and documentation."),
- DOC("config/defaults",
- "List of default values for configuration options. "
- "See also config/names"),
- PREFIX("current-time/", current_time, "Current time."),
- DOC("current-time/local", "Current time on the local system."),
- DOC("current-time/utc", "Current UTC time."),
- 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,
- "Events that the controller can ask for with SETEVENTS."),
- ITEM("signal/names", misc, "Signal names recognized by the SIGNAL command"),
- ITEM("features/names", misc, "What arguments can USEFEATURE take?"),
- PREFIX("desc/id/", dir, "Router descriptors by ID."),
- PREFIX("desc/name/", dir, "Router descriptors by nickname."),
- ITEM("desc/all-recent", dir,
- "All non-expired, non-superseded router descriptors."),
- ITEM("desc/download-enabled", dir,
- "Do we try to download router descriptors?"),
- ITEM("desc/all-recent-extrainfo-hack", dir, NULL), /* Hack. */
- ITEM("md/all", dir, "All known microdescriptors."),
- PREFIX("md/id/", dir, "Microdescriptors by ID"),
- PREFIX("md/name/", dir, "Microdescriptors by name"),
- ITEM("md/download-enabled", dir,
- "Do we try to download microdescriptors?"),
- PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."),
- PREFIX("hs/client/desc/id", dir,
- "Hidden Service descriptor in client's cache by onion."),
- PREFIX("hs/service/desc/id/", dir,
- "Hidden Service descriptor in services's cache by onion."),
- PREFIX("net/listeners/", listeners, "Bound addresses by type"),
- ITEM("ns/all", networkstatus,
- "Brief summary of router status (v2 directory format)"),
- PREFIX("ns/id/", networkstatus,
- "Brief summary of router status by ID (v2 directory format)."),
- PREFIX("ns/name/", networkstatus,
- "Brief summary of router status by nickname (v2 directory format)."),
- PREFIX("ns/purpose/", networkstatus,
- "Brief summary of router status by purpose (v2 directory format)."),
- PREFIX("consensus/", networkstatus,
- "Information about and from the ns consensus."),
- ITEM("network-status", dir,
- "Brief summary of router status (v1 directory format)"),
- ITEM("network-liveness", liveness,
- "Current opinion on whether the network is live"),
- ITEM("circuit-status", events, "List of current circuits originating here."),
- ITEM("stream-status", events,"List of current streams."),
- ITEM("orconn-status", events, "A list of current OR connections."),
- ITEM("dormant", misc,
- "Is Tor dormant (not building circuits because it's idle)?"),
- PREFIX("address-mappings/", events, NULL),
- DOC("address-mappings/all", "Current address mappings."),
- DOC("address-mappings/cache", "Current cached DNS replies."),
- DOC("address-mappings/config",
- "Current address mappings from configuration."),
- DOC("address-mappings/control", "Current address mappings from controller."),
- PREFIX("status/", events, NULL),
- DOC("status/circuit-established",
- "Whether we think client functionality is working."),
- DOC("status/enough-dir-info",
- "Whether we have enough up-to-date directory information to build "
- "circuits."),
- DOC("status/bootstrap-phase",
- "The last bootstrap phase status event that Tor sent."),
- DOC("status/clients-seen",
- "Breakdown of client countries seen by a bridge."),
- DOC("status/fresh-relay-descs",
- "A fresh relay/ei descriptor pair for Tor's current state. Not stored."),
- DOC("status/version/recommended", "List of currently recommended versions."),
- DOC("status/version/current", "Status of the current version."),
- ITEM("address", misc, "IP address of this Tor host, if we can guess it."),
- ITEM("traffic/read", misc,"Bytes read since the process was started."),
- ITEM("traffic/written", misc,
- "Bytes written since the process was started."),
- ITEM("uptime", misc, "Uptime of the Tor daemon in seconds."),
- ITEM("process/pid", misc, "Process id belonging to the main tor process."),
- ITEM("process/uid", misc, "User id running the tor process."),
- ITEM("process/user", misc,
- "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"),
- PREFIX("desc-annotations/id/", dir, "Router annotations by hexdigest."),
- PREFIX("dir/server/", dir,"Router descriptors as retrieved from a DirPort."),
- PREFIX("dir/status/", dir,
- "v2 networkstatus docs as retrieved from a DirPort."),
- ITEM("dir/status-vote/current/consensus", dir,
- "v3 Networkstatus consensus as retrieved from a DirPort."),
- ITEM("exit-policy/default", policies,
- "The default value appended to the configured exit policy."),
- ITEM("exit-policy/reject-private/default", policies,
- "The default rules appended to the configured exit policy by"
- " ExitPolicyRejectPrivate."),
- ITEM("exit-policy/reject-private/relay", policies,
- "The relay-specific rules appended to the configured exit policy by"
- " 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"),
- PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"),
- ITEM("onions/current", onions,
- "Onion services owned by the current control connection."),
- ITEM("onions/detached", onions,
- "Onion services detached from the control connection."),
- ITEM("sr/current", sr, "Get current shared random value."),
- ITEM("sr/previous", sr, "Get previous shared random value."),
- { NULL, NULL, NULL, 0 }
-};
-
-/** Allocate and return a list of recognized GETINFO options. */
-static char *
-list_getinfo_options(void)
-{
- int i;
- smartlist_t *lines = smartlist_new();
- char *ans;
- for (i = 0; getinfo_items[i].varname; ++i) {
- if (!getinfo_items[i].desc)
- continue;
-
- smartlist_add_asprintf(lines, "%s%s -- %s\n",
- getinfo_items[i].varname,
- getinfo_items[i].is_prefix ? "*" : "",
- getinfo_items[i].desc);
- }
- smartlist_sort_strings(lines);
-
- ans = smartlist_join_strings(lines, "", 0, NULL);
- SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
- smartlist_free(lines);
-
- return ans;
-}
-
-/** Lookup the 'getinfo' entry <b>question</b>, and return
- * the answer in <b>*answer</b> (or NULL if key not recognized).
- * Return 0 if success or unrecognized, or -1 if recognized but
- * internal error. */
-static int
-handle_getinfo_helper(control_connection_t *control_conn,
- const char *question, char **answer,
- const char **err_out)
-{
- int i;
- *answer = NULL; /* unrecognized key by default */
-
- for (i = 0; getinfo_items[i].varname; ++i) {
- int match;
- if (getinfo_items[i].is_prefix)
- match = !strcmpstart(question, getinfo_items[i].varname);
- else
- match = !strcmp(question, getinfo_items[i].varname);
- if (match) {
- tor_assert(getinfo_items[i].fn);
- return getinfo_items[i].fn(control_conn, question, answer, err_out);
- }
- }
-
- return 0; /* unrecognized */
-}
-
-/** Called when we receive a GETINFO command. Try to fetch all requested
- * information, and reply with information or error message. */
-static int
-handle_control_getinfo(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- smartlist_t *questions = smartlist_new();
- smartlist_t *answers = smartlist_new();
- smartlist_t *unrecognized = smartlist_new();
- char *ans = NULL;
- int i;
- (void) len; /* body is NUL-terminated, so it's safe to ignore the length. */
-
- smartlist_split_string(questions, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
- const char *errmsg = NULL;
-
- if (handle_getinfo_helper(conn, q, &ans, &errmsg) < 0) {
- if (!errmsg)
- errmsg = "Internal error";
- connection_printf_to_buf(conn, "551 %s\r\n", errmsg);
- goto done;
- }
- if (!ans) {
- if (errmsg) /* use provided error message */
- smartlist_add_strdup(unrecognized, errmsg);
- else /* use default error message */
- smartlist_add_asprintf(unrecognized, "Unrecognized key \"%s\"", q);
- } else {
- smartlist_add_strdup(answers, q);
- smartlist_add(answers, ans);
- }
- } SMARTLIST_FOREACH_END(q);
-
- if (smartlist_len(unrecognized)) {
- /* control-spec section 2.3, mid-reply '-' or end of reply ' ' */
- for (i=0; i < smartlist_len(unrecognized)-1; ++i)
- connection_printf_to_buf(conn,
- "552-%s\r\n",
- (char *)smartlist_get(unrecognized, i));
-
- connection_printf_to_buf(conn,
- "552 %s\r\n",
- (char *)smartlist_get(unrecognized, i));
- goto done;
- }
-
- for (i = 0; i < smartlist_len(answers); i += 2) {
- char *k = smartlist_get(answers, i);
- char *v = smartlist_get(answers, i+1);
- if (!strchr(v, '\n') && !strchr(v, '\r')) {
- connection_printf_to_buf(conn, "250-%s=", k);
- connection_write_str_to_buf(v, conn);
- connection_write_str_to_buf("\r\n", conn);
- } else {
- char *esc = NULL;
- size_t esc_len;
- esc_len = write_escaped_data(v, strlen(v), &esc);
- connection_printf_to_buf(conn, "250+%s=\r\n", k);
- connection_buf_add(esc, esc_len, TO_CONN(conn));
- tor_free(esc);
- }
- }
- connection_write_str_to_buf("250 OK\r\n", conn);
-
- done:
- SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp));
- smartlist_free(answers);
- SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp));
- smartlist_free(questions);
- SMARTLIST_FOREACH(unrecognized, char *, cp, tor_free(cp));
- smartlist_free(unrecognized);
-
- return 0;
-}
-
-/** Given a string, convert it to a circuit purpose. */
-static uint8_t
-circuit_purpose_from_string(const char *string)
-{
- if (!strcasecmpstart(string, "purpose="))
- string += strlen("purpose=");
-
- if (!strcasecmp(string, "general"))
- return CIRCUIT_PURPOSE_C_GENERAL;
- else if (!strcasecmp(string, "controller"))
- return CIRCUIT_PURPOSE_CONTROLLER;
- else
- return CIRCUIT_PURPOSE_UNKNOWN;
-}
-
-/** Return a newly allocated smartlist containing the arguments to the command
- * waiting in <b>body</b>. If there are fewer than <b>min_args</b> arguments,
- * or if <b>max_args</b> is nonnegative and there are more than
- * <b>max_args</b> arguments, send a 512 error to the controller, using
- * <b>command</b> as the command name in the error message. */
-static smartlist_t *
-getargs_helper(const char *command, control_connection_t *conn,
- const char *body, int min_args, int max_args)
-{
- smartlist_t *args = smartlist_new();
- smartlist_split_string(args, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- if (smartlist_len(args) < min_args) {
- connection_printf_to_buf(conn, "512 Missing argument to %s\r\n",command);
- goto err;
- } else if (max_args >= 0 && smartlist_len(args) > max_args) {
- connection_printf_to_buf(conn, "512 Too many arguments to %s\r\n",command);
- goto err;
- }
- return args;
- err:
- SMARTLIST_FOREACH(args, char *, s, tor_free(s));
- smartlist_free(args);
- return NULL;
-}
-
-/** Helper. Return the first element of <b>sl</b> at index <b>start_at</b> or
- * higher that starts with <b>prefix</b>, case-insensitive. Return NULL if no
- * such element exists. */
-static const char *
-find_element_starting_with(smartlist_t *sl, int start_at, const char *prefix)
-{
- int i;
- for (i = start_at; i < smartlist_len(sl); ++i) {
- const char *elt = smartlist_get(sl, i);
- if (!strcasecmpstart(elt, prefix))
- return elt;
- }
- return NULL;
-}
-
-/** Helper. Return true iff s is an argument that we should treat as a
- * key-value pair. */
-static int
-is_keyval_pair(const char *s)
-{
- /* An argument is a key-value pair if it has an =, and it isn't of the form
- * $fingeprint=name */
- return strchr(s, '=') && s[0] != '$';
-}
-
-/** Called when we get an EXTENDCIRCUIT message. Try to extend the listed
- * circuit, and report success or failure. */
-static int
-handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- smartlist_t *router_nicknames=NULL, *nodes=NULL;
- origin_circuit_t *circ = NULL;
- int zero_circ;
- uint8_t intended_purpose = CIRCUIT_PURPOSE_C_GENERAL;
- smartlist_t *args;
- (void) len;
-
- router_nicknames = smartlist_new();
-
- args = getargs_helper("EXTENDCIRCUIT", conn, body, 1, -1);
- if (!args)
- goto done;
-
- zero_circ = !strcmp("0", (char*)smartlist_get(args,0));
-
- if (zero_circ) {
- const char *purp = find_element_starting_with(args, 1, "PURPOSE=");
-
- if (purp) {
- intended_purpose = circuit_purpose_from_string(purp);
- if (intended_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
- connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp);
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- goto done;
- }
- }
-
- if ((smartlist_len(args) == 1) ||
- (smartlist_len(args) >= 2 && is_keyval_pair(smartlist_get(args, 1)))) {
- // "EXTENDCIRCUIT 0" || EXTENDCIRCUIT 0 foo=bar"
- circ = circuit_launch(intended_purpose, CIRCLAUNCH_NEED_CAPACITY);
- if (!circ) {
- connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
- } else {
- connection_printf_to_buf(conn, "250 EXTENDED %lu\r\n",
- (unsigned long)circ->global_identifier);
- }
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- goto done;
- }
- // "EXTENDCIRCUIT 0 router1,router2" ||
- // "EXTENDCIRCUIT 0 router1,router2 PURPOSE=foo"
- }
-
- if (!zero_circ && !(circ = get_circ(smartlist_get(args,0)))) {
- connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
- (char*)smartlist_get(args, 0));
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- goto done;
- }
-
- if (smartlist_len(args) < 2) {
- connection_printf_to_buf(conn,
- "512 syntax error: not enough arguments.\r\n");
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- goto done;
- }
-
- smartlist_split_string(router_nicknames, smartlist_get(args,1), ",", 0, 0);
-
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
-
- nodes = smartlist_new();
- int first_node = zero_circ;
- SMARTLIST_FOREACH_BEGIN(router_nicknames, const char *, n) {
- const node_t *node = node_get_by_nickname(n, 0);
- if (!node) {
- connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n);
- goto done;
- }
- if (!node_has_preferred_descriptor(node, first_node)) {
- connection_printf_to_buf(conn, "552 No descriptor for \"%s\"\r\n", n);
- goto done;
- }
- smartlist_add(nodes, (void*)node);
- first_node = 0;
- } SMARTLIST_FOREACH_END(n);
- if (!smartlist_len(nodes)) {
- connection_write_str_to_buf("512 No router names provided\r\n", conn);
- goto done;
- }
-
- if (zero_circ) {
- /* start a new circuit */
- circ = origin_circuit_init(intended_purpose, 0);
- }
-
- /* now circ refers to something that is ready to be extended */
- first_node = zero_circ;
- SMARTLIST_FOREACH(nodes, const node_t *, node,
- {
- extend_info_t *info = extend_info_from_node(node, first_node);
- if (!info) {
- tor_assert_nonfatal(first_node);
- log_warn(LD_CONTROL,
- "controller tried to connect to a node that lacks a suitable "
- "descriptor, or which doesn't have any "
- "addresses that are allowed by the firewall configuration; "
- "circuit marked for closing.");
- circuit_mark_for_close(TO_CIRCUIT(circ), -END_CIRC_REASON_CONNECTFAILED);
- connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
- goto done;
- }
- circuit_append_new_exit(circ, info);
- if (circ->build_state->desired_path_len > 1) {
- circ->build_state->onehop_tunnel = 0;
- }
- extend_info_free(info);
- first_node = 0;
- });
-
- /* now that we've populated the cpath, start extending */
- if (zero_circ) {
- int err_reason = 0;
- if ((err_reason = circuit_handle_first_hop(circ)) < 0) {
- circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason);
- connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
- goto done;
- }
- } else {
- if (circ->base_.state == CIRCUIT_STATE_OPEN ||
- circ->base_.state == CIRCUIT_STATE_GUARD_WAIT) {
- int err_reason = 0;
- circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
- if ((err_reason = circuit_send_next_onion_skin(circ)) < 0) {
- log_info(LD_CONTROL,
- "send_next_onion_skin failed; circuit marked for closing.");
- circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason);
- connection_write_str_to_buf("551 Couldn't send onion skin\r\n", conn);
- goto done;
- }
- }
- }
-
- connection_printf_to_buf(conn, "250 EXTENDED %lu\r\n",
- (unsigned long)circ->global_identifier);
- if (zero_circ) /* send a 'launched' event, for completeness */
- circuit_event_status(circ, CIRC_EVENT_LAUNCHED, 0);
- done:
- SMARTLIST_FOREACH(router_nicknames, char *, n, tor_free(n));
- smartlist_free(router_nicknames);
- smartlist_free(nodes);
- return 0;
-}
-
-/** Called when we get a SETCIRCUITPURPOSE message. If we can find the
- * circuit and it's a valid purpose, change it. */
-static int
-handle_control_setcircuitpurpose(control_connection_t *conn,
- uint32_t len, const char *body)
-{
- origin_circuit_t *circ = NULL;
- uint8_t new_purpose;
- smartlist_t *args;
- (void) len; /* body is NUL-terminated, so it's safe to ignore the length. */
-
- args = getargs_helper("SETCIRCUITPURPOSE", conn, body, 2, -1);
- if (!args)
- goto done;
-
- if (!(circ = get_circ(smartlist_get(args,0)))) {
- connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
- (char*)smartlist_get(args, 0));
- goto done;
- }
-
- {
- const char *purp = find_element_starting_with(args,1,"PURPOSE=");
- if (!purp) {
- connection_write_str_to_buf("552 No purpose given\r\n", conn);
- goto done;
- }
- new_purpose = circuit_purpose_from_string(purp);
- if (new_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
- connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp);
- goto done;
- }
- }
-
- circuit_change_purpose(TO_CIRCUIT(circ), new_purpose);
- connection_write_str_to_buf("250 OK\r\n", conn);
-
- done:
- if (args) {
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- }
- return 0;
-}
-
-/** Called when we get an ATTACHSTREAM message. Try to attach the requested
- * stream, and report success or failure. */
-static int
-handle_control_attachstream(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- entry_connection_t *ap_conn = NULL;
- origin_circuit_t *circ = NULL;
- int zero_circ;
- smartlist_t *args;
- crypt_path_t *cpath=NULL;
- int hop=0, hop_line_ok=1;
- (void) len;
-
- args = getargs_helper("ATTACHSTREAM", conn, body, 2, -1);
- if (!args)
- return 0;
-
- zero_circ = !strcmp("0", (char*)smartlist_get(args,1));
-
- if (!(ap_conn = get_stream(smartlist_get(args, 0)))) {
- connection_printf_to_buf(conn, "552 Unknown stream \"%s\"\r\n",
- (char*)smartlist_get(args, 0));
- } else if (!zero_circ && !(circ = get_circ(smartlist_get(args, 1)))) {
- connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
- (char*)smartlist_get(args, 1));
- } else if (circ) {
- const char *hopstring = find_element_starting_with(args,2,"HOP=");
- if (hopstring) {
- hopstring += strlen("HOP=");
- hop = (int) tor_parse_ulong(hopstring, 10, 0, INT_MAX,
- &hop_line_ok, NULL);
- if (!hop_line_ok) { /* broken hop line */
- connection_printf_to_buf(conn, "552 Bad value hop=%s\r\n", hopstring);
- }
- }
- }
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- if (!ap_conn || (!zero_circ && !circ) || !hop_line_ok)
- return 0;
-
- if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT &&
- ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONNECT_WAIT &&
- ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_RESOLVE_WAIT) {
- connection_write_str_to_buf(
- "555 Connection is not managed by controller.\r\n",
- conn);
- return 0;
- }
-
- /* Do we need to detach it first? */
- if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT) {
- edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
- circuit_t *tmpcirc = circuit_get_by_edge_conn(edge_conn);
- connection_edge_end(edge_conn, END_STREAM_REASON_TIMEOUT);
- /* Un-mark it as ending, since we're going to reuse it. */
- edge_conn->edge_has_sent_end = 0;
- edge_conn->end_reason = 0;
- if (tmpcirc)
- circuit_detach_stream(tmpcirc, edge_conn);
- CONNECTION_AP_EXPECT_NONPENDING(ap_conn);
- TO_CONN(edge_conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
- }
-
- if (circ && (circ->base_.state != CIRCUIT_STATE_OPEN)) {
- connection_write_str_to_buf(
- "551 Can't attach stream to non-open origin circuit\r\n",
- conn);
- return 0;
- }
- /* Is this a single hop circuit? */
- if (circ && (circuit_get_cpath_len(circ)<2 || hop==1)) {
- connection_write_str_to_buf(
- "551 Can't attach stream to this one-hop circuit.\r\n", conn);
- return 0;
- }
-
- if (circ && hop>0) {
- /* find this hop in the circuit, and set cpath */
- cpath = circuit_get_cpath_hop(circ, hop);
- if (!cpath) {
- connection_printf_to_buf(conn,
- "551 Circuit doesn't have %d hops.\r\n", hop);
- return 0;
- }
- }
- if (connection_ap_handshake_rewrite_and_attach(ap_conn, circ, cpath) < 0) {
- connection_write_str_to_buf("551 Unable to attach stream\r\n", conn);
- return 0;
- }
- send_control_done(conn);
- return 0;
-}
-
-/** Called when we get a POSTDESCRIPTOR message. Try to learn the provided
- * descriptor, and report success or failure. */
-static int
-handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- char *desc;
- const char *msg=NULL;
- uint8_t purpose = ROUTER_PURPOSE_GENERAL;
- int cache = 0; /* eventually, we may switch this to 1 */
-
- const char *cp = memchr(body, '\n', len);
-
- 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) {
- if (!strcasecmpstart(option, "purpose=")) {
- option += strlen("purpose=");
- purpose = router_purpose_from_string(option);
- if (purpose == ROUTER_PURPOSE_UNKNOWN) {
- connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n",
- option);
- goto done;
- }
- } else if (!strcasecmpstart(option, "cache=")) {
- option += strlen("cache=");
- if (!strcasecmp(option, "no"))
- cache = 0;
- else if (!strcasecmp(option, "yes"))
- cache = 1;
- else {
- connection_printf_to_buf(conn, "552 Unknown cache request \"%s\"\r\n",
- option);
- goto done;
- }
- } else { /* unrecognized argument? */
- connection_printf_to_buf(conn,
- "512 Unexpected argument \"%s\" to postdescriptor\r\n", option);
- goto done;
- }
- } SMARTLIST_FOREACH_END(option);
-
- read_escaped_data(cp, len-(cp-body), &desc);
-
- switch (router_load_single_router(desc, purpose, cache, &msg)) {
- case -1:
- if (!msg) msg = "Could not parse descriptor";
- connection_printf_to_buf(conn, "554 %s\r\n", msg);
- break;
- case 0:
- if (!msg) msg = "Descriptor not added";
- connection_printf_to_buf(conn, "251 %s\r\n",msg);
- break;
- case 1:
- send_control_done(conn);
- break;
- }
-
- tor_free(desc);
- done:
- SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
- smartlist_free(args);
- tor_free(cmdline);
- return 0;
-}
-
-/** Called when we receive a REDIRECTSTERAM command. Try to change the target
- * address of the named AP stream, and report success or failure. */
-static int
-handle_control_redirectstream(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- entry_connection_t *ap_conn = NULL;
- char *new_addr = NULL;
- uint16_t new_port = 0;
- smartlist_t *args;
- (void) len;
-
- args = getargs_helper("REDIRECTSTREAM", conn, body, 2, -1);
- if (!args)
- return 0;
-
- if (!(ap_conn = get_stream(smartlist_get(args, 0)))
- || !ap_conn->socks_request) {
- connection_printf_to_buf(conn, "552 Unknown stream \"%s\"\r\n",
- (char*)smartlist_get(args, 0));
- } else {
- int ok = 1;
- if (smartlist_len(args) > 2) { /* they included a port too */
- new_port = (uint16_t) tor_parse_ulong(smartlist_get(args, 2),
- 10, 1, 65535, &ok, NULL);
- }
- if (!ok) {
- connection_printf_to_buf(conn, "512 Cannot parse port \"%s\"\r\n",
- (char*)smartlist_get(args, 2));
- } else {
- new_addr = tor_strdup(smartlist_get(args, 1));
- }
- }
-
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- if (!new_addr)
- return 0;
-
- strlcpy(ap_conn->socks_request->address, new_addr,
- sizeof(ap_conn->socks_request->address));
- if (new_port)
- ap_conn->socks_request->port = new_port;
- tor_free(new_addr);
- send_control_done(conn);
- return 0;
-}
-
-/** Called when we get a CLOSESTREAM command; try to close the named stream
- * and report success or failure. */
-static int
-handle_control_closestream(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- entry_connection_t *ap_conn=NULL;
- uint8_t reason=0;
- smartlist_t *args;
- int ok;
- (void) len;
-
- args = getargs_helper("CLOSESTREAM", conn, body, 2, -1);
- if (!args)
- return 0;
-
- else if (!(ap_conn = get_stream(smartlist_get(args, 0))))
- connection_printf_to_buf(conn, "552 Unknown stream \"%s\"\r\n",
- (char*)smartlist_get(args, 0));
- else {
- reason = (uint8_t) tor_parse_ulong(smartlist_get(args,1), 10, 0, 255,
- &ok, NULL);
- if (!ok) {
- connection_printf_to_buf(conn, "552 Unrecognized reason \"%s\"\r\n",
- (char*)smartlist_get(args, 1));
- ap_conn = NULL;
- }
- }
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- if (!ap_conn)
- return 0;
-
- connection_mark_unattached_ap(ap_conn, reason);
- send_control_done(conn);
- return 0;
-}
-
-/** Called when we get a CLOSECIRCUIT command; try to close the named circuit
- * and report success or failure. */
-static int
-handle_control_closecircuit(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- origin_circuit_t *circ = NULL;
- int safe = 0;
- smartlist_t *args;
- (void) len;
-
- args = getargs_helper("CLOSECIRCUIT", conn, body, 1, -1);
- if (!args)
- return 0;
-
- if (!(circ=get_circ(smartlist_get(args, 0))))
- connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
- (char*)smartlist_get(args, 0));
- else {
- int i;
- for (i=1; i < smartlist_len(args); ++i) {
- if (!strcasecmp(smartlist_get(args, i), "IfUnused"))
- safe = 1;
- else
- log_info(LD_CONTROL, "Skipping unknown option %s",
- (char*)smartlist_get(args,i));
- }
- }
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- if (!circ)
- return 0;
-
- if (!safe || !circ->p_streams) {
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_REQUESTED);
- }
-
- send_control_done(conn);
- return 0;
-}
-
-/** Called when we get a RESOLVE command: start trying to resolve
- * the listed addresses. */
-static int
-handle_control_resolve(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- smartlist_t *args, *failed;
- int is_reverse = 0;
- (void) len; /* body is nul-terminated; it's safe to ignore the length */
-
- if (!(conn->event_mask & (((event_mask_t)1)<<EVENT_ADDRMAP))) {
- log_warn(LD_CONTROL, "Controller asked us to resolve an address, but "
- "isn't listening for ADDRMAP events. It probably won't see "
- "the answer.");
- }
- args = smartlist_new();
- smartlist_split_string(args, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- {
- const char *modearg = find_element_starting_with(args, 0, "mode=");
- if (modearg && !strcasecmp(modearg, "mode=reverse"))
- is_reverse = 1;
- }
- failed = smartlist_new();
- SMARTLIST_FOREACH(args, const char *, arg, {
- if (!is_keyval_pair(arg)) {
- if (dnsserv_launch_request(arg, is_reverse, conn)<0)
- smartlist_add(failed, (char*)arg);
- }
- });
-
- send_control_done(conn);
- SMARTLIST_FOREACH(failed, const char *, arg, {
- control_event_address_mapped(arg, arg, time(NULL),
- "internal", 0);
- });
-
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- smartlist_free(failed);
- return 0;
-}
-
-/** Called when we get a PROTOCOLINFO command: send back a reply. */
-static int
-handle_control_protocolinfo(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- const char *bad_arg = NULL;
- smartlist_t *args;
- (void)len;
-
- conn->have_sent_protocolinfo = 1;
- args = smartlist_new();
- smartlist_split_string(args, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- SMARTLIST_FOREACH(args, const char *, arg, {
- int ok;
- tor_parse_long(arg, 10, 0, LONG_MAX, &ok, NULL);
- if (!ok) {
- bad_arg = arg;
- break;
- }
- });
- if (bad_arg) {
- connection_printf_to_buf(conn, "513 No such version %s\r\n",
- escaped(bad_arg));
- /* Don't tolerate bad arguments when not authenticated. */
- if (!STATE_IS_OPEN(TO_CONN(conn)->state))
- connection_mark_for_close(TO_CONN(conn));
- goto done;
- } else {
- const or_options_t *options = get_options();
- int cookies = options->CookieAuthentication;
- char *cfile = get_controller_cookie_file_name();
- char *abs_cfile;
- char *esc_cfile;
- char *methods;
- abs_cfile = make_path_absolute(cfile);
- esc_cfile = esc_for_log(abs_cfile);
- {
- int passwd = (options->HashedControlPassword != NULL ||
- options->HashedControlSessionPassword != NULL);
- smartlist_t *mlist = smartlist_new();
- if (cookies) {
- smartlist_add(mlist, (char*)"COOKIE");
- smartlist_add(mlist, (char*)"SAFECOOKIE");
- }
- if (passwd)
- smartlist_add(mlist, (char*)"HASHEDPASSWORD");
- if (!cookies && !passwd)
- smartlist_add(mlist, (char*)"NULL");
- methods = smartlist_join_strings(mlist, ",", 0, NULL);
- smartlist_free(mlist);
- }
-
- connection_printf_to_buf(conn,
- "250-PROTOCOLINFO 1\r\n"
- "250-AUTH METHODS=%s%s%s\r\n"
- "250-VERSION Tor=%s\r\n"
- "250 OK\r\n",
- methods,
- cookies?" COOKIEFILE=":"",
- cookies?esc_cfile:"",
- escaped(VERSION));
- tor_free(methods);
- tor_free(cfile);
- tor_free(abs_cfile);
- tor_free(esc_cfile);
- }
- done:
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- return 0;
-}
-
-/** Called when we get an AUTHCHALLENGE command. */
-static int
-handle_control_authchallenge(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- const char *cp = body;
- char *client_nonce;
- size_t client_nonce_len;
- char server_hash[DIGEST256_LEN];
- char server_hash_encoded[HEX_DIGEST256_LEN+1];
- char server_nonce[SAFECOOKIE_SERVER_NONCE_LEN];
- char server_nonce_encoded[(2*SAFECOOKIE_SERVER_NONCE_LEN) + 1];
-
- cp += strspn(cp, " \t\n\r");
- if (!strcasecmpstart(cp, "SAFECOOKIE")) {
- cp += strlen("SAFECOOKIE");
- } else {
- connection_write_str_to_buf("513 AUTHCHALLENGE only supports SAFECOOKIE "
- "authentication\r\n", conn);
- connection_mark_for_close(TO_CONN(conn));
- return -1;
- }
-
- if (!authentication_cookie_is_set) {
- connection_write_str_to_buf("515 Cookie authentication is disabled\r\n",
- conn);
- connection_mark_for_close(TO_CONN(conn));
- return -1;
- }
-
- cp += strspn(cp, " \t\n\r");
- if (*cp == '"') {
- const char *newcp =
- decode_escaped_string(cp, len - (cp - body),
- &client_nonce, &client_nonce_len);
- if (newcp == NULL) {
- connection_write_str_to_buf("513 Invalid quoted client nonce\r\n",
- conn);
- connection_mark_for_close(TO_CONN(conn));
- return -1;
- }
- cp = newcp;
- } else {
- size_t client_nonce_encoded_len = strspn(cp, "0123456789ABCDEFabcdef");
-
- client_nonce_len = client_nonce_encoded_len / 2;
- client_nonce = tor_malloc_zero(client_nonce_len);
-
- if (base16_decode(client_nonce, client_nonce_len,
- 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));
- tor_free(client_nonce);
- return -1;
- }
-
- cp += client_nonce_encoded_len;
- }
-
- cp += strspn(cp, " \t\n\r");
- if (*cp != '\0' ||
- cp != body + len) {
- connection_write_str_to_buf("513 Junk at end of AUTHCHALLENGE command\r\n",
- conn);
- connection_mark_for_close(TO_CONN(conn));
- tor_free(client_nonce);
- return -1;
- }
- crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
-
- /* Now compute and send the server-to-controller response, and the
- * server's nonce. */
- tor_assert(authentication_cookie != NULL);
-
- {
- size_t tmp_len = (AUTHENTICATION_COOKIE_LEN +
- client_nonce_len +
- SAFECOOKIE_SERVER_NONCE_LEN);
- char *tmp = tor_malloc_zero(tmp_len);
- char *client_hash = tor_malloc_zero(DIGEST256_LEN);
- memcpy(tmp, authentication_cookie, AUTHENTICATION_COOKIE_LEN);
- memcpy(tmp + AUTHENTICATION_COOKIE_LEN, client_nonce, client_nonce_len);
- memcpy(tmp + AUTHENTICATION_COOKIE_LEN + client_nonce_len,
- server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
-
- crypto_hmac_sha256(server_hash,
- SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT,
- strlen(SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT),
- tmp,
- tmp_len);
-
- crypto_hmac_sha256(client_hash,
- SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT,
- strlen(SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT),
- tmp,
- tmp_len);
-
- conn->safecookie_client_hash = client_hash;
-
- tor_free(tmp);
- }
-
- base16_encode(server_hash_encoded, sizeof(server_hash_encoded),
- server_hash, sizeof(server_hash));
- base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded),
- server_nonce, sizeof(server_nonce));
-
- connection_printf_to_buf(conn,
- "250 AUTHCHALLENGE SERVERHASH=%s "
- "SERVERNONCE=%s\r\n",
- server_hash_encoded,
- server_nonce_encoded);
-
- tor_free(client_nonce);
- return 0;
-}
-
-/** Called when we get a USEFEATURE command: parse the feature list, and
- * set up the control_connection's options properly. */
-static int
-handle_control_usefeature(control_connection_t *conn,
- uint32_t len,
- const char *body)
-{
- smartlist_t *args;
- int bad = 0;
- (void) len; /* body is nul-terminated; it's safe to ignore the length */
- args = smartlist_new();
- smartlist_split_string(args, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
- if (!strcasecmp(arg, "VERBOSE_NAMES"))
- ;
- else if (!strcasecmp(arg, "EXTENDED_EVENTS"))
- ;
- else {
- connection_printf_to_buf(conn, "552 Unrecognized feature \"%s\"\r\n",
- arg);
- bad = 1;
- break;
- }
- } SMARTLIST_FOREACH_END(arg);
-
- if (!bad) {
- send_control_done(conn);
- }
-
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- return 0;
-}
-
-/** Implementation for the DROPGUARDS command. */
-static int
-handle_control_dropguards(control_connection_t *conn,
- uint32_t len,
- const char *body)
-{
- smartlist_t *args;
- (void) len; /* body is nul-terminated; it's safe to ignore the length */
- args = smartlist_new();
- smartlist_split_string(args, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
-
- static int have_warned = 0;
- if (! have_warned) {
- log_warn(LD_CONTROL, "DROPGUARDS is dangerous; make sure you understand "
- "the risks before using it. It may be removed in a future "
- "version of Tor.");
- have_warned = 1;
- }
-
- if (smartlist_len(args)) {
- connection_printf_to_buf(conn, "512 Too many arguments to DROPGUARDS\r\n");
- } else {
- remove_all_entry_guards();
- send_control_done(conn);
- }
-
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- return 0;
-}
-
-/** Implementation for the HSFETCH command. */
-static int
-handle_control_hsfetch(control_connection_t *conn, uint32_t len,
- const char *body)
-{
- int i;
- char digest[DIGEST_LEN], *hsaddress = NULL, *arg1 = NULL, *desc_id = NULL;
- smartlist_t *args = NULL, *hsdirs = NULL;
- (void) len; /* body is nul-terminated; it's safe to ignore the length */
- static const char *hsfetch_command = "HSFETCH";
- static const char *v2_str = "v2-";
- const size_t v2_str_len = strlen(v2_str);
- rend_data_t *rend_query = NULL;
-
- /* Make sure we have at least one argument, the HSAddress. */
- args = getargs_helper(hsfetch_command, conn, body, 1, -1);
- if (!args) {
- goto exit;
- }
-
- /* Extract the first argument (either HSAddress or DescID). */
- arg1 = smartlist_get(args, 0);
- /* Test if it's an HS address without the .onion part. */
- if (rend_valid_v2_service_id(arg1)) {
- hsaddress = arg1;
- } else if (strcmpstart(arg1, v2_str) == 0 &&
- rend_valid_descriptor_id(arg1 + v2_str_len) &&
- base32_decode(digest, sizeof(digest), arg1 + v2_str_len,
- REND_DESC_ID_V2_LEN_BASE32) == 0) {
- /* We have a well formed version 2 descriptor ID. Keep the decoded value
- * of the id. */
- desc_id = digest;
- } else {
- connection_printf_to_buf(conn, "513 Invalid argument \"%s\"\r\n",
- arg1);
- goto done;
- }
-
- static const char *opt_server = "SERVER=";
-
- /* Skip first argument because it's the HSAddress or DescID. */
- for (i = 1; i < smartlist_len(args); ++i) {
- const char *arg = smartlist_get(args, i);
- const node_t *node;
-
- if (!strcasecmpstart(arg, opt_server)) {
- const char *server;
-
- server = arg + strlen(opt_server);
- node = node_get_by_hex_id(server, 0);
- if (!node) {
- connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
- server);
- goto done;
- }
- if (!hsdirs) {
- /* Stores routerstatus_t object for each specified server. */
- hsdirs = smartlist_new();
- }
- /* Valid server, add it to our local list. */
- smartlist_add(hsdirs, node->rs);
- } else {
- connection_printf_to_buf(conn, "513 Unexpected argument \"%s\"\r\n",
- arg);
- goto done;
- }
- }
-
- rend_query = rend_data_client_create(hsaddress, desc_id, NULL,
- REND_NO_AUTH);
- if (rend_query == NULL) {
- connection_printf_to_buf(conn, "551 Error creating the HS query\r\n");
- goto done;
- }
-
- /* Using a descriptor ID, we force the user to provide at least one
- * hsdir server using the SERVER= option. */
- if (desc_id && (!hsdirs || !smartlist_len(hsdirs))) {
- connection_printf_to_buf(conn, "512 %s option is required\r\n",
- opt_server);
- goto done;
- }
-
- /* We are about to trigger HSDir fetch so send the OK now because after
- * that 650 event(s) are possible so better to have the 250 OK before them
- * to avoid out of order replies. */
- send_control_done(conn);
-
- /* Trigger the fetch using the built rend query and possibly a list of HS
- * directory to use. This function ignores the client cache thus this will
- * always send a fetch command. */
- rend_client_fetch_v2_desc(rend_query, hsdirs);
-
- done:
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- /* Contains data pointer that we don't own thus no cleanup. */
- smartlist_free(hsdirs);
- rend_data_free(rend_query);
- exit:
- return 0;
-}
-
-/** Implementation for the HSPOST command. */
-static int
-handle_control_hspost(control_connection_t *conn,
- uint32_t len,
- const char *body)
-{
- static const char *opt_server = "SERVER=";
- static const char *opt_hsaddress = "HSADDRESS=";
- smartlist_t *hs_dirs = NULL;
- const char *encoded_desc = body;
- size_t encoded_desc_len = len;
- const char *onion_address = NULL;
-
- 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= or HSADDRESS= options were specified, try to parse
- * the options line. */
- if (!strcasecmpstart(argline, opt_server) ||
- !strcasecmpstart(argline, opt_hsaddress)) {
- /* encoded_desc begins after a newline character */
- cp = cp + 1;
- encoded_desc = cp;
- encoded_desc_len = len-(cp-body);
-
- smartlist_split_string(args, argline, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
- if (!strcasecmpstart(arg, opt_server)) {
- const char *server = arg + strlen(opt_server);
- const node_t *node = node_get_by_hex_id(server, 0);
-
- if (!node || !node->rs) {
- connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
- server);
- goto done;
- }
- /* Valid server, add it to our local list. */
- if (!hs_dirs)
- hs_dirs = smartlist_new();
- smartlist_add(hs_dirs, node->rs);
- } else if (!strcasecmpstart(arg, opt_hsaddress)) {
- const char *address = arg + strlen(opt_hsaddress);
- if (!hs_address_is_valid(address)) {
- connection_printf_to_buf(conn, "512 Malformed onion address\r\n");
- goto done;
- }
- onion_address = address;
- } else {
- connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n",
- arg);
- goto done;
- }
- } SMARTLIST_FOREACH_END(arg);
- }
-
- /* Handle the v3 case. */
- if (onion_address) {
- char *desc_str = NULL;
- read_escaped_data(encoded_desc, encoded_desc_len, &desc_str);
- if (hs_control_hspost_command(desc_str, onion_address, hs_dirs) < 0) {
- connection_printf_to_buf(conn, "554 Invalid descriptor\r\n");
- } else {
- send_control_done(conn);
- }
- tor_free(desc_str);
- goto done;
- }
-
- /* From this point on, it is only v2. */
-
- /* Read the dot encoded descriptor, and parse it. */
- rend_encoded_v2_service_descriptor_t *desc =
- tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
- read_escaped_data(encoded_desc, encoded_desc_len, &desc->desc_str);
-
- rend_service_descriptor_t *parsed = NULL;
- char *intro_content = NULL;
- size_t intro_size;
- size_t encoded_size;
- const char *next_desc;
- if (!rend_parse_v2_service_descriptor(&parsed, desc->desc_id, &intro_content,
- &intro_size, &encoded_size,
- &next_desc, desc->desc_str, 1)) {
- /* Post the descriptor. */
- char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
- if (!rend_get_service_id(parsed->pk, serviceid)) {
- smartlist_t *descs = smartlist_new();
- smartlist_add(descs, desc);
-
- /* We are about to trigger HS descriptor upload so send the OK now
- * because after that 650 event(s) are possible so better to have the
- * 250 OK before them to avoid out of order replies. */
- send_control_done(conn);
-
- /* Trigger the descriptor upload */
- directory_post_to_hs_dir(parsed, descs, hs_dirs, serviceid, 0);
- smartlist_free(descs);
- }
-
- rend_service_descriptor_free(parsed);
- } else {
- connection_printf_to_buf(conn, "554 Invalid descriptor\r\n");
- }
-
- tor_free(intro_content);
- rend_encoded_v2_service_descriptor_free(desc);
- done:
- tor_free(argline);
- smartlist_free(hs_dirs); /* Contents belong to the rend service code. */
- SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
- smartlist_free(args);
- return 0;
-}
-
-/* Helper function for ADD_ONION that adds an ephemeral service depending on
- * the given hs_version.
- *
- * The secret key in pk depends on the hs_version. The ownership of the key
- * used in pk is given to the HS subsystem so the caller must stop accessing
- * it after.
- *
- * The port_cfgs is a list of service port. Ownership transferred to service.
- * The max_streams refers to the MaxStreams= key.
- * The max_streams_close_circuit refers to the MaxStreamsCloseCircuit key.
- * The auth_type is the authentication type of the clients in auth_clients.
- * The ownership of that list is transferred to the service.
- *
- * On success (RSAE_OKAY), the address_out points to a newly allocated string
- * containing the onion address without the .onion part. On error, address_out
- * is untouched. */
-static hs_service_add_ephemeral_status_t
-add_onion_helper_add_service(int hs_version,
- add_onion_secret_key_t *pk,
- smartlist_t *port_cfgs, int max_streams,
- int max_streams_close_circuit, int auth_type,
- smartlist_t *auth_clients, char **address_out)
-{
- hs_service_add_ephemeral_status_t ret;
-
- tor_assert(pk);
- tor_assert(port_cfgs);
- tor_assert(address_out);
-
- switch (hs_version) {
- case HS_VERSION_TWO:
- ret = rend_service_add_ephemeral(pk->v2, port_cfgs, max_streams,
- max_streams_close_circuit, auth_type,
- auth_clients, address_out);
- break;
- case HS_VERSION_THREE:
- ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams,
- max_streams_close_circuit, address_out);
- break;
- default:
- tor_assert_unreached();
- }
-
- return ret;
-}
-
-/** Called when we get a ADD_ONION command; parse the body, and set up
- * the new ephemeral Onion Service. */
-static int
-handle_control_add_onion(control_connection_t *conn,
- uint32_t len,
- const char *body)
-{
- smartlist_t *args;
- int arg_len;
- (void) len; /* body is nul-terminated; it's safe to ignore the length */
- args = getargs_helper("ADD_ONION", conn, body, 2, -1);
- if (!args)
- return 0;
- arg_len = smartlist_len(args);
-
- /* Parse all of the arguments that do not involve handling cryptographic
- * material first, since there's no reason to touch that at all if any of
- * 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 (int 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, (int)i);
- if (!strcasecmpstart(arg, port_prefix)) {
- /* "Port=VIRTPORT[,TARGET]". */
- const char *port_str = arg + strlen(port_prefix);
-
- rend_service_port_config_t *cfg =
- rend_service_parse_port_config(port_str, ",", NULL);
- if (!cfg) {
- connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n");
- goto out;
- }
- smartlist_add(port_cfgs, cfg);
- } else if (!strcasecmpstart(arg, max_s_prefix)) {
- /* "MaxStreams=[0..65535]". */
- const char *max_s_str = arg + strlen(max_s_prefix);
- int ok = 0;
- max_streams = (int)tor_parse_long(max_s_str, 10, 0, 65535, &ok, NULL);
- if (!ok) {
- connection_printf_to_buf(conn, "512 Invalid MaxStreams\r\n");
- goto out;
- }
- } else if (!strcasecmpstart(arg, flags_prefix)) {
- /* "Flags=Flag[,Flag]", where Flag can be:
- * * 'DiscardPK' - If tor generates the keypair, do not include it in
- * the response.
- * * 'Detach' - Do not tie this onion service to any particular control
- * 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;
-
- smartlist_split_string(flags, arg + strlen(flags_prefix), ",",
- SPLIT_IGNORE_BLANK, 0);
- if (smartlist_len(flags) < 1) {
- connection_printf_to_buf(conn, "512 Invalid 'Flags' argument\r\n");
- bad = 1;
- }
- SMARTLIST_FOREACH_BEGIN(flags, const char *, flag)
- {
- if (!strcasecmp(flag, discard_flag)) {
- discard_pk = 1;
- } else if (!strcasecmp(flag, detach_flag)) {
- 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",
- escaped(flag));
- bad = 1;
- break;
- }
- } SMARTLIST_FOREACH_END(flag);
- SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp));
- 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;
- }
- }
- 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. */
- int hs_version = 0;
- add_onion_secret_key_t pk = { NULL };
- const char *key_new_alg = NULL;
- char *key_new_blob = NULL;
- char *err_msg = NULL;
-
- if (add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk,
- &key_new_alg, &key_new_blob, &pk, &hs_version,
- &err_msg) < 0) {
- if (err_msg) {
- connection_write_str_to_buf(err_msg, conn);
- tor_free(err_msg);
- }
- goto out;
- }
- tor_assert(!err_msg);
-
- /* Hidden service version 3 don't have client authentication support so if
- * ClientAuth was given, send back an error. */
- if (hs_version == HS_VERSION_THREE && auth_clients) {
- connection_printf_to_buf(conn, "513 ClientAuth not supported\r\n");
- goto out;
- }
-
- /* 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 = add_onion_helper_add_service(hs_version, &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:
- {
- if (detach) {
- if (!detached_onion_services)
- detached_onion_services = smartlist_new();
- smartlist_add(detached_onion_services, service_id);
- } else {
- if (!conn->ephemeral_onion_services)
- conn->ephemeral_onion_services = smartlist_new();
- smartlist_add(conn->ephemeral_onion_services, service_id);
- }
-
- 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:
- connection_printf_to_buf(conn, "551 Failed to generate onion address\r\n");
- break;
- case RSAE_ADDREXISTS:
- connection_printf_to_buf(conn, "550 Onion address collision\r\n");
- break;
- 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");
- }
- if (key_new_blob) {
- memwipe(key_new_blob, 0, strlen(key_new_blob));
- tor_free(key_new_blob);
- }
-
- out:
- if (port_cfgs) {
- SMARTLIST_FOREACH(port_cfgs, rend_service_port_config_t*, p,
- rend_service_port_config_free(p));
- 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);
- });
- smartlist_free(args);
- return 0;
-}
-
-/** Helper function to handle parsing the KeyType:KeyBlob argument to the
- * ADD_ONION command. Return a new crypto_pk_t and if a new key was generated
- * and the private key not discarded, the algorithm and serialized private key,
- * or NULL and an optional control protocol error message on failure. The
- * caller is responsible for freeing the returned key_new_blob and err_msg.
- *
- * Note: The error messages returned are deliberately vague to avoid echoing
- * key material.
- */
-STATIC int
-add_onion_helper_keyarg(const char *arg, int discard_pk,
- const char **key_new_alg_out, char **key_new_blob_out,
- add_onion_secret_key_t *decoded_key, int *hs_version,
- char **err_msg_out)
-{
- smartlist_t *key_args = smartlist_new();
- crypto_pk_t *pk = NULL;
- const char *key_new_alg = NULL;
- char *key_new_blob = NULL;
- char *err_msg = NULL;
- int ret = -1;
-
- smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0);
- if (smartlist_len(key_args) != 2) {
- err_msg = tor_strdup("512 Invalid key type/blob\r\n");
- goto err;
- }
-
- /* The format is "KeyType:KeyBlob". */
- static const char *key_type_new = "NEW";
- static const char *key_type_best = "BEST";
- static const char *key_type_rsa1024 = "RSA1024";
- static const char *key_type_ed25519_v3 = "ED25519-V3";
-
- const char *key_type = smartlist_get(key_args, 0);
- const char *key_blob = smartlist_get(key_args, 1);
-
- if (!strcasecmp(key_type_rsa1024, key_type)) {
- /* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */
- pk = crypto_pk_base64_decode_private(key_blob, strlen(key_blob));
- if (!pk) {
- err_msg = tor_strdup("512 Failed to decode RSA key\r\n");
- goto err;
- }
- if (crypto_pk_num_bits(pk) != PK_BYTES*8) {
- crypto_pk_free(pk);
- err_msg = tor_strdup("512 Invalid RSA key size\r\n");
- goto err;
- }
- decoded_key->v2 = pk;
- *hs_version = HS_VERSION_TWO;
- } else if (!strcasecmp(key_type_ed25519_v3, key_type)) {
- /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */
- ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
- if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob,
- strlen(key_blob)) != sizeof(sk->seckey)) {
- tor_free(sk);
- err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n");
- goto err;
- }
- decoded_key->v3 = sk;
- *hs_version = HS_VERSION_THREE;
- } else if (!strcasecmp(key_type_new, key_type)) {
- /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */
- if (!strcasecmp(key_type_rsa1024, key_blob) ||
- !strcasecmp(key_type_best, key_blob)) {
- /* "RSA1024", RSA 1024 bit, also currently "BEST" by default. */
- pk = crypto_pk_new();
- if (crypto_pk_generate_key(pk)) {
- tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n",
- key_type_rsa1024);
- goto err;
- }
- if (!discard_pk) {
- if (crypto_pk_base64_encode_private(pk, &key_new_blob)) {
- crypto_pk_free(pk);
- tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
- key_type_rsa1024);
- goto err;
- }
- key_new_alg = key_type_rsa1024;
- }
- decoded_key->v2 = pk;
- *hs_version = HS_VERSION_TWO;
- } else if (!strcasecmp(key_type_ed25519_v3, key_blob)) {
- ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
- if (ed25519_secret_key_generate(sk, 1) < 0) {
- tor_free(sk);
- tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n",
- key_type_ed25519_v3);
- goto err;
- }
- if (!discard_pk) {
- ssize_t len = base64_encode_size(sizeof(sk->seckey), 0) + 1;
- key_new_blob = tor_malloc_zero(len);
- if (base64_encode(key_new_blob, len, (const char *) sk->seckey,
- sizeof(sk->seckey), 0) != (len - 1)) {
- tor_free(sk);
- tor_free(key_new_blob);
- tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
- key_type_ed25519_v3);
- goto err;
- }
- key_new_alg = key_type_ed25519_v3;
- }
- decoded_key->v3 = sk;
- *hs_version = HS_VERSION_THREE;
- } else {
- err_msg = tor_strdup("513 Invalid key type\r\n");
- goto err;
- }
- } else {
- err_msg = tor_strdup("513 Invalid key type\r\n");
- goto err;
- }
-
- /* Succeeded in loading or generating a private key. */
- ret = 0;
-
- err:
- SMARTLIST_FOREACH(key_args, char *, cp, {
- memwipe(cp, 0, strlen(cp));
- tor_free(cp);
- });
- smartlist_free(key_args);
-
- if (err_msg_out) {
- *err_msg_out = err_msg;
- } else {
- tor_free(err_msg);
- }
- *key_new_alg_out = key_new_alg;
- *key_new_blob_out = key_new_blob;
-
- return ret;
-}
-
-/** 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
-handle_control_del_onion(control_connection_t *conn,
- uint32_t len,
- const char *body)
-{
- int hs_version = 0;
- smartlist_t *args;
- (void) len; /* body is nul-terminated; it's safe to ignore the length */
- args = getargs_helper("DEL_ONION", conn, body, 1, 1);
- if (!args)
- return 0;
-
- const char *service_id = smartlist_get(args, 0);
- if (rend_valid_v2_service_id(service_id)) {
- hs_version = HS_VERSION_TWO;
- } else if (hs_address_is_valid(service_id)) {
- hs_version = HS_VERSION_THREE;
- } else {
- connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n");
- goto out;
- }
-
- /* Determine if the onion service belongs to this particular control
- * connection, or if it is in the global list of detached services. If it
- * is in neither, either the service ID is invalid in some way, or it
- * explicitly belongs to a different control connection, and an error
- * should be returned.
- */
- smartlist_t *services[2] = {
- conn->ephemeral_onion_services,
- detached_onion_services
- };
- smartlist_t *onion_services = NULL;
- int idx = -1;
- for (size_t i = 0; i < ARRAY_LENGTH(services); i++) {
- idx = smartlist_string_pos(services[i], service_id);
- if (idx != -1) {
- onion_services = services[i];
- break;
- }
- }
- if (onion_services == NULL) {
- connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n");
- } else {
- int ret = -1;
- switch (hs_version) {
- case HS_VERSION_TWO:
- ret = rend_service_del_ephemeral(service_id);
- break;
- case HS_VERSION_THREE:
- ret = hs_service_del_ephemeral(service_id);
- break;
- default:
- /* The ret value will be -1 thus hitting the warning below. This should
- * never happen because of the check at the start of the function. */
- break;
- }
- if (ret < 0) {
- /* This should *NEVER* fail, since the service is on either the
- * per-control connection list, or the global one.
- */
- log_warn(LD_BUG, "Failed to remove Onion Service %s.",
- escaped(service_id));
- tor_fragile_assert();
- }
-
- /* Remove/scrub the service_id from the appropriate list. */
- char *cp = smartlist_get(onion_services, idx);
- smartlist_del(onion_services, idx);
- memwipe(cp, 0, strlen(cp));
- tor_free(cp);
-
- send_control_done(conn);
- }
-
- out:
- SMARTLIST_FOREACH(args, char *, cp, {
- memwipe(cp, 0, strlen(cp));
- tor_free(cp);
- });
- smartlist_free(args);
- return 0;
-}
-
/** Called when <b>conn</b> has no more bytes left on its outbuf. */
int
connection_control_finished_flushing(control_connection_t *conn)
@@ -5362,6 +275,44 @@ peek_connection_has_http_command(connection_t *conn)
return peek_buf_has_http_command(conn->inbuf);
}
+/**
+ * Helper: take a nul-terminated command of given length, and find where the
+ * command starts and the arguments begin. Separate them, allocate a new
+ * string in <b>current_cmd_out</b> for the command, and return a pointer
+ * to the arguments.
+ **/
+STATIC char *
+control_split_incoming_command(char *incoming_cmd,
+ size_t *data_len,
+ char **current_cmd_out)
+{
+ const bool is_multiline = *data_len && incoming_cmd[0] == '+';
+ size_t cmd_len = 0;
+ while (cmd_len < *data_len
+ && !TOR_ISSPACE(incoming_cmd[cmd_len]))
+ ++cmd_len;
+
+ *current_cmd_out = tor_memdup_nulterm(incoming_cmd, cmd_len);
+ char *args = incoming_cmd+cmd_len;
+ tor_assert(*data_len>=cmd_len);
+ *data_len -= cmd_len;
+ if (is_multiline) {
+ // Only match horizontal space: any line after the first is data,
+ // not arguments.
+ while ((*args == '\t' || *args == ' ') && *data_len) {
+ ++args;
+ --*data_len;
+ }
+ } else {
+ while (TOR_ISSPACE(*args) && *data_len) {
+ ++args;
+ --*data_len;
+ }
+ }
+
+ return args;
+}
+
static const char CONTROLPORT_IS_NOT_AN_HTTP_PROXY_MSG[] =
"HTTP/1.0 501 Tor ControlPort is not an HTTP proxy"
"\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n"
@@ -5396,7 +347,6 @@ connection_control_process_inbuf(control_connection_t *conn)
{
size_t data_len;
uint32_t cmd_data_len;
- int cmd_len;
char *args;
tor_assert(conn);
@@ -5451,7 +401,7 @@ connection_control_process_inbuf(control_connection_t *conn)
return 0;
else if (r == -1) {
if (data_len + conn->incoming_cmd_cur_len > MAX_COMMAND_LINE_LENGTH) {
- connection_write_str_to_buf("500 Line too long.\r\n", conn);
+ control_write_endreply(conn, 500, "Line too long.");
connection_stop_reading(TO_CONN(conn));
connection_mark_and_flush(TO_CONN(conn));
}
@@ -5488,22 +438,15 @@ connection_control_process_inbuf(control_connection_t *conn)
/* Otherwise, read another line. */
}
data_len = conn->incoming_cmd_cur_len;
+
/* Okay, we now have a command sitting on conn->incoming_cmd. See if we
* recognize it.
*/
- cmd_len = 0;
- while ((size_t)cmd_len < data_len
- && !TOR_ISSPACE(conn->incoming_cmd[cmd_len]))
- ++cmd_len;
-
- conn->incoming_cmd[cmd_len]='\0';
- args = conn->incoming_cmd+cmd_len+1;
- tor_assert(data_len>(size_t)cmd_len);
- data_len -= (cmd_len+1); /* skip the command and NUL we added after it */
- while (TOR_ISSPACE(*args)) {
- ++args;
- --data_len;
- }
+ tor_free(conn->current_cmd);
+ args = control_split_incoming_command(conn->incoming_cmd, &data_len,
+ &conn->current_cmd);
+ if (BUG(!conn->current_cmd))
+ return -1;
/* If the connection is already closing, ignore further commands */
if (TO_CONN(conn)->marked_for_close) {
@@ -5511,1452 +454,50 @@ connection_control_process_inbuf(control_connection_t *conn)
}
/* Otherwise, Quit is always valid. */
- if (!strcasecmp(conn->incoming_cmd, "QUIT")) {
- connection_write_str_to_buf("250 closing connection\r\n", conn);
+ if (!strcasecmp(conn->current_cmd, "QUIT")) {
+ control_write_endreply(conn, 250, "closing connection");
connection_mark_and_flush(TO_CONN(conn));
return 0;
}
if (conn->base_.state == CONTROL_CONN_STATE_NEEDAUTH &&
- !is_valid_initial_command(conn, conn->incoming_cmd)) {
- connection_write_str_to_buf("514 Authentication required.\r\n", conn);
+ !is_valid_initial_command(conn, conn->current_cmd)) {
+ control_write_endreply(conn, 514, "Authentication required.");
connection_mark_for_close(TO_CONN(conn));
return 0;
}
if (data_len >= UINT32_MAX) {
- connection_write_str_to_buf("500 A 4GB command? Nice try.\r\n", conn);
+ control_write_endreply(conn, 500, "A 4GB command? Nice try.");
connection_mark_for_close(TO_CONN(conn));
return 0;
}
- /* XXXX Why is this not implemented as a table like the GETINFO
- * items are? Even handling the plus signs at the beginnings of
- * commands wouldn't be very hard with proper macros. */
cmd_data_len = (uint32_t)data_len;
- if (!strcasecmp(conn->incoming_cmd, "SETCONF")) {
- if (handle_control_setconf(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "RESETCONF")) {
- if (handle_control_resetconf(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "GETCONF")) {
- if (handle_control_getconf(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "+LOADCONF")) {
- if (handle_control_loadconf(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "SETEVENTS")) {
- if (handle_control_setevents(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "AUTHENTICATE")) {
- if (handle_control_authenticate(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "SAVECONF")) {
- if (handle_control_saveconf(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "SIGNAL")) {
- if (handle_control_signal(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "TAKEOWNERSHIP")) {
- if (handle_control_takeownership(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "DROPOWNERSHIP")) {
- if (handle_control_dropownership(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "MAPADDRESS")) {
- if (handle_control_mapaddress(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "GETINFO")) {
- if (handle_control_getinfo(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "EXTENDCIRCUIT")) {
- if (handle_control_extendcircuit(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "SETCIRCUITPURPOSE")) {
- if (handle_control_setcircuitpurpose(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "SETROUTERPURPOSE")) {
- connection_write_str_to_buf("511 SETROUTERPURPOSE is obsolete.\r\n", conn);
- } else if (!strcasecmp(conn->incoming_cmd, "ATTACHSTREAM")) {
- if (handle_control_attachstream(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "+POSTDESCRIPTOR")) {
- if (handle_control_postdescriptor(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "REDIRECTSTREAM")) {
- if (handle_control_redirectstream(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "CLOSESTREAM")) {
- if (handle_control_closestream(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "CLOSECIRCUIT")) {
- if (handle_control_closecircuit(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "USEFEATURE")) {
- if (handle_control_usefeature(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "RESOLVE")) {
- if (handle_control_resolve(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "PROTOCOLINFO")) {
- if (handle_control_protocolinfo(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "AUTHCHALLENGE")) {
- if (handle_control_authchallenge(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "DROPGUARDS")) {
- if (handle_control_dropguards(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) {
- if (handle_control_hsfetch(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "+HSPOST")) {
- if (handle_control_hspost(conn, cmd_data_len, args))
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "ADD_ONION")) {
- int ret = handle_control_add_onion(conn, cmd_data_len, args);
- memwipe(args, 0, cmd_data_len); /* Scrub the private key. */
- if (ret)
- return -1;
- } else if (!strcasecmp(conn->incoming_cmd, "DEL_ONION")) {
- int ret = handle_control_del_onion(conn, cmd_data_len, args);
- memwipe(args, 0, cmd_data_len); /* Scrub the service id/pk. */
- if (ret)
- return -1;
- } else {
- connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n",
- conn->incoming_cmd);
- }
+ if (handle_control_command(conn, cmd_data_len, args) < 0)
+ return -1;
conn->incoming_cmd_cur_len = 0;
goto again;
}
-/** Something major has happened to circuit <b>circ</b>: tell any
- * interested control connections. */
-int
-control_event_circuit_status(origin_circuit_t *circ, circuit_status_event_t tp,
- int reason_code)
-{
- const char *status;
- char reasons[64] = "";
-
- if (!EVENT_IS_INTERESTING(EVENT_CIRCUIT_STATUS))
- return 0;
- tor_assert(circ);
-
- switch (tp)
- {
- case CIRC_EVENT_LAUNCHED: status = "LAUNCHED"; break;
- case CIRC_EVENT_BUILT: status = "BUILT"; break;
- case CIRC_EVENT_EXTENDED: status = "EXTENDED"; break;
- case CIRC_EVENT_FAILED: status = "FAILED"; break;
- case CIRC_EVENT_CLOSED: status = "CLOSED"; break;
- default:
- log_warn(LD_BUG, "Unrecognized status code %d", (int)tp);
- tor_fragile_assert();
- return 0;
- }
-
- if (tp == CIRC_EVENT_FAILED || tp == CIRC_EVENT_CLOSED) {
- const char *reason_str = circuit_end_reason_to_control_string(reason_code);
- char unk_reason_buf[16];
- if (!reason_str) {
- tor_snprintf(unk_reason_buf, 16, "UNKNOWN_%d", reason_code);
- reason_str = unk_reason_buf;
- }
- if (reason_code > 0 && reason_code & END_CIRC_REASON_FLAG_REMOTE) {
- tor_snprintf(reasons, sizeof(reasons),
- " REASON=DESTROYED REMOTE_REASON=%s", reason_str);
- } else {
- tor_snprintf(reasons, sizeof(reasons),
- " REASON=%s", reason_str);
- }
- }
-
- {
- char *circdesc = circuit_describe_status_for_controller(circ);
- const char *sp = strlen(circdesc) ? " " : "";
- send_control_event(EVENT_CIRCUIT_STATUS,
- "650 CIRC %lu %s%s%s%s\r\n",
- (unsigned long)circ->global_identifier,
- status, sp,
- circdesc,
- reasons);
- tor_free(circdesc);
- }
-
- return 0;
-}
-
-/** Something minor has happened to circuit <b>circ</b>: tell any
- * interested control connections. */
-static int
-control_event_circuit_status_minor(origin_circuit_t *circ,
- circuit_status_minor_event_t e,
- int purpose, const struct timeval *tv)
-{
- const char *event_desc;
- char event_tail[160] = "";
- if (!EVENT_IS_INTERESTING(EVENT_CIRCUIT_STATUS_MINOR))
- return 0;
- tor_assert(circ);
-
- switch (e)
- {
- case CIRC_MINOR_EVENT_PURPOSE_CHANGED:
- event_desc = "PURPOSE_CHANGED";
-
- {
- /* event_tail can currently be up to 68 chars long */
- const char *hs_state_str =
- circuit_purpose_to_controller_hs_state_string(purpose);
- tor_snprintf(event_tail, sizeof(event_tail),
- " OLD_PURPOSE=%s%s%s",
- circuit_purpose_to_controller_string(purpose),
- (hs_state_str != NULL) ? " OLD_HS_STATE=" : "",
- (hs_state_str != NULL) ? hs_state_str : "");
- }
-
- break;
- case CIRC_MINOR_EVENT_CANNIBALIZED:
- event_desc = "CANNIBALIZED";
-
- {
- /* event_tail can currently be up to 130 chars long */
- const char *hs_state_str =
- circuit_purpose_to_controller_hs_state_string(purpose);
- const struct timeval *old_timestamp_began = tv;
- char tbuf[ISO_TIME_USEC_LEN+1];
- format_iso_time_nospace_usec(tbuf, old_timestamp_began);
-
- tor_snprintf(event_tail, sizeof(event_tail),
- " OLD_PURPOSE=%s%s%s OLD_TIME_CREATED=%s",
- circuit_purpose_to_controller_string(purpose),
- (hs_state_str != NULL) ? " OLD_HS_STATE=" : "",
- (hs_state_str != NULL) ? hs_state_str : "",
- tbuf);
- }
-
- break;
- default:
- log_warn(LD_BUG, "Unrecognized status code %d", (int)e);
- tor_fragile_assert();
- return 0;
- }
-
- {
- char *circdesc = circuit_describe_status_for_controller(circ);
- const char *sp = strlen(circdesc) ? " " : "";
- send_control_event(EVENT_CIRCUIT_STATUS_MINOR,
- "650 CIRC_MINOR %lu %s%s%s%s\r\n",
- (unsigned long)circ->global_identifier,
- event_desc, sp,
- circdesc,
- event_tail);
- tor_free(circdesc);
- }
-
- return 0;
-}
-
-/**
- * <b>circ</b> has changed its purpose from <b>old_purpose</b>: tell any
- * interested controllers.
- */
-int
-control_event_circuit_purpose_changed(origin_circuit_t *circ,
- int old_purpose)
-{
- return control_event_circuit_status_minor(circ,
- CIRC_MINOR_EVENT_PURPOSE_CHANGED,
- old_purpose,
- NULL);
-}
-
-/**
- * <b>circ</b> has changed its purpose from <b>old_purpose</b>, and its
- * created-time from <b>old_tv_created</b>: tell any interested controllers.
- */
-int
-control_event_circuit_cannibalized(origin_circuit_t *circ,
- int old_purpose,
- const struct timeval *old_tv_created)
-{
- return control_event_circuit_status_minor(circ,
- CIRC_MINOR_EVENT_CANNIBALIZED,
- old_purpose,
- old_tv_created);
-}
-
-/** Given an AP connection <b>conn</b> and a <b>len</b>-character buffer
- * <b>buf</b>, determine the address:port combination requested on
- * <b>conn</b>, and write it to <b>buf</b>. Return 0 on success, -1 on
- * failure. */
-static int
-write_stream_target_to_buf(entry_connection_t *conn, char *buf, size_t len)
-{
- char buf2[256];
- if (conn->chosen_exit_name)
- if (tor_snprintf(buf2, sizeof(buf2), ".%s.exit", conn->chosen_exit_name)<0)
- return -1;
- if (!conn->socks_request)
- return -1;
- if (tor_snprintf(buf, len, "%s%s%s:%d",
- conn->socks_request->address,
- conn->chosen_exit_name ? buf2 : "",
- !conn->chosen_exit_name && connection_edge_is_rendezvous_stream(
- ENTRY_TO_EDGE_CONN(conn)) ? ".onion" : "",
- conn->socks_request->port)<0)
- return -1;
- return 0;
-}
-
-/** Something has happened to the stream associated with AP connection
- * <b>conn</b>: tell any interested control connections. */
-int
-control_event_stream_status(entry_connection_t *conn, stream_status_event_t tp,
- int reason_code)
-{
- char reason_buf[64];
- char addrport_buf[64];
- const char *status;
- circuit_t *circ;
- origin_circuit_t *origin_circ = NULL;
- char buf[256];
- const char *purpose = "";
- tor_assert(conn->socks_request);
-
- if (!EVENT_IS_INTERESTING(EVENT_STREAM_STATUS))
- return 0;
-
- if (tp == STREAM_EVENT_CLOSED &&
- (reason_code & END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED))
- return 0;
-
- write_stream_target_to_buf(conn, buf, sizeof(buf));
-
- reason_buf[0] = '\0';
- switch (tp)
- {
- case STREAM_EVENT_SENT_CONNECT: status = "SENTCONNECT"; break;
- case STREAM_EVENT_SENT_RESOLVE: status = "SENTRESOLVE"; break;
- case STREAM_EVENT_SUCCEEDED: status = "SUCCEEDED"; break;
- case STREAM_EVENT_FAILED: status = "FAILED"; break;
- case STREAM_EVENT_CLOSED: status = "CLOSED"; break;
- case STREAM_EVENT_NEW: status = "NEW"; break;
- case STREAM_EVENT_NEW_RESOLVE: status = "NEWRESOLVE"; break;
- case STREAM_EVENT_FAILED_RETRIABLE: status = "DETACHED"; break;
- case STREAM_EVENT_REMAP: status = "REMAP"; break;
- default:
- log_warn(LD_BUG, "Unrecognized status code %d", (int)tp);
- return 0;
- }
- if (reason_code && (tp == STREAM_EVENT_FAILED ||
- tp == STREAM_EVENT_CLOSED ||
- tp == STREAM_EVENT_FAILED_RETRIABLE)) {
- const char *reason_str = stream_end_reason_to_control_string(reason_code);
- char *r = NULL;
- if (!reason_str) {
- tor_asprintf(&r, " UNKNOWN_%d", reason_code);
- reason_str = r;
- }
- if (reason_code & END_STREAM_REASON_FLAG_REMOTE)
- tor_snprintf(reason_buf, sizeof(reason_buf),
- " REASON=END REMOTE_REASON=%s", reason_str);
- else
- tor_snprintf(reason_buf, sizeof(reason_buf),
- " REASON=%s", reason_str);
- tor_free(r);
- } else if (reason_code && tp == STREAM_EVENT_REMAP) {
- switch (reason_code) {
- case REMAP_STREAM_SOURCE_CACHE:
- strlcpy(reason_buf, " SOURCE=CACHE", sizeof(reason_buf));
- break;
- case REMAP_STREAM_SOURCE_EXIT:
- strlcpy(reason_buf, " SOURCE=EXIT", sizeof(reason_buf));
- break;
- default:
- tor_snprintf(reason_buf, sizeof(reason_buf), " REASON=UNKNOWN_%d",
- reason_code);
- /* XXX do we want SOURCE=UNKNOWN_%d above instead? -RD */
- break;
- }
- }
-
- if (tp == STREAM_EVENT_NEW || tp == STREAM_EVENT_NEW_RESOLVE) {
- /*
- * When the control conn is an AF_UNIX socket and we have no address,
- * it gets set to "(Tor_internal)"; see dnsserv_launch_request() in
- * dnsserv.c.
- */
- if (strcmp(ENTRY_TO_CONN(conn)->address, "(Tor_internal)") != 0) {
- tor_snprintf(addrport_buf,sizeof(addrport_buf), " SOURCE_ADDR=%s:%d",
- ENTRY_TO_CONN(conn)->address, ENTRY_TO_CONN(conn)->port);
- } else {
- /*
- * else leave it blank so control on AF_UNIX doesn't need to make
- * something up.
- */
- addrport_buf[0] = '\0';
- }
- } else {
- addrport_buf[0] = '\0';
- }
-
- if (tp == STREAM_EVENT_NEW_RESOLVE) {
- purpose = " PURPOSE=DNS_REQUEST";
- } else if (tp == STREAM_EVENT_NEW) {
- if (conn->use_begindir) {
- connection_t *linked = ENTRY_TO_CONN(conn)->linked_conn;
- int linked_dir_purpose = -1;
- if (linked && linked->type == CONN_TYPE_DIR)
- linked_dir_purpose = linked->purpose;
- if (DIR_PURPOSE_IS_UPLOAD(linked_dir_purpose))
- purpose = " PURPOSE=DIR_UPLOAD";
- else
- purpose = " PURPOSE=DIR_FETCH";
- } else
- purpose = " PURPOSE=USER";
- }
-
- circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn));
- if (circ && CIRCUIT_IS_ORIGIN(circ))
- origin_circ = TO_ORIGIN_CIRCUIT(circ);
- send_control_event(EVENT_STREAM_STATUS,
- "650 STREAM %"PRIu64" %s %lu %s%s%s%s\r\n",
- (ENTRY_TO_CONN(conn)->global_identifier),
- status,
- origin_circ?
- (unsigned long)origin_circ->global_identifier : 0ul,
- buf, reason_buf, addrport_buf, purpose);
-
- /* XXX need to specify its intended exit, etc? */
-
- return 0;
-}
-
-/** Figure out the best name for the target router of an OR connection
- * <b>conn</b>, and write it into the <b>len</b>-character buffer
- * <b>name</b>. */
-static void
-orconn_target_get_name(char *name, size_t len, or_connection_t *conn)
-{
- const node_t *node = node_get_by_id(conn->identity_digest);
- if (node) {
- tor_assert(len > MAX_VERBOSE_NICKNAME_LEN);
- node_get_verbose_nickname(node, name);
- } else if (! tor_digest_is_zero(conn->identity_digest)) {
- name[0] = '$';
- base16_encode(name+1, len-1, conn->identity_digest,
- DIGEST_LEN);
- } else {
- tor_snprintf(name, len, "%s:%d",
- conn->base_.address, conn->base_.port);
- }
-}
-
-/** Called when the status of an OR connection <b>conn</b> changes: tell any
- * interested control connections. <b>tp</b> is the new status for the
- * connection. If <b>conn</b> has just closed or failed, then <b>reason</b>
- * may be the reason why.
- */
-int
-control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp,
- int reason)
-{
- int ncircs = 0;
- const char *status;
- char name[128];
- char ncircs_buf[32] = {0}; /* > 8 + log10(2^32)=10 + 2 */
-
- if (!EVENT_IS_INTERESTING(EVENT_OR_CONN_STATUS))
- return 0;
-
- switch (tp)
- {
- case OR_CONN_EVENT_LAUNCHED: status = "LAUNCHED"; break;
- case OR_CONN_EVENT_CONNECTED: status = "CONNECTED"; break;
- case OR_CONN_EVENT_FAILED: status = "FAILED"; break;
- case OR_CONN_EVENT_CLOSED: status = "CLOSED"; break;
- case OR_CONN_EVENT_NEW: status = "NEW"; break;
- default:
- log_warn(LD_BUG, "Unrecognized status code %d", (int)tp);
- return 0;
- }
- if (conn->chan) {
- ncircs = circuit_count_pending_on_channel(TLS_CHAN_TO_BASE(conn->chan));
- } else {
- ncircs = 0;
- }
- ncircs += connection_or_get_num_circuits(conn);
- if (ncircs && (tp == OR_CONN_EVENT_FAILED || tp == OR_CONN_EVENT_CLOSED)) {
- tor_snprintf(ncircs_buf, sizeof(ncircs_buf), " NCIRCS=%d", ncircs);
- }
-
- orconn_target_get_name(name, sizeof(name), conn);
- send_control_event(EVENT_OR_CONN_STATUS,
- "650 ORCONN %s %s%s%s%s ID=%"PRIu64"\r\n",
- name, status,
- reason ? " REASON=" : "",
- orconn_end_reason_to_control_string(reason),
- ncircs_buf,
- (conn->base_.global_identifier));
-
- return 0;
-}
-
-/**
- * Print out STREAM_BW event for a single conn
- */
-int
-control_event_stream_bandwidth(edge_connection_t *edge_conn)
-{
- struct timeval now;
- char tbuf[ISO_TIME_USEC_LEN+1];
- if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
- if (!edge_conn->n_read && !edge_conn->n_written)
- return 0;
-
- tor_gettimeofday(&now);
- format_iso_time_nospace_usec(tbuf, &now);
- send_control_event(EVENT_STREAM_BANDWIDTH_USED,
- "650 STREAM_BW %"PRIu64" %lu %lu %s\r\n",
- (edge_conn->base_.global_identifier),
- (unsigned long)edge_conn->n_read,
- (unsigned long)edge_conn->n_written,
- tbuf);
-
- edge_conn->n_written = edge_conn->n_read = 0;
- }
-
- return 0;
-}
-
-/** A second or more has elapsed: tell any interested control
- * connections how much bandwidth streams have used. */
-int
-control_event_stream_bandwidth_used(void)
-{
- if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
- smartlist_t *conns = get_connection_array();
- edge_connection_t *edge_conn;
- struct timeval now;
- char tbuf[ISO_TIME_USEC_LEN+1];
-
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn)
- {
- if (conn->type != CONN_TYPE_AP)
- continue;
- edge_conn = TO_EDGE_CONN(conn);
- if (!edge_conn->n_read && !edge_conn->n_written)
- continue;
-
- tor_gettimeofday(&now);
- format_iso_time_nospace_usec(tbuf, &now);
- send_control_event(EVENT_STREAM_BANDWIDTH_USED,
- "650 STREAM_BW %"PRIu64" %lu %lu %s\r\n",
- (edge_conn->base_.global_identifier),
- (unsigned long)edge_conn->n_read,
- (unsigned long)edge_conn->n_written,
- tbuf);
-
- edge_conn->n_written = edge_conn->n_read = 0;
- }
- SMARTLIST_FOREACH_END(conn);
- }
-
- return 0;
-}
-
-/** A second or more has elapsed: tell any interested control connections
- * how much bandwidth origin circuits have used. */
-int
-control_event_circ_bandwidth_used(void)
-{
- if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED))
- return 0;
-
- SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
- if (!CIRCUIT_IS_ORIGIN(circ))
- continue;
-
- control_event_circ_bandwidth_used_for_circ(TO_ORIGIN_CIRCUIT(circ));
- }
- SMARTLIST_FOREACH_END(circ);
-
- return 0;
-}
-
-/**
- * Emit a CIRC_BW event line for a specific circuit.
- *
- * This function sets the values it emits to 0, and does not emit
- * an event if there is no new data to report since the last call.
- *
- * Therefore, it may be called at any frequency.
- */
-int
-control_event_circ_bandwidth_used_for_circ(origin_circuit_t *ocirc)
-{
- struct timeval now;
- char tbuf[ISO_TIME_USEC_LEN+1];
-
- tor_assert(ocirc);
-
- if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED))
- return 0;
-
- /* n_read_circ_bw and n_written_circ_bw are always updated
- * when there is any new cell on a circuit, and set to 0 after
- * the event, below.
- *
- * Therefore, checking them is sufficient to determine if there
- * is new data to report. */
- if (!ocirc->n_read_circ_bw && !ocirc->n_written_circ_bw)
- return 0;
-
- tor_gettimeofday(&now);
- format_iso_time_nospace_usec(tbuf, &now);
- send_control_event(EVENT_CIRC_BANDWIDTH_USED,
- "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu TIME=%s "
- "DELIVERED_READ=%lu OVERHEAD_READ=%lu "
- "DELIVERED_WRITTEN=%lu OVERHEAD_WRITTEN=%lu\r\n",
- ocirc->global_identifier,
- (unsigned long)ocirc->n_read_circ_bw,
- (unsigned long)ocirc->n_written_circ_bw,
- tbuf,
- (unsigned long)ocirc->n_delivered_read_circ_bw,
- (unsigned long)ocirc->n_overhead_read_circ_bw,
- (unsigned long)ocirc->n_delivered_written_circ_bw,
- (unsigned long)ocirc->n_overhead_written_circ_bw);
- ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
- ocirc->n_overhead_written_circ_bw = ocirc->n_overhead_read_circ_bw = 0;
- ocirc->n_delivered_written_circ_bw = ocirc->n_delivered_read_circ_bw = 0;
-
- return 0;
-}
-
-/** Print out CONN_BW event for a single OR/DIR/EXIT <b>conn</b> and reset
- * bandwidth counters. */
-int
-control_event_conn_bandwidth(connection_t *conn)
-{
- const char *conn_type_str;
- if (!get_options()->TestingEnableConnBwEvent ||
- !EVENT_IS_INTERESTING(EVENT_CONN_BW))
- return 0;
- if (!conn->n_read_conn_bw && !conn->n_written_conn_bw)
- return 0;
- switch (conn->type) {
- case CONN_TYPE_OR:
- conn_type_str = "OR";
- break;
- case CONN_TYPE_DIR:
- conn_type_str = "DIR";
- break;
- case CONN_TYPE_EXIT:
- conn_type_str = "EXIT";
- break;
- default:
- return 0;
- }
- send_control_event(EVENT_CONN_BW,
- "650 CONN_BW ID=%"PRIu64" TYPE=%s "
- "READ=%lu WRITTEN=%lu\r\n",
- (conn->global_identifier),
- conn_type_str,
- (unsigned long)conn->n_read_conn_bw,
- (unsigned long)conn->n_written_conn_bw);
- conn->n_written_conn_bw = conn->n_read_conn_bw = 0;
- return 0;
-}
-
-/** A second or more has elapsed: tell any interested control
- * connections how much bandwidth connections have used. */
-int
-control_event_conn_bandwidth_used(void)
-{
- if (get_options()->TestingEnableConnBwEvent &&
- EVENT_IS_INTERESTING(EVENT_CONN_BW)) {
- SMARTLIST_FOREACH(get_connection_array(), connection_t *, conn,
- control_event_conn_bandwidth(conn));
- }
- return 0;
-}
-
-/** Helper: iterate over cell statistics of <b>circ</b> and sum up added
- * cells, removed cells, and waiting times by cell command and direction.
- * Store results in <b>cell_stats</b>. Free cell statistics of the
- * circuit afterwards. */
-void
-sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats)
-{
- memset(cell_stats, 0, sizeof(cell_stats_t));
- SMARTLIST_FOREACH_BEGIN(circ->testing_cell_stats,
- const testing_cell_stats_entry_t *, ent) {
- tor_assert(ent->command <= CELL_COMMAND_MAX_);
- if (!ent->removed && !ent->exitward) {
- cell_stats->added_cells_appward[ent->command] += 1;
- } else if (!ent->removed && ent->exitward) {
- cell_stats->added_cells_exitward[ent->command] += 1;
- } else if (!ent->exitward) {
- cell_stats->removed_cells_appward[ent->command] += 1;
- cell_stats->total_time_appward[ent->command] += ent->waiting_time * 10;
- } else {
- cell_stats->removed_cells_exitward[ent->command] += 1;
- cell_stats->total_time_exitward[ent->command] += ent->waiting_time * 10;
- }
- } SMARTLIST_FOREACH_END(ent);
- circuit_clear_testing_cell_stats(circ);
-}
-
-/** Helper: append a cell statistics string to <code>event_parts</code>,
- * prefixed with <code>key</code>=. Statistics consist of comma-separated
- * key:value pairs with lower-case command strings as keys and cell
- * numbers or total waiting times as values. A key:value pair is included
- * if the entry in <code>include_if_non_zero</code> is not zero, but with
- * the (possibly zero) entry from <code>number_to_include</code>. Both
- * arrays are expected to have a length of CELL_COMMAND_MAX_ + 1. If no
- * entry in <code>include_if_non_zero</code> is positive, no string will
- * be added to <code>event_parts</code>. */
-void
-append_cell_stats_by_command(smartlist_t *event_parts, const char *key,
- const uint64_t *include_if_non_zero,
- const uint64_t *number_to_include)
-{
- smartlist_t *key_value_strings = smartlist_new();
- int i;
- for (i = 0; i <= CELL_COMMAND_MAX_; i++) {
- if (include_if_non_zero[i] > 0) {
- smartlist_add_asprintf(key_value_strings, "%s:%"PRIu64,
- cell_command_to_string(i),
- (number_to_include[i]));
- }
- }
- if (smartlist_len(key_value_strings) > 0) {
- char *joined = smartlist_join_strings(key_value_strings, ",", 0, NULL);
- smartlist_add_asprintf(event_parts, "%s=%s", key, joined);
- SMARTLIST_FOREACH(key_value_strings, char *, cp, tor_free(cp));
- tor_free(joined);
- }
- smartlist_free(key_value_strings);
-}
-
-/** Helper: format <b>cell_stats</b> for <b>circ</b> for inclusion in a
- * CELL_STATS event and write result string to <b>event_string</b>. */
-void
-format_cell_stats(char **event_string, circuit_t *circ,
- cell_stats_t *cell_stats)
-{
- smartlist_t *event_parts = smartlist_new();
- if (CIRCUIT_IS_ORIGIN(circ)) {
- origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
- smartlist_add_asprintf(event_parts, "ID=%lu",
- (unsigned long)ocirc->global_identifier);
- } else if (TO_OR_CIRCUIT(circ)->p_chan) {
- or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
- smartlist_add_asprintf(event_parts, "InboundQueue=%lu",
- (unsigned long)or_circ->p_circ_id);
- smartlist_add_asprintf(event_parts, "InboundConn=%"PRIu64,
- (or_circ->p_chan->global_identifier));
- append_cell_stats_by_command(event_parts, "InboundAdded",
- cell_stats->added_cells_appward,
- cell_stats->added_cells_appward);
- append_cell_stats_by_command(event_parts, "InboundRemoved",
- cell_stats->removed_cells_appward,
- cell_stats->removed_cells_appward);
- append_cell_stats_by_command(event_parts, "InboundTime",
- cell_stats->removed_cells_appward,
- cell_stats->total_time_appward);
- }
- if (circ->n_chan) {
- smartlist_add_asprintf(event_parts, "OutboundQueue=%lu",
- (unsigned long)circ->n_circ_id);
- smartlist_add_asprintf(event_parts, "OutboundConn=%"PRIu64,
- (circ->n_chan->global_identifier));
- append_cell_stats_by_command(event_parts, "OutboundAdded",
- cell_stats->added_cells_exitward,
- cell_stats->added_cells_exitward);
- append_cell_stats_by_command(event_parts, "OutboundRemoved",
- cell_stats->removed_cells_exitward,
- cell_stats->removed_cells_exitward);
- append_cell_stats_by_command(event_parts, "OutboundTime",
- cell_stats->removed_cells_exitward,
- cell_stats->total_time_exitward);
- }
- *event_string = smartlist_join_strings(event_parts, " ", 0, NULL);
- SMARTLIST_FOREACH(event_parts, char *, cp, tor_free(cp));
- smartlist_free(event_parts);
-}
-
-/** A second or more has elapsed: tell any interested control connection
- * how many cells have been processed for a given circuit. */
-int
-control_event_circuit_cell_stats(void)
-{
- cell_stats_t *cell_stats;
- char *event_string;
- if (!get_options()->TestingEnableCellStatsEvent ||
- !EVENT_IS_INTERESTING(EVENT_CELL_STATS))
- return 0;
- cell_stats = tor_malloc(sizeof(cell_stats_t));
- SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
- if (!circ->testing_cell_stats)
- continue;
- sum_up_cell_stats_by_command(circ, cell_stats);
- format_cell_stats(&event_string, circ, cell_stats);
- send_control_event(EVENT_CELL_STATS,
- "650 CELL_STATS %s\r\n", event_string);
- tor_free(event_string);
- }
- SMARTLIST_FOREACH_END(circ);
- tor_free(cell_stats);
- return 0;
-}
-
-/* about 5 minutes worth. */
-#define N_BW_EVENTS_TO_CACHE 300
-/* Index into cached_bw_events to next write. */
-static int next_measurement_idx = 0;
-/* number of entries set in n_measurements */
-static int n_measurements = 0;
-static struct cached_bw_event_s {
- uint32_t n_read;
- uint32_t n_written;
-} cached_bw_events[N_BW_EVENTS_TO_CACHE];
-
-/** A second or more has elapsed: tell any interested control
- * connections how much bandwidth we used. */
-int
-control_event_bandwidth_used(uint32_t n_read, uint32_t n_written)
-{
- cached_bw_events[next_measurement_idx].n_read = n_read;
- cached_bw_events[next_measurement_idx].n_written = n_written;
- if (++next_measurement_idx == N_BW_EVENTS_TO_CACHE)
- next_measurement_idx = 0;
- if (n_measurements < N_BW_EVENTS_TO_CACHE)
- ++n_measurements;
-
- if (EVENT_IS_INTERESTING(EVENT_BANDWIDTH_USED)) {
- send_control_event(EVENT_BANDWIDTH_USED,
- "650 BW %lu %lu\r\n",
- (unsigned long)n_read,
- (unsigned long)n_written);
- }
-
- return 0;
-}
-
-STATIC char *
-get_bw_samples(void)
-{
- int i;
- int idx = (next_measurement_idx + N_BW_EVENTS_TO_CACHE - n_measurements)
- % N_BW_EVENTS_TO_CACHE;
- tor_assert(0 <= idx && idx < N_BW_EVENTS_TO_CACHE);
-
- smartlist_t *elements = smartlist_new();
-
- for (i = 0; i < n_measurements; ++i) {
- tor_assert(0 <= idx && idx < N_BW_EVENTS_TO_CACHE);
- const struct cached_bw_event_s *bwe = &cached_bw_events[idx];
-
- smartlist_add_asprintf(elements, "%u,%u",
- (unsigned)bwe->n_read,
- (unsigned)bwe->n_written);
-
- idx = (idx + 1) % N_BW_EVENTS_TO_CACHE;
- }
-
- char *result = smartlist_join_strings(elements, " ", 0, NULL);
-
- SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
- smartlist_free(elements);
-
- return result;
-}
-
-/** Called when we are sending a log message to the controllers: suspend
- * sending further log messages to the controllers until we're done. Used by
- * CONN_LOG_PROTECT. */
-void
-disable_control_logging(void)
-{
- ++disable_log_messages;
-}
-
-/** We're done sending a log message to the controllers: re-enable controller
- * logging. Used by CONN_LOG_PROTECT. */
-void
-enable_control_logging(void)
-{
- if (--disable_log_messages < 0)
- tor_assert(0);
-}
-
-/** We got a log message: tell any interested control connections. */
-void
-control_event_logmsg(int severity, uint32_t domain, const char *msg)
-{
- int event;
-
- /* Don't even think of trying to add stuff to a buffer from a cpuworker
- * thread. (See #25987 for plan to fix.) */
- if (! in_main_thread())
- return;
-
- if (disable_log_messages)
- return;
-
- if (domain == LD_BUG && EVENT_IS_INTERESTING(EVENT_STATUS_GENERAL) &&
- severity <= LOG_NOTICE) {
- char *esc = esc_for_log(msg);
- ++disable_log_messages;
- control_event_general_status(severity, "BUG REASON=%s", esc);
- --disable_log_messages;
- tor_free(esc);
- }
-
- event = log_severity_to_event(severity);
- if (event >= 0 && EVENT_IS_INTERESTING(event)) {
- char *b = NULL;
- const char *s;
- if (strchr(msg, '\n')) {
- char *cp;
- b = tor_strdup(msg);
- for (cp = b; *cp; ++cp)
- if (*cp == '\r' || *cp == '\n')
- *cp = ' ';
- }
- switch (severity) {
- case LOG_DEBUG: s = "DEBUG"; break;
- case LOG_INFO: s = "INFO"; break;
- case LOG_NOTICE: s = "NOTICE"; break;
- case LOG_WARN: s = "WARN"; break;
- case LOG_ERR: s = "ERR"; break;
- default: s = "UnknownLogSeverity"; break;
- }
- ++disable_log_messages;
- send_control_event(event, "650 %s %s\r\n", s, b?b:msg);
- if (severity == LOG_ERR) {
- /* Force a flush, since we may be about to die horribly */
- queued_events_flush_all(1);
- }
- --disable_log_messages;
- tor_free(b);
- }
-}
-
-/**
- * Logging callback: called when there is a queued pending log callback.
- */
-void
-control_event_logmsg_pending(void)
-{
- if (! in_main_thread()) {
- /* We can't handle this case yet, since we're using a
- * mainloop_event_t to invoke queued_events_flush_all. We ought to
- * use a different mechanism instead: see #25987.
- **/
- return;
- }
- tor_assert(flush_queued_events_event);
- mainloop_event_activate(flush_queued_events_event);
-}
-
-/** Called whenever we receive new router descriptors: tell any
- * interested control connections. <b>routers</b> is a list of
- * routerinfo_t's.
- */
-int
-control_event_descriptors_changed(smartlist_t *routers)
-{
- char *msg;
-
- if (!EVENT_IS_INTERESTING(EVENT_NEW_DESC))
- return 0;
-
- {
- smartlist_t *names = smartlist_new();
- char *ids;
- SMARTLIST_FOREACH(routers, routerinfo_t *, ri, {
- char *b = tor_malloc(MAX_VERBOSE_NICKNAME_LEN+1);
- router_get_verbose_nickname(b, ri);
- smartlist_add(names, b);
- });
- ids = smartlist_join_strings(names, " ", 0, NULL);
- tor_asprintf(&msg, "650 NEWDESC %s\r\n", ids);
- send_control_event_string(EVENT_NEW_DESC, msg);
- tor_free(ids);
- tor_free(msg);
- SMARTLIST_FOREACH(names, char *, cp, tor_free(cp));
- smartlist_free(names);
- }
- return 0;
-}
-
-/** Called when an address mapping on <b>from</b> from changes to <b>to</b>.
- * <b>expires</b> values less than 3 are special; see connection_edge.c. If
- * <b>error</b> is non-NULL, it is an error code describing the failure
- * mode of the mapping.
- */
-int
-control_event_address_mapped(const char *from, const char *to, time_t expires,
- const char *error, const int cached)
-{
- if (!EVENT_IS_INTERESTING(EVENT_ADDRMAP))
- return 0;
-
- if (expires < 3 || expires == TIME_MAX)
- send_control_event(EVENT_ADDRMAP,
- "650 ADDRMAP %s %s NEVER %s%s"
- "CACHED=\"%s\"\r\n",
- from, to, error?error:"", error?" ":"",
- cached?"YES":"NO");
- else {
- char buf[ISO_TIME_LEN+1];
- char buf2[ISO_TIME_LEN+1];
- format_local_iso_time(buf,expires);
- format_iso_time(buf2,expires);
- send_control_event(EVENT_ADDRMAP,
- "650 ADDRMAP %s %s \"%s\""
- " %s%sEXPIRES=\"%s\" CACHED=\"%s\"\r\n",
- from, to, buf,
- error?error:"", error?" ":"",
- buf2, cached?"YES":"NO");
- }
-
- return 0;
-}
-
/** Cached liveness for network liveness events and GETINFO
*/
static int network_is_live = 0;
-static int
+int
get_cached_network_liveness(void)
{
return network_is_live;
}
-static void
+void
set_cached_network_liveness(int liveness)
{
network_is_live = liveness;
}
-/** The network liveness has changed; this is called from circuitstats.c
- * whenever we receive a cell, or when timeout expires and we assume the
- * network is down. */
-int
-control_event_network_liveness_update(int liveness)
-{
- if (liveness > 0) {
- if (get_cached_network_liveness() <= 0) {
- /* Update cached liveness */
- set_cached_network_liveness(1);
- log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS UP");
- send_control_event_string(EVENT_NETWORK_LIVENESS,
- "650 NETWORK_LIVENESS UP\r\n");
- }
- /* else was already live, no-op */
- } else {
- if (get_cached_network_liveness() > 0) {
- /* Update cached liveness */
- set_cached_network_liveness(0);
- log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS DOWN");
- send_control_event_string(EVENT_NETWORK_LIVENESS,
- "650 NETWORK_LIVENESS DOWN\r\n");
- }
- /* else was already dead, no-op */
- }
-
- return 0;
-}
-
-/** Helper function for NS-style events. Constructs and sends an event
- * of type <b>event</b> with string <b>event_string</b> out of the set of
- * networkstatuses <b>statuses</b>. Currently it is used for NS events
- * and NEWCONSENSUS events. */
-static int
-control_event_networkstatus_changed_helper(smartlist_t *statuses,
- uint16_t event,
- const char *event_string)
-{
- smartlist_t *strs;
- char *s, *esc = NULL;
- if (!EVENT_IS_INTERESTING(event) || !smartlist_len(statuses))
- return 0;
-
- strs = smartlist_new();
- smartlist_add_strdup(strs, "650+");
- smartlist_add_strdup(strs, event_string);
- smartlist_add_strdup(strs, "\r\n");
- SMARTLIST_FOREACH(statuses, const routerstatus_t *, rs,
- {
- s = networkstatus_getinfo_helper_single(rs);
- if (!s) continue;
- smartlist_add(strs, s);
- });
-
- s = smartlist_join_strings(strs, "", 0, NULL);
- write_escaped_data(s, strlen(s), &esc);
- SMARTLIST_FOREACH(strs, char *, cp, tor_free(cp));
- smartlist_free(strs);
- tor_free(s);
- send_control_event_string(event, esc);
- send_control_event_string(event,
- "650 OK\r\n");
-
- tor_free(esc);
- return 0;
-}
-
-/** Called when the routerstatus_ts <b>statuses</b> have changed: sends
- * an NS event to any controller that cares. */
-int
-control_event_networkstatus_changed(smartlist_t *statuses)
-{
- return control_event_networkstatus_changed_helper(statuses, EVENT_NS, "NS");
-}
-
-/** Called when we get a new consensus networkstatus. Sends a NEWCONSENSUS
- * event consisting of an NS-style line for each relay in the consensus. */
-int
-control_event_newconsensus(const networkstatus_t *consensus)
-{
- if (!control_event_is_interesting(EVENT_NEWCONSENSUS))
- return 0;
- return control_event_networkstatus_changed_helper(
- consensus->routerstatus_list, EVENT_NEWCONSENSUS, "NEWCONSENSUS");
-}
-
-/** Called when we compute a new circuitbuildtimeout */
-int
-control_event_buildtimeout_set(buildtimeout_set_event_t type,
- const char *args)
-{
- const char *type_string = NULL;
-
- if (!control_event_is_interesting(EVENT_BUILDTIMEOUT_SET))
- return 0;
-
- switch (type) {
- case BUILDTIMEOUT_SET_EVENT_COMPUTED:
- type_string = "COMPUTED";
- break;
- case BUILDTIMEOUT_SET_EVENT_RESET:
- type_string = "RESET";
- break;
- case BUILDTIMEOUT_SET_EVENT_SUSPENDED:
- type_string = "SUSPENDED";
- break;
- case BUILDTIMEOUT_SET_EVENT_DISCARD:
- type_string = "DISCARD";
- break;
- case BUILDTIMEOUT_SET_EVENT_RESUME:
- type_string = "RESUME";
- break;
- default:
- type_string = "UNKNOWN";
- break;
- }
-
- send_control_event(EVENT_BUILDTIMEOUT_SET,
- "650 BUILDTIMEOUT_SET %s %s\r\n",
- type_string, args);
-
- return 0;
-}
-
-/** Called when a signal has been processed from signal_callback */
-int
-control_event_signal(uintptr_t signal_num)
-{
- const char *signal_string = NULL;
-
- if (!control_event_is_interesting(EVENT_GOT_SIGNAL))
- return 0;
-
- switch (signal_num) {
- case SIGHUP:
- signal_string = "RELOAD";
- break;
- case SIGUSR1:
- signal_string = "DUMP";
- break;
- case SIGUSR2:
- signal_string = "DEBUG";
- break;
- case SIGNEWNYM:
- signal_string = "NEWNYM";
- break;
- case SIGCLEARDNSCACHE:
- signal_string = "CLEARDNSCACHE";
- break;
- case SIGHEARTBEAT:
- signal_string = "HEARTBEAT";
- break;
- default:
- log_warn(LD_BUG, "Unrecognized signal %lu in control_event_signal",
- (unsigned long)signal_num);
- return -1;
- }
-
- send_control_event(EVENT_GOT_SIGNAL, "650 SIGNAL %s\r\n",
- signal_string);
- return 0;
-}
-
-/** Called when a single local_routerstatus_t has changed: Sends an NS event
- * to any controller that cares. */
-int
-control_event_networkstatus_changed_single(const routerstatus_t *rs)
-{
- smartlist_t *statuses;
- int r;
-
- if (!EVENT_IS_INTERESTING(EVENT_NS))
- return 0;
-
- statuses = smartlist_new();
- smartlist_add(statuses, (void*)rs);
- r = control_event_networkstatus_changed(statuses);
- smartlist_free(statuses);
- return r;
-}
-
-/** Our own router descriptor has changed; tell any controllers that care.
- */
-int
-control_event_my_descriptor_changed(void)
-{
- send_control_event(EVENT_DESCCHANGED, "650 DESCCHANGED\r\n");
- return 0;
-}
-
-/** Helper: sends a status event where <b>type</b> is one of
- * EVENT_STATUS_{GENERAL,CLIENT,SERVER}, where <b>severity</b> is one of
- * LOG_{NOTICE,WARN,ERR}, and where <b>format</b> is a printf-style format
- * string corresponding to <b>args</b>. */
-static int
-control_event_status(int type, int severity, const char *format, va_list args)
-{
- char *user_buf = NULL;
- char format_buf[160];
- const char *status, *sev;
-
- switch (type) {
- case EVENT_STATUS_GENERAL:
- status = "STATUS_GENERAL";
- break;
- case EVENT_STATUS_CLIENT:
- status = "STATUS_CLIENT";
- break;
- case EVENT_STATUS_SERVER:
- status = "STATUS_SERVER";
- break;
- default:
- log_warn(LD_BUG, "Unrecognized status type %d", type);
- return -1;
- }
- switch (severity) {
- case LOG_NOTICE:
- sev = "NOTICE";
- break;
- case LOG_WARN:
- sev = "WARN";
- break;
- case LOG_ERR:
- sev = "ERR";
- break;
- default:
- log_warn(LD_BUG, "Unrecognized status severity %d", severity);
- return -1;
- }
- if (tor_snprintf(format_buf, sizeof(format_buf), "650 %s %s",
- status, sev)<0) {
- log_warn(LD_BUG, "Format string too long.");
- return -1;
- }
- tor_vasprintf(&user_buf, format, args);
-
- send_control_event(type, "%s %s\r\n", format_buf, user_buf);
- tor_free(user_buf);
- return 0;
-}
-
-#define CONTROL_EVENT_STATUS_BODY(event, sev) \
- int r; \
- do { \
- va_list ap; \
- if (!EVENT_IS_INTERESTING(event)) \
- return 0; \
- \
- va_start(ap, format); \
- r = control_event_status((event), (sev), format, ap); \
- va_end(ap); \
- } while (0)
-
-/** Format and send an EVENT_STATUS_GENERAL event whose main text is obtained
- * by formatting the arguments using the printf-style <b>format</b>. */
-int
-control_event_general_status(int severity, const char *format, ...)
-{
- CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_GENERAL, severity);
- return r;
-}
-
-/** Format and send an EVENT_STATUS_GENERAL LOG_ERR event, and flush it to the
- * controller(s) immediately. */
-int
-control_event_general_error(const char *format, ...)
-{
- CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_GENERAL, LOG_ERR);
- /* Force a flush, since we may be about to die horribly */
- queued_events_flush_all(1);
- return r;
-}
-
-/** Format and send an EVENT_STATUS_CLIENT event whose main text is obtained
- * by formatting the arguments using the printf-style <b>format</b>. */
-int
-control_event_client_status(int severity, const char *format, ...)
-{
- CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_CLIENT, severity);
- return r;
-}
-
-/** Format and send an EVENT_STATUS_CLIENT LOG_ERR event, and flush it to the
- * controller(s) immediately. */
-int
-control_event_client_error(const char *format, ...)
-{
- CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_CLIENT, LOG_ERR);
- /* Force a flush, since we may be about to die horribly */
- queued_events_flush_all(1);
- return r;
-}
-
-/** Format and send an EVENT_STATUS_SERVER event whose main text is obtained
- * by formatting the arguments using the printf-style <b>format</b>. */
-int
-control_event_server_status(int severity, const char *format, ...)
-{
- CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_SERVER, severity);
- return r;
-}
-
-/** Format and send an EVENT_STATUS_SERVER LOG_ERR event, and flush it to the
- * controller(s) immediately. */
-int
-control_event_server_error(const char *format, ...)
-{
- CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_SERVER, LOG_ERR);
- /* Force a flush, since we may be about to die horribly */
- queued_events_flush_all(1);
- return r;
-}
-
-/** Called when the status of an entry guard with the given <b>nickname</b>
- * and identity <b>digest</b> has changed to <b>status</b>: tells any
- * controllers that care. */
-int
-control_event_guard(const char *nickname, const char *digest,
- const char *status)
-{
- char hbuf[HEX_DIGEST_LEN+1];
- base16_encode(hbuf, sizeof(hbuf), digest, DIGEST_LEN);
- if (!EVENT_IS_INTERESTING(EVENT_GUARD))
- return 0;
-
- {
- char buf[MAX_VERBOSE_NICKNAME_LEN+1];
- const node_t *node = node_get_by_id(digest);
- if (node) {
- node_get_verbose_nickname(node, buf);
- } else {
- tor_snprintf(buf, sizeof(buf), "$%s~%s", hbuf, nickname);
- }
- send_control_event(EVENT_GUARD,
- "650 GUARD ENTRY %s %s\r\n", buf, status);
- }
- return 0;
-}
-
-/** Called when a configuration option changes. This is generally triggered
- * by SETCONF requests and RELOAD/SIGHUP signals. The <b>elements</b> is
- * a smartlist_t containing (key, value, ...) pairs in sequence.
- * <b>value</b> can be NULL. */
-int
-control_event_conf_changed(const smartlist_t *elements)
-{
- int i;
- char *result;
- smartlist_t *lines;
- if (!EVENT_IS_INTERESTING(EVENT_CONF_CHANGED) ||
- smartlist_len(elements) == 0) {
- return 0;
- }
- lines = smartlist_new();
- for (i = 0; i < smartlist_len(elements); i += 2) {
- char *k = smartlist_get(elements, i);
- char *v = smartlist_get(elements, i+1);
- if (v == NULL) {
- smartlist_add_asprintf(lines, "650-%s", k);
- } else {
- smartlist_add_asprintf(lines, "650-%s=%s", k, v);
- }
- }
- result = smartlist_join_strings(lines, "\r\n", 0, NULL);
- send_control_event(EVENT_CONF_CHANGED,
- "650-CONF_CHANGED\r\n%s\r\n650 OK\r\n", result);
- tor_free(result);
- SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
- smartlist_free(lines);
- return 0;
-}
-
-/** Helper: Return a newly allocated string containing a path to the
- * file where we store our authentication cookie. */
-char *
-get_controller_cookie_file_name(void)
-{
- const or_options_t *options = get_options();
- if (options->CookieAuthFile && strlen(options->CookieAuthFile)) {
- return tor_strdup(options->CookieAuthFile);
- } else {
- return get_datadir_fname("control_auth_cookie");
- }
-}
-
-/* Initialize the cookie-based authentication system of the
- * ControlPort. If <b>enabled</b> is 0, then disable the cookie
- * authentication system. */
-int
-init_control_cookie_authentication(int enabled)
-{
- char *fname = NULL;
- int retval;
-
- if (!enabled) {
- authentication_cookie_is_set = 0;
- return 0;
- }
-
- fname = get_controller_cookie_file_name();
- retval = init_cookie_authentication(fname, "", /* no header */
- AUTHENTICATION_COOKIE_LEN,
- get_options()->CookieAuthFileGroupReadable,
- &authentication_cookie,
- &authentication_cookie_is_set);
- tor_free(fname);
- return retval;
-}
-
/** A copy of the process specifier of Tor's owning controller, or
* NULL if this Tor instance is not currently owned by a process. */
static char *owning_controller_process_spec = NULL;
@@ -7025,553 +566,12 @@ monitor_owning_controller_process(const char *process_spec)
}
}
-/** We just generated a new summary of which countries we've seen clients
- * from recently. Send a copy to the controller in case it wants to
- * display it for the user. */
-void
-control_event_clients_seen(const char *controller_str)
-{
- send_control_event(EVENT_CLIENTS_SEEN,
- "650 CLIENTS_SEEN %s\r\n", controller_str);
-}
-
-/** A new pluggable transport called <b>transport_name</b> was
- * launched on <b>addr</b>:<b>port</b>. <b>mode</b> is either
- * "server" or "client" depending on the mode of the pluggable
- * transport.
- * "650" SP "TRANSPORT_LAUNCHED" SP Mode SP Name SP Address SP Port
- */
-void
-control_event_transport_launched(const char *mode, const char *transport_name,
- tor_addr_t *addr, uint16_t port)
-{
- send_control_event(EVENT_TRANSPORT_LAUNCHED,
- "650 TRANSPORT_LAUNCHED %s %s %s %u\r\n",
- mode, transport_name, fmt_addr(addr), port);
-}
-
-/** A pluggable transport called <b>pt_name</b> has emitted a log message
- * found in <b>message</b> at <b>severity</b> log level. */
-void
-control_event_pt_log(const char *log)
-{
- send_control_event(EVENT_PT_LOG,
- "650 PT_LOG %s\r\n",
- log);
-}
-
-/** A pluggable transport has emitted a STATUS message found in
- * <b>status</b>. */
-void
-control_event_pt_status(const char *status)
-{
- send_control_event(EVENT_PT_STATUS,
- "650 PT_STATUS %s\r\n",
- status);
-}
-
-/** Convert rendezvous auth type to string for HS_DESC control events
- */
-const char *
-rend_auth_type_to_string(rend_auth_type_t auth_type)
-{
- const char *str;
-
- switch (auth_type) {
- case REND_NO_AUTH:
- str = "NO_AUTH";
- break;
- case REND_BASIC_AUTH:
- str = "BASIC_AUTH";
- break;
- case REND_STEALTH_AUTH:
- str = "STEALTH_AUTH";
- break;
- default:
- str = "UNKNOWN";
- }
-
- return str;
-}
-
-/** Return a longname the node whose identity is <b>id_digest</b>. If
- * node_get_by_id() returns NULL, base 16 encoding of <b>id_digest</b> is
- * returned instead.
- *
- * This function is not thread-safe. Each call to this function invalidates
- * previous values returned by this function.
- */
-MOCK_IMPL(const char *,
-node_describe_longname_by_id,(const char *id_digest))
-{
- static char longname[MAX_VERBOSE_NICKNAME_LEN+1];
- node_get_verbose_nickname_by_id(id_digest, longname);
- return longname;
-}
-
-/** Return either the onion address if the given pointer is a non empty
- * string else the unknown string. */
-static const char *
-rend_hsaddress_str_or_unknown(const char *onion_address)
-{
- static const char *str_unknown = "UNKNOWN";
- const char *str_ret = str_unknown;
-
- /* No valid pointer, unknown it is. */
- if (!onion_address) {
- goto end;
- }
- /* Empty onion address thus we don't know, unknown it is. */
- if (onion_address[0] == '\0') {
- goto end;
- }
- /* All checks are good so return the given onion address. */
- str_ret = onion_address;
-
- end:
- return str_ret;
-}
-
-/** send HS_DESC requested event.
- *
- * <b>rend_query</b> is used to fetch requested onion address and auth type.
- * <b>hs_dir</b> is the description of contacting hs directory.
- * <b>desc_id_base32</b> is the ID of requested hs descriptor.
- * <b>hsdir_index</b> is the HSDir fetch index value for v3, an hex string.
- */
-void
-control_event_hs_descriptor_requested(const char *onion_address,
- rend_auth_type_t auth_type,
- const char *id_digest,
- const char *desc_id,
- const char *hsdir_index)
-{
- char *hsdir_index_field = NULL;
-
- if (BUG(!id_digest || !desc_id)) {
- return;
- }
-
- if (hsdir_index) {
- tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index);
- }
-
- send_control_event(EVENT_HS_DESC,
- "650 HS_DESC REQUESTED %s %s %s %s%s\r\n",
- rend_hsaddress_str_or_unknown(onion_address),
- rend_auth_type_to_string(auth_type),
- node_describe_longname_by_id(id_digest),
- desc_id,
- hsdir_index_field ? hsdir_index_field : "");
- tor_free(hsdir_index_field);
-}
-
-/** For an HS descriptor query <b>rend_data</b>, using the
- * <b>onion_address</b> and HSDir fingerprint <b>hsdir_fp</b>, find out
- * which descriptor ID in the query is the right one.
- *
- * Return a pointer of the binary descriptor ID found in the query's object
- * or NULL if not found. */
-static const char *
-get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
-{
- int replica;
- const char *desc_id = NULL;
- const rend_data_v2_t *rend_data_v2 = TO_REND_DATA_V2(rend_data);
-
- /* Possible if the fetch was done using a descriptor ID. This means that
- * the HSFETCH command was used. */
- if (!tor_digest_is_zero(rend_data_v2->desc_id_fetch)) {
- desc_id = rend_data_v2->desc_id_fetch;
- goto end;
- }
-
- /* Without a directory fingerprint at this stage, we can't do much. */
- if (hsdir_fp == NULL) {
- goto end;
- }
-
- /* OK, we have an onion address so now let's find which descriptor ID
- * is the one associated with the HSDir fingerprint. */
- for (replica = 0; replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
- replica++) {
- const char *digest = rend_data_get_desc_id(rend_data, replica, NULL);
-
- SMARTLIST_FOREACH_BEGIN(rend_data->hsdirs_fp, char *, fingerprint) {
- if (tor_memcmp(fingerprint, hsdir_fp, DIGEST_LEN) == 0) {
- /* Found it! This descriptor ID is the right one. */
- desc_id = digest;
- goto end;
- }
- } SMARTLIST_FOREACH_END(fingerprint);
- }
-
- end:
- return desc_id;
-}
-
-/** send HS_DESC CREATED event when a local service generates a descriptor.
- *
- * <b>onion_address</b> is service address.
- * <b>desc_id</b> is the descriptor ID.
- * <b>replica</b> is the the descriptor replica number. If it is negative, it
- * is ignored.
- */
-void
-control_event_hs_descriptor_created(const char *onion_address,
- const char *desc_id,
- int replica)
-{
- char *replica_field = NULL;
-
- if (BUG(!onion_address || !desc_id)) {
- return;
- }
-
- if (replica >= 0) {
- tor_asprintf(&replica_field, " REPLICA=%d", replica);
- }
-
- send_control_event(EVENT_HS_DESC,
- "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s%s\r\n",
- onion_address, desc_id,
- replica_field ? replica_field : "");
- tor_free(replica_field);
-}
-
-/** send HS_DESC upload event.
- *
- * <b>onion_address</b> is service address.
- * <b>hs_dir</b> is the description of contacting hs directory.
- * <b>desc_id</b> is the ID of requested hs descriptor.
- */
-void
-control_event_hs_descriptor_upload(const char *onion_address,
- const char *id_digest,
- const char *desc_id,
- const char *hsdir_index)
-{
- char *hsdir_index_field = NULL;
-
- if (BUG(!onion_address || !id_digest || !desc_id)) {
- return;
- }
-
- if (hsdir_index) {
- tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index);
- }
-
- send_control_event(EVENT_HS_DESC,
- "650 HS_DESC UPLOAD %s UNKNOWN %s %s%s\r\n",
- onion_address,
- node_describe_longname_by_id(id_digest),
- desc_id,
- hsdir_index_field ? hsdir_index_field : "");
- tor_free(hsdir_index_field);
-}
-
-/** send HS_DESC event after got response from hs directory.
- *
- * NOTE: this is an internal function used by following functions:
- * control_event_hsv2_descriptor_received
- * control_event_hsv2_descriptor_failed
- * control_event_hsv3_descriptor_failed
- *
- * So do not call this function directly.
- */
-static void
-event_hs_descriptor_receive_end(const char *action,
- const char *onion_address,
- const char *desc_id,
- rend_auth_type_t auth_type,
- const char *hsdir_id_digest,
- const char *reason)
-{
- char *reason_field = NULL;
-
- if (BUG(!action || !onion_address)) {
- return;
- }
-
- if (reason) {
- tor_asprintf(&reason_field, " REASON=%s", reason);
- }
-
- send_control_event(EVENT_HS_DESC,
- "650 HS_DESC %s %s %s %s%s%s\r\n",
- action,
- rend_hsaddress_str_or_unknown(onion_address),
- rend_auth_type_to_string(auth_type),
- hsdir_id_digest ?
- node_describe_longname_by_id(hsdir_id_digest) :
- "UNKNOWN",
- desc_id ? desc_id : "",
- reason_field ? reason_field : "");
-
- tor_free(reason_field);
-}
-
-/** send HS_DESC event after got response from hs directory.
- *
- * NOTE: this is an internal function used by following functions:
- * control_event_hs_descriptor_uploaded
- * control_event_hs_descriptor_upload_failed
- *
- * So do not call this function directly.
- */
-void
-control_event_hs_descriptor_upload_end(const char *action,
- const char *onion_address,
- const char *id_digest,
- const char *reason)
-{
- char *reason_field = NULL;
-
- if (BUG(!action || !id_digest)) {
- return;
- }
-
- if (reason) {
- tor_asprintf(&reason_field, " REASON=%s", reason);
- }
-
- send_control_event(EVENT_HS_DESC,
- "650 HS_DESC %s %s UNKNOWN %s%s\r\n",
- action,
- rend_hsaddress_str_or_unknown(onion_address),
- node_describe_longname_by_id(id_digest),
- reason_field ? reason_field : "");
-
- tor_free(reason_field);
-}
-
-/** send HS_DESC RECEIVED event
- *
- * called when we successfully received a hidden service descriptor.
- */
-void
-control_event_hsv2_descriptor_received(const char *onion_address,
- const rend_data_t *rend_data,
- const char *hsdir_id_digest)
-{
- char *desc_id_field = NULL;
- const char *desc_id;
-
- if (BUG(!rend_data || !hsdir_id_digest || !onion_address)) {
- return;
- }
-
- desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
- if (desc_id != NULL) {
- char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
- /* Set the descriptor ID digest to base32 so we can send it. */
- base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
- DIGEST_LEN);
- /* Extra whitespace is needed before the value. */
- tor_asprintf(&desc_id_field, " %s", desc_id_base32);
- }
-
- event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
- TO_REND_DATA_V2(rend_data)->auth_type,
- hsdir_id_digest, NULL);
- tor_free(desc_id_field);
-}
-
-/* Send HS_DESC RECEIVED event
- *
- * Called when we successfully received a hidden service descriptor. */
-void
-control_event_hsv3_descriptor_received(const char *onion_address,
- const char *desc_id,
- const char *hsdir_id_digest)
-{
- char *desc_id_field = NULL;
-
- if (BUG(!onion_address || !desc_id || !hsdir_id_digest)) {
- return;
- }
-
- /* Because DescriptorID is an optional positional value, we need to add a
- * whitespace before in order to not be next to the HsDir value. */
- tor_asprintf(&desc_id_field, " %s", desc_id);
-
- event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
- REND_NO_AUTH, hsdir_id_digest, NULL);
- tor_free(desc_id_field);
-}
-
-/** send HS_DESC UPLOADED event
- *
- * called when we successfully uploaded a hidden service descriptor.
- */
-void
-control_event_hs_descriptor_uploaded(const char *id_digest,
- const char *onion_address)
-{
- if (BUG(!id_digest)) {
- return;
- }
-
- control_event_hs_descriptor_upload_end("UPLOADED", onion_address,
- id_digest, NULL);
-}
-
-/** Send HS_DESC event to inform controller that query <b>rend_data</b>
- * failed to retrieve hidden service descriptor from directory identified by
- * <b>id_digest</b>. If NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL,
- * add it to REASON= field.
- */
-void
-control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
- const char *hsdir_id_digest,
- const char *reason)
-{
- char *desc_id_field = NULL;
- const char *desc_id;
-
- if (BUG(!rend_data)) {
- return;
- }
-
- desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
- if (desc_id != NULL) {
- char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
- /* Set the descriptor ID digest to base32 so we can send it. */
- base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
- DIGEST_LEN);
- /* Extra whitespace is needed before the value. */
- tor_asprintf(&desc_id_field, " %s", desc_id_base32);
- }
-
- event_hs_descriptor_receive_end("FAILED", rend_data_get_address(rend_data),
- desc_id_field,
- TO_REND_DATA_V2(rend_data)->auth_type,
- hsdir_id_digest, reason);
- tor_free(desc_id_field);
-}
-
-/** Send HS_DESC event to inform controller that the query to
- * <b>onion_address</b> failed to retrieve hidden service descriptor
- * <b>desc_id</b> from directory identified by <b>hsdir_id_digest</b>. If
- * NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL, add it to REASON=
- * field. */
-void
-control_event_hsv3_descriptor_failed(const char *onion_address,
- const char *desc_id,
- const char *hsdir_id_digest,
- const char *reason)
-{
- char *desc_id_field = NULL;
-
- if (BUG(!onion_address || !desc_id || !reason)) {
- return;
- }
-
- /* Because DescriptorID is an optional positional value, we need to add a
- * whitespace before in order to not be next to the HsDir value. */
- tor_asprintf(&desc_id_field, " %s", desc_id);
-
- event_hs_descriptor_receive_end("FAILED", onion_address, desc_id_field,
- REND_NO_AUTH, hsdir_id_digest, reason);
- tor_free(desc_id_field);
-}
-
-/** Send HS_DESC_CONTENT event after completion of a successful fetch
- * from hs directory. If <b>hsdir_id_digest</b> is NULL, it is replaced
- * by "UNKNOWN". If <b>content</b> is NULL, it is replaced by an empty
- * string. The <b>onion_address</b> or <b>desc_id</b> set to NULL will
- * not trigger the control event. */
-void
-control_event_hs_descriptor_content(const char *onion_address,
- const char *desc_id,
- const char *hsdir_id_digest,
- const char *content)
-{
- static const char *event_name = "HS_DESC_CONTENT";
- char *esc_content = NULL;
-
- if (!onion_address || !desc_id) {
- log_warn(LD_BUG, "Called with onion_address==%p, desc_id==%p, ",
- onion_address, desc_id);
- return;
- }
-
- if (content == NULL) {
- /* Point it to empty content so it can still be escaped. */
- content = "";
- }
- write_escaped_data(content, strlen(content), &esc_content);
-
- send_control_event(EVENT_HS_DESC_CONTENT,
- "650+%s %s %s %s\r\n%s650 OK\r\n",
- event_name,
- rend_hsaddress_str_or_unknown(onion_address),
- desc_id,
- hsdir_id_digest ?
- node_describe_longname_by_id(hsdir_id_digest) :
- "UNKNOWN",
- esc_content);
- tor_free(esc_content);
-}
-
-/** Send HS_DESC event to inform controller upload of hidden service
- * descriptor identified by <b>id_digest</b> failed. If <b>reason</b>
- * is not NULL, add it to REASON= field.
- */
-void
-control_event_hs_descriptor_upload_failed(const char *id_digest,
- const char *onion_address,
- const char *reason)
-{
- if (BUG(!id_digest)) {
- return;
- }
- control_event_hs_descriptor_upload_end("FAILED", onion_address,
- id_digest, reason);
-}
-
/** Free any leftover allocated memory of the control.c subsystem. */
void
control_free_all(void)
{
- smartlist_t *queued_events = NULL;
-
- stats_prev_n_read = stats_prev_n_written = 0;
-
- if (authentication_cookie) /* Free the auth cookie */
- tor_free(authentication_cookie);
- if (detached_onion_services) { /* Free the detached onion services */
- SMARTLIST_FOREACH(detached_onion_services, char *, cp, tor_free(cp));
- smartlist_free(detached_onion_services);
- }
-
- if (queued_control_events_lock) {
- tor_mutex_acquire(queued_control_events_lock);
- flush_queued_event_pending = 0;
- queued_events = queued_control_events;
- queued_control_events = NULL;
- tor_mutex_release(queued_control_events_lock);
- }
- if (queued_events) {
- SMARTLIST_FOREACH(queued_events, queued_event_t *, ev,
- queued_event_free(ev));
- smartlist_free(queued_events);
- }
- if (flush_queued_events_event) {
- mainloop_event_free(flush_queued_events_event);
- flush_queued_events_event = NULL;
- }
+ control_auth_free_all();
+ control_events_free_all();
+ control_cmd_free_all();
control_event_bootstrap_reset();
- authentication_cookie_is_set = 0;
- global_event_mask = 0;
- disable_log_messages = 0;
-}
-
-#ifdef TOR_UNIT_TESTS
-/* For testing: change the value of global_event_mask */
-void
-control_testing_set_global_event_mask(uint64_t mask)
-{
- global_event_mask = mask;
}
-#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/feature/control/control.h b/src/feature/control/control.h
index b2ab4c1997..8d3595d2ed 100644
--- a/src/feature/control/control.h
+++ b/src/feature/control/control.h
@@ -12,84 +12,6 @@
#ifndef TOR_CONTROL_H
#define TOR_CONTROL_H
-#include "core/or/ocirc_event.h"
-
-/** Used to indicate the type of a CIRC_MINOR event passed to the controller.
- * The various types are defined in control-spec.txt . */
-typedef enum circuit_status_minor_event_t {
- CIRC_MINOR_EVENT_PURPOSE_CHANGED,
- CIRC_MINOR_EVENT_CANNIBALIZED,
-} circuit_status_minor_event_t;
-
-#include "core/or/orconn_event.h"
-
-/** Used to indicate the type of a stream event passed to the controller.
- * The various types are defined in control-spec.txt */
-typedef enum stream_status_event_t {
- STREAM_EVENT_SENT_CONNECT = 0,
- STREAM_EVENT_SENT_RESOLVE = 1,
- STREAM_EVENT_SUCCEEDED = 2,
- STREAM_EVENT_FAILED = 3,
- STREAM_EVENT_CLOSED = 4,
- STREAM_EVENT_NEW = 5,
- STREAM_EVENT_NEW_RESOLVE = 6,
- STREAM_EVENT_FAILED_RETRIABLE = 7,
- STREAM_EVENT_REMAP = 8
-} stream_status_event_t;
-
-/** Used to indicate the type of a buildtime event */
-typedef enum buildtimeout_set_event_t {
- BUILDTIMEOUT_SET_EVENT_COMPUTED = 0,
- BUILDTIMEOUT_SET_EVENT_RESET = 1,
- BUILDTIMEOUT_SET_EVENT_SUSPENDED = 2,
- BUILDTIMEOUT_SET_EVENT_DISCARD = 3,
- BUILDTIMEOUT_SET_EVENT_RESUME = 4
-} buildtimeout_set_event_t;
-
-/** Enum describing various stages of bootstrapping, for use with controller
- * bootstrap status events. The values range from 0 to 100. */
-typedef enum {
- BOOTSTRAP_STATUS_UNDEF=-1,
- BOOTSTRAP_STATUS_STARTING=0,
-
- /* Initial connection to any relay */
-
- BOOTSTRAP_STATUS_CONN_PT=1,
- BOOTSTRAP_STATUS_CONN_DONE_PT=2,
- BOOTSTRAP_STATUS_CONN_PROXY=3,
- BOOTSTRAP_STATUS_CONN_DONE_PROXY=4,
- BOOTSTRAP_STATUS_CONN=5,
- BOOTSTRAP_STATUS_CONN_DONE=10,
- BOOTSTRAP_STATUS_HANDSHAKE=14,
- BOOTSTRAP_STATUS_HANDSHAKE_DONE=15,
-
- /* Loading directory info */
-
- BOOTSTRAP_STATUS_ONEHOP_CREATE=20,
- BOOTSTRAP_STATUS_REQUESTING_STATUS=25,
- BOOTSTRAP_STATUS_LOADING_STATUS=30,
- BOOTSTRAP_STATUS_LOADING_KEYS=40,
- BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS=45,
- BOOTSTRAP_STATUS_LOADING_DESCRIPTORS=50,
- BOOTSTRAP_STATUS_ENOUGH_DIRINFO=75,
-
- /* Connecting to a relay for AP circuits */
-
- BOOTSTRAP_STATUS_AP_CONN_PT=76,
- BOOTSTRAP_STATUS_AP_CONN_DONE_PT=77,
- BOOTSTRAP_STATUS_AP_CONN_PROXY=78,
- BOOTSTRAP_STATUS_AP_CONN_DONE_PROXY=79,
- BOOTSTRAP_STATUS_AP_CONN=80,
- BOOTSTRAP_STATUS_AP_CONN_DONE=85,
- BOOTSTRAP_STATUS_AP_HANDSHAKE=89,
- BOOTSTRAP_STATUS_AP_HANDSHAKE_DONE=90,
-
- /* Creating AP circuits */
-
- BOOTSTRAP_STATUS_CIRCUIT_CREATE=95,
- BOOTSTRAP_STATUS_DONE=100
-} bootstrap_status_t;
-
control_connection_t *TO_CONTROL_CONN(connection_t *);
#define CONTROL_CONN_STATE_MIN_ 1
@@ -100,18 +22,6 @@ control_connection_t *TO_CONTROL_CONN(connection_t *);
#define CONTROL_CONN_STATE_NEEDAUTH 2
#define CONTROL_CONN_STATE_MAX_ 2
-/** Reason for remapping an AP connection's address: we have a cached
- * answer. */
-#define REMAP_STREAM_SOURCE_CACHE 1
-/** Reason for remapping an AP connection's address: the exit node told us an
- * answer. */
-#define REMAP_STREAM_SOURCE_EXIT 2
-
-void control_initialize_event_queue(void);
-
-void control_update_global_event_mask(void);
-void control_adjust_event_log_severity(void);
-
void control_ports_write_to_file(void);
/** Log information about the connection <b>conn</b>, protecting it as with
@@ -132,300 +42,28 @@ void connection_control_closed(control_connection_t *conn);
int connection_control_process_inbuf(control_connection_t *conn);
-#define EVENT_NS 0x000F
-int control_event_is_interesting(int event);
-
-void control_per_second_events(void);
-int control_any_per_second_event_enabled(void);
-
-int control_event_circuit_status(origin_circuit_t *circ,
- circuit_status_event_t e, int reason);
-int control_event_circuit_purpose_changed(origin_circuit_t *circ,
- int old_purpose);
-int control_event_circuit_cannibalized(origin_circuit_t *circ,
- int old_purpose,
- const struct timeval *old_tv_created);
-int control_event_stream_status(entry_connection_t *conn,
- stream_status_event_t e,
- int reason);
-int control_event_or_conn_status(or_connection_t *conn,
- or_conn_status_event_t e, int reason);
-int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written);
-int control_event_stream_bandwidth(edge_connection_t *edge_conn);
-int control_event_stream_bandwidth_used(void);
-int control_event_circ_bandwidth_used(void);
-int control_event_circ_bandwidth_used_for_circ(origin_circuit_t *ocirc);
-int control_event_conn_bandwidth(connection_t *conn);
-int control_event_conn_bandwidth_used(void);
-int control_event_circuit_cell_stats(void);
-void control_event_logmsg(int severity, uint32_t domain, const char *msg);
-void control_event_logmsg_pending(void);
-int control_event_descriptors_changed(smartlist_t *routers);
-int control_event_address_mapped(const char *from, const char *to,
- time_t expires, const char *error,
- const int cached);
-int control_event_my_descriptor_changed(void);
-int control_event_network_liveness_update(int liveness);
-int control_event_networkstatus_changed(smartlist_t *statuses);
-
-int control_event_newconsensus(const networkstatus_t *consensus);
-int control_event_networkstatus_changed_single(const routerstatus_t *rs);
-int control_event_general_status(int severity, const char *format, ...)
- CHECK_PRINTF(2,3);
-int control_event_client_status(int severity, const char *format, ...)
- CHECK_PRINTF(2,3);
-int control_event_server_status(int severity, const char *format, ...)
- CHECK_PRINTF(2,3);
-
-int control_event_general_error(const char *format, ...)
- CHECK_PRINTF(1,2);
-int control_event_client_error(const char *format, ...)
- CHECK_PRINTF(1,2);
-int control_event_server_error(const char *format, ...)
- CHECK_PRINTF(1,2);
-
-int control_event_guard(const char *nickname, const char *digest,
- const char *status);
-int control_event_conf_changed(const smartlist_t *elements);
-int control_event_buildtimeout_set(buildtimeout_set_event_t type,
- const char *args);
-int control_event_signal(uintptr_t signal);
-
-int init_control_cookie_authentication(int enabled);
-char *get_controller_cookie_file_name(void);
-struct config_line_t;
-smartlist_t *decode_hashed_passwords(struct config_line_t *passwords);
void disable_control_logging(void);
void enable_control_logging(void);
void monitor_owning_controller_process(const char *process_spec);
-void control_event_bootstrap(bootstrap_status_t status, int progress);
-MOCK_DECL(void, control_event_bootstrap_prob_or,(const char *warn,
- int reason,
- or_connection_t *or_conn));
-void control_event_boot_dir(bootstrap_status_t status, int progress);
-void control_event_boot_first_orconn(void);
-void control_event_bootstrap_problem(const char *warn, const char *reason,
- const connection_t *conn, int dowarn);
-char *control_event_boot_last_msg(void);
-void control_event_bootstrap_reset(void);
-
-void control_event_clients_seen(const char *controller_str);
-void control_event_transport_launched(const char *mode,
- const char *transport_name,
- tor_addr_t *addr, uint16_t port);
-void control_event_pt_log(const char *log);
-void control_event_pt_status(const char *status);
const char *rend_auth_type_to_string(rend_auth_type_t auth_type);
-MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
-void control_event_hs_descriptor_requested(const char *onion_address,
- rend_auth_type_t auth_type,
- const char *id_digest,
- const char *desc_id,
- const char *hsdir_index);
-void control_event_hs_descriptor_created(const char *onion_address,
- const char *desc_id,
- int replica);
-void control_event_hs_descriptor_upload(const char *onion_address,
- const char *desc_id,
- const char *hs_dir,
- const char *hsdir_index);
-void control_event_hs_descriptor_upload_end(const char *action,
- const char *onion_address,
- const char *hs_dir,
- const char *reason);
-void control_event_hs_descriptor_uploaded(const char *hs_dir,
- const char *onion_address);
-/* Hidden service v2 HS_DESC specific. */
-void control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
- const char *id_digest,
- const char *reason);
-void control_event_hsv2_descriptor_received(const char *onion_address,
- const rend_data_t *rend_data,
- const char *id_digest);
-/* Hidden service v3 HS_DESC specific. */
-void control_event_hsv3_descriptor_failed(const char *onion_address,
- const char *desc_id,
- const char *hsdir_id_digest,
- const char *reason);
-void control_event_hsv3_descriptor_received(const char *onion_address,
- const char *desc_id,
- const char *hsdir_id_digest);
-void control_event_hs_descriptor_upload_failed(const char *hs_dir,
- const char *onion_address,
- const char *reason);
-void control_event_hs_descriptor_content(const char *onion_address,
- const char *desc_id,
- const char *hsdir_fp,
- const char *content);
void control_free_all(void);
-#ifdef CONTROL_PRIVATE
-#include "lib/crypt_ops/crypto_ed25519.h"
-
-/* Recognized asynchronous event types. It's okay to expand this list
- * because it is used both as a list of v0 event types, and as indices
- * into the bitfield to determine which controllers want which events.
- */
-/* This bitfield has no event zero 0x0000 */
-#define EVENT_MIN_ 0x0001
-#define EVENT_CIRCUIT_STATUS 0x0001
-#define EVENT_STREAM_STATUS 0x0002
-#define EVENT_OR_CONN_STATUS 0x0003
-#define EVENT_BANDWIDTH_USED 0x0004
-#define EVENT_CIRCUIT_STATUS_MINOR 0x0005
-#define EVENT_NEW_DESC 0x0006
-#define EVENT_DEBUG_MSG 0x0007
-#define EVENT_INFO_MSG 0x0008
-#define EVENT_NOTICE_MSG 0x0009
-#define EVENT_WARN_MSG 0x000A
-#define EVENT_ERR_MSG 0x000B
-#define EVENT_ADDRMAP 0x000C
-/* There was an AUTHDIR_NEWDESCS event, but it no longer exists. We
- can reclaim 0x000D. */
-#define EVENT_DESCCHANGED 0x000E
-/* Exposed above */
-// #define EVENT_NS 0x000F
-#define EVENT_STATUS_CLIENT 0x0010
-#define EVENT_STATUS_SERVER 0x0011
-#define EVENT_STATUS_GENERAL 0x0012
-#define EVENT_GUARD 0x0013
-#define EVENT_STREAM_BANDWIDTH_USED 0x0014
-#define EVENT_CLIENTS_SEEN 0x0015
-#define EVENT_NEWCONSENSUS 0x0016
-#define EVENT_BUILDTIMEOUT_SET 0x0017
-#define EVENT_GOT_SIGNAL 0x0018
-#define EVENT_CONF_CHANGED 0x0019
-#define EVENT_CONN_BW 0x001A
-#define EVENT_CELL_STATS 0x001B
-/* UNUSED : 0x001C */
-#define EVENT_CIRC_BANDWIDTH_USED 0x001D
-#define EVENT_TRANSPORT_LAUNCHED 0x0020
-#define EVENT_HS_DESC 0x0021
-#define EVENT_HS_DESC_CONTENT 0x0022
-#define EVENT_NETWORK_LIVENESS 0x0023
-#define EVENT_PT_LOG 0x0024
-#define EVENT_PT_STATUS 0x0025
-#define EVENT_MAX_ 0x0025
-
-/* sizeof(control_connection_t.event_mask) in bits, currently a uint64_t */
-#define EVENT_CAPACITY_ 0x0040
-
-/* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a
- * different structure, as it can only handle a maximum left shift of 1<<63. */
+#ifdef CONTROL_MODULE_PRIVATE
+struct signal_name_t {
+ int sig;
+ const char *signal_name;
+};
+extern const struct signal_name_t signal_table[];
+int get_cached_network_liveness(void);
+void set_cached_network_liveness(int liveness);
+#endif /* defined(CONTROL_MODULE_PRIVATE) */
-#if EVENT_MAX_ >= EVENT_CAPACITY_
-#error control_connection_t.event_mask has an event greater than its capacity
+#ifdef CONTROL_PRIVATE
+STATIC char *control_split_incoming_command(char *incoming_cmd,
+ size_t *data_len,
+ char **current_cmd_out);
#endif
-#define EVENT_MASK_(e) (((uint64_t)1)<<(e))
-
-#define EVENT_MASK_NONE_ ((uint64_t)0x0)
-
-#define EVENT_MASK_ABOVE_MIN_ ((~((uint64_t)0x0)) << EVENT_MIN_)
-#define EVENT_MASK_BELOW_MAX_ ((~((uint64_t)0x0)) \
- >> (EVENT_CAPACITY_ - EVENT_MAX_ \
- - EVENT_MIN_))
-
-#define EVENT_MASK_ALL_ (EVENT_MASK_ABOVE_MIN_ \
- & EVENT_MASK_BELOW_MAX_)
-
-/* Used only by control.c and test.c */
-STATIC size_t write_escaped_data(const char *data, size_t len, char **out);
-STATIC size_t read_escaped_data(const char *data, size_t len, char **out);
-
-#ifdef TOR_UNIT_TESTS
-MOCK_DECL(STATIC void,
- send_control_event_string,(uint16_t event, const char *msg));
-
-MOCK_DECL(STATIC void,
- queue_control_event_string,(uint16_t event, char *msg));
-
-void control_testing_set_global_event_mask(uint64_t mask);
-#endif /* defined(TOR_UNIT_TESTS) */
-
-/** Helper structure: temporarily stores cell statistics for a circuit. */
-typedef struct cell_stats_t {
- /** Number of cells added in app-ward direction by command. */
- uint64_t added_cells_appward[CELL_COMMAND_MAX_ + 1];
- /** Number of cells added in exit-ward direction by command. */
- uint64_t added_cells_exitward[CELL_COMMAND_MAX_ + 1];
- /** Number of cells removed in app-ward direction by command. */
- uint64_t removed_cells_appward[CELL_COMMAND_MAX_ + 1];
- /** Number of cells removed in exit-ward direction by command. */
- uint64_t removed_cells_exitward[CELL_COMMAND_MAX_ + 1];
- /** Total waiting time of cells in app-ward direction by command. */
- uint64_t total_time_appward[CELL_COMMAND_MAX_ + 1];
- /** Total waiting time of cells in exit-ward direction by command. */
- uint64_t total_time_exitward[CELL_COMMAND_MAX_ + 1];
-} cell_stats_t;
-void sum_up_cell_stats_by_command(circuit_t *circ,
- cell_stats_t *cell_stats);
-void append_cell_stats_by_command(smartlist_t *event_parts,
- const char *key,
- const uint64_t *include_if_non_zero,
- const uint64_t *number_to_include);
-void format_cell_stats(char **event_string, circuit_t *circ,
- cell_stats_t *cell_stats);
-STATIC char *get_bw_samples(void);
-
-/* ADD_ONION secret key to create an ephemeral service. The command supports
- * multiple versions so this union stores the key and passes it to the HS
- * subsystem depending on the requested version. */
-typedef union add_onion_secret_key_t {
- /* Hidden service v2 secret key. */
- crypto_pk_t *v2;
- /* Hidden service v3 secret key. */
- ed25519_secret_key_t *v3;
-} add_onion_secret_key_t;
-
-STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk,
- const char **key_new_alg_out,
- char **key_new_blob_out,
- add_onion_secret_key_t *decoded_key,
- int *hs_version, char **err_msg_out);
-
-STATIC rend_authorized_client_t *
-add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out);
-
-STATIC int getinfo_helper_onions(
- control_connection_t *control_conn,
- const char *question,
- char **answer,
- const char **errmsg);
-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);
-STATIC int getinfo_helper_dir(
- control_connection_t *control_conn,
- const char *question, char **answer,
- const char **errmsg);
-STATIC int getinfo_helper_current_time(
- control_connection_t *control_conn,
- const char *question, char **answer,
- const char **errmsg);
-
-#endif /* defined(CONTROL_PRIVATE) */
-
#endif /* !defined(TOR_CONTROL_H) */
diff --git a/src/feature/control/control_auth.c b/src/feature/control/control_auth.c
new file mode 100644
index 0000000000..49d4d415c6
--- /dev/null
+++ b/src/feature/control/control_auth.c
@@ -0,0 +1,445 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_auth.c
+ * \brief Authentication for Tor's control-socket interface.
+ **/
+
+#include "core/or/or.h"
+#include "app/config/config.h"
+#include "core/mainloop/connection.h"
+#include "feature/control/control.h"
+#include "feature/control/control_cmd.h"
+#include "feature/control/control_auth.h"
+#include "feature/control/control_cmd_args_st.h"
+#include "feature/control/control_connection_st.h"
+#include "feature/control/control_proto.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/encoding/confline.h"
+#include "lib/encoding/kvline.h"
+#include "lib/encoding/qstring.h"
+
+#include "lib/crypt_ops/crypto_s2k.h"
+
+/** If we're using cookie-type authentication, how long should our cookies be?
+ */
+#define AUTHENTICATION_COOKIE_LEN 32
+
+/** If true, we've set authentication_cookie to a secret code and
+ * stored it to disk. */
+static int authentication_cookie_is_set = 0;
+/** If authentication_cookie_is_set, a secret cookie that we've stored to disk
+ * and which we're using to authenticate controllers. (If the controller can
+ * read it off disk, it has permission to connect.) */
+static uint8_t *authentication_cookie = NULL;
+
+#define SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT \
+ "Tor safe cookie authentication server-to-controller hash"
+#define SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT \
+ "Tor safe cookie authentication controller-to-server hash"
+#define SAFECOOKIE_SERVER_NONCE_LEN DIGEST256_LEN
+
+/** Helper: Return a newly allocated string containing a path to the
+ * file where we store our authentication cookie. */
+char *
+get_controller_cookie_file_name(void)
+{
+ const or_options_t *options = get_options();
+ if (options->CookieAuthFile && strlen(options->CookieAuthFile)) {
+ return tor_strdup(options->CookieAuthFile);
+ } else {
+ return get_datadir_fname("control_auth_cookie");
+ }
+}
+
+/* Initialize the cookie-based authentication system of the
+ * ControlPort. If <b>enabled</b> is 0, then disable the cookie
+ * authentication system. */
+int
+init_control_cookie_authentication(int enabled)
+{
+ char *fname = NULL;
+ int retval;
+
+ if (!enabled) {
+ authentication_cookie_is_set = 0;
+ return 0;
+ }
+
+ fname = get_controller_cookie_file_name();
+ retval = init_cookie_authentication(fname, "", /* no header */
+ AUTHENTICATION_COOKIE_LEN,
+ get_options()->CookieAuthFileGroupReadable,
+ &authentication_cookie,
+ &authentication_cookie_is_set);
+ tor_free(fname);
+ return retval;
+}
+
+/** Decode the hashed, base64'd passwords stored in <b>passwords</b>.
+ * Return a smartlist of acceptable passwords (unterminated strings of
+ * length S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN) on success, or NULL on
+ * failure.
+ */
+smartlist_t *
+decode_hashed_passwords(config_line_t *passwords)
+{
+ char decoded[64];
+ config_line_t *cl;
+ smartlist_t *sl = smartlist_new();
+
+ tor_assert(passwords);
+
+ for (cl = passwords; cl; cl = cl->next) {
+ const char *hashed = cl->value;
+
+ if (!strcmpstart(hashed, "16:")) {
+ 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;
+ }
+ } else {
+ if (base64_decode(decoded, sizeof(decoded), hashed, strlen(hashed))
+ != S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN) {
+ goto err;
+ }
+ }
+ smartlist_add(sl,
+ tor_memdup(decoded, S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN));
+ }
+
+ return sl;
+
+ err:
+ SMARTLIST_FOREACH(sl, char*, cp, tor_free(cp));
+ smartlist_free(sl);
+ return NULL;
+}
+
+const control_cmd_syntax_t authchallenge_syntax = {
+ .min_args = 1,
+ .max_args = 1,
+ .accept_keywords=true,
+ .kvline_flags=KV_OMIT_KEYS|KV_QUOTED_QSTRING,
+ .store_raw_body=true
+};
+
+/** Called when we get an AUTHCHALLENGE command. */
+int
+handle_control_authchallenge(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ char *client_nonce;
+ size_t client_nonce_len;
+ char server_hash[DIGEST256_LEN];
+ char server_hash_encoded[HEX_DIGEST256_LEN+1];
+ char server_nonce[SAFECOOKIE_SERVER_NONCE_LEN];
+ char server_nonce_encoded[(2*SAFECOOKIE_SERVER_NONCE_LEN) + 1];
+
+ if (strcasecmp(smartlist_get(args->args, 0), "SAFECOOKIE")) {
+ control_write_endreply(conn, 513,
+ "AUTHCHALLENGE only supports SAFECOOKIE "
+ "authentication");
+ goto fail;
+ }
+ if (!authentication_cookie_is_set) {
+ control_write_endreply(conn, 515, "Cookie authentication is disabled");
+ goto fail;
+ }
+ if (args->kwargs == NULL || args->kwargs->next != NULL) {
+ /* connection_write_str_to_buf("512 AUTHCHALLENGE requires exactly "
+ "2 arguments.\r\n", conn);
+ */
+ control_printf_endreply(conn, 512,
+ "AUTHCHALLENGE dislikes argument list %s",
+ escaped(args->raw_body));
+ goto fail;
+ }
+ if (strcmp(args->kwargs->key, "")) {
+ control_write_endreply(conn, 512,
+ "AUTHCHALLENGE does not accept keyword "
+ "arguments.");
+ goto fail;
+ }
+
+ bool contains_quote = strchr(args->raw_body, '\"');
+ if (contains_quote) {
+ /* The nonce was quoted */
+ client_nonce = tor_strdup(args->kwargs->value);
+ client_nonce_len = strlen(client_nonce);
+ } else {
+ /* The nonce was should be in hex. */
+ const char *hex_nonce = args->kwargs->value;
+ client_nonce_len = strlen(hex_nonce) / 2;
+ client_nonce = tor_malloc(client_nonce_len);
+ if (base16_decode(client_nonce, client_nonce_len, hex_nonce,
+ strlen(hex_nonce)) != (int)client_nonce_len) {
+ control_write_endreply(conn, 513, "Invalid base16 client nonce");
+ tor_free(client_nonce);
+ goto fail;
+ }
+ }
+
+ crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
+
+ /* Now compute and send the server-to-controller response, and the
+ * server's nonce. */
+ tor_assert(authentication_cookie != NULL);
+
+ {
+ size_t tmp_len = (AUTHENTICATION_COOKIE_LEN +
+ client_nonce_len +
+ SAFECOOKIE_SERVER_NONCE_LEN);
+ char *tmp = tor_malloc_zero(tmp_len);
+ char *client_hash = tor_malloc_zero(DIGEST256_LEN);
+ memcpy(tmp, authentication_cookie, AUTHENTICATION_COOKIE_LEN);
+ memcpy(tmp + AUTHENTICATION_COOKIE_LEN, client_nonce, client_nonce_len);
+ memcpy(tmp + AUTHENTICATION_COOKIE_LEN + client_nonce_len,
+ server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
+
+ crypto_hmac_sha256(server_hash,
+ SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT,
+ strlen(SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT),
+ tmp,
+ tmp_len);
+
+ crypto_hmac_sha256(client_hash,
+ SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT,
+ strlen(SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT),
+ tmp,
+ tmp_len);
+
+ conn->safecookie_client_hash = client_hash;
+
+ tor_free(tmp);
+ }
+
+ base16_encode(server_hash_encoded, sizeof(server_hash_encoded),
+ server_hash, sizeof(server_hash));
+ base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded),
+ server_nonce, sizeof(server_nonce));
+
+ control_printf_endreply(conn, 250,
+ "AUTHCHALLENGE SERVERHASH=%s SERVERNONCE=%s",
+ server_hash_encoded,
+ server_nonce_encoded);
+
+ tor_free(client_nonce);
+ return 0;
+ fail:
+ connection_mark_for_close(TO_CONN(conn));
+ return -1;
+}
+
+const control_cmd_syntax_t authenticate_syntax = {
+ .max_args = 0,
+ .accept_keywords=true,
+ .kvline_flags=KV_OMIT_KEYS|KV_QUOTED_QSTRING,
+ .store_raw_body=true
+};
+
+/** Called when we get an AUTHENTICATE message. Check whether the
+ * authentication is valid, and if so, update the connection's state to
+ * OPEN. Reply with DONE or ERROR.
+ */
+int
+handle_control_authenticate(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ bool used_quoted_string = false;
+ const or_options_t *options = get_options();
+ const char *errstr = "Unknown error";
+ char *password;
+ size_t password_len;
+ int bad_cookie=0, bad_password=0;
+ smartlist_t *sl = NULL;
+
+ if (args->kwargs == NULL) {
+ password = tor_strdup("");
+ password_len = 0;
+ } else if (args->kwargs->next) {
+ control_write_endreply(conn, 512, "Too many arguments to AUTHENTICATE.");
+ connection_mark_for_close(TO_CONN(conn));
+ return 0;
+ } else if (strcmp(args->kwargs->key, "")) {
+ control_write_endreply(conn, 512,
+ "AUTHENTICATE does not accept keyword arguments.");
+ connection_mark_for_close(TO_CONN(conn));
+ return 0;
+ } else if (strchr(args->raw_body, '\"')) {
+ used_quoted_string = true;
+ password = tor_strdup(args->kwargs->value);
+ password_len = strlen(password);
+ } else {
+ const char *hex_passwd = args->kwargs->value;
+ password_len = strlen(hex_passwd) / 2;
+ password = tor_malloc(password_len+1);
+ if (base16_decode(password, password_len+1, hex_passwd, strlen(hex_passwd))
+ != (int) password_len) {
+ control_write_endreply(conn, 551,
+ "Invalid hexadecimal encoding. Maybe you tried a plain text "
+ "password? If so, the standard requires that you put it in "
+ "double quotes.");
+ connection_mark_for_close(TO_CONN(conn));
+ tor_free(password);
+ return 0;
+ }
+ }
+
+ if (conn->safecookie_client_hash != NULL) {
+ /* The controller has chosen safe cookie authentication; the only
+ * acceptable authentication value is the controller-to-server
+ * response. */
+
+ tor_assert(authentication_cookie_is_set);
+
+ if (password_len != DIGEST256_LEN) {
+ log_warn(LD_CONTROL,
+ "Got safe cookie authentication response with wrong length "
+ "(%d)", (int)password_len);
+ errstr = "Wrong length for safe cookie response.";
+ goto err;
+ }
+
+ if (tor_memneq(conn->safecookie_client_hash, password, DIGEST256_LEN)) {
+ log_warn(LD_CONTROL,
+ "Got incorrect safe cookie authentication response");
+ errstr = "Safe cookie response did not match expected value.";
+ goto err;
+ }
+
+ tor_free(conn->safecookie_client_hash);
+ goto ok;
+ }
+
+ if (!options->CookieAuthentication && !options->HashedControlPassword &&
+ !options->HashedControlSessionPassword) {
+ /* if Tor doesn't demand any stronger authentication, then
+ * the controller can get in with anything. */
+ goto ok;
+ }
+
+ if (options->CookieAuthentication) {
+ int also_password = options->HashedControlPassword != NULL ||
+ options->HashedControlSessionPassword != NULL;
+ if (password_len != AUTHENTICATION_COOKIE_LEN) {
+ if (!also_password) {
+ log_warn(LD_CONTROL, "Got authentication cookie with wrong length "
+ "(%d)", (int)password_len);
+ errstr = "Wrong length on authentication cookie.";
+ goto err;
+ }
+ bad_cookie = 1;
+ } else if (tor_memneq(authentication_cookie, password, password_len)) {
+ if (!also_password) {
+ log_warn(LD_CONTROL, "Got mismatched authentication cookie");
+ errstr = "Authentication cookie did not match expected value.";
+ goto err;
+ }
+ bad_cookie = 1;
+ } else {
+ goto ok;
+ }
+ }
+
+ if (options->HashedControlPassword ||
+ options->HashedControlSessionPassword) {
+ int bad = 0;
+ smartlist_t *sl_tmp;
+ char received[DIGEST_LEN];
+ int also_cookie = options->CookieAuthentication;
+ sl = smartlist_new();
+ if (options->HashedControlPassword) {
+ sl_tmp = decode_hashed_passwords(options->HashedControlPassword);
+ if (!sl_tmp)
+ bad = 1;
+ else {
+ smartlist_add_all(sl, sl_tmp);
+ smartlist_free(sl_tmp);
+ }
+ }
+ if (options->HashedControlSessionPassword) {
+ sl_tmp = decode_hashed_passwords(options->HashedControlSessionPassword);
+ if (!sl_tmp)
+ bad = 1;
+ else {
+ smartlist_add_all(sl, sl_tmp);
+ smartlist_free(sl_tmp);
+ }
+ }
+ if (bad) {
+ if (!also_cookie) {
+ log_warn(LD_BUG,
+ "Couldn't decode HashedControlPassword: invalid base16");
+ errstr="Couldn't decode HashedControlPassword value in configuration.";
+ goto err;
+ }
+ bad_password = 1;
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
+ smartlist_free(sl);
+ sl = NULL;
+ } else {
+ SMARTLIST_FOREACH(sl, char *, expected,
+ {
+ secret_to_key_rfc2440(received,DIGEST_LEN,
+ password,password_len,expected);
+ if (tor_memeq(expected + S2K_RFC2440_SPECIFIER_LEN,
+ received, DIGEST_LEN))
+ goto ok;
+ });
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
+ smartlist_free(sl);
+ sl = NULL;
+
+ if (used_quoted_string)
+ errstr = "Password did not match HashedControlPassword value from "
+ "configuration";
+ else
+ errstr = "Password did not match HashedControlPassword value from "
+ "configuration. Maybe you tried a plain text password? "
+ "If so, the standard requires that you put it in double quotes.";
+ bad_password = 1;
+ if (!also_cookie)
+ goto err;
+ }
+ }
+
+ /** We only get here if both kinds of authentication failed. */
+ tor_assert(bad_password && bad_cookie);
+ log_warn(LD_CONTROL, "Bad password or authentication cookie on controller.");
+ errstr = "Password did not match HashedControlPassword *or* authentication "
+ "cookie.";
+
+ err:
+ tor_free(password);
+ control_printf_endreply(conn, 515, "Authentication failed: %s", errstr);
+ connection_mark_for_close(TO_CONN(conn));
+ if (sl) { /* clean up */
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
+ smartlist_free(sl);
+ }
+ return 0;
+ ok:
+ log_info(LD_CONTROL, "Authenticated control connection ("TOR_SOCKET_T_FORMAT
+ ")", conn->base_.s);
+ send_control_done(conn);
+ conn->base_.state = CONTROL_CONN_STATE_OPEN;
+ tor_free(password);
+ if (sl) { /* clean up */
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
+ smartlist_free(sl);
+ }
+ return 0;
+}
+
+void
+control_auth_free_all(void)
+{
+ if (authentication_cookie) /* Free the auth cookie */
+ tor_free(authentication_cookie);
+ authentication_cookie_is_set = 0;
+}
diff --git a/src/feature/control/control_auth.h b/src/feature/control/control_auth.h
new file mode 100644
index 0000000000..246e18ccbc
--- /dev/null
+++ b/src/feature/control/control_auth.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_auth.h
+ * \brief Header file for control_auth.c.
+ **/
+
+#ifndef TOR_CONTROL_AUTH_H
+#define TOR_CONTROL_AUTH_H
+
+struct control_cmd_args_t;
+struct control_cmd_syntax_t;
+
+int init_control_cookie_authentication(int enabled);
+char *get_controller_cookie_file_name(void);
+struct config_line_t;
+smartlist_t *decode_hashed_passwords(struct config_line_t *passwords);
+
+int handle_control_authchallenge(control_connection_t *conn,
+ const struct control_cmd_args_t *args);
+int handle_control_authenticate(control_connection_t *conn,
+ const struct control_cmd_args_t *args);
+void control_auth_free_all(void);
+
+extern const struct control_cmd_syntax_t authchallenge_syntax;
+extern const struct control_cmd_syntax_t authenticate_syntax;
+
+#endif /* !defined(TOR_CONTROL_AUTH_H) */
diff --git a/src/feature/control/control_bootstrap.c b/src/feature/control/control_bootstrap.c
index 8153d7595a..098e24682e 100644
--- a/src/feature/control/control_bootstrap.c
+++ b/src/feature/control/control_bootstrap.c
@@ -14,7 +14,7 @@
#include "core/or/connection_st.h"
#include "core/or/or_connection_st.h"
#include "core/or/reasons.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/hibernate/hibernate.h"
#include "lib/malloc/malloc.h"
diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c
new file mode 100644
index 0000000000..abb579bd43
--- /dev/null
+++ b/src/feature/control/control_cmd.c
@@ -0,0 +1,2408 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_cmd.c
+ * \brief Implement various commands for Tor's control-socket interface.
+ **/
+
+#define CONTROL_MODULE_PRIVATE
+#define CONTROL_CMD_PRIVATE
+#define CONTROL_EVENTS_PRIVATE
+
+#include "core/or/or.h"
+#include "app/config/config.h"
+#include "app/config/confparse.h"
+#include "app/main/main.h"
+#include "core/mainloop/connection.h"
+#include "core/or/circuitbuild.h"
+#include "core/or/circuitlist.h"
+#include "core/or/circuituse.h"
+#include "core/or/connection_edge.h"
+#include "feature/client/addressmap.h"
+#include "feature/client/dnsserv.h"
+#include "feature/client/entrynodes.h"
+#include "feature/control/control.h"
+#include "feature/control/control_auth.h"
+#include "feature/control/control_cmd.h"
+#include "feature/control/control_events.h"
+#include "feature/control/control_getinfo.h"
+#include "feature/control/control_proto.h"
+#include "feature/hs/hs_control.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerinfo.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/rend/rendclient.h"
+#include "feature/rend/rendcommon.h"
+#include "feature/rend/rendparse.h"
+#include "feature/rend/rendservice.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/encoding/confline.h"
+#include "lib/encoding/kvline.h"
+
+#include "core/or/cpath_build_state_st.h"
+#include "core/or/entry_connection_st.h"
+#include "core/or/origin_circuit_st.h"
+#include "core/or/socks_request_st.h"
+#include "feature/control/control_cmd_args_st.h"
+#include "feature/control/control_connection_st.h"
+#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/rend/rend_authorized_client_st.h"
+#include "feature/rend/rend_encoded_v2_service_descriptor_st.h"
+#include "feature/rend/rend_service_descriptor_st.h"
+
+static int control_setconf_helper(control_connection_t *conn,
+ const control_cmd_args_t *args,
+ int use_defaults);
+
+/** Yield true iff <b>s</b> is the state of a control_connection_t that has
+ * finished authentication and is accepting commands. */
+#define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN)
+
+/**
+ * Release all storage held in <b>args</b>
+ **/
+void
+control_cmd_args_free_(control_cmd_args_t *args)
+{
+ if (! args)
+ return;
+
+ if (args->args) {
+ SMARTLIST_FOREACH(args->args, char *, c, tor_free(c));
+ smartlist_free(args->args);
+ }
+ config_free_lines(args->kwargs);
+ tor_free(args->cmddata);
+
+ tor_free(args);
+}
+
+/** Erase all memory held in <b>args</b>. */
+void
+control_cmd_args_wipe(control_cmd_args_t *args)
+{
+ if (!args)
+ return;
+
+ if (args->args) {
+ SMARTLIST_FOREACH(args->args, char *, c, memwipe(c, 0, strlen(c)));
+ }
+ for (config_line_t *line = args->kwargs; line; line = line->next) {
+ memwipe(line->key, 0, strlen(line->key));
+ memwipe(line->value, 0, strlen(line->value));
+ }
+ if (args->cmddata)
+ memwipe(args->cmddata, 0, args->cmddata_len);
+}
+
+/**
+ * Return true iff any element of the NULL-terminated <b>array</b> matches
+ * <b>kwd</b>. Case-insensitive.
+ **/
+static bool
+string_array_contains_keyword(const char **array, const char *kwd)
+{
+ for (unsigned i = 0; array[i]; ++i) {
+ if (! strcasecmp(array[i], kwd))
+ return true;
+ }
+ return false;
+}
+
+/** Helper for argument parsing: check whether the keyword arguments just
+ * parsed in <b>result</b> were well-formed according to <b>syntax</b>.
+ *
+ * On success, return 0. On failure, return -1 and set *<b>error_out</b>
+ * to a newly allocated error string.
+ **/
+static int
+kvline_check_keyword_args(const control_cmd_args_t *result,
+ const control_cmd_syntax_t *syntax,
+ char **error_out)
+{
+ if (result->kwargs == NULL) {
+ tor_asprintf(error_out, "Cannot parse keyword argument(s)");
+ return -1;
+ }
+
+ if (! syntax->allowed_keywords) {
+ /* All keywords are permitted. */
+ return 0;
+ }
+
+ /* Check for unpermitted arguments */
+ const config_line_t *line;
+ for (line = result->kwargs; line; line = line->next) {
+ if (! string_array_contains_keyword(syntax->allowed_keywords,
+ line->key)) {
+ tor_asprintf(error_out, "Unrecognized keyword argument %s",
+ escaped(line->key));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Helper: parse the arguments to a command according to <b>syntax</b>. On
+ * success, set *<b>error_out</b> to NULL and return a newly allocated
+ * control_cmd_args_t. On failure, set *<b>error_out</b> to newly allocated
+ * error string, and return NULL.
+ **/
+STATIC control_cmd_args_t *
+control_cmd_parse_args(const char *command,
+ const control_cmd_syntax_t *syntax,
+ size_t body_len,
+ const char *body,
+ char **error_out)
+{
+ *error_out = NULL;
+ control_cmd_args_t *result = tor_malloc_zero(sizeof(control_cmd_args_t));
+ const char *cmdline;
+ char *cmdline_alloc = NULL;
+ tor_assert(syntax->max_args < INT_MAX || syntax->max_args == UINT_MAX);
+
+ result->command = command;
+
+ if (syntax->store_raw_body) {
+ tor_assert(body[body_len] == 0);
+ result->raw_body = body;
+ }
+
+ const char *eol = memchr(body, '\n', body_len);
+ if (syntax->want_cmddata) {
+ if (! eol || (eol+1) == body+body_len) {
+ *error_out = tor_strdup("Empty body");
+ goto err;
+ }
+ cmdline_alloc = tor_memdup_nulterm(body, eol-body);
+ cmdline = cmdline_alloc;
+ ++eol;
+ result->cmddata_len = read_escaped_data(eol, (body+body_len)-eol,
+ &result->cmddata);
+ } else {
+ if (eol && (eol+1) != body+body_len) {
+ *error_out = tor_strdup("Unexpected body");
+ goto err;
+ }
+ cmdline = body;
+ }
+
+ result->args = smartlist_new();
+ smartlist_split_string(result->args, cmdline, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK,
+ (int)(syntax->max_args+1));
+ size_t n_args = smartlist_len(result->args);
+ if (n_args < syntax->min_args) {
+ tor_asprintf(error_out, "Need at least %u argument(s)",
+ syntax->min_args);
+ goto err;
+ } else if (n_args > syntax->max_args && ! syntax->accept_keywords) {
+ tor_asprintf(error_out, "Cannot accept more than %u argument(s)",
+ syntax->max_args);
+ goto err;
+ }
+
+ if (n_args > syntax->max_args) {
+ /* We have extra arguments after the positional arguments, and we didn't
+ treat them as an error, so they must count as keyword arguments: Either
+ K=V pairs, or flags, or both. */
+ tor_assert(n_args == syntax->max_args + 1);
+ tor_assert(syntax->accept_keywords);
+ char *remainder = smartlist_pop_last(result->args);
+ result->kwargs = kvline_parse(remainder, syntax->kvline_flags);
+ tor_free(remainder);
+ if (kvline_check_keyword_args(result, syntax, error_out) < 0) {
+ goto err;
+ }
+ }
+
+ tor_assert_nonfatal(*error_out == NULL);
+ goto done;
+ err:
+ tor_assert_nonfatal(*error_out != NULL);
+ control_cmd_args_free(result);
+ done:
+ tor_free(cmdline_alloc);
+ return result;
+}
+
+/**
+ * Return true iff <b>lines</b> contains <b>flags</b> as a no-value
+ * (keyword-only) entry.
+ **/
+static bool
+config_lines_contain_flag(const config_line_t *lines, const char *flag)
+{
+ const config_line_t *line = config_line_find_case(lines, flag);
+ return line && !strcmp(line->value, "");
+}
+
+static const control_cmd_syntax_t setconf_syntax = {
+ .max_args=0,
+ .accept_keywords=true,
+ .kvline_flags=KV_OMIT_VALS|KV_QUOTED,
+};
+
+/** Called when we receive a SETCONF message: parse the body and try
+ * to update our configuration. Reply with a DONE or ERROR message.
+ * Modifies the contents of body.*/
+static int
+handle_control_setconf(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ return control_setconf_helper(conn, args, 0);
+}
+
+static const control_cmd_syntax_t resetconf_syntax = {
+ .max_args=0,
+ .accept_keywords=true,
+ .kvline_flags=KV_OMIT_VALS|KV_QUOTED,
+};
+
+/** Called when we receive a RESETCONF message: parse the body and try
+ * to update our configuration. Reply with a DONE or ERROR message.
+ * Modifies the contents of body. */
+static int
+handle_control_resetconf(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ return control_setconf_helper(conn, args, 1);
+}
+
+static const control_cmd_syntax_t getconf_syntax = {
+ .max_args=UINT_MAX
+};
+
+/** Called when we receive a GETCONF message. Parse the request, and
+ * reply with a CONFVALUE or an ERROR message */
+static int
+handle_control_getconf(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ const smartlist_t *questions = args->args;
+ smartlist_t *answers = smartlist_new();
+ smartlist_t *unrecognized = smartlist_new();
+ char *msg = NULL;
+ size_t msg_len;
+ const or_options_t *options = get_options();
+ int i, len;
+
+ SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
+ if (!option_is_recognized(q)) {
+ smartlist_add(unrecognized, (char*) q);
+ } else {
+ config_line_t *answer = option_get_assignment(options,q);
+ if (!answer) {
+ const char *name = option_get_canonical_name(q);
+ smartlist_add_asprintf(answers, "250-%s\r\n", name);
+ }
+
+ while (answer) {
+ config_line_t *next;
+ smartlist_add_asprintf(answers, "250-%s=%s\r\n",
+ answer->key, answer->value);
+
+ next = answer->next;
+ tor_free(answer->key);
+ tor_free(answer->value);
+ tor_free(answer);
+ answer = next;
+ }
+ }
+ } SMARTLIST_FOREACH_END(q);
+
+ if ((len = smartlist_len(unrecognized))) {
+ for (i=0; i < len-1; ++i)
+ control_printf_midreply(conn, 552,
+ "Unrecognized configuration key \"%s\"",
+ (char*)smartlist_get(unrecognized, i));
+ control_printf_endreply(conn, 552,
+ "Unrecognized configuration key \"%s\"",
+ (char*)smartlist_get(unrecognized, len-1));
+ } else if ((len = smartlist_len(answers))) {
+ char *tmp = smartlist_get(answers, len-1);
+ tor_assert(strlen(tmp)>4);
+ tmp[3] = ' ';
+ msg = smartlist_join_strings(answers, "", 0, &msg_len);
+ connection_buf_add(msg, msg_len, TO_CONN(conn));
+ } else {
+ send_control_done(conn);
+ }
+
+ SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp));
+ smartlist_free(answers);
+ smartlist_free(unrecognized);
+
+ tor_free(msg);
+
+ return 0;
+}
+
+static const control_cmd_syntax_t loadconf_syntax = {
+ .want_cmddata = true
+};
+
+/** Called when we get a +LOADCONF message. */
+static int
+handle_control_loadconf(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ setopt_err_t retval;
+ char *errstring = NULL;
+
+ retval = options_init_from_string(NULL, args->cmddata,
+ CMD_RUN_TOR, NULL, &errstring);
+
+ if (retval != SETOPT_OK)
+ log_warn(LD_CONTROL,
+ "Controller gave us config file that didn't validate: %s",
+ errstring);
+
+#define SEND_ERRMSG(code, msg) \
+ control_printf_endreply(conn, code, msg "%s%s", \
+ errstring ? ": " : "", \
+ errstring ? errstring : "")
+ switch (retval) {
+ case SETOPT_ERR_PARSE:
+ SEND_ERRMSG(552, "Invalid config file");
+ break;
+ case SETOPT_ERR_TRANSITION:
+ SEND_ERRMSG(553, "Transition not allowed");
+ break;
+ case SETOPT_ERR_SETTING:
+ SEND_ERRMSG(553, "Unable to set option");
+ break;
+ case SETOPT_ERR_MISC:
+ default:
+ SEND_ERRMSG(550, "Unable to load config");
+ break;
+ case SETOPT_OK:
+ send_control_done(conn);
+ break;
+ }
+#undef SEND_ERRMSG
+ tor_free(errstring);
+ return 0;
+}
+
+static const control_cmd_syntax_t setevents_syntax = {
+ .max_args = UINT_MAX
+};
+
+/** Called when we get a SETEVENTS message: update conn->event_mask,
+ * and reply with DONE or ERROR. */
+static int
+handle_control_setevents(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ int event_code;
+ event_mask_t event_mask = 0;
+ const smartlist_t *events = args->args;
+
+ SMARTLIST_FOREACH_BEGIN(events, const char *, ev)
+ {
+ if (!strcasecmp(ev, "EXTENDED") ||
+ !strcasecmp(ev, "AUTHDIR_NEWDESCS")) {
+ log_warn(LD_CONTROL, "The \"%s\" SETEVENTS argument is no longer "
+ "supported.", ev);
+ continue;
+ } else {
+ int i;
+ event_code = -1;
+
+ for (i = 0; control_event_table[i].event_name != NULL; ++i) {
+ if (!strcasecmp(ev, control_event_table[i].event_name)) {
+ event_code = control_event_table[i].event_code;
+ break;
+ }
+ }
+
+ if (event_code == -1) {
+ control_printf_endreply(conn, 552, "Unrecognized event \"%s\"", ev);
+ return 0;
+ }
+ }
+ event_mask |= (((event_mask_t)1) << event_code);
+ }
+ SMARTLIST_FOREACH_END(ev);
+
+ conn->event_mask = event_mask;
+
+ control_update_global_event_mask();
+ send_control_done(conn);
+ return 0;
+}
+
+static const control_cmd_syntax_t saveconf_syntax = {
+ .max_args = 0,
+ .accept_keywords = true,
+ .kvline_flags=KV_OMIT_VALS,
+};
+
+/** Called when we get a SAVECONF command. Try to flush the current options to
+ * disk, and report success or failure. */
+static int
+handle_control_saveconf(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ bool force = config_lines_contain_flag(args->kwargs, "FORCE");
+ const or_options_t *options = get_options();
+ if ((!force && options->IncludeUsed) || options_save_current() < 0) {
+ control_write_endreply(conn, 551,
+ "Unable to write configuration to disk.");
+ } else {
+ send_control_done(conn);
+ }
+ return 0;
+}
+
+static const control_cmd_syntax_t signal_syntax = {
+ .min_args = 1,
+ .max_args = 1,
+};
+
+/** Called when we get a SIGNAL command. React to the provided signal, and
+ * report success or failure. (If the signal results in a shutdown, success
+ * may not be reported.) */
+static int
+handle_control_signal(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ int sig = -1;
+ int i;
+
+ tor_assert(smartlist_len(args->args) == 1);
+ const char *s = smartlist_get(args->args, 0);
+
+ for (i = 0; signal_table[i].signal_name != NULL; ++i) {
+ if (!strcasecmp(s, signal_table[i].signal_name)) {
+ sig = signal_table[i].sig;
+ break;
+ }
+ }
+
+ if (sig < 0)
+ control_printf_endreply(conn, 552, "Unrecognized signal code \"%s\"", s);
+ if (sig < 0)
+ return 0;
+
+ send_control_done(conn);
+ /* Flush the "done" first if the signal might make us shut down. */
+ if (sig == SIGTERM || sig == SIGINT)
+ connection_flush(TO_CONN(conn));
+
+ activate_signal(sig);
+
+ return 0;
+}
+
+static const control_cmd_syntax_t takeownership_syntax = {
+ .max_args = UINT_MAX, // This should probably become zero. XXXXX
+};
+
+/** Called when we get a TAKEOWNERSHIP command. Mark this connection
+ * as an owning connection, so that we will exit if the connection
+ * closes. */
+static int
+handle_control_takeownership(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ (void)args;
+
+ conn->is_owning_control_connection = 1;
+
+ log_info(LD_CONTROL, "Control connection %d has taken ownership of this "
+ "Tor instance.",
+ (int)(conn->base_.s));
+
+ send_control_done(conn);
+ return 0;
+}
+
+static const control_cmd_syntax_t dropownership_syntax = {
+ .max_args = UINT_MAX, // This should probably become zero. XXXXX
+};
+
+/** Called when we get a DROPOWNERSHIP command. Mark this connection
+ * as a non-owning connection, so that we will not exit if the connection
+ * closes. */
+static int
+handle_control_dropownership(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ (void)args;
+
+ conn->is_owning_control_connection = 0;
+
+ log_info(LD_CONTROL, "Control connection %d has dropped ownership of this "
+ "Tor instance.",
+ (int)(conn->base_.s));
+
+ send_control_done(conn);
+ return 0;
+}
+
+/** Given a text circuit <b>id</b>, return the corresponding circuit. */
+static origin_circuit_t *
+get_circ(const char *id)
+{
+ uint32_t n_id;
+ int ok;
+ n_id = (uint32_t) tor_parse_ulong(id, 10, 0, UINT32_MAX, &ok, NULL);
+ if (!ok)
+ return NULL;
+ return circuit_get_by_global_id(n_id);
+}
+
+/** Given a text stream <b>id</b>, return the corresponding AP connection. */
+static entry_connection_t *
+get_stream(const char *id)
+{
+ uint64_t n_id;
+ int ok;
+ connection_t *conn;
+ n_id = tor_parse_uint64(id, 10, 0, UINT64_MAX, &ok, NULL);
+ if (!ok)
+ return NULL;
+ conn = connection_get_by_global_id(n_id);
+ if (!conn || conn->type != CONN_TYPE_AP || conn->marked_for_close)
+ return NULL;
+ return TO_ENTRY_CONN(conn);
+}
+
+/** Helper for setconf and resetconf. Acts like setconf, except
+ * it passes <b>use_defaults</b> on to options_trial_assign(). Modifies the
+ * contents of body.
+ */
+static int
+control_setconf_helper(control_connection_t *conn,
+ const control_cmd_args_t *args,
+ int use_defaults)
+{
+ setopt_err_t opt_err;
+ char *errstring = NULL;
+ const unsigned flags =
+ CAL_CLEAR_FIRST | (use_defaults ? CAL_USE_DEFAULTS : 0);
+
+ // We need a copy here, since confparse.c wants to canonicalize cases.
+ config_line_t *lines = config_lines_dup(args->kwargs);
+
+ opt_err = options_trial_assign(lines, flags, &errstring);
+ {
+#define SEND_ERRMSG(code, msg) \
+ control_printf_endreply(conn, code, msg ": %s", errstring);
+
+ switch (opt_err) {
+ case SETOPT_ERR_MISC:
+ SEND_ERRMSG(552, "Unrecognized option");
+ break;
+ case SETOPT_ERR_PARSE:
+ SEND_ERRMSG(513, "Unacceptable option value");
+ break;
+ case SETOPT_ERR_TRANSITION:
+ SEND_ERRMSG(553, "Transition not allowed");
+ break;
+ case SETOPT_ERR_SETTING:
+ default:
+ SEND_ERRMSG(553, "Unable to set option");
+ break;
+ case SETOPT_OK:
+ config_free_lines(lines);
+ send_control_done(conn);
+ return 0;
+ }
+#undef SEND_ERRMSG
+ log_warn(LD_CONTROL,
+ "Controller gave us config lines that didn't validate: %s",
+ errstring);
+ config_free_lines(lines);
+ tor_free(errstring);
+ return 0;
+ }
+}
+
+/** Return true iff <b>addr</b> is unusable as a mapaddress target because of
+ * containing funny characters. */
+static int
+address_is_invalid_mapaddress_target(const char *addr)
+{
+ if (!strcmpstart(addr, "*."))
+ return address_is_invalid_destination(addr+2, 1);
+ else
+ return address_is_invalid_destination(addr, 1);
+}
+
+static const control_cmd_syntax_t mapaddress_syntax = {
+ .max_args=1,
+ .accept_keywords=true,
+};
+
+/** Called when we get a MAPADDRESS command; try to bind all listed addresses,
+ * and report success or failure. */
+static int
+handle_control_mapaddress(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ smartlist_t *reply;
+ char *r;
+ size_t sz;
+
+ reply = smartlist_new();
+ const config_line_t *line;
+ for (line = args->kwargs; line; line = line->next) {
+ const char *from = line->key;
+ const char *to = line->value;
+ {
+ if (address_is_invalid_mapaddress_target(to)) {
+ smartlist_add_asprintf(reply,
+ "512-syntax error: invalid address '%s'", to);
+ log_warn(LD_CONTROL,
+ "Skipping invalid argument '%s' in MapAddress msg", to);
+ } else if (!strcmp(from, ".") || !strcmp(from, "0.0.0.0") ||
+ !strcmp(from, "::")) {
+ const char type =
+ !strcmp(from,".") ? RESOLVED_TYPE_HOSTNAME :
+ (!strcmp(from, "0.0.0.0") ? RESOLVED_TYPE_IPV4 : RESOLVED_TYPE_IPV6);
+ const char *address = addressmap_register_virtual_address(
+ type, tor_strdup(to));
+ if (!address) {
+ smartlist_add_asprintf(reply,
+ "451-resource exhausted: skipping '%s=%s'", from,to);
+ log_warn(LD_CONTROL,
+ "Unable to allocate address for '%s' in MapAddress msg",
+ safe_str_client(to));
+ } else {
+ smartlist_add_asprintf(reply, "250-%s=%s", address, to);
+ }
+ } else {
+ const char *msg;
+ if (addressmap_register_auto(from, to, 1,
+ ADDRMAPSRC_CONTROLLER, &msg) < 0) {
+ smartlist_add_asprintf(reply,
+ "512-syntax error: invalid address mapping "
+ " '%s=%s': %s", from, to, msg);
+ log_warn(LD_CONTROL,
+ "Skipping invalid argument '%s=%s' in MapAddress msg: %s",
+ from, to, msg);
+ } else {
+ smartlist_add_asprintf(reply, "250-%s=%s", from, to);
+ }
+ }
+ }
+ }
+
+ if (smartlist_len(reply)) {
+ ((char*)smartlist_get(reply,smartlist_len(reply)-1))[3] = ' ';
+ r = smartlist_join_strings(reply, "\r\n", 1, &sz);
+ connection_buf_add(r, sz, TO_CONN(conn));
+ tor_free(r);
+ } else {
+ const char *response =
+ "512 syntax error: not enough arguments to mapaddress.\r\n";
+ connection_buf_add(response, strlen(response), TO_CONN(conn));
+ }
+
+ SMARTLIST_FOREACH(reply, char *, cp, tor_free(cp));
+ smartlist_free(reply);
+ return 0;
+}
+
+/** Given a string, convert it to a circuit purpose. */
+static uint8_t
+circuit_purpose_from_string(const char *string)
+{
+ if (!strcasecmpstart(string, "purpose="))
+ string += strlen("purpose=");
+
+ if (!strcasecmp(string, "general"))
+ return CIRCUIT_PURPOSE_C_GENERAL;
+ else if (!strcasecmp(string, "controller"))
+ return CIRCUIT_PURPOSE_CONTROLLER;
+ else
+ return CIRCUIT_PURPOSE_UNKNOWN;
+}
+
+static const control_cmd_syntax_t extendcircuit_syntax = {
+ .min_args=1,
+ .max_args=1, // see note in function
+ .accept_keywords=true,
+ .kvline_flags=KV_OMIT_VALS
+};
+
+/** Called when we get an EXTENDCIRCUIT message. Try to extend the listed
+ * circuit, and report success or failure. */
+static int
+handle_control_extendcircuit(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ smartlist_t *router_nicknames=smartlist_new(), *nodes=NULL;
+ origin_circuit_t *circ = NULL;
+ uint8_t intended_purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ const config_line_t *kwargs = args->kwargs;
+ const char *circ_id = smartlist_get(args->args, 0);
+ const char *path_str = NULL;
+ char *path_str_alloc = NULL;
+
+ /* The syntax for this command is unfortunate. The second argument is
+ optional, and is a comma-separated list long-format fingerprints, which
+ can (historically!) contain an equals sign.
+
+ Here we check the second argument to see if it's a path, and if so we
+ remove it from the kwargs list and put it in path_str.
+ */
+ if (kwargs) {
+ const config_line_t *arg1 = kwargs;
+ if (!strcmp(arg1->value, "")) {
+ path_str = arg1->key;
+ kwargs = kwargs->next;
+ } else if (arg1->key[0] == '$') {
+ tor_asprintf(&path_str_alloc, "%s=%s", arg1->key, arg1->value);
+ path_str = path_str_alloc;
+ kwargs = kwargs->next;
+ }
+ }
+
+ const config_line_t *purpose_line = config_line_find_case(kwargs, "PURPOSE");
+ bool zero_circ = !strcmp("0", circ_id);
+
+ if (purpose_line) {
+ intended_purpose = circuit_purpose_from_string(purpose_line->value);
+ if (intended_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
+ control_printf_endreply(conn, 552, "Unknown purpose \"%s\"",
+ purpose_line->value);
+ goto done;
+ }
+ }
+
+ if (zero_circ) {
+ if (!path_str) {
+ // "EXTENDCIRCUIT 0" with no path.
+ circ = circuit_launch(intended_purpose, CIRCLAUNCH_NEED_CAPACITY);
+ if (!circ) {
+ control_write_endreply(conn, 551, "Couldn't start circuit");
+ } else {
+ control_printf_endreply(conn, 250, "EXTENDED %lu",
+ (unsigned long)circ->global_identifier);
+ }
+ goto done;
+ }
+ }
+
+ if (!zero_circ && !(circ = get_circ(circ_id))) {
+ control_printf_endreply(conn, 552, "Unknown circuit \"%s\"", circ_id);
+ goto done;
+ }
+
+ if (!path_str) {
+ control_write_endreply(conn, 512, "syntax error: path required.");
+ goto done;
+ }
+
+ smartlist_split_string(router_nicknames, path_str, ",", 0, 0);
+
+ nodes = smartlist_new();
+ bool first_node = zero_circ;
+ SMARTLIST_FOREACH_BEGIN(router_nicknames, const char *, n) {
+ const node_t *node = node_get_by_nickname(n, 0);
+ if (!node) {
+ control_printf_endreply(conn, 552, "No such router \"%s\"", n);
+ goto done;
+ }
+ if (!node_has_preferred_descriptor(node, first_node)) {
+ control_printf_endreply(conn, 552, "No descriptor for \"%s\"", n);
+ goto done;
+ }
+ smartlist_add(nodes, (void*)node);
+ first_node = false;
+ } SMARTLIST_FOREACH_END(n);
+
+ if (!smartlist_len(nodes)) {
+ control_write_endreply(conn, 512, "No router names provided");
+ goto done;
+ }
+
+ if (zero_circ) {
+ /* start a new circuit */
+ circ = origin_circuit_init(intended_purpose, 0);
+ }
+
+ /* now circ refers to something that is ready to be extended */
+ first_node = zero_circ;
+ SMARTLIST_FOREACH(nodes, const node_t *, node,
+ {
+ extend_info_t *info = extend_info_from_node(node, first_node);
+ if (!info) {
+ tor_assert_nonfatal(first_node);
+ log_warn(LD_CONTROL,
+ "controller tried to connect to a node that lacks a suitable "
+ "descriptor, or which doesn't have any "
+ "addresses that are allowed by the firewall configuration; "
+ "circuit marked for closing.");
+ circuit_mark_for_close(TO_CIRCUIT(circ), -END_CIRC_REASON_CONNECTFAILED);
+ connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
+ goto done;
+ }
+ circuit_append_new_exit(circ, info);
+ if (circ->build_state->desired_path_len > 1) {
+ circ->build_state->onehop_tunnel = 0;
+ }
+ extend_info_free(info);
+ first_node = 0;
+ });
+
+ /* now that we've populated the cpath, start extending */
+ if (zero_circ) {
+ int err_reason = 0;
+ if ((err_reason = circuit_handle_first_hop(circ)) < 0) {
+ circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason);
+ control_write_endreply(conn, 551, "Couldn't start circuit");
+ goto done;
+ }
+ } else {
+ if (circ->base_.state == CIRCUIT_STATE_OPEN ||
+ circ->base_.state == CIRCUIT_STATE_GUARD_WAIT) {
+ int err_reason = 0;
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
+ if ((err_reason = circuit_send_next_onion_skin(circ)) < 0) {
+ log_info(LD_CONTROL,
+ "send_next_onion_skin failed; circuit marked for closing.");
+ circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason);
+ control_write_endreply(conn, 551, "Couldn't send onion skin");
+ goto done;
+ }
+ }
+ }
+
+ control_printf_endreply(conn, 250, "EXTENDED %lu",
+ (unsigned long)circ->global_identifier);
+ if (zero_circ) /* send a 'launched' event, for completeness */
+ circuit_event_status(circ, CIRC_EVENT_LAUNCHED, 0);
+ done:
+ SMARTLIST_FOREACH(router_nicknames, char *, n, tor_free(n));
+ smartlist_free(router_nicknames);
+ smartlist_free(nodes);
+ tor_free(path_str_alloc);
+ return 0;
+}
+
+static const control_cmd_syntax_t setcircuitpurpose_syntax = {
+ .max_args=1,
+ .accept_keywords=true,
+};
+
+/** Called when we get a SETCIRCUITPURPOSE message. If we can find the
+ * circuit and it's a valid purpose, change it. */
+static int
+handle_control_setcircuitpurpose(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ origin_circuit_t *circ = NULL;
+ uint8_t new_purpose;
+ const char *circ_id = smartlist_get(args->args,0);
+
+ if (!(circ = get_circ(circ_id))) {
+ control_printf_endreply(conn, 552, "Unknown circuit \"%s\"", circ_id);
+ goto done;
+ }
+
+ {
+ const config_line_t *purp = config_line_find_case(args->kwargs, "PURPOSE");
+ if (!purp) {
+ control_write_endreply(conn, 552, "No purpose given");
+ goto done;
+ }
+ new_purpose = circuit_purpose_from_string(purp->value);
+ if (new_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
+ control_printf_endreply(conn, 552, "Unknown purpose \"%s\"",
+ purp->value);
+ goto done;
+ }
+ }
+
+ circuit_change_purpose(TO_CIRCUIT(circ), new_purpose);
+ send_control_done(conn);
+
+ done:
+ return 0;
+}
+
+static const char *attachstream_keywords[] = {
+ "HOP", NULL
+};
+static const control_cmd_syntax_t attachstream_syntax = {
+ .min_args=2, .max_args=2,
+ .accept_keywords=true,
+ .allowed_keywords=attachstream_keywords
+};
+
+/** Called when we get an ATTACHSTREAM message. Try to attach the requested
+ * stream, and report success or failure. */
+static int
+handle_control_attachstream(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ entry_connection_t *ap_conn = NULL;
+ origin_circuit_t *circ = NULL;
+ crypt_path_t *cpath=NULL;
+ int hop=0, hop_line_ok=1;
+ const char *stream_id = smartlist_get(args->args, 0);
+ const char *circ_id = smartlist_get(args->args, 1);
+ int zero_circ = !strcmp(circ_id, "0");
+ const config_line_t *hoparg = config_line_find_case(args->kwargs, "HOP");
+
+ if (!(ap_conn = get_stream(stream_id))) {
+ control_printf_endreply(conn, 552, "Unknown stream \"%s\"", stream_id);
+ return 0;
+ } else if (!zero_circ && !(circ = get_circ(circ_id))) {
+ control_printf_endreply(conn, 552, "Unknown circuit \"%s\"", circ_id);
+ return 0;
+ } else if (circ) {
+ if (hoparg) {
+ hop = (int) tor_parse_ulong(hoparg->value, 10, 0, INT_MAX,
+ &hop_line_ok, NULL);
+ if (!hop_line_ok) { /* broken hop line */
+ control_printf_endreply(conn, 552, "Bad value hop=%s",
+ hoparg->value);
+ return 0;
+ }
+ }
+ }
+
+ if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT &&
+ ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONNECT_WAIT &&
+ ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_RESOLVE_WAIT) {
+ control_write_endreply(conn, 555,
+ "Connection is not managed by controller.");
+ return 0;
+ }
+
+ /* Do we need to detach it first? */
+ if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT) {
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
+ circuit_t *tmpcirc = circuit_get_by_edge_conn(edge_conn);
+ connection_edge_end(edge_conn, END_STREAM_REASON_TIMEOUT);
+ /* Un-mark it as ending, since we're going to reuse it. */
+ edge_conn->edge_has_sent_end = 0;
+ edge_conn->end_reason = 0;
+ if (tmpcirc)
+ circuit_detach_stream(tmpcirc, edge_conn);
+ CONNECTION_AP_EXPECT_NONPENDING(ap_conn);
+ TO_CONN(edge_conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
+ }
+
+ if (circ && (circ->base_.state != CIRCUIT_STATE_OPEN)) {
+ control_write_endreply(conn, 551,
+ "Can't attach stream to non-open origin circuit");
+ return 0;
+ }
+ /* Is this a single hop circuit? */
+ if (circ && (circuit_get_cpath_len(circ)<2 || hop==1)) {
+ control_write_endreply(conn, 551,
+ "Can't attach stream to this one-hop circuit.");
+ return 0;
+ }
+
+ if (circ && hop>0) {
+ /* find this hop in the circuit, and set cpath */
+ cpath = circuit_get_cpath_hop(circ, hop);
+ if (!cpath) {
+ control_printf_endreply(conn, 551, "Circuit doesn't have %d hops.", hop);
+ return 0;
+ }
+ }
+ if (connection_ap_handshake_rewrite_and_attach(ap_conn, circ, cpath) < 0) {
+ control_write_endreply(conn, 551, "Unable to attach stream");
+ return 0;
+ }
+ send_control_done(conn);
+ return 0;
+}
+
+static const char *postdescriptor_keywords[] = {
+ "cache", "purpose", NULL,
+};
+
+static const control_cmd_syntax_t postdescriptor_syntax = {
+ .max_args = 0,
+ .accept_keywords = true,
+ .allowed_keywords = postdescriptor_keywords,
+ .want_cmddata = true,
+};
+
+/** Called when we get a POSTDESCRIPTOR message. Try to learn the provided
+ * descriptor, and report success or failure. */
+static int
+handle_control_postdescriptor(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ const char *msg=NULL;
+ uint8_t purpose = ROUTER_PURPOSE_GENERAL;
+ int cache = 0; /* eventually, we may switch this to 1 */
+ const config_line_t *line;
+
+ line = config_line_find_case(args->kwargs, "purpose");
+ if (line) {
+ purpose = router_purpose_from_string(line->value);
+ if (purpose == ROUTER_PURPOSE_UNKNOWN) {
+ control_printf_endreply(conn, 552, "Unknown purpose \"%s\"",
+ line->value);
+ goto done;
+ }
+ }
+ line = config_line_find_case(args->kwargs, "cache");
+ if (line) {
+ if (!strcasecmp(line->value, "no"))
+ cache = 0;
+ else if (!strcasecmp(line->value, "yes"))
+ cache = 1;
+ else {
+ control_printf_endreply(conn, 552, "Unknown cache request \"%s\"",
+ line->value);
+ goto done;
+ }
+ }
+
+ switch (router_load_single_router(args->cmddata, purpose, cache, &msg)) {
+ case -1:
+ if (!msg) msg = "Could not parse descriptor";
+ control_write_endreply(conn, 554, msg);
+ break;
+ case 0:
+ if (!msg) msg = "Descriptor not added";
+ control_write_endreply(conn, 251, msg);
+ break;
+ case 1:
+ send_control_done(conn);
+ break;
+ }
+
+ done:
+ return 0;
+}
+
+static const control_cmd_syntax_t redirectstream_syntax = {
+ .min_args = 2,
+ .max_args = UINT_MAX, // XXX should be 3.
+};
+
+/** Called when we receive a REDIRECTSTERAM command. Try to change the target
+ * address of the named AP stream, and report success or failure. */
+static int
+handle_control_redirectstream(control_connection_t *conn,
+ const control_cmd_args_t *cmd_args)
+{
+ entry_connection_t *ap_conn = NULL;
+ char *new_addr = NULL;
+ uint16_t new_port = 0;
+ const smartlist_t *args = cmd_args->args;
+
+ if (!(ap_conn = get_stream(smartlist_get(args, 0)))
+ || !ap_conn->socks_request) {
+ control_printf_endreply(conn, 552, "Unknown stream \"%s\"",
+ (char*)smartlist_get(args, 0));
+ } else {
+ int ok = 1;
+ if (smartlist_len(args) > 2) { /* they included a port too */
+ new_port = (uint16_t) tor_parse_ulong(smartlist_get(args, 2),
+ 10, 1, 65535, &ok, NULL);
+ }
+ if (!ok) {
+ control_printf_endreply(conn, 512, "Cannot parse port \"%s\"",
+ (char*)smartlist_get(args, 2));
+ } else {
+ new_addr = tor_strdup(smartlist_get(args, 1));
+ }
+ }
+
+ if (!new_addr)
+ return 0;
+
+ strlcpy(ap_conn->socks_request->address, new_addr,
+ sizeof(ap_conn->socks_request->address));
+ if (new_port)
+ ap_conn->socks_request->port = new_port;
+ tor_free(new_addr);
+ send_control_done(conn);
+ return 0;
+}
+
+static const control_cmd_syntax_t closestream_syntax = {
+ .min_args = 2,
+ .max_args = UINT_MAX, /* XXXX This is the original behavior, but
+ * maybe we should change the spec. */
+};
+
+/** Called when we get a CLOSESTREAM command; try to close the named stream
+ * and report success or failure. */
+static int
+handle_control_closestream(control_connection_t *conn,
+ const control_cmd_args_t *cmd_args)
+{
+ entry_connection_t *ap_conn=NULL;
+ uint8_t reason=0;
+ int ok;
+ const smartlist_t *args = cmd_args->args;
+
+ tor_assert(smartlist_len(args) >= 2);
+
+ if (!(ap_conn = get_stream(smartlist_get(args, 0))))
+ control_printf_endreply(conn, 552, "Unknown stream \"%s\"",
+ (char*)smartlist_get(args, 0));
+ else {
+ reason = (uint8_t) tor_parse_ulong(smartlist_get(args,1), 10, 0, 255,
+ &ok, NULL);
+ if (!ok) {
+ control_printf_endreply(conn, 552, "Unrecognized reason \"%s\"",
+ (char*)smartlist_get(args, 1));
+ ap_conn = NULL;
+ }
+ }
+ if (!ap_conn)
+ return 0;
+
+ connection_mark_unattached_ap(ap_conn, reason);
+ send_control_done(conn);
+ return 0;
+}
+
+static const control_cmd_syntax_t closecircuit_syntax = {
+ .min_args=1, .max_args=1,
+ .accept_keywords=true,
+ .kvline_flags=KV_OMIT_VALS,
+ // XXXX we might want to exclude unrecognized flags, but for now we
+ // XXXX just ignore them for backward compatibility.
+};
+
+/** Called when we get a CLOSECIRCUIT command; try to close the named circuit
+ * and report success or failure. */
+static int
+handle_control_closecircuit(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ const char *circ_id = smartlist_get(args->args, 0);
+ origin_circuit_t *circ = NULL;
+
+ if (!(circ=get_circ(circ_id))) {
+ control_printf_endreply(conn, 552, "Unknown circuit \"%s\"", circ_id);
+ return 0;
+ }
+
+ bool safe = config_lines_contain_flag(args->kwargs, "IfUnused");
+
+ if (!safe || !circ->p_streams) {
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_REQUESTED);
+ }
+
+ send_control_done(conn);
+ return 0;
+}
+
+static const control_cmd_syntax_t resolve_syntax = {
+ .max_args=0,
+ .accept_keywords=true,
+ .kvline_flags=KV_OMIT_VALS,
+};
+
+/** Called when we get a RESOLVE command: start trying to resolve
+ * the listed addresses. */
+static int
+handle_control_resolve(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ smartlist_t *failed;
+ int is_reverse = 0;
+
+ if (!(conn->event_mask & (((event_mask_t)1)<<EVENT_ADDRMAP))) {
+ log_warn(LD_CONTROL, "Controller asked us to resolve an address, but "
+ "isn't listening for ADDRMAP events. It probably won't see "
+ "the answer.");
+ }
+
+ {
+ const config_line_t *modearg = config_line_find_case(args->kwargs, "mode");
+ if (modearg && !strcasecmp(modearg->value, "reverse"))
+ is_reverse = 1;
+ }
+ failed = smartlist_new();
+ for (const config_line_t *line = args->kwargs; line; line = line->next) {
+ if (!strlen(line->value)) {
+ const char *addr = line->key;
+ if (dnsserv_launch_request(addr, is_reverse, conn)<0)
+ smartlist_add(failed, (char*)addr);
+ } else {
+ // XXXX arguably we should reject unrecognized keyword arguments,
+ // XXXX but the old implementation didn't do that.
+ }
+ }
+
+ send_control_done(conn);
+ SMARTLIST_FOREACH(failed, const char *, arg, {
+ control_event_address_mapped(arg, arg, time(NULL),
+ "internal", 0);
+ });
+
+ smartlist_free(failed);
+ return 0;
+}
+
+static const control_cmd_syntax_t protocolinfo_syntax = {
+ .max_args = UINT_MAX
+};
+
+/** Called when we get a PROTOCOLINFO command: send back a reply. */
+static int
+handle_control_protocolinfo(control_connection_t *conn,
+ const control_cmd_args_t *cmd_args)
+{
+ const char *bad_arg = NULL;
+ const smartlist_t *args = cmd_args->args;
+
+ conn->have_sent_protocolinfo = 1;
+
+ SMARTLIST_FOREACH(args, const char *, arg, {
+ int ok;
+ tor_parse_long(arg, 10, 0, LONG_MAX, &ok, NULL);
+ if (!ok) {
+ bad_arg = arg;
+ break;
+ }
+ });
+ if (bad_arg) {
+ control_printf_endreply(conn, 513, "No such version %s",
+ escaped(bad_arg));
+ /* Don't tolerate bad arguments when not authenticated. */
+ if (!STATE_IS_OPEN(TO_CONN(conn)->state))
+ connection_mark_for_close(TO_CONN(conn));
+ goto done;
+ } else {
+ const or_options_t *options = get_options();
+ int cookies = options->CookieAuthentication;
+ char *cfile = get_controller_cookie_file_name();
+ char *abs_cfile;
+ char *esc_cfile;
+ char *methods;
+ abs_cfile = make_path_absolute(cfile);
+ esc_cfile = esc_for_log(abs_cfile);
+ {
+ int passwd = (options->HashedControlPassword != NULL ||
+ options->HashedControlSessionPassword != NULL);
+ smartlist_t *mlist = smartlist_new();
+ if (cookies) {
+ smartlist_add(mlist, (char*)"COOKIE");
+ smartlist_add(mlist, (char*)"SAFECOOKIE");
+ }
+ if (passwd)
+ smartlist_add(mlist, (char*)"HASHEDPASSWORD");
+ if (!cookies && !passwd)
+ smartlist_add(mlist, (char*)"NULL");
+ methods = smartlist_join_strings(mlist, ",", 0, NULL);
+ smartlist_free(mlist);
+ }
+
+ control_write_midreply(conn, 250, "PROTOCOLINFO 1");
+ control_printf_midreply(conn, 250, "AUTH METHODS=%s%s%s", methods,
+ cookies?" COOKIEFILE=":"",
+ cookies?esc_cfile:"");
+ control_printf_midreply(conn, 250, "VERSION Tor=%s", escaped(VERSION));
+ send_control_done(conn);
+
+ tor_free(methods);
+ tor_free(cfile);
+ tor_free(abs_cfile);
+ tor_free(esc_cfile);
+ }
+ done:
+ return 0;
+}
+
+static const control_cmd_syntax_t usefeature_syntax = {
+ .max_args = UINT_MAX
+};
+
+/** Called when we get a USEFEATURE command: parse the feature list, and
+ * set up the control_connection's options properly. */
+static int
+handle_control_usefeature(control_connection_t *conn,
+ const control_cmd_args_t *cmd_args)
+{
+ const smartlist_t *args = cmd_args->args;
+ int bad = 0;
+ SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
+ if (!strcasecmp(arg, "VERBOSE_NAMES"))
+ ;
+ else if (!strcasecmp(arg, "EXTENDED_EVENTS"))
+ ;
+ else {
+ control_printf_endreply(conn, 552, "Unrecognized feature \"%s\"",
+ arg);
+ bad = 1;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(arg);
+
+ if (!bad) {
+ send_control_done(conn);
+ }
+
+ return 0;
+}
+
+static const control_cmd_syntax_t dropguards_syntax = {
+ .max_args = 0,
+};
+
+/** Implementation for the DROPGUARDS command. */
+static int
+handle_control_dropguards(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ (void) args; /* We don't take arguments. */
+
+ static int have_warned = 0;
+ if (! have_warned) {
+ log_warn(LD_CONTROL, "DROPGUARDS is dangerous; make sure you understand "
+ "the risks before using it. It may be removed in a future "
+ "version of Tor.");
+ have_warned = 1;
+ }
+
+ remove_all_entry_guards();
+ send_control_done(conn);
+
+ return 0;
+}
+
+static const char *hsfetch_keywords[] = {
+ "SERVER", NULL,
+};
+static const control_cmd_syntax_t hsfetch_syntax = {
+ .min_args = 1, .max_args = 1,
+ .accept_keywords = true,
+ .allowed_keywords = hsfetch_keywords,
+};
+
+/** Implementation for the HSFETCH command. */
+static int
+handle_control_hsfetch(control_connection_t *conn,
+ const control_cmd_args_t *args)
+
+{
+ char digest[DIGEST_LEN], *desc_id = NULL;
+ smartlist_t *hsdirs = NULL;
+ static const char *v2_str = "v2-";
+ const size_t v2_str_len = strlen(v2_str);
+ rend_data_t *rend_query = NULL;
+ ed25519_public_key_t v3_pk;
+ uint32_t version;
+ const char *hsaddress = NULL;
+
+ /* Extract the first argument (either HSAddress or DescID). */
+ const char *arg1 = smartlist_get(args->args, 0);
+ /* Test if it's an HS address without the .onion part. */
+ if (rend_valid_v2_service_id(arg1)) {
+ hsaddress = arg1;
+ version = HS_VERSION_TWO;
+ } else if (strcmpstart(arg1, v2_str) == 0 &&
+ rend_valid_descriptor_id(arg1 + v2_str_len) &&
+ base32_decode(digest, sizeof(digest), arg1 + v2_str_len,
+ REND_DESC_ID_V2_LEN_BASE32) ==
+ REND_DESC_ID_V2_LEN_BASE32) {
+ /* We have a well formed version 2 descriptor ID. Keep the decoded value
+ * of the id. */
+ desc_id = digest;
+ version = HS_VERSION_TWO;
+ } else if (hs_address_is_valid(arg1)) {
+ hsaddress = arg1;
+ version = HS_VERSION_THREE;
+ hs_parse_address(hsaddress, &v3_pk, NULL, NULL);
+ } else {
+ control_printf_endreply(conn, 513, "Invalid argument \"%s\"", arg1);
+ goto done;
+ }
+
+ for (const config_line_t *line = args->kwargs; line; line = line->next) {
+ if (!strcasecmp(line->key, "SERVER")) {
+ const char *server = line->value;
+
+ const node_t *node = node_get_by_hex_id(server, 0);
+ if (!node) {
+ control_printf_endreply(conn, 552, "Server \"%s\" not found", server);
+ goto done;
+ }
+ if (!hsdirs) {
+ /* Stores routerstatus_t cmddata for each specified server. */
+ hsdirs = smartlist_new();
+ }
+ /* Valid server, add it to our local list. */
+ smartlist_add(hsdirs, node->rs);
+ } else {
+ tor_assert_nonfatal_unreached();
+ }
+ }
+
+ if (version == HS_VERSION_TWO) {
+ rend_query = rend_data_client_create(hsaddress, desc_id, NULL,
+ REND_NO_AUTH);
+ if (rend_query == NULL) {
+ control_write_endreply(conn, 551, "Error creating the HS query");
+ goto done;
+ }
+ }
+
+ /* Using a descriptor ID, we force the user to provide at least one
+ * hsdir server using the SERVER= option. */
+ if (desc_id && (!hsdirs || !smartlist_len(hsdirs))) {
+ control_write_endreply(conn, 512, "SERVER option is required");
+ goto done;
+ }
+
+ /* We are about to trigger HSDir fetch so send the OK now because after
+ * that 650 event(s) are possible so better to have the 250 OK before them
+ * to avoid out of order replies. */
+ send_control_done(conn);
+
+ /* Trigger the fetch using the built rend query and possibly a list of HS
+ * directory to use. This function ignores the client cache thus this will
+ * always send a fetch command. */
+ if (version == HS_VERSION_TWO) {
+ rend_client_fetch_v2_desc(rend_query, hsdirs);
+ } else if (version == HS_VERSION_THREE) {
+ hs_control_hsfetch_command(&v3_pk, hsdirs);
+ }
+
+ done:
+ /* Contains data pointer that we don't own thus no cleanup. */
+ smartlist_free(hsdirs);
+ rend_data_free(rend_query);
+ return 0;
+}
+
+static const char *hspost_keywords[] = {
+ "SERVER", "HSADDRESS", NULL
+};
+static const control_cmd_syntax_t hspost_syntax = {
+ .min_args = 0, .max_args = 0,
+ .accept_keywords = true,
+ .want_cmddata = true,
+ .allowed_keywords = hspost_keywords
+};
+
+/** Implementation for the HSPOST command. */
+static int
+handle_control_hspost(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ smartlist_t *hs_dirs = NULL;
+ const char *encoded_desc = args->cmddata;
+ size_t encoded_desc_len = args->cmddata_len;
+ const char *onion_address = NULL;
+ const config_line_t *line;
+
+ for (line = args->kwargs; line; line = line->next) {
+ if (!strcasecmpstart(line->key, "SERVER")) {
+ const char *server = line->value;
+ const node_t *node = node_get_by_hex_id(server, 0);
+
+ if (!node || !node->rs) {
+ control_printf_endreply(conn, 552, "Server \"%s\" not found",
+ server);
+ goto done;
+ }
+ /* Valid server, add it to our local list. */
+ if (!hs_dirs)
+ hs_dirs = smartlist_new();
+ smartlist_add(hs_dirs, node->rs);
+ } else if (!strcasecmpstart(line->key, "HSADDRESS")) {
+ const char *address = line->value;
+ if (!hs_address_is_valid(address)) {
+ control_write_endreply(conn, 512, "Malformed onion address");
+ goto done;
+ }
+ onion_address = address;
+ } else {
+ tor_assert_nonfatal_unreached();
+ }
+ }
+
+ /* Handle the v3 case. */
+ if (onion_address) {
+ if (hs_control_hspost_command(encoded_desc, onion_address, hs_dirs) < 0) {
+ control_write_endreply(conn, 554, "Invalid descriptor");
+ } else {
+ send_control_done(conn);
+ }
+ goto done;
+ }
+
+ /* From this point on, it is only v2. */
+
+ /* parse it. */
+ rend_encoded_v2_service_descriptor_t *desc =
+ tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
+ desc->desc_str = tor_memdup_nulterm(encoded_desc, encoded_desc_len);
+
+ rend_service_descriptor_t *parsed = NULL;
+ char *intro_content = NULL;
+ size_t intro_size;
+ size_t encoded_size;
+ const char *next_desc;
+ if (!rend_parse_v2_service_descriptor(&parsed, desc->desc_id, &intro_content,
+ &intro_size, &encoded_size,
+ &next_desc, desc->desc_str, 1)) {
+ /* Post the descriptor. */
+ char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
+ if (!rend_get_service_id(parsed->pk, serviceid)) {
+ smartlist_t *descs = smartlist_new();
+ smartlist_add(descs, desc);
+
+ /* We are about to trigger HS descriptor upload so send the OK now
+ * because after that 650 event(s) are possible so better to have the
+ * 250 OK before them to avoid out of order replies. */
+ send_control_done(conn);
+
+ /* Trigger the descriptor upload */
+ directory_post_to_hs_dir(parsed, descs, hs_dirs, serviceid, 0);
+ smartlist_free(descs);
+ }
+
+ rend_service_descriptor_free(parsed);
+ } else {
+ control_write_endreply(conn, 554, "Invalid descriptor");
+ }
+
+ tor_free(intro_content);
+ rend_encoded_v2_service_descriptor_free(desc);
+ done:
+ smartlist_free(hs_dirs); /* Contents belong to the rend service code. */
+ return 0;
+}
+
+/* Helper function for ADD_ONION that adds an ephemeral service depending on
+ * the given hs_version.
+ *
+ * The secret key in pk depends on the hs_version. The ownership of the key
+ * used in pk is given to the HS subsystem so the caller must stop accessing
+ * it after.
+ *
+ * The port_cfgs is a list of service port. Ownership transferred to service.
+ * The max_streams refers to the MaxStreams= key.
+ * The max_streams_close_circuit refers to the MaxStreamsCloseCircuit key.
+ * The auth_type is the authentication type of the clients in auth_clients.
+ * The ownership of that list is transferred to the service.
+ *
+ * On success (RSAE_OKAY), the address_out points to a newly allocated string
+ * containing the onion address without the .onion part. On error, address_out
+ * is untouched. */
+static hs_service_add_ephemeral_status_t
+add_onion_helper_add_service(int hs_version,
+ add_onion_secret_key_t *pk,
+ smartlist_t *port_cfgs, int max_streams,
+ int max_streams_close_circuit, int auth_type,
+ smartlist_t *auth_clients, char **address_out)
+{
+ hs_service_add_ephemeral_status_t ret;
+
+ tor_assert(pk);
+ tor_assert(port_cfgs);
+ tor_assert(address_out);
+
+ switch (hs_version) {
+ case HS_VERSION_TWO:
+ ret = rend_service_add_ephemeral(pk->v2, port_cfgs, max_streams,
+ max_streams_close_circuit, auth_type,
+ auth_clients, address_out);
+ break;
+ case HS_VERSION_THREE:
+ ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams,
+ max_streams_close_circuit, address_out);
+ break;
+ default:
+ tor_assert_unreached();
+ }
+
+ return ret;
+}
+
+/** The list of onion services that have been added via ADD_ONION that do not
+ * belong to any particular control connection.
+ */
+static smartlist_t *detached_onion_services = NULL;
+
+/**
+ * Return a list of detached onion services, or NULL if none exist.
+ **/
+smartlist_t *
+get_detached_onion_services(void)
+{
+ return detached_onion_services;
+}
+
+static const char *add_onion_keywords[] = {
+ "Port", "Flags", "MaxStreams", "ClientAuth", NULL
+};
+static const control_cmd_syntax_t add_onion_syntax = {
+ .min_args = 1, .max_args = 1,
+ .accept_keywords = true,
+ .allowed_keywords = add_onion_keywords
+};
+
+/** Called when we get a ADD_ONION command; parse the body, and set up
+ * the new ephemeral Onion Service. */
+static int
+handle_control_add_onion(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ /* Parse all of the arguments that do not involve handling cryptographic
+ * material first, since there's no reason to touch that at all if any of
+ * 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;
+ int non_anonymous = 0;
+ const config_line_t *arg;
+
+ for (arg = args->kwargs; arg; arg = arg->next) {
+ if (!strcasecmp(arg->key, "Port")) {
+ /* "Port=VIRTPORT[,TARGET]". */
+ rend_service_port_config_t *cfg =
+ rend_service_parse_port_config(arg->value, ",", NULL);
+ if (!cfg) {
+ control_write_endreply(conn, 512, "Invalid VIRTPORT/TARGET");
+ goto out;
+ }
+ smartlist_add(port_cfgs, cfg);
+ } else if (!strcasecmp(arg->key, "MaxStreams")) {
+ /* "MaxStreams=[0..65535]". */
+ int ok = 0;
+ max_streams = (int)tor_parse_long(arg->value, 10, 0, 65535, &ok, NULL);
+ if (!ok) {
+ control_write_endreply(conn, 512, "Invalid MaxStreams");
+ goto out;
+ }
+ } else if (!strcasecmp(arg->key, "Flags")) {
+ /* "Flags=Flag[,Flag]", where Flag can be:
+ * * 'DiscardPK' - If tor generates the keypair, do not include it in
+ * the response.
+ * * 'Detach' - Do not tie this onion service to any particular control
+ * 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;
+
+ smartlist_split_string(flags, arg->value, ",", SPLIT_IGNORE_BLANK, 0);
+ if (smartlist_len(flags) < 1) {
+ control_write_endreply(conn, 512, "Invalid 'Flags' argument");
+ bad = 1;
+ }
+ SMARTLIST_FOREACH_BEGIN(flags, const char *, flag)
+ {
+ if (!strcasecmp(flag, discard_flag)) {
+ discard_pk = 1;
+ } else if (!strcasecmp(flag, detach_flag)) {
+ 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 {
+ control_printf_endreply(conn, 512, "Invalid 'Flags' argument: %s",
+ escaped(flag));
+ bad = 1;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(flag);
+ SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp));
+ smartlist_free(flags);
+ if (bad)
+ goto out;
+
+ } else if (!strcasecmp(arg->key, "ClientAuth")) {
+ char *err_msg = NULL;
+ int created = 0;
+ rend_authorized_client_t *client =
+ add_onion_helper_clientauth(arg->value,
+ &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) {
+ control_write_endreply(conn, 512, "Duplicate name in ClientAuth");
+ 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 {
+ tor_assert_nonfatal_unreached();
+ goto out;
+ }
+ }
+ if (smartlist_len(port_cfgs) == 0) {
+ control_write_endreply(conn, 512, "Missing 'Port' argument");
+ goto out;
+ } else if (auth_type == REND_NO_AUTH && auth_clients != NULL) {
+ control_write_endreply(conn, 512, "No auth type specified");
+ goto out;
+ } else if (auth_type != REND_NO_AUTH && auth_clients == NULL) {
+ control_write_endreply(conn, 512, "No auth clients specified");
+ goto out;
+ } else if ((auth_type == REND_BASIC_AUTH &&
+ smartlist_len(auth_clients) > 512) ||
+ (auth_type == REND_STEALTH_AUTH &&
+ smartlist_len(auth_clients) > 16)) {
+ control_write_endreply(conn, 512, "Too many auth clients");
+ 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.)
+ */
+ control_printf_endreply(conn, 512,
+ "Tor is in %sanonymous hidden service " "mode",
+ non_anonymous ? "" : "non-");
+ goto out;
+ }
+
+ /* Parse the "keytype:keyblob" argument. */
+ int hs_version = 0;
+ add_onion_secret_key_t pk = { NULL };
+ const char *key_new_alg = NULL;
+ char *key_new_blob = NULL;
+ char *err_msg = NULL;
+
+ const char *onionkey = smartlist_get(args->args, 0);
+ if (add_onion_helper_keyarg(onionkey, discard_pk,
+ &key_new_alg, &key_new_blob, &pk, &hs_version,
+ &err_msg) < 0) {
+ if (err_msg) {
+ connection_write_str_to_buf(err_msg, conn);
+ tor_free(err_msg);
+ }
+ goto out;
+ }
+ tor_assert(!err_msg);
+
+ /* Hidden service version 3 don't have client authentication support so if
+ * ClientAuth was given, send back an error. */
+ if (hs_version == HS_VERSION_THREE && auth_clients) {
+ control_write_endreply(conn, 513, "ClientAuth not supported");
+ goto out;
+ }
+
+ /* 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 = add_onion_helper_add_service(hs_version, &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:
+ {
+ if (detach) {
+ if (!detached_onion_services)
+ detached_onion_services = smartlist_new();
+ smartlist_add(detached_onion_services, service_id);
+ } else {
+ if (!conn->ephemeral_onion_services)
+ conn->ephemeral_onion_services = smartlist_new();
+ smartlist_add(conn->ephemeral_onion_services, service_id);
+ }
+
+ tor_assert(service_id);
+ control_printf_midreply(conn, 250, "ServiceID=%s", service_id);
+ if (key_new_alg) {
+ tor_assert(key_new_blob);
+ control_printf_midreply(conn, 250, "PrivateKey=%s:%s",
+ 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);
+ });
+ }
+
+ send_control_done(conn);
+ break;
+ }
+ case RSAE_BADPRIVKEY:
+ control_write_endreply(conn, 551, "Failed to generate onion address");
+ break;
+ case RSAE_ADDREXISTS:
+ control_write_endreply(conn, 550, "Onion address collision");
+ break;
+ case RSAE_BADVIRTPORT:
+ control_write_endreply(conn, 512, "Invalid VIRTPORT/TARGET");
+ break;
+ case RSAE_BADAUTH:
+ control_write_endreply(conn, 512, "Invalid client authorization");
+ break;
+ case RSAE_INTERNAL: /* FALLSTHROUGH */
+ default:
+ control_write_endreply(conn, 551, "Failed to add Onion Service");
+ }
+ if (key_new_blob) {
+ memwipe(key_new_blob, 0, strlen(key_new_blob));
+ tor_free(key_new_blob);
+ }
+
+ out:
+ if (port_cfgs) {
+ SMARTLIST_FOREACH(port_cfgs, rend_service_port_config_t*, p,
+ rend_service_port_config_free(p));
+ 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);
+ }
+ return 0;
+}
+
+/** Helper function to handle parsing the KeyType:KeyBlob argument to the
+ * ADD_ONION command. Return a new crypto_pk_t and if a new key was generated
+ * and the private key not discarded, the algorithm and serialized private key,
+ * or NULL and an optional control protocol error message on failure. The
+ * caller is responsible for freeing the returned key_new_blob and err_msg.
+ *
+ * Note: The error messages returned are deliberately vague to avoid echoing
+ * key material.
+ */
+STATIC int
+add_onion_helper_keyarg(const char *arg, int discard_pk,
+ const char **key_new_alg_out, char **key_new_blob_out,
+ add_onion_secret_key_t *decoded_key, int *hs_version,
+ char **err_msg_out)
+{
+ smartlist_t *key_args = smartlist_new();
+ crypto_pk_t *pk = NULL;
+ const char *key_new_alg = NULL;
+ char *key_new_blob = NULL;
+ char *err_msg = NULL;
+ int ret = -1;
+
+ smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0);
+ if (smartlist_len(key_args) != 2) {
+ err_msg = tor_strdup("512 Invalid key type/blob\r\n");
+ goto err;
+ }
+
+ /* The format is "KeyType:KeyBlob". */
+ static const char *key_type_new = "NEW";
+ static const char *key_type_best = "BEST";
+ static const char *key_type_rsa1024 = "RSA1024";
+ static const char *key_type_ed25519_v3 = "ED25519-V3";
+
+ const char *key_type = smartlist_get(key_args, 0);
+ const char *key_blob = smartlist_get(key_args, 1);
+
+ if (!strcasecmp(key_type_rsa1024, key_type)) {
+ /* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */
+ pk = crypto_pk_base64_decode_private(key_blob, strlen(key_blob));
+ if (!pk) {
+ err_msg = tor_strdup("512 Failed to decode RSA key\r\n");
+ goto err;
+ }
+ if (crypto_pk_num_bits(pk) != PK_BYTES*8) {
+ crypto_pk_free(pk);
+ err_msg = tor_strdup("512 Invalid RSA key size\r\n");
+ goto err;
+ }
+ decoded_key->v2 = pk;
+ *hs_version = HS_VERSION_TWO;
+ } else if (!strcasecmp(key_type_ed25519_v3, key_type)) {
+ /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */
+ ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
+ if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob,
+ strlen(key_blob)) != sizeof(sk->seckey)) {
+ tor_free(sk);
+ err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n");
+ goto err;
+ }
+ decoded_key->v3 = sk;
+ *hs_version = HS_VERSION_THREE;
+ } else if (!strcasecmp(key_type_new, key_type)) {
+ /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */
+ if (!strcasecmp(key_type_rsa1024, key_blob) ||
+ !strcasecmp(key_type_best, key_blob)) {
+ /* "RSA1024", RSA 1024 bit, also currently "BEST" by default. */
+ pk = crypto_pk_new();
+ if (crypto_pk_generate_key(pk)) {
+ tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n",
+ key_type_rsa1024);
+ goto err;
+ }
+ if (!discard_pk) {
+ if (crypto_pk_base64_encode_private(pk, &key_new_blob)) {
+ crypto_pk_free(pk);
+ tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
+ key_type_rsa1024);
+ goto err;
+ }
+ key_new_alg = key_type_rsa1024;
+ }
+ decoded_key->v2 = pk;
+ *hs_version = HS_VERSION_TWO;
+ } else if (!strcasecmp(key_type_ed25519_v3, key_blob)) {
+ ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
+ if (ed25519_secret_key_generate(sk, 1) < 0) {
+ tor_free(sk);
+ tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n",
+ key_type_ed25519_v3);
+ goto err;
+ }
+ if (!discard_pk) {
+ ssize_t len = base64_encode_size(sizeof(sk->seckey), 0) + 1;
+ key_new_blob = tor_malloc_zero(len);
+ if (base64_encode(key_new_blob, len, (const char *) sk->seckey,
+ sizeof(sk->seckey), 0) != (len - 1)) {
+ tor_free(sk);
+ tor_free(key_new_blob);
+ tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
+ key_type_ed25519_v3);
+ goto err;
+ }
+ key_new_alg = key_type_ed25519_v3;
+ }
+ decoded_key->v3 = sk;
+ *hs_version = HS_VERSION_THREE;
+ } else {
+ err_msg = tor_strdup("513 Invalid key type\r\n");
+ goto err;
+ }
+ } else {
+ err_msg = tor_strdup("513 Invalid key type\r\n");
+ goto err;
+ }
+
+ /* Succeeded in loading or generating a private key. */
+ ret = 0;
+
+ err:
+ SMARTLIST_FOREACH(key_args, char *, cp, {
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+ });
+ smartlist_free(key_args);
+
+ if (err_msg_out) {
+ *err_msg_out = err_msg;
+ } else {
+ tor_free(err_msg);
+ }
+ *key_new_alg_out = key_new_alg;
+ *key_new_blob_out = key_new_blob;
+
+ return ret;
+}
+
+/** 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;
+}
+
+static const control_cmd_syntax_t del_onion_syntax = {
+ .min_args = 1, .max_args = 1,
+};
+
+/** Called when we get a DEL_ONION command; parse the body, and remove
+ * the existing ephemeral Onion Service. */
+static int
+handle_control_del_onion(control_connection_t *conn,
+ const control_cmd_args_t *cmd_args)
+{
+ int hs_version = 0;
+ smartlist_t *args = cmd_args->args;
+ tor_assert(smartlist_len(args) == 1);
+
+ const char *service_id = smartlist_get(args, 0);
+ if (rend_valid_v2_service_id(service_id)) {
+ hs_version = HS_VERSION_TWO;
+ } else if (hs_address_is_valid(service_id)) {
+ hs_version = HS_VERSION_THREE;
+ } else {
+ control_write_endreply(conn, 512, "Malformed Onion Service id");
+ goto out;
+ }
+
+ /* Determine if the onion service belongs to this particular control
+ * connection, or if it is in the global list of detached services. If it
+ * is in neither, either the service ID is invalid in some way, or it
+ * explicitly belongs to a different control connection, and an error
+ * should be returned.
+ */
+ smartlist_t *services[2] = {
+ conn->ephemeral_onion_services,
+ detached_onion_services
+ };
+ smartlist_t *onion_services = NULL;
+ int idx = -1;
+ for (size_t i = 0; i < ARRAY_LENGTH(services); i++) {
+ idx = smartlist_string_pos(services[i], service_id);
+ if (idx != -1) {
+ onion_services = services[i];
+ break;
+ }
+ }
+ if (onion_services == NULL) {
+ control_write_endreply(conn, 552, "Unknown Onion Service id");
+ } else {
+ int ret = -1;
+ switch (hs_version) {
+ case HS_VERSION_TWO:
+ ret = rend_service_del_ephemeral(service_id);
+ break;
+ case HS_VERSION_THREE:
+ ret = hs_service_del_ephemeral(service_id);
+ break;
+ default:
+ /* The ret value will be -1 thus hitting the warning below. This should
+ * never happen because of the check at the start of the function. */
+ break;
+ }
+ if (ret < 0) {
+ /* This should *NEVER* fail, since the service is on either the
+ * per-control connection list, or the global one.
+ */
+ log_warn(LD_BUG, "Failed to remove Onion Service %s.",
+ escaped(service_id));
+ tor_fragile_assert();
+ }
+
+ /* Remove/scrub the service_id from the appropriate list. */
+ char *cp = smartlist_get(onion_services, idx);
+ smartlist_del(onion_services, idx);
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+
+ send_control_done(conn);
+ }
+
+ out:
+ return 0;
+}
+
+static const control_cmd_syntax_t obsolete_syntax = {
+ .max_args = UINT_MAX
+};
+
+/**
+ * Called when we get an obsolete command: tell the controller that it is
+ * obsolete.
+ */
+static int
+handle_control_obsolete(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ (void)args;
+ char *command = tor_strdup(conn->current_cmd);
+ tor_strupper(command);
+ control_printf_endreply(conn, 511, "%s is obsolete.", command);
+ tor_free(command);
+ return 0;
+}
+
+/**
+ * Function pointer to a handler function for a controller command.
+ **/
+typedef int (*handler_fn_t) (control_connection_t *conn,
+ const control_cmd_args_t *args);
+
+/**
+ * Definition for a controller command.
+ */
+typedef struct control_cmd_def_t {
+ /**
+ * The name of the command. If the command is multiline, the name must
+ * begin with "+". This is not case-sensitive. */
+ const char *name;
+ /**
+ * A function to execute the command.
+ */
+ handler_fn_t handler;
+ /**
+ * Zero or more CMD_FL_* flags, or'd together.
+ */
+ unsigned flags;
+ /**
+ * For parsed command: a syntax description.
+ */
+ const control_cmd_syntax_t *syntax;
+} control_cmd_def_t;
+
+/**
+ * Indicates that the command's arguments are sensitive, and should be
+ * memwiped after use.
+ */
+#define CMD_FL_WIPE (1u<<0)
+
+/** Macro: declare a command with a one-line argument, a given set of flags,
+ * and a syntax definition.
+ **/
+#define ONE_LINE(name, flags) \
+ { \
+ #name, \
+ handle_control_ ##name, \
+ flags, \
+ &name##_syntax, \
+ }
+
+/**
+ * Macro: declare a command with a multi-line argument and a given set of
+ * flags.
+ **/
+#define MULTLINE(name, flags) \
+ { "+"#name, \
+ handle_control_ ##name, \
+ flags, \
+ &name##_syntax \
+ }
+
+/**
+ * Macro: declare an obsolete command. (Obsolete commands give a different
+ * error than non-existent ones.)
+ **/
+#define OBSOLETE(name) \
+ { #name, \
+ handle_control_obsolete, \
+ 0, \
+ &obsolete_syntax, \
+ }
+
+/**
+ * An array defining all the recognized controller commands.
+ **/
+static const control_cmd_def_t CONTROL_COMMANDS[] =
+{
+ ONE_LINE(setconf, 0),
+ ONE_LINE(resetconf, 0),
+ ONE_LINE(getconf, 0),
+ MULTLINE(loadconf, 0),
+ ONE_LINE(setevents, 0),
+ ONE_LINE(authenticate, CMD_FL_WIPE),
+ ONE_LINE(saveconf, 0),
+ ONE_LINE(signal, 0),
+ ONE_LINE(takeownership, 0),
+ ONE_LINE(dropownership, 0),
+ ONE_LINE(mapaddress, 0),
+ ONE_LINE(getinfo, 0),
+ ONE_LINE(extendcircuit, 0),
+ ONE_LINE(setcircuitpurpose, 0),
+ OBSOLETE(setrouterpurpose),
+ ONE_LINE(attachstream, 0),
+ MULTLINE(postdescriptor, 0),
+ ONE_LINE(redirectstream, 0),
+ ONE_LINE(closestream, 0),
+ ONE_LINE(closecircuit, 0),
+ ONE_LINE(usefeature, 0),
+ ONE_LINE(resolve, 0),
+ ONE_LINE(protocolinfo, 0),
+ ONE_LINE(authchallenge, CMD_FL_WIPE),
+ ONE_LINE(dropguards, 0),
+ ONE_LINE(hsfetch, 0),
+ MULTLINE(hspost, 0),
+ ONE_LINE(add_onion, CMD_FL_WIPE),
+ ONE_LINE(del_onion, CMD_FL_WIPE),
+};
+
+/**
+ * The number of entries in CONTROL_COMMANDS.
+ **/
+static const size_t N_CONTROL_COMMANDS = ARRAY_LENGTH(CONTROL_COMMANDS);
+
+/**
+ * Run a single control command, as defined by a control_cmd_def_t,
+ * with a given set of arguments.
+ */
+static int
+handle_single_control_command(const control_cmd_def_t *def,
+ control_connection_t *conn,
+ uint32_t cmd_data_len,
+ char *args)
+{
+ int rv = 0;
+
+ control_cmd_args_t *parsed_args;
+ char *err=NULL;
+ tor_assert(def->syntax);
+ parsed_args = control_cmd_parse_args(conn->current_cmd,
+ def->syntax,
+ cmd_data_len, args,
+ &err);
+ if (!parsed_args) {
+ control_printf_endreply(conn, 512, "Bad arguments to %s: %s",
+ conn->current_cmd, err?err:"");
+ tor_free(err);
+ } else {
+ if (BUG(err))
+ tor_free(err);
+ if (def->handler(conn, parsed_args))
+ rv = 0;
+
+ if (def->flags & CMD_FL_WIPE)
+ control_cmd_args_wipe(parsed_args);
+
+ control_cmd_args_free(parsed_args);
+ }
+
+ if (def->flags & CMD_FL_WIPE)
+ memwipe(args, 0, cmd_data_len);
+
+ return rv;
+}
+
+/**
+ * Run a given controller command, as selected by the current_cmd field of
+ * <b>conn</b>.
+ */
+int
+handle_control_command(control_connection_t *conn,
+ uint32_t cmd_data_len,
+ char *args)
+{
+ tor_assert(conn);
+ tor_assert(args);
+ tor_assert(args[cmd_data_len] == '\0');
+
+ for (unsigned i = 0; i < N_CONTROL_COMMANDS; ++i) {
+ const control_cmd_def_t *def = &CONTROL_COMMANDS[i];
+ if (!strcasecmp(conn->current_cmd, def->name)) {
+ return handle_single_control_command(def, conn, cmd_data_len, args);
+ }
+ }
+
+ control_printf_endreply(conn, 510, "Unrecognized command \"%s\"",
+ conn->current_cmd);
+
+ return 0;
+}
+
+void
+control_cmd_free_all(void)
+{
+ if (detached_onion_services) { /* Free the detached onion services */
+ SMARTLIST_FOREACH(detached_onion_services, char *, cp, tor_free(cp));
+ smartlist_free(detached_onion_services);
+ }
+}
diff --git a/src/feature/control/control_cmd.h b/src/feature/control/control_cmd.h
new file mode 100644
index 0000000000..5c3d1a1cec
--- /dev/null
+++ b/src/feature/control/control_cmd.h
@@ -0,0 +1,112 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_cmd.h
+ * \brief Header file for control_cmd.c.
+ **/
+
+#ifndef TOR_CONTROL_CMD_H
+#define TOR_CONTROL_CMD_H
+
+#include "lib/malloc/malloc.h"
+
+int handle_control_command(control_connection_t *conn,
+ uint32_t cmd_data_len,
+ char *args);
+void control_cmd_free_all(void);
+
+typedef struct control_cmd_args_t control_cmd_args_t;
+void control_cmd_args_free_(control_cmd_args_t *args);
+void control_cmd_args_wipe(control_cmd_args_t *args);
+
+#define control_cmd_args_free(v) \
+ FREE_AND_NULL(control_cmd_args_t, control_cmd_args_free_, (v))
+
+/**
+ * Definition for the syntax of a controller command, as parsed by
+ * control_cmd_parse_args.
+ *
+ * WORK IN PROGRESS: This structure is going to get more complex as this
+ * branch goes on.
+ **/
+typedef struct control_cmd_syntax_t {
+ /**
+ * Lowest number of positional arguments that this command accepts.
+ * 0 for "it's okay not to have positional arguments."
+ **/
+ unsigned int min_args;
+ /**
+ * Highest number of positional arguments that this command accepts.
+ * UINT_MAX for no limit.
+ **/
+ unsigned int max_args;
+ /**
+ * If true, we should parse options after the positional arguments
+ * as a set of unordered flags and key=value arguments.
+ *
+ * Requires that max_args is not UINT_MAX.
+ **/
+ bool accept_keywords;
+ /**
+ * If accept_keywords is true, then only the keywords listed in this
+ * (NULL-terminated) array are valid keywords for this command.
+ **/
+ const char **allowed_keywords;
+ /**
+ * If accept_keywords is true, this option is passed to kvline_parse() as
+ * its flags.
+ **/
+ unsigned kvline_flags;
+ /**
+ * True iff this command wants to be followed by a multiline object.
+ **/
+ bool want_cmddata;
+ /**
+ * True iff this command needs access to the raw body of the input.
+ *
+ * This should not be needed for pure commands; it is purely a legacy
+ * option.
+ **/
+ bool store_raw_body;
+} control_cmd_syntax_t;
+
+#ifdef CONTROL_CMD_PRIVATE
+#include "lib/crypt_ops/crypto_ed25519.h"
+
+/* ADD_ONION secret key to create an ephemeral service. The command supports
+ * multiple versions so this union stores the key and passes it to the HS
+ * subsystem depending on the requested version. */
+typedef union add_onion_secret_key_t {
+ /* Hidden service v2 secret key. */
+ crypto_pk_t *v2;
+ /* Hidden service v3 secret key. */
+ ed25519_secret_key_t *v3;
+} add_onion_secret_key_t;
+
+STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk,
+ const char **key_new_alg_out,
+ char **key_new_blob_out,
+ add_onion_secret_key_t *decoded_key,
+ int *hs_version, char **err_msg_out);
+
+STATIC rend_authorized_client_t *add_onion_helper_clientauth(const char *arg,
+ int *created, char **err_msg_out);
+
+STATIC control_cmd_args_t *control_cmd_parse_args(
+ const char *command,
+ const control_cmd_syntax_t *syntax,
+ size_t body_len,
+ const char *body,
+ char **error_out);
+
+#endif /* defined(CONTROL_CMD_PRIVATE) */
+
+#ifdef CONTROL_MODULE_PRIVATE
+smartlist_t * get_detached_onion_services(void);
+#endif /* defined(CONTROL_MODULE_PRIVATE) */
+
+#endif /* !defined(TOR_CONTROL_CMD_H) */
diff --git a/src/feature/control/control_cmd_args_st.h b/src/feature/control/control_cmd_args_st.h
new file mode 100644
index 0000000000..8d7a4f55b3
--- /dev/null
+++ b/src/feature/control/control_cmd_args_st.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_cmd_args_st.h
+ * \brief Definition for control_cmd_args_t
+ **/
+
+#ifndef TOR_CONTROL_CMD_ST_H
+#define TOR_CONTROL_CMD_ST_H
+
+struct smartlist_t;
+struct config_line_t;
+
+/**
+ * Parsed arguments for a control command.
+ *
+ * WORK IN PROGRESS: This structure is going to get more complex as this
+ * branch goes on.
+ **/
+struct control_cmd_args_t {
+ /**
+ * The command itself, as provided by the controller. Not owned by this
+ * structure.
+ **/
+ const char *command;
+ /**
+ * Positional arguments to the command.
+ **/
+ struct smartlist_t *args;
+ /**
+ * Keyword arguments to the command.
+ **/
+ struct config_line_t *kwargs;
+ /**
+ * Number of bytes in <b>cmddata</b>; 0 if <b>cmddata</b> is not set.
+ **/
+ size_t cmddata_len;
+ /**
+ * A multiline object passed with this command.
+ **/
+ char *cmddata;
+ /**
+ * If set, a nul-terminated string containing the raw unparsed arguments.
+ **/
+ const char *raw_body;
+};
+
+#endif /* !defined(TOR_CONTROL_CMD_ST_H) */
diff --git a/src/feature/control/control_connection_st.h b/src/feature/control/control_connection_st.h
index 177a916257..c9164f03b3 100644
--- a/src/feature/control/control_connection_st.h
+++ b/src/feature/control/control_connection_st.h
@@ -40,7 +40,8 @@ struct control_connection_t {
/** A control command that we're reading from the inbuf, but which has not
* yet arrived completely. */
char *incoming_cmd;
+ /** The control command that we are currently processing. */
+ char *current_cmd;
};
-#endif
-
+#endif /* !defined(CONTROL_CONNECTION_ST_H) */
diff --git a/src/feature/control/control_events.c b/src/feature/control/control_events.c
new file mode 100644
index 0000000000..9e0966ca54
--- /dev/null
+++ b/src/feature/control/control_events.c
@@ -0,0 +1,2318 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_events.c
+ * \brief Implement the event-reporting part of the controller API.
+ **/
+
+#define CONTROL_MODULE_PRIVATE
+#define CONTROL_EVENTS_PRIVATE
+#define OCIRC_EVENT_PRIVATE
+
+#include "core/or/or.h"
+#include "app/config/config.h"
+#include "core/mainloop/connection.h"
+#include "core/mainloop/mainloop.h"
+#include "core/or/channeltls.h"
+#include "core/or/circuitlist.h"
+#include "core/or/command.h"
+#include "core/or/connection_edge.h"
+#include "core/or/connection_or.h"
+#include "core/or/reasons.h"
+#include "feature/control/control.h"
+#include "feature/control/control_events.h"
+#include "feature/control/control_fmt.h"
+#include "feature/control/control_proto.h"
+#include "feature/dircommon/directory.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerinfo.h"
+
+#include "feature/control/control_connection_st.h"
+#include "core/or/entry_connection_st.h"
+#include "feature/nodelist/networkstatus_st.h"
+#include "core/or/or_connection_st.h"
+#include "core/or/or_circuit_st.h"
+#include "core/or/origin_circuit_st.h"
+
+#include "lib/evloop/compat_libevent.h"
+
+static void flush_queued_events_cb(mainloop_event_t *event, void *arg);
+static void control_get_bytes_rw_last_sec(uint64_t *r, uint64_t *w);
+
+/** Yield true iff <b>s</b> is the state of a control_connection_t that has
+ * finished authentication and is accepting commands. */
+#define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN)
+
+/** An event mask of all the events that any controller is interested in
+ * receiving. */
+static event_mask_t global_event_mask = 0;
+
+/** True iff we have disabled log messages from being sent to the controller */
+static int disable_log_messages = 0;
+
+/** Macro: true if any control connection is interested in events of type
+ * <b>e</b>. */
+#define EVENT_IS_INTERESTING(e) \
+ (!! (global_event_mask & EVENT_MASK_(e)))
+
+/** Macro: true if any event from the bitfield 'e' is interesting. */
+#define ANY_EVENT_IS_INTERESTING(e) \
+ (!! (global_event_mask & (e)))
+
+static void send_control_event_impl(uint16_t event,
+ const char *format, va_list ap)
+ CHECK_PRINTF(2,0);
+static int control_event_status(int type, int severity, const char *format,
+ va_list args)
+ CHECK_PRINTF(3,0);
+
+static void send_control_event(uint16_t event,
+ const char *format, ...)
+ CHECK_PRINTF(2,3);
+
+/** Table mapping event values to their names. Used to implement SETEVENTS
+ * and GETINFO events/names, and to keep they in sync. */
+const struct control_event_t control_event_table[] = {
+ { EVENT_CIRCUIT_STATUS, "CIRC" },
+ { EVENT_CIRCUIT_STATUS_MINOR, "CIRC_MINOR" },
+ { EVENT_STREAM_STATUS, "STREAM" },
+ { EVENT_OR_CONN_STATUS, "ORCONN" },
+ { EVENT_BANDWIDTH_USED, "BW" },
+ { EVENT_DEBUG_MSG, "DEBUG" },
+ { EVENT_INFO_MSG, "INFO" },
+ { EVENT_NOTICE_MSG, "NOTICE" },
+ { EVENT_WARN_MSG, "WARN" },
+ { EVENT_ERR_MSG, "ERR" },
+ { EVENT_NEW_DESC, "NEWDESC" },
+ { EVENT_ADDRMAP, "ADDRMAP" },
+ { EVENT_DESCCHANGED, "DESCCHANGED" },
+ { EVENT_NS, "NS" },
+ { EVENT_STATUS_GENERAL, "STATUS_GENERAL" },
+ { EVENT_STATUS_CLIENT, "STATUS_CLIENT" },
+ { EVENT_STATUS_SERVER, "STATUS_SERVER" },
+ { EVENT_GUARD, "GUARD" },
+ { EVENT_STREAM_BANDWIDTH_USED, "STREAM_BW" },
+ { EVENT_CLIENTS_SEEN, "CLIENTS_SEEN" },
+ { EVENT_NEWCONSENSUS, "NEWCONSENSUS" },
+ { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" },
+ { EVENT_GOT_SIGNAL, "SIGNAL" },
+ { EVENT_CONF_CHANGED, "CONF_CHANGED"},
+ { EVENT_CONN_BW, "CONN_BW" },
+ { EVENT_CELL_STATS, "CELL_STATS" },
+ { EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" },
+ { EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" },
+ { EVENT_HS_DESC, "HS_DESC" },
+ { EVENT_HS_DESC_CONTENT, "HS_DESC_CONTENT" },
+ { EVENT_NETWORK_LIVENESS, "NETWORK_LIVENESS" },
+ { 0, NULL },
+};
+
+/** Given a log severity, return the corresponding control event code. */
+static inline int
+log_severity_to_event(int severity)
+{
+ switch (severity) {
+ case LOG_DEBUG: return EVENT_DEBUG_MSG;
+ case LOG_INFO: return EVENT_INFO_MSG;
+ case LOG_NOTICE: return EVENT_NOTICE_MSG;
+ case LOG_WARN: return EVENT_WARN_MSG;
+ case LOG_ERR: return EVENT_ERR_MSG;
+ default: return -1;
+ }
+}
+
+/** Helper: clear bandwidth counters of all origin circuits. */
+static void
+clear_circ_bw_fields(void)
+{
+ origin_circuit_t *ocirc;
+ SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
+ if (!CIRCUIT_IS_ORIGIN(circ))
+ continue;
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
+ ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
+ ocirc->n_overhead_written_circ_bw = ocirc->n_overhead_read_circ_bw = 0;
+ ocirc->n_delivered_written_circ_bw = ocirc->n_delivered_read_circ_bw = 0;
+ }
+ SMARTLIST_FOREACH_END(circ);
+}
+
+/** Set <b>global_event_mask*</b> to the bitwise OR of each live control
+ * connection's event_mask field. */
+void
+control_update_global_event_mask(void)
+{
+ smartlist_t *conns = get_connection_array();
+ event_mask_t old_mask, new_mask;
+ old_mask = global_event_mask;
+ int any_old_per_sec_events = control_any_per_second_event_enabled();
+
+ global_event_mask = 0;
+ SMARTLIST_FOREACH(conns, connection_t *, _conn,
+ {
+ if (_conn->type == CONN_TYPE_CONTROL &&
+ STATE_IS_OPEN(_conn->state)) {
+ control_connection_t *conn = TO_CONTROL_CONN(_conn);
+ global_event_mask |= conn->event_mask;
+ }
+ });
+
+ new_mask = global_event_mask;
+
+ /* Handle the aftermath. Set up the log callback to tell us only what
+ * we want to hear...*/
+ control_adjust_event_log_severity();
+
+ /* Macro: true if ev was false before and is true now. */
+#define NEWLY_ENABLED(ev) \
+ (! (old_mask & (ev)) && (new_mask & (ev)))
+
+ /* ...then, if we've started logging stream or circ bw, clear the
+ * appropriate fields. */
+ if (NEWLY_ENABLED(EVENT_STREAM_BANDWIDTH_USED)) {
+ SMARTLIST_FOREACH(conns, connection_t *, conn,
+ {
+ if (conn->type == CONN_TYPE_AP) {
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ edge_conn->n_written = edge_conn->n_read = 0;
+ }
+ });
+ }
+ if (NEWLY_ENABLED(EVENT_CIRC_BANDWIDTH_USED)) {
+ clear_circ_bw_fields();
+ }
+ if (NEWLY_ENABLED(EVENT_BANDWIDTH_USED)) {
+ uint64_t r, w;
+ control_get_bytes_rw_last_sec(&r, &w);
+ }
+ if (any_old_per_sec_events != control_any_per_second_event_enabled()) {
+ rescan_periodic_events(get_options());
+ }
+
+#undef NEWLY_ENABLED
+}
+
+/** Given a control event code for a message event, return the corresponding
+ * log severity. */
+static inline int
+event_to_log_severity(int event)
+{
+ switch (event) {
+ case EVENT_DEBUG_MSG: return LOG_DEBUG;
+ case EVENT_INFO_MSG: return LOG_INFO;
+ case EVENT_NOTICE_MSG: return LOG_NOTICE;
+ case EVENT_WARN_MSG: return LOG_WARN;
+ case EVENT_ERR_MSG: return LOG_ERR;
+ default: return -1;
+ }
+}
+
+/** Adjust the log severities that result in control_event_logmsg being called
+ * to match the severity of log messages that any controllers are interested
+ * in. */
+void
+control_adjust_event_log_severity(void)
+{
+ int i;
+ int min_log_event=EVENT_ERR_MSG, max_log_event=EVENT_DEBUG_MSG;
+
+ for (i = EVENT_DEBUG_MSG; i <= EVENT_ERR_MSG; ++i) {
+ if (EVENT_IS_INTERESTING(i)) {
+ min_log_event = i;
+ break;
+ }
+ }
+ for (i = EVENT_ERR_MSG; i >= EVENT_DEBUG_MSG; --i) {
+ if (EVENT_IS_INTERESTING(i)) {
+ max_log_event = i;
+ break;
+ }
+ }
+ if (EVENT_IS_INTERESTING(EVENT_STATUS_GENERAL)) {
+ if (min_log_event > EVENT_NOTICE_MSG)
+ min_log_event = EVENT_NOTICE_MSG;
+ if (max_log_event < EVENT_ERR_MSG)
+ max_log_event = EVENT_ERR_MSG;
+ }
+ if (min_log_event <= max_log_event)
+ change_callback_log_severity(event_to_log_severity(min_log_event),
+ event_to_log_severity(max_log_event),
+ control_event_logmsg);
+ else
+ change_callback_log_severity(LOG_ERR, LOG_ERR,
+ control_event_logmsg);
+}
+
+/** Return true iff the event with code <b>c</b> is being sent to any current
+ * control connection. This is useful if the amount of work needed to prepare
+ * to call the appropriate control_event_...() function is high.
+ */
+int
+control_event_is_interesting(int event)
+{
+ return EVENT_IS_INTERESTING(event);
+}
+
+/** Return true if any event that needs to fire once a second is enabled. */
+int
+control_any_per_second_event_enabled(void)
+{
+ return ANY_EVENT_IS_INTERESTING(
+ EVENT_MASK_(EVENT_BANDWIDTH_USED) |
+ EVENT_MASK_(EVENT_CELL_STATS) |
+ EVENT_MASK_(EVENT_CIRC_BANDWIDTH_USED) |
+ EVENT_MASK_(EVENT_CONN_BW) |
+ EVENT_MASK_(EVENT_STREAM_BANDWIDTH_USED)
+ );
+}
+
+/* The value of 'get_bytes_read()' the previous time that
+ * control_get_bytes_rw_last_sec() as called. */
+static uint64_t stats_prev_n_read = 0;
+/* The value of 'get_bytes_written()' the previous time that
+ * control_get_bytes_rw_last_sec() as called. */
+static uint64_t stats_prev_n_written = 0;
+
+/**
+ * Set <b>n_read</b> and <b>n_written</b> to the total number of bytes read
+ * and written by Tor since the last call to this function.
+ *
+ * Call this only from the main thread.
+ */
+static void
+control_get_bytes_rw_last_sec(uint64_t *n_read,
+ uint64_t *n_written)
+{
+ const uint64_t stats_n_bytes_read = get_bytes_read();
+ const uint64_t stats_n_bytes_written = get_bytes_written();
+
+ *n_read = stats_n_bytes_read - stats_prev_n_read;
+ *n_written = stats_n_bytes_written - stats_prev_n_written;
+ stats_prev_n_read = stats_n_bytes_read;
+ stats_prev_n_written = stats_n_bytes_written;
+}
+
+/**
+ * Run all the controller events (if any) that are scheduled to trigger once
+ * per second.
+ */
+void
+control_per_second_events(void)
+{
+ if (!control_any_per_second_event_enabled())
+ return;
+
+ uint64_t bytes_read, bytes_written;
+ control_get_bytes_rw_last_sec(&bytes_read, &bytes_written);
+ control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
+
+ control_event_stream_bandwidth_used();
+ control_event_conn_bandwidth_used();
+ control_event_circ_bandwidth_used();
+ control_event_circuit_cell_stats();
+}
+
+/** Represents an event that's queued to be sent to one or more
+ * controllers. */
+typedef struct queued_event_s {
+ uint16_t event;
+ char *msg;
+} queued_event_t;
+
+/** 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_flag;
+
+/** Holds a smartlist of queued_event_t objects that may need to be sent
+ * to one or more controllers */
+static smartlist_t *queued_control_events = NULL;
+
+/** True if the flush_queued_events_event is pending. */
+static int flush_queued_event_pending = 0;
+
+/** Lock to protect the above fields. */
+static tor_mutex_t *queued_control_events_lock = NULL;
+
+/** An event that should fire in order to flush the contents of
+ * queued_control_events. */
+static mainloop_event_t *flush_queued_events_event = NULL;
+
+void
+control_initialize_event_queue(void)
+{
+ if (queued_control_events == NULL) {
+ queued_control_events = smartlist_new();
+ }
+
+ if (flush_queued_events_event == NULL) {
+ struct event_base *b = tor_libevent_get_base();
+ if (b) {
+ flush_queued_events_event =
+ mainloop_event_new(flush_queued_events_cb, NULL);
+ tor_assert(flush_queued_events_event);
+ }
+ }
+
+ if (queued_control_events_lock == NULL) {
+ queued_control_events_lock = tor_mutex_new();
+ tor_threadlocal_init(&block_event_queue_flag);
+ }
+}
+
+static int *
+get_block_event_queue(void)
+{
+ 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_flag, val);
+ }
+ return val;
+}
+
+/** Helper: inserts an event on the list of events queued to be sent to
+ * one or more controllers, and schedules the events to be flushed if needed.
+ *
+ * This function takes ownership of <b>msg</b>, and may free it.
+ *
+ * We queue these events rather than send them immediately in order to break
+ * the dependency in our callgraph from code that generates events for the
+ * controller, and the network layer at large. Otherwise, nearly every
+ * interesting part of Tor would potentially call every other interesting part
+ * of Tor.
+ */
+MOCK_IMPL(STATIC void,
+queue_control_event_string,(uint16_t event, char *msg))
+{
+ /* This is redundant with checks done elsewhere, but it's a last-ditch
+ * attempt to avoid queueing something we shouldn't have to queue. */
+ if (PREDICT_UNLIKELY( ! EVENT_IS_INTERESTING(event) )) {
+ tor_free(msg);
+ return;
+ }
+
+ int *block_event_queue = get_block_event_queue();
+ if (*block_event_queue) {
+ tor_free(msg);
+ return;
+ }
+
+ queued_event_t *ev = tor_malloc(sizeof(*ev));
+ ev->event = event;
+ ev->msg = msg;
+
+ /* No queueing an event while queueing an event */
+ ++*block_event_queue;
+
+ tor_mutex_acquire(queued_control_events_lock);
+ tor_assert(queued_control_events);
+ smartlist_add(queued_control_events, ev);
+
+ int activate_event = 0;
+ if (! flush_queued_event_pending && in_main_thread()) {
+ activate_event = 1;
+ flush_queued_event_pending = 1;
+ }
+
+ tor_mutex_release(queued_control_events_lock);
+
+ --*block_event_queue;
+
+ /* We just put an event on the queue; mark the queue to be
+ * flushed. We only do this from the main thread for now; otherwise,
+ * we'd need to incur locking overhead in Libevent or use a socket.
+ */
+ if (activate_event) {
+ tor_assert(flush_queued_events_event);
+ mainloop_event_activate(flush_queued_events_event);
+ }
+}
+
+#define queued_event_free(ev) \
+ FREE_AND_NULL(queued_event_t, queued_event_free_, (ev))
+
+/** Release all storage held by <b>ev</b>. */
+static void
+queued_event_free_(queued_event_t *ev)
+{
+ if (ev == NULL)
+ return;
+
+ tor_free(ev->msg);
+ tor_free(ev);
+}
+
+/** Send every queued event to every controller that's interested in it,
+ * and remove the events from the queue. If <b>force</b> is true,
+ * then make all controllers send their data out immediately, since we
+ * may be about to shut down. */
+static void
+queued_events_flush_all(int force)
+{
+ /* Make sure that we get all the pending log events, if there are any. */
+ flush_pending_log_callbacks();
+
+ if (PREDICT_UNLIKELY(queued_control_events == NULL)) {
+ return;
+ }
+ smartlist_t *all_conns = get_connection_array();
+ smartlist_t *controllers = smartlist_new();
+ smartlist_t *queued_events;
+
+ int *block_event_queue = get_block_event_queue();
+ ++*block_event_queue;
+
+ tor_mutex_acquire(queued_control_events_lock);
+ /* No queueing an event while flushing events. */
+ flush_queued_event_pending = 0;
+ queued_events = queued_control_events;
+ queued_control_events = smartlist_new();
+ tor_mutex_release(queued_control_events_lock);
+
+ /* Gather all the controllers that will care... */
+ SMARTLIST_FOREACH_BEGIN(all_conns, connection_t *, conn) {
+ if (conn->type == CONN_TYPE_CONTROL &&
+ !conn->marked_for_close &&
+ conn->state == CONTROL_CONN_STATE_OPEN) {
+ control_connection_t *control_conn = TO_CONTROL_CONN(conn);
+
+ smartlist_add(controllers, control_conn);
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
+ SMARTLIST_FOREACH_BEGIN(queued_events, queued_event_t *, ev) {
+ const event_mask_t bit = ((event_mask_t)1) << ev->event;
+ const size_t msg_len = strlen(ev->msg);
+ SMARTLIST_FOREACH_BEGIN(controllers, control_connection_t *,
+ control_conn) {
+ if (control_conn->event_mask & bit) {
+ connection_buf_add(ev->msg, msg_len, TO_CONN(control_conn));
+ }
+ } SMARTLIST_FOREACH_END(control_conn);
+
+ queued_event_free(ev);
+ } SMARTLIST_FOREACH_END(ev);
+
+ if (force) {
+ SMARTLIST_FOREACH_BEGIN(controllers, control_connection_t *,
+ control_conn) {
+ connection_flush(TO_CONN(control_conn));
+ } SMARTLIST_FOREACH_END(control_conn);
+ }
+
+ smartlist_free(queued_events);
+ smartlist_free(controllers);
+
+ --*block_event_queue;
+}
+
+/** Libevent callback: Flushes pending events to controllers that are
+ * interested in them. */
+static void
+flush_queued_events_cb(mainloop_event_t *event, void *arg)
+{
+ (void) event;
+ (void) arg;
+ queued_events_flush_all(0);
+}
+
+/** Send an event to all v1 controllers that are listening for code
+ * <b>event</b>. The event's body is given by <b>msg</b>.
+ *
+ * The EXTENDED_FORMAT and NONEXTENDED_FORMAT flags behave similarly with
+ * respect to the EXTENDED_EVENTS feature. */
+MOCK_IMPL(STATIC void,
+send_control_event_string,(uint16_t event,
+ const char *msg))
+{
+ tor_assert(event >= EVENT_MIN_ && event <= EVENT_MAX_);
+ queue_control_event_string(event, tor_strdup(msg));
+}
+
+/** Helper for send_control_event and control_event_status:
+ * Send an event to all v1 controllers that are listening for code
+ * <b>event</b>. The event's body is created by the printf-style format in
+ * <b>format</b>, and other arguments as provided. */
+static void
+send_control_event_impl(uint16_t event,
+ const char *format, va_list ap)
+{
+ char *buf = NULL;
+ int len;
+
+ len = tor_vasprintf(&buf, format, ap);
+ if (len < 0) {
+ log_warn(LD_BUG, "Unable to format event for controller.");
+ return;
+ }
+
+ queue_control_event_string(event, buf);
+}
+
+/** Send an event to all v1 controllers that are listening for code
+ * <b>event</b>. The event's body is created by the printf-style format in
+ * <b>format</b>, and other arguments as provided. */
+static void
+send_control_event(uint16_t event,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ send_control_event_impl(event, format, ap);
+ va_end(ap);
+}
+
+/** Something major has happened to circuit <b>circ</b>: tell any
+ * interested control connections. */
+int
+control_event_circuit_status(origin_circuit_t *circ, circuit_status_event_t tp,
+ int reason_code)
+{
+ const char *status;
+ char reasons[64] = "";
+
+ if (!EVENT_IS_INTERESTING(EVENT_CIRCUIT_STATUS))
+ return 0;
+ tor_assert(circ);
+
+ switch (tp)
+ {
+ case CIRC_EVENT_LAUNCHED: status = "LAUNCHED"; break;
+ case CIRC_EVENT_BUILT: status = "BUILT"; break;
+ case CIRC_EVENT_EXTENDED: status = "EXTENDED"; break;
+ case CIRC_EVENT_FAILED: status = "FAILED"; break;
+ case CIRC_EVENT_CLOSED: status = "CLOSED"; break;
+ default:
+ log_warn(LD_BUG, "Unrecognized status code %d", (int)tp);
+ tor_fragile_assert();
+ return 0;
+ }
+
+ if (tp == CIRC_EVENT_FAILED || tp == CIRC_EVENT_CLOSED) {
+ const char *reason_str = circuit_end_reason_to_control_string(reason_code);
+ char unk_reason_buf[16];
+ if (!reason_str) {
+ tor_snprintf(unk_reason_buf, 16, "UNKNOWN_%d", reason_code);
+ reason_str = unk_reason_buf;
+ }
+ if (reason_code > 0 && reason_code & END_CIRC_REASON_FLAG_REMOTE) {
+ tor_snprintf(reasons, sizeof(reasons),
+ " REASON=DESTROYED REMOTE_REASON=%s", reason_str);
+ } else {
+ tor_snprintf(reasons, sizeof(reasons),
+ " REASON=%s", reason_str);
+ }
+ }
+
+ {
+ char *circdesc = circuit_describe_status_for_controller(circ);
+ const char *sp = strlen(circdesc) ? " " : "";
+ send_control_event(EVENT_CIRCUIT_STATUS,
+ "650 CIRC %lu %s%s%s%s\r\n",
+ (unsigned long)circ->global_identifier,
+ status, sp,
+ circdesc,
+ reasons);
+ tor_free(circdesc);
+ }
+
+ return 0;
+}
+
+/** Something minor has happened to circuit <b>circ</b>: tell any
+ * interested control connections. */
+static int
+control_event_circuit_status_minor(origin_circuit_t *circ,
+ circuit_status_minor_event_t e,
+ int purpose, const struct timeval *tv)
+{
+ const char *event_desc;
+ char event_tail[160] = "";
+ if (!EVENT_IS_INTERESTING(EVENT_CIRCUIT_STATUS_MINOR))
+ return 0;
+ tor_assert(circ);
+
+ switch (e)
+ {
+ case CIRC_MINOR_EVENT_PURPOSE_CHANGED:
+ event_desc = "PURPOSE_CHANGED";
+
+ {
+ /* event_tail can currently be up to 68 chars long */
+ const char *hs_state_str =
+ circuit_purpose_to_controller_hs_state_string(purpose);
+ tor_snprintf(event_tail, sizeof(event_tail),
+ " OLD_PURPOSE=%s%s%s",
+ circuit_purpose_to_controller_string(purpose),
+ (hs_state_str != NULL) ? " OLD_HS_STATE=" : "",
+ (hs_state_str != NULL) ? hs_state_str : "");
+ }
+
+ break;
+ case CIRC_MINOR_EVENT_CANNIBALIZED:
+ event_desc = "CANNIBALIZED";
+
+ {
+ /* event_tail can currently be up to 130 chars long */
+ const char *hs_state_str =
+ circuit_purpose_to_controller_hs_state_string(purpose);
+ const struct timeval *old_timestamp_began = tv;
+ char tbuf[ISO_TIME_USEC_LEN+1];
+ format_iso_time_nospace_usec(tbuf, old_timestamp_began);
+
+ tor_snprintf(event_tail, sizeof(event_tail),
+ " OLD_PURPOSE=%s%s%s OLD_TIME_CREATED=%s",
+ circuit_purpose_to_controller_string(purpose),
+ (hs_state_str != NULL) ? " OLD_HS_STATE=" : "",
+ (hs_state_str != NULL) ? hs_state_str : "",
+ tbuf);
+ }
+
+ break;
+ default:
+ log_warn(LD_BUG, "Unrecognized status code %d", (int)e);
+ tor_fragile_assert();
+ return 0;
+ }
+
+ {
+ char *circdesc = circuit_describe_status_for_controller(circ);
+ const char *sp = strlen(circdesc) ? " " : "";
+ send_control_event(EVENT_CIRCUIT_STATUS_MINOR,
+ "650 CIRC_MINOR %lu %s%s%s%s\r\n",
+ (unsigned long)circ->global_identifier,
+ event_desc, sp,
+ circdesc,
+ event_tail);
+ tor_free(circdesc);
+ }
+
+ return 0;
+}
+
+/**
+ * <b>circ</b> has changed its purpose from <b>old_purpose</b>: tell any
+ * interested controllers.
+ */
+int
+control_event_circuit_purpose_changed(origin_circuit_t *circ,
+ int old_purpose)
+{
+ return control_event_circuit_status_minor(circ,
+ CIRC_MINOR_EVENT_PURPOSE_CHANGED,
+ old_purpose,
+ NULL);
+}
+
+/**
+ * <b>circ</b> has changed its purpose from <b>old_purpose</b>, and its
+ * created-time from <b>old_tv_created</b>: tell any interested controllers.
+ */
+int
+control_event_circuit_cannibalized(origin_circuit_t *circ,
+ int old_purpose,
+ const struct timeval *old_tv_created)
+{
+ return control_event_circuit_status_minor(circ,
+ CIRC_MINOR_EVENT_CANNIBALIZED,
+ old_purpose,
+ old_tv_created);
+}
+
+/** Something has happened to the stream associated with AP connection
+ * <b>conn</b>: tell any interested control connections. */
+int
+control_event_stream_status(entry_connection_t *conn, stream_status_event_t tp,
+ int reason_code)
+{
+ char reason_buf[64];
+ char addrport_buf[64];
+ const char *status;
+ circuit_t *circ;
+ origin_circuit_t *origin_circ = NULL;
+ char buf[256];
+ const char *purpose = "";
+ tor_assert(conn->socks_request);
+
+ if (!EVENT_IS_INTERESTING(EVENT_STREAM_STATUS))
+ return 0;
+
+ if (tp == STREAM_EVENT_CLOSED &&
+ (reason_code & END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED))
+ return 0;
+
+ write_stream_target_to_buf(conn, buf, sizeof(buf));
+
+ reason_buf[0] = '\0';
+ switch (tp)
+ {
+ case STREAM_EVENT_SENT_CONNECT: status = "SENTCONNECT"; break;
+ case STREAM_EVENT_SENT_RESOLVE: status = "SENTRESOLVE"; break;
+ case STREAM_EVENT_SUCCEEDED: status = "SUCCEEDED"; break;
+ case STREAM_EVENT_FAILED: status = "FAILED"; break;
+ case STREAM_EVENT_CLOSED: status = "CLOSED"; break;
+ case STREAM_EVENT_NEW: status = "NEW"; break;
+ case STREAM_EVENT_NEW_RESOLVE: status = "NEWRESOLVE"; break;
+ case STREAM_EVENT_FAILED_RETRIABLE: status = "DETACHED"; break;
+ case STREAM_EVENT_REMAP: status = "REMAP"; break;
+ default:
+ log_warn(LD_BUG, "Unrecognized status code %d", (int)tp);
+ return 0;
+ }
+ if (reason_code && (tp == STREAM_EVENT_FAILED ||
+ tp == STREAM_EVENT_CLOSED ||
+ tp == STREAM_EVENT_FAILED_RETRIABLE)) {
+ const char *reason_str = stream_end_reason_to_control_string(reason_code);
+ char *r = NULL;
+ if (!reason_str) {
+ tor_asprintf(&r, " UNKNOWN_%d", reason_code);
+ reason_str = r;
+ }
+ if (reason_code & END_STREAM_REASON_FLAG_REMOTE)
+ tor_snprintf(reason_buf, sizeof(reason_buf),
+ " REASON=END REMOTE_REASON=%s", reason_str);
+ else
+ tor_snprintf(reason_buf, sizeof(reason_buf),
+ " REASON=%s", reason_str);
+ tor_free(r);
+ } else if (reason_code && tp == STREAM_EVENT_REMAP) {
+ switch (reason_code) {
+ case REMAP_STREAM_SOURCE_CACHE:
+ strlcpy(reason_buf, " SOURCE=CACHE", sizeof(reason_buf));
+ break;
+ case REMAP_STREAM_SOURCE_EXIT:
+ strlcpy(reason_buf, " SOURCE=EXIT", sizeof(reason_buf));
+ break;
+ default:
+ tor_snprintf(reason_buf, sizeof(reason_buf), " REASON=UNKNOWN_%d",
+ reason_code);
+ /* XXX do we want SOURCE=UNKNOWN_%d above instead? -RD */
+ break;
+ }
+ }
+
+ if (tp == STREAM_EVENT_NEW || tp == STREAM_EVENT_NEW_RESOLVE) {
+ /*
+ * When the control conn is an AF_UNIX socket and we have no address,
+ * it gets set to "(Tor_internal)"; see dnsserv_launch_request() in
+ * dnsserv.c.
+ */
+ if (strcmp(ENTRY_TO_CONN(conn)->address, "(Tor_internal)") != 0) {
+ tor_snprintf(addrport_buf,sizeof(addrport_buf), " SOURCE_ADDR=%s:%d",
+ ENTRY_TO_CONN(conn)->address, ENTRY_TO_CONN(conn)->port);
+ } else {
+ /*
+ * else leave it blank so control on AF_UNIX doesn't need to make
+ * something up.
+ */
+ addrport_buf[0] = '\0';
+ }
+ } else {
+ addrport_buf[0] = '\0';
+ }
+
+ if (tp == STREAM_EVENT_NEW_RESOLVE) {
+ purpose = " PURPOSE=DNS_REQUEST";
+ } else if (tp == STREAM_EVENT_NEW) {
+ if (conn->use_begindir) {
+ connection_t *linked = ENTRY_TO_CONN(conn)->linked_conn;
+ int linked_dir_purpose = -1;
+ if (linked && linked->type == CONN_TYPE_DIR)
+ linked_dir_purpose = linked->purpose;
+ if (DIR_PURPOSE_IS_UPLOAD(linked_dir_purpose))
+ purpose = " PURPOSE=DIR_UPLOAD";
+ else
+ purpose = " PURPOSE=DIR_FETCH";
+ } else
+ purpose = " PURPOSE=USER";
+ }
+
+ circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn));
+ if (circ && CIRCUIT_IS_ORIGIN(circ))
+ origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ send_control_event(EVENT_STREAM_STATUS,
+ "650 STREAM %"PRIu64" %s %lu %s%s%s%s\r\n",
+ (ENTRY_TO_CONN(conn)->global_identifier),
+ status,
+ origin_circ?
+ (unsigned long)origin_circ->global_identifier : 0ul,
+ buf, reason_buf, addrport_buf, purpose);
+
+ /* XXX need to specify its intended exit, etc? */
+
+ return 0;
+}
+
+/** Called when the status of an OR connection <b>conn</b> changes: tell any
+ * interested control connections. <b>tp</b> is the new status for the
+ * connection. If <b>conn</b> has just closed or failed, then <b>reason</b>
+ * may be the reason why.
+ */
+int
+control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp,
+ int reason)
+{
+ int ncircs = 0;
+ const char *status;
+ char name[128];
+ char ncircs_buf[32] = {0}; /* > 8 + log10(2^32)=10 + 2 */
+
+ if (!EVENT_IS_INTERESTING(EVENT_OR_CONN_STATUS))
+ return 0;
+
+ switch (tp)
+ {
+ case OR_CONN_EVENT_LAUNCHED: status = "LAUNCHED"; break;
+ case OR_CONN_EVENT_CONNECTED: status = "CONNECTED"; break;
+ case OR_CONN_EVENT_FAILED: status = "FAILED"; break;
+ case OR_CONN_EVENT_CLOSED: status = "CLOSED"; break;
+ case OR_CONN_EVENT_NEW: status = "NEW"; break;
+ default:
+ log_warn(LD_BUG, "Unrecognized status code %d", (int)tp);
+ return 0;
+ }
+ if (conn->chan) {
+ ncircs = circuit_count_pending_on_channel(TLS_CHAN_TO_BASE(conn->chan));
+ } else {
+ ncircs = 0;
+ }
+ ncircs += connection_or_get_num_circuits(conn);
+ if (ncircs && (tp == OR_CONN_EVENT_FAILED || tp == OR_CONN_EVENT_CLOSED)) {
+ tor_snprintf(ncircs_buf, sizeof(ncircs_buf), " NCIRCS=%d", ncircs);
+ }
+
+ orconn_target_get_name(name, sizeof(name), conn);
+ send_control_event(EVENT_OR_CONN_STATUS,
+ "650 ORCONN %s %s%s%s%s ID=%"PRIu64"\r\n",
+ name, status,
+ reason ? " REASON=" : "",
+ orconn_end_reason_to_control_string(reason),
+ ncircs_buf,
+ (conn->base_.global_identifier));
+
+ return 0;
+}
+
+/**
+ * Print out STREAM_BW event for a single conn
+ */
+int
+control_event_stream_bandwidth(edge_connection_t *edge_conn)
+{
+ struct timeval now;
+ char tbuf[ISO_TIME_USEC_LEN+1];
+ if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
+ if (!edge_conn->n_read && !edge_conn->n_written)
+ return 0;
+
+ tor_gettimeofday(&now);
+ format_iso_time_nospace_usec(tbuf, &now);
+ send_control_event(EVENT_STREAM_BANDWIDTH_USED,
+ "650 STREAM_BW %"PRIu64" %lu %lu %s\r\n",
+ (edge_conn->base_.global_identifier),
+ (unsigned long)edge_conn->n_read,
+ (unsigned long)edge_conn->n_written,
+ tbuf);
+
+ edge_conn->n_written = edge_conn->n_read = 0;
+ }
+
+ return 0;
+}
+
+/** A second or more has elapsed: tell any interested control
+ * connections how much bandwidth streams have used. */
+int
+control_event_stream_bandwidth_used(void)
+{
+ if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
+ smartlist_t *conns = get_connection_array();
+ edge_connection_t *edge_conn;
+ struct timeval now;
+ char tbuf[ISO_TIME_USEC_LEN+1];
+
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn)
+ {
+ if (conn->type != CONN_TYPE_AP)
+ continue;
+ edge_conn = TO_EDGE_CONN(conn);
+ if (!edge_conn->n_read && !edge_conn->n_written)
+ continue;
+
+ tor_gettimeofday(&now);
+ format_iso_time_nospace_usec(tbuf, &now);
+ send_control_event(EVENT_STREAM_BANDWIDTH_USED,
+ "650 STREAM_BW %"PRIu64" %lu %lu %s\r\n",
+ (edge_conn->base_.global_identifier),
+ (unsigned long)edge_conn->n_read,
+ (unsigned long)edge_conn->n_written,
+ tbuf);
+
+ edge_conn->n_written = edge_conn->n_read = 0;
+ }
+ SMARTLIST_FOREACH_END(conn);
+ }
+
+ return 0;
+}
+
+/** A second or more has elapsed: tell any interested control connections
+ * how much bandwidth origin circuits have used. */
+int
+control_event_circ_bandwidth_used(void)
+{
+ if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED))
+ return 0;
+
+ SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
+ if (!CIRCUIT_IS_ORIGIN(circ))
+ continue;
+
+ control_event_circ_bandwidth_used_for_circ(TO_ORIGIN_CIRCUIT(circ));
+ }
+ SMARTLIST_FOREACH_END(circ);
+
+ return 0;
+}
+
+/**
+ * Emit a CIRC_BW event line for a specific circuit.
+ *
+ * This function sets the values it emits to 0, and does not emit
+ * an event if there is no new data to report since the last call.
+ *
+ * Therefore, it may be called at any frequency.
+ */
+int
+control_event_circ_bandwidth_used_for_circ(origin_circuit_t *ocirc)
+{
+ struct timeval now;
+ char tbuf[ISO_TIME_USEC_LEN+1];
+
+ tor_assert(ocirc);
+
+ if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED))
+ return 0;
+
+ /* n_read_circ_bw and n_written_circ_bw are always updated
+ * when there is any new cell on a circuit, and set to 0 after
+ * the event, below.
+ *
+ * Therefore, checking them is sufficient to determine if there
+ * is new data to report. */
+ if (!ocirc->n_read_circ_bw && !ocirc->n_written_circ_bw)
+ return 0;
+
+ tor_gettimeofday(&now);
+ format_iso_time_nospace_usec(tbuf, &now);
+ send_control_event(EVENT_CIRC_BANDWIDTH_USED,
+ "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu TIME=%s "
+ "DELIVERED_READ=%lu OVERHEAD_READ=%lu "
+ "DELIVERED_WRITTEN=%lu OVERHEAD_WRITTEN=%lu\r\n",
+ ocirc->global_identifier,
+ (unsigned long)ocirc->n_read_circ_bw,
+ (unsigned long)ocirc->n_written_circ_bw,
+ tbuf,
+ (unsigned long)ocirc->n_delivered_read_circ_bw,
+ (unsigned long)ocirc->n_overhead_read_circ_bw,
+ (unsigned long)ocirc->n_delivered_written_circ_bw,
+ (unsigned long)ocirc->n_overhead_written_circ_bw);
+ ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
+ ocirc->n_overhead_written_circ_bw = ocirc->n_overhead_read_circ_bw = 0;
+ ocirc->n_delivered_written_circ_bw = ocirc->n_delivered_read_circ_bw = 0;
+
+ return 0;
+}
+
+/** Print out CONN_BW event for a single OR/DIR/EXIT <b>conn</b> and reset
+ * bandwidth counters. */
+int
+control_event_conn_bandwidth(connection_t *conn)
+{
+ const char *conn_type_str;
+ if (!get_options()->TestingEnableConnBwEvent ||
+ !EVENT_IS_INTERESTING(EVENT_CONN_BW))
+ return 0;
+ if (!conn->n_read_conn_bw && !conn->n_written_conn_bw)
+ return 0;
+ switch (conn->type) {
+ case CONN_TYPE_OR:
+ conn_type_str = "OR";
+ break;
+ case CONN_TYPE_DIR:
+ conn_type_str = "DIR";
+ break;
+ case CONN_TYPE_EXIT:
+ conn_type_str = "EXIT";
+ break;
+ default:
+ return 0;
+ }
+ send_control_event(EVENT_CONN_BW,
+ "650 CONN_BW ID=%"PRIu64" TYPE=%s "
+ "READ=%lu WRITTEN=%lu\r\n",
+ (conn->global_identifier),
+ conn_type_str,
+ (unsigned long)conn->n_read_conn_bw,
+ (unsigned long)conn->n_written_conn_bw);
+ conn->n_written_conn_bw = conn->n_read_conn_bw = 0;
+ return 0;
+}
+
+/** A second or more has elapsed: tell any interested control
+ * connections how much bandwidth connections have used. */
+int
+control_event_conn_bandwidth_used(void)
+{
+ if (get_options()->TestingEnableConnBwEvent &&
+ EVENT_IS_INTERESTING(EVENT_CONN_BW)) {
+ SMARTLIST_FOREACH(get_connection_array(), connection_t *, conn,
+ control_event_conn_bandwidth(conn));
+ }
+ return 0;
+}
+
+/** Helper: iterate over cell statistics of <b>circ</b> and sum up added
+ * cells, removed cells, and waiting times by cell command and direction.
+ * Store results in <b>cell_stats</b>. Free cell statistics of the
+ * circuit afterwards. */
+void
+sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats)
+{
+ memset(cell_stats, 0, sizeof(cell_stats_t));
+ SMARTLIST_FOREACH_BEGIN(circ->testing_cell_stats,
+ const testing_cell_stats_entry_t *, ent) {
+ tor_assert(ent->command <= CELL_COMMAND_MAX_);
+ if (!ent->removed && !ent->exitward) {
+ cell_stats->added_cells_appward[ent->command] += 1;
+ } else if (!ent->removed && ent->exitward) {
+ cell_stats->added_cells_exitward[ent->command] += 1;
+ } else if (!ent->exitward) {
+ cell_stats->removed_cells_appward[ent->command] += 1;
+ cell_stats->total_time_appward[ent->command] += ent->waiting_time * 10;
+ } else {
+ cell_stats->removed_cells_exitward[ent->command] += 1;
+ cell_stats->total_time_exitward[ent->command] += ent->waiting_time * 10;
+ }
+ } SMARTLIST_FOREACH_END(ent);
+ circuit_clear_testing_cell_stats(circ);
+}
+
+/** Helper: append a cell statistics string to <code>event_parts</code>,
+ * prefixed with <code>key</code>=. Statistics consist of comma-separated
+ * key:value pairs with lower-case command strings as keys and cell
+ * numbers or total waiting times as values. A key:value pair is included
+ * if the entry in <code>include_if_non_zero</code> is not zero, but with
+ * the (possibly zero) entry from <code>number_to_include</code>. Both
+ * arrays are expected to have a length of CELL_COMMAND_MAX_ + 1. If no
+ * entry in <code>include_if_non_zero</code> is positive, no string will
+ * be added to <code>event_parts</code>. */
+void
+append_cell_stats_by_command(smartlist_t *event_parts, const char *key,
+ const uint64_t *include_if_non_zero,
+ const uint64_t *number_to_include)
+{
+ smartlist_t *key_value_strings = smartlist_new();
+ int i;
+ for (i = 0; i <= CELL_COMMAND_MAX_; i++) {
+ if (include_if_non_zero[i] > 0) {
+ smartlist_add_asprintf(key_value_strings, "%s:%"PRIu64,
+ cell_command_to_string(i),
+ (number_to_include[i]));
+ }
+ }
+ if (smartlist_len(key_value_strings) > 0) {
+ char *joined = smartlist_join_strings(key_value_strings, ",", 0, NULL);
+ smartlist_add_asprintf(event_parts, "%s=%s", key, joined);
+ SMARTLIST_FOREACH(key_value_strings, char *, cp, tor_free(cp));
+ tor_free(joined);
+ }
+ smartlist_free(key_value_strings);
+}
+
+/** Helper: format <b>cell_stats</b> for <b>circ</b> for inclusion in a
+ * CELL_STATS event and write result string to <b>event_string</b>. */
+void
+format_cell_stats(char **event_string, circuit_t *circ,
+ cell_stats_t *cell_stats)
+{
+ smartlist_t *event_parts = smartlist_new();
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ smartlist_add_asprintf(event_parts, "ID=%lu",
+ (unsigned long)ocirc->global_identifier);
+ } else if (TO_OR_CIRCUIT(circ)->p_chan) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ smartlist_add_asprintf(event_parts, "InboundQueue=%lu",
+ (unsigned long)or_circ->p_circ_id);
+ smartlist_add_asprintf(event_parts, "InboundConn=%"PRIu64,
+ (or_circ->p_chan->global_identifier));
+ append_cell_stats_by_command(event_parts, "InboundAdded",
+ cell_stats->added_cells_appward,
+ cell_stats->added_cells_appward);
+ append_cell_stats_by_command(event_parts, "InboundRemoved",
+ cell_stats->removed_cells_appward,
+ cell_stats->removed_cells_appward);
+ append_cell_stats_by_command(event_parts, "InboundTime",
+ cell_stats->removed_cells_appward,
+ cell_stats->total_time_appward);
+ }
+ if (circ->n_chan) {
+ smartlist_add_asprintf(event_parts, "OutboundQueue=%lu",
+ (unsigned long)circ->n_circ_id);
+ smartlist_add_asprintf(event_parts, "OutboundConn=%"PRIu64,
+ (circ->n_chan->global_identifier));
+ append_cell_stats_by_command(event_parts, "OutboundAdded",
+ cell_stats->added_cells_exitward,
+ cell_stats->added_cells_exitward);
+ append_cell_stats_by_command(event_parts, "OutboundRemoved",
+ cell_stats->removed_cells_exitward,
+ cell_stats->removed_cells_exitward);
+ append_cell_stats_by_command(event_parts, "OutboundTime",
+ cell_stats->removed_cells_exitward,
+ cell_stats->total_time_exitward);
+ }
+ *event_string = smartlist_join_strings(event_parts, " ", 0, NULL);
+ SMARTLIST_FOREACH(event_parts, char *, cp, tor_free(cp));
+ smartlist_free(event_parts);
+}
+
+/** A second or more has elapsed: tell any interested control connection
+ * how many cells have been processed for a given circuit. */
+int
+control_event_circuit_cell_stats(void)
+{
+ cell_stats_t *cell_stats;
+ char *event_string;
+ if (!get_options()->TestingEnableCellStatsEvent ||
+ !EVENT_IS_INTERESTING(EVENT_CELL_STATS))
+ return 0;
+ cell_stats = tor_malloc(sizeof(cell_stats_t));
+ SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
+ if (!circ->testing_cell_stats)
+ continue;
+ sum_up_cell_stats_by_command(circ, cell_stats);
+ format_cell_stats(&event_string, circ, cell_stats);
+ send_control_event(EVENT_CELL_STATS,
+ "650 CELL_STATS %s\r\n", event_string);
+ tor_free(event_string);
+ }
+ SMARTLIST_FOREACH_END(circ);
+ tor_free(cell_stats);
+ return 0;
+}
+
+/* about 5 minutes worth. */
+#define N_BW_EVENTS_TO_CACHE 300
+/* Index into cached_bw_events to next write. */
+static int next_measurement_idx = 0;
+/* number of entries set in n_measurements */
+static int n_measurements = 0;
+static struct cached_bw_event_s {
+ uint32_t n_read;
+ uint32_t n_written;
+} cached_bw_events[N_BW_EVENTS_TO_CACHE];
+
+/** A second or more has elapsed: tell any interested control
+ * connections how much bandwidth we used. */
+int
+control_event_bandwidth_used(uint32_t n_read, uint32_t n_written)
+{
+ cached_bw_events[next_measurement_idx].n_read = n_read;
+ cached_bw_events[next_measurement_idx].n_written = n_written;
+ if (++next_measurement_idx == N_BW_EVENTS_TO_CACHE)
+ next_measurement_idx = 0;
+ if (n_measurements < N_BW_EVENTS_TO_CACHE)
+ ++n_measurements;
+
+ if (EVENT_IS_INTERESTING(EVENT_BANDWIDTH_USED)) {
+ send_control_event(EVENT_BANDWIDTH_USED,
+ "650 BW %lu %lu\r\n",
+ (unsigned long)n_read,
+ (unsigned long)n_written);
+ }
+
+ return 0;
+}
+
+char *
+get_bw_samples(void)
+{
+ int i;
+ int idx = (next_measurement_idx + N_BW_EVENTS_TO_CACHE - n_measurements)
+ % N_BW_EVENTS_TO_CACHE;
+ tor_assert(0 <= idx && idx < N_BW_EVENTS_TO_CACHE);
+
+ smartlist_t *elements = smartlist_new();
+
+ for (i = 0; i < n_measurements; ++i) {
+ tor_assert(0 <= idx && idx < N_BW_EVENTS_TO_CACHE);
+ const struct cached_bw_event_s *bwe = &cached_bw_events[idx];
+
+ smartlist_add_asprintf(elements, "%u,%u",
+ (unsigned)bwe->n_read,
+ (unsigned)bwe->n_written);
+
+ idx = (idx + 1) % N_BW_EVENTS_TO_CACHE;
+ }
+
+ char *result = smartlist_join_strings(elements, " ", 0, NULL);
+
+ SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
+ smartlist_free(elements);
+
+ return result;
+}
+
+/** Called when we are sending a log message to the controllers: suspend
+ * sending further log messages to the controllers until we're done. Used by
+ * CONN_LOG_PROTECT. */
+void
+disable_control_logging(void)
+{
+ ++disable_log_messages;
+}
+
+/** We're done sending a log message to the controllers: re-enable controller
+ * logging. Used by CONN_LOG_PROTECT. */
+void
+enable_control_logging(void)
+{
+ if (--disable_log_messages < 0)
+ tor_assert(0);
+}
+
+/** We got a log message: tell any interested control connections. */
+void
+control_event_logmsg(int severity, log_domain_mask_t domain, const char *msg)
+{
+ int event;
+
+ /* Don't even think of trying to add stuff to a buffer from a cpuworker
+ * thread. (See #25987 for plan to fix.) */
+ if (! in_main_thread())
+ return;
+
+ if (disable_log_messages)
+ return;
+
+ if (domain == LD_BUG && EVENT_IS_INTERESTING(EVENT_STATUS_GENERAL) &&
+ severity <= LOG_NOTICE) {
+ char *esc = esc_for_log(msg);
+ ++disable_log_messages;
+ control_event_general_status(severity, "BUG REASON=%s", esc);
+ --disable_log_messages;
+ tor_free(esc);
+ }
+
+ event = log_severity_to_event(severity);
+ if (event >= 0 && EVENT_IS_INTERESTING(event)) {
+ char *b = NULL;
+ const char *s;
+ if (strchr(msg, '\n')) {
+ char *cp;
+ b = tor_strdup(msg);
+ for (cp = b; *cp; ++cp)
+ if (*cp == '\r' || *cp == '\n')
+ *cp = ' ';
+ }
+ switch (severity) {
+ case LOG_DEBUG: s = "DEBUG"; break;
+ case LOG_INFO: s = "INFO"; break;
+ case LOG_NOTICE: s = "NOTICE"; break;
+ case LOG_WARN: s = "WARN"; break;
+ case LOG_ERR: s = "ERR"; break;
+ default: s = "UnknownLogSeverity"; break;
+ }
+ ++disable_log_messages;
+ send_control_event(event, "650 %s %s\r\n", s, b?b:msg);
+ if (severity == LOG_ERR) {
+ /* Force a flush, since we may be about to die horribly */
+ queued_events_flush_all(1);
+ }
+ --disable_log_messages;
+ tor_free(b);
+ }
+}
+
+/**
+ * Logging callback: called when there is a queued pending log callback.
+ */
+void
+control_event_logmsg_pending(void)
+{
+ if (! in_main_thread()) {
+ /* We can't handle this case yet, since we're using a
+ * mainloop_event_t to invoke queued_events_flush_all. We ought to
+ * use a different mechanism instead: see #25987.
+ **/
+ return;
+ }
+ tor_assert(flush_queued_events_event);
+ mainloop_event_activate(flush_queued_events_event);
+}
+
+/** Called whenever we receive new router descriptors: tell any
+ * interested control connections. <b>routers</b> is a list of
+ * routerinfo_t's.
+ */
+int
+control_event_descriptors_changed(smartlist_t *routers)
+{
+ char *msg;
+
+ if (!EVENT_IS_INTERESTING(EVENT_NEW_DESC))
+ return 0;
+
+ {
+ smartlist_t *names = smartlist_new();
+ char *ids;
+ SMARTLIST_FOREACH(routers, routerinfo_t *, ri, {
+ char *b = tor_malloc(MAX_VERBOSE_NICKNAME_LEN+1);
+ router_get_verbose_nickname(b, ri);
+ smartlist_add(names, b);
+ });
+ ids = smartlist_join_strings(names, " ", 0, NULL);
+ tor_asprintf(&msg, "650 NEWDESC %s\r\n", ids);
+ send_control_event_string(EVENT_NEW_DESC, msg);
+ tor_free(ids);
+ tor_free(msg);
+ SMARTLIST_FOREACH(names, char *, cp, tor_free(cp));
+ smartlist_free(names);
+ }
+ return 0;
+}
+
+/** Called when an address mapping on <b>from</b> from changes to <b>to</b>.
+ * <b>expires</b> values less than 3 are special; see connection_edge.c. If
+ * <b>error</b> is non-NULL, it is an error code describing the failure
+ * mode of the mapping.
+ */
+int
+control_event_address_mapped(const char *from, const char *to, time_t expires,
+ const char *error, const int cached)
+{
+ if (!EVENT_IS_INTERESTING(EVENT_ADDRMAP))
+ return 0;
+
+ if (expires < 3 || expires == TIME_MAX)
+ send_control_event(EVENT_ADDRMAP,
+ "650 ADDRMAP %s %s NEVER %s%s"
+ "CACHED=\"%s\"\r\n",
+ from, to, error?error:"", error?" ":"",
+ cached?"YES":"NO");
+ else {
+ char buf[ISO_TIME_LEN+1];
+ char buf2[ISO_TIME_LEN+1];
+ format_local_iso_time(buf,expires);
+ format_iso_time(buf2,expires);
+ send_control_event(EVENT_ADDRMAP,
+ "650 ADDRMAP %s %s \"%s\""
+ " %s%sEXPIRES=\"%s\" CACHED=\"%s\"\r\n",
+ from, to, buf,
+ error?error:"", error?" ":"",
+ buf2, cached?"YES":"NO");
+ }
+
+ return 0;
+}
+/** The network liveness has changed; this is called from circuitstats.c
+ * whenever we receive a cell, or when timeout expires and we assume the
+ * network is down. */
+int
+control_event_network_liveness_update(int liveness)
+{
+ if (liveness > 0) {
+ if (get_cached_network_liveness() <= 0) {
+ /* Update cached liveness */
+ set_cached_network_liveness(1);
+ log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS UP");
+ send_control_event_string(EVENT_NETWORK_LIVENESS,
+ "650 NETWORK_LIVENESS UP\r\n");
+ }
+ /* else was already live, no-op */
+ } else {
+ if (get_cached_network_liveness() > 0) {
+ /* Update cached liveness */
+ set_cached_network_liveness(0);
+ log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS DOWN");
+ send_control_event_string(EVENT_NETWORK_LIVENESS,
+ "650 NETWORK_LIVENESS DOWN\r\n");
+ }
+ /* else was already dead, no-op */
+ }
+
+ return 0;
+}
+
+/** Helper function for NS-style events. Constructs and sends an event
+ * of type <b>event</b> with string <b>event_string</b> out of the set of
+ * networkstatuses <b>statuses</b>. Currently it is used for NS events
+ * and NEWCONSENSUS events. */
+static int
+control_event_networkstatus_changed_helper(smartlist_t *statuses,
+ uint16_t event,
+ const char *event_string)
+{
+ smartlist_t *strs;
+ char *s, *esc = NULL;
+ if (!EVENT_IS_INTERESTING(event) || !smartlist_len(statuses))
+ return 0;
+
+ strs = smartlist_new();
+ smartlist_add_strdup(strs, "650+");
+ smartlist_add_strdup(strs, event_string);
+ smartlist_add_strdup(strs, "\r\n");
+ SMARTLIST_FOREACH(statuses, const routerstatus_t *, rs,
+ {
+ s = networkstatus_getinfo_helper_single(rs);
+ if (!s) continue;
+ smartlist_add(strs, s);
+ });
+
+ s = smartlist_join_strings(strs, "", 0, NULL);
+ write_escaped_data(s, strlen(s), &esc);
+ SMARTLIST_FOREACH(strs, char *, cp, tor_free(cp));
+ smartlist_free(strs);
+ tor_free(s);
+ send_control_event_string(event, esc);
+ send_control_event_string(event,
+ "650 OK\r\n");
+
+ tor_free(esc);
+ return 0;
+}
+
+/** Called when the routerstatus_ts <b>statuses</b> have changed: sends
+ * an NS event to any controller that cares. */
+int
+control_event_networkstatus_changed(smartlist_t *statuses)
+{
+ return control_event_networkstatus_changed_helper(statuses, EVENT_NS, "NS");
+}
+
+/** Called when we get a new consensus networkstatus. Sends a NEWCONSENSUS
+ * event consisting of an NS-style line for each relay in the consensus. */
+int
+control_event_newconsensus(const networkstatus_t *consensus)
+{
+ if (!control_event_is_interesting(EVENT_NEWCONSENSUS))
+ return 0;
+ return control_event_networkstatus_changed_helper(
+ consensus->routerstatus_list, EVENT_NEWCONSENSUS, "NEWCONSENSUS");
+}
+
+/** Called when we compute a new circuitbuildtimeout */
+int
+control_event_buildtimeout_set(buildtimeout_set_event_t type,
+ const char *args)
+{
+ const char *type_string = NULL;
+
+ if (!control_event_is_interesting(EVENT_BUILDTIMEOUT_SET))
+ return 0;
+
+ switch (type) {
+ case BUILDTIMEOUT_SET_EVENT_COMPUTED:
+ type_string = "COMPUTED";
+ break;
+ case BUILDTIMEOUT_SET_EVENT_RESET:
+ type_string = "RESET";
+ break;
+ case BUILDTIMEOUT_SET_EVENT_SUSPENDED:
+ type_string = "SUSPENDED";
+ break;
+ case BUILDTIMEOUT_SET_EVENT_DISCARD:
+ type_string = "DISCARD";
+ break;
+ case BUILDTIMEOUT_SET_EVENT_RESUME:
+ type_string = "RESUME";
+ break;
+ default:
+ type_string = "UNKNOWN";
+ break;
+ }
+
+ send_control_event(EVENT_BUILDTIMEOUT_SET,
+ "650 BUILDTIMEOUT_SET %s %s\r\n",
+ type_string, args);
+
+ return 0;
+}
+
+/** Called when a signal has been processed from signal_callback */
+int
+control_event_signal(uintptr_t signal_num)
+{
+ const char *signal_string = NULL;
+
+ if (!control_event_is_interesting(EVENT_GOT_SIGNAL))
+ return 0;
+
+ switch (signal_num) {
+ case SIGHUP:
+ signal_string = "RELOAD";
+ break;
+ case SIGUSR1:
+ signal_string = "DUMP";
+ break;
+ case SIGUSR2:
+ signal_string = "DEBUG";
+ break;
+ case SIGNEWNYM:
+ signal_string = "NEWNYM";
+ break;
+ case SIGCLEARDNSCACHE:
+ signal_string = "CLEARDNSCACHE";
+ break;
+ case SIGHEARTBEAT:
+ signal_string = "HEARTBEAT";
+ break;
+ default:
+ log_warn(LD_BUG, "Unrecognized signal %lu in control_event_signal",
+ (unsigned long)signal_num);
+ return -1;
+ }
+
+ send_control_event(EVENT_GOT_SIGNAL, "650 SIGNAL %s\r\n",
+ signal_string);
+ return 0;
+}
+
+/** Called when a single local_routerstatus_t has changed: Sends an NS event
+ * to any controller that cares. */
+int
+control_event_networkstatus_changed_single(const routerstatus_t *rs)
+{
+ smartlist_t *statuses;
+ int r;
+
+ if (!EVENT_IS_INTERESTING(EVENT_NS))
+ return 0;
+
+ statuses = smartlist_new();
+ smartlist_add(statuses, (void*)rs);
+ r = control_event_networkstatus_changed(statuses);
+ smartlist_free(statuses);
+ return r;
+}
+
+/** Our own router descriptor has changed; tell any controllers that care.
+ */
+int
+control_event_my_descriptor_changed(void)
+{
+ send_control_event(EVENT_DESCCHANGED, "650 DESCCHANGED\r\n");
+ return 0;
+}
+
+/** Helper: sends a status event where <b>type</b> is one of
+ * EVENT_STATUS_{GENERAL,CLIENT,SERVER}, where <b>severity</b> is one of
+ * LOG_{NOTICE,WARN,ERR}, and where <b>format</b> is a printf-style format
+ * string corresponding to <b>args</b>. */
+static int
+control_event_status(int type, int severity, const char *format, va_list args)
+{
+ char *user_buf = NULL;
+ char format_buf[160];
+ const char *status, *sev;
+
+ switch (type) {
+ case EVENT_STATUS_GENERAL:
+ status = "STATUS_GENERAL";
+ break;
+ case EVENT_STATUS_CLIENT:
+ status = "STATUS_CLIENT";
+ break;
+ case EVENT_STATUS_SERVER:
+ status = "STATUS_SERVER";
+ break;
+ default:
+ log_warn(LD_BUG, "Unrecognized status type %d", type);
+ return -1;
+ }
+ switch (severity) {
+ case LOG_NOTICE:
+ sev = "NOTICE";
+ break;
+ case LOG_WARN:
+ sev = "WARN";
+ break;
+ case LOG_ERR:
+ sev = "ERR";
+ break;
+ default:
+ log_warn(LD_BUG, "Unrecognized status severity %d", severity);
+ return -1;
+ }
+ if (tor_snprintf(format_buf, sizeof(format_buf), "650 %s %s",
+ status, sev)<0) {
+ log_warn(LD_BUG, "Format string too long.");
+ return -1;
+ }
+ tor_vasprintf(&user_buf, format, args);
+
+ send_control_event(type, "%s %s\r\n", format_buf, user_buf);
+ tor_free(user_buf);
+ return 0;
+}
+
+#define CONTROL_EVENT_STATUS_BODY(event, sev) \
+ int r; \
+ do { \
+ va_list ap; \
+ if (!EVENT_IS_INTERESTING(event)) \
+ return 0; \
+ \
+ va_start(ap, format); \
+ r = control_event_status((event), (sev), format, ap); \
+ va_end(ap); \
+ } while (0)
+
+/** Format and send an EVENT_STATUS_GENERAL event whose main text is obtained
+ * by formatting the arguments using the printf-style <b>format</b>. */
+int
+control_event_general_status(int severity, const char *format, ...)
+{
+ CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_GENERAL, severity);
+ return r;
+}
+
+/** Format and send an EVENT_STATUS_GENERAL LOG_ERR event, and flush it to the
+ * controller(s) immediately. */
+int
+control_event_general_error(const char *format, ...)
+{
+ CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_GENERAL, LOG_ERR);
+ /* Force a flush, since we may be about to die horribly */
+ queued_events_flush_all(1);
+ return r;
+}
+
+/** Format and send an EVENT_STATUS_CLIENT event whose main text is obtained
+ * by formatting the arguments using the printf-style <b>format</b>. */
+int
+control_event_client_status(int severity, const char *format, ...)
+{
+ CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_CLIENT, severity);
+ return r;
+}
+
+/** Format and send an EVENT_STATUS_CLIENT LOG_ERR event, and flush it to the
+ * controller(s) immediately. */
+int
+control_event_client_error(const char *format, ...)
+{
+ CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_CLIENT, LOG_ERR);
+ /* Force a flush, since we may be about to die horribly */
+ queued_events_flush_all(1);
+ return r;
+}
+
+/** Format and send an EVENT_STATUS_SERVER event whose main text is obtained
+ * by formatting the arguments using the printf-style <b>format</b>. */
+int
+control_event_server_status(int severity, const char *format, ...)
+{
+ CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_SERVER, severity);
+ return r;
+}
+
+/** Format and send an EVENT_STATUS_SERVER LOG_ERR event, and flush it to the
+ * controller(s) immediately. */
+int
+control_event_server_error(const char *format, ...)
+{
+ CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_SERVER, LOG_ERR);
+ /* Force a flush, since we may be about to die horribly */
+ queued_events_flush_all(1);
+ return r;
+}
+
+/** Called when the status of an entry guard with the given <b>nickname</b>
+ * and identity <b>digest</b> has changed to <b>status</b>: tells any
+ * controllers that care. */
+int
+control_event_guard(const char *nickname, const char *digest,
+ const char *status)
+{
+ char hbuf[HEX_DIGEST_LEN+1];
+ base16_encode(hbuf, sizeof(hbuf), digest, DIGEST_LEN);
+ if (!EVENT_IS_INTERESTING(EVENT_GUARD))
+ return 0;
+
+ {
+ char buf[MAX_VERBOSE_NICKNAME_LEN+1];
+ const node_t *node = node_get_by_id(digest);
+ if (node) {
+ node_get_verbose_nickname(node, buf);
+ } else {
+ tor_snprintf(buf, sizeof(buf), "$%s~%s", hbuf, nickname);
+ }
+ send_control_event(EVENT_GUARD,
+ "650 GUARD ENTRY %s %s\r\n", buf, status);
+ }
+ return 0;
+}
+
+/** Called when a configuration option changes. This is generally triggered
+ * by SETCONF requests and RELOAD/SIGHUP signals. The <b>elements</b> is
+ * a smartlist_t containing (key, value, ...) pairs in sequence.
+ * <b>value</b> can be NULL. */
+int
+control_event_conf_changed(const smartlist_t *elements)
+{
+ int i;
+ char *result;
+ smartlist_t *lines;
+ if (!EVENT_IS_INTERESTING(EVENT_CONF_CHANGED) ||
+ smartlist_len(elements) == 0) {
+ return 0;
+ }
+ lines = smartlist_new();
+ for (i = 0; i < smartlist_len(elements); i += 2) {
+ char *k = smartlist_get(elements, i);
+ char *v = smartlist_get(elements, i+1);
+ if (v == NULL) {
+ smartlist_add_asprintf(lines, "650-%s", k);
+ } else {
+ smartlist_add_asprintf(lines, "650-%s=%s", k, v);
+ }
+ }
+ result = smartlist_join_strings(lines, "\r\n", 0, NULL);
+ send_control_event(EVENT_CONF_CHANGED,
+ "650-CONF_CHANGED\r\n%s\r\n650 OK\r\n", result);
+ tor_free(result);
+ SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
+ smartlist_free(lines);
+ return 0;
+}
+
+/** We just generated a new summary of which countries we've seen clients
+ * from recently. Send a copy to the controller in case it wants to
+ * display it for the user. */
+void
+control_event_clients_seen(const char *controller_str)
+{
+ send_control_event(EVENT_CLIENTS_SEEN,
+ "650 CLIENTS_SEEN %s\r\n", controller_str);
+}
+
+/** A new pluggable transport called <b>transport_name</b> was
+ * launched on <b>addr</b>:<b>port</b>. <b>mode</b> is either
+ * "server" or "client" depending on the mode of the pluggable
+ * transport.
+ * "650" SP "TRANSPORT_LAUNCHED" SP Mode SP Name SP Address SP Port
+ */
+void
+control_event_transport_launched(const char *mode, const char *transport_name,
+ tor_addr_t *addr, uint16_t port)
+{
+ send_control_event(EVENT_TRANSPORT_LAUNCHED,
+ "650 TRANSPORT_LAUNCHED %s %s %s %u\r\n",
+ mode, transport_name, fmt_addr(addr), port);
+}
+
+/** A pluggable transport called <b>pt_name</b> has emitted a log message
+ * found in <b>message</b> at <b>severity</b> log level. */
+void
+control_event_pt_log(const char *log)
+{
+ send_control_event(EVENT_PT_LOG,
+ "650 PT_LOG %s\r\n",
+ log);
+}
+
+/** A pluggable transport has emitted a STATUS message found in
+ * <b>status</b>. */
+void
+control_event_pt_status(const char *status)
+{
+ send_control_event(EVENT_PT_STATUS,
+ "650 PT_STATUS %s\r\n",
+ status);
+}
+
+/** Convert rendezvous auth type to string for HS_DESC control events
+ */
+const char *
+rend_auth_type_to_string(rend_auth_type_t auth_type)
+{
+ const char *str;
+
+ switch (auth_type) {
+ case REND_NO_AUTH:
+ str = "NO_AUTH";
+ break;
+ case REND_BASIC_AUTH:
+ str = "BASIC_AUTH";
+ break;
+ case REND_STEALTH_AUTH:
+ str = "STEALTH_AUTH";
+ break;
+ default:
+ str = "UNKNOWN";
+ }
+
+ return str;
+}
+
+/** Return either the onion address if the given pointer is a non empty
+ * string else the unknown string. */
+static const char *
+rend_hsaddress_str_or_unknown(const char *onion_address)
+{
+ static const char *str_unknown = "UNKNOWN";
+ const char *str_ret = str_unknown;
+
+ /* No valid pointer, unknown it is. */
+ if (!onion_address) {
+ goto end;
+ }
+ /* Empty onion address thus we don't know, unknown it is. */
+ if (onion_address[0] == '\0') {
+ goto end;
+ }
+ /* All checks are good so return the given onion address. */
+ str_ret = onion_address;
+
+ end:
+ return str_ret;
+}
+
+/** send HS_DESC requested event.
+ *
+ * <b>rend_query</b> is used to fetch requested onion address and auth type.
+ * <b>hs_dir</b> is the description of contacting hs directory.
+ * <b>desc_id_base32</b> is the ID of requested hs descriptor.
+ * <b>hsdir_index</b> is the HSDir fetch index value for v3, an hex string.
+ */
+void
+control_event_hs_descriptor_requested(const char *onion_address,
+ rend_auth_type_t auth_type,
+ const char *id_digest,
+ const char *desc_id,
+ const char *hsdir_index)
+{
+ char *hsdir_index_field = NULL;
+
+ if (BUG(!id_digest || !desc_id)) {
+ return;
+ }
+
+ if (hsdir_index) {
+ tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index);
+ }
+
+ send_control_event(EVENT_HS_DESC,
+ "650 HS_DESC REQUESTED %s %s %s %s%s\r\n",
+ rend_hsaddress_str_or_unknown(onion_address),
+ rend_auth_type_to_string(auth_type),
+ node_describe_longname_by_id(id_digest),
+ desc_id,
+ hsdir_index_field ? hsdir_index_field : "");
+ tor_free(hsdir_index_field);
+}
+
+/** send HS_DESC CREATED event when a local service generates a descriptor.
+ *
+ * <b>onion_address</b> is service address.
+ * <b>desc_id</b> is the descriptor ID.
+ * <b>replica</b> is the the descriptor replica number. If it is negative, it
+ * is ignored.
+ */
+void
+control_event_hs_descriptor_created(const char *onion_address,
+ const char *desc_id,
+ int replica)
+{
+ char *replica_field = NULL;
+
+ if (BUG(!onion_address || !desc_id)) {
+ return;
+ }
+
+ if (replica >= 0) {
+ tor_asprintf(&replica_field, " REPLICA=%d", replica);
+ }
+
+ send_control_event(EVENT_HS_DESC,
+ "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s%s\r\n",
+ onion_address, desc_id,
+ replica_field ? replica_field : "");
+ tor_free(replica_field);
+}
+
+/** send HS_DESC upload event.
+ *
+ * <b>onion_address</b> is service address.
+ * <b>hs_dir</b> is the description of contacting hs directory.
+ * <b>desc_id</b> is the ID of requested hs descriptor.
+ */
+void
+control_event_hs_descriptor_upload(const char *onion_address,
+ const char *id_digest,
+ const char *desc_id,
+ const char *hsdir_index)
+{
+ char *hsdir_index_field = NULL;
+
+ if (BUG(!onion_address || !id_digest || !desc_id)) {
+ return;
+ }
+
+ if (hsdir_index) {
+ tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index);
+ }
+
+ send_control_event(EVENT_HS_DESC,
+ "650 HS_DESC UPLOAD %s UNKNOWN %s %s%s\r\n",
+ onion_address,
+ node_describe_longname_by_id(id_digest),
+ desc_id,
+ hsdir_index_field ? hsdir_index_field : "");
+ tor_free(hsdir_index_field);
+}
+
+/** send HS_DESC event after got response from hs directory.
+ *
+ * NOTE: this is an internal function used by following functions:
+ * control_event_hsv2_descriptor_received
+ * control_event_hsv2_descriptor_failed
+ * control_event_hsv3_descriptor_failed
+ *
+ * So do not call this function directly.
+ */
+static void
+event_hs_descriptor_receive_end(const char *action,
+ const char *onion_address,
+ const char *desc_id,
+ rend_auth_type_t auth_type,
+ const char *hsdir_id_digest,
+ const char *reason)
+{
+ char *reason_field = NULL;
+
+ if (BUG(!action || !onion_address)) {
+ return;
+ }
+
+ if (reason) {
+ tor_asprintf(&reason_field, " REASON=%s", reason);
+ }
+
+ send_control_event(EVENT_HS_DESC,
+ "650 HS_DESC %s %s %s %s%s%s\r\n",
+ action,
+ rend_hsaddress_str_or_unknown(onion_address),
+ rend_auth_type_to_string(auth_type),
+ hsdir_id_digest ?
+ node_describe_longname_by_id(hsdir_id_digest) :
+ "UNKNOWN",
+ desc_id ? desc_id : "",
+ reason_field ? reason_field : "");
+
+ tor_free(reason_field);
+}
+
+/** send HS_DESC event after got response from hs directory.
+ *
+ * NOTE: this is an internal function used by following functions:
+ * control_event_hs_descriptor_uploaded
+ * control_event_hs_descriptor_upload_failed
+ *
+ * So do not call this function directly.
+ */
+void
+control_event_hs_descriptor_upload_end(const char *action,
+ const char *onion_address,
+ const char *id_digest,
+ const char *reason)
+{
+ char *reason_field = NULL;
+
+ if (BUG(!action || !id_digest)) {
+ return;
+ }
+
+ if (reason) {
+ tor_asprintf(&reason_field, " REASON=%s", reason);
+ }
+
+ send_control_event(EVENT_HS_DESC,
+ "650 HS_DESC %s %s UNKNOWN %s%s\r\n",
+ action,
+ rend_hsaddress_str_or_unknown(onion_address),
+ node_describe_longname_by_id(id_digest),
+ reason_field ? reason_field : "");
+
+ tor_free(reason_field);
+}
+
+/** For an HS descriptor query <b>rend_data</b>, using the
+ * <b>onion_address</b> and HSDir fingerprint <b>hsdir_fp</b>, find out
+ * which descriptor ID in the query is the right one.
+ *
+ * Return a pointer of the binary descriptor ID found in the query's object
+ * or NULL if not found. */
+static const char *
+get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
+{
+ int replica;
+ const char *desc_id = NULL;
+ const rend_data_v2_t *rend_data_v2 = TO_REND_DATA_V2(rend_data);
+
+ /* Possible if the fetch was done using a descriptor ID. This means that
+ * the HSFETCH command was used. */
+ if (!tor_digest_is_zero(rend_data_v2->desc_id_fetch)) {
+ desc_id = rend_data_v2->desc_id_fetch;
+ goto end;
+ }
+
+ /* Without a directory fingerprint at this stage, we can't do much. */
+ if (hsdir_fp == NULL) {
+ goto end;
+ }
+
+ /* OK, we have an onion address so now let's find which descriptor ID
+ * is the one associated with the HSDir fingerprint. */
+ for (replica = 0; replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
+ replica++) {
+ const char *digest = rend_data_get_desc_id(rend_data, replica, NULL);
+
+ SMARTLIST_FOREACH_BEGIN(rend_data->hsdirs_fp, char *, fingerprint) {
+ if (tor_memcmp(fingerprint, hsdir_fp, DIGEST_LEN) == 0) {
+ /* Found it! This descriptor ID is the right one. */
+ desc_id = digest;
+ goto end;
+ }
+ } SMARTLIST_FOREACH_END(fingerprint);
+ }
+
+ end:
+ return desc_id;
+}
+
+/** send HS_DESC RECEIVED event
+ *
+ * called when we successfully received a hidden service descriptor.
+ */
+void
+control_event_hsv2_descriptor_received(const char *onion_address,
+ const rend_data_t *rend_data,
+ const char *hsdir_id_digest)
+{
+ char *desc_id_field = NULL;
+ const char *desc_id;
+
+ if (BUG(!rend_data || !hsdir_id_digest || !onion_address)) {
+ return;
+ }
+
+ desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
+ if (desc_id != NULL) {
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ /* Set the descriptor ID digest to base32 so we can send it. */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
+ /* Extra whitespace is needed before the value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id_base32);
+ }
+
+ event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
+ TO_REND_DATA_V2(rend_data)->auth_type,
+ hsdir_id_digest, NULL);
+ tor_free(desc_id_field);
+}
+
+/* Send HS_DESC RECEIVED event
+ *
+ * Called when we successfully received a hidden service descriptor. */
+void
+control_event_hsv3_descriptor_received(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest)
+{
+ char *desc_id_field = NULL;
+
+ if (BUG(!onion_address || !desc_id || !hsdir_id_digest)) {
+ return;
+ }
+
+ /* Because DescriptorID is an optional positional value, we need to add a
+ * whitespace before in order to not be next to the HsDir value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id);
+
+ event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
+ REND_NO_AUTH, hsdir_id_digest, NULL);
+ tor_free(desc_id_field);
+}
+
+/** send HS_DESC UPLOADED event
+ *
+ * called when we successfully uploaded a hidden service descriptor.
+ */
+void
+control_event_hs_descriptor_uploaded(const char *id_digest,
+ const char *onion_address)
+{
+ if (BUG(!id_digest)) {
+ return;
+ }
+
+ control_event_hs_descriptor_upload_end("UPLOADED", onion_address,
+ id_digest, NULL);
+}
+
+/** Send HS_DESC event to inform controller that query <b>rend_data</b>
+ * failed to retrieve hidden service descriptor from directory identified by
+ * <b>id_digest</b>. If NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL,
+ * add it to REASON= field.
+ */
+void
+control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
+ const char *hsdir_id_digest,
+ const char *reason)
+{
+ char *desc_id_field = NULL;
+ const char *desc_id;
+
+ if (BUG(!rend_data)) {
+ return;
+ }
+
+ desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
+ if (desc_id != NULL) {
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ /* Set the descriptor ID digest to base32 so we can send it. */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
+ /* Extra whitespace is needed before the value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id_base32);
+ }
+
+ event_hs_descriptor_receive_end("FAILED", rend_data_get_address(rend_data),
+ desc_id_field,
+ TO_REND_DATA_V2(rend_data)->auth_type,
+ hsdir_id_digest, reason);
+ tor_free(desc_id_field);
+}
+
+/** Send HS_DESC event to inform controller that the query to
+ * <b>onion_address</b> failed to retrieve hidden service descriptor
+ * <b>desc_id</b> from directory identified by <b>hsdir_id_digest</b>. If
+ * NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL, add it to REASON=
+ * field. */
+void
+control_event_hsv3_descriptor_failed(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest,
+ const char *reason)
+{
+ char *desc_id_field = NULL;
+
+ if (BUG(!onion_address || !desc_id || !reason)) {
+ return;
+ }
+
+ /* Because DescriptorID is an optional positional value, we need to add a
+ * whitespace before in order to not be next to the HsDir value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id);
+
+ event_hs_descriptor_receive_end("FAILED", onion_address, desc_id_field,
+ REND_NO_AUTH, hsdir_id_digest, reason);
+ tor_free(desc_id_field);
+}
+
+/** Send HS_DESC_CONTENT event after completion of a successful fetch
+ * from hs directory. If <b>hsdir_id_digest</b> is NULL, it is replaced
+ * by "UNKNOWN". If <b>content</b> is NULL, it is replaced by an empty
+ * string. The <b>onion_address</b> or <b>desc_id</b> set to NULL will
+ * not trigger the control event. */
+void
+control_event_hs_descriptor_content(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest,
+ const char *content)
+{
+ static const char *event_name = "HS_DESC_CONTENT";
+ char *esc_content = NULL;
+
+ if (!onion_address || !desc_id) {
+ log_warn(LD_BUG, "Called with onion_address==%p, desc_id==%p, ",
+ onion_address, desc_id);
+ return;
+ }
+
+ if (content == NULL) {
+ /* Point it to empty content so it can still be escaped. */
+ content = "";
+ }
+ write_escaped_data(content, strlen(content), &esc_content);
+
+ send_control_event(EVENT_HS_DESC_CONTENT,
+ "650+%s %s %s %s\r\n%s650 OK\r\n",
+ event_name,
+ rend_hsaddress_str_or_unknown(onion_address),
+ desc_id,
+ hsdir_id_digest ?
+ node_describe_longname_by_id(hsdir_id_digest) :
+ "UNKNOWN",
+ esc_content);
+ tor_free(esc_content);
+}
+
+/** Send HS_DESC event to inform controller upload of hidden service
+ * descriptor identified by <b>id_digest</b> failed. If <b>reason</b>
+ * is not NULL, add it to REASON= field.
+ */
+void
+control_event_hs_descriptor_upload_failed(const char *id_digest,
+ const char *onion_address,
+ const char *reason)
+{
+ if (BUG(!id_digest)) {
+ return;
+ }
+ control_event_hs_descriptor_upload_end("FAILED", onion_address,
+ id_digest, reason);
+}
+
+void
+control_events_free_all(void)
+{
+ smartlist_t *queued_events = NULL;
+
+ stats_prev_n_read = stats_prev_n_written = 0;
+
+ if (queued_control_events_lock) {
+ tor_mutex_acquire(queued_control_events_lock);
+ flush_queued_event_pending = 0;
+ queued_events = queued_control_events;
+ queued_control_events = NULL;
+ tor_mutex_release(queued_control_events_lock);
+ }
+ if (queued_events) {
+ SMARTLIST_FOREACH(queued_events, queued_event_t *, ev,
+ queued_event_free(ev));
+ smartlist_free(queued_events);
+ }
+ if (flush_queued_events_event) {
+ mainloop_event_free(flush_queued_events_event);
+ flush_queued_events_event = NULL;
+ }
+ global_event_mask = 0;
+ disable_log_messages = 0;
+}
+
+#ifdef TOR_UNIT_TESTS
+/* For testing: change the value of global_event_mask */
+void
+control_testing_set_global_event_mask(uint64_t mask)
+{
+ global_event_mask = mask;
+}
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/feature/control/control_events.h b/src/feature/control/control_events.h
new file mode 100644
index 0000000000..34986fdb89
--- /dev/null
+++ b/src/feature/control/control_events.h
@@ -0,0 +1,352 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_events.h
+ * \brief Header file for control_events.c.
+ **/
+
+#ifndef TOR_CONTROL_EVENTS_H
+#define TOR_CONTROL_EVENTS_H
+
+#include "core/or/ocirc_event.h"
+
+/** Used to indicate the type of a CIRC_MINOR event passed to the controller.
+ * The various types are defined in control-spec.txt . */
+typedef enum circuit_status_minor_event_t {
+ CIRC_MINOR_EVENT_PURPOSE_CHANGED,
+ CIRC_MINOR_EVENT_CANNIBALIZED,
+} circuit_status_minor_event_t;
+
+#include "core/or/orconn_event.h"
+
+/** Used to indicate the type of a stream event passed to the controller.
+ * The various types are defined in control-spec.txt */
+typedef enum stream_status_event_t {
+ STREAM_EVENT_SENT_CONNECT = 0,
+ STREAM_EVENT_SENT_RESOLVE = 1,
+ STREAM_EVENT_SUCCEEDED = 2,
+ STREAM_EVENT_FAILED = 3,
+ STREAM_EVENT_CLOSED = 4,
+ STREAM_EVENT_NEW = 5,
+ STREAM_EVENT_NEW_RESOLVE = 6,
+ STREAM_EVENT_FAILED_RETRIABLE = 7,
+ STREAM_EVENT_REMAP = 8
+} stream_status_event_t;
+
+/** Used to indicate the type of a buildtime event */
+typedef enum buildtimeout_set_event_t {
+ BUILDTIMEOUT_SET_EVENT_COMPUTED = 0,
+ BUILDTIMEOUT_SET_EVENT_RESET = 1,
+ BUILDTIMEOUT_SET_EVENT_SUSPENDED = 2,
+ BUILDTIMEOUT_SET_EVENT_DISCARD = 3,
+ BUILDTIMEOUT_SET_EVENT_RESUME = 4
+} buildtimeout_set_event_t;
+
+/** Enum describing various stages of bootstrapping, for use with controller
+ * bootstrap status events. The values range from 0 to 100. */
+typedef enum {
+ BOOTSTRAP_STATUS_UNDEF=-1,
+ BOOTSTRAP_STATUS_STARTING=0,
+
+ /* Initial connection to any relay */
+
+ BOOTSTRAP_STATUS_CONN_PT=1,
+ BOOTSTRAP_STATUS_CONN_DONE_PT=2,
+ BOOTSTRAP_STATUS_CONN_PROXY=3,
+ BOOTSTRAP_STATUS_CONN_DONE_PROXY=4,
+ BOOTSTRAP_STATUS_CONN=5,
+ BOOTSTRAP_STATUS_CONN_DONE=10,
+ BOOTSTRAP_STATUS_HANDSHAKE=14,
+ BOOTSTRAP_STATUS_HANDSHAKE_DONE=15,
+
+ /* Loading directory info */
+
+ BOOTSTRAP_STATUS_ONEHOP_CREATE=20,
+ BOOTSTRAP_STATUS_REQUESTING_STATUS=25,
+ BOOTSTRAP_STATUS_LOADING_STATUS=30,
+ BOOTSTRAP_STATUS_LOADING_KEYS=40,
+ BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS=45,
+ BOOTSTRAP_STATUS_LOADING_DESCRIPTORS=50,
+ BOOTSTRAP_STATUS_ENOUGH_DIRINFO=75,
+
+ /* Connecting to a relay for AP circuits */
+
+ BOOTSTRAP_STATUS_AP_CONN_PT=76,
+ BOOTSTRAP_STATUS_AP_CONN_DONE_PT=77,
+ BOOTSTRAP_STATUS_AP_CONN_PROXY=78,
+ BOOTSTRAP_STATUS_AP_CONN_DONE_PROXY=79,
+ BOOTSTRAP_STATUS_AP_CONN=80,
+ BOOTSTRAP_STATUS_AP_CONN_DONE=85,
+ BOOTSTRAP_STATUS_AP_HANDSHAKE=89,
+ BOOTSTRAP_STATUS_AP_HANDSHAKE_DONE=90,
+
+ /* Creating AP circuits */
+
+ BOOTSTRAP_STATUS_CIRCUIT_CREATE=95,
+ BOOTSTRAP_STATUS_DONE=100
+} bootstrap_status_t;
+
+/** Reason for remapping an AP connection's address: we have a cached
+ * answer. */
+#define REMAP_STREAM_SOURCE_CACHE 1
+/** Reason for remapping an AP connection's address: the exit node told us an
+ * answer. */
+#define REMAP_STREAM_SOURCE_EXIT 2
+
+void control_initialize_event_queue(void);
+
+void control_update_global_event_mask(void);
+void control_adjust_event_log_severity(void);
+
+#define EVENT_NS 0x000F
+int control_event_is_interesting(int event);
+
+void control_per_second_events(void);
+int control_any_per_second_event_enabled(void);
+
+int control_event_circuit_status(origin_circuit_t *circ,
+ circuit_status_event_t e, int reason);
+int control_event_circuit_purpose_changed(origin_circuit_t *circ,
+ int old_purpose);
+int control_event_circuit_cannibalized(origin_circuit_t *circ,
+ int old_purpose,
+ const struct timeval *old_tv_created);
+int control_event_stream_status(entry_connection_t *conn,
+ stream_status_event_t e,
+ int reason);
+int control_event_or_conn_status(or_connection_t *conn,
+ or_conn_status_event_t e, int reason);
+int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written);
+int control_event_stream_bandwidth(edge_connection_t *edge_conn);
+int control_event_stream_bandwidth_used(void);
+int control_event_circ_bandwidth_used(void);
+int control_event_circ_bandwidth_used_for_circ(origin_circuit_t *ocirc);
+int control_event_conn_bandwidth(connection_t *conn);
+int control_event_conn_bandwidth_used(void);
+int control_event_circuit_cell_stats(void);
+void control_event_logmsg(int severity, log_domain_mask_t domain,
+ const char *msg);
+void control_event_logmsg_pending(void);
+int control_event_descriptors_changed(smartlist_t *routers);
+int control_event_address_mapped(const char *from, const char *to,
+ time_t expires, const char *error,
+ const int cached);
+int control_event_my_descriptor_changed(void);
+int control_event_network_liveness_update(int liveness);
+int control_event_networkstatus_changed(smartlist_t *statuses);
+
+int control_event_newconsensus(const networkstatus_t *consensus);
+int control_event_networkstatus_changed_single(const routerstatus_t *rs);
+int control_event_general_status(int severity, const char *format, ...)
+ CHECK_PRINTF(2,3);
+int control_event_client_status(int severity, const char *format, ...)
+ CHECK_PRINTF(2,3);
+int control_event_server_status(int severity, const char *format, ...)
+ CHECK_PRINTF(2,3);
+
+int control_event_general_error(const char *format, ...)
+ CHECK_PRINTF(1,2);
+int control_event_client_error(const char *format, ...)
+ CHECK_PRINTF(1,2);
+int control_event_server_error(const char *format, ...)
+ CHECK_PRINTF(1,2);
+
+int control_event_guard(const char *nickname, const char *digest,
+ const char *status);
+int control_event_conf_changed(const smartlist_t *elements);
+int control_event_buildtimeout_set(buildtimeout_set_event_t type,
+ const char *args);
+int control_event_signal(uintptr_t signal);
+
+void control_event_bootstrap(bootstrap_status_t status, int progress);
+MOCK_DECL(void, control_event_bootstrap_prob_or,(const char *warn,
+ int reason,
+ or_connection_t *or_conn));
+void control_event_boot_dir(bootstrap_status_t status, int progress);
+void control_event_boot_first_orconn(void);
+void control_event_bootstrap_problem(const char *warn, const char *reason,
+ const connection_t *conn, int dowarn);
+char *control_event_boot_last_msg(void);
+void control_event_bootstrap_reset(void);
+
+void control_event_clients_seen(const char *controller_str);
+void control_event_transport_launched(const char *mode,
+ const char *transport_name,
+ tor_addr_t *addr, uint16_t port);
+void control_event_pt_log(const char *log);
+void control_event_pt_status(const char *status);
+
+void control_event_hs_descriptor_requested(const char *onion_address,
+ rend_auth_type_t auth_type,
+ const char *id_digest,
+ const char *desc_id,
+ const char *hsdir_index);
+void control_event_hs_descriptor_created(const char *onion_address,
+ const char *desc_id,
+ int replica);
+void control_event_hs_descriptor_upload(const char *onion_address,
+ const char *desc_id,
+ const char *hs_dir,
+ const char *hsdir_index);
+void control_event_hs_descriptor_upload_end(const char *action,
+ const char *onion_address,
+ const char *hs_dir,
+ const char *reason);
+void control_event_hs_descriptor_uploaded(const char *hs_dir,
+ const char *onion_address);
+/* Hidden service v2 HS_DESC specific. */
+void control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
+ const char *id_digest,
+ const char *reason);
+void control_event_hsv2_descriptor_received(const char *onion_address,
+ const rend_data_t *rend_data,
+ const char *id_digest);
+/* Hidden service v3 HS_DESC specific. */
+void control_event_hsv3_descriptor_failed(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest,
+ const char *reason);
+void control_event_hsv3_descriptor_received(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest);
+void control_event_hs_descriptor_upload_failed(const char *hs_dir,
+ const char *onion_address,
+ const char *reason);
+void control_event_hs_descriptor_content(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_fp,
+ const char *content);
+
+void control_events_free_all(void);
+
+#ifdef CONTROL_MODULE_PRIVATE
+char *get_bw_samples(void);
+#endif /* defined(CONTROL_MODULE_PRIVATE) */
+
+#ifdef CONTROL_EVENTS_PRIVATE
+/** Bitfield: The bit 1&lt;&lt;e is set if <b>any</b> open control
+ * connection is interested in events of type <b>e</b>. We use this
+ * so that we can decide to skip generating event messages that nobody
+ * has interest in without having to walk over the global connection
+ * list to find out.
+ **/
+typedef uint64_t event_mask_t;
+
+/* Recognized asynchronous event types. It's okay to expand this list
+ * because it is used both as a list of v0 event types, and as indices
+ * into the bitfield to determine which controllers want which events.
+ */
+/* This bitfield has no event zero 0x0000 */
+#define EVENT_MIN_ 0x0001
+#define EVENT_CIRCUIT_STATUS 0x0001
+#define EVENT_STREAM_STATUS 0x0002
+#define EVENT_OR_CONN_STATUS 0x0003
+#define EVENT_BANDWIDTH_USED 0x0004
+#define EVENT_CIRCUIT_STATUS_MINOR 0x0005
+#define EVENT_NEW_DESC 0x0006
+#define EVENT_DEBUG_MSG 0x0007
+#define EVENT_INFO_MSG 0x0008
+#define EVENT_NOTICE_MSG 0x0009
+#define EVENT_WARN_MSG 0x000A
+#define EVENT_ERR_MSG 0x000B
+#define EVENT_ADDRMAP 0x000C
+/* There was an AUTHDIR_NEWDESCS event, but it no longer exists. We
+ can reclaim 0x000D. */
+#define EVENT_DESCCHANGED 0x000E
+/* Exposed above */
+// #define EVENT_NS 0x000F
+#define EVENT_STATUS_CLIENT 0x0010
+#define EVENT_STATUS_SERVER 0x0011
+#define EVENT_STATUS_GENERAL 0x0012
+#define EVENT_GUARD 0x0013
+#define EVENT_STREAM_BANDWIDTH_USED 0x0014
+#define EVENT_CLIENTS_SEEN 0x0015
+#define EVENT_NEWCONSENSUS 0x0016
+#define EVENT_BUILDTIMEOUT_SET 0x0017
+#define EVENT_GOT_SIGNAL 0x0018
+#define EVENT_CONF_CHANGED 0x0019
+#define EVENT_CONN_BW 0x001A
+#define EVENT_CELL_STATS 0x001B
+/* UNUSED : 0x001C */
+#define EVENT_CIRC_BANDWIDTH_USED 0x001D
+#define EVENT_TRANSPORT_LAUNCHED 0x0020
+#define EVENT_HS_DESC 0x0021
+#define EVENT_HS_DESC_CONTENT 0x0022
+#define EVENT_NETWORK_LIVENESS 0x0023
+#define EVENT_PT_LOG 0x0024
+#define EVENT_PT_STATUS 0x0025
+#define EVENT_MAX_ 0x0025
+
+/* sizeof(control_connection_t.event_mask) in bits, currently a uint64_t */
+#define EVENT_CAPACITY_ 0x0040
+
+/* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a
+ * different structure, as it can only handle a maximum left shift of 1<<63. */
+
+#if EVENT_MAX_ >= EVENT_CAPACITY_
+#error control_connection_t.event_mask has an event greater than its capacity
+#endif
+
+#define EVENT_MASK_(e) (((uint64_t)1)<<(e))
+
+#define EVENT_MASK_NONE_ ((uint64_t)0x0)
+
+#define EVENT_MASK_ABOVE_MIN_ ((~((uint64_t)0x0)) << EVENT_MIN_)
+#define EVENT_MASK_BELOW_MAX_ ((~((uint64_t)0x0)) \
+ >> (EVENT_CAPACITY_ - EVENT_MAX_ \
+ - EVENT_MIN_))
+
+#define EVENT_MASK_ALL_ (EVENT_MASK_ABOVE_MIN_ \
+ & EVENT_MASK_BELOW_MAX_)
+
+/** Helper structure: temporarily stores cell statistics for a circuit. */
+typedef struct cell_stats_t {
+ /** Number of cells added in app-ward direction by command. */
+ uint64_t added_cells_appward[CELL_COMMAND_MAX_ + 1];
+ /** Number of cells added in exit-ward direction by command. */
+ uint64_t added_cells_exitward[CELL_COMMAND_MAX_ + 1];
+ /** Number of cells removed in app-ward direction by command. */
+ uint64_t removed_cells_appward[CELL_COMMAND_MAX_ + 1];
+ /** Number of cells removed in exit-ward direction by command. */
+ uint64_t removed_cells_exitward[CELL_COMMAND_MAX_ + 1];
+ /** Total waiting time of cells in app-ward direction by command. */
+ uint64_t total_time_appward[CELL_COMMAND_MAX_ + 1];
+ /** Total waiting time of cells in exit-ward direction by command. */
+ uint64_t total_time_exitward[CELL_COMMAND_MAX_ + 1];
+} cell_stats_t;
+
+void sum_up_cell_stats_by_command(circuit_t *circ,
+ cell_stats_t *cell_stats);
+void append_cell_stats_by_command(smartlist_t *event_parts,
+ const char *key,
+ const uint64_t *include_if_non_zero,
+ const uint64_t *number_to_include);
+void format_cell_stats(char **event_string, circuit_t *circ,
+ cell_stats_t *cell_stats);
+
+/** Helper structure: maps event values to their names. */
+struct control_event_t {
+ uint16_t event_code;
+ const char *event_name;
+};
+
+extern const struct control_event_t control_event_table[];
+
+#ifdef TOR_UNIT_TESTS
+MOCK_DECL(STATIC void,
+ send_control_event_string,(uint16_t event, const char *msg));
+
+MOCK_DECL(STATIC void,
+ queue_control_event_string,(uint16_t event, char *msg));
+
+void control_testing_set_global_event_mask(uint64_t mask);
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
+#endif /* defined(CONTROL_EVENTS_PRIVATE) */
+
+#endif /* !defined(TOR_CONTROL_EVENTS_H) */
diff --git a/src/feature/control/control_fmt.c b/src/feature/control/control_fmt.c
new file mode 100644
index 0000000000..e0e77eb2d0
--- /dev/null
+++ b/src/feature/control/control_fmt.c
@@ -0,0 +1,181 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_fmt.c
+ * \brief Formatting functions for controller data.
+ */
+
+#include "core/or/or.h"
+
+#include "core/mainloop/connection.h"
+#include "core/or/circuitbuild.h"
+#include "core/or/circuitlist.h"
+#include "core/or/connection_edge.h"
+#include "feature/control/control_fmt.h"
+#include "feature/control/control_proto.h"
+#include "feature/nodelist/nodelist.h"
+
+#include "core/or/cpath_build_state_st.h"
+#include "core/or/entry_connection_st.h"
+#include "core/or/or_connection_st.h"
+#include "core/or/origin_circuit_st.h"
+#include "core/or/socks_request_st.h"
+#include "feature/control/control_connection_st.h"
+
+/** Given an AP connection <b>conn</b> and a <b>len</b>-character buffer
+ * <b>buf</b>, determine the address:port combination requested on
+ * <b>conn</b>, and write it to <b>buf</b>. Return 0 on success, -1 on
+ * failure. */
+int
+write_stream_target_to_buf(entry_connection_t *conn, char *buf, size_t len)
+{
+ char buf2[256];
+ if (conn->chosen_exit_name)
+ if (tor_snprintf(buf2, sizeof(buf2), ".%s.exit", conn->chosen_exit_name)<0)
+ return -1;
+ if (!conn->socks_request)
+ return -1;
+ if (tor_snprintf(buf, len, "%s%s%s:%d",
+ conn->socks_request->address,
+ conn->chosen_exit_name ? buf2 : "",
+ !conn->chosen_exit_name && connection_edge_is_rendezvous_stream(
+ ENTRY_TO_EDGE_CONN(conn)) ? ".onion" : "",
+ conn->socks_request->port)<0)
+ return -1;
+ return 0;
+}
+
+/** Figure out the best name for the target router of an OR connection
+ * <b>conn</b>, and write it into the <b>len</b>-character buffer
+ * <b>name</b>. */
+void
+orconn_target_get_name(char *name, size_t len, or_connection_t *conn)
+{
+ const node_t *node = node_get_by_id(conn->identity_digest);
+ if (node) {
+ tor_assert(len > MAX_VERBOSE_NICKNAME_LEN);
+ node_get_verbose_nickname(node, name);
+ } else if (! tor_digest_is_zero(conn->identity_digest)) {
+ name[0] = '$';
+ base16_encode(name+1, len-1, conn->identity_digest,
+ DIGEST_LEN);
+ } else {
+ tor_snprintf(name, len, "%s:%d",
+ conn->base_.address, conn->base_.port);
+ }
+}
+
+/** Allocate and return a description of <b>circ</b>'s current status,
+ * including its path (if any). */
+char *
+circuit_describe_status_for_controller(origin_circuit_t *circ)
+{
+ char *rv;
+ smartlist_t *descparts = smartlist_new();
+
+ {
+ char *vpath = circuit_list_path_for_controller(circ);
+ if (*vpath) {
+ smartlist_add(descparts, vpath);
+ } else {
+ tor_free(vpath); /* empty path; don't put an extra space in the result */
+ }
+ }
+
+ {
+ cpath_build_state_t *build_state = circ->build_state;
+ smartlist_t *flaglist = smartlist_new();
+ char *flaglist_joined;
+
+ if (build_state->onehop_tunnel)
+ smartlist_add(flaglist, (void *)"ONEHOP_TUNNEL");
+ if (build_state->is_internal)
+ smartlist_add(flaglist, (void *)"IS_INTERNAL");
+ if (build_state->need_capacity)
+ smartlist_add(flaglist, (void *)"NEED_CAPACITY");
+ if (build_state->need_uptime)
+ smartlist_add(flaglist, (void *)"NEED_UPTIME");
+
+ /* Only emit a BUILD_FLAGS argument if it will have a non-empty value. */
+ if (smartlist_len(flaglist)) {
+ flaglist_joined = smartlist_join_strings(flaglist, ",", 0, NULL);
+
+ smartlist_add_asprintf(descparts, "BUILD_FLAGS=%s", flaglist_joined);
+
+ tor_free(flaglist_joined);
+ }
+
+ smartlist_free(flaglist);
+ }
+
+ smartlist_add_asprintf(descparts, "PURPOSE=%s",
+ circuit_purpose_to_controller_string(circ->base_.purpose));
+
+ {
+ const char *hs_state =
+ circuit_purpose_to_controller_hs_state_string(circ->base_.purpose);
+
+ if (hs_state != NULL) {
+ smartlist_add_asprintf(descparts, "HS_STATE=%s", hs_state);
+ }
+ }
+
+ if (circ->rend_data != NULL || circ->hs_ident != NULL) {
+ char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ const char *onion_address;
+ if (circ->rend_data) {
+ onion_address = rend_data_get_address(circ->rend_data);
+ } else {
+ hs_build_address(&circ->hs_ident->identity_pk, HS_VERSION_THREE, addr);
+ onion_address = addr;
+ }
+ smartlist_add_asprintf(descparts, "REND_QUERY=%s", onion_address);
+ }
+
+ {
+ char tbuf[ISO_TIME_USEC_LEN+1];
+ format_iso_time_nospace_usec(tbuf, &circ->base_.timestamp_created);
+
+ smartlist_add_asprintf(descparts, "TIME_CREATED=%s", tbuf);
+ }
+
+ // Show username and/or password if available.
+ if (circ->socks_username_len > 0) {
+ char* socks_username_escaped = esc_for_log_len(circ->socks_username,
+ (size_t) circ->socks_username_len);
+ smartlist_add_asprintf(descparts, "SOCKS_USERNAME=%s",
+ socks_username_escaped);
+ tor_free(socks_username_escaped);
+ }
+ if (circ->socks_password_len > 0) {
+ char* socks_password_escaped = esc_for_log_len(circ->socks_password,
+ (size_t) circ->socks_password_len);
+ smartlist_add_asprintf(descparts, "SOCKS_PASSWORD=%s",
+ socks_password_escaped);
+ tor_free(socks_password_escaped);
+ }
+
+ rv = smartlist_join_strings(descparts, " ", 0, NULL);
+
+ SMARTLIST_FOREACH(descparts, char *, cp, tor_free(cp));
+ smartlist_free(descparts);
+
+ return rv;
+}
+
+/** Return a longname the node whose identity is <b>id_digest</b>. If
+ * node_get_by_id() returns NULL, base 16 encoding of <b>id_digest</b> is
+ * returned instead.
+ *
+ * This function is not thread-safe. Each call to this function invalidates
+ * previous values returned by this function.
+ */
+MOCK_IMPL(const char *,
+node_describe_longname_by_id,(const char *id_digest))
+{
+ static char longname[MAX_VERBOSE_NICKNAME_LEN+1];
+ node_get_verbose_nickname_by_id(id_digest, longname);
+ return longname;
+}
diff --git a/src/feature/control/control_fmt.h b/src/feature/control/control_fmt.h
new file mode 100644
index 0000000000..6446e37079
--- /dev/null
+++ b/src/feature/control/control_fmt.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_fmt.h
+ * \brief Header file for control_fmt.c.
+ **/
+
+#ifndef TOR_CONTROL_FMT_H
+#define TOR_CONTROL_FMT_H
+
+int write_stream_target_to_buf(entry_connection_t *conn, char *buf,
+ size_t len);
+void orconn_target_get_name(char *buf, size_t len,
+ or_connection_t *conn);
+char *circuit_describe_status_for_controller(origin_circuit_t *circ);
+
+MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
+
+#endif /* !defined(TOR_CONTROL_FMT_H) */
diff --git a/src/feature/control/control_getinfo.c b/src/feature/control/control_getinfo.c
new file mode 100644
index 0000000000..3e31bb9e8f
--- /dev/null
+++ b/src/feature/control/control_getinfo.c
@@ -0,0 +1,1654 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_getinfo.c
+ * \brief Implementation for miscellaneous controller getinfo commands.
+ */
+
+#define CONTROL_EVENTS_PRIVATE
+#define CONTROL_MODULE_PRIVATE
+#define CONTROL_GETINFO_PRIVATE
+
+#include "core/or/or.h"
+#include "app/config/config.h"
+#include "core/mainloop/connection.h"
+#include "core/mainloop/mainloop.h"
+#include "core/or/circuitlist.h"
+#include "core/or/connection_edge.h"
+#include "core/or/connection_or.h"
+#include "core/or/policies.h"
+#include "core/or/versions.h"
+#include "feature/client/addressmap.h"
+#include "feature/client/bridges.h"
+#include "feature/client/entrynodes.h"
+#include "feature/control/control.h"
+#include "feature/control/control_cmd.h"
+#include "feature/control/control_events.h"
+#include "feature/control/control_fmt.h"
+#include "feature/control/control_getinfo.h"
+#include "feature/control/control_proto.h"
+#include "feature/control/fmt_serverstatus.h"
+#include "feature/control/getinfo_geoip.h"
+#include "feature/dircache/dirserv.h"
+#include "feature/dirclient/dirclient.h"
+#include "feature/dirclient/dlstatus.h"
+#include "feature/hibernate/hibernate.h"
+#include "feature/hs/hs_cache.h"
+#include "feature/hs_common/shared_random_client.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/microdesc.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerinfo.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/relay/router.h"
+#include "feature/relay/routermode.h"
+#include "feature/relay/selftest.h"
+#include "feature/rend/rendcache.h"
+#include "feature/stats/geoip_stats.h"
+#include "feature/stats/predict_ports.h"
+#include "lib/version/torversion.h"
+
+#include "core/or/entry_connection_st.h"
+#include "core/or/or_connection_st.h"
+#include "core/or/origin_circuit_st.h"
+#include "core/or/socks_request_st.h"
+#include "feature/control/control_connection_st.h"
+#include "feature/control/control_cmd_args_st.h"
+#include "feature/dircache/cached_dir_st.h"
+#include "feature/nodelist/extrainfo_st.h"
+#include "feature/nodelist/microdesc_st.h"
+#include "feature/nodelist/networkstatus_st.h"
+#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/routerlist_st.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef _WIN32
+#include <pwd.h>
+#endif
+
+static char *list_getinfo_options(void);
+static char *download_status_to_string(const download_status_t *dl);
+
+/** Implementation helper for GETINFO: knows the answers for various
+ * trivial-to-implement questions. */
+static int
+getinfo_helper_misc(control_connection_t *conn, const char *question,
+ char **answer, const char **errmsg)
+{
+ (void) conn;
+ if (!strcmp(question, "version")) {
+ *answer = tor_strdup(get_version());
+ } else if (!strcmp(question, "bw-event-cache")) {
+ *answer = get_bw_samples();
+ } else if (!strcmp(question, "config-file")) {
+ const char *a = get_torrc_fname(0);
+ if (a)
+ *answer = tor_strdup(a);
+ } else if (!strcmp(question, "config-defaults-file")) {
+ const char *a = get_torrc_fname(1);
+ if (a)
+ *answer = tor_strdup(a);
+ } else if (!strcmp(question, "config-text")) {
+ *answer = options_dump(get_options(), OPTIONS_DUMP_MINIMAL);
+ } else if (!strcmp(question, "config-can-saveconf")) {
+ *answer = tor_strdup(get_options()->IncludeUsed ? "0" : "1");
+ } else if (!strcmp(question, "info/names")) {
+ *answer = list_getinfo_options();
+ } else if (!strcmp(question, "dormant")) {
+ int dormant = rep_hist_circbuilding_dormant(time(NULL));
+ *answer = tor_strdup(dormant ? "1" : "0");
+ } else if (!strcmp(question, "events/names")) {
+ int i;
+ smartlist_t *event_names = smartlist_new();
+
+ for (i = 0; control_event_table[i].event_name != NULL; ++i) {
+ smartlist_add(event_names, (char *)control_event_table[i].event_name);
+ }
+
+ *answer = smartlist_join_strings(event_names, " ", 0, NULL);
+
+ smartlist_free(event_names);
+ } else if (!strcmp(question, "signal/names")) {
+ smartlist_t *signal_names = smartlist_new();
+ int j;
+ for (j = 0; signal_table[j].signal_name != NULL; ++j) {
+ smartlist_add(signal_names, (char*)signal_table[j].signal_name);
+ }
+
+ *answer = smartlist_join_strings(signal_names, " ", 0, NULL);
+
+ smartlist_free(signal_names);
+ } else if (!strcmp(question, "features/names")) {
+ *answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS");
+ } else if (!strcmp(question, "address")) {
+ uint32_t addr;
+ if (router_pick_published_address(get_options(), &addr, 0) < 0) {
+ *errmsg = "Address unknown";
+ return -1;
+ }
+ *answer = tor_dup_ip(addr);
+ } else if (!strcmp(question, "traffic/read")) {
+ tor_asprintf(answer, "%"PRIu64, (get_bytes_read()));
+ } else if (!strcmp(question, "traffic/written")) {
+ tor_asprintf(answer, "%"PRIu64, (get_bytes_written()));
+ } else if (!strcmp(question, "uptime")) {
+ long uptime_secs = get_uptime();
+ tor_asprintf(answer, "%ld", uptime_secs);
+ } else if (!strcmp(question, "process/pid")) {
+ int myPid = -1;
+
+#ifdef _WIN32
+ myPid = _getpid();
+#else
+ myPid = getpid();
+#endif
+
+ tor_asprintf(answer, "%d", myPid);
+ } else if (!strcmp(question, "process/uid")) {
+#ifdef _WIN32
+ *answer = tor_strdup("-1");
+#else
+ int myUid = geteuid();
+ tor_asprintf(answer, "%d", myUid);
+#endif /* defined(_WIN32) */
+ } else if (!strcmp(question, "process/user")) {
+#ifdef _WIN32
+ *answer = tor_strdup("");
+#else
+ int myUid = geteuid();
+ const struct passwd *myPwEntry = tor_getpwuid(myUid);
+
+ if (myPwEntry) {
+ *answer = tor_strdup(myPwEntry->pw_name);
+ } else {
+ *answer = tor_strdup("");
+ }
+#endif /* defined(_WIN32) */
+ } else if (!strcmp(question, "process/descriptor-limit")) {
+ int max_fds = get_max_sockets();
+ tor_asprintf(answer, "%d", max_fds);
+ } else if (!strcmp(question, "limits/max-mem-in-queues")) {
+ tor_asprintf(answer, "%"PRIu64,
+ (get_options()->MaxMemInQueues));
+ } else if (!strcmp(question, "fingerprint")) {
+ crypto_pk_t *server_key;
+ if (!server_mode(get_options())) {
+ *errmsg = "Not running in server mode";
+ return -1;
+ }
+ server_key = get_server_identity_key();
+ *answer = tor_malloc(HEX_DIGEST_LEN+1);
+ crypto_pk_get_fingerprint(server_key, *answer, 0);
+ }
+ return 0;
+}
+
+/** Awful hack: return a newly allocated string based on a routerinfo and
+ * (possibly) an extrainfo, sticking the read-history and write-history from
+ * <b>ei</b> into the resulting string. The thing you get back won't
+ * necessarily have a valid signature.
+ *
+ * New code should never use this; it's for backward compatibility.
+ *
+ * NOTE: <b>ri_body</b> is as returned by signed_descriptor_get_body: it might
+ * not be NUL-terminated. */
+static char *
+munge_extrainfo_into_routerinfo(const char *ri_body,
+ const signed_descriptor_t *ri,
+ const signed_descriptor_t *ei)
+{
+ char *out = NULL, *outp;
+ int i;
+ const char *router_sig;
+ const char *ei_body = signed_descriptor_get_body(ei);
+ size_t ri_len = ri->signed_descriptor_len;
+ size_t ei_len = ei->signed_descriptor_len;
+ if (!ei_body)
+ goto bail;
+
+ outp = out = tor_malloc(ri_len+ei_len+1);
+ if (!(router_sig = tor_memstr(ri_body, ri_len, "\nrouter-signature")))
+ goto bail;
+ ++router_sig;
+ memcpy(out, ri_body, router_sig-ri_body);
+ outp += router_sig-ri_body;
+
+ for (i=0; i < 2; ++i) {
+ const char *kwd = i ? "\nwrite-history " : "\nread-history ";
+ const char *cp, *eol;
+ if (!(cp = tor_memstr(ei_body, ei_len, kwd)))
+ continue;
+ ++cp;
+ if (!(eol = memchr(cp, '\n', ei_len - (cp-ei_body))))
+ continue;
+ memcpy(outp, cp, eol-cp+1);
+ outp += eol-cp+1;
+ }
+ memcpy(outp, router_sig, ri_len - (router_sig-ri_body));
+ *outp++ = '\0';
+ tor_assert(outp-out < (int)(ri_len+ei_len+1));
+
+ return out;
+ bail:
+ tor_free(out);
+ return tor_strndup(ri_body, ri->signed_descriptor_len);
+}
+
+/** Implementation helper for GETINFO: answers requests for information about
+ * which ports are bound. */
+static int
+getinfo_helper_listeners(control_connection_t *control_conn,
+ const char *question,
+ char **answer, const char **errmsg)
+{
+ int type;
+ smartlist_t *res;
+
+ (void)control_conn;
+ (void)errmsg;
+
+ if (!strcmp(question, "net/listeners/or"))
+ type = CONN_TYPE_OR_LISTENER;
+ else if (!strcmp(question, "net/listeners/extor"))
+ type = CONN_TYPE_EXT_OR_LISTENER;
+ else if (!strcmp(question, "net/listeners/dir"))
+ type = CONN_TYPE_DIR_LISTENER;
+ else if (!strcmp(question, "net/listeners/socks"))
+ type = CONN_TYPE_AP_LISTENER;
+ else if (!strcmp(question, "net/listeners/trans"))
+ type = CONN_TYPE_AP_TRANS_LISTENER;
+ else if (!strcmp(question, "net/listeners/natd"))
+ type = CONN_TYPE_AP_NATD_LISTENER;
+ else if (!strcmp(question, "net/listeners/httptunnel"))
+ type = CONN_TYPE_AP_HTTP_CONNECT_LISTENER;
+ else if (!strcmp(question, "net/listeners/dns"))
+ type = CONN_TYPE_AP_DNS_LISTENER;
+ else if (!strcmp(question, "net/listeners/control"))
+ type = CONN_TYPE_CONTROL_LISTENER;
+ else
+ return 0; /* unknown key */
+
+ res = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ struct sockaddr_storage ss;
+ socklen_t ss_len = sizeof(ss);
+
+ if (conn->type != type || conn->marked_for_close || !SOCKET_OK(conn->s))
+ continue;
+
+ if (getsockname(conn->s, (struct sockaddr *)&ss, &ss_len) < 0) {
+ smartlist_add_asprintf(res, "%s:%d", conn->address, (int)conn->port);
+ } else {
+ char *tmp = tor_sockaddr_to_str((struct sockaddr *)&ss);
+ smartlist_add(res, esc_for_log(tmp));
+ tor_free(tmp);
+ }
+
+ } SMARTLIST_FOREACH_END(conn);
+
+ *answer = smartlist_join_strings(res, " ", 0, NULL);
+
+ SMARTLIST_FOREACH(res, char *, cp, tor_free(cp));
+ smartlist_free(res);
+ return 0;
+}
+
+/** Implementation helper for GETINFO: answers requests for information about
+ * the current time in both local and UTC forms. */
+STATIC int
+getinfo_helper_current_time(control_connection_t *control_conn,
+ const char *question,
+ char **answer, const char **errmsg)
+{
+ (void)control_conn;
+ (void)errmsg;
+
+ struct timeval now;
+ tor_gettimeofday(&now);
+ char timebuf[ISO_TIME_LEN+1];
+
+ if (!strcmp(question, "current-time/local"))
+ format_local_iso_time_nospace(timebuf, (time_t)now.tv_sec);
+ else if (!strcmp(question, "current-time/utc"))
+ format_iso_time_nospace(timebuf, (time_t)now.tv_sec);
+ else
+ return 0;
+
+ *answer = tor_strdup(timebuf);
+ return 0;
+}
+
+/** Implementation helper for GETINFO: knows the answers for questions about
+ * directory information. */
+STATIC int
+getinfo_helper_dir(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg)
+{
+ (void) control_conn;
+ if (!strcmpstart(question, "desc/id/")) {
+ const routerinfo_t *ri = NULL;
+ const node_t *node = node_get_by_hex_id(question+strlen("desc/id/"), 0);
+ if (node)
+ ri = node->ri;
+ if (ri) {
+ const char *body = signed_descriptor_get_body(&ri->cache_info);
+ if (body)
+ *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
+ } else if (! we_fetch_router_descriptors(get_options())) {
+ /* Descriptors won't be available, provide proper error */
+ *errmsg = "We fetch microdescriptors, not router "
+ "descriptors. You'll need to use md/id/* "
+ "instead of desc/id/*.";
+ return 0;
+ }
+ } else if (!strcmpstart(question, "desc/name/")) {
+ const routerinfo_t *ri = NULL;
+ /* XXX Setting 'warn_if_unnamed' here is a bit silly -- the
+ * warning goes to the user, not to the controller. */
+ const node_t *node =
+ node_get_by_nickname(question+strlen("desc/name/"), 0);
+ if (node)
+ ri = node->ri;
+ if (ri) {
+ const char *body = signed_descriptor_get_body(&ri->cache_info);
+ if (body)
+ *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
+ } else if (! we_fetch_router_descriptors(get_options())) {
+ /* Descriptors won't be available, provide proper error */
+ *errmsg = "We fetch microdescriptors, not router "
+ "descriptors. You'll need to use md/name/* "
+ "instead of desc/name/*.";
+ return 0;
+ }
+ } else if (!strcmp(question, "desc/download-enabled")) {
+ int r = we_fetch_router_descriptors(get_options());
+ tor_asprintf(answer, "%d", !!r);
+ } else if (!strcmp(question, "desc/all-recent")) {
+ routerlist_t *routerlist = router_get_routerlist();
+ smartlist_t *sl = smartlist_new();
+ if (routerlist && routerlist->routers) {
+ SMARTLIST_FOREACH(routerlist->routers, const routerinfo_t *, ri,
+ {
+ const char *body = signed_descriptor_get_body(&ri->cache_info);
+ if (body)
+ smartlist_add(sl,
+ tor_strndup(body, ri->cache_info.signed_descriptor_len));
+ });
+ }
+ *answer = smartlist_join_strings(sl, "", 0, NULL);
+ SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
+ smartlist_free(sl);
+ } else if (!strcmp(question, "desc/all-recent-extrainfo-hack")) {
+ /* XXXX Remove this once Torstat asks for extrainfos. */
+ routerlist_t *routerlist = router_get_routerlist();
+ smartlist_t *sl = smartlist_new();
+ if (routerlist && routerlist->routers) {
+ SMARTLIST_FOREACH_BEGIN(routerlist->routers, const routerinfo_t *, ri) {
+ const char *body = signed_descriptor_get_body(&ri->cache_info);
+ signed_descriptor_t *ei = extrainfo_get_by_descriptor_digest(
+ ri->cache_info.extra_info_digest);
+ if (ei && body) {
+ smartlist_add(sl, munge_extrainfo_into_routerinfo(body,
+ &ri->cache_info, ei));
+ } else if (body) {
+ smartlist_add(sl,
+ tor_strndup(body, ri->cache_info.signed_descriptor_len));
+ }
+ } SMARTLIST_FOREACH_END(ri);
+ }
+ *answer = smartlist_join_strings(sl, "", 0, NULL);
+ SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
+ smartlist_free(sl);
+ } else if (!strcmpstart(question, "hs/client/desc/id/")) {
+ hostname_type_t addr_type;
+
+ question += strlen("hs/client/desc/id/");
+ if (rend_valid_v2_service_id(question)) {
+ addr_type = ONION_V2_HOSTNAME;
+ } else if (hs_address_is_valid(question)) {
+ addr_type = ONION_V3_HOSTNAME;
+ } else {
+ *errmsg = "Invalid address";
+ return -1;
+ }
+
+ if (addr_type == ONION_V2_HOSTNAME) {
+ rend_cache_entry_t *e = NULL;
+ if (!rend_cache_lookup_entry(question, -1, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
+ } else {
+ ed25519_public_key_t service_pk;
+ const char *desc;
+
+ /* The check before this if/else makes sure of this. */
+ tor_assert(addr_type == ONION_V3_HOSTNAME);
+
+ if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) {
+ *errmsg = "Invalid v3 address";
+ return -1;
+ }
+
+ desc = hs_cache_lookup_encoded_as_client(&service_pk);
+ if (desc) {
+ *answer = tor_strdup(desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
+ }
+ } else if (!strcmpstart(question, "hs/service/desc/id/")) {
+ hostname_type_t addr_type;
+
+ question += strlen("hs/service/desc/id/");
+ if (rend_valid_v2_service_id(question)) {
+ addr_type = ONION_V2_HOSTNAME;
+ } else if (hs_address_is_valid(question)) {
+ addr_type = ONION_V3_HOSTNAME;
+ } else {
+ *errmsg = "Invalid address";
+ return -1;
+ }
+ rend_cache_entry_t *e = NULL;
+
+ if (addr_type == ONION_V2_HOSTNAME) {
+ if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
+ } else {
+ ed25519_public_key_t service_pk;
+ char *desc;
+
+ /* The check before this if/else makes sure of this. */
+ tor_assert(addr_type == ONION_V3_HOSTNAME);
+
+ if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) {
+ *errmsg = "Invalid v3 address";
+ return -1;
+ }
+
+ desc = hs_service_lookup_current_desc(&service_pk);
+ if (desc) {
+ /* Newly allocated string, we have ownership. */
+ *answer = desc;
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
+ }
+ } else if (!strcmp(question, "md/all")) {
+ const smartlist_t *nodes = nodelist_get_list();
+ tor_assert(nodes);
+
+ if (smartlist_len(nodes) == 0) {
+ *answer = tor_strdup("");
+ return 0;
+ }
+
+ smartlist_t *microdescs = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(nodes, node_t *, n) {
+ if (n->md && n->md->body) {
+ char *copy = tor_strndup(n->md->body, n->md->bodylen);
+ smartlist_add(microdescs, copy);
+ }
+ } SMARTLIST_FOREACH_END(n);
+
+ *answer = smartlist_join_strings(microdescs, "", 0, NULL);
+ SMARTLIST_FOREACH(microdescs, char *, md, tor_free(md));
+ smartlist_free(microdescs);
+ } else if (!strcmpstart(question, "md/id/")) {
+ const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0);
+ const microdesc_t *md = NULL;
+ if (node) md = node->md;
+ if (md && md->body) {
+ *answer = tor_strndup(md->body, md->bodylen);
+ }
+ } else if (!strcmpstart(question, "md/name/")) {
+ /* XXX Setting 'warn_if_unnamed' here is a bit silly -- the
+ * warning goes to the user, not to the controller. */
+ const node_t *node = node_get_by_nickname(question+strlen("md/name/"), 0);
+ /* XXXX duplicated code */
+ const microdesc_t *md = NULL;
+ if (node) md = node->md;
+ if (md && md->body) {
+ *answer = tor_strndup(md->body, md->bodylen);
+ }
+ } else if (!strcmp(question, "md/download-enabled")) {
+ int r = we_fetch_microdescriptors(get_options());
+ tor_asprintf(answer, "%d", !!r);
+ } else if (!strcmpstart(question, "desc-annotations/id/")) {
+ const routerinfo_t *ri = NULL;
+ const node_t *node =
+ node_get_by_hex_id(question+strlen("desc-annotations/id/"), 0);
+ if (node)
+ ri = node->ri;
+ if (ri) {
+ const char *annotations =
+ signed_descriptor_get_annotations(&ri->cache_info);
+ if (annotations)
+ *answer = tor_strndup(annotations,
+ ri->cache_info.annotations_len);
+ }
+ } else if (!strcmpstart(question, "dir/server/")) {
+ size_t answer_len = 0;
+ char *url = NULL;
+ smartlist_t *descs = smartlist_new();
+ const char *msg;
+ int res;
+ char *cp;
+ tor_asprintf(&url, "/tor/%s", question+4);
+ res = dirserv_get_routerdescs(descs, url, &msg);
+ if (res) {
+ log_warn(LD_CONTROL, "getinfo '%s': %s", question, msg);
+ smartlist_free(descs);
+ tor_free(url);
+ *errmsg = msg;
+ return -1;
+ }
+ SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
+ answer_len += sd->signed_descriptor_len);
+ cp = *answer = tor_malloc(answer_len+1);
+ SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
+ {
+ memcpy(cp, signed_descriptor_get_body(sd),
+ sd->signed_descriptor_len);
+ cp += sd->signed_descriptor_len;
+ });
+ *cp = '\0';
+ tor_free(url);
+ smartlist_free(descs);
+ } else if (!strcmpstart(question, "dir/status/")) {
+ *answer = tor_strdup("");
+ } else if (!strcmp(question, "dir/status-vote/current/consensus")) { /* v3 */
+ if (we_want_to_fetch_flavor(get_options(), FLAV_NS)) {
+ const cached_dir_t *consensus = dirserv_get_consensus("ns");
+ if (consensus)
+ *answer = tor_strdup(consensus->dir);
+ }
+ if (!*answer) { /* try loading it from disk */
+ tor_mmap_t *mapped = networkstatus_map_cached_consensus("ns");
+ if (mapped) {
+ *answer = tor_memdup_nulterm(mapped->data, mapped->size);
+ tor_munmap_file(mapped);
+ }
+ if (!*answer) { /* generate an error */
+ *errmsg = "Could not open cached consensus. "
+ "Make sure FetchUselessDescriptors is set to 1.";
+ return -1;
+ }
+ }
+ } else if (!strcmp(question, "network-status")) { /* v1 */
+ static int network_status_warned = 0;
+ if (!network_status_warned) {
+ log_warn(LD_CONTROL, "GETINFO network-status is deprecated; it will "
+ "go away in a future version of Tor.");
+ network_status_warned = 1;
+ }
+ routerlist_t *routerlist = router_get_routerlist();
+ if (!routerlist || !routerlist->routers ||
+ list_server_status_v1(routerlist->routers, answer, 1) < 0) {
+ return -1;
+ }
+ } else if (!strcmpstart(question, "extra-info/digest/")) {
+ question += strlen("extra-info/digest/");
+ if (strlen(question) == HEX_DIGEST_LEN) {
+ char d[DIGEST_LEN];
+ signed_descriptor_t *sd = NULL;
+ 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
+ * when it might be freed) are different from those for using one
+ * we have downloaded. */
+ if (router_extrainfo_digest_is_me(d))
+ sd = &(router_get_my_extrainfo()->cache_info);
+ else
+ sd = extrainfo_get_by_descriptor_digest(d);
+ }
+ if (sd) {
+ const char *body = signed_descriptor_get_body(sd);
+ if (body)
+ *answer = tor_strndup(body, sd->signed_descriptor_len);
+ }
+ }
+ }
+
+ 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;
+ 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, download_status_get_next_attempt_at(dl));
+
+ 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;
+ }
+
+ backoff_str = "DL_SCHED_RANDOM_EXPONENTIAL";
+
+ /* Now assemble them */
+ tor_asprintf(&rv,
+ "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"
+ "last-backoff-position %u\n"
+ "last-delay-used %d\n",
+ tbuf,
+ dl->n_download_failures,
+ dl->n_download_attempts,
+ schedule_str,
+ want_authority_str,
+ increment_on_str,
+ backoff_str,
+ dl->last_backoff_position,
+ dl->last_delay_used);
+ }
+
+ 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;
+ }
+}
+
+/** Implementation helper for GETINFO: knows how to generate summaries of the
+ * current states of things we send events about. */
+static int
+getinfo_helper_events(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg)
+{
+ const or_options_t *options = get_options();
+ (void) control_conn;
+ if (!strcmp(question, "circuit-status")) {
+ smartlist_t *status = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ_) {
+ origin_circuit_t *circ;
+ char *circdesc;
+ const char *state;
+ if (! CIRCUIT_IS_ORIGIN(circ_) || circ_->marked_for_close)
+ continue;
+ circ = TO_ORIGIN_CIRCUIT(circ_);
+
+ if (circ->base_.state == CIRCUIT_STATE_OPEN)
+ state = "BUILT";
+ else if (circ->base_.state == CIRCUIT_STATE_GUARD_WAIT)
+ state = "GUARD_WAIT";
+ else if (circ->cpath)
+ state = "EXTENDED";
+ else
+ state = "LAUNCHED";
+
+ circdesc = circuit_describe_status_for_controller(circ);
+
+ smartlist_add_asprintf(status, "%lu %s%s%s",
+ (unsigned long)circ->global_identifier,
+ state, *circdesc ? " " : "", circdesc);
+ tor_free(circdesc);
+ }
+ SMARTLIST_FOREACH_END(circ_);
+ *answer = smartlist_join_strings(status, "\r\n", 0, NULL);
+ SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
+ smartlist_free(status);
+ } else if (!strcmp(question, "stream-status")) {
+ smartlist_t *conns = get_connection_array();
+ smartlist_t *status = smartlist_new();
+ char buf[256];
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
+ const char *state;
+ entry_connection_t *conn;
+ circuit_t *circ;
+ origin_circuit_t *origin_circ = NULL;
+ if (base_conn->type != CONN_TYPE_AP ||
+ base_conn->marked_for_close ||
+ base_conn->state == AP_CONN_STATE_SOCKS_WAIT ||
+ base_conn->state == AP_CONN_STATE_NATD_WAIT)
+ continue;
+ conn = TO_ENTRY_CONN(base_conn);
+ switch (base_conn->state)
+ {
+ case AP_CONN_STATE_CONTROLLER_WAIT:
+ case AP_CONN_STATE_CIRCUIT_WAIT:
+ if (conn->socks_request &&
+ SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command))
+ state = "NEWRESOLVE";
+ else
+ state = "NEW";
+ break;
+ case AP_CONN_STATE_RENDDESC_WAIT:
+ case AP_CONN_STATE_CONNECT_WAIT:
+ state = "SENTCONNECT"; break;
+ case AP_CONN_STATE_RESOLVE_WAIT:
+ state = "SENTRESOLVE"; break;
+ case AP_CONN_STATE_OPEN:
+ state = "SUCCEEDED"; break;
+ default:
+ log_warn(LD_BUG, "Asked for stream in unknown state %d",
+ base_conn->state);
+ continue;
+ }
+ circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn));
+ if (circ && CIRCUIT_IS_ORIGIN(circ))
+ origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ write_stream_target_to_buf(conn, buf, sizeof(buf));
+ smartlist_add_asprintf(status, "%lu %s %lu %s",
+ (unsigned long) base_conn->global_identifier,state,
+ origin_circ?
+ (unsigned long)origin_circ->global_identifier : 0ul,
+ buf);
+ } SMARTLIST_FOREACH_END(base_conn);
+ *answer = smartlist_join_strings(status, "\r\n", 0, NULL);
+ SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
+ smartlist_free(status);
+ } else if (!strcmp(question, "orconn-status")) {
+ smartlist_t *conns = get_connection_array();
+ smartlist_t *status = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
+ const char *state;
+ char name[128];
+ or_connection_t *conn;
+ if (base_conn->type != CONN_TYPE_OR || base_conn->marked_for_close)
+ continue;
+ conn = TO_OR_CONN(base_conn);
+ if (conn->base_.state == OR_CONN_STATE_OPEN)
+ state = "CONNECTED";
+ else if (conn->nickname)
+ state = "LAUNCHED";
+ else
+ state = "NEW";
+ orconn_target_get_name(name, sizeof(name), conn);
+ smartlist_add_asprintf(status, "%s %s", name, state);
+ } SMARTLIST_FOREACH_END(base_conn);
+ *answer = smartlist_join_strings(status, "\r\n", 0, NULL);
+ SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
+ smartlist_free(status);
+ } else if (!strcmpstart(question, "address-mappings/")) {
+ time_t min_e, max_e;
+ smartlist_t *mappings;
+ question += strlen("address-mappings/");
+ if (!strcmp(question, "all")) {
+ min_e = 0; max_e = TIME_MAX;
+ } else if (!strcmp(question, "cache")) {
+ min_e = 2; max_e = TIME_MAX;
+ } else if (!strcmp(question, "config")) {
+ min_e = 0; max_e = 0;
+ } else if (!strcmp(question, "control")) {
+ min_e = 1; max_e = 1;
+ } else {
+ return 0;
+ }
+ mappings = smartlist_new();
+ addressmap_get_mappings(mappings, min_e, max_e, 1);
+ *answer = smartlist_join_strings(mappings, "\r\n", 0, NULL);
+ SMARTLIST_FOREACH(mappings, char *, cp, tor_free(cp));
+ smartlist_free(mappings);
+ } else if (!strcmpstart(question, "status/")) {
+ /* Note that status/ is not a catch-all for events; there's only supposed
+ * to be a status GETINFO if there's a corresponding STATUS event. */
+ if (!strcmp(question, "status/circuit-established")) {
+ *answer = tor_strdup(have_completed_a_circuit() ? "1" : "0");
+ } else if (!strcmp(question, "status/enough-dir-info")) {
+ *answer = tor_strdup(router_have_minimum_dir_info() ? "1" : "0");
+ } else if (!strcmp(question, "status/good-server-descriptor") ||
+ !strcmp(question, "status/accepted-server-descriptor")) {
+ /* They're equivalent for now, until we can figure out how to make
+ * good-server-descriptor be what we want. See comment in
+ * control-spec.txt. */
+ *answer = tor_strdup(directories_have_accepted_server_descriptor()
+ ? "1" : "0");
+ } else if (!strcmp(question, "status/reachability-succeeded/or")) {
+ *answer = tor_strdup(check_whether_orport_reachable(options) ?
+ "1" : "0");
+ } else if (!strcmp(question, "status/reachability-succeeded/dir")) {
+ *answer = tor_strdup(check_whether_dirport_reachable(options) ?
+ "1" : "0");
+ } else if (!strcmp(question, "status/reachability-succeeded")) {
+ tor_asprintf(answer, "OR=%d DIR=%d",
+ check_whether_orport_reachable(options) ? 1 : 0,
+ check_whether_dirport_reachable(options) ? 1 : 0);
+ } else if (!strcmp(question, "status/bootstrap-phase")) {
+ *answer = control_event_boot_last_msg();
+ } else if (!strcmpstart(question, "status/version/")) {
+ int is_server = server_mode(options);
+ networkstatus_t *c = networkstatus_get_latest_consensus();
+ version_status_t status;
+ const char *recommended;
+ if (c) {
+ recommended = is_server ? c->server_versions : c->client_versions;
+ status = tor_version_is_obsolete(VERSION, recommended);
+ } else {
+ recommended = "?";
+ status = VS_UNKNOWN;
+ }
+
+ if (!strcmp(question, "status/version/recommended")) {
+ *answer = tor_strdup(recommended);
+ return 0;
+ }
+ if (!strcmp(question, "status/version/current")) {
+ switch (status)
+ {
+ case VS_RECOMMENDED: *answer = tor_strdup("recommended"); break;
+ case VS_OLD: *answer = tor_strdup("obsolete"); break;
+ case VS_NEW: *answer = tor_strdup("new"); break;
+ case VS_NEW_IN_SERIES: *answer = tor_strdup("new in series"); break;
+ case VS_UNRECOMMENDED: *answer = tor_strdup("unrecommended"); break;
+ case VS_EMPTY: *answer = tor_strdup("none recommended"); break;
+ case VS_UNKNOWN: *answer = tor_strdup("unknown"); break;
+ default: tor_fragile_assert();
+ }
+ }
+ } else if (!strcmp(question, "status/clients-seen")) {
+ char *bridge_stats = geoip_get_bridge_stats_controller(time(NULL));
+ if (!bridge_stats) {
+ *errmsg = "No bridge-client stats available";
+ return -1;
+ }
+ *answer = bridge_stats;
+ } else if (!strcmp(question, "status/fresh-relay-descs")) {
+ if (!server_mode(options)) {
+ *errmsg = "Only relays have descriptors";
+ return -1;
+ }
+ routerinfo_t *r;
+ extrainfo_t *e;
+ if (router_build_fresh_descriptor(&r, &e) < 0) {
+ *errmsg = "Error generating descriptor";
+ return -1;
+ }
+ size_t size = r->cache_info.signed_descriptor_len + 1;
+ if (e) {
+ size += e->cache_info.signed_descriptor_len + 1;
+ }
+ tor_assert(r->cache_info.signed_descriptor_len);
+ char *descs = tor_malloc(size);
+ char *cp = descs;
+ memcpy(cp, signed_descriptor_get_body(&r->cache_info),
+ r->cache_info.signed_descriptor_len);
+ cp += r->cache_info.signed_descriptor_len - 1;
+ if (e) {
+ if (cp[0] == '\0') {
+ cp[0] = '\n';
+ } else if (cp[0] != '\n') {
+ cp[1] = '\n';
+ cp++;
+ }
+ memcpy(cp, signed_descriptor_get_body(&e->cache_info),
+ e->cache_info.signed_descriptor_len);
+ cp += e->cache_info.signed_descriptor_len - 1;
+ }
+ if (cp[0] == '\n') {
+ cp[0] = '\0';
+ } else if (cp[0] != '\0') {
+ cp[1] = '\0';
+ }
+ *answer = descs;
+ routerinfo_free(r);
+ extrainfo_free(e);
+ } else {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/** Implementation helper for GETINFO: knows how to enumerate hidden services
+ * created via the control port. */
+STATIC int
+getinfo_helper_onions(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg)
+{
+ smartlist_t *onion_list = NULL;
+ (void) errmsg; /* no errors from this method */
+
+ if (control_conn && !strcmp(question, "onions/current")) {
+ onion_list = control_conn->ephemeral_onion_services;
+ } else if (!strcmp(question, "onions/detached")) {
+ onion_list = get_detached_onion_services();
+ } else {
+ return 0;
+ }
+ if (!onion_list || smartlist_len(onion_list) == 0) {
+ if (answer) {
+ *answer = tor_strdup("");
+ }
+ } else {
+ if (answer) {
+ *answer = smartlist_join_strings(onion_list, "\r\n", 0, NULL);
+ }
+ }
+
+ return 0;
+}
+
+/** Implementation helper for GETINFO: answers queries about network
+ * liveness. */
+static int
+getinfo_helper_liveness(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg)
+{
+ (void)control_conn;
+ (void)errmsg;
+ if (strcmp(question, "network-liveness") == 0) {
+ if (get_cached_network_liveness()) {
+ *answer = tor_strdup("up");
+ } else {
+ *answer = tor_strdup("down");
+ }
+ }
+
+ return 0;
+}
+
+/** Implementation helper for GETINFO: answers queries about shared random
+ * value. */
+static int
+getinfo_helper_sr(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg)
+{
+ (void) control_conn;
+ (void) errmsg;
+
+ if (!strcmp(question, "sr/current")) {
+ *answer = sr_get_current_for_control();
+ } else if (!strcmp(question, "sr/previous")) {
+ *answer = sr_get_previous_for_control();
+ }
+ /* Else statement here is unrecognized key so do nothing. */
+
+ return 0;
+}
+
+/** Callback function for GETINFO: on a given control connection, try to
+ * answer the question <b>q</b> and store the newly-allocated answer in
+ * *<b>a</b>. If an internal error occurs, return -1 and optionally set
+ * *<b>error_out</b> to point to an error message to be delivered to the
+ * controller. On success, _or if the key is not recognized_, return 0. Do not
+ * set <b>a</b> if the key is not recognized but you may set <b>error_out</b>
+ * to improve the error message.
+ */
+typedef int (*getinfo_helper_t)(control_connection_t *,
+ const char *q, char **a,
+ const char **error_out);
+
+/** A single item for the GETINFO question-to-answer-function table. */
+typedef struct getinfo_item_t {
+ const char *varname; /**< The value (or prefix) of the question. */
+ getinfo_helper_t fn; /**< The function that knows the answer: NULL if
+ * this entry is documentation-only. */
+ const char *desc; /**< Description of the variable. */
+ int is_prefix; /** Must varname match exactly, or must it be a prefix? */
+} getinfo_item_t;
+
+#define ITEM(name, fn, desc) { name, getinfo_helper_##fn, desc, 0 }
+#define PREFIX(name, fn, desc) { name, getinfo_helper_##fn, desc, 1 }
+#define DOC(name, desc) { name, NULL, desc, 0 }
+
+/** Table mapping questions accepted by GETINFO to the functions that know how
+ * to answer them. */
+static const getinfo_item_t getinfo_items[] = {
+ ITEM("version", misc, "The current version of Tor."),
+ ITEM("bw-event-cache", misc, "Cached BW events for a short interval."),
+ ITEM("config-file", misc, "Current location of the \"torrc\" file."),
+ ITEM("config-defaults-file", misc, "Current location of the defaults file."),
+ ITEM("config-text", misc,
+ "Return the string that would be written by a saveconf command."),
+ ITEM("config-can-saveconf", misc,
+ "Is it possible to save the configuration to the \"torrc\" file?"),
+ ITEM("accounting/bytes", accounting,
+ "Number of bytes read/written so far in the accounting interval."),
+ ITEM("accounting/bytes-left", accounting,
+ "Number of bytes left to write/read so far in the accounting interval."),
+ ITEM("accounting/enabled", accounting, "Is accounting currently enabled?"),
+ ITEM("accounting/hibernating", accounting, "Are we hibernating or awake?"),
+ ITEM("accounting/interval-start", accounting,
+ "Time when the accounting period starts."),
+ ITEM("accounting/interval-end", accounting,
+ "Time when the accounting period ends."),
+ ITEM("accounting/interval-wake", accounting,
+ "Time to wake up in this accounting period."),
+ ITEM("helper-nodes", entry_guards, NULL), /* deprecated */
+ ITEM("entry-guards", entry_guards,
+ "Which nodes are we using as entry guards?"),
+ ITEM("fingerprint", misc, NULL),
+ PREFIX("config/", config, "Current configuration values."),
+ DOC("config/names",
+ "List of configuration options, types, and documentation."),
+ DOC("config/defaults",
+ "List of default values for configuration options. "
+ "See also config/names"),
+ PREFIX("current-time/", current_time, "Current time."),
+ DOC("current-time/local", "Current time on the local system."),
+ DOC("current-time/utc", "Current UTC time."),
+ 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,
+ "Events that the controller can ask for with SETEVENTS."),
+ ITEM("signal/names", misc, "Signal names recognized by the SIGNAL command"),
+ ITEM("features/names", misc, "What arguments can USEFEATURE take?"),
+ PREFIX("desc/id/", dir, "Router descriptors by ID."),
+ PREFIX("desc/name/", dir, "Router descriptors by nickname."),
+ ITEM("desc/all-recent", dir,
+ "All non-expired, non-superseded router descriptors."),
+ ITEM("desc/download-enabled", dir,
+ "Do we try to download router descriptors?"),
+ ITEM("desc/all-recent-extrainfo-hack", dir, NULL), /* Hack. */
+ ITEM("md/all", dir, "All known microdescriptors."),
+ PREFIX("md/id/", dir, "Microdescriptors by ID"),
+ PREFIX("md/name/", dir, "Microdescriptors by name"),
+ ITEM("md/download-enabled", dir,
+ "Do we try to download microdescriptors?"),
+ PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."),
+ PREFIX("hs/client/desc/id", dir,
+ "Hidden Service descriptor in client's cache by onion."),
+ PREFIX("hs/service/desc/id/", dir,
+ "Hidden Service descriptor in services's cache by onion."),
+ PREFIX("net/listeners/", listeners, "Bound addresses by type"),
+ ITEM("ns/all", networkstatus,
+ "Brief summary of router status (v2 directory format)"),
+ PREFIX("ns/id/", networkstatus,
+ "Brief summary of router status by ID (v2 directory format)."),
+ PREFIX("ns/name/", networkstatus,
+ "Brief summary of router status by nickname (v2 directory format)."),
+ PREFIX("ns/purpose/", networkstatus,
+ "Brief summary of router status by purpose (v2 directory format)."),
+ PREFIX("consensus/", networkstatus,
+ "Information about and from the ns consensus."),
+ ITEM("network-status", dir,
+ "Brief summary of router status (v1 directory format)"),
+ ITEM("network-liveness", liveness,
+ "Current opinion on whether the network is live"),
+ ITEM("circuit-status", events, "List of current circuits originating here."),
+ ITEM("stream-status", events,"List of current streams."),
+ ITEM("orconn-status", events, "A list of current OR connections."),
+ ITEM("dormant", misc,
+ "Is Tor dormant (not building circuits because it's idle)?"),
+ PREFIX("address-mappings/", events, NULL),
+ DOC("address-mappings/all", "Current address mappings."),
+ DOC("address-mappings/cache", "Current cached DNS replies."),
+ DOC("address-mappings/config",
+ "Current address mappings from configuration."),
+ DOC("address-mappings/control", "Current address mappings from controller."),
+ PREFIX("status/", events, NULL),
+ DOC("status/circuit-established",
+ "Whether we think client functionality is working."),
+ DOC("status/enough-dir-info",
+ "Whether we have enough up-to-date directory information to build "
+ "circuits."),
+ DOC("status/bootstrap-phase",
+ "The last bootstrap phase status event that Tor sent."),
+ DOC("status/clients-seen",
+ "Breakdown of client countries seen by a bridge."),
+ DOC("status/fresh-relay-descs",
+ "A fresh relay/ei descriptor pair for Tor's current state. Not stored."),
+ DOC("status/version/recommended", "List of currently recommended versions."),
+ DOC("status/version/current", "Status of the current version."),
+ ITEM("address", misc, "IP address of this Tor host, if we can guess it."),
+ ITEM("traffic/read", misc,"Bytes read since the process was started."),
+ ITEM("traffic/written", misc,
+ "Bytes written since the process was started."),
+ ITEM("uptime", misc, "Uptime of the Tor daemon in seconds."),
+ ITEM("process/pid", misc, "Process id belonging to the main tor process."),
+ ITEM("process/uid", misc, "User id running the tor process."),
+ ITEM("process/user", misc,
+ "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"),
+ PREFIX("desc-annotations/id/", dir, "Router annotations by hexdigest."),
+ PREFIX("dir/server/", dir,"Router descriptors as retrieved from a DirPort."),
+ PREFIX("dir/status/", dir,
+ "v2 networkstatus docs as retrieved from a DirPort."),
+ ITEM("dir/status-vote/current/consensus", dir,
+ "v3 Networkstatus consensus as retrieved from a DirPort."),
+ ITEM("exit-policy/default", policies,
+ "The default value appended to the configured exit policy."),
+ ITEM("exit-policy/reject-private/default", policies,
+ "The default rules appended to the configured exit policy by"
+ " ExitPolicyRejectPrivate."),
+ ITEM("exit-policy/reject-private/relay", policies,
+ "The relay-specific rules appended to the configured exit policy by"
+ " 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"),
+ PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"),
+ ITEM("onions/current", onions,
+ "Onion services owned by the current control connection."),
+ ITEM("onions/detached", onions,
+ "Onion services detached from the control connection."),
+ ITEM("sr/current", sr, "Get current shared random value."),
+ ITEM("sr/previous", sr, "Get previous shared random value."),
+ { NULL, NULL, NULL, 0 }
+};
+
+/** Allocate and return a list of recognized GETINFO options. */
+static char *
+list_getinfo_options(void)
+{
+ int i;
+ smartlist_t *lines = smartlist_new();
+ char *ans;
+ for (i = 0; getinfo_items[i].varname; ++i) {
+ if (!getinfo_items[i].desc)
+ continue;
+
+ smartlist_add_asprintf(lines, "%s%s -- %s\n",
+ getinfo_items[i].varname,
+ getinfo_items[i].is_prefix ? "*" : "",
+ getinfo_items[i].desc);
+ }
+ smartlist_sort_strings(lines);
+
+ ans = smartlist_join_strings(lines, "", 0, NULL);
+ SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
+ smartlist_free(lines);
+
+ return ans;
+}
+
+/** Lookup the 'getinfo' entry <b>question</b>, and return
+ * the answer in <b>*answer</b> (or NULL if key not recognized).
+ * Return 0 if success or unrecognized, or -1 if recognized but
+ * internal error. */
+static int
+handle_getinfo_helper(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **err_out)
+{
+ int i;
+ *answer = NULL; /* unrecognized key by default */
+
+ for (i = 0; getinfo_items[i].varname; ++i) {
+ int match;
+ if (getinfo_items[i].is_prefix)
+ match = !strcmpstart(question, getinfo_items[i].varname);
+ else
+ match = !strcmp(question, getinfo_items[i].varname);
+ if (match) {
+ tor_assert(getinfo_items[i].fn);
+ return getinfo_items[i].fn(control_conn, question, answer, err_out);
+ }
+ }
+
+ return 0; /* unrecognized */
+}
+
+const control_cmd_syntax_t getinfo_syntax = {
+ .max_args = UINT_MAX,
+};
+
+/** Called when we receive a GETINFO command. Try to fetch all requested
+ * information, and reply with information or error message. */
+int
+handle_control_getinfo(control_connection_t *conn,
+ const control_cmd_args_t *args)
+{
+ const smartlist_t *questions = args->args;
+ smartlist_t *answers = smartlist_new();
+ smartlist_t *unrecognized = smartlist_new();
+ char *ans = NULL;
+ int i;
+
+ SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
+ const char *errmsg = NULL;
+
+ if (handle_getinfo_helper(conn, q, &ans, &errmsg) < 0) {
+ if (!errmsg)
+ errmsg = "Internal error";
+ control_write_endreply(conn, 551, errmsg);
+ goto done;
+ }
+ if (!ans) {
+ if (errmsg) /* use provided error message */
+ smartlist_add_strdup(unrecognized, errmsg);
+ else /* use default error message */
+ smartlist_add_asprintf(unrecognized, "Unrecognized key \"%s\"", q);
+ } else {
+ smartlist_add_strdup(answers, q);
+ smartlist_add(answers, ans);
+ }
+ } SMARTLIST_FOREACH_END(q);
+
+ if (smartlist_len(unrecognized)) {
+ /* control-spec section 2.3, mid-reply '-' or end of reply ' ' */
+ for (i=0; i < smartlist_len(unrecognized)-1; ++i)
+ control_write_midreply(conn, 552,
+ (char *)smartlist_get(unrecognized, i));
+
+ control_write_endreply(conn, 552, (char *)smartlist_get(unrecognized, i));
+ goto done;
+ }
+
+ for (i = 0; i < smartlist_len(answers); i += 2) {
+ char *k = smartlist_get(answers, i);
+ char *v = smartlist_get(answers, i+1);
+ if (!strchr(v, '\n') && !strchr(v, '\r')) {
+ control_printf_midreply(conn, 250, "%s=%s", k, v);
+ } else {
+ control_printf_datareply(conn, 250, "%s=", k);
+ control_write_data(conn, v);
+ }
+ }
+ send_control_done(conn);
+
+ done:
+ SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp));
+ smartlist_free(answers);
+ SMARTLIST_FOREACH(unrecognized, char *, cp, tor_free(cp));
+ smartlist_free(unrecognized);
+
+ return 0;
+}
diff --git a/src/feature/control/control_getinfo.h b/src/feature/control/control_getinfo.h
new file mode 100644
index 0000000000..52978686d8
--- /dev/null
+++ b/src/feature/control/control_getinfo.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control.h
+ * \brief Header file for control.c.
+ **/
+
+#ifndef TOR_CONTROL_GETINFO_H
+#define TOR_CONTROL_GETINFO_H
+
+struct control_cmd_syntax_t;
+struct control_cmd_args_t;
+extern const struct control_cmd_syntax_t getinfo_syntax;
+
+int handle_control_getinfo(control_connection_t *conn,
+ const struct control_cmd_args_t *args);
+
+#ifdef CONTROL_GETINFO_PRIVATE
+STATIC int getinfo_helper_onions(
+ control_connection_t *control_conn,
+ const char *question,
+ char **answer,
+ const char **errmsg);
+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);
+STATIC int getinfo_helper_dir(
+ control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg);
+STATIC int getinfo_helper_current_time(
+ control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg);
+#endif /* defined(CONTROL_GETINFO_PRIVATE) */
+
+#endif /* !defined(TOR_CONTROL_GETINFO_H) */
diff --git a/src/feature/control/control_proto.c b/src/feature/control/control_proto.c
new file mode 100644
index 0000000000..1dd62da2be
--- /dev/null
+++ b/src/feature/control/control_proto.c
@@ -0,0 +1,276 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_proto.c
+ * \brief Formatting functions for controller data.
+ */
+
+#include "core/or/or.h"
+
+#include "core/mainloop/connection.h"
+#include "core/or/circuitbuild.h"
+#include "core/or/circuitlist.h"
+#include "core/or/connection_edge.h"
+#include "feature/control/control_proto.h"
+#include "feature/nodelist/nodelist.h"
+
+#include "core/or/cpath_build_state_st.h"
+#include "core/or/entry_connection_st.h"
+#include "core/or/or_connection_st.h"
+#include "core/or/origin_circuit_st.h"
+#include "core/or/socks_request_st.h"
+#include "feature/control/control_connection_st.h"
+
+/** Append a NUL-terminated string <b>s</b> to the end of
+ * <b>conn</b>-\>outbuf.
+ */
+void
+connection_write_str_to_buf(const char *s, control_connection_t *conn)
+{
+ size_t len = strlen(s);
+ connection_buf_add(s, len, TO_CONN(conn));
+}
+
+/** Acts like sprintf, but writes its formatted string to the end of
+ * <b>conn</b>-\>outbuf. */
+void
+connection_printf_to_buf(control_connection_t *conn, const char *format, ...)
+{
+ va_list ap;
+ char *buf = NULL;
+ int len;
+
+ va_start(ap,format);
+ len = tor_vasprintf(&buf, format, ap);
+ va_end(ap);
+
+ if (len < 0) {
+ log_err(LD_BUG, "Unable to format string for controller.");
+ tor_assert(0);
+ }
+
+ connection_buf_add(buf, (size_t)len, TO_CONN(conn));
+
+ tor_free(buf);
+}
+
+/** Given a <b>len</b>-character string in <b>data</b>, made of lines
+ * terminated by CRLF, allocate a new string in *<b>out</b>, and copy the
+ * contents of <b>data</b> into *<b>out</b>, adding a period before any period
+ * that appears at the start of a line, and adding a period-CRLF line at
+ * the end. Replace all LF characters sequences with CRLF. Return the number
+ * of bytes in *<b>out</b>.
+ *
+ * This corresponds to CmdData in control-spec.txt.
+ */
+size_t
+write_escaped_data(const char *data, size_t len, char **out)
+{
+ tor_assert(len < SIZE_MAX - 9);
+ size_t sz_out = len+8+1;
+ char *outp;
+ const char *start = data, *end;
+ size_t i;
+ int start_of_line;
+ for (i=0; i < len; ++i) {
+ if (data[i] == '\n') {
+ sz_out += 2; /* Maybe add a CR; maybe add a dot. */
+ if (sz_out >= SIZE_T_CEILING) {
+ log_warn(LD_BUG, "Input to write_escaped_data was too long");
+ *out = tor_strdup(".\r\n");
+ return 3;
+ }
+ }
+ }
+ *out = outp = tor_malloc(sz_out);
+ end = data+len;
+ start_of_line = 1;
+ while (data < end) {
+ if (*data == '\n') {
+ if (data > start && data[-1] != '\r')
+ *outp++ = '\r';
+ start_of_line = 1;
+ } else if (*data == '.') {
+ if (start_of_line) {
+ start_of_line = 0;
+ *outp++ = '.';
+ }
+ } else {
+ start_of_line = 0;
+ }
+ *outp++ = *data++;
+ }
+ if (outp < *out+2 || fast_memcmp(outp-2, "\r\n", 2)) {
+ *outp++ = '\r';
+ *outp++ = '\n';
+ }
+ *outp++ = '.';
+ *outp++ = '\r';
+ *outp++ = '\n';
+ *outp = '\0'; /* NUL-terminate just in case. */
+ tor_assert(outp >= *out);
+ tor_assert((size_t)(outp - *out) <= sz_out);
+ return outp - *out;
+}
+
+/** Given a <b>len</b>-character string in <b>data</b>, made of lines
+ * terminated by CRLF, allocate a new string in *<b>out</b>, and copy
+ * the contents of <b>data</b> into *<b>out</b>, removing any period
+ * that appears at the start of a line, and replacing all CRLF sequences
+ * with LF. Return the number of
+ * bytes in *<b>out</b>.
+ *
+ * This corresponds to CmdData in control-spec.txt.
+ */
+size_t
+read_escaped_data(const char *data, size_t len, char **out)
+{
+ char *outp;
+ const char *next;
+ const char *end;
+
+ *out = outp = tor_malloc(len+1);
+
+ end = data+len;
+
+ while (data < end) {
+ /* we're at the start of a line. */
+ if (*data == '.')
+ ++data;
+ next = memchr(data, '\n', end-data);
+ if (next) {
+ size_t n_to_copy = next-data;
+ /* Don't copy a CR that precedes this LF. */
+ if (n_to_copy && *(next-1) == '\r')
+ --n_to_copy;
+ memcpy(outp, data, n_to_copy);
+ outp += n_to_copy;
+ data = next+1; /* This will point at the start of the next line,
+ * or the end of the string, or a period. */
+ } else {
+ memcpy(outp, data, end-data);
+ outp += (end-data);
+ *outp = '\0';
+ return outp - *out;
+ }
+ *outp++ = '\n';
+ }
+
+ *outp = '\0';
+ return outp - *out;
+}
+
+/** Send a "DONE" message down the control connection <b>conn</b>. */
+void
+send_control_done(control_connection_t *conn)
+{
+ control_write_endreply(conn, 250, "OK");
+}
+
+/** Write a reply to the control channel.
+ *
+ * @param conn control connection
+ * @param code numeric result code
+ * @param c separator character, usually ' ', '-', or '+'
+ * @param s string
+ */
+void
+control_write_reply(control_connection_t *conn, int code, int c, const char *s)
+{
+ connection_printf_to_buf(conn, "%03d%c%s\r\n", code, c, s);
+}
+
+/** Write a formatted reply to the control channel.
+ *
+ * @param conn control connection
+ * @param code numeric result code
+ * @param c separator character, usually ' ', '-', or '+'
+ * @param fmt format string
+ * @param ap va_list from caller
+ */
+void
+control_vprintf_reply(control_connection_t *conn, int code, int c,
+ const char *fmt, va_list ap)
+{
+ char *buf = NULL;
+ int len;
+
+ len = tor_vasprintf(&buf, fmt, ap);
+ if (len < 0) {
+ log_err(LD_BUG, "Unable to format string for controller.");
+ tor_assert(0);
+ }
+ control_write_reply(conn, code, c, buf);
+ tor_free(buf);
+}
+
+/** Write an EndReplyLine */
+void
+control_write_endreply(control_connection_t *conn, int code, const char *s)
+{
+ control_write_reply(conn, code, ' ', s);
+}
+
+/** Write a formatted EndReplyLine */
+void
+control_printf_endreply(control_connection_t *conn, int code,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ control_vprintf_reply(conn, code, ' ', fmt, ap);
+ va_end(ap);
+}
+
+/** Write a MidReplyLine */
+void
+control_write_midreply(control_connection_t *conn, int code, const char *s)
+{
+ control_write_reply(conn, code, '-', s);
+}
+
+/** Write a formatted MidReplyLine */
+void
+control_printf_midreply(control_connection_t *conn, int code, const char *fmt,
+ ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ control_vprintf_reply(conn, code, '-', fmt, ap);
+ va_end(ap);
+}
+
+/** Write a DataReplyLine */
+void
+control_write_datareply(control_connection_t *conn, int code, const char *s)
+{
+ control_write_reply(conn, code, '+', s);
+}
+
+/** Write a formatted DataReplyLine */
+void
+control_printf_datareply(control_connection_t *conn, int code, const char *fmt,
+ ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ control_vprintf_reply(conn, code, '+', fmt, ap);
+ va_end(ap);
+}
+
+/** Write a CmdData */
+void
+control_write_data(control_connection_t *conn, const char *data)
+{
+ char *esc = NULL;
+ size_t esc_len;
+
+ esc_len = write_escaped_data(data, strlen(data), &esc);
+ connection_buf_add(esc, esc_len, TO_CONN(conn));
+ tor_free(esc);
+}
diff --git a/src/feature/control/control_proto.h b/src/feature/control/control_proto.h
new file mode 100644
index 0000000000..101b808d88
--- /dev/null
+++ b/src/feature/control/control_proto.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_proto.h
+ * \brief Header file for control_proto.c.
+ **/
+
+#ifndef TOR_CONTROL_PROTO_H
+#define TOR_CONTROL_PROTO_H
+
+void connection_write_str_to_buf(const char *s, control_connection_t *conn);
+void connection_printf_to_buf(control_connection_t *conn,
+ const char *format, ...)
+ CHECK_PRINTF(2,3);
+
+size_t write_escaped_data(const char *data, size_t len, char **out);
+size_t read_escaped_data(const char *data, size_t len, char **out);
+void send_control_done(control_connection_t *conn);
+
+void control_write_reply(control_connection_t *conn, int code, int c,
+ const char *s);
+void control_vprintf_reply(control_connection_t *conn, int code, int c,
+ const char *fmt, va_list ap)
+ CHECK_PRINTF(4, 0);
+void control_write_endreply(control_connection_t *conn, int code,
+ const char *s);
+void control_printf_endreply(control_connection_t *conn, int code,
+ const char *fmt, ...)
+ CHECK_PRINTF(3, 4);
+void control_write_midreply(control_connection_t *conn, int code,
+ const char *s);
+void control_printf_midreply(control_connection_t *conn, int code,
+ const char *fmt,
+ ...)
+ CHECK_PRINTF(3, 4);
+void control_write_datareply(control_connection_t *conn, int code,
+ const char *s);
+void control_printf_datareply(control_connection_t *conn, int code,
+ const char *fmt,
+ ...)
+ CHECK_PRINTF(3, 4);
+void control_write_data(control_connection_t *conn, const char *data);
+
+#endif /* !defined(TOR_CONTROL_PROTO_H) */
diff --git a/src/feature/control/fmt_serverstatus.c b/src/feature/control/fmt_serverstatus.c
index a1ddd2119a..a80bf50ad9 100644
--- a/src/feature/control/fmt_serverstatus.c
+++ b/src/feature/control/fmt_serverstatus.c
@@ -66,11 +66,9 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
smartlist_t *rs_entries;
time_t now = time(NULL);
time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
- const or_options_t *options = get_options();
/* We include v2 dir auths here too, because they need to answer
* controllers. Eventually we'll deprecate this whole function;
* see also networkstatus_getinfo_by_purpose(). */
- int authdir = authdir_mode_publishes_statuses(options);
tor_assert(router_status_out);
rs_entries = smartlist_new();
@@ -78,10 +76,6 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
tor_assert(node);
- if (authdir) {
- /* Update router status in routerinfo_t. */
- dirserv_set_router_is_running(ri, now);
- }
if (for_controller) {
char name_buf[MAX_VERBOSE_NICKNAME_LEN+2];
char *cp = name_buf;
diff --git a/src/feature/control/fmt_serverstatus.h b/src/feature/control/fmt_serverstatus.h
index 4b95e5b59f..d9190cb7e1 100644
--- a/src/feature/control/fmt_serverstatus.h
+++ b/src/feature/control/fmt_serverstatus.h
@@ -15,4 +15,4 @@
int list_server_status_v1(smartlist_t *routers, char **router_status_out,
int for_controller);
-#endif
+#endif /* !defined(TOR_FMT_SERVERSTATUS_H) */
diff --git a/src/feature/control/getinfo_geoip.h b/src/feature/control/getinfo_geoip.h
index fe22137859..94759d0d18 100644
--- a/src/feature/control/getinfo_geoip.h
+++ b/src/feature/control/getinfo_geoip.h
@@ -11,4 +11,4 @@ int getinfo_helper_geoip(control_connection_t *control_conn,
const char *question, char **answer,
const char **errmsg);
-#endif
+#endif /* !defined(TOR_GETINFO_GEOIP_H) */
diff --git a/src/feature/dirauth/authmode.h b/src/feature/dirauth/authmode.h
index 876a1f947b..48afc3cdb4 100644
--- a/src/feature/dirauth/authmode.h
+++ b/src/feature/dirauth/authmode.h
@@ -29,7 +29,7 @@ authdir_mode_v3(const or_options_t *options)
#define have_module_dirauth() (1)
-#else /* HAVE_MODULE_DIRAUTH */
+#else /* !(defined(HAVE_MODULE_DIRAUTH)) */
#define authdir_mode(options) (((void)(options)),0)
#define authdir_mode_handles_descs(options,purpose) \
@@ -41,6 +41,6 @@ authdir_mode_v3(const or_options_t *options)
#define have_module_dirauth() (0)
-#endif /* HAVE_MODULE_DIRAUTH */
+#endif /* defined(HAVE_MODULE_DIRAUTH) */
-#endif /* TOR_MODE_H */
+#endif /* !defined(TOR_DIRAUTH_MODE_H) */
diff --git a/src/feature/dirauth/bridgeauth.c b/src/feature/dirauth/bridgeauth.c
new file mode 100644
index 0000000000..4aaefc7a6d
--- /dev/null
+++ b/src/feature/dirauth/bridgeauth.c
@@ -0,0 +1,55 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "core/or/or.h"
+#include "feature/dirauth/bridgeauth.h"
+#include "feature/dirauth/voteflags.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/relay/router.h"
+#include "app/config/config.h"
+
+#include "feature/nodelist/routerinfo_st.h"
+
+/** Write out router status entries for all our bridge descriptors. Here, we
+ * also mark routers as running. */
+void
+bridgeauth_dump_bridge_status_to_file(time_t now)
+{
+ char *status;
+ char *fname = NULL;
+ char *thresholds = NULL;
+ char *published_thresholds_and_status = NULL;
+ char published[ISO_TIME_LEN+1];
+ const routerinfo_t *me = router_get_my_routerinfo();
+ char fingerprint[FINGERPRINT_LEN+1];
+ char *fingerprint_line = NULL;
+
+ dirserv_set_bridges_running(now);
+ status = networkstatus_getinfo_by_purpose("bridge", now);
+
+ if (me && crypto_pk_get_fingerprint(me->identity_pkey,
+ fingerprint, 0) >= 0) {
+ tor_asprintf(&fingerprint_line, "fingerprint %s\n", fingerprint);
+ } else {
+ log_warn(LD_BUG, "Error computing fingerprint for bridge status.");
+ }
+ format_iso_time(published, now);
+ dirserv_compute_bridge_flag_thresholds();
+ thresholds = dirserv_get_flag_thresholds_line();
+ tor_asprintf(&published_thresholds_and_status,
+ "published %s\nflag-thresholds %s\n%s%s",
+ published, thresholds, fingerprint_line ? fingerprint_line : "",
+ status);
+ fname = get_datadir_fname("networkstatus-bridges");
+ if (write_str_to_file(fname,published_thresholds_and_status,0)<0) {
+ log_warn(LD_DIRSERV, "Unable to write networkstatus-bridges file.");
+ }
+ tor_free(thresholds);
+ tor_free(published_thresholds_and_status);
+ tor_free(fname);
+ tor_free(status);
+ tor_free(fingerprint_line);
+}
diff --git a/src/feature/dirauth/bridgeauth.h b/src/feature/dirauth/bridgeauth.h
new file mode 100644
index 0000000000..4905e9c3ee
--- /dev/null
+++ b/src/feature/dirauth/bridgeauth.h
@@ -0,0 +1,12 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_DIRAUTH_BRIDGEAUTH_H
+#define TOR_DIRAUTH_BRIDGEAUTH_H
+
+void bridgeauth_dump_bridge_status_to_file(time_t now);
+
+#endif /* !defined(TOR_DIRAUTH_BRIDGEAUTH_H) */
diff --git a/src/feature/dirauth/bwauth.c b/src/feature/dirauth/bwauth.c
index 1cfd8119df..e60c8b86bd 100644
--- a/src/feature/dirauth/bwauth.c
+++ b/src/feature/dirauth/bwauth.c
@@ -199,9 +199,32 @@ dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri)
}
/**
- * Read the measured bandwidth list file, apply it to the list of
- * vote_routerstatus_t and store all the headers in <b>bw_file_headers</b>.
+ * Read the measured bandwidth list <b>from_file</b>:
+ * - store all the headers in <b>bw_file_headers</b>,
+ * - apply bandwidth lines to the list of vote_routerstatus_t in
+ * <b>routerstatuses</b>,
+ * - cache bandwidth lines for dirserv_get_bandwidth_for_router(),
+ * - expire old entries in the measured bandwidth cache, and
+ * - store the DIGEST_SHA256 of the contents of the file in <b>digest_out</b>.
+ *
* Returns -1 on error, 0 otherwise.
+ *
+ * If the file can't be read, or is empty:
+ * - <b>bw_file_headers</b> is empty,
+ * - <b>routerstatuses</b> is not modified,
+ * - the measured bandwidth cache is not modified, and
+ * - <b>digest_out</b> is the zero-byte digest.
+ *
+ * Otherwise, if there is an error later in the file:
+ * - <b>bw_file_headers</b> contains all the headers up to the error,
+ * - <b>routerstatuses</b> is updated with all the relay lines up to the error,
+ * - the measured bandwidth cache is updated with all the relay lines up to
+ * the error,
+ * - if the timestamp is valid and recent, old entries in the measured
+ * bandwidth cache are expired, and
+ * - <b>digest_out</b> is the digest up to the first read error (if any).
+ * The digest is taken over all the readable file contents, even if the
+ * file is outdated or unparseable.
*/
int
dirserv_read_measured_bandwidths(const char *from_file,
@@ -223,15 +246,12 @@ dirserv_read_measured_bandwidths(const char *from_file,
size_t n = 0;
crypto_digest_t *digest = crypto_digest256_new(DIGEST_SHA256);
- /* Initialise line, so that we can't possibly run off the end. */
-
if (fp == NULL) {
log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s",
from_file);
goto err;
}
- /* If fgets fails, line is either unmodified, or indeterminate. */
if (tor_getline(&line,&n,fp) <= 0) {
log_warn(LD_DIRSERV, "Empty bandwidth file");
goto err;
@@ -345,6 +365,9 @@ dirserv_read_measured_bandwidths(const char *from_file,
* the header block yet. If we encounter an incomplete bw line, return -1 but
* don't warn since there could be additional header lines coming. If we
* encounter a proper bw line, return 0 (and we got past the headers).
+ *
+ * If the line contains "vote=0", stop parsing it, and return -1, so that the
+ * line is ignored during voting.
*/
STATIC int
measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line,
diff --git a/src/feature/dirauth/bwauth.h b/src/feature/dirauth/bwauth.h
index 8b7acc4a1c..81c8affbd7 100644
--- a/src/feature/dirauth/bwauth.h
+++ b/src/feature/dirauth/bwauth.h
@@ -55,4 +55,4 @@ STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
STATIC void dirserv_expire_measured_bw_cache(time_t now);
#endif /* defined(BWAUTH_PRIVATE) */
-#endif
+#endif /* !defined(TOR_BWAUTH_H) */
diff --git a/src/feature/dirauth/dirauth_periodic.c b/src/feature/dirauth/dirauth_periodic.c
new file mode 100644
index 0000000000..02727d61b4
--- /dev/null
+++ b/src/feature/dirauth/dirauth_periodic.c
@@ -0,0 +1,161 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "core/or/or.h"
+
+#include "app/config/or_options_st.h"
+#include "core/mainloop/netstatus.h"
+#include "feature/dirauth/reachability.h"
+#include "feature/stats/rephist.h"
+
+#include "feature/dirauth/bridgeauth.h"
+#include "feature/dirauth/dirvote.h"
+#include "feature/dirauth/dirauth_periodic.h"
+#include "feature/dirauth/authmode.h"
+
+#include "core/mainloop/periodic.h"
+
+#define DECLARE_EVENT(name, roles, flags) \
+ static periodic_event_item_t name ## _event = \
+ PERIODIC_EVENT(name, \
+ PERIODIC_EVENT_ROLE_##roles, \
+ flags)
+
+#define FL(name) (PERIODIC_EVENT_FLAG_##name)
+
+/**
+ * Periodic callback: if we're an authority, check on our authority
+ * certificate (the one that authenticates our authority signing key).
+ */
+static int
+check_authority_cert_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
+ /* 1e. Periodically, if we're a v3 authority, we check whether our cert is
+ * close to expiring and warn the admin if it is. */
+ v3_authority_check_key_expiry();
+#define CHECK_V3_CERTIFICATE_INTERVAL (5*60)
+ return CHECK_V3_CERTIFICATE_INTERVAL;
+}
+
+DECLARE_EVENT(check_authority_cert, DIRAUTH, 0);
+
+/**
+ * Scheduled callback: Run directory-authority voting functionality.
+ *
+ * The schedule is a bit complicated here, so dirvote_act() manages the
+ * schedule itself.
+ **/
+static int
+dirvote_callback(time_t now, const or_options_t *options)
+{
+ if (!authdir_mode_v3(options)) {
+ tor_assert_nonfatal_unreached();
+ return 3600;
+ }
+
+ time_t next = dirvote_act(options, now);
+ if (BUG(next == TIME_MAX)) {
+ /* This shouldn't be returned unless we called dirvote_act() without
+ * being an authority. If it happens, maybe our configuration will
+ * fix itself in an hour or so? */
+ return 3600;
+ }
+ return safe_timer_diff(now, next);
+}
+
+DECLARE_EVENT(dirvote, DIRAUTH, FL(NEED_NET));
+
+/** Reschedule the directory-authority voting event. Run this whenever the
+ * schedule has changed. */
+void
+reschedule_dirvote(const or_options_t *options)
+{
+ if (authdir_mode_v3(options)) {
+ periodic_event_reschedule(&dirvote_event);
+ }
+}
+
+/**
+ * Periodic callback: if we're an authority, record our measured stability
+ * information from rephist in an mtbf file.
+ */
+static int
+save_stability_callback(time_t now, const or_options_t *options)
+{
+ if (authdir_mode_tests_reachability(options)) {
+ if (rep_hist_record_mtbf_data(now, 1)<0) {
+ log_warn(LD_GENERAL, "Couldn't store mtbf data.");
+ }
+ }
+#define SAVE_STABILITY_INTERVAL (30*60)
+ return SAVE_STABILITY_INTERVAL;
+}
+
+DECLARE_EVENT(save_stability, AUTHORITIES, 0);
+
+/**
+ * Periodic callback: if we're an authority, make sure we test
+ * the routers on the network for reachability.
+ */
+static int
+launch_reachability_tests_callback(time_t now, const or_options_t *options)
+{
+ if (authdir_mode_tests_reachability(options) &&
+ !net_is_disabled()) {
+ /* try to determine reachability of the other Tor relays */
+ dirserv_test_reachability(now);
+ }
+ return REACHABILITY_TEST_INTERVAL;
+}
+
+DECLARE_EVENT(launch_reachability_tests, AUTHORITIES, FL(NEED_NET));
+
+/**
+ * Periodic callback: if we're an authority, discount the stability
+ * information (and other rephist information) that's older.
+ */
+static int
+downrate_stability_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
+ /* 1d. Periodically, we discount older stability information so that new
+ * stability info counts more, and save the stability information to disk as
+ * appropriate. */
+ time_t next = rep_hist_downrate_old_runs(now);
+ return safe_timer_diff(now, next);
+}
+
+DECLARE_EVENT(downrate_stability, AUTHORITIES, 0);
+
+/**
+ * Periodic callback: if we're the bridge authority, write a networkstatus
+ * file to disk.
+ */
+static int
+write_bridge_ns_callback(time_t now, const or_options_t *options)
+{
+ if (options->BridgeAuthoritativeDir) {
+ bridgeauth_dump_bridge_status_to_file(now);
+#define BRIDGE_STATUSFILE_INTERVAL (30*60)
+ return BRIDGE_STATUSFILE_INTERVAL;
+ }
+ return PERIODIC_EVENT_NO_UPDATE;
+}
+
+DECLARE_EVENT(write_bridge_ns, BRIDGEAUTH, 0);
+
+void
+dirauth_register_periodic_events(void)
+{
+ periodic_events_register(&downrate_stability_event);
+ periodic_events_register(&launch_reachability_tests_event);
+ periodic_events_register(&save_stability_event);
+ periodic_events_register(&check_authority_cert_event);
+ periodic_events_register(&dirvote_event);
+ periodic_events_register(&write_bridge_ns_event);
+}
diff --git a/src/feature/dirauth/dirauth_periodic.h b/src/feature/dirauth/dirauth_periodic.h
new file mode 100644
index 0000000000..1124fae952
--- /dev/null
+++ b/src/feature/dirauth/dirauth_periodic.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef DIRVOTE_PERIODIC_H
+#define DIRVOTE_PERIODIC_H
+
+#ifdef HAVE_MODULE_DIRAUTH
+
+void dirauth_register_periodic_events(void);
+void reschedule_dirvote(const or_options_t *options);
+
+#else /* !(defined(HAVE_MODULE_DIRAUTH)) */
+
+static inline void
+reschedule_dirvote(const or_options_t *options)
+{
+ (void)options;
+}
+
+#endif /* defined(HAVE_MODULE_DIRAUTH) */
+
+#endif /* !defined(DIRVOTE_PERIODIC_H) */
diff --git a/src/feature/dirauth/dirauth_sys.c b/src/feature/dirauth/dirauth_sys.c
new file mode 100644
index 0000000000..e38d391300
--- /dev/null
+++ b/src/feature/dirauth/dirauth_sys.c
@@ -0,0 +1,40 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "core/or/or.h"
+
+#include "feature/dirauth/bwauth.h"
+#include "feature/dirauth/dirauth_sys.h"
+#include "feature/dirauth/dirvote.h"
+#include "feature/dirauth/dirauth_periodic.h"
+#include "feature/dirauth/keypin.h"
+#include "feature/dirauth/process_descs.h"
+
+#include "lib/subsys/subsys.h"
+
+static int
+subsys_dirauth_initialize(void)
+{
+ dirauth_register_periodic_events();
+ return 0;
+}
+
+static void
+subsys_dirauth_shutdown(void)
+{
+ dirserv_free_fingerprint_list();
+ dirvote_free_all();
+ dirserv_clear_measured_bw_cache();
+ keypin_close_journal();
+}
+
+const struct subsys_fns_t sys_dirauth = {
+ .name = "dirauth",
+ .supported = true,
+ .level = 70,
+ .initialize = subsys_dirauth_initialize,
+ .shutdown = subsys_dirauth_shutdown,
+};
diff --git a/src/feature/dirauth/dirauth_sys.h b/src/feature/dirauth/dirauth_sys.h
new file mode 100644
index 0000000000..4e9b6a2ab4
--- /dev/null
+++ b/src/feature/dirauth/dirauth_sys.h
@@ -0,0 +1,12 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef DIRAUTH_SYS_H
+#define DIRAUTH_SYS_H
+
+extern const struct subsys_fns_t sys_dirauth;
+
+#endif /* !defined(DIRAUTH_SYS_H) */
diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c
index 755b99bae2..cdbdf5a216 100644
--- a/src/feature/dirauth/dirvote.c
+++ b/src/feature/dirauth/dirvote.c
@@ -2598,7 +2598,7 @@ networkstatus_add_detached_signatures(networkstatus_t *target,
return -1;
}
for (alg = DIGEST_SHA1; alg < N_COMMON_DIGEST_ALGORITHMS; ++alg) {
- if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) {
+ if (!fast_mem_is_zero(digests->d[alg], DIGEST256_LEN)) {
if (fast_memeq(target->digests.d[alg], digests->d[alg],
DIGEST256_LEN)) {
++n_matches;
@@ -2794,7 +2794,7 @@ networkstatus_get_detached_signatures(smartlist_t *consensuses)
char d[HEX_DIGEST256_LEN+1];
const char *alg_name =
crypto_digest_algorithm_get_name(alg);
- if (tor_mem_is_zero(ns->digests.d[alg], DIGEST256_LEN))
+ if (fast_mem_is_zero(ns->digests.d[alg], DIGEST256_LEN))
continue;
base16_encode(d, sizeof(d), ns->digests.d[alg], DIGEST256_LEN);
smartlist_add_asprintf(elements, "additional-digest %s %s %s\n",
@@ -3913,8 +3913,7 @@ dirvote_format_microdesc_vote_line(char *out_buf, size_t out_buf_len,
",");
tor_assert(microdesc_consensus_methods);
- if (digest256_to_base64(d64, md->digest)<0)
- goto out;
+ digest256_to_base64(d64, md->digest);
if (tor_snprintf(out_buf, out_buf_len, "m %s sha256=%s\n",
microdesc_consensus_methods, d64)<0)
@@ -4545,8 +4544,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
- set_routerstatus_from_routerinfo(rs, node, ri, now,
- listbadexits);
+ dirauth_set_routerstatus_from_routerinfo(rs, node, ri, now,
+ listbadexits);
if (ri->cache_info.signing_key_cert) {
memcpy(vrs->ed25519_id,
diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h
index f9de5ebc41..a0cfe0a34c 100644
--- a/src/feature/dirauth/dirvote.h
+++ b/src/feature/dirauth/dirvote.h
@@ -128,7 +128,7 @@ struct config_line_t;
char *format_recommended_version_list(const struct config_line_t *line,
int warn);
-#else /* HAVE_MODULE_DIRAUTH */
+#else /* !(defined(HAVE_MODULE_DIRAUTH)) */
static inline time_t
dirvote_act(const or_options_t *options, time_t now)
@@ -193,7 +193,7 @@ dirvote_add_signatures(const char *detached_signatures_body,
return 0;
}
-#endif /* HAVE_MODULE_DIRAUTH */
+#endif /* defined(HAVE_MODULE_DIRAUTH) */
/* Item access */
MOCK_DECL(const char*, dirvote_get_pending_consensus,
diff --git a/src/feature/dirauth/dsigs_parse.c b/src/feature/dirauth/dsigs_parse.c
index d88176fee9..c5c8e18866 100644
--- a/src/feature/dirauth/dsigs_parse.c
+++ b/src/feature/dirauth/dsigs_parse.c
@@ -127,7 +127,7 @@ 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], digest_length)) {
+ if (!fast_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;
diff --git a/src/feature/dirauth/dsigs_parse.h b/src/feature/dirauth/dsigs_parse.h
index fec51ba488..0cc53072f8 100644
--- a/src/feature/dirauth/dsigs_parse.h
+++ b/src/feature/dirauth/dsigs_parse.h
@@ -19,4 +19,4 @@ void ns_detached_signatures_free_(ns_detached_signatures_t *s);
#define ns_detached_signatures_free(s) \
FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s))
-#endif
+#endif /* !defined(TOR_DSIGS_PARSE_H) */
diff --git a/src/feature/dirauth/guardfraction.h b/src/feature/dirauth/guardfraction.h
index 72404907a4..9f01ded838 100644
--- a/src/feature/dirauth/guardfraction.h
+++ b/src/feature/dirauth/guardfraction.h
@@ -21,4 +21,4 @@ dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str,
int dirserv_read_guardfraction_file(const char *fname,
smartlist_t *vote_routerstatuses);
-#endif
+#endif /* !defined(TOR_GUARDFRACTION_H) */
diff --git a/src/feature/dirauth/ns_detached_signatures_st.h b/src/feature/dirauth/ns_detached_signatures_st.h
index 0f92be2f0d..61d20b7525 100644
--- a/src/feature/dirauth/ns_detached_signatures_st.h
+++ b/src/feature/dirauth/ns_detached_signatures_st.h
@@ -18,5 +18,5 @@ struct ns_detached_signatures_t {
* document_signature_t */
};
-#endif
+#endif /* !defined(NS_DETACHED_SIGNATURES_ST_H) */
diff --git a/src/feature/dirauth/process_descs.h b/src/feature/dirauth/process_descs.h
index 510e54f813..001c866eba 100644
--- a/src/feature/dirauth/process_descs.h
+++ b/src/feature/dirauth/process_descs.h
@@ -36,4 +36,4 @@ void dirserv_set_node_flags_from_authoritative_status(node_t *node,
int dirserv_would_reject_router(const routerstatus_t *rs);
-#endif
+#endif /* !defined(TOR_RECV_UPLOADS_H) */
diff --git a/src/feature/dirauth/reachability.h b/src/feature/dirauth/reachability.h
index 5a938673ff..873a3f9a23 100644
--- a/src/feature/dirauth/reachability.h
+++ b/src/feature/dirauth/reachability.h
@@ -33,4 +33,4 @@ int dirserv_should_launch_reachability_test(const routerinfo_t *ri,
void dirserv_single_reachability_test(time_t now, routerinfo_t *router);
void dirserv_test_reachability(time_t now);
-#endif
+#endif /* !defined(TOR_REACHABILITY_H) */
diff --git a/src/feature/dirauth/recommend_pkg.h b/src/feature/dirauth/recommend_pkg.h
index 8200d78f72..af17e945e8 100644
--- a/src/feature/dirauth/recommend_pkg.h
+++ b/src/feature/dirauth/recommend_pkg.h
@@ -12,6 +12,18 @@
#ifndef TOR_RECOMMEND_PKG_H
#define TOR_RECOMMEND_PKG_H
+#ifdef HAVE_MODULE_DIRAUTH
int validate_recommended_package_line(const char *line);
-#endif
+#else
+
+static inline int
+validate_recommended_package_line(const char *line)
+{
+ (void) line;
+ return 0;
+}
+
+#endif /* defined(HAVE_MODULE_DIRAUTH) */
+
+#endif /* !defined(TOR_RECOMMEND_PKG_H) */
diff --git a/src/feature/dirauth/shared_random.c b/src/feature/dirauth/shared_random.c
index 137c49800f..5ccf1a95e5 100644
--- a/src/feature/dirauth/shared_random.c
+++ b/src/feature/dirauth/shared_random.c
@@ -224,7 +224,7 @@ verify_commit_and_reveal(const sr_commit_t *commit)
STATIC int
commit_has_reveal_value(const sr_commit_t *commit)
{
- return !tor_mem_is_zero(commit->encoded_reveal,
+ return !fast_mem_is_zero(commit->encoded_reveal,
sizeof(commit->encoded_reveal));
}
@@ -486,7 +486,7 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
{
/* 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,
+ if (fast_mem_is_zero(commit->encoded_reveal,
sizeof(commit->encoded_reveal))) {
reveal_str = "";
}
diff --git a/src/feature/dirauth/shared_random.h b/src/feature/dirauth/shared_random.h
index 0b45ad1ed7..1d8fa89b0f 100644
--- a/src/feature/dirauth/shared_random.h
+++ b/src/feature/dirauth/shared_random.h
@@ -110,7 +110,7 @@ int sr_init(int save_to_disk);
void sr_save_and_cleanup(void);
void sr_act_post_consensus(const networkstatus_t *consensus);
-#else /* HAVE_MODULE_DIRAUTH */
+#else /* !(defined(HAVE_MODULE_DIRAUTH)) */
static inline int
sr_init(int save_to_disk)
@@ -131,7 +131,7 @@ sr_act_post_consensus(const networkstatus_t *consensus)
(void) consensus;
}
-#endif /* HAVE_MODULE_DIRAUTH */
+#endif /* defined(HAVE_MODULE_DIRAUTH) */
/* Public methods used only by dirauth code. */
diff --git a/src/feature/dirauth/shared_random_state.c b/src/feature/dirauth/shared_random_state.c
index a7b7480edd..b669e3836e 100644
--- a/src/feature/dirauth/shared_random_state.c
+++ b/src/feature/dirauth/shared_random_state.c
@@ -538,7 +538,7 @@ disk_state_put_commit_line(const sr_commit_t *commit, config_line_t *line)
tor_assert(commit);
tor_assert(line);
- if (!tor_mem_is_zero(commit->encoded_reveal,
+ if (!fast_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);
@@ -937,7 +937,7 @@ state_query_del_all_(sr_state_object_t obj_type)
} DIGESTMAP_FOREACH_END;
break;
}
- /* The following object are _NOT_ suppose to be removed. */
+ /* The following objects are _NOT_ supposed to be removed. */
case SR_STATE_OBJ_CURSRV:
case SR_STATE_OBJ_PREVSRV:
case SR_STATE_OBJ_PHASE:
diff --git a/src/feature/dirauth/vote_microdesc_hash_st.h b/src/feature/dirauth/vote_microdesc_hash_st.h
index 92acdf1157..7869f92b4f 100644
--- a/src/feature/dirauth/vote_microdesc_hash_st.h
+++ b/src/feature/dirauth/vote_microdesc_hash_st.h
@@ -18,5 +18,5 @@ struct vote_microdesc_hash_t {
char *microdesc_hash_line;
};
-#endif
+#endif /* !defined(VOTE_MICRODESC_HASH_ST_H) */
diff --git a/src/feature/dirauth/voteflags.c b/src/feature/dirauth/voteflags.c
index 4f7593a3e1..f552af98c4 100644
--- a/src/feature/dirauth/voteflags.c
+++ b/src/feature/dirauth/voteflags.c
@@ -29,6 +29,7 @@
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/routerlist_st.h"
#include "feature/nodelist/vote_routerstatus_st.h"
#include "lib/container/order.h"
@@ -238,7 +239,7 @@ dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil)
uint32_t *uptimes, *bandwidths_kb, *bandwidths_excluding_exits_kb;
long *tks;
double *mtbfs, *wfus;
- smartlist_t *nodelist;
+ const smartlist_t *nodelist;
time_t now = time(NULL);
const or_options_t *options = get_options();
@@ -531,38 +532,45 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
node->is_running = answer;
}
-/** Extract status information from <b>ri</b> and from other authority
- * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is
- * set.
- *
- * We assume that ri-\>is_running has already been set, e.g. by
- * dirserv_set_router_is_running(ri, now);
+/* Check <b>node</b> and <b>ri</b> on whether or not we should publish a
+ * relay's IPv6 addresses. */
+static int
+should_publish_node_ipv6(const node_t *node, const routerinfo_t *ri,
+ time_t now)
+{
+ const or_options_t *options = get_options();
+
+ return options->AuthDirHasIPv6Connectivity == 1 &&
+ !tor_addr_is_null(&ri->ipv6_addr) &&
+ ((node->last_reachable6 >= now - REACHABLE_TIMEOUT) ||
+ router_is_me(ri));
+}
+
+/**
+ * Extract status information from <b>ri</b> and from other authority
+ * functions and store it in <b>rs</b>, as per
+ * <b>set_routerstatus_from_routerinfo</b>. Additionally, sets information
+ * in from the authority subsystem.
*/
void
-set_routerstatus_from_routerinfo(routerstatus_t *rs,
- node_t *node,
- const routerinfo_t *ri,
- time_t now,
- int listbadexits)
+dirauth_set_routerstatus_from_routerinfo(routerstatus_t *rs,
+ node_t *node,
+ const routerinfo_t *ri,
+ time_t now,
+ int listbadexits)
{
const or_options_t *options = get_options();
uint32_t routerbw_kb = dirserv_get_credible_bandwidth_kb(ri);
- memset(rs, 0, sizeof(routerstatus_t));
-
- rs->is_authority =
- router_digest_is_trusted_dir(ri->cache_info.identity_digest);
-
- /* Already set by compute_performance_thresholds. */
- rs->is_exit = node->is_exit;
- rs->is_stable = node->is_stable =
- !dirserv_thinks_router_is_unreliable(now, ri, 1, 0);
- rs->is_fast = node->is_fast =
- !dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
- rs->is_flagged_running = node->is_running; /* computed above */
+ /* Set these flags so that set_routerstatus_from_routerinfo can copy them.
+ */
+ node->is_stable = !dirserv_thinks_router_is_unreliable(now, ri, 1, 0);
+ node->is_fast = !dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
+ node->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, node, now);
- rs->is_valid = node->is_valid;
+ set_routerstatus_from_routerinfo(rs, node, ri);
+ /* Override rs->is_possible_guard. */
if (node->is_fast && node->is_stable &&
ri->supports_tunnelled_dir_requests &&
((options->AuthDirGuardBWGuarantee &&
@@ -578,33 +586,16 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
rs->is_possible_guard = 0;
}
+ /* Override rs->is_bad_exit */
rs->is_bad_exit = listbadexits && node->is_bad_exit;
- rs->is_hs_dir = node->is_hs_dir =
- dirserv_thinks_router_is_hs_dir(ri, node, now);
-
- rs->is_named = rs->is_unnamed = 0;
-
- rs->published_on = ri->cache_info.published_on;
- memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
- memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
- DIGEST_LEN);
- rs->addr = ri->addr;
- strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname));
- rs->or_port = ri->or_port;
- rs->dir_port = ri->dir_port;
- rs->is_v2_dir = ri->supports_tunnelled_dir_requests;
+ /* Set rs->is_staledesc. */
rs->is_staledesc =
(ri->cache_info.published_on + DESC_IS_STALE_INTERVAL) < now;
- if (options->AuthDirHasIPv6Connectivity == 1 &&
- !tor_addr_is_null(&ri->ipv6_addr) &&
- node->last_reachable6 >= now - REACHABLE_TIMEOUT) {
- /* We're configured as having IPv6 connectivity. There's an IPv6
- OR port and it's reachable so copy it to the routerstatus. */
- tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr);
- rs->ipv6_orport = ri->ipv6_orport;
- } else {
+ if (! should_publish_node_ipv6(node, ri, now)) {
+ /* We're not configured as having IPv6 connectivity or the node isn't:
+ * zero its IPv6 information. */
tor_addr_make_null(&rs->ipv6_addr, AF_INET6);
rs->ipv6_orport = 0;
}
@@ -646,3 +637,20 @@ dirserv_set_routerstatus_testing(routerstatus_t *rs)
rs->is_hs_dir = 0;
}
}
+
+/** Use dirserv_set_router_is_running() to set bridges as running if they're
+ * reachable.
+ *
+ * This function is called from set_bridge_running_callback() when running as
+ * a bridge authority.
+ */
+void
+dirserv_set_bridges_running(time_t now)
+{
+ routerlist_t *rl = router_get_routerlist();
+
+ SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) {
+ if (ri->purpose == ROUTER_PURPOSE_BRIDGE)
+ dirserv_set_router_is_running(ri, now);
+ } SMARTLIST_FOREACH_END(ri);
+}
diff --git a/src/feature/dirauth/voteflags.h b/src/feature/dirauth/voteflags.h
index cca6f53746..c4f36e7817 100644
--- a/src/feature/dirauth/voteflags.h
+++ b/src/feature/dirauth/voteflags.h
@@ -12,24 +12,28 @@
#ifndef TOR_VOTEFLAGS_H
#define TOR_VOTEFLAGS_H
+#ifdef HAVE_MODULE_DIRAUTH
void dirserv_set_router_is_running(routerinfo_t *router, time_t now);
char *dirserv_get_flag_thresholds_line(void);
void dirserv_compute_bridge_flag_thresholds(void);
int running_long_enough_to_decide_unreachable(void);
-void set_routerstatus_from_routerinfo(routerstatus_t *rs,
- node_t *node,
- const routerinfo_t *ri,
- time_t now,
- int listbadexits);
+void dirauth_set_routerstatus_from_routerinfo(routerstatus_t *rs,
+ node_t *node,
+ const routerinfo_t *ri,
+ time_t now,
+ int listbadexits);
void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil);
+#endif /* defined(HAVE_MODULE_DIRAUTH) */
+
+void dirserv_set_bridges_running(time_t now);
#ifdef VOTEFLAGS_PRIVATE
/** Any descriptor older than this age causes the authorities to set the
* StaleDesc flag. */
#define DESC_IS_STALE_INTERVAL (18*60*60)
STATIC void dirserv_set_routerstatus_testing(routerstatus_t *rs);
-#endif
+#endif /* defined(VOTEFLAGS_PRIVATE) */
-#endif
+#endif /* !defined(TOR_VOTEFLAGS_H) */
diff --git a/src/feature/dircache/cached_dir_st.h b/src/feature/dircache/cached_dir_st.h
index 71dca8c3a2..a28802f905 100644
--- a/src/feature/dircache/cached_dir_st.h
+++ b/src/feature/dircache/cached_dir_st.h
@@ -21,5 +21,5 @@ struct cached_dir_t {
int refcnt; /**< Reference count for this cached_dir_t. */
};
-#endif
+#endif /* !defined(CACHED_DIR_ST_H) */
diff --git a/src/feature/dircache/consdiffmgr.c b/src/feature/dircache/consdiffmgr.c
index 6b16307e3c..397efa0341 100644
--- a/src/feature/dircache/consdiffmgr.c
+++ b/src/feature/dircache/consdiffmgr.c
@@ -525,7 +525,7 @@ consdiffmgr_add_consensus_nulterm(const char *consensus,
tor_free(ctmp);
return r;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/**
* Given a buffer containing a networkstatus consensus, and the results of
diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c
index eece1e6503..1b36f716f4 100644
--- a/src/feature/dircache/dircache.c
+++ b/src/feature/dircache/dircache.c
@@ -1072,13 +1072,11 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
if (compress_method != NO_METHOD) {
conn->compress_state = tor_compress_new(1, compress_method,
choose_compression_level(estimated_len));
- SMARTLIST_FOREACH(items, const char *, c,
- connection_buf_add_compress(c, strlen(c), conn, 0));
- connection_buf_add_compress("", 0, conn, 1);
- } else {
- SMARTLIST_FOREACH(items, const char *, c,
- connection_buf_add(c, strlen(c), TO_CONN(conn)));
}
+
+ SMARTLIST_FOREACH(items, const char *, c,
+ connection_dir_buf_add(c, strlen(c), conn,
+ c_sl_idx == c_sl_len - 1));
} else {
SMARTLIST_FOREACH(dir_items, cached_dir_t *, d,
connection_buf_add(compress_method != NO_METHOD ?
@@ -1329,19 +1327,13 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
if (compress_method != NO_METHOD) {
conn->compress_state = tor_compress_new(1, compress_method,
choose_compression_level(len));
- SMARTLIST_FOREACH(certs, authority_cert_t *, c,
- connection_buf_add_compress(
- c->cache_info.signed_descriptor_body,
- c->cache_info.signed_descriptor_len,
- conn, 0));
- connection_buf_add_compress("", 0, conn, 1);
- } else {
- SMARTLIST_FOREACH(certs, authority_cert_t *, c,
- connection_buf_add(c->cache_info.signed_descriptor_body,
- c->cache_info.signed_descriptor_len,
- TO_CONN(conn)));
}
- keys_done:
+
+ SMARTLIST_FOREACH(certs, authority_cert_t *, c,
+ connection_dir_buf_add(c->cache_info.signed_descriptor_body,
+ c->cache_info.signed_descriptor_len,
+ conn, c_sl_idx == c_sl_len - 1));
+ keys_done:
smartlist_free(certs);
goto done;
}
diff --git a/src/feature/dircache/dircache.h b/src/feature/dircache/dircache.h
index 236ea649ef..de0d205f6a 100644
--- a/src/feature/dircache/dircache.h
+++ b/src/feature/dircache/dircache.h
@@ -38,6 +38,6 @@ STATIC int parse_hs_version_from_post(const char *url, const char *prefix,
const char **end_pos);
STATIC unsigned parse_accept_encoding_header(const char *h);
-#endif
+#endif /* defined(DIRCACHE_PRIVATE) */
#endif /* !defined(TOR_DIRCACHE_H) */
diff --git a/src/feature/dircache/dirserv.c b/src/feature/dircache/dirserv.c
index 4be6836fe1..79400bf15f 100644
--- a/src/feature/dircache/dirserv.c
+++ b/src/feature/dircache/dirserv.c
@@ -583,11 +583,9 @@ spooled_resource_flush_some(spooled_resource_t *spooled,
/* Absent objects count as "done". */
return SRFS_DONE;
}
- if (conn->compress_state) {
- connection_buf_add_compress((const char*)body, bodylen, conn, 0);
- } else {
- connection_buf_add((const char*)body, bodylen, TO_CONN(conn));
- }
+
+ connection_dir_buf_add((const char*)body, bodylen, conn, 0);
+
return SRFS_DONE;
} else {
cached_dir_t *cached = spooled->cached_dir_ref;
@@ -622,14 +620,10 @@ spooled_resource_flush_some(spooled_resource_t *spooled,
if (BUG(remaining < 0))
return SRFS_ERR;
ssize_t bytes = (ssize_t) MIN(DIRSERV_CACHED_DIR_CHUNK_SIZE, remaining);
- if (conn->compress_state) {
- connection_buf_add_compress(
- ptr + spooled->cached_dir_offset,
- bytes, conn, 0);
- } else {
- connection_buf_add(ptr + spooled->cached_dir_offset,
- bytes, TO_CONN(conn));
- }
+
+ connection_dir_buf_add(ptr + spooled->cached_dir_offset,
+ bytes, conn, 0);
+
spooled->cached_dir_offset += bytes;
if (spooled->cached_dir_offset >= (off_t)total_len) {
return SRFS_DONE;
diff --git a/src/feature/dirclient/dir_server_st.h b/src/feature/dirclient/dir_server_st.h
index 2f5706cdd9..8e35532435 100644
--- a/src/feature/dirclient/dir_server_st.h
+++ b/src/feature/dirclient/dir_server_st.h
@@ -51,4 +51,4 @@ struct dir_server_t {
**/
};
-#endif
+#endif /* !defined(DIR_SERVER_ST_H) */
diff --git a/src/feature/dirclient/dirclient.c b/src/feature/dirclient/dirclient.c
index 70b6a20028..1ea50fd350 100644
--- a/src/feature/dirclient/dirclient.c
+++ b/src/feature/dirclient/dirclient.c
@@ -14,7 +14,7 @@
#include "core/or/policies.h"
#include "feature/client/bridges.h"
#include "feature/client/entrynodes.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/authmode.h"
#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/shared_random.h"
@@ -2531,7 +2531,7 @@ handle_response_fetch_microdesc(dir_connection_t *conn,
conn->base_.port);
tor_assert(conn->requested_resource &&
!strcmpstart(conn->requested_resource, "d/"));
- tor_assert_nonfatal(!tor_mem_is_zero(conn->identity_digest, DIGEST_LEN));
+ tor_assert_nonfatal(!fast_mem_is_zero(conn->identity_digest, DIGEST_LEN));
which = smartlist_new();
dir_split_resource_into_fingerprints(conn->requested_resource+2,
which, NULL,
diff --git a/src/feature/dirclient/dirclient.h b/src/feature/dirclient/dirclient.h
index 1a93265dc3..be4374c7cf 100644
--- a/src/feature/dirclient/dirclient.h
+++ b/src/feature/dirclient/dirclient.h
@@ -167,6 +167,6 @@ STATIC int handle_response_fetch_consensus(dir_connection_t *conn,
STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose,
const char *resource);
-#endif
+#endif /* defined(DIRCLIENT_PRIVATE) */
#endif /* !defined(TOR_DIRCLIENT_H) */
diff --git a/src/feature/dirclient/dlstatus.h b/src/feature/dirclient/dlstatus.h
index 99e0d0225b..681712b059 100644
--- a/src/feature/dirclient/dlstatus.h
+++ b/src/feature/dirclient/dlstatus.h
@@ -53,6 +53,6 @@ STATIC void next_random_exponential_delay_range(int *low_bound_out,
/* no more than triple the previous delay */
#define DIR_TEST_NET_RANDOM_MULTIPLIER (2)
-#endif
+#endif /* defined(DLSTATUS_PRIVATE) */
#endif /* !defined(TOR_DLSTATUS_H) */
diff --git a/src/feature/dirclient/download_status_st.h b/src/feature/dirclient/download_status_st.h
index 11555a1dcc..39a5ad2860 100644
--- a/src/feature/dirclient/download_status_st.h
+++ b/src/feature/dirclient/download_status_st.h
@@ -61,5 +61,5 @@ struct download_status_t {
* only updated if backoff == 1 */
};
-#endif
+#endif /* !defined(DOWNLOAD_STATUS_ST_H) */
diff --git a/src/feature/dircommon/dir_connection_st.h b/src/feature/dircommon/dir_connection_st.h
index 8c59cc7a46..a858560c29 100644
--- a/src/feature/dircommon/dir_connection_st.h
+++ b/src/feature/dircommon/dir_connection_st.h
@@ -64,4 +64,4 @@ struct dir_connection_t {
#endif /* defined(MEASUREMENTS_21206) */
};
-#endif
+#endif /* !defined(DIR_CONNECTION_ST_H) */
diff --git a/src/feature/dircommon/vote_timing_st.h b/src/feature/dircommon/vote_timing_st.h
index 47b90ab009..814a325314 100644
--- a/src/feature/dircommon/vote_timing_st.h
+++ b/src/feature/dircommon/vote_timing_st.h
@@ -20,5 +20,5 @@ struct vote_timing_t {
int dist_delay;
};
-#endif
+#endif /* !defined(VOTE_TIMING_ST_H) */
diff --git a/src/feature/dircommon/voting_schedule.c b/src/feature/dircommon/voting_schedule.c
index 0a7476eda7..5576ec69f7 100644
--- a/src/feature/dircommon/voting_schedule.c
+++ b/src/feature/dircommon/voting_schedule.c
@@ -150,7 +150,7 @@ voting_schedule_get_next_valid_after_time(void)
/* This is a safe guard in order to make sure that the voting schedule
* static object is at least initialized. Using this function with a zeroed
* voting schedule can lead to bugs. */
- if (tor_mem_is_zero((const char *) &voting_schedule,
+ if (fast_mem_is_zero((const char *) &voting_schedule,
sizeof(voting_schedule))) {
need_to_recalculate_voting_schedule = true;
goto done; /* no need for next check if we have to recalculate anyway */
diff --git a/src/feature/dircommon/voting_schedule.h b/src/feature/dircommon/voting_schedule.h
index bafd81184e..d78c7ee2da 100644
--- a/src/feature/dircommon/voting_schedule.h
+++ b/src/feature/dircommon/voting_schedule.h
@@ -61,5 +61,5 @@ time_t voting_schedule_get_start_of_next_interval(time_t now,
int offset);
time_t voting_schedule_get_next_valid_after_time(void);
-#endif /* TOR_VOTING_SCHEDULE_H */
+#endif /* !defined(TOR_VOTING_SCHEDULE_H) */
diff --git a/src/feature/dirparse/microdesc_parse.h b/src/feature/dirparse/microdesc_parse.h
index 23a90084b1..95af85544a 100644
--- a/src/feature/dirparse/microdesc_parse.h
+++ b/src/feature/dirparse/microdesc_parse.h
@@ -17,4 +17,4 @@ smartlist_t *microdescs_parse_from_string(const char *s, const char *eos,
saved_location_t where,
smartlist_t *invalid_digests_out);
-#endif
+#endif /* !defined(TOR_MICRODESC_PARSE_H) */
diff --git a/src/feature/dirparse/ns_parse.c b/src/feature/dirparse/ns_parse.c
index d653a59826..d5405e6464 100644
--- a/src/feature/dirparse/ns_parse.c
+++ b/src/feature/dirparse/ns_parse.c
@@ -1478,7 +1478,7 @@ networkstatus_parse_vote_from_string(const char *s,
SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, vote_routerstatus_t *,
vrs) {
if (! vrs->has_ed25519_listing ||
- tor_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN))
+ fast_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN))
continue;
if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) {
log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not "
diff --git a/src/feature/dirparse/ns_parse.h b/src/feature/dirparse/ns_parse.h
index dedfa6fc88..0cf2cc88d0 100644
--- a/src/feature/dirparse/ns_parse.h
+++ b/src/feature/dirparse/ns_parse.h
@@ -42,6 +42,6 @@ STATIC routerstatus_t *routerstatus_parse_entry_from_string(
vote_routerstatus_t *vote_rs,
int consensus_method,
consensus_flavor_t flav);
-#endif
+#endif /* defined(NS_PARSE_PRIVATE) */
-#endif
+#endif /* !defined(TOR_NS_PARSE_H) */
diff --git a/src/feature/dirparse/sigcommon.h b/src/feature/dirparse/sigcommon.h
index fdd8e839a9..b6b34e8f62 100644
--- a/src/feature/dirparse/sigcommon.h
+++ b/src/feature/dirparse/sigcommon.h
@@ -43,6 +43,6 @@ MOCK_DECL(STATIC int, signed_digest_equals,
MOCK_DECL(STATIC int, router_compute_hash_final,(char *digest,
const char *start, size_t len,
digest_algorithm_t alg));
-#endif
+#endif /* defined(SIGCOMMON_PRIVATE) */
#endif /* !defined(TOR_SIGCOMMON_H) */
diff --git a/src/feature/dirparse/signing.h b/src/feature/dirparse/signing.h
index 2e3699baf8..8b119b4eb2 100644
--- a/src/feature/dirparse/signing.h
+++ b/src/feature/dirparse/signing.h
@@ -20,4 +20,4 @@ int router_append_dirobj_signature(char *buf, size_t buf_len,
const char *digest,
size_t digest_len,
crypto_pk_t *private_key);
-#endif
+#endif /* !defined(TOR_SIGNING_H) */
diff --git a/src/feature/dirparse/unparseable.h b/src/feature/dirparse/unparseable.h
index 853fe8cb0f..49e047961f 100644
--- a/src/feature/dirparse/unparseable.h
+++ b/src/feature/dirparse/unparseable.h
@@ -51,6 +51,6 @@ EXTERN(struct smartlist_t *, descs_dumped)
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);
-#endif
+#endif /* defined(UNPARSEABLE_PRIVATE) */
#endif /* !defined(TOR_UNPARSEABLE_H) */
diff --git a/src/feature/hibernate/hibernate.c b/src/feature/hibernate/hibernate.c
index 70c2b4f69f..674fe3c813 100644
--- a/src/feature/hibernate/hibernate.c
+++ b/src/feature/hibernate/hibernate.c
@@ -35,7 +35,7 @@ hibernating, phase 2:
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
#include "core/or/connection_or.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/defs/time.h"
#include "feature/hibernate/hibernate.h"
@@ -57,7 +57,7 @@ hibernating, phase 2:
* Coverity. Here's a kludge to unconfuse it.
*/
# define __INCLUDE_LEVEL__ 2
-# endif /* defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) */
+#endif /* defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) */
#include <systemd/sd-daemon.h>
#endif /* defined(HAVE_SYSTEMD) */
@@ -893,7 +893,7 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
*/
sd_notifyf(0, "EXTEND_TIMEOUT_USEC=%" PRIu64,
((uint64_t)(options->ShutdownWaitLength) + 30) * TOR_USEC_PER_SEC);
-#endif
+#endif /* defined(HAVE_SYSTEMD) */
} else { /* soft limit reached */
hibernate_end_time = interval_end_time;
}
diff --git a/src/feature/hibernate/hibernate.h b/src/feature/hibernate/hibernate.h
index 3309ef0ce3..2e245f6ab1 100644
--- a/src/feature/hibernate/hibernate.h
+++ b/src/feature/hibernate/hibernate.h
@@ -32,6 +32,7 @@ int getinfo_helper_accounting(control_connection_t *conn,
const char **errmsg);
uint64_t get_accounting_max_total(void);
void accounting_free_all(void);
+bool accounting_tor_is_dormant(void);
#ifdef HIBERNATE_PRIVATE
/** Possible values of hibernate_state */
diff --git a/src/feature/hs/hs_cell.c b/src/feature/hs/hs_cell.c
index 613ffe7260..69f1ccbef4 100644
--- a/src/feature/hs/hs_cell.c
+++ b/src/feature/hs/hs_cell.c
@@ -758,7 +758,14 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
idx < trn_cell_introduce_encrypted_get_nspec(enc_cell); idx++) {
link_specifier_t *lspec =
trn_cell_introduce_encrypted_get_nspecs(enc_cell, idx);
- smartlist_add(data->link_specifiers, hs_link_specifier_dup(lspec));
+ if (BUG(!lspec)) {
+ goto done;
+ }
+ link_specifier_t *lspec_dup = link_specifier_dup(lspec);
+ if (BUG(!lspec_dup)) {
+ goto done;
+ }
+ smartlist_add(data->link_specifiers, lspec_dup);
}
/* Success. */
diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c
index e3873d2f18..716c4b1f17 100644
--- a/src/feature/hs/hs_circuit.c
+++ b/src/feature/hs/hs_circuit.c
@@ -15,6 +15,7 @@
#include "core/or/circuituse.h"
#include "core/or/policies.h"
#include "core/or/relay.h"
+#include "core/or/crypt_path.h"
#include "feature/client/circpathbias.h"
#include "feature/hs/hs_cell.h"
#include "feature/hs/hs_circuit.h"
@@ -89,7 +90,7 @@ create_rend_cpath(const uint8_t *ntor_key_seed, size_t seed_len,
cpath = tor_malloc_zero(sizeof(crypt_path_t));
cpath->magic = CRYPT_PATH_MAGIC;
- if (circuit_init_cpath_crypto(cpath, (char*)keys, sizeof(keys),
+ if (cpath_init_circuit_crypto(cpath, (char*)keys, sizeof(keys),
is_service_side, 1) < 0) {
tor_free(cpath);
goto err;
@@ -126,7 +127,7 @@ create_rend_cpath_legacy(origin_circuit_t *circ, const uint8_t *rend_cell_body)
goto err;
}
/* ... and set up cpath. */
- if (circuit_init_cpath_crypto(hop,
+ if (cpath_init_circuit_crypto(hop,
keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN,
0, 0) < 0)
goto err;
@@ -177,7 +178,7 @@ finalize_rend_circuit(origin_circuit_t *circ, crypt_path_t *hop,
circ->hs_circ_has_timed_out = 0;
/* Append the hop to the cpath of this circuit */
- onion_append_to_cpath(&circ->cpath, hop);
+ cpath_extend_linked_list(&circ->cpath, hop);
/* In legacy code, 'pending_final_cpath' points to the final hop we just
* appended to the cpath. We set the original pointer to NULL so that we
@@ -405,8 +406,12 @@ launch_rendezvous_point_circuit(const hs_service_t *service,
if (circ_needs_uptime) {
circ_flags |= CIRCLAUNCH_NEED_UPTIME;
}
- /* Firewall and policies are checked when getting the extend info. */
- if (service->config.is_single_onion) {
+ /* Firewall and policies are checked when getting the extend info.
+ *
+ * We only use a one-hop path on the first attempt. If the first attempt
+ * fails, we use a 3-hop path for reachability / reliability.
+ * See the comment in retry_service_rendezvous_point() for details. */
+ if (service->config.is_single_onion && i == 0) {
circ_flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
}
@@ -565,81 +570,6 @@ retry_service_rendezvous_point(const origin_circuit_t *circ)
return;
}
-/* Add all possible link specifiers in node to lspecs:
- * - legacy ID is mandatory thus MUST be present in node;
- * - include ed25519 link specifier if present in the node, and the node
- * supports ed25519 link authentication, even if its link versions are not
- * compatible with us;
- * - include IPv4 link specifier, if the primary address is not IPv4, log a
- * BUG() warning, and return an empty smartlist;
- * - include IPv6 link specifier if present in the node. */
-static void
-get_lspecs_from_node(const node_t *node, smartlist_t *lspecs)
-{
- link_specifier_t *ls;
- tor_addr_port_t ap;
-
- tor_assert(node);
- tor_assert(lspecs);
-
- /* Get the relay's IPv4 address. */
- node_get_prim_orport(node, &ap);
-
- /* We expect the node's primary address to be a valid IPv4 address.
- * This conforms to the protocol, which requires either an IPv4 or IPv6
- * address (or both). */
- if (BUG(!tor_addr_is_v4(&ap.addr)) ||
- BUG(!tor_addr_port_is_valid_ap(&ap, 0))) {
- return;
- }
-
- ls = link_specifier_new();
- link_specifier_set_ls_type(ls, LS_IPV4);
- link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ap.addr));
- link_specifier_set_un_ipv4_port(ls, ap.port);
- /* Four bytes IPv4 and two bytes port. */
- link_specifier_set_ls_len(ls, sizeof(ap.addr.addr.in_addr) +
- sizeof(ap.port));
- smartlist_add(lspecs, ls);
-
- /* Legacy ID is mandatory and will always be present in node. */
- ls = link_specifier_new();
- link_specifier_set_ls_type(ls, LS_LEGACY_ID);
- memcpy(link_specifier_getarray_un_legacy_id(ls), node->identity,
- link_specifier_getlen_un_legacy_id(ls));
- link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls));
- smartlist_add(lspecs, ls);
-
- /* ed25519 ID is only included if the node has it, and the node declares a
- protocol version that supports ed25519 link authentication, even if that
- link version is not compatible with us. (We are sending the ed25519 key
- to another tor, which may support different link versions.) */
- if (!ed25519_public_key_is_zero(&node->ed25519_id) &&
- node_supports_ed25519_link_authentication(node, 0)) {
- ls = link_specifier_new();
- link_specifier_set_ls_type(ls, LS_ED25519_ID);
- memcpy(link_specifier_getarray_un_ed25519_id(ls), &node->ed25519_id,
- link_specifier_getlen_un_ed25519_id(ls));
- link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls));
- smartlist_add(lspecs, ls);
- }
-
- /* Check for IPv6. If so, include it as well. */
- if (node_has_ipv6_orport(node)) {
- ls = link_specifier_new();
- node_get_pref_ipv6_orport(node, &ap);
- link_specifier_set_ls_type(ls, LS_IPV6);
- size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls);
- const uint8_t *in6_addr = tor_addr_to_in6_addr8(&ap.addr);
- uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls);
- memcpy(ipv6_array, in6_addr, addr_len);
- link_specifier_set_un_ipv6_port(ls, ap.port);
- /* Sixteen bytes IPv6 and two bytes port. */
- link_specifier_set_ls_len(ls, addr_len + sizeof(ap.port));
- smartlist_add(lspecs, ls);
- }
-}
-
/* Using the given descriptor intro point ip, the node of the
* rendezvous point rp_node and the service's subcredential, populate the
* already allocated intro1_data object with the needed key material and link
@@ -662,10 +592,9 @@ setup_introduce1_data(const hs_desc_intro_point_t *ip,
tor_assert(subcredential);
tor_assert(intro1_data);
- /* Build the link specifiers from the extend information of the rendezvous
- * circuit that we've picked previously. */
- rp_lspecs = smartlist_new();
- get_lspecs_from_node(rp_node, rp_lspecs);
+ /* Build the link specifiers from the node at the end of the rendezvous
+ * circuit that we opened for this introduction. */
+ rp_lspecs = node_get_link_specifier_smartlist(rp_node, 0);
if (smartlist_len(rp_lspecs) == 0) {
/* We can't rendezvous without link specifiers. */
smartlist_free(rp_lspecs);
@@ -754,13 +683,16 @@ hs_circ_retry_service_rendezvous_point(origin_circuit_t *circ)
}
/* For a given service and a service intro point, launch a circuit to the
- * extend info ei. If the service is a single onion, a one-hop circuit will be
- * requested. Return 0 if the circuit was successfully launched and tagged
+ * extend info ei. If the service is a single onion, and direct_conn is true,
+ * a one-hop circuit will be requested.
+ *
+ * Return 0 if the circuit was successfully launched and tagged
* with the correct identifier. On error, a negative value is returned. */
int
hs_circ_launch_intro_point(hs_service_t *service,
const hs_service_intro_point_t *ip,
- extend_info_t *ei)
+ extend_info_t *ei,
+ bool direct_conn)
{
/* Standard flags for introduction circuit. */
int ret = -1, circ_flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
@@ -772,7 +704,16 @@ hs_circ_launch_intro_point(hs_service_t *service,
/* Update circuit flags in case of a single onion service that requires a
* direct connection. */
- if (service->config.is_single_onion) {
+ tor_assert_nonfatal(ip->circuit_retries > 0);
+ /* Only single onion services can make direct conns */
+ if (BUG(!service->config.is_single_onion && direct_conn)) {
+ goto end;
+ }
+ /* We only use a one-hop path on the first attempt. If the first attempt
+ * fails, we use a 3-hop path for reachability / reliability.
+ * (Unlike v2, retries is incremented by the caller before it calls this
+ * function.) */
+ if (direct_conn && ip->circuit_retries == 1) {
circ_flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
}
@@ -1044,9 +985,7 @@ hs_circ_handle_introduce2(const hs_service_t *service,
ret = 0;
done:
- SMARTLIST_FOREACH(data.link_specifiers, link_specifier_t *, lspec,
- link_specifier_free(lspec));
- smartlist_free(data.link_specifiers);
+ link_specifier_smartlist_free(data.link_specifiers);
memwipe(&data, 0, sizeof(data));
return ret;
}
diff --git a/src/feature/hs/hs_circuit.h b/src/feature/hs/hs_circuit.h
index b8d8b25add..e168b301f1 100644
--- a/src/feature/hs/hs_circuit.h
+++ b/src/feature/hs/hs_circuit.h
@@ -26,7 +26,8 @@ void hs_circ_service_rp_has_opened(const hs_service_t *service,
origin_circuit_t *circ);
int hs_circ_launch_intro_point(hs_service_t *service,
const hs_service_intro_point_t *ip,
- extend_info_t *ei);
+ extend_info_t *ei,
+ bool direct_conn);
int hs_circ_launch_rendezvous_point(const hs_service_t *service,
const curve25519_public_key_t *onion_key,
const uint8_t *rendezvous_cookie);
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 2a5765aec2..f8d47f0114 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -167,9 +167,7 @@ purge_hid_serv_request(const ed25519_public_key_t *identity_pk)
* some point and we don't care about those anymore. */
hs_build_blinded_pubkey(identity_pk, NULL, 0,
hs_get_time_period_num(0), &blinded_pk);
- if (BUG(ed25519_public_to_base64(base64_blinded_pk, &blinded_pk) < 0)) {
- return;
- }
+ ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
/* Purge last hidden service request from cache for this blinded key. */
hs_purge_hid_serv_from_last_hid_serv_requests(base64_blinded_pk);
}
@@ -356,7 +354,6 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
ed25519_public_key_t blinded_pubkey;
char base64_blinded_pubkey[ED25519_BASE64_LEN + 1];
hs_ident_dir_conn_t hs_conn_dir_ident;
- int retval;
tor_assert(hsdir);
tor_assert(onion_identity_pk);
@@ -365,10 +362,7 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
hs_build_blinded_pubkey(onion_identity_pk, NULL, 0,
current_time_period, &blinded_pubkey);
/* ...and base64 it. */
- retval = ed25519_public_to_base64(base64_blinded_pubkey, &blinded_pubkey);
- if (BUG(retval < 0)) {
- return HS_CLIENT_FETCH_ERROR;
- }
+ ed25519_public_to_base64(base64_blinded_pubkey, &blinded_pubkey);
/* Copy onion pk to a dir_ident so that we attach it to the dir conn */
hs_ident_dir_conn_init(onion_identity_pk, &blinded_pubkey,
@@ -407,7 +401,6 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
STATIC routerstatus_t *
pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk)
{
- int retval;
char base64_blinded_pubkey[ED25519_BASE64_LEN + 1];
uint64_t current_time_period = hs_get_time_period_num(0);
smartlist_t *responsible_hsdirs = NULL;
@@ -420,10 +413,7 @@ pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk)
hs_build_blinded_pubkey(onion_identity_pk, NULL, 0,
current_time_period, &blinded_pubkey);
/* ...and base64 it. */
- retval = ed25519_public_to_base64(base64_blinded_pubkey, &blinded_pubkey);
- if (BUG(retval < 0)) {
- return NULL;
- }
+ ed25519_public_to_base64(base64_blinded_pubkey, &blinded_pubkey);
/* Get responsible hsdirs of service for this time period */
responsible_hsdirs = smartlist_new();
@@ -436,7 +426,7 @@ pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk)
/* Pick an HSDir from the responsible ones. The ownership of
* responsible_hsdirs is given to this function so no need to free it. */
- hsdir_rs = hs_pick_hsdir(responsible_hsdirs, base64_blinded_pubkey);
+ hsdir_rs = hs_pick_hsdir(responsible_hsdirs, base64_blinded_pubkey, NULL);
return hsdir_rs;
}
@@ -461,6 +451,24 @@ fetch_v3_desc, (const ed25519_public_key_t *onion_identity_pk))
return directory_launch_v3_desc_fetch(onion_identity_pk, hsdir_rs);
}
+/* With a given <b>onion_identity_pk</b>, fetch its descriptor. If
+ * <b>hsdirs</b> is specified, use the directory servers specified in the list.
+ * Else, use a random server. */
+void
+hs_client_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
+ const smartlist_t *hsdirs)
+{
+ tor_assert(onion_identity_pk);
+
+ if (hsdirs != NULL) {
+ SMARTLIST_FOREACH_BEGIN(hsdirs, const routerstatus_t *, hsdir) {
+ directory_launch_v3_desc_fetch(onion_identity_pk, hsdir);
+ } SMARTLIST_FOREACH_END(hsdir);
+ } else {
+ fetch_v3_desc(onion_identity_pk);
+ }
+}
+
/* Make sure that the given v3 origin circuit circ is a valid correct
* introduction circuit. This will BUG() on any problems and hard assert if
* the anonymity of the circuit is not ok. Return 0 on success else -1 where
@@ -530,13 +538,15 @@ find_desc_intro_point_by_legacy_id(const char *legacy_id,
SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
hs_desc_intro_point_t *, ip) {
SMARTLIST_FOREACH_BEGIN(ip->link_specifiers,
- const hs_desc_link_specifier_t *, lspec) {
+ const link_specifier_t *, lspec) {
/* Not all tor node have an ed25519 identity key so we still rely on the
* legacy identity digest. */
- if (lspec->type != LS_LEGACY_ID) {
+ if (link_specifier_get_ls_type(lspec) != LS_LEGACY_ID) {
continue;
}
- if (fast_memneq(legacy_id, lspec->u.legacy_id, DIGEST_LEN)) {
+ if (fast_memneq(legacy_id,
+ link_specifier_getconstarray_un_legacy_id(lspec),
+ DIGEST_LEN)) {
break;
}
/* Found it. */
@@ -689,7 +699,7 @@ setup_intro_circ_auth_key(origin_circuit_t *circ)
}
/* Reaching this point means we didn't find any intro point for this circuit
- * which is not suppose to happen. */
+ * which is not supposed to happen. */
tor_assert_nonfatal_unreached();
end:
@@ -755,24 +765,13 @@ STATIC extend_info_t *
desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip)
{
extend_info_t *ei;
- smartlist_t *lspecs = smartlist_new();
tor_assert(ip);
- /* We first encode the descriptor link specifiers into the binary
- * representation which is a trunnel object. */
- SMARTLIST_FOREACH_BEGIN(ip->link_specifiers,
- const hs_desc_link_specifier_t *, desc_lspec) {
- link_specifier_t *lspec = hs_desc_lspec_to_trunnel(desc_lspec);
- smartlist_add(lspecs, lspec);
- } SMARTLIST_FOREACH_END(desc_lspec);
-
/* Explicitly put the direct connection option to 0 because this is client
* side and there is no such thing as a non anonymous client. */
- ei = hs_get_extend_info_from_lspecs(lspecs, &ip->onion_key, 0);
+ ei = hs_get_extend_info_from_lspecs(ip->link_specifiers, &ip->onion_key, 0);
- SMARTLIST_FOREACH(lspecs, link_specifier_t *, ls, link_specifier_free(ls));
- smartlist_free(lspecs);
return ei;
}
@@ -1543,7 +1542,10 @@ parse_auth_file_content(const char *client_key_str)
auth = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
if (base32_decode((char *) auth->enc_seckey.secret_key,
sizeof(auth->enc_seckey.secret_key),
- seckey_b32, strlen(seckey_b32)) < 0) {
+ seckey_b32, strlen(seckey_b32)) !=
+ sizeof(auth->enc_seckey.secret_key)) {
+ log_warn(LD_REND, "Client authorization encoded base32 private key "
+ "can't be decoded: %s", seckey_b32);
goto err;
}
strncpy(auth->onion_address, onion_address, HS_SERVICE_ADDR_LEN_BASE32);
diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h
index dadfa024b8..96a96755fd 100644
--- a/src/feature/hs/hs_client.h
+++ b/src/feature/hs/hs_client.h
@@ -44,6 +44,10 @@ typedef struct hs_client_service_authorization_t {
void hs_client_note_connection_attempt_succeeded(
const edge_connection_t *conn);
+void hs_client_launch_v3_desc_fetch(
+ const ed25519_public_key_t *onion_identity_pk,
+ const smartlist_t *hsdirs);
+
int hs_client_decode_descriptor(
const char *desc_str,
const ed25519_public_key_t *service_identity_pk,
diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c
index ebe49f09a5..a5747fe170 100644
--- a/src/feature/hs/hs_common.c
+++ b/src/feature/hs/hs_common.c
@@ -926,7 +926,8 @@ hs_parse_address(const char *address, ed25519_public_key_t *key_out,
}
/* Decode address so we can extract needed fields. */
- if (base32_decode(decoded, sizeof(decoded), address, strlen(address)) < 0) {
+ if (base32_decode(decoded, sizeof(decoded), address, strlen(address))
+ != sizeof(decoded)) {
log_warn(LD_REND, "Service address %s can't be decoded.",
escaped_safe_str(address));
goto invalid;
@@ -940,7 +941,7 @@ hs_parse_address(const char *address, ed25519_public_key_t *key_out,
return -1;
}
-/* Validate a given onion address. The length, the base32 decoding and
+/* Validate a given onion address. The length, the base32 decoding, and
* checksum are validated. Return 1 if valid else 0. */
int
hs_address_is_valid(const char *address)
@@ -955,7 +956,7 @@ hs_address_is_valid(const char *address)
goto invalid;
}
- /* Get the checksum it's suppose to be and compare it with what we have
+ /* Get the checksum it's supposed to be and compare it with what we have
* encoded in the address. */
build_hs_checksum(&service_pubkey, version, target_checksum);
if (tor_memcmp(checksum, target_checksum, sizeof(checksum))) {
@@ -983,7 +984,7 @@ hs_address_is_valid(const char *address)
* The returned address is base32 encoded and put in addr_out. The caller MUST
* make sure the addr_out is at least HS_SERVICE_ADDR_LEN_BASE32 + 1 long.
*
- * Format is as follow:
+ * Format is as follows:
* base32(PUBKEY || CHECKSUM || VERSION)
* CHECKSUM = H(".onion checksum" || PUBKEY || VERSION)
* */
@@ -1009,24 +1010,6 @@ hs_build_address(const ed25519_public_key_t *key, uint8_t version,
tor_assert(hs_address_is_valid(addr_out));
}
-/* Return a newly allocated copy of lspec. */
-link_specifier_t *
-hs_link_specifier_dup(const link_specifier_t *lspec)
-{
- link_specifier_t *result = link_specifier_new();
- memcpy(result, lspec, sizeof(*result));
- /* The unrecognized field is a dynamic array so make sure to copy its
- * content and not the pointer. */
- link_specifier_setlen_un_unrecognized(
- result, link_specifier_getlen_un_unrecognized(lspec));
- if (link_specifier_getlen_un_unrecognized(result)) {
- memcpy(link_specifier_getarray_un_unrecognized(result),
- link_specifier_getconstarray_un_unrecognized(lspec),
- link_specifier_getlen_un_unrecognized(result));
- }
- return result;
-}
-
/* From a given ed25519 public key pk and an optional secret, compute a
* blinded public key and put it in blinded_pk_out. This is only useful to
* the client side because the client only has access to the identity public
@@ -1042,7 +1025,7 @@ hs_build_blinded_pubkey(const ed25519_public_key_t *pk,
tor_assert(pk);
tor_assert(blinded_pk_out);
- tor_assert(!tor_mem_is_zero((char *) pk, ED25519_PUBKEY_LEN));
+ tor_assert(!fast_mem_is_zero((char *) pk, ED25519_PUBKEY_LEN));
build_blinded_key_param(pk, secret, secret_len,
time_period_num, get_time_period_length(), param);
@@ -1067,8 +1050,8 @@ hs_build_blinded_keypair(const ed25519_keypair_t *kp,
tor_assert(kp);
tor_assert(blinded_kp_out);
/* Extra safety. A zeroed key is bad. */
- tor_assert(!tor_mem_is_zero((char *) &kp->pubkey, ED25519_PUBKEY_LEN));
- tor_assert(!tor_mem_is_zero((char *) &kp->seckey, ED25519_SECKEY_LEN));
+ tor_assert(!fast_mem_is_zero((char *) &kp->pubkey, ED25519_PUBKEY_LEN));
+ tor_assert(!fast_mem_is_zero((char *) &kp->seckey, ED25519_SECKEY_LEN));
build_blinded_key_param(&kp->pubkey, secret, secret_len,
time_period_num, get_time_period_length(), param);
@@ -1300,15 +1283,15 @@ node_has_hsdir_index(const node_t *node)
/* At this point, since the node has a desc, this node must also have an
* hsdir index. If not, something went wrong, so BUG out. */
- if (BUG(tor_mem_is_zero((const char*)node->hsdir_index.fetch,
+ if (BUG(fast_mem_is_zero((const char*)node->hsdir_index.fetch,
DIGEST256_LEN))) {
return 0;
}
- if (BUG(tor_mem_is_zero((const char*)node->hsdir_index.store_first,
+ if (BUG(fast_mem_is_zero((const char*)node->hsdir_index.store_first,
DIGEST256_LEN))) {
return 0;
}
- if (BUG(tor_mem_is_zero((const char*)node->hsdir_index.store_second,
+ if (BUG(fast_mem_is_zero((const char*)node->hsdir_index.store_second,
DIGEST256_LEN))) {
return 0;
}
@@ -1606,20 +1589,25 @@ hs_purge_last_hid_serv_requests(void)
/** Given the list of responsible HSDirs in <b>responsible_dirs</b>, pick the
* one that we should use to fetch a descriptor right now. Take into account
* previous failed attempts at fetching this descriptor from HSDirs using the
- * string identifier <b>req_key_str</b>.
+ * string identifier <b>req_key_str</b>. We return whether we are rate limited
+ * into *<b>is_rate_limited_out</b> if it is not NULL.
*
* Steals ownership of <b>responsible_dirs</b>.
*
* Return the routerstatus of the chosen HSDir if successful, otherwise return
* NULL if no HSDirs are worth trying right now. */
routerstatus_t *
-hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str)
+hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str,
+ bool *is_rate_limited_out)
{
smartlist_t *usable_responsible_dirs = smartlist_new();
const or_options_t *options = get_options();
routerstatus_t *hs_dir;
time_t now = time(NULL);
int excluded_some;
+ bool rate_limited = false;
+ int rate_limited_count = 0;
+ int responsible_dirs_count = smartlist_len(responsible_dirs);
tor_assert(req_key_str);
@@ -1639,6 +1627,7 @@ hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str)
if (last + hs_hsdir_requery_period(options) >= now ||
!node || !node_has_preferred_descriptor(node, 0)) {
SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
+ rate_limited_count++;
continue;
}
if (!routerset_contains_node(options->ExcludeNodes, node)) {
@@ -1646,6 +1635,10 @@ hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str)
}
} SMARTLIST_FOREACH_END(dir);
+ if (rate_limited_count > 0 || responsible_dirs_count > 0) {
+ rate_limited = rate_limited_count == responsible_dirs_count;
+ }
+
excluded_some =
smartlist_len(usable_responsible_dirs) < smartlist_len(responsible_dirs);
@@ -1657,9 +1650,10 @@ hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str)
smartlist_free(responsible_dirs);
smartlist_free(usable_responsible_dirs);
if (!hs_dir) {
+ const char *warn_str = (rate_limited) ? "we are rate limited." :
+ "we requested them all recently without success";
log_info(LD_REND, "Could not pick one of the responsible hidden "
- "service directories, because we requested them all "
- "recently without success.");
+ "service directories, because %s.", warn_str);
if (options->StrictNodes && excluded_some) {
log_warn(LD_REND, "Could not pick a hidden service directory for the "
"requested hidden service: they are all either down or "
@@ -1671,17 +1665,23 @@ hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str)
hs_lookup_last_hid_serv_request(hs_dir, req_key_str, now, 1);
}
+ if (is_rate_limited_out != NULL) {
+ *is_rate_limited_out = rate_limited;
+ }
+
return hs_dir;
}
-/* From a list of link specifier, an onion key and if we are requesting a
- * direct connection (ex: single onion service), return a newly allocated
- * extend_info_t object. This function always returns an extend info with
- * an IPv4 address, or NULL.
+/* Given a list of link specifiers lspecs, a curve 25519 onion_key, and
+ * a direct connection boolean direct_conn (true for single onion services),
+ * return a newly allocated extend_info_t object.
+ *
+ * This function always returns an extend info with a valid IP address and
+ * ORPort, or NULL. If direct_conn is false, the IP address is always IPv4.
*
* It performs the following checks:
- * if either IPv4 or legacy ID is missing, return NULL.
- * if direct_conn, and we can't reach the IPv4 address, return NULL.
+ * if there is no usable IP address, or legacy ID is missing, return NULL.
+ * if direct_conn, and we can't reach any IP address, return NULL.
*/
extend_info_t *
hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
@@ -1690,21 +1690,40 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
{
int have_v4 = 0, have_legacy_id = 0, have_ed25519_id = 0;
char legacy_id[DIGEST_LEN] = {0};
- uint16_t port_v4 = 0;
- tor_addr_t addr_v4;
ed25519_public_key_t ed25519_pk;
extend_info_t *info = NULL;
+ tor_addr_port_t ap;
- tor_assert(lspecs);
+ tor_addr_make_null(&ap.addr, AF_UNSPEC);
+ ap.port = 0;
+
+ if (lspecs == NULL) {
+ log_warn(LD_BUG, "Specified link specifiers is null");
+ goto done;
+ }
+
+ if (onion_key == NULL) {
+ log_warn(LD_BUG, "Specified onion key is null");
+ goto done;
+ }
+
+ if (smartlist_len(lspecs) == 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_REND, "Empty link specifier list.");
+ /* Return NULL. */
+ goto done;
+ }
SMARTLIST_FOREACH_BEGIN(lspecs, const link_specifier_t *, ls) {
switch (link_specifier_get_ls_type(ls)) {
case LS_IPV4:
- /* Skip if we already seen a v4. */
- if (have_v4) continue;
- tor_addr_from_ipv4h(&addr_v4,
+ /* Skip if we already seen a v4. If direct_conn is true, we skip this
+ * block because fascist_firewall_choose_address_ls() will set ap. If
+ * direct_conn is false, set ap to the first IPv4 address and port in
+ * the link specifiers.*/
+ if (have_v4 || direct_conn) continue;
+ tor_addr_from_ipv4h(&ap.addr,
link_specifier_get_un_ipv4_addr(ls));
- port_v4 = link_specifier_get_un_ipv4_port(ls);
+ ap.port = link_specifier_get_un_ipv4_port(ls);
have_v4 = 1;
break;
case LS_LEGACY_ID:
@@ -1728,45 +1747,38 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
}
} SMARTLIST_FOREACH_END(ls);
- /* Legacy ID is mandatory, and we require IPv4. */
- if (!have_v4 || !have_legacy_id) {
+ /* Choose a preferred address first, but fall back to an allowed address. */
+ if (direct_conn)
+ fascist_firewall_choose_address_ls(lspecs, 0, &ap);
+
+ /* Legacy ID is mandatory, and we require an IP address. */
+ if (!tor_addr_port_is_valid_ap(&ap, 0)) {
+ /* If we're missing the IP address, log a warning and return NULL. */
+ log_info(LD_NET, "Unreachable or invalid IP address in link state");
goto done;
}
-
- /* We know we have IPv4, because we just checked. */
- if (!direct_conn) {
- /* All clients can extend to any IPv4 via a 3-hop path. */
- goto validate;
- } else if (direct_conn &&
- fascist_firewall_allows_address_addr(&addr_v4, port_v4,
- FIREWALL_OR_CONNECTION,
- 0, 0)) {
- /* Direct connection and we can reach it in IPv4 so go for it. */
- goto validate;
-
- /* We will add support for falling back to a 3-hop path in a later
- * release. */
- } else {
- /* If we can't reach IPv4, return NULL. */
+ if (!have_legacy_id) {
+ /* If we're missing the legacy ID, log a warning and return NULL. */
+ log_warn(LD_PROTOCOL, "Missing Legacy ID in link state");
goto done;
}
- /* We will add support for IPv6 in a later release. */
+ /* We will add support for falling back to a 3-hop path in a later
+ * release. */
- validate:
/* We'll validate now that the address we've picked isn't a private one. If
- * it is, are we allowing to extend to private address? */
- if (!extend_info_addr_is_allowed(&addr_v4)) {
+ * it is, are we allowed to extend to private addresses? */
+ if (!extend_info_addr_is_allowed(&ap.addr)) {
log_fn(LOG_PROTOCOL_WARN, LD_REND,
"Requested address is private and we are not allowed to extend to "
- "it: %s:%u", fmt_addr(&addr_v4), port_v4);
+ "it: %s:%u", fmt_addr(&ap.addr), ap.port);
goto done;
}
/* We do have everything for which we think we can connect successfully. */
info = extend_info_new(NULL, legacy_id,
(have_ed25519_id) ? &ed25519_pk : NULL, NULL,
- onion_key, &addr_v4, port_v4);
+ onion_key, &ap.addr, ap.port);
done:
return info;
}
@@ -1827,3 +1839,42 @@ hs_inc_rdv_stream_counter(origin_circuit_t *circ)
tor_assert_nonfatal_unreached();
}
}
+
+/* Return a newly allocated link specifier object that is a copy of dst. */
+link_specifier_t *
+link_specifier_dup(const link_specifier_t *src)
+{
+ link_specifier_t *dup = NULL;
+ uint8_t *buf = NULL;
+
+ if (BUG(!src)) {
+ goto err;
+ }
+
+ ssize_t encoded_len_alloc = link_specifier_encoded_len(src);
+ if (BUG(encoded_len_alloc < 0)) {
+ goto err;
+ }
+
+ buf = tor_malloc_zero(encoded_len_alloc);
+ ssize_t encoded_len_data = link_specifier_encode(buf,
+ encoded_len_alloc,
+ src);
+ if (BUG(encoded_len_data < 0)) {
+ goto err;
+ }
+
+ ssize_t parsed_len = link_specifier_parse(&dup, buf, encoded_len_alloc);
+ if (BUG(parsed_len < 0)) {
+ goto err;
+ }
+
+ goto done;
+
+ err:
+ dup = NULL;
+
+ done:
+ tor_free(buf);
+ return dup;
+}
diff --git a/src/feature/hs/hs_common.h b/src/feature/hs/hs_common.h
index a44505930a..3009780d90 100644
--- a/src/feature/hs/hs_common.h
+++ b/src/feature/hs/hs_common.h
@@ -217,8 +217,6 @@ uint64_t hs_get_time_period_num(time_t now);
uint64_t hs_get_next_time_period_num(time_t now);
time_t hs_get_start_time_of_next_time_period(time_t now);
-link_specifier_t *hs_link_specifier_dup(const link_specifier_t *lspec);
-
MOCK_DECL(int, hs_in_period_between_tp_and_srv,
(const networkstatus_t *consensus, time_t now));
@@ -243,7 +241,8 @@ void hs_get_responsible_hsdirs(const struct ed25519_public_key_t *blinded_pk,
int use_second_hsdir_index,
int for_fetching, smartlist_t *responsible_dirs);
routerstatus_t *hs_pick_hsdir(smartlist_t *responsible_dirs,
- const char *req_key_str);
+ const char *req_key_str,
+ bool *is_rate_limited_out);
time_t hs_hsdir_requery_period(const or_options_t *options);
time_t hs_lookup_last_hid_serv_request(routerstatus_t *hs_dir,
@@ -262,6 +261,8 @@ extend_info_t *hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
const struct curve25519_public_key_t *onion_key,
int direct_conn);
+link_specifier_t *link_specifier_dup(const link_specifier_t *src);
+
#ifdef HS_COMMON_PRIVATE
STATIC void get_disaster_srv(uint64_t time_period_num, uint8_t *srv_out);
diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c
index ee4499ef5b..87f6257591 100644
--- a/src/feature/hs/hs_config.c
+++ b/src/feature/hs/hs_config.c
@@ -496,15 +496,6 @@ config_generic_service(const config_line_t *line_,
* becomes a single onion service. */
if (rend_service_non_anonymous_mode_enabled(options)) {
config->is_single_onion = 1;
- /* We will add support for IPv6-only v3 single onion services in a future
- * Tor version. This won't catch "ReachableAddresses reject *4", but that
- * option doesn't work anyway. */
- if (options->ClientUseIPv4 == 0 && config->version == HS_VERSION_THREE) {
- log_warn(LD_CONFIG, "IPv6-only v3 single onion services are not "
- "supported. Set HiddenServiceSingleHopMode 0 and "
- "HiddenServiceNonAnonymousMode 0, or set ClientUseIPv4 1.");
- goto err;
- }
}
/* Success */
diff --git a/src/feature/hs/hs_control.c b/src/feature/hs/hs_control.c
index 9970fdd123..abb421345c 100644
--- a/src/feature/hs/hs_control.c
+++ b/src/feature/hs/hs_control.c
@@ -7,9 +7,10 @@
**/
#include "core/or/or.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "lib/crypt_ops/crypto_format.h"
#include "lib/crypt_ops/crypto_util.h"
+#include "feature/hs/hs_client.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_control.h"
#include "feature/hs/hs_descriptor.h"
@@ -73,10 +74,7 @@ hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident,
tor_assert(reason);
/* Build onion address and encoded blinded key. */
- IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
- &ident->blinded_pk) < 0) {
- return;
- }
+ ed25519_public_to_base64(base64_blinded_pk, &ident->blinded_pk);
hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
control_event_hsv3_descriptor_failed(onion_address, base64_blinded_pk,
@@ -98,10 +96,7 @@ hs_control_desc_event_received(const hs_ident_dir_conn_t *ident,
tor_assert(hsdir_id_digest);
/* Build onion address and encoded blinded key. */
- IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
- &ident->blinded_pk) < 0) {
- return;
- }
+ ed25519_public_to_base64(base64_blinded_pk, &ident->blinded_pk);
hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
control_event_hsv3_descriptor_received(onion_address, base64_blinded_pk,
@@ -122,9 +117,7 @@ hs_control_desc_event_created(const char *onion_address,
tor_assert(blinded_pk);
/* Build base64 encoded blinded key. */
- IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) {
- return;
- }
+ ed25519_public_to_base64(base64_blinded_pk, blinded_pk);
/* Version 3 doesn't use the replica number in its descriptor ID computation
* so we pass negative value so the control port subsystem can ignore it. */
@@ -150,9 +143,7 @@ hs_control_desc_event_upload(const char *onion_address,
tor_assert(hsdir_index);
/* Build base64 encoded blinded key. */
- IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) {
- return;
- }
+ ed25519_public_to_base64(base64_blinded_pk, blinded_pk);
control_event_hs_descriptor_upload(onion_address, hsdir_id_digest,
base64_blinded_pk,
@@ -195,10 +186,7 @@ hs_control_desc_event_content(const hs_ident_dir_conn_t *ident,
tor_assert(hsdir_id_digest);
/* Build onion address and encoded blinded key. */
- IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
- &ident->blinded_pk) < 0) {
- return;
- }
+ ed25519_public_to_base64(base64_blinded_pk, &ident->blinded_pk);
hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
control_event_hs_descriptor_content(onion_address, base64_blinded_pk,
@@ -259,3 +247,16 @@ hs_control_hspost_command(const char *body, const char *onion_address,
smartlist_free(hsdirs);
return ret;
}
+
+/* With a given <b>onion_identity_pk</b>, fetch its descriptor, optionally
+ * using the list of directory servers given in <b>hsdirs</b>, or a random
+ * server if it is NULL. This function calls hs_client_launch_v3_desc_fetch().
+ */
+void
+hs_control_hsfetch_command(const ed25519_public_key_t *onion_identity_pk,
+ const smartlist_t *hsdirs)
+{
+ tor_assert(onion_identity_pk);
+
+ hs_client_launch_v3_desc_fetch(onion_identity_pk, hsdirs);
+}
diff --git a/src/feature/hs/hs_control.h b/src/feature/hs/hs_control.h
index f7ab642652..b55e4c53c9 100644
--- a/src/feature/hs/hs_control.h
+++ b/src/feature/hs/hs_control.h
@@ -48,5 +48,9 @@ void hs_control_desc_event_content(const hs_ident_dir_conn_t *ident,
int hs_control_hspost_command(const char *body, const char *onion_address,
const smartlist_t *hsdirs_rs);
+/* Command "HSFETCH [...]" */
+void hs_control_hsfetch_command(const ed25519_public_key_t *onion_identity_pk,
+ const smartlist_t *hsdirs);
+
#endif /* !defined(TOR_HS_CONTROL_H) */
diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index b09d50e010..a8796c0029 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -324,12 +324,11 @@ encode_link_specifiers(const smartlist_t *specs)
link_specifier_list_set_n_spec(lslist, smartlist_len(specs));
- SMARTLIST_FOREACH_BEGIN(specs, const hs_desc_link_specifier_t *,
+ SMARTLIST_FOREACH_BEGIN(specs, const link_specifier_t *,
spec) {
- link_specifier_t *ls = hs_desc_lspec_to_trunnel(spec);
- if (ls) {
- link_specifier_list_add_spec(lslist, ls);
- }
+ link_specifier_t *ls = link_specifier_dup(spec);
+ tor_assert(ls);
+ link_specifier_list_add_spec(lslist, ls);
} SMARTLIST_FOREACH_END(spec);
{
@@ -404,9 +403,7 @@ encode_enc_key(const hs_desc_intro_point_t *ip)
tor_assert(ip);
/* Base64 encode the encryption key for the "enc-key" field. */
- if (curve25519_public_to_base64(key_b64, &ip->enc_key) < 0) {
- goto done;
- }
+ curve25519_public_to_base64(key_b64, &ip->enc_key);
if (tor_cert_encode_ed22519(ip->enc_key_cert, &encoded_cert) < 0) {
goto done;
}
@@ -422,7 +419,7 @@ encode_enc_key(const hs_desc_intro_point_t *ip)
}
/* Encode an introduction point onion key. Return a newly allocated string
- * with it. On failure, return NULL. */
+ * with it. Can not fail. */
static char *
encode_onion_key(const hs_desc_intro_point_t *ip)
{
@@ -432,12 +429,9 @@ encode_onion_key(const hs_desc_intro_point_t *ip)
tor_assert(ip);
/* Base64 encode the encryption key for the "onion-key" field. */
- if (curve25519_public_to_base64(key_b64, &ip->onion_key) < 0) {
- goto done;
- }
+ curve25519_public_to_base64(key_b64, &ip->onion_key);
tor_asprintf(&encoded, "%s ntor %s", str_ip_onion_key, key_b64);
- done:
return encoded;
}
@@ -684,7 +678,7 @@ get_auth_client_str(const hs_desc_authorized_client_t *client)
char encrypted_cookie_b64[HS_DESC_ENCRYPED_COOKIE_LEN * 2];
#define ASSERT_AND_BASE64(field) STMT_BEGIN \
- tor_assert(!tor_mem_is_zero((char *) client->field, \
+ tor_assert(!fast_mem_is_zero((char *) client->field, \
sizeof(client->field))); \
ret = base64_encode_nopad(field##_b64, sizeof(field##_b64), \
client->field, sizeof(client->field)); \
@@ -798,8 +792,8 @@ get_inner_encrypted_layer_plaintext(const hs_descriptor_t *desc)
/* Create the middle layer of the descriptor, which includes the client auth
* data and the encrypted inner layer (provided as a base64 string at
* <b>layer2_b64_ciphertext</b>). Return a newly-allocated string with the
- * layer plaintext, or NULL if an error occurred. It's the responsibility of
- * the caller to free the returned string. */
+ * layer plaintext. It's the responsibility of the caller to free the returned
+ * string. Can not fail. */
static char *
get_outer_encrypted_layer_plaintext(const hs_descriptor_t *desc,
const char *layer2_b64_ciphertext)
@@ -815,13 +809,10 @@ get_outer_encrypted_layer_plaintext(const hs_descriptor_t *desc,
const curve25519_public_key_t *ephemeral_pubkey;
ephemeral_pubkey = &desc->superencrypted_data.auth_ephemeral_pubkey;
- tor_assert(!tor_mem_is_zero((char *) ephemeral_pubkey->public_key,
+ tor_assert(!fast_mem_is_zero((char *) ephemeral_pubkey->public_key,
CURVE25519_PUBKEY_LEN));
- if (curve25519_public_to_base64(ephemeral_key_base64,
- ephemeral_pubkey) < 0) {
- goto done;
- }
+ curve25519_public_to_base64(ephemeral_key_base64, ephemeral_pubkey);
smartlist_add_asprintf(lines, "%s %s\n",
str_desc_auth_key, ephemeral_key_base64);
@@ -846,7 +837,6 @@ get_outer_encrypted_layer_plaintext(const hs_descriptor_t *desc,
layer1_str = smartlist_join_strings(lines, "", 0, NULL);
- done:
/* We need to memwipe all lines because it contains the ephemeral key */
SMARTLIST_FOREACH(lines, char *, a, memwipe(a, 0, strlen(a)));
SMARTLIST_FOREACH(lines, char *, a, tor_free(a));
@@ -1092,11 +1082,7 @@ desc_encode_v3(const hs_descriptor_t *desc,
tor_free(encoded_str);
goto err;
}
- if (ed25519_signature_to_base64(ed_sig_b64, &sig) < 0) {
- log_warn(LD_BUG, "Can't base64 encode descriptor signature!");
- tor_free(encoded_str);
- goto err;
- }
+ ed25519_signature_to_base64(ed_sig_b64, &sig);
/* Create the signature line. */
smartlist_add_asprintf(lines, "%s %s", str_signature, ed_sig_b64);
}
@@ -1190,52 +1176,22 @@ decode_link_specifiers(const char *encoded)
results = smartlist_new();
for (i = 0; i < link_specifier_list_getlen_spec(specs); i++) {
- hs_desc_link_specifier_t *hs_spec;
link_specifier_t *ls = link_specifier_list_get_spec(specs, i);
- tor_assert(ls);
-
- hs_spec = tor_malloc_zero(sizeof(*hs_spec));
- hs_spec->type = link_specifier_get_ls_type(ls);
- switch (hs_spec->type) {
- case LS_IPV4:
- tor_addr_from_ipv4h(&hs_spec->u.ap.addr,
- link_specifier_get_un_ipv4_addr(ls));
- hs_spec->u.ap.port = link_specifier_get_un_ipv4_port(ls);
- break;
- case LS_IPV6:
- tor_addr_from_ipv6_bytes(&hs_spec->u.ap.addr, (const char *)
- link_specifier_getarray_un_ipv6_addr(ls));
- hs_spec->u.ap.port = link_specifier_get_un_ipv6_port(ls);
- break;
- case LS_LEGACY_ID:
- /* Both are known at compile time so let's make sure they are the same
- * else we can copy memory out of bound. */
- tor_assert(link_specifier_getlen_un_legacy_id(ls) ==
- sizeof(hs_spec->u.legacy_id));
- memcpy(hs_spec->u.legacy_id, link_specifier_getarray_un_legacy_id(ls),
- sizeof(hs_spec->u.legacy_id));
- break;
- case LS_ED25519_ID:
- /* Both are known at compile time so let's make sure they are the same
- * else we can copy memory out of bound. */
- tor_assert(link_specifier_getlen_un_ed25519_id(ls) ==
- sizeof(hs_spec->u.ed25519_id));
- memcpy(hs_spec->u.ed25519_id,
- link_specifier_getconstarray_un_ed25519_id(ls),
- sizeof(hs_spec->u.ed25519_id));
- break;
- default:
- tor_free(hs_spec);
+ if (BUG(!ls)) {
goto err;
}
-
- smartlist_add(results, hs_spec);
+ link_specifier_t *ls_dup = link_specifier_dup(ls);
+ if (BUG(!ls_dup)) {
+ goto err;
+ }
+ smartlist_add(results, ls_dup);
}
goto done;
err:
if (results) {
- SMARTLIST_FOREACH(results, hs_desc_link_specifier_t *, s, tor_free(s));
+ SMARTLIST_FOREACH(results, link_specifier_t *, s,
+ link_specifier_free(s));
smartlist_free(results);
results = NULL;
}
@@ -1465,12 +1421,12 @@ decrypt_descriptor_cookie(const hs_descriptor_t *desc,
tor_assert(desc);
tor_assert(client);
tor_assert(client_auth_sk);
- tor_assert(!tor_mem_is_zero(
+ tor_assert(!fast_mem_is_zero(
(char *) &desc->superencrypted_data.auth_ephemeral_pubkey,
sizeof(desc->superencrypted_data.auth_ephemeral_pubkey)));
- tor_assert(!tor_mem_is_zero((char *) client_auth_sk,
+ tor_assert(!fast_mem_is_zero((char *) client_auth_sk,
sizeof(*client_auth_sk)));
- tor_assert(!tor_mem_is_zero((char *) desc->subcredential, DIGEST256_LEN));
+ tor_assert(!fast_mem_is_zero((char *) desc->subcredential, DIGEST256_LEN));
/* Get the KEYS component to derive the CLIENT-ID and COOKIE-KEY. */
keystream_length =
@@ -2012,6 +1968,7 @@ decode_intro_points(const hs_descriptor_t *desc,
SMARTLIST_FOREACH(intro_points, char *, a, tor_free(a));
smartlist_free(intro_points);
}
+
/* Return 1 iff the given base64 encoded signature in b64_sig from the encoded
* descriptor in encoded_desc validates the descriptor content. */
STATIC int
@@ -2615,7 +2572,7 @@ hs_desc_decode_descriptor(const char *encoded,
/* Subcredentials are not optional. */
if (BUG(!subcredential ||
- tor_mem_is_zero((char*)subcredential, DIGEST256_LEN))) {
+ fast_mem_is_zero((char*)subcredential, DIGEST256_LEN))) {
log_warn(LD_GENERAL, "Tried to decrypt without subcred. Impossible!");
goto err;
}
@@ -2878,8 +2835,8 @@ hs_desc_intro_point_free_(hs_desc_intro_point_t *ip)
return;
}
if (ip->link_specifiers) {
- SMARTLIST_FOREACH(ip->link_specifiers, hs_desc_link_specifier_t *,
- ls, hs_desc_link_specifier_free(ls));
+ SMARTLIST_FOREACH(ip->link_specifiers, link_specifier_t *,
+ ls, link_specifier_free(ls));
smartlist_free(ip->link_specifiers);
}
tor_cert_free(ip->auth_key_cert);
@@ -2928,13 +2885,13 @@ hs_desc_build_authorized_client(const uint8_t *subcredential,
tor_assert(descriptor_cookie);
tor_assert(client_out);
tor_assert(subcredential);
- tor_assert(!tor_mem_is_zero((char *) auth_ephemeral_sk,
+ tor_assert(!fast_mem_is_zero((char *) auth_ephemeral_sk,
sizeof(*auth_ephemeral_sk)));
- tor_assert(!tor_mem_is_zero((char *) client_auth_pk,
+ tor_assert(!fast_mem_is_zero((char *) client_auth_pk,
sizeof(*client_auth_pk)));
- tor_assert(!tor_mem_is_zero((char *) descriptor_cookie,
+ tor_assert(!fast_mem_is_zero((char *) descriptor_cookie,
HS_DESC_DESCRIPTOR_COOKIE_LEN));
- tor_assert(!tor_mem_is_zero((char *) subcredential,
+ tor_assert(!fast_mem_is_zero((char *) subcredential,
DIGEST256_LEN));
/* Get the KEYS part so we can derive the CLIENT-ID and COOKIE-KEY. */
@@ -2972,69 +2929,6 @@ hs_desc_authorized_client_free_(hs_desc_authorized_client_t *client)
tor_free(client);
}
-/* Free the given descriptor link specifier. */
-void
-hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls)
-{
- if (ls == NULL) {
- return;
- }
- tor_free(ls);
-}
-
-/* Return a newly allocated descriptor link specifier using the given extend
- * info and requested type. Return NULL on error. */
-hs_desc_link_specifier_t *
-hs_desc_link_specifier_new(const extend_info_t *info, uint8_t type)
-{
- hs_desc_link_specifier_t *ls = NULL;
-
- tor_assert(info);
-
- ls = tor_malloc_zero(sizeof(*ls));
- ls->type = type;
- switch (ls->type) {
- case LS_IPV4:
- if (info->addr.family != AF_INET) {
- goto err;
- }
- tor_addr_copy(&ls->u.ap.addr, &info->addr);
- ls->u.ap.port = info->port;
- break;
- case LS_IPV6:
- if (info->addr.family != AF_INET6) {
- goto err;
- }
- tor_addr_copy(&ls->u.ap.addr, &info->addr);
- ls->u.ap.port = info->port;
- break;
- case LS_LEGACY_ID:
- /* Bug out if the identity digest is not set */
- if (BUG(tor_mem_is_zero(info->identity_digest,
- sizeof(info->identity_digest)))) {
- goto err;
- }
- memcpy(ls->u.legacy_id, info->identity_digest, sizeof(ls->u.legacy_id));
- break;
- case LS_ED25519_ID:
- /* ed25519 keys are optional for intro points */
- if (ed25519_public_key_is_zero(&info->ed_identity)) {
- goto err;
- }
- memcpy(ls->u.ed25519_id, info->ed_identity.pubkey,
- sizeof(ls->u.ed25519_id));
- break;
- default:
- /* Unknown type is code flow error. */
- tor_assert(0);
- }
-
- return ls;
- err:
- tor_free(ls);
- return NULL;
-}
-
/* From the given descriptor, remove and free every introduction point. */
void
hs_descriptor_clear_intro_points(hs_descriptor_t *desc)
@@ -3050,59 +2944,3 @@ hs_descriptor_clear_intro_points(hs_descriptor_t *desc)
smartlist_clear(ips);
}
}
-
-/* From a descriptor link specifier object spec, returned a newly allocated
- * link specifier object that is the encoded representation of spec. Return
- * NULL on error. */
-link_specifier_t *
-hs_desc_lspec_to_trunnel(const hs_desc_link_specifier_t *spec)
-{
- tor_assert(spec);
-
- link_specifier_t *ls = link_specifier_new();
- link_specifier_set_ls_type(ls, spec->type);
-
- switch (spec->type) {
- case LS_IPV4:
- link_specifier_set_un_ipv4_addr(ls,
- tor_addr_to_ipv4h(&spec->u.ap.addr));
- link_specifier_set_un_ipv4_port(ls, spec->u.ap.port);
- /* Four bytes IPv4 and two bytes port. */
- link_specifier_set_ls_len(ls, sizeof(spec->u.ap.addr.addr.in_addr) +
- sizeof(spec->u.ap.port));
- break;
- case LS_IPV6:
- {
- size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls);
- const uint8_t *in6_addr = tor_addr_to_in6_addr8(&spec->u.ap.addr);
- uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls);
- memcpy(ipv6_array, in6_addr, addr_len);
- link_specifier_set_un_ipv6_port(ls, spec->u.ap.port);
- /* Sixteen bytes IPv6 and two bytes port. */
- link_specifier_set_ls_len(ls, addr_len + sizeof(spec->u.ap.port));
- break;
- }
- case LS_LEGACY_ID:
- {
- size_t legacy_id_len = link_specifier_getlen_un_legacy_id(ls);
- uint8_t *legacy_id_array = link_specifier_getarray_un_legacy_id(ls);
- memcpy(legacy_id_array, spec->u.legacy_id, legacy_id_len);
- link_specifier_set_ls_len(ls, legacy_id_len);
- break;
- }
- case LS_ED25519_ID:
- {
- size_t ed25519_id_len = link_specifier_getlen_un_ed25519_id(ls);
- uint8_t *ed25519_id_array = link_specifier_getarray_un_ed25519_id(ls);
- memcpy(ed25519_id_array, spec->u.ed25519_id, ed25519_id_len);
- link_specifier_set_ls_len(ls, ed25519_id_len);
- break;
- }
- default:
- tor_assert_nonfatal_unreached();
- link_specifier_free(ls);
- ls = NULL;
- }
-
- return ls;
-}
diff --git a/src/feature/hs/hs_descriptor.h b/src/feature/hs/hs_descriptor.h
index 04a8e16d63..dbe0cb1c94 100644
--- a/src/feature/hs/hs_descriptor.h
+++ b/src/feature/hs/hs_descriptor.h
@@ -69,28 +69,10 @@ typedef enum {
HS_DESC_AUTH_ED25519 = 1
} hs_desc_auth_type_t;
-/* Link specifier object that contains information on how to extend to the
- * relay that is the address, port and handshake type. */
-typedef struct hs_desc_link_specifier_t {
- /* Indicate the type of link specifier. See trunnel ed25519_cert
- * specification. */
- uint8_t type;
-
- /* It must be one of these types, can't be more than one. */
- union {
- /* IP address and port of the relay use to extend. */
- tor_addr_port_t ap;
- /* Legacy identity. A 20-byte SHA1 identity fingerprint. */
- uint8_t legacy_id[DIGEST_LEN];
- /* ed25519 identity. A 32-byte key. */
- uint8_t ed25519_id[ED25519_PUBKEY_LEN];
- } u;
-} hs_desc_link_specifier_t;
-
/* Introduction point information located in a descriptor. */
typedef struct hs_desc_intro_point_t {
/* Link specifier(s) which details how to extend to the relay. This list
- * contains hs_desc_link_specifier_t object. It MUST have at least one. */
+ * contains link_specifier_t objects. It MUST have at least one. */
smartlist_t *link_specifiers;
/* Onion key of the introduction point used to extend to it for the ntor
@@ -261,12 +243,6 @@ void hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc);
#define hs_desc_encrypted_data_free(desc) \
FREE_AND_NULL(hs_desc_encrypted_data_t, hs_desc_encrypted_data_free_, (desc))
-void hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls);
-#define hs_desc_link_specifier_free(ls) \
- FREE_AND_NULL(hs_desc_link_specifier_t, hs_desc_link_specifier_free_, (ls))
-
-hs_desc_link_specifier_t *hs_desc_link_specifier_new(
- const extend_info_t *info, uint8_t type);
void hs_descriptor_clear_intro_points(hs_descriptor_t *desc);
MOCK_DECL(int,
@@ -299,9 +275,6 @@ void hs_desc_authorized_client_free_(hs_desc_authorized_client_t *client);
FREE_AND_NULL(hs_desc_authorized_client_t, \
hs_desc_authorized_client_free_, (client))
-link_specifier_t *hs_desc_lspec_to_trunnel(
- const hs_desc_link_specifier_t *spec);
-
hs_desc_authorized_client_t *hs_desc_build_fake_authorized_client(void);
void hs_desc_build_authorized_client(const uint8_t *subcredential,
const curve25519_public_key_t *
diff --git a/src/feature/hs/hs_intropoint.c b/src/feature/hs/hs_intropoint.c
index 7717ed53d4..9333060e7e 100644
--- a/src/feature/hs/hs_intropoint.c
+++ b/src/feature/hs/hs_intropoint.c
@@ -392,7 +392,7 @@ validate_introduce1_parsed_cell(const trn_cell_introduce1_t *cell)
* safety net here. The legacy ID must be zeroes in this case. */
legacy_key_id_len = trn_cell_introduce1_getlen_legacy_key_id(cell);
legacy_key_id = trn_cell_introduce1_getconstarray_legacy_key_id(cell);
- if (BUG(!tor_mem_is_zero((char *) legacy_key_id, legacy_key_id_len))) {
+ if (BUG(!fast_mem_is_zero((char *) legacy_key_id, legacy_key_id_len))) {
goto invalid;
}
@@ -518,7 +518,7 @@ introduce1_cell_is_legacy(const uint8_t *request)
/* If the first 20 bytes of the cell (DIGEST_LEN) are NOT zeroes, it
* indicates a legacy cell (v2). */
- if (!tor_mem_is_zero((const char *) request, DIGEST_LEN)) {
+ if (!fast_mem_is_zero((const char *) request, DIGEST_LEN)) {
/* Legacy cell. */
return 1;
}
@@ -602,8 +602,8 @@ hs_intropoint_clear(hs_intropoint_t *ip)
return;
}
tor_cert_free(ip->auth_key_cert);
- SMARTLIST_FOREACH(ip->link_specifiers, hs_desc_link_specifier_t *, ls,
- hs_desc_link_specifier_free(ls));
+ SMARTLIST_FOREACH(ip->link_specifiers, link_specifier_t *, ls,
+ link_specifier_free(ls));
smartlist_free(ip->link_specifiers);
memset(ip, 0, sizeof(hs_intropoint_t));
}
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index 21eadd2998..2835912742 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -280,9 +280,10 @@ describe_intro_point(const hs_service_intro_point_t *ip)
const char *legacy_id = NULL;
SMARTLIST_FOREACH_BEGIN(ip->base.link_specifiers,
- const hs_desc_link_specifier_t *, lspec) {
- if (lspec->type == LS_LEGACY_ID) {
- legacy_id = (const char *) lspec->u.legacy_id;
+ const link_specifier_t *, lspec) {
+ if (link_specifier_get_ls_type(lspec) == LS_LEGACY_ID) {
+ legacy_id = (const char *)
+ link_specifier_getconstarray_un_legacy_id(lspec);
break;
}
} SMARTLIST_FOREACH_END(lspec);
@@ -426,23 +427,16 @@ service_intro_point_free_void(void *obj)
}
/* Return a newly allocated service intro point and fully initialized from the
- * given extend_info_t ei if non NULL.
- * If is_legacy is true, we also generate the legacy key.
- * If supports_ed25519_link_handshake_any is true, we add the relay's ed25519
- * key to the link specifiers.
+ * given node_t node, if non NULL.
*
- * If ei is NULL, returns a hs_service_intro_point_t with an empty link
+ * If node is NULL, returns a hs_service_intro_point_t with an empty link
* specifier list and no onion key. (This is used for testing.)
* On any other error, NULL is returned.
*
- * ei must be an extend_info_t containing an IPv4 address. (We will add supoort
- * for IPv6 in a later release.) When calling extend_info_from_node(), pass
- * 0 in for_direct_connection to make sure ei always has an IPv4 address. */
+ * node must be an node_t with an IPv4 address. */
STATIC hs_service_intro_point_t *
-service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy,
- unsigned int supports_ed25519_link_handshake_any)
+service_intro_point_new(const node_t *node)
{
- hs_desc_link_specifier_t *ls;
hs_service_intro_point_t *ip;
ip = tor_malloc_zero(sizeof(*ip));
@@ -472,12 +466,17 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy,
ip->replay_cache = replaycache_new(0, 0);
/* Initialize the base object. We don't need the certificate object. */
- ip->base.link_specifiers = smartlist_new();
+ ip->base.link_specifiers = node_get_link_specifier_smartlist(node, 0);
+
+ if (node == NULL) {
+ goto done;
+ }
/* Generate the encryption key for this intro point. */
curve25519_keypair_generate(&ip->enc_key_kp, 0);
- /* Figure out if this chosen node supports v3 or is legacy only. */
- if (is_legacy) {
+ /* Figure out if this chosen node supports v3 or is legacy only.
+ * NULL nodes are used in the unit tests. */
+ if (!node_supports_ed25519_hs_intro(node)) {
ip->base.is_only_legacy = 1;
/* Legacy mode that is doesn't support v3+ with ed25519 auth key. */
ip->legacy_key = crypto_pk_new();
@@ -490,40 +489,9 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy,
}
}
- if (ei == NULL) {
- goto done;
- }
-
- /* We'll try to add all link specifiers. Legacy is mandatory.
- * IPv4 or IPv6 is required, and we always send IPv4. */
- ls = hs_desc_link_specifier_new(ei, LS_IPV4);
- /* It is impossible to have an extend info object without a v4. */
- if (BUG(!ls)) {
- goto err;
- }
- smartlist_add(ip->base.link_specifiers, ls);
-
- ls = hs_desc_link_specifier_new(ei, LS_LEGACY_ID);
- /* It is impossible to have an extend info object without an identity
- * digest. */
- if (BUG(!ls)) {
- goto err;
- }
- smartlist_add(ip->base.link_specifiers, ls);
-
- /* ed25519 identity key is optional for intro points. If the node supports
- * ed25519 link authentication, we include it. */
- if (supports_ed25519_link_handshake_any) {
- ls = hs_desc_link_specifier_new(ei, LS_ED25519_ID);
- if (ls) {
- smartlist_add(ip->base.link_specifiers, ls);
- }
- }
-
- /* IPv6 is not supported in this release. */
-
- /* Finally, copy onion key from the extend_info_t object. */
- memcpy(&ip->onion_key, &ei->curve25519_onion_key, sizeof(ip->onion_key));
+ /* Finally, copy onion key from the node. */
+ memcpy(&ip->onion_key, node_get_curve25519_onion_key(node),
+ sizeof(ip->onion_key));
done:
return ip;
@@ -656,16 +624,16 @@ get_objects_from_ident(const hs_ident_circuit_t *ident,
* encountered in the link specifier list. Return NULL if it can't be found.
*
* The caller does NOT have ownership of the object, the intro point does. */
-static hs_desc_link_specifier_t *
+static link_specifier_t *
get_link_spec_by_type(const hs_service_intro_point_t *ip, uint8_t type)
{
- hs_desc_link_specifier_t *lnk_spec = NULL;
+ link_specifier_t *lnk_spec = NULL;
tor_assert(ip);
SMARTLIST_FOREACH_BEGIN(ip->base.link_specifiers,
- hs_desc_link_specifier_t *, ls) {
- if (ls->type == type) {
+ link_specifier_t *, ls) {
+ if (link_specifier_get_ls_type(ls) == type) {
lnk_spec = ls;
goto end;
}
@@ -681,7 +649,7 @@ get_link_spec_by_type(const hs_service_intro_point_t *ip, uint8_t type)
STATIC const node_t *
get_node_from_intro_point(const hs_service_intro_point_t *ip)
{
- const hs_desc_link_specifier_t *ls;
+ const link_specifier_t *ls;
tor_assert(ip);
@@ -690,7 +658,8 @@ get_node_from_intro_point(const hs_service_intro_point_t *ip)
return NULL;
}
/* XXX In the future, we want to only use the ed25519 ID (#22173). */
- return node_get_by_id((const char *) ls->u.legacy_id);
+ return node_get_by_id(
+ (const char *) link_specifier_getconstarray_un_legacy_id(ls));
}
/* Given a service intro point, return the extend_info_t for it. This can
@@ -1179,7 +1148,8 @@ parse_authorized_client(const char *client_key_str)
client = tor_malloc_zero(sizeof(hs_service_authorized_client_t));
if (base32_decode((char *) client->client_pk.public_key,
sizeof(client->client_pk.public_key),
- pubkey_b32, strlen(pubkey_b32)) < 0) {
+ pubkey_b32, strlen(pubkey_b32)) !=
+ sizeof(client->client_pk.public_key)) {
log_warn(LD_REND, "Client authorization public key cannot be decoded: %s",
pubkey_b32);
goto err;
@@ -1261,7 +1231,7 @@ load_client_keys(hs_service_t *service)
client_key_str = read_file_to_str(client_key_file_path, 0, NULL);
/* If we cannot read the file, continue with the next file. */
- if (!client_key_str) {
+ if (!client_key_str) {
log_warn(LD_REND, "Client authorization file %s can't be read. "
"Corrupted or verify permission? Ignoring.",
client_key_file_path);
@@ -1556,7 +1526,7 @@ remember_failing_intro_point(const hs_service_intro_point_t *ip,
hs_service_descriptor_t *desc, time_t now)
{
time_t *time_of_failure, *prev_ptr;
- const hs_desc_link_specifier_t *legacy_ls;
+ const link_specifier_t *legacy_ls;
tor_assert(ip);
tor_assert(desc);
@@ -1565,22 +1535,13 @@ remember_failing_intro_point(const hs_service_intro_point_t *ip,
*time_of_failure = now;
legacy_ls = get_link_spec_by_type(ip, LS_LEGACY_ID);
tor_assert(legacy_ls);
- prev_ptr = digestmap_set(desc->intro_points.failed_id,
- (const char *) legacy_ls->u.legacy_id,
- time_of_failure);
+ prev_ptr = digestmap_set(
+ desc->intro_points.failed_id,
+ (const char *) link_specifier_getconstarray_un_legacy_id(legacy_ls),
+ time_of_failure);
tor_free(prev_ptr);
}
-/* Copy the descriptor link specifier object from src to dst. */
-static void
-link_specifier_copy(hs_desc_link_specifier_t *dst,
- const hs_desc_link_specifier_t *src)
-{
- tor_assert(dst);
- tor_assert(src);
- memcpy(dst, src, sizeof(hs_desc_link_specifier_t));
-}
-
/* Using a given descriptor signing keypair signing_kp, a service intro point
* object ip and the time now, setup the content of an already allocated
* descriptor intro desc_ip.
@@ -1615,9 +1576,14 @@ setup_desc_intro_point(const ed25519_keypair_t *signing_kp,
/* Copy link specifier(s). */
SMARTLIST_FOREACH_BEGIN(ip->base.link_specifiers,
- const hs_desc_link_specifier_t *, ls) {
- hs_desc_link_specifier_t *copy = tor_malloc_zero(sizeof(*copy));
- link_specifier_copy(copy, ls);
+ const link_specifier_t *, ls) {
+ if (BUG(!ls)) {
+ goto done;
+ }
+ link_specifier_t *copy = link_specifier_dup(ls);
+ if (BUG(!copy)) {
+ goto done;
+ }
smartlist_add(desc_ip->link_specifiers, copy);
} SMARTLIST_FOREACH_END(ls);
@@ -1780,7 +1746,7 @@ build_service_desc_superencrypted(const hs_service_t *service,
sizeof(curve25519_public_key_t));
/* Test that subcred is not zero because we might use it below */
- if (BUG(tor_mem_is_zero((char*)desc->desc->subcredential, DIGEST256_LEN))) {
+ if (BUG(fast_mem_is_zero((char*)desc->desc->subcredential, DIGEST256_LEN))) {
return -1;
}
@@ -1846,9 +1812,9 @@ build_service_desc_plaintext(const hs_service_t *service,
tor_assert(service);
tor_assert(desc);
- tor_assert(!tor_mem_is_zero((char *) &desc->blinded_kp,
+ tor_assert(!fast_mem_is_zero((char *) &desc->blinded_kp,
sizeof(desc->blinded_kp)));
- tor_assert(!tor_mem_is_zero((char *) &desc->signing_kp,
+ tor_assert(!fast_mem_is_zero((char *) &desc->signing_kp,
sizeof(desc->signing_kp)));
/* Set the subcredential. */
@@ -1898,7 +1864,7 @@ build_service_desc_keys(const hs_service_t *service,
ed25519_keypair_t kp;
tor_assert(desc);
- tor_assert(!tor_mem_is_zero((char *) &service->keys.identity_pk,
+ tor_assert(!fast_mem_is_zero((char *) &service->keys.identity_pk,
ED25519_PUBKEY_LEN));
/* XXX: Support offline key feature (#18098). */
@@ -2105,19 +2071,27 @@ build_all_descriptors(time_t now)
static hs_service_intro_point_t *
pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes)
{
+ const or_options_t *options = get_options();
const node_t *node;
- extend_info_t *info = NULL;
hs_service_intro_point_t *ip = NULL;
/* Normal 3-hop introduction point flags. */
router_crn_flags_t flags = CRN_NEED_UPTIME | CRN_NEED_DESC;
/* Single onion flags. */
router_crn_flags_t direct_flags = flags | CRN_PREF_ADDR | CRN_DIRECT_CONN;
- node = router_choose_random_node(exclude_nodes, get_options()->ExcludeNodes,
+ node = router_choose_random_node(exclude_nodes, options->ExcludeNodes,
direct_conn ? direct_flags : flags);
- /* Unable to find a node. When looking for a node for a direct connection,
- * we could try a 3-hop path instead. We'll add support for this in a later
- * release. */
+
+ /* If we are in single onion mode, retry node selection for a 3-hop
+ * path */
+ if (direct_conn && !node) {
+ log_info(LD_REND,
+ "Unable to find an intro point that we can connect to "
+ "directly, falling back to a 3-hop path.");
+ node = router_choose_random_node(exclude_nodes, options->ExcludeNodes,
+ flags);
+ }
+
if (!node) {
goto err;
}
@@ -2127,43 +2101,17 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes)
* we don't want to use that node anymore. */
smartlist_add(exclude_nodes, (void *) node);
- /* We do this to ease our life but also this call makes appropriate checks
- * of the node object such as validating ntor support for instance.
- *
- * We must provide an extend_info for clients to connect over a 3-hop path,
- * so we don't pass direct_conn here. */
- info = extend_info_from_node(node, 0);
- if (BUG(info == NULL)) {
- goto err;
- }
-
- /* Let's do a basic sanity check here so that we don't end up advertising the
- * ed25519 identity key of relays that don't actually support the link
- * protocol */
- if (!node_supports_ed25519_link_authentication(node, 0)) {
- tor_assert_nonfatal(ed25519_public_key_is_zero(&info->ed_identity));
- } else {
- /* Make sure we *do* have an ed key if we support the link authentication.
- * Sending an empty key would result in a failure to extend. */
- tor_assert_nonfatal(!ed25519_public_key_is_zero(&info->ed_identity));
- }
+ /* Create our objects and populate them with the node information. */
+ ip = service_intro_point_new(node);
- /* Create our objects and populate them with the node information.
- * We don't care if the intro's link auth is compatible with us, because
- * we are sending the ed25519 key to a remote client via the descriptor. */
- ip = service_intro_point_new(info, !node_supports_ed25519_hs_intro(node),
- node_supports_ed25519_link_authentication(node,
- 0));
if (ip == NULL) {
goto err;
}
- log_info(LD_REND, "Picked intro point: %s", extend_info_describe(info));
- extend_info_free(info);
+ log_info(LD_REND, "Picked intro point: %s", node_describe(node));
return ip;
err:
service_intro_point_free(ip);
- extend_info_free(info);
return NULL;
}
@@ -2644,7 +2592,7 @@ launch_intro_point_circuits(hs_service_t *service)
* circuits using the current map. */
FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
/* Keep a ref on if we need a direct connection. We use this often. */
- unsigned int direct_conn = service->config.is_single_onion;
+ bool direct_conn = service->config.is_single_onion;
DIGEST256MAP_FOREACH_MODIFY(desc->intro_points.map, key,
hs_service_intro_point_t *, ip) {
@@ -2655,8 +2603,15 @@ launch_intro_point_circuits(hs_service_t *service)
if (hs_circ_service_get_intro_circ(ip)) {
continue;
}
-
ei = get_extend_info_from_intro_point(ip, direct_conn);
+
+ /* If we can't connect directly to the intro point, get an extend_info
+ * for a multi-hop path instead. */
+ if (ei == NULL && direct_conn) {
+ direct_conn = false;
+ ei = get_extend_info_from_intro_point(ip, 0);
+ }
+
if (ei == NULL) {
/* This is possible if we can get a node_t but not the extend info out
* of it. In this case, we remove the intro point and a new one will
@@ -2668,7 +2623,7 @@ launch_intro_point_circuits(hs_service_t *service)
/* Launch a circuit to the intro point. */
ip->circuit_retries++;
- if (hs_circ_launch_intro_point(service, ip, ei) < 0) {
+ if (hs_circ_launch_intro_point(service, ip, ei, direct_conn) < 0) {
log_info(LD_REND, "Unable to launch intro circuit to node %s "
"for service %s.",
safe_str_client(extend_info_describe(ei)),
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index ec53f2f23b..22aa00b2d7 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -361,7 +361,7 @@ STATIC hs_service_t *get_first_service(void);
STATIC hs_service_intro_point_t *service_intro_point_find_by_ident(
const hs_service_t *service,
const hs_ident_circuit_t *ident);
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/* Service accessors. */
STATIC hs_service_t *find_service(hs_service_ht *map,
@@ -369,10 +369,7 @@ STATIC hs_service_t *find_service(hs_service_ht *map,
STATIC void remove_service(hs_service_ht *map, hs_service_t *service);
STATIC int register_service(hs_service_ht *map, hs_service_t *service);
/* Service introduction point functions. */
-STATIC hs_service_intro_point_t *service_intro_point_new(
- const extend_info_t *ei,
- unsigned int is_legacy,
- unsigned int supports_ed25519_link_handshake_any);
+STATIC hs_service_intro_point_t *service_intro_point_new(const node_t *node);
STATIC void service_intro_point_free_(hs_service_intro_point_t *ip);
#define service_intro_point_free(ip) \
FREE_AND_NULL(hs_service_intro_point_t, \
diff --git a/src/feature/hs/hs_stats.h b/src/feature/hs/hs_stats.h
index d89440faca..6700eca15b 100644
--- a/src/feature/hs/hs_stats.h
+++ b/src/feature/hs/hs_stats.h
@@ -6,9 +6,13 @@
* \brief Header file for hs_stats.c
**/
+#ifndef TOR_HS_STATS_H
+#define TOR_HS_STATS_H
+
void hs_stats_note_introduce2_cell(int is_hsv3);
uint32_t hs_stats_get_n_introduce2_v3_cells(void);
uint32_t hs_stats_get_n_introduce2_v2_cells(void);
void hs_stats_note_service_rendezvous_launch(void);
uint32_t hs_stats_get_n_rendezvous_launches(void);
+#endif /* !defined(TOR_HS_STATS_H) */
diff --git a/src/feature/hs/hsdir_index_st.h b/src/feature/hs/hsdir_index_st.h
index 7d4116d8bb..6c86c02f47 100644
--- a/src/feature/hs/hsdir_index_st.h
+++ b/src/feature/hs/hsdir_index_st.h
@@ -20,5 +20,5 @@ struct hsdir_index_t {
uint8_t store_second[DIGEST256_LEN];
};
-#endif
+#endif /* !defined(HSDIR_INDEX_ST_H) */
diff --git a/src/feature/hs_common/shared_random_client.h b/src/feature/hs_common/shared_random_client.h
index 95fe2c65ab..c90c52cfea 100644
--- a/src/feature/hs_common/shared_random_client.h
+++ b/src/feature/hs_common/shared_random_client.h
@@ -44,5 +44,5 @@ time_t get_start_time_of_current_round(void);
#endif /* TOR_UNIT_TESTS */
-#endif /* TOR_SHARED_RANDOM_CLIENT_H */
+#endif /* !defined(TOR_SHARED_RANDOM_CLIENT_H) */
diff --git a/src/feature/keymgt/loadkey.h b/src/feature/keymgt/loadkey.h
index 8beee57a20..0a5af0b804 100644
--- a/src/feature/keymgt/loadkey.h
+++ b/src/feature/keymgt/loadkey.h
@@ -52,4 +52,4 @@ int read_encrypted_secret_key(ed25519_secret_key_t *out,
int write_encrypted_secret_key(const ed25519_secret_key_t *out,
const char *fname);
-#endif
+#endif /* !defined(TOR_LOADKEY_H) */
diff --git a/src/feature/nodelist/authcert.h b/src/feature/nodelist/authcert.h
index 2effdb06e6..071293f9ee 100644
--- a/src/feature/nodelist/authcert.h
+++ b/src/feature/nodelist/authcert.h
@@ -57,4 +57,4 @@ MOCK_DECL(download_status_t *, download_status_for_authority_id_and_sk,
void authcert_free_all(void);
-#endif
+#endif /* !defined(TOR_AUTHCERT_H) */
diff --git a/src/feature/nodelist/authority_cert_st.h b/src/feature/nodelist/authority_cert_st.h
index 68a84bc452..bf9b690c24 100644
--- a/src/feature/nodelist/authority_cert_st.h
+++ b/src/feature/nodelist/authority_cert_st.h
@@ -28,5 +28,5 @@ struct authority_cert_t {
uint16_t dir_port;
};
-#endif
+#endif /* !defined(AUTHORITY_CERT_ST_H) */
diff --git a/src/feature/nodelist/desc_store_st.h b/src/feature/nodelist/desc_store_st.h
index b04a1abc7d..4d1378cdfa 100644
--- a/src/feature/nodelist/desc_store_st.h
+++ b/src/feature/nodelist/desc_store_st.h
@@ -36,4 +36,4 @@ struct desc_store_t {
size_t bytes_dropped;
};
-#endif
+#endif /* !defined(DESC_STORE_ST_H) */
diff --git a/src/feature/nodelist/describe.h b/src/feature/nodelist/describe.h
index 018af6470e..d29192200e 100644
--- a/src/feature/nodelist/describe.h
+++ b/src/feature/nodelist/describe.h
@@ -22,4 +22,4 @@ const char *node_describe(const struct node_t *node);
const char *router_describe(const struct routerinfo_t *ri);
const char *routerstatus_describe(const struct routerstatus_t *ri);
-#endif
+#endif /* !defined(TOR_DESCRIBE_H) */
diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c
index 93baa6e4e0..e2a1d6a9fa 100644
--- a/src/feature/nodelist/dirlist.c
+++ b/src/feature/nodelist/dirlist.c
@@ -28,7 +28,7 @@
#include "app/config/config.h"
#include "core/or/policies.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/authmode.h"
#include "feature/dircommon/directory.h"
#include "feature/nodelist/dirlist.h"
diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h
index 9fabd0a44a..b6dda32d85 100644
--- a/src/feature/nodelist/dirlist.h
+++ b/src/feature/nodelist/dirlist.h
@@ -44,4 +44,4 @@ void dir_server_add(dir_server_t *ent);
void clear_dir_servers(void);
void dirlist_free_all(void);
-#endif
+#endif /* !defined(TOR_DIRLIST_H) */
diff --git a/src/feature/nodelist/document_signature_st.h b/src/feature/nodelist/document_signature_st.h
index 66e32c422f..ac2a803252 100644
--- a/src/feature/nodelist/document_signature_st.h
+++ b/src/feature/nodelist/document_signature_st.h
@@ -25,5 +25,5 @@ struct document_signature_t {
* as good. */
};
-#endif
+#endif /* !defined(DOCUMENT_SIGNATURE_ST_H) */
diff --git a/src/feature/nodelist/extrainfo_st.h b/src/feature/nodelist/extrainfo_st.h
index c54277b05e..22c708f018 100644
--- a/src/feature/nodelist/extrainfo_st.h
+++ b/src/feature/nodelist/extrainfo_st.h
@@ -26,5 +26,5 @@ struct extrainfo_t {
size_t pending_sig_len;
};
-#endif
+#endif /* !defined(EXTRAINFO_ST_H) */
diff --git a/src/feature/nodelist/fmt_routerstatus.c b/src/feature/nodelist/fmt_routerstatus.c
index 8c9212e05c..fea7cf4c65 100644
--- a/src/feature/nodelist/fmt_routerstatus.c
+++ b/src/feature/nodelist/fmt_routerstatus.c
@@ -14,55 +14,14 @@
#include "core/or/or.h"
#include "feature/nodelist/fmt_routerstatus.h"
-/* #include "lib/container/buffers.h" */
-/* #include "app/config/config.h" */
-/* #include "app/config/confparse.h" */
-/* #include "core/or/channel.h" */
-/* #include "core/or/channeltls.h" */
-/* #include "core/or/command.h" */
-/* #include "core/mainloop/connection.h" */
-/* #include "core/or/connection_or.h" */
-/* #include "feature/dircache/conscache.h" */
-/* #include "feature/dircache/consdiffmgr.h" */
-/* #include "feature/control/control.h" */
-/* #include "feature/dircache/directory.h" */
-/* #include "feature/dircache/dirserv.h" */
-/* #include "feature/hibernate/hibernate.h" */
-/* #include "feature/dirauth/keypin.h" */
-/* #include "core/mainloop/mainloop.h" */
-/* #include "feature/nodelist/microdesc.h" */
-/* #include "feature/nodelist/networkstatus.h" */
-/* #include "feature/nodelist/nodelist.h" */
#include "core/or/policies.h"
-/* #include "core/or/protover.h" */
-/* #include "feature/stats/rephist.h" */
-/* #include "feature/relay/router.h" */
-/* #include "feature/nodelist/dirlist.h" */
#include "feature/nodelist/routerlist.h"
-
-/* #include "feature/nodelist/routerparse.h" */
-/* #include "feature/nodelist/routerset.h" */
-/* #include "feature/nodelist/torcert.h" */
-/* #include "feature/dircommon/voting_schedule.h" */
-
#include "feature/dirauth/dirvote.h"
-/* #include "feature/dircache/cached_dir_st.h" */
-/* #include "feature/dircommon/dir_connection_st.h" */
-/* #include "feature/nodelist/extrainfo_st.h" */
-/* #include "feature/nodelist/microdesc_st.h" */
-/* #include "feature/nodelist/node_st.h" */
#include "feature/nodelist/routerinfo_st.h"
-/* #include "feature/nodelist/routerlist_st.h" */
-/* #include "core/or/tor_version_st.h" */
#include "feature/nodelist/vote_routerstatus_st.h"
-/* #include "lib/compress/compress.h" */
-/* #include "lib/container/order.h" */
#include "lib/crypt_ops/crypto_format.h"
-/* #include "lib/encoding/confline.h" */
-
-/* #include "lib/encoding/keyval.h" */
/** Helper: write the router-status information in <b>rs</b> into a newly
* allocated character buffer. Use the same format as in network-status
@@ -233,7 +192,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
}
if (format == NS_V3_VOTE && vrs) {
- if (tor_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) {
+ if (fast_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) {
smartlist_add_strdup(chunks, "id ed25519 none\n");
} else {
char ed_b64[BASE64_DIGEST256_LEN+1];
diff --git a/src/feature/nodelist/microdesc.c b/src/feature/nodelist/microdesc.c
index 36922561a0..89ac0a2f83 100644
--- a/src/feature/nodelist/microdesc.c
+++ b/src/feature/nodelist/microdesc.c
@@ -536,8 +536,8 @@ microdesc_cache_reload(microdesc_cache_t *cache)
journal_content = read_file_to_str(cache->journal_fname,
RFTS_IGNORE_MISSING, &st);
if (journal_content) {
- cache->journal_len = (size_t) st.st_size;
- warn_if_nul_found(journal_content, cache->journal_len, 0,
+ cache->journal_len = strlen(journal_content);
+ warn_if_nul_found(journal_content, (size_t)st.st_size, 0,
"reading microdesc journal");
added = microdescs_add_to_cache(cache, journal_content,
journal_content+st.st_size,
@@ -970,7 +970,7 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
continue;
if (skip && digest256map_get(skip, (const uint8_t*)rs->descriptor_digest))
continue;
- if (tor_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN))
+ if (fast_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN))
continue;
/* XXXX Also skip if we're a noncache and wouldn't use this router.
* XXXX NM Microdesc
diff --git a/src/feature/nodelist/microdesc_st.h b/src/feature/nodelist/microdesc_st.h
index 367e6a3ef6..c8265cb778 100644
--- a/src/feature/nodelist/microdesc_st.h
+++ b/src/feature/nodelist/microdesc_st.h
@@ -78,4 +78,4 @@ struct microdesc_t {
struct short_policy_t *ipv6_exit_policy;
};
-#endif
+#endif /* !defined(MICRODESC_ST_H) */
diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c
index 24e3b212f0..2db293a8af 100644
--- a/src/feature/nodelist/networkstatus.c
+++ b/src/feature/nodelist/networkstatus.c
@@ -58,7 +58,7 @@
#include "feature/client/bridges.h"
#include "feature/client/entrynodes.h"
#include "feature/client/transports.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/reachability.h"
#include "feature/dircache/consdiffmgr.h"
#include "feature/dircache/dirserv.h"
@@ -82,6 +82,7 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
+#include "feature/dirauth/dirauth_periodic.h"
#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/authmode.h"
#include "feature/dirauth/shared_random.h"
@@ -1771,7 +1772,7 @@ reload_consensus_from_file(const char *fname,
flavor, flags, source_dir);
tor_free(content);
}
-#endif
+#endif /* defined(_WIN32) */
if (rv < -1) {
log_warn(LD_GENERAL, "Couldn't set consensus from cache file %s",
escaped(fname));
@@ -2365,6 +2366,49 @@ networkstatus_getinfo_helper_single(const routerstatus_t *rs)
NULL);
}
+/**
+ * Extract status information from <b>ri</b> and from other authority
+ * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is
+ * set.
+ *
+ * We assume that node-\>is_running has already been set, e.g. by
+ * dirserv_set_router_is_running(ri, now);
+ */
+void
+set_routerstatus_from_routerinfo(routerstatus_t *rs,
+ const node_t *node,
+ const routerinfo_t *ri)
+{
+ memset(rs, 0, sizeof(routerstatus_t));
+
+ rs->is_authority =
+ router_digest_is_trusted_dir(ri->cache_info.identity_digest);
+
+ /* Set by compute_performance_thresholds or from consensus */
+ rs->is_exit = node->is_exit;
+ rs->is_stable = node->is_stable;
+ rs->is_fast = node->is_fast;
+ rs->is_flagged_running = node->is_running;
+ rs->is_valid = node->is_valid;
+ rs->is_possible_guard = node->is_possible_guard;
+ rs->is_bad_exit = node->is_bad_exit;
+ rs->is_hs_dir = node->is_hs_dir;
+ rs->is_named = rs->is_unnamed = 0;
+
+ rs->published_on = ri->cache_info.published_on;
+ memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
+ memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
+ DIGEST_LEN);
+ rs->addr = ri->addr;
+ strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname));
+ rs->or_port = ri->or_port;
+ rs->dir_port = ri->dir_port;
+ rs->is_v2_dir = ri->supports_tunnelled_dir_requests;
+
+ tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr);
+ rs->ipv6_orport = ri->ipv6_orport;
+}
+
/** Alloc and return a string describing routerstatuses for the most
* recent info of each router we know about that is of purpose
* <b>purpose_string</b>. Return NULL if unrecognized purpose.
@@ -2381,7 +2425,6 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
smartlist_t *statuses;
const uint8_t purpose = router_purpose_from_string(purpose_string);
routerstatus_t rs;
- const int bridge_auth = authdir_mode_bridge(get_options());
if (purpose == ROUTER_PURPOSE_UNKNOWN) {
log_info(LD_DIR, "Unrecognized purpose '%s' when listing router statuses.",
@@ -2398,11 +2441,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
continue;
if (ri->purpose != purpose)
continue;
- /* TODO: modifying the running flag in a getinfo is a bad idea */
- if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE)
- dirserv_set_router_is_running(ri, now);
- /* then generate and write out status lines for each of them */
- set_routerstatus_from_routerinfo(&rs, node, ri, now, 0);
+ set_routerstatus_from_routerinfo(&rs, node, ri);
smartlist_add(statuses, networkstatus_getinfo_helper_single(&rs));
} SMARTLIST_FOREACH_END(ri);
@@ -2412,43 +2451,6 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
return answer;
}
-/** Write out router status entries for all our bridge descriptors. */
-void
-networkstatus_dump_bridge_status_to_file(time_t now)
-{
- char *status = networkstatus_getinfo_by_purpose("bridge", now);
- char *fname = NULL;
- char *thresholds = NULL;
- char *published_thresholds_and_status = NULL;
- char published[ISO_TIME_LEN+1];
- const routerinfo_t *me = router_get_my_routerinfo();
- char fingerprint[FINGERPRINT_LEN+1];
- char *fingerprint_line = NULL;
-
- if (me && crypto_pk_get_fingerprint(me->identity_pkey,
- fingerprint, 0) >= 0) {
- tor_asprintf(&fingerprint_line, "fingerprint %s\n", fingerprint);
- } else {
- log_warn(LD_BUG, "Error computing fingerprint for bridge status.");
- }
- format_iso_time(published, now);
- dirserv_compute_bridge_flag_thresholds();
- thresholds = dirserv_get_flag_thresholds_line();
- tor_asprintf(&published_thresholds_and_status,
- "published %s\nflag-thresholds %s\n%s%s",
- published, thresholds, fingerprint_line ? fingerprint_line : "",
- status);
- fname = get_datadir_fname("networkstatus-bridges");
- if (write_str_to_file(fname,published_thresholds_and_status,0)<0) {
- log_warn(LD_DIRSERV, "Unable to write networkstatus-bridges file.");
- }
- tor_free(thresholds);
- tor_free(published_thresholds_and_status);
- tor_free(fname);
- tor_free(status);
- tor_free(fingerprint_line);
-}
-
/* DOCDOC get_net_param_from_list */
static int32_t
get_net_param_from_list(smartlist_t *net_params, const char *param_name,
diff --git a/src/feature/nodelist/networkstatus.h b/src/feature/nodelist/networkstatus.h
index 8269fc6182..600fd7fbd5 100644
--- a/src/feature/nodelist/networkstatus.h
+++ b/src/feature/nodelist/networkstatus.h
@@ -122,7 +122,6 @@ void signed_descs_update_status_from_consensus_networkstatus(
char *networkstatus_getinfo_helper_single(const routerstatus_t *rs);
char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now);
-void networkstatus_dump_bridge_status_to_file(time_t now);
MOCK_DECL(int32_t, networkstatus_get_param,
(const networkstatus_t *ns, const char *param_name,
int32_t default_val, int32_t min_val, int32_t max_val));
@@ -149,6 +148,10 @@ void vote_routerstatus_free_(vote_routerstatus_t *rs);
#define vote_routerstatus_free(rs) \
FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs))
+void set_routerstatus_from_routerinfo(routerstatus_t *rs,
+ const node_t *node,
+ const routerinfo_t *ri);
+
#ifdef NETWORKSTATUS_PRIVATE
#ifdef TOR_UNIT_TESTS
STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
diff --git a/src/feature/nodelist/networkstatus_sr_info_st.h b/src/feature/nodelist/networkstatus_sr_info_st.h
index 677d8ed811..420c3d61e4 100644
--- a/src/feature/nodelist/networkstatus_sr_info_st.h
+++ b/src/feature/nodelist/networkstatus_sr_info_st.h
@@ -19,5 +19,5 @@ struct networkstatus_sr_info_t {
smartlist_t *commits;
};
-#endif
+#endif /* !defined(NETWORKSTATUS_SR_INFO_ST_H) */
diff --git a/src/feature/nodelist/networkstatus_st.h b/src/feature/nodelist/networkstatus_st.h
index 5c1eea3259..6e84c170d6 100644
--- a/src/feature/nodelist/networkstatus_st.h
+++ b/src/feature/nodelist/networkstatus_st.h
@@ -104,4 +104,4 @@ struct networkstatus_t {
uint8_t bw_file_digest256[DIGEST256_LEN];
};
-#endif
+#endif /* !defined(NETWORKSTATUS_ST_H) */
diff --git a/src/feature/nodelist/networkstatus_voter_info_st.h b/src/feature/nodelist/networkstatus_voter_info_st.h
index 4037fcdeca..66af82a8e3 100644
--- a/src/feature/nodelist/networkstatus_voter_info_st.h
+++ b/src/feature/nodelist/networkstatus_voter_info_st.h
@@ -27,4 +27,4 @@ struct networkstatus_voter_info_t {
smartlist_t *sigs;
};
-#endif
+#endif /* !defined(NETWORKSTATUS_VOTER_INFO_ST_H) */
diff --git a/src/feature/nodelist/nickname.h b/src/feature/nodelist/nickname.h
index 9bdc6b50e8..78db2a5f91 100644
--- a/src/feature/nodelist/nickname.h
+++ b/src/feature/nodelist/nickname.h
@@ -16,4 +16,4 @@ int is_legal_nickname(const char *s);
int is_legal_nickname_or_hexdigest(const char *s);
int is_legal_hexdigest(const char *s);
-#endif
+#endif /* !defined(TOR_NICKNAME_H) */
diff --git a/src/feature/nodelist/node_select.c b/src/feature/nodelist/node_select.c
index e31abb247f..719b4b1b27 100644
--- a/src/feature/nodelist/node_select.c
+++ b/src/feature/nodelist/node_select.c
@@ -30,6 +30,7 @@
#include "feature/nodelist/routerset.h"
#include "feature/relay/router.h"
#include "feature/relay/routermode.h"
+#include "lib/container/bitarray.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/math/fp.h"
@@ -585,6 +586,7 @@ compute_weighted_bandwidths(const smartlist_t *sl,
}
weight_scale = networkstatus_get_weight_scale_param(NULL);
+ tor_assert(weight_scale >= 1);
if (rule == WEIGHT_FOR_GUARD) {
Wg = networkstatus_get_bw_weight(NULL, "Wgg", -1);
@@ -825,6 +827,58 @@ routerlist_add_node_and_family(smartlist_t *sl, const routerinfo_t *router)
nodelist_add_node_and_family(sl, node);
}
+/**
+ * Remove every node_t that appears in <b>excluded</b> from <b>sl</b>.
+ *
+ * Behaves like smartlist_subtract, but uses nodelist_idx values to deliver
+ * linear performance when smartlist_subtract would be quadratic.
+ **/
+static void
+nodelist_subtract(smartlist_t *sl, const smartlist_t *excluded)
+{
+ const smartlist_t *nodelist = nodelist_get_list();
+ const int nodelist_len = smartlist_len(nodelist);
+ bitarray_t *excluded_idx = bitarray_init_zero(nodelist_len);
+
+ /* We haven't used nodelist_idx in this way previously, so I'm going to be
+ * paranoid in this code, and check that nodelist_idx is correct for every
+ * node before we use it. If we fail, we fall back to smartlist_subtract().
+ */
+
+ /* Set the excluded_idx bit corresponding to every excluded node...
+ */
+ SMARTLIST_FOREACH_BEGIN(excluded, const node_t *, node) {
+ const int idx = node->nodelist_idx;
+ if (BUG(idx < 0) || BUG(idx >= nodelist_len) ||
+ BUG(node != smartlist_get(nodelist, idx))) {
+ goto internal_error;
+ }
+ bitarray_set(excluded_idx, idx);
+ } SMARTLIST_FOREACH_END(node);
+
+ /* Then remove them from sl.
+ */
+ SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
+ const int idx = node->nodelist_idx;
+ if (BUG(idx < 0) || BUG(idx >= nodelist_len) ||
+ BUG(node != smartlist_get(nodelist, idx))) {
+ goto internal_error;
+ }
+ if (bitarray_is_set(excluded_idx, idx)) {
+ SMARTLIST_DEL_CURRENT(sl, node);
+ }
+ } SMARTLIST_FOREACH_END(node);
+
+ bitarray_free(excluded_idx);
+ return;
+
+ internal_error:
+ log_warn(LD_BUG, "Internal error prevented us from using the fast method "
+ "for subtracting nodelists. Falling back to the quadratic way.");
+ smartlist_subtract(sl, excluded);
+ bitarray_free(excluded_idx);
+}
+
/** Return a random running node from the nodelist. Never
* pick a node that is in
* <b>excludedsmartlist</b>, or which matches <b>excludedset</b>,
@@ -859,6 +913,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
const int direct_conn = (flags & CRN_DIRECT_CONN) != 0;
const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0;
+ const smartlist_t *node_list = nodelist_get_list();
smartlist_t *sl=smartlist_new(),
*excludednodes=smartlist_new();
const node_t *choice = NULL;
@@ -869,17 +924,17 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
rule = weight_for_exit ? WEIGHT_FOR_EXIT :
(need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID);
- SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
+ SMARTLIST_FOREACH_BEGIN(node_list, const node_t *, node) {
if (node_allows_single_hop_exits(node)) {
/* Exclude relays that allow single hop exit circuits. This is an
* obsolete option since 0.2.9.2-alpha and done by default in
* 0.3.1.0-alpha. */
- smartlist_add(excludednodes, node);
+ smartlist_add(excludednodes, (node_t*)node);
} else if (rendezvous_v3 &&
!node_supports_v3_rendezvous_point(node)) {
/* Exclude relays that do not support to rendezvous for a hidden service
* version 3. */
- smartlist_add(excludednodes, node);
+ smartlist_add(excludednodes, (node_t*)node);
}
} SMARTLIST_FOREACH_END(node);
@@ -896,19 +951,11 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
"We found %d running nodes.",
smartlist_len(sl));
- smartlist_subtract(sl,excludednodes);
- log_debug(LD_CIRC,
- "We removed %d excludednodes, leaving %d nodes.",
- smartlist_len(excludednodes),
- smartlist_len(sl));
-
if (excludedsmartlist) {
- smartlist_subtract(sl,excludedsmartlist);
- log_debug(LD_CIRC,
- "We removed %d excludedsmartlist, leaving %d nodes.",
- smartlist_len(excludedsmartlist),
- smartlist_len(sl));
+ smartlist_add_all(excludednodes, excludedsmartlist);
}
+ nodelist_subtract(sl, excludednodes);
+
if (excludedset) {
routerset_subtract_nodes(sl,excludedset);
log_debug(LD_CIRC,
diff --git a/src/feature/nodelist/node_select.h b/src/feature/nodelist/node_select.h
index ed7450b92c..d8b4aca5c1 100644
--- a/src/feature/nodelist/node_select.h
+++ b/src/feature/nodelist/node_select.h
@@ -97,6 +97,6 @@ STATIC const routerstatus_t *router_pick_directory_server_impl(
int *n_busy_out);
STATIC int router_is_already_dir_fetching(const tor_addr_port_t *ap,
int serverdesc, int microdesc);
-#endif
+#endif /* defined(NODE_SELECT_PRIVATE) */
-#endif
+#endif /* !defined(TOR_NODE_SELECT_H) */
diff --git a/src/feature/nodelist/node_st.h b/src/feature/nodelist/node_st.h
index 53ffde29e4..c63a535a19 100644
--- a/src/feature/nodelist/node_st.h
+++ b/src/feature/nodelist/node_st.h
@@ -99,4 +99,4 @@ struct node_t {
struct hsdir_index_t hsdir_index;
};
-#endif
+#endif /* !defined(NODE_ST_H) */
diff --git a/src/feature/nodelist/nodefamily.h b/src/feature/nodelist/nodefamily.h
index bc5dafce03..31b71e77a0 100644
--- a/src/feature/nodelist/nodefamily.h
+++ b/src/feature/nodelist/nodefamily.h
@@ -47,4 +47,4 @@ char *nodefamily_canonicalize(const char *s, const uint8_t *rsa_id_self,
void nodefamily_free_all(void);
-#endif
+#endif /* !defined(TOR_NODEFAMILY_H) */
diff --git a/src/feature/nodelist/nodefamily_st.h b/src/feature/nodelist/nodefamily_st.h
index be533da824..20390c9308 100644
--- a/src/feature/nodelist/nodefamily_st.h
+++ b/src/feature/nodelist/nodefamily_st.h
@@ -45,4 +45,4 @@ struct nodefamily_t {
#define NODEFAMILY_MEMBER_PTR(nf, i) \
(&((nf)->family_members[(i) * NODEFAMILY_MEMBER_LEN]))
-#endif
+#endif /* !defined(TOR_NODEFAMILY_ST_H) */
diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c
index 8b02dd9c66..21914c6c6d 100644
--- a/src/feature/nodelist/nodelist.c
+++ b/src/feature/nodelist/nodelist.c
@@ -49,7 +49,7 @@
#include "core/or/protover.h"
#include "feature/client/bridges.h"
#include "feature/client/entrynodes.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/process_descs.h"
#include "feature/dircache/dirserv.h"
#include "feature/hs/hs_client.h"
@@ -944,7 +944,7 @@ nodelist_ensure_freshness(networkstatus_t *ns)
/** Return a list of a node_t * for every node we know about. The caller
* MUST NOT modify the list. (You can set and clear flags in the nodes if
* you must, but you must not add or remove nodes.) */
-MOCK_IMPL(smartlist_t *,
+MOCK_IMPL(const smartlist_t *,
nodelist_get_list,(void))
{
init_nodelist();
@@ -1189,6 +1189,102 @@ node_get_rsa_id_digest(const node_t *node)
return (const uint8_t*)node->identity;
}
+/* Returns a new smartlist with all possible link specifiers from node:
+ * - legacy ID is mandatory thus MUST be present in node;
+ * - include ed25519 link specifier if present in the node, and the node
+ * supports ed25519 link authentication, and:
+ * - if direct_conn is true, its link versions are compatible with us,
+ * - if direct_conn is false, regardless of its link versions;
+ * - include IPv4 link specifier, if the primary address is not IPv4, log a
+ * BUG() warning, and return an empty smartlist;
+ * - include IPv6 link specifier if present in the node.
+ *
+ * If node is NULL, returns an empty smartlist.
+ *
+ * The smartlist must be freed using link_specifier_smartlist_free(). */
+smartlist_t *
+node_get_link_specifier_smartlist(const node_t *node, bool direct_conn)
+{
+ link_specifier_t *ls;
+ tor_addr_port_t ap;
+ smartlist_t *lspecs = smartlist_new();
+
+ if (!node)
+ return lspecs;
+
+ /* Get the relay's IPv4 address. */
+ node_get_prim_orport(node, &ap);
+
+ /* We expect the node's primary address to be a valid IPv4 address.
+ * This conforms to the protocol, which requires either an IPv4 or IPv6
+ * address (or both). */
+ if (BUG(!tor_addr_is_v4(&ap.addr)) ||
+ BUG(!tor_addr_port_is_valid_ap(&ap, 0))) {
+ return lspecs;
+ }
+
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_IPV4);
+ link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ap.addr));
+ link_specifier_set_un_ipv4_port(ls, ap.port);
+ /* Four bytes IPv4 and two bytes port. */
+ link_specifier_set_ls_len(ls, sizeof(ap.addr.addr.in_addr) +
+ sizeof(ap.port));
+ smartlist_add(lspecs, ls);
+
+ /* Legacy ID is mandatory and will always be present in node. */
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_LEGACY_ID);
+ memcpy(link_specifier_getarray_un_legacy_id(ls), node->identity,
+ link_specifier_getlen_un_legacy_id(ls));
+ link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls));
+ smartlist_add(lspecs, ls);
+
+ /* ed25519 ID is only included if the node has it, and the node declares a
+ protocol version that supports ed25519 link authentication.
+ If direct_conn is true, we also require that the node's link version is
+ compatible with us. (Otherwise, we will be sending the ed25519 key
+ to another tor, which may support different link versions.) */
+ if (!ed25519_public_key_is_zero(&node->ed25519_id) &&
+ node_supports_ed25519_link_authentication(node, direct_conn)) {
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_ED25519_ID);
+ memcpy(link_specifier_getarray_un_ed25519_id(ls), &node->ed25519_id,
+ link_specifier_getlen_un_ed25519_id(ls));
+ link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls));
+ smartlist_add(lspecs, ls);
+ }
+
+ /* Check for IPv6. If so, include it as well. */
+ if (node_has_ipv6_orport(node)) {
+ ls = link_specifier_new();
+ node_get_pref_ipv6_orport(node, &ap);
+ link_specifier_set_ls_type(ls, LS_IPV6);
+ size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls);
+ const uint8_t *in6_addr = tor_addr_to_in6_addr8(&ap.addr);
+ uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls);
+ memcpy(ipv6_array, in6_addr, addr_len);
+ link_specifier_set_un_ipv6_port(ls, ap.port);
+ /* Sixteen bytes IPv6 and two bytes port. */
+ link_specifier_set_ls_len(ls, addr_len + sizeof(ap.port));
+ smartlist_add(lspecs, ls);
+ }
+
+ return lspecs;
+}
+
+/* Free a link specifier list. */
+void
+link_specifier_smartlist_free_(smartlist_t *ls_list)
+{
+ if (!ls_list)
+ return;
+
+ SMARTLIST_FOREACH(ls_list, link_specifier_t *, lspec,
+ link_specifier_free(lspec));
+ smartlist_free(ls_list);
+}
+
/** Return the nickname of <b>node</b>, or NULL if we can't find one. */
const char *
node_get_nickname(const node_t *node)
@@ -1763,7 +1859,7 @@ microdesc_has_curve25519_onion_key(const microdesc_t *md)
return 0;
}
- if (tor_mem_is_zero((const char*)md->onion_curve25519_pkey->public_key,
+ if (fast_mem_is_zero((const char*)md->onion_curve25519_pkey->public_key,
CURVE25519_PUBKEY_LEN)) {
return 0;
}
@@ -1843,7 +1939,7 @@ node_set_country(node_t *node)
void
nodelist_refresh_countries(void)
{
- smartlist_t *nodes = nodelist_get_list();
+ const smartlist_t *nodes = nodelist_get_list();
SMARTLIST_FOREACH(nodes, node_t *, node,
node_set_country(node));
}
diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h
index 3420959618..84ab5f7a54 100644
--- a/src/feature/nodelist/nodelist.h
+++ b/src/feature/nodelist/nodelist.h
@@ -77,6 +77,11 @@ int node_supports_v3_hsdir(const node_t *node);
int node_supports_ed25519_hs_intro(const node_t *node);
int node_supports_v3_rendezvous_point(const node_t *node);
const uint8_t *node_get_rsa_id_digest(const node_t *node);
+smartlist_t *node_get_link_specifier_smartlist(const node_t *node,
+ bool direct_conn);
+void link_specifier_smartlist_free_(smartlist_t *ls_list);
+#define link_specifier_smartlist_free(ls_list) \
+ FREE_AND_NULL(smartlist_t, link_specifier_smartlist_free_, (ls_list))
int node_has_ipv6_addr(const node_t *node);
int node_has_ipv6_orport(const node_t *node);
@@ -96,7 +101,7 @@ const struct curve25519_public_key_t *node_get_curve25519_onion_key(
const node_t *node);
crypto_pk_t *node_get_rsa_onion_key(const node_t *node);
-MOCK_DECL(smartlist_t *, nodelist_get_list, (void));
+MOCK_DECL(const smartlist_t *, nodelist_get_list, (void));
/* Temporary during transition to multiple addresses. */
void node_get_addr(const node_t *node, tor_addr_t *addr_out);
diff --git a/src/feature/nodelist/routerinfo.h b/src/feature/nodelist/routerinfo.h
index bfa28c7754..ca66e660b3 100644
--- a/src/feature/nodelist/routerinfo.h
+++ b/src/feature/nodelist/routerinfo.h
@@ -24,4 +24,4 @@ smartlist_t *router_get_all_orports(const routerinfo_t *ri);
const char *router_purpose_to_string(uint8_t p);
uint8_t router_purpose_from_string(const char *s);
-#endif
+#endif /* !defined(TOR_ROUTERINFO_H) */
diff --git a/src/feature/nodelist/routerinfo_st.h b/src/feature/nodelist/routerinfo_st.h
index 59656818c1..59fd56d0a0 100644
--- a/src/feature/nodelist/routerinfo_st.h
+++ b/src/feature/nodelist/routerinfo_st.h
@@ -112,4 +112,4 @@ struct routerinfo_t {
uint8_t purpose;
};
-#endif
+#endif /* !defined(ROUTERINFO_ST_H) */
diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c
index b33dca67e3..c56b714cb0 100644
--- a/src/feature/nodelist/routerlist.c
+++ b/src/feature/nodelist/routerlist.c
@@ -67,7 +67,7 @@
#include "core/mainloop/mainloop.h"
#include "core/or/policies.h"
#include "feature/client/bridges.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/authmode.h"
#include "feature/dirauth/process_descs.h"
#include "feature/dirauth/reachability.h"
@@ -1926,6 +1926,8 @@ routerlist_remove_old_routers(void)
void
routerlist_descriptors_added(smartlist_t *sl, int from_cache)
{
+ // XXXX use pubsub mechanism here.
+
tor_assert(sl);
control_event_descriptors_changed(sl);
SMARTLIST_FOREACH_BEGIN(sl, routerinfo_t *, ri) {
@@ -1933,7 +1935,9 @@ routerlist_descriptors_added(smartlist_t *sl, int from_cache)
learned_bridge_descriptor(ri, from_cache);
if (ri->needs_retest_if_added) {
ri->needs_retest_if_added = 0;
+#ifdef HAVE_MODULE_DIRAUTH
dirserv_single_reachability_test(approx_time(), ri);
+#endif
}
} SMARTLIST_FOREACH_END(ri);
}
@@ -2971,7 +2975,7 @@ routerinfo_incompatible_with_extrainfo(const crypto_pk_t *identity_pkey,
digest256_matches = tor_memeq(ei->digest256,
sd->extra_info_digest256, DIGEST256_LEN);
digest256_matches |=
- tor_mem_is_zero(sd->extra_info_digest256, DIGEST256_LEN);
+ fast_mem_is_zero(sd->extra_info_digest256, DIGEST256_LEN);
/* The identity must match exactly to have been generated at the same time
* by the same router. */
@@ -3055,7 +3059,7 @@ routerinfo_has_curve25519_onion_key(const routerinfo_t *ri)
return 0;
}
- if (tor_mem_is_zero((const char*)ri->onion_curve25519_pkey->public_key,
+ if (fast_mem_is_zero((const char*)ri->onion_curve25519_pkey->public_key,
CURVE25519_PUBKEY_LEN)) {
return 0;
}
diff --git a/src/feature/nodelist/routerlist_st.h b/src/feature/nodelist/routerlist_st.h
index 7446ead3cb..10b919a1bf 100644
--- a/src/feature/nodelist/routerlist_st.h
+++ b/src/feature/nodelist/routerlist_st.h
@@ -36,5 +36,5 @@ struct routerlist_t {
desc_store_t extrainfo_store;
};
-#endif
+#endif /* !defined(ROUTERLIST_ST_H) */
diff --git a/src/feature/nodelist/routerset.c b/src/feature/nodelist/routerset.c
index 55e2756959..e801fd81b1 100644
--- a/src/feature/nodelist/routerset.c
+++ b/src/feature/nodelist/routerset.c
@@ -378,7 +378,7 @@ routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
} else {
/* We need to iterate over the routerlist to get all the ones of the
* right kind. */
- smartlist_t *nodes = nodelist_get_list();
+ const smartlist_t *nodes = nodelist_get_list();
SMARTLIST_FOREACH(nodes, const node_t *, node, {
if (running_only && !node->is_running)
continue;
diff --git a/src/feature/nodelist/routerstatus_st.h b/src/feature/nodelist/routerstatus_st.h
index 8d91b45e11..46337c9e52 100644
--- a/src/feature/nodelist/routerstatus_st.h
+++ b/src/feature/nodelist/routerstatus_st.h
@@ -78,5 +78,5 @@ struct routerstatus_t {
};
-#endif
+#endif /* !defined(ROUTERSTATUS_ST_H) */
diff --git a/src/feature/nodelist/signed_descriptor_st.h b/src/feature/nodelist/signed_descriptor_st.h
index bdcebf184a..64c28f7440 100644
--- a/src/feature/nodelist/signed_descriptor_st.h
+++ b/src/feature/nodelist/signed_descriptor_st.h
@@ -57,5 +57,5 @@ struct signed_descriptor_t {
unsigned int send_unencrypted : 1;
};
-#endif
+#endif /* !defined(SIGNED_DESCRIPTOR_ST_H) */
diff --git a/src/feature/nodelist/torcert.c b/src/feature/nodelist/torcert.c
index b0197e9f13..270c14eb1c 100644
--- a/src/feature/nodelist/torcert.c
+++ b/src/feature/nodelist/torcert.c
@@ -74,7 +74,7 @@ tor_cert_sign_impl(const ed25519_keypair_t *signing_key,
tor_assert(real_len == alloc_len);
tor_assert(real_len > ED25519_SIG_LEN);
uint8_t *sig = encoded + (real_len - ED25519_SIG_LEN);
- tor_assert(tor_mem_is_zero((char*)sig, ED25519_SIG_LEN));
+ tor_assert(fast_mem_is_zero((char*)sig, ED25519_SIG_LEN));
ed25519_signature_t signature;
if (ed25519_sign(&signature, encoded,
@@ -290,8 +290,8 @@ tor_cert_describe_signature_status(const tor_cert_t *cert)
}
/** Return a new copy of <b>cert</b> */
-tor_cert_t *
-tor_cert_dup(const tor_cert_t *cert)
+MOCK_IMPL(tor_cert_t *,
+tor_cert_dup,(const tor_cert_t *cert))
{
tor_cert_t *newcert = tor_memdup(cert, sizeof(tor_cert_t));
if (cert->encoded)
diff --git a/src/feature/nodelist/torcert.h b/src/feature/nodelist/torcert.h
index 492275b514..03d5bdca93 100644
--- a/src/feature/nodelist/torcert.h
+++ b/src/feature/nodelist/torcert.h
@@ -71,7 +71,7 @@ int tor_cert_checksig(tor_cert_t *cert,
const ed25519_public_key_t *pubkey, time_t now);
const char *tor_cert_describe_signature_status(const tor_cert_t *cert);
-tor_cert_t *tor_cert_dup(const tor_cert_t *cert);
+MOCK_DECL(tor_cert_t *,tor_cert_dup,(const tor_cert_t *cert));
int tor_cert_eq(const tor_cert_t *cert1, const tor_cert_t *cert2);
int tor_cert_opt_eq(const tor_cert_t *cert1, const tor_cert_t *cert2);
diff --git a/src/feature/nodelist/vote_routerstatus_st.h b/src/feature/nodelist/vote_routerstatus_st.h
index 366754c166..0d909da260 100644
--- a/src/feature/nodelist/vote_routerstatus_st.h
+++ b/src/feature/nodelist/vote_routerstatus_st.h
@@ -38,4 +38,4 @@ struct vote_routerstatus_t {
uint8_t ed25519_id[ED25519_PUBKEY_LEN];
};
-#endif
+#endif /* !defined(VOTE_ROUTERSTATUS_ST_H) */
diff --git a/src/feature/relay/dns.c b/src/feature/relay/dns.c
index feaf18e764..05b97e0ae2 100644
--- a/src/feature/relay/dns.c
+++ b/src/feature/relay/dns.c
@@ -59,7 +59,7 @@
#include "core/or/connection_edge.h"
#include "core/or/policies.h"
#include "core/or/relay.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/relay/dns.h"
#include "feature/relay/router.h"
#include "feature/relay/routermode.h"
@@ -1394,7 +1394,7 @@ configured_nameserver_address(const size_t idx)
return NULL;
}
-#endif
+#endif /* defined(HAVE_EVDNS_BASE_GET_NAMESERVER_ADDR) */
/** Configure eventdns nameservers if force is true, or if the configuration
* has changed since the last time we called this function, or if we failed on
diff --git a/src/feature/relay/ext_orport.c b/src/feature/relay/ext_orport.c
index 8589efb48d..c343d19b8d 100644
--- a/src/feature/relay/ext_orport.c
+++ b/src/feature/relay/ext_orport.c
@@ -20,7 +20,7 @@
#include "core/or/or.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_or.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "app/config/config.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
@@ -659,4 +659,3 @@ ext_orport_free_all(void)
if (ext_or_auth_cookie) /* Free the auth cookie */
tor_free(ext_or_auth_cookie);
}
-
diff --git a/src/feature/relay/onion_queue.c b/src/feature/relay/onion_queue.c
index 696905cf5e..c37745cf33 100644
--- a/src/feature/relay/onion_queue.c
+++ b/src/feature/relay/onion_queue.c
@@ -212,10 +212,12 @@ num_ntors_per_tap(void)
#define MIN_NUM_NTORS_PER_TAP 1
#define MAX_NUM_NTORS_PER_TAP 100000
- return networkstatus_get_param(NULL, "NumNTorsPerTAP",
- DEFAULT_NUM_NTORS_PER_TAP,
- MIN_NUM_NTORS_PER_TAP,
- MAX_NUM_NTORS_PER_TAP);
+ int result = networkstatus_get_param(NULL, "NumNTorsPerTAP",
+ DEFAULT_NUM_NTORS_PER_TAP,
+ MIN_NUM_NTORS_PER_TAP,
+ MAX_NUM_NTORS_PER_TAP);
+ tor_assert(result > 0);
+ return result;
}
/** Choose which onion queue we'll pull from next. If one is empty choose
diff --git a/src/feature/relay/onion_queue.h b/src/feature/relay/onion_queue.h
index 0df921e057..cf478bc1a0 100644
--- a/src/feature/relay/onion_queue.h
+++ b/src/feature/relay/onion_queue.h
@@ -20,4 +20,4 @@ int onion_num_pending(uint16_t handshake_type);
void onion_pending_remove(or_circuit_t *circ);
void clear_pending_onions(void);
-#endif
+#endif /* !defined(TOR_ONION_QUEUE_H) */
diff --git a/src/feature/relay/relay_periodic.c b/src/feature/relay/relay_periodic.c
new file mode 100644
index 0000000000..b48b495895
--- /dev/null
+++ b/src/feature/relay/relay_periodic.c
@@ -0,0 +1,308 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file relay_periodic.c
+ * @brief Periodic functions for the relay subsytem
+ **/
+
+#include "orconfig.h"
+#include "core/or/or.h"
+
+#include "core/mainloop/periodic.h"
+#include "core/mainloop/cpuworker.h" // XXXX use a pubsub event.
+#include "core/mainloop/mainloop.h"
+#include "core/mainloop/netstatus.h"
+#include "core/or/circuituse.h" // XXXX move have_performed_bandwidth_test
+
+#include "feature/relay/dns.h"
+#include "feature/relay/relay_periodic.h"
+#include "feature/relay/router.h"
+#include "feature/relay/routerkeys.h"
+#include "feature/relay/routermode.h"
+#include "feature/relay/selftest.h"
+#include "feature/stats/predict_ports.h"
+
+#include "lib/crypt_ops/crypto_rand.h"
+
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/control/control_events.h"
+
+#define DECLARE_EVENT(name, roles, flags) \
+ static periodic_event_item_t name ## _event = \
+ PERIODIC_EVENT(name, \
+ PERIODIC_EVENT_ROLE_##roles, \
+ flags)
+
+#define FL(name) (PERIODIC_EVENT_FLAG_##name)
+
+/**
+ * Periodic callback: If we're a server and initializing dns failed, retry.
+ */
+static int
+retry_dns_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+#define RETRY_DNS_INTERVAL (10*60)
+ if (server_mode(options) && has_dns_init_failed())
+ dns_init();
+ return RETRY_DNS_INTERVAL;
+}
+
+DECLARE_EVENT(retry_dns, ROUTER, 0);
+
+static int dns_honesty_first_time = 1;
+
+/**
+ * Periodic event: if we're an exit, see if our DNS server is telling us
+ * obvious lies.
+ */
+static int
+check_dns_honesty_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ /* 9. and if we're an exit node, check whether our DNS is telling stories
+ * to us. */
+ if (net_is_disabled() ||
+ ! public_server_mode(options) ||
+ router_my_exit_policy_is_reject_star())
+ return PERIODIC_EVENT_NO_UPDATE;
+
+ if (dns_honesty_first_time) {
+ /* Don't launch right when we start */
+ dns_honesty_first_time = 0;
+ return crypto_rand_int_range(60, 180);
+ }
+
+ dns_launch_correctness_checks();
+ return 12*3600 + crypto_rand_int(12*3600);
+}
+
+DECLARE_EVENT(check_dns_honesty, RELAY, FL(NEED_NET));
+
+/* Periodic callback: rotate the onion keys after the period defined by the
+ * "onion-key-rotation-days" consensus parameter, shut down and restart all
+ * cpuworkers, and update our descriptor if necessary.
+ */
+static int
+rotate_onion_key_callback(time_t now, const or_options_t *options)
+{
+ if (server_mode(options)) {
+ int onion_key_lifetime = get_onion_key_lifetime();
+ time_t rotation_time = get_onion_key_set_at()+onion_key_lifetime;
+ if (rotation_time > now) {
+ return ONION_KEY_CONSENSUS_CHECK_INTERVAL;
+ }
+
+ log_info(LD_GENERAL,"Rotating onion key.");
+ rotate_onion_key();
+ cpuworkers_rotate_keyinfo();
+ if (router_rebuild_descriptor(1)<0) {
+ log_info(LD_CONFIG, "Couldn't rebuild router descriptor");
+ }
+ if (advertised_server_mode() && !net_is_disabled())
+ router_upload_dir_desc_to_dirservers(0);
+ return ONION_KEY_CONSENSUS_CHECK_INTERVAL;
+ }
+ return PERIODIC_EVENT_NO_UPDATE;
+}
+
+DECLARE_EVENT(rotate_onion_key, ROUTER, 0);
+
+/** Periodic callback: consider rebuilding or and re-uploading our descriptor
+ * (if we've passed our internal checks). */
+static int
+check_descriptor_callback(time_t now, const or_options_t *options)
+{
+/** How often do we check whether part of our router info has changed in a
+ * way that would require an upload? That includes checking whether our IP
+ * address has changed. */
+#define CHECK_DESCRIPTOR_INTERVAL (60)
+
+ (void)options;
+
+ /* 2b. Once per minute, regenerate and upload the descriptor if the old
+ * one is inaccurate. */
+ if (!net_is_disabled()) {
+ check_descriptor_bandwidth_changed(now);
+ check_descriptor_ipaddress_changed(now);
+ mark_my_descriptor_dirty_if_too_old(now);
+ consider_publishable_server(0);
+ }
+
+ return CHECK_DESCRIPTOR_INTERVAL;
+}
+
+DECLARE_EVENT(check_descriptor, ROUTER, FL(NEED_NET));
+
+static int dirport_reachability_count = 0;
+
+/**
+ * Periodic callback: check whether we're reachable (as a relay), and
+ * whether our bandwidth has changed enough that we need to
+ * publish a new descriptor.
+ */
+static int
+check_for_reachability_bw_callback(time_t now, const or_options_t *options)
+{
+ /* XXXX This whole thing was stuck in the middle of what is now
+ * XXXX check_descriptor_callback. I'm not sure it's right. */
+
+ /* also, check religiously for reachability, if it's within the first
+ * 20 minutes of our uptime. */
+ if (server_mode(options) &&
+ (have_completed_a_circuit() || !any_predicted_circuits(now)) &&
+ !net_is_disabled()) {
+ if (get_uptime() < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
+ router_do_reachability_checks(1, dirport_reachability_count==0);
+ if (++dirport_reachability_count > 5)
+ dirport_reachability_count = 0;
+ return 1;
+ } else {
+ /* If we haven't checked for 12 hours and our bandwidth estimate is
+ * low, do another bandwidth test. This is especially important for
+ * bridges, since they might go long periods without much use. */
+ const routerinfo_t *me = router_get_my_routerinfo();
+ static int first_time = 1;
+ if (!first_time && me &&
+ me->bandwidthcapacity < me->bandwidthrate &&
+ me->bandwidthcapacity < 51200) {
+ reset_bandwidth_test();
+ }
+ first_time = 0;
+#define BANDWIDTH_RECHECK_INTERVAL (12*60*60)
+ return BANDWIDTH_RECHECK_INTERVAL;
+ }
+ }
+ return CHECK_DESCRIPTOR_INTERVAL;
+}
+
+DECLARE_EVENT(check_for_reachability_bw, ROUTER, FL(NEED_NET));
+
+/**
+ * Callback: Send warnings if Tor doesn't find its ports reachable.
+ */
+static int
+reachability_warnings_callback(time_t now, const or_options_t *options)
+{
+ (void) now;
+
+ if (get_uptime() < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
+ return (int)(TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT - get_uptime());
+ }
+
+ if (server_mode(options) &&
+ !net_is_disabled() &&
+ have_completed_a_circuit()) {
+ /* every 20 minutes, check and complain if necessary */
+ const routerinfo_t *me = router_get_my_routerinfo();
+ if (me && !check_whether_orport_reachable(options)) {
+ char *address = tor_dup_ip(me->addr);
+ log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that "
+ "its ORPort is reachable. Relays do not publish descriptors "
+ "until their ORPort and DirPort are reachable. Please check "
+ "your firewalls, ports, address, /etc/hosts file, etc.",
+ address, me->or_port);
+ control_event_server_status(LOG_WARN,
+ "REACHABILITY_FAILED ORADDRESS=%s:%d",
+ address, me->or_port);
+ tor_free(address);
+ }
+
+ if (me && !check_whether_dirport_reachable(options)) {
+ char *address = tor_dup_ip(me->addr);
+ log_warn(LD_CONFIG,
+ "Your server (%s:%d) has not managed to confirm that its "
+ "DirPort is reachable. Relays do not publish descriptors "
+ "until their ORPort and DirPort are reachable. Please check "
+ "your firewalls, ports, address, /etc/hosts file, etc.",
+ address, me->dir_port);
+ control_event_server_status(LOG_WARN,
+ "REACHABILITY_FAILED DIRADDRESS=%s:%d",
+ address, me->dir_port);
+ tor_free(address);
+ }
+ }
+
+ return TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT;
+}
+
+DECLARE_EVENT(reachability_warnings, ROUTER, FL(NEED_NET));
+
+/* Periodic callback: Every 30 seconds, check whether it's time to make new
+ * Ed25519 subkeys.
+ */
+static int
+check_ed_keys_callback(time_t now, const or_options_t *options)
+{
+ if (server_mode(options)) {
+ if (should_make_new_ed_keys(options, now)) {
+ int new_signing_key = load_ed_keys(options, now);
+ if (new_signing_key < 0 ||
+ generate_ed_link_cert(options, now, new_signing_key > 0)) {
+ log_err(LD_OR, "Unable to update Ed25519 keys! Exiting.");
+ tor_shutdown_event_loop_and_exit(1);
+ }
+ }
+ return 30;
+ }
+ return PERIODIC_EVENT_NO_UPDATE;
+}
+
+DECLARE_EVENT(check_ed_keys, ROUTER, 0);
+
+/* Period callback: Check if our old onion keys are still valid after the
+ * period of time defined by the consensus parameter
+ * "onion-key-grace-period-days", otherwise expire them by setting them to
+ * NULL.
+ */
+static int
+check_onion_keys_expiry_time_callback(time_t now, const or_options_t *options)
+{
+ if (server_mode(options)) {
+ int onion_key_grace_period = get_onion_key_grace_period();
+ time_t expiry_time = get_onion_key_set_at()+onion_key_grace_period;
+ if (expiry_time > now) {
+ return ONION_KEY_CONSENSUS_CHECK_INTERVAL;
+ }
+
+ log_info(LD_GENERAL, "Expiring old onion keys.");
+ expire_old_onion_keys();
+ cpuworkers_rotate_keyinfo();
+ return ONION_KEY_CONSENSUS_CHECK_INTERVAL;
+ }
+
+ return PERIODIC_EVENT_NO_UPDATE;
+}
+
+DECLARE_EVENT(check_onion_keys_expiry_time, ROUTER, 0);
+
+void
+relay_register_periodic_events(void)
+{
+ periodic_events_register(&retry_dns_event);
+ periodic_events_register(&check_dns_honesty_event);
+ periodic_events_register(&rotate_onion_key_event);
+ periodic_events_register(&check_descriptor_event);
+ periodic_events_register(&check_for_reachability_bw_event);
+ periodic_events_register(&reachability_warnings_event);
+ periodic_events_register(&check_ed_keys_event);
+ periodic_events_register(&check_onion_keys_expiry_time_event);
+
+ dns_honesty_first_time = 1;
+ dirport_reachability_count = 0;
+}
+
+/**
+ * Update our schedule so that we'll check whether we need to update our
+ * descriptor immediately, rather than after up to CHECK_DESCRIPTOR_INTERVAL
+ * seconds.
+ */
+void
+reschedule_descriptor_update_check(void)
+{
+ periodic_event_reschedule(&check_descriptor_event);
+}
diff --git a/src/feature/relay/relay_periodic.h b/src/feature/relay/relay_periodic.h
new file mode 100644
index 0000000000..b6ea83c749
--- /dev/null
+++ b/src/feature/relay/relay_periodic.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file relay_periodic.h
+ * @brief Header for feature/relay/relay_periodic.c
+ **/
+
+#ifndef TOR_FEATURE_RELAY_RELAY_PERIODIC_H
+#define TOR_FEATURE_RELAY_RELAY_PERIODIC_H
+
+void relay_register_periodic_events(void);
+void reschedule_descriptor_update_check(void);
+
+#endif /* !defined(TOR_FEATURE_RELAY_RELAY_PERIODIC_H) */
diff --git a/src/feature/relay/relay_sys.c b/src/feature/relay/relay_sys.c
new file mode 100644
index 0000000000..106e88b2a5
--- /dev/null
+++ b/src/feature/relay/relay_sys.c
@@ -0,0 +1,48 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file relay_sys.c
+ * @brief Subsystem definitions for the relay module.
+ **/
+
+#include "orconfig.h"
+#include "core/or/or.h"
+
+#include "feature/relay/dns.h"
+#include "feature/relay/ext_orport.h"
+#include "feature/relay/onion_queue.h"
+#include "feature/relay/relay_periodic.h"
+#include "feature/relay/relay_sys.h"
+#include "feature/relay/routerkeys.h"
+#include "feature/relay/router.h"
+
+#include "lib/subsys/subsys.h"
+
+static int
+subsys_relay_initialize(void)
+{
+ relay_register_periodic_events();
+ return 0;
+}
+
+static void
+subsys_relay_shutdown(void)
+{
+ dns_free_all();
+ ext_orport_free_all();
+ clear_pending_onions();
+ routerkeys_free_all();
+ router_free_all();
+}
+
+const struct subsys_fns_t sys_relay = {
+ .name = "relay",
+ .supported = true,
+ .level = 50,
+ .initialize = subsys_relay_initialize,
+ .shutdown = subsys_relay_shutdown,
+};
diff --git a/src/feature/relay/relay_sys.h b/src/feature/relay/relay_sys.h
new file mode 100644
index 0000000000..32e21d90d8
--- /dev/null
+++ b/src/feature/relay/relay_sys.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file relay_sys.h
+ * @brief Header for feature/relay/relay_sys.c
+ **/
+
+#ifndef TOR_FEATURE_RELAY_RELAY_SYS_H
+#define TOR_FEATURE_RELAY_RELAY_SYS_H
+
+extern const struct subsys_fns_t sys_relay;
+
+#endif /* !defined(TOR_FEATURE_RELAY_RELAY_SYS_H) */
diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c
index cdd032f78d..25bb1835c2 100644
--- a/src/feature/relay/router.c
+++ b/src/feature/relay/router.c
@@ -16,7 +16,7 @@
#include "core/or/policies.h"
#include "core/or/protover.h"
#include "feature/client/transports.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/process_descs.h"
#include "feature/dircache/dirserv.h"
#include "feature/dirclient/dirclient.h"
@@ -152,6 +152,8 @@ routerinfo_err_to_string(int err)
return "Cannot generate descriptor";
case TOR_ROUTERINFO_ERROR_DESC_REBUILDING:
return "Descriptor still rebuilding - not ready yet";
+ case TOR_ROUTERINFO_ERROR_INTERNAL_BUG:
+ return "Internal bug, see logs for details";
}
log_warn(LD_BUG, "unknown routerinfo error %d - shouldn't happen", err);
@@ -194,8 +196,8 @@ set_onion_key(crypto_pk_t *k)
/** Return the current onion key. Requires that the onion key has been
* loaded or generated. */
-crypto_pk_t *
-get_onion_key(void)
+MOCK_IMPL(crypto_pk_t *,
+get_onion_key,(void))
{
tor_assert(onionkey);
return onionkey;
@@ -242,7 +244,7 @@ expire_old_onion_keys(void)
lastonionkey = NULL;
}
- /* We zero out the keypair. See the tor_mem_is_zero() check made in
+ /* We zero out the keypair. See the fast_mem_is_zero() check made in
* construct_ntor_key_map() below. */
memset(&last_curve25519_onion_key, 0, sizeof(last_curve25519_onion_key));
@@ -269,11 +271,12 @@ expire_old_onion_keys(void)
/** Return the current secret onion key for the ntor handshake. Must only
* be called from the main thread. */
-static const curve25519_keypair_t *
-get_current_curve25519_keypair(void)
+MOCK_IMPL(STATIC const struct curve25519_keypair_t *,
+get_current_curve25519_keypair,(void))
{
return &curve25519_onion_key;
}
+
/** Return a map from KEYID (the key itself) to keypairs for use in the ntor
* handshake. Must only be called from the main thread. */
di_digest256_map_t *
@@ -281,7 +284,7 @@ construct_ntor_key_map(void)
{
di_digest256_map_t *m = NULL;
- if (!tor_mem_is_zero((const char*)
+ if (!fast_mem_is_zero((const char*)
curve25519_onion_key.pubkey.public_key,
CURVE25519_PUBKEY_LEN)) {
dimap_add_entry(&m,
@@ -289,7 +292,7 @@ construct_ntor_key_map(void)
tor_memdup(&curve25519_onion_key,
sizeof(curve25519_keypair_t)));
}
- if (!tor_mem_is_zero((const char*)
+ if (!fast_mem_is_zero((const char*)
last_curve25519_onion_key.pubkey.public_key,
CURVE25519_PUBKEY_LEN)) {
dimap_add_entry(&m,
@@ -350,7 +353,7 @@ set_server_identity_key_digest_testing(const uint8_t *digest)
{
memcpy(server_identitykey_digest, digest, DIGEST_LEN);
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** Make sure that we have set up our identity keys to match or not match as
* appropriate, and die with an assertion if we have not. */
@@ -374,8 +377,8 @@ assert_identity_keys_ok(void)
/** Returns the current server identity key; requires that the key has
* been set, and that we are running as a Tor server.
*/
-crypto_pk_t *
-get_server_identity_key(void)
+MOCK_IMPL(crypto_pk_t *,
+get_server_identity_key,(void))
{
tor_assert(server_identitykey);
tor_assert(server_mode(get_options()));
@@ -1046,7 +1049,7 @@ init_keys(void)
return -1;
keydir = get_keydir_fname("secret_onion_key_ntor.old");
- if (tor_mem_is_zero((const char *)
+ if (fast_mem_is_zero((const char *)
last_curve25519_onion_key.pubkey.public_key,
CURVE25519_PUBKEY_LEN) &&
file_status(keydir) == FN_FILE) {
@@ -1941,26 +1944,33 @@ get_my_declared_family(const or_options_t *options)
return result;
}
-/** Build a fresh routerinfo, signed server descriptor, and extra-info document
- * for this OR. Set r to the generated routerinfo, e to the generated
- * extra-info document. Return 0 on success, -1 on temporary error. Failure to
- * generate an extra-info document is not an error and is indicated by setting
- * e to NULL. Caller is responsible for freeing generated documents if 0 is
- * returned.
+/** Allocate a fresh, unsigned routerinfo for this OR, without any of the
+ * fields that depend on the corresponding extrainfo.
+ *
+ * On success, set ri_out to the new routerinfo, and return 0.
+ * Caller is responsible for freeing the generated routerinfo.
+ *
+ * Returns a negative value and sets ri_out to NULL on temporary error.
*/
-int
-router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
+MOCK_IMPL(STATIC int,
+router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out))
{
- routerinfo_t *ri;
- extrainfo_t *ei;
+ routerinfo_t *ri = NULL;
uint32_t addr;
char platform[256];
int hibernating = we_are_hibernating();
const or_options_t *options = get_options();
+ int result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
+
+ if (BUG(!ri_out)) {
+ result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
+ goto err;
+ }
if (router_pick_published_address(options, &addr, 0) < 0) {
log_warn(LD_CONFIG, "Don't know my address while generating descriptor");
- return TOR_ROUTERINFO_ERROR_NO_EXT_ADDR;
+ result = TOR_ROUTERINFO_ERROR_NO_EXT_ADDR;
+ goto err;
}
/* Log a message if the address in the descriptor doesn't match the ORPort
@@ -2017,8 +2027,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key());
if (BUG(crypto_pk_get_digest(ri->identity_pkey,
ri->cache_info.identity_digest) < 0)) {
- routerinfo_free(ri);
- return TOR_ROUTERINFO_ERROR_DIGEST_FAILED;
+ result = TOR_ROUTERINFO_ERROR_DIGEST_FAILED;
+ goto err;
}
ri->cache_info.signing_key_cert =
tor_cert_dup(get_master_signing_key_cert());
@@ -2057,85 +2067,258 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
ri->declared_family = get_my_declared_family(options);
+ if (options->BridgeRelay) {
+ ri->purpose = ROUTER_PURPOSE_BRIDGE;
+ /* Bridges shouldn't be able to send their descriptors unencrypted,
+ anyway, since they don't have a DirPort, and always connect to the
+ bridge authority anonymously. But just in case they somehow think of
+ sending them on an unencrypted connection, don't allow them to try. */
+ ri->cache_info.send_unencrypted = 0;
+ } else {
+ ri->purpose = ROUTER_PURPOSE_GENERAL;
+ ri->cache_info.send_unencrypted = 1;
+ }
+
+ goto done;
+
+ err:
+ routerinfo_free(ri);
+ *ri_out = NULL;
+ return result;
+
+ done:
+ *ri_out = ri;
+ return 0;
+}
+
+/** Allocate and return a fresh, unsigned extrainfo for this OR, based on the
+ * routerinfo ri.
+ *
+ * Uses options->Nickname to set the nickname, and options->BridgeRelay to set
+ * ei->cache_info.send_unencrypted.
+ *
+ * If ri is NULL, logs a BUG() warning and returns NULL.
+ * Caller is responsible for freeing the generated extrainfo.
+ */
+static extrainfo_t *
+router_build_fresh_unsigned_extrainfo(const routerinfo_t *ri)
+{
+ extrainfo_t *ei = NULL;
+ const or_options_t *options = get_options();
+
+ if (BUG(!ri))
+ return NULL;
+
/* Now generate the extrainfo. */
ei = tor_malloc_zero(sizeof(extrainfo_t));
ei->cache_info.is_extrainfo = 1;
- strlcpy(ei->nickname, get_options()->Nickname, sizeof(ei->nickname));
+ strlcpy(ei->nickname, options->Nickname, sizeof(ei->nickname));
ei->cache_info.published_on = ri->cache_info.published_on;
ei->cache_info.signing_key_cert =
tor_cert_dup(get_master_signing_key_cert());
memcpy(ei->cache_info.identity_digest, ri->cache_info.identity_digest,
DIGEST_LEN);
+
+ if (options->BridgeRelay) {
+ /* See note in router_build_fresh_routerinfo(). */
+ ei->cache_info.send_unencrypted = 0;
+ } else {
+ ei->cache_info.send_unencrypted = 1;
+ }
+
+ return ei;
+}
+
+/** Dump the extrainfo descriptor body for ei, sign it, and add the body and
+ * signature to ei->cache_info. Note that the extrainfo body is determined by
+ * ei, and some additional config and statistics state: see
+ * extrainfo_dump_to_string() for details.
+ *
+ * Return 0 on success, -1 on temporary error.
+ * If ei is NULL, logs a BUG() warning and returns -1.
+ * On error, ei->cache_info is not modified.
+ */
+static int
+router_dump_and_sign_extrainfo_descriptor_body(extrainfo_t *ei)
+{
+ if (BUG(!ei))
+ return -1;
+
if (extrainfo_dump_to_string(&ei->cache_info.signed_descriptor_body,
ei, get_server_identity_key(),
get_master_signing_keypair()) < 0) {
log_warn(LD_BUG, "Couldn't generate extra-info descriptor.");
- extrainfo_free(ei);
- ei = NULL;
- } else {
- ei->cache_info.signed_descriptor_len =
- strlen(ei->cache_info.signed_descriptor_body);
- router_get_extrainfo_hash(ei->cache_info.signed_descriptor_body,
- ei->cache_info.signed_descriptor_len,
- ei->cache_info.signed_descriptor_digest);
- crypto_digest256((char*) ei->digest256,
- ei->cache_info.signed_descriptor_body,
- ei->cache_info.signed_descriptor_len,
- DIGEST_SHA256);
+ return -1;
}
- /* Now finish the router descriptor. */
- if (ei) {
- memcpy(ri->cache_info.extra_info_digest,
- ei->cache_info.signed_descriptor_digest,
- DIGEST_LEN);
- memcpy(ri->cache_info.extra_info_digest256,
- ei->digest256,
- DIGEST256_LEN);
- } else {
- /* ri was allocated with tor_malloc_zero, so there is no need to
- * zero ri->cache_info.extra_info_digest here. */
+ ei->cache_info.signed_descriptor_len =
+ strlen(ei->cache_info.signed_descriptor_body);
+
+ router_get_extrainfo_hash(ei->cache_info.signed_descriptor_body,
+ ei->cache_info.signed_descriptor_len,
+ ei->cache_info.signed_descriptor_digest);
+ crypto_digest256((char*) ei->digest256,
+ ei->cache_info.signed_descriptor_body,
+ ei->cache_info.signed_descriptor_len,
+ DIGEST_SHA256);
+
+ return 0;
+}
+
+/** Allocate and return a fresh, signed extrainfo for this OR, based on the
+ * routerinfo ri.
+ *
+ * If ri is NULL, logs a BUG() warning and returns NULL.
+ * Caller is responsible for freeing the generated extrainfo.
+ */
+STATIC extrainfo_t *
+router_build_fresh_signed_extrainfo(const routerinfo_t *ri)
+{
+ int result = -1;
+ extrainfo_t *ei = NULL;
+
+ if (BUG(!ri))
+ return NULL;
+
+ ei = router_build_fresh_unsigned_extrainfo(ri);
+ /* router_build_fresh_unsigned_extrainfo() should not fail. */
+ if (BUG(!ei))
+ goto err;
+
+ result = router_dump_and_sign_extrainfo_descriptor_body(ei);
+ if (result < 0)
+ goto err;
+
+ goto done;
+
+ err:
+ extrainfo_free(ei);
+ return NULL;
+
+ done:
+ return ei;
+}
+
+/** Set the fields in ri that depend on ei.
+ *
+ * If ei is NULL, logs a BUG() warning and zeroes the relevant fields.
+ */
+STATIC void
+router_update_routerinfo_from_extrainfo(routerinfo_t *ri,
+ const extrainfo_t *ei)
+{
+ if (BUG(!ei)) {
+ /* Just to be safe, zero ri->cache_info.extra_info_digest here. */
+ memset(ri->cache_info.extra_info_digest, 0, DIGEST_LEN);
+ memset(ri->cache_info.extra_info_digest256, 0, DIGEST256_LEN);
+ return;
}
+
+ /* Now finish the router descriptor. */
+ memcpy(ri->cache_info.extra_info_digest,
+ ei->cache_info.signed_descriptor_digest,
+ DIGEST_LEN);
+ memcpy(ri->cache_info.extra_info_digest256,
+ ei->digest256,
+ DIGEST256_LEN);
+}
+
+/** Dump the descriptor body for ri, sign it, and add the body and signature to
+ * ri->cache_info. Note that the descriptor body is determined by ri, and some
+ * additional config and state: see router_dump_router_to_string() for details.
+ *
+ * Return 0 on success, and a negative value on temporary error.
+ * If ri is NULL, logs a BUG() warning and returns a negative value.
+ * On error, ri->cache_info is not modified.
+ */
+STATIC int
+router_dump_and_sign_routerinfo_descriptor_body(routerinfo_t *ri)
+{
+ if (BUG(!ri))
+ return TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
+
if (! (ri->cache_info.signed_descriptor_body =
router_dump_router_to_string(ri, get_server_identity_key(),
get_onion_key(),
get_current_curve25519_keypair(),
get_master_signing_keypair())) ) {
log_warn(LD_BUG, "Couldn't generate router descriptor.");
- routerinfo_free(ri);
- extrainfo_free(ei);
return TOR_ROUTERINFO_ERROR_CANNOT_GENERATE;
}
+
ri->cache_info.signed_descriptor_len =
strlen(ri->cache_info.signed_descriptor_body);
- ri->purpose =
- options->BridgeRelay ? ROUTER_PURPOSE_BRIDGE : ROUTER_PURPOSE_GENERAL;
- if (options->BridgeRelay) {
- /* Bridges shouldn't be able to send their descriptors unencrypted,
- anyway, since they don't have a DirPort, and always connect to the
- bridge authority anonymously. But just in case they somehow think of
- sending them on an unencrypted connection, don't allow them to try. */
- ri->cache_info.send_unencrypted = 0;
- if (ei)
- ei->cache_info.send_unencrypted = 0;
- } else {
- ri->cache_info.send_unencrypted = 1;
- if (ei)
- ei->cache_info.send_unencrypted = 1;
- }
-
router_get_router_hash(ri->cache_info.signed_descriptor_body,
strlen(ri->cache_info.signed_descriptor_body),
ri->cache_info.signed_descriptor_digest);
+ return 0;
+}
+
+/** Build a fresh routerinfo, signed server descriptor, and signed extrainfo
+ * document for this OR.
+ *
+ * Set r to the generated routerinfo, e to the generated extrainfo document.
+ * Failure to generate an extra-info document is not an error and is indicated
+ * by setting e to NULL.
+ * Return 0 on success, and a negative value on temporary error.
+ * Caller is responsible for freeing generated documents on success.
+ */
+int
+router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
+{
+ int result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
+ routerinfo_t *ri = NULL;
+ extrainfo_t *ei = NULL;
+
+ if (BUG(!r))
+ goto err;
+
+ if (BUG(!e))
+ goto err;
+
+ result = router_build_fresh_unsigned_routerinfo(&ri);
+ if (result < 0) {
+ goto err;
+ }
+ /* If ri is NULL, then result should be negative. So this check should be
+ * unreachable. */
+ if (BUG(!ri)) {
+ result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
+ goto err;
+ }
+
+ ei = router_build_fresh_signed_extrainfo(ri);
+
+ /* Failing to create an ei is not an error. */
+ if (ei) {
+ router_update_routerinfo_from_extrainfo(ri, ei);
+ }
+
+ result = router_dump_and_sign_routerinfo_descriptor_body(ri);
+ if (result < 0)
+ goto err;
+
if (ei) {
- tor_assert(!
- routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei,
- &ri->cache_info, NULL));
+ if (BUG(routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei,
+ &ri->cache_info, NULL))) {
+ result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
+ goto err;
+ }
}
+ goto done;
+
+ err:
+ routerinfo_free(ri);
+ extrainfo_free(ei);
+ *r = NULL;
+ *e = NULL;
+ return result;
+
+ done:
*r = ri;
*e = ei;
return 0;
@@ -2478,6 +2661,10 @@ get_platform_str(char *platform, size_t len)
/** OR only: Given a routerinfo for this router, and an identity key to sign
* with, encode the routerinfo as a signed server descriptor and return a new
* string encoding the result, or NULL on failure.
+ *
+ * In addition to the fields in router, this function calls
+ * onion_key_lifetime(), get_options(), and we_are_hibernating(), and uses the
+ * results to populate some fields in the descriptor.
*/
char *
router_dump_router_to_string(routerinfo_t *router,
@@ -2541,11 +2728,8 @@ router_dump_router_to_string(routerinfo_t *router,
log_err(LD_BUG,"Couldn't base64-encode signing key certificate!");
goto err;
}
- if (ed25519_public_to_base64(ed_fp_base64,
- &router->cache_info.signing_key_cert->signing_key)<0) {
- log_err(LD_BUG,"Couldn't base64-encode identity key\n");
- goto err;
- }
+ ed25519_public_to_base64(ed_fp_base64,
+ &router->cache_info.signing_key_cert->signing_key);
tor_asprintf(&ed_cert_line, "identity-ed25519\n"
"-----BEGIN ED25519 CERT-----\n"
"%s"
@@ -2790,8 +2974,7 @@ router_dump_router_to_string(routerinfo_t *router,
if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN,
signing_keypair) < 0)
goto err;
- if (ed25519_signature_to_base64(buf, &sig) < 0)
- goto err;
+ ed25519_signature_to_base64(buf, &sig);
smartlist_add_asprintf(chunks, "%s\n", buf);
}
@@ -2930,9 +3113,14 @@ load_stats_file(const char *filename, const char *end_line, time_t now,
return r;
}
-/** Write the contents of <b>extrainfo</b> and aggregated statistics to
- * *<b>s_out</b>, signing them with <b>ident_key</b>. Return 0 on
- * success, negative on failure. */
+/** Write the contents of <b>extrainfo</b>, to * *<b>s_out</b>, signing them
+ * with <b>ident_key</b>.
+ *
+ * If ExtraInfoStatistics is 1, also write aggregated statistics and related
+ * configuration data before signing. Most statistics also have an option that
+ * enables or disables that particular statistic.
+ *
+ * Return 0 on success, negative on failure. */
int
extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
crypto_pk_t *ident_key,
@@ -2942,7 +3130,6 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
char identity[HEX_DIGEST_LEN+1];
char published[ISO_TIME_LEN+1];
char digest[DIGEST_LEN];
- char *bandwidth_usage;
int result;
static int write_stats_to_extrainfo = 1;
char sig[DIROBJ_MAX_SIG_LEN+1];
@@ -2957,7 +3144,6 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
base16_encode(identity, sizeof(identity),
extrainfo->cache_info.identity_digest, DIGEST_LEN);
format_iso_time(published, extrainfo->cache_info.published_on);
- bandwidth_usage = rep_hist_get_bandwidth_lines();
if (emit_ed_sigs) {
if (!extrainfo->cache_info.signing_key_cert->signing_key_included ||
!ed25519_pubkey_eq(&extrainfo->cache_info.signing_key_cert->signed_key,
@@ -2983,21 +3169,35 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
ed_cert_line = tor_strdup("");
}
- tor_asprintf(&pre, "extra-info %s %s\n%spublished %s\n%s",
+ tor_asprintf(&pre, "extra-info %s %s\n%spublished %s\n",
extrainfo->nickname, identity,
ed_cert_line,
- published, bandwidth_usage);
+ published);
smartlist_add(chunks, pre);
- if (geoip_is_loaded(AF_INET))
- smartlist_add_asprintf(chunks, "geoip-db-digest %s\n",
- geoip_db_digest(AF_INET));
- if (geoip_is_loaded(AF_INET6))
- smartlist_add_asprintf(chunks, "geoip6-db-digest %s\n",
- geoip_db_digest(AF_INET6));
+ /* Add information about the pluggable transports we support, even if we
+ * are not publishing statistics. This information is needed by BridgeDB
+ * to distribute bridges. */
+ if (options->ServerTransportPlugin) {
+ char *pluggable_transports = pt_get_extra_info_descriptor_string();
+ if (pluggable_transports)
+ smartlist_add(chunks, pluggable_transports);
+ }
if (options->ExtraInfoStatistics && write_stats_to_extrainfo) {
log_info(LD_GENERAL, "Adding stats to extra-info descriptor.");
+ /* Bandwidth usage stats don't have their own option */
+ {
+ contents = rep_hist_get_bandwidth_lines();
+ smartlist_add(chunks, contents);
+ }
+ /* geoip hashes aren't useful unless we are publishing other stats */
+ if (geoip_is_loaded(AF_INET))
+ smartlist_add_asprintf(chunks, "geoip-db-digest %s\n",
+ geoip_db_digest(AF_INET));
+ if (geoip_is_loaded(AF_INET6))
+ smartlist_add_asprintf(chunks, "geoip6-db-digest %s\n",
+ geoip_db_digest(AF_INET6));
if (options->DirReqStatistics &&
load_stats_file("stats"PATH_SEPARATOR"dirreq-stats",
"dirreq-stats-end", now, &contents) > 0) {
@@ -3033,19 +3233,12 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
if (contents)
smartlist_add(chunks, contents);
}
- }
-
- /* Add information about the pluggable transports we support. */
- if (options->ServerTransportPlugin) {
- char *pluggable_transports = pt_get_extra_info_descriptor_string();
- if (pluggable_transports)
- smartlist_add(chunks, pluggable_transports);
- }
-
- if (should_record_bridge_info(options) && write_stats_to_extrainfo) {
- const char *bridge_stats = geoip_get_bridge_stats_extrainfo(now);
- if (bridge_stats) {
- smartlist_add_strdup(chunks, bridge_stats);
+ /* bridge statistics */
+ if (should_record_bridge_info(options)) {
+ const char *bridge_stats = geoip_get_bridge_stats_extrainfo(now);
+ if (bridge_stats) {
+ smartlist_add_strdup(chunks, bridge_stats);
+ }
}
}
@@ -3060,8 +3253,7 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
if (ed25519_sign(&ed_sig, (const uint8_t*)sha256_digest, DIGEST256_LEN,
signing_keypair) < 0)
goto err;
- if (ed25519_signature_to_base64(buf, &ed_sig) < 0)
- goto err;
+ ed25519_signature_to_base64(buf, &ed_sig);
smartlist_add_asprintf(chunks, "%s\n", buf);
}
@@ -3139,7 +3331,6 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
tor_free(s_dup);
tor_free(ed_cert_line);
extrainfo_free(ei_tmp);
- tor_free(bandwidth_usage);
return result;
}
diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h
index 60bc857ceb..55b9ef9e68 100644
--- a/src/feature/relay/router.h
+++ b/src/feature/relay/router.h
@@ -23,11 +23,12 @@ struct ed25519_keypair_t;
#define TOR_ROUTERINFO_ERROR_DIGEST_FAILED (-4)
#define TOR_ROUTERINFO_ERROR_CANNOT_GENERATE (-5)
#define TOR_ROUTERINFO_ERROR_DESC_REBUILDING (-6)
+#define TOR_ROUTERINFO_ERROR_INTERNAL_BUG (-7)
-crypto_pk_t *get_onion_key(void);
+MOCK_DECL(crypto_pk_t *,get_onion_key,(void));
time_t get_onion_key_set_at(void);
void set_server_identity_key(crypto_pk_t *k);
-crypto_pk_t *get_server_identity_key(void);
+MOCK_DECL(crypto_pk_t *,get_server_identity_key,(void));
int server_identity_key_is_set(void);
void set_client_identity_key(crypto_pk_t *k);
crypto_pk_t *get_tlsclient_identity_key(void);
@@ -114,7 +115,7 @@ void router_reset_reachability(void);
void router_free_all(void);
#ifdef ROUTER_PRIVATE
-/* Used only by router.c and test.c */
+/* Used only by router.c and the unit tests */
STATIC void get_platform_str(char *platform, size_t len);
STATIC int router_write_fingerprint(int hashed);
STATIC smartlist_t *get_my_declared_family(const or_options_t *options);
@@ -123,8 +124,18 @@ STATIC smartlist_t *get_my_declared_family(const or_options_t *options);
extern time_t desc_clean_since;
extern const char *desc_dirty_reason;
void set_server_identity_key_digest_testing(const uint8_t *digest);
-#endif
-
-#endif
+MOCK_DECL(STATIC const struct curve25519_keypair_t *,
+ get_current_curve25519_keypair,(void));
+
+MOCK_DECL(STATIC int,
+ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out));
+STATIC extrainfo_t *router_build_fresh_signed_extrainfo(
+ const routerinfo_t *ri);
+STATIC void router_update_routerinfo_from_extrainfo(routerinfo_t *ri,
+ const extrainfo_t *ei);
+STATIC int router_dump_and_sign_routerinfo_descriptor_body(routerinfo_t *ri);
+#endif /* defined(TOR_UNIT_TESTS) */
+
+#endif /* defined(ROUTER_PRIVATE) */
#endif /* !defined(TOR_ROUTER_H) */
diff --git a/src/feature/relay/routerkeys.c b/src/feature/relay/routerkeys.c
index f639fc91e7..a9190b2e13 100644
--- a/src/feature/relay/routerkeys.c
+++ b/src/feature/relay/routerkeys.c
@@ -226,7 +226,7 @@ load_ed_keys(const or_options_t *options, time_t now)
tor_free(fname);
}
}
- if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey)))
+ if (safe_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey)))
sign_signing_key_with_id = NULL;
else
sign_signing_key_with_id = id;
@@ -631,14 +631,14 @@ get_master_identity_keypair(void)
}
#endif /* defined(TOR_UNIT_TESTS) */
-const ed25519_keypair_t *
-get_master_signing_keypair(void)
+MOCK_IMPL(const ed25519_keypair_t *,
+get_master_signing_keypair,(void))
{
return master_signing_key;
}
-const struct tor_cert_st *
-get_master_signing_key_cert(void)
+MOCK_IMPL(const struct tor_cert_st *,
+get_master_signing_key_cert,(void))
{
return signing_key_cert;
}
@@ -706,6 +706,8 @@ make_tap_onion_key_crosscert(const crypto_pk_t *onion_key,
*len_out = 0;
if (crypto_pk_get_digest(rsa_id_key, (char*)signed_data) < 0) {
+ log_info(LD_OR, "crypto_pk_get_digest failed in "
+ "make_tap_onion_key_crosscert!");
return NULL;
}
memcpy(signed_data + DIGEST_LEN, master_id_key->pubkey, ED25519_PUBKEY_LEN);
@@ -713,8 +715,12 @@ make_tap_onion_key_crosscert(const crypto_pk_t *onion_key,
int r = crypto_pk_private_sign(onion_key,
(char*)signature, sizeof(signature),
(const char*)signed_data, sizeof(signed_data));
- if (r < 0)
+ if (r < 0) {
+ /* It's probably missing the private key */
+ log_info(LD_OR, "crypto_pk_private_sign failed in "
+ "make_tap_onion_key_crosscert!");
return NULL;
+ }
*len_out = r;
diff --git a/src/feature/relay/routerkeys.h b/src/feature/relay/routerkeys.h
index 0badd34191..cde07b52c3 100644
--- a/src/feature/relay/routerkeys.h
+++ b/src/feature/relay/routerkeys.h
@@ -7,8 +7,8 @@
#include "lib/crypt_ops/crypto_ed25519.h"
const ed25519_public_key_t *get_master_identity_key(void);
-const ed25519_keypair_t *get_master_signing_keypair(void);
-const struct tor_cert_st *get_master_signing_key_cert(void);
+MOCK_DECL(const ed25519_keypair_t *, get_master_signing_keypair,(void));
+MOCK_DECL(const struct tor_cert_st *, get_master_signing_key_cert,(void));
const ed25519_keypair_t *get_current_auth_keypair(void);
const struct tor_cert_st *get_current_link_cert_cert(void);
diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c
index 064eea6c46..f8b54ff45d 100644
--- a/src/feature/relay/selftest.c
+++ b/src/feature/relay/selftest.c
@@ -26,7 +26,7 @@
#include "core/or/crypt_path_st.h"
#include "core/or/origin_circuit_st.h"
#include "core/or/relay.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirclient/dirclient.h"
#include "feature/dircommon/directory.h"
#include "feature/nodelist/authority_cert_st.h"
@@ -35,6 +35,7 @@
#include "feature/nodelist/routerlist.h" // but...
#include "feature/nodelist/routerset.h"
#include "feature/nodelist/torcert.h"
+#include "feature/relay/relay_periodic.h"
#include "feature/relay/router.h"
#include "feature/relay/selftest.h"
diff --git a/src/feature/relay/selftest.h b/src/feature/relay/selftest.h
index a80ec8936e..aea77ec791 100644
--- a/src/feature/relay/selftest.h
+++ b/src/feature/relay/selftest.h
@@ -21,4 +21,4 @@ void router_orport_found_reachable(void);
void router_dirport_found_reachable(void);
void router_perform_bandwidth_test(int num_circs, time_t now);
-#endif
+#endif /* !defined(TOR_SELFTEST_H) */
diff --git a/src/feature/rend/rend_authorized_client_st.h b/src/feature/rend/rend_authorized_client_st.h
index 7bd4f2fe8c..51a1798fcb 100644
--- a/src/feature/rend/rend_authorized_client_st.h
+++ b/src/feature/rend/rend_authorized_client_st.h
@@ -14,5 +14,5 @@ struct rend_authorized_client_t {
crypto_pk_t *client_key;
};
-#endif
+#endif /* !defined(REND_AUTHORIZED_CLIENT_ST_H) */
diff --git a/src/feature/rend/rend_encoded_v2_service_descriptor_st.h b/src/feature/rend/rend_encoded_v2_service_descriptor_st.h
index 05ff145d53..bd8a60f0d9 100644
--- a/src/feature/rend/rend_encoded_v2_service_descriptor_st.h
+++ b/src/feature/rend/rend_encoded_v2_service_descriptor_st.h
@@ -13,5 +13,5 @@ struct rend_encoded_v2_service_descriptor_t {
char *desc_str; /**< Descriptor string. */
};
-#endif
+#endif /* !defined(REND_ENCODED_V2_SERVICE_DESCRIPTOR_ST_H) */
diff --git a/src/feature/rend/rend_intro_point_st.h b/src/feature/rend/rend_intro_point_st.h
index de6987e569..4882b62752 100644
--- a/src/feature/rend/rend_intro_point_st.h
+++ b/src/feature/rend/rend_intro_point_st.h
@@ -73,4 +73,4 @@ struct rend_intro_point_t {
unsigned int circuit_established:1;
};
-#endif
+#endif /* !defined(REND_INTRO_POINT_ST_H) */
diff --git a/src/feature/rend/rend_service_descriptor_st.h b/src/feature/rend/rend_service_descriptor_st.h
index aeb3178064..ff7627ce96 100644
--- a/src/feature/rend/rend_service_descriptor_st.h
+++ b/src/feature/rend/rend_service_descriptor_st.h
@@ -30,5 +30,5 @@ struct rend_service_descriptor_t {
smartlist_t *successful_uploads;
};
-#endif
+#endif /* !defined(REND_SERVICE_DESCRIPTOR_ST_H) */
diff --git a/src/feature/rend/rendcache.c b/src/feature/rend/rendcache.c
index fadfb43883..c3f86d8c82 100644
--- a/src/feature/rend/rendcache.c
+++ b/src/feature/rend/rendcache.c
@@ -19,6 +19,8 @@
#include "feature/rend/rend_intro_point_st.h"
#include "feature/rend/rend_service_descriptor_st.h"
+#include "lib/ctime/di_ops.h"
+
/** Map from service id (as generated by rend_get_service_id) to
* rend_cache_entry_t. */
STATIC strmap_t *rend_cache = NULL;
@@ -593,10 +595,10 @@ rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc)
char desc_id_digest[DIGEST_LEN];
tor_assert(rend_cache_v2_dir);
if (base32_decode(desc_id_digest, DIGEST_LEN,
- desc_id, REND_DESC_ID_V2_LEN_BASE32) < 0) {
+ desc_id, REND_DESC_ID_V2_LEN_BASE32) != DIGEST_LEN) {
log_fn(LOG_PROTOCOL_WARN, LD_REND,
"Rejecting v2 rendezvous descriptor request -- descriptor ID "
- "contains illegal characters: %s",
+ "has wrong length or illegal characters: %s",
safe_str(desc_id));
return -1;
}
@@ -854,7 +856,8 @@ rend_cache_store_v2_desc_as_client(const char *desc,
*entry = NULL;
}
if (base32_decode(want_desc_id, sizeof(want_desc_id),
- desc_id_base32, strlen(desc_id_base32)) != 0) {
+ desc_id_base32, strlen(desc_id_base32)) !=
+ sizeof(want_desc_id)) {
log_warn(LD_BUG, "Couldn't decode base32 %s for descriptor id.",
escaped_safe_str_client(desc_id_base32));
goto err;
@@ -888,8 +891,8 @@ rend_cache_store_v2_desc_as_client(const char *desc,
if (intro_content && intro_size > 0) {
int n_intro_points;
if (rend_data->auth_type != REND_NO_AUTH &&
- !tor_mem_is_zero(rend_data->descriptor_cookie,
- sizeof(rend_data->descriptor_cookie))) {
+ !safe_mem_is_zero(rend_data->descriptor_cookie,
+ sizeof(rend_data->descriptor_cookie))) {
char *ipos_decrypted = NULL;
size_t ipos_decrypted_size;
if (rend_decrypt_introduction_points(&ipos_decrypted,
@@ -1005,4 +1008,3 @@ rend_cache_store_v2_desc_as_client(const char *desc,
tor_free(intro_content);
return retval;
}
-
diff --git a/src/feature/rend/rendclient.c b/src/feature/rend/rendclient.c
index 4ca783c7c3..5bdd4d453e 100644
--- a/src/feature/rend/rendclient.c
+++ b/src/feature/rend/rendclient.c
@@ -17,7 +17,7 @@
#include "core/or/connection_edge.h"
#include "core/or/relay.h"
#include "feature/client/circpathbias.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirclient/dirclient.h"
#include "feature/dircommon/directory.h"
#include "feature/hs/hs_circuit.h"
@@ -403,14 +403,23 @@ rend_client_introduction_acked(origin_circuit_t *circ,
} else {
log_info(LD_REND,"...Found no rend circ. Dropping on the floor.");
}
+ /* Save the rend data digest to a temporary object so that we don't access
+ * it after we mark the circuit for close. */
+ const uint8_t *rend_digest_tmp = NULL;
+ size_t digest_len;
+ uint8_t *cached_rend_digest = NULL;
+ rend_digest_tmp = rend_data_get_pk_digest(circ->rend_data, &digest_len);
+ cached_rend_digest = tor_malloc_zero(digest_len);
+ memcpy(cached_rend_digest, rend_digest_tmp, digest_len);
+
/* close the circuit: we won't need it anymore. */
circuit_change_purpose(TO_CIRCUIT(circ),
CIRCUIT_PURPOSE_C_INTRODUCE_ACKED);
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
/* close any other intros launched in parallel */
- rend_client_close_other_intros(rend_data_get_pk_digest(circ->rend_data,
- NULL));
+ rend_client_close_other_intros(cached_rend_digest);
+ tor_free(cached_rend_digest); /* free the temporary digest */
} else {
/* It's a NAK; the introduction point didn't relay our request. */
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_INTRODUCING);
@@ -469,16 +478,19 @@ directory_get_from_hs_dir(const char *desc_id,
/* Automatically pick an hs dir if none given. */
if (!rs_hsdir) {
+ bool rate_limited = false;
+
/* Determine responsible dirs. Even if we can't get all we want, work with
* the ones we have. If it's empty, we'll notice in hs_pick_hsdir(). */
smartlist_t *responsible_dirs = smartlist_new();
hid_serv_get_responsible_directories(responsible_dirs, desc_id);
- hs_dir = hs_pick_hsdir(responsible_dirs, desc_id_base32);
+ hs_dir = hs_pick_hsdir(responsible_dirs, desc_id_base32, &rate_limited);
if (!hs_dir) {
/* No suitable hs dir can be found, stop right now. */
- control_event_hsv2_descriptor_failed(rend_query, NULL,
- "QUERY_NO_HSDIR");
+ const char *query_response = (rate_limited) ? "QUERY_RATE_LIMITED" :
+ "QUERY_NO_HSDIR";
+ control_event_hsv2_descriptor_failed(rend_query, NULL, query_response);
control_event_hs_descriptor_content(rend_data_get_address(rend_query),
desc_id_base32, NULL, NULL);
return 0;
diff --git a/src/feature/rend/rendcommon.c b/src/feature/rend/rendcommon.c
index de48af795f..777de2984c 100644
--- a/src/feature/rend/rendcommon.c
+++ b/src/feature/rend/rendcommon.c
@@ -15,7 +15,7 @@
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
#include "app/config/config.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "feature/hs/hs_client.h"
@@ -171,9 +171,10 @@ rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
}
/* Convert service ID to binary. */
if (base32_decode(service_id_binary, REND_SERVICE_ID_LEN,
- service_id, REND_SERVICE_ID_LEN_BASE32) < 0) {
+ service_id, REND_SERVICE_ID_LEN_BASE32) !=
+ REND_SERVICE_ID_LEN) {
log_warn(LD_REND, "Could not compute v2 descriptor ID: "
- "Illegal characters in service ID: %s",
+ "Illegal characters or wrong length for service ID: %s",
safe_str_client(service_id));
return -1;
}
diff --git a/src/feature/rend/rendmid.c b/src/feature/rend/rendmid.c
index 849f355990..421e0f2139 100644
--- a/src/feature/rend/rendmid.c
+++ b/src/feature/rend/rendmid.c
@@ -70,7 +70,8 @@ rend_mid_establish_intro_legacy(or_circuit_t *circ, const uint8_t *request,
goto err;
}
if (tor_memneq(expected_digest, request+2+asn1len, DIGEST_LEN)) {
- log_warn(LD_PROTOCOL, "Hash of session info was not as expected.");
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Hash of session info was not as expected.");
reason = END_CIRC_REASON_TORPROTOCOL;
goto err;
}
diff --git a/src/feature/rend/rendparse.c b/src/feature/rend/rendparse.c
index abd0feb448..a98cb3ad88 100644
--- a/src/feature/rend/rendparse.c
+++ b/src/feature/rend/rendparse.c
@@ -143,8 +143,9 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
goto err;
}
if (base32_decode(desc_id_out, DIGEST_LEN,
- tok->args[0], REND_DESC_ID_V2_LEN_BASE32) < 0) {
- log_warn(LD_REND, "Descriptor ID contains illegal characters: %s",
+ tok->args[0], REND_DESC_ID_V2_LEN_BASE32) != DIGEST_LEN) {
+ log_warn(LD_REND,
+ "Descriptor ID has wrong length or illegal characters: %s",
tok->args[0]);
goto err;
}
@@ -174,8 +175,10 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]);
goto err;
}
- if (base32_decode(secret_id_part, DIGEST_LEN, tok->args[0], 32) < 0) {
- log_warn(LD_REND, "Secret ID part contains illegal characters: %s",
+ if (base32_decode(secret_id_part, DIGEST_LEN, tok->args[0], 32) !=
+ DIGEST_LEN) {
+ log_warn(LD_REND,
+ "Secret ID part has wrong length or illegal characters: %s",
tok->args[0]);
goto err;
}
@@ -429,8 +432,10 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed,
/* Parse identifier. */
tok = find_by_keyword(tokens, R_IPO_IDENTIFIER);
if (base32_decode(info->identity_digest, DIGEST_LEN,
- tok->args[0], REND_INTRO_POINT_ID_LEN_BASE32) < 0) {
- log_warn(LD_REND, "Identity digest contains illegal characters: %s",
+ tok->args[0], REND_INTRO_POINT_ID_LEN_BASE32) !=
+ DIGEST_LEN) {
+ log_warn(LD_REND,
+ "Identity digest has wrong length or illegal characters: %s",
tok->args[0]);
rend_intro_point_free(intro);
goto err;
diff --git a/src/feature/rend/rendparse.h b/src/feature/rend/rendparse.h
index 0cef931e90..b1ccce9b6c 100644
--- a/src/feature/rend/rendparse.h
+++ b/src/feature/rend/rendparse.h
@@ -29,4 +29,4 @@ 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);
-#endif
+#endif /* !defined(TOR_REND_PARSE_H) */
diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c
index 5ee084b0b7..119a6f9c89 100644
--- a/src/feature/rend/rendservice.c
+++ b/src/feature/rend/rendservice.c
@@ -18,8 +18,9 @@
#include "core/or/circuituse.h"
#include "core/or/policies.h"
#include "core/or/relay.h"
+#include "core/or/crypt_path.h"
#include "feature/client/circpathbias.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dirclient/dirclient.h"
#include "feature/dircommon/directory.h"
#include "feature/hs/hs_common.h"
@@ -2122,8 +2123,12 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
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)) {
+ * firewall rules permit direct connections to the address.
+ *
+ * We only use a one-hop path on the first attempt. If the first attempt
+ * fails, we use a 3-hop path for reachability / reliability.
+ * See the comment in rend_service_relauch_rendezvous() for details. */
+ if (rend_service_use_direct_connection(options, rp) && i == 0) {
flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL;
}
launched = circuit_launch_by_extend_info(
@@ -2163,7 +2168,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
cpath->rend_dh_handshake_state = dh;
dh = NULL;
- if (circuit_init_cpath_crypto(cpath,
+ if (cpath_init_circuit_crypto(cpath,
keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN,
1, 0)<0)
goto err;
@@ -3012,6 +3017,10 @@ rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
{
origin_circuit_t *newcirc;
cpath_build_state_t *newstate, *oldstate;
+ const char *rend_pk_digest;
+ rend_service_t *service = NULL;
+
+ int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
tor_assert(oldcirc->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
oldstate = oldcirc->build_state;
@@ -3026,13 +3035,31 @@ 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)));
+ /* Look up the service. */
+ rend_pk_digest = (char *) rend_data_get_pk_digest(oldcirc->rend_data, NULL);
+ service = rend_service_get_by_pk_digest(rend_pk_digest);
+
+ if (!service) {
+ char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
+ base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
+ rend_pk_digest, REND_SERVICE_ID_LEN);
+
+ log_warn(LD_BUG, "Internal error: Trying to relaunch a rendezvous circ "
+ "for an unrecognized service %s.",
+ safe_str_client(serviceid));
+ return;
+ }
+
+ if (hs_service_requires_uptime_circ(service->ports)) {
+ flags |= CIRCLAUNCH_NEED_UPTIME;
+ }
+
/* 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);
+ oldstate->chosen_exit, flags);
if (!newcirc) {
log_warn(LD_REND,"Couldn't relaunch rendezvous circuit to '%s'.",
@@ -3063,8 +3090,15 @@ rend_service_launch_establish_intro(rend_service_t *service,
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)) {
+ /* Are we in single onion mode?
+ *
+ * We only use a one-hop path on the first attempt. If the first attempt
+ * fails, we use a 3-hop path for reachability / reliability.
+ * (Unlike v3, retries is incremented by the caller after it calls this
+ * function.)
+ */
+ if (rend_service_allow_non_anonymous_connection(options) &&
+ intro->circuit_retries == 0) {
/* 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
@@ -3525,7 +3559,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
hop->package_window = circuit_initial_package_window();
hop->deliver_window = CIRCWINDOW_START;
- onion_append_to_cpath(&circuit->cpath, hop);
+ cpath_extend_linked_list(&circuit->cpath, hop);
circuit->build_state->pending_final_cpath = NULL; /* prevent double-free */
/* Change the circuit purpose. */
@@ -4205,6 +4239,7 @@ rend_consider_services_intro_points(time_t now)
* directly ourselves. */
intro->extend_info = extend_info_from_node(node, 0);
if (BUG(intro->extend_info == NULL)) {
+ tor_free(intro);
break;
}
intro->intro_key = crypto_pk_new();
diff --git a/src/feature/stats/geoip_stats.c b/src/feature/stats/geoip_stats.c
index 5119da19a0..6fb21f4f79 100644
--- a/src/feature/stats/geoip_stats.c
+++ b/src/feature/stats/geoip_stats.c
@@ -32,7 +32,7 @@
#include "ht.h"
#include "lib/buf/buffers.h"
#include "app/config/config.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/client/dnsserv.h"
#include "core/or/dos.h"
#include "lib/geoip/geoip.h"
diff --git a/src/feature/stats/predict_ports.h b/src/feature/stats/predict_ports.h
index 272344da2f..45b206c23a 100644
--- a/src/feature/stats/predict_ports.h
+++ b/src/feature/stats/predict_ports.h
@@ -27,4 +27,4 @@ int rep_hist_circbuilding_dormant(time_t now);
int predicted_ports_prediction_time_remaining(time_t now);
void predicted_ports_free_all(void);
-#endif
+#endif /* !defined(TOR_PREDICT_PORTS_H) */
diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h
index 3accc8c610..0d72946382 100644
--- a/src/feature/stats/rephist.h
+++ b/src/feature/stats/rephist.h
@@ -103,7 +103,7 @@ typedef struct bw_array_t bw_array_t;
STATIC uint64_t find_largest_max(bw_array_t *b);
STATIC void commit_max(bw_array_t *b);
STATIC void advance_obs(bw_array_t *b);
-#endif
+#endif /* defined(REPHIST_PRIVATE) */
/**
* Represents the type of a cell for padding accounting
diff --git a/src/include.am b/src/include.am
index 9070a69a03..77c126ba45 100644
--- a/src/include.am
+++ b/src/include.am
@@ -8,6 +8,7 @@ include src/lib/compress/include.am
include src/lib/container/include.am
include src/lib/crypt_ops/include.am
include src/lib/defs/include.am
+include src/lib/dispatch/include.am
include src/lib/encoding/include.am
include src/lib/evloop/include.am
include src/lib/fdio/include.am
@@ -24,6 +25,7 @@ include src/lib/malloc/include.am
include src/lib/net/include.am
include src/lib/osinfo/include.am
include src/lib/process/include.am
+include src/lib/pubsub/include.am
include src/lib/sandbox/include.am
include src/lib/string/include.am
include src/lib/subsys/include.am
diff --git a/src/lib/arch/bytes.h b/src/lib/arch/bytes.h
index fa82241b28..b8b6288139 100644
--- a/src/lib/arch/bytes.h
+++ b/src/lib/arch/bytes.h
@@ -129,7 +129,7 @@ tor_ntohll(uint64_t a)
{
return a;
}
-#else
+#else /* !(defined(WORDS_BIGENDIAN)) */
static inline uint16_t
tor_htons(uint16_t a)
{
@@ -177,6 +177,6 @@ tor_ntohll(uint64_t a)
{
return tor_htonll(a);
}
-#endif
+#endif /* defined(WORDS_BIGENDIAN) */
-#endif
+#endif /* !defined(TOR_BYTES_H) */
diff --git a/src/lib/arch/include.am b/src/lib/arch/include.am
index f92ee9222f..c5926c6330 100644
--- a/src/lib/arch/include.am
+++ b/src/lib/arch/include.am
@@ -1,3 +1,4 @@
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/arch/bytes.h
diff --git a/src/lib/buf/include.am b/src/lib/buf/include.am
index 3338c3dbdb..27430d1d38 100644
--- a/src/lib/buf/include.am
+++ b/src/lib/buf/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-buf-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_buf_a_SOURCES = \
src/lib/buf/buffers.c
@@ -13,5 +14,6 @@ src_lib_libtor_buf_testing_a_SOURCES = \
src_lib_libtor_buf_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_buf_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/buf/buffers.h
diff --git a/src/lib/cc/compat_compiler.h b/src/lib/cc/compat_compiler.h
index 3a0f307186..a8d1593214 100644
--- a/src/lib/cc/compat_compiler.h
+++ b/src/lib/cc/compat_compiler.h
@@ -217,4 +217,16 @@
/** Macro: Yields the number of elements in array x. */
#define ARRAY_LENGTH(x) ((sizeof(x)) / sizeof(x[0]))
-#endif /* !defined(TOR_COMPAT_H) */
+/**
+ * "Eat" a semicolon that somebody puts at the end of a top-level macro.
+ *
+ * Frequently, we want to declare a macro that people will use at file scope,
+ * and we want to allow people to put a semicolon after the macro.
+ *
+ * This declaration of a struct can be repeated any number of times, and takes
+ * a trailing semicolon afterwards.
+ **/
+#define EAT_SEMICOLON \
+ struct dummy_semicolon_eater__
+
+#endif /* !defined(TOR_COMPAT_COMPILER_H) */
diff --git a/src/lib/cc/ctassert.h b/src/lib/cc/ctassert.h
index e42976360f..bedf0b83a6 100644
--- a/src/lib/cc/ctassert.h
+++ b/src/lib/cc/ctassert.h
@@ -22,7 +22,7 @@
/* If C11 is available, just use _Static_assert. */
#define CTASSERT(x) _Static_assert((x), #x)
-#else
+#else /* !(__STDC_VERSION__ >= 201112L) */
/*
* If C11 is not available, expand __COUNTER__, or __INCLUDE_LEVEL__
@@ -42,12 +42,12 @@
#else
/* hope it's unique enough */
#define CTASSERT(x) CTASSERT_EXPN((x), l, __LINE__)
-#endif
+#endif /* defined(__COUNTER__) || ... */
#define CTASSERT_EXPN(x, a, b) CTASSERT_DECL(x, a, b)
#define CTASSERT_DECL(x, a, b) \
typedef char tor_ctassert_##a##_##b[(x) ? 1 : -1] ATTR_UNUSED
-#endif
+#endif /* __STDC_VERSION__ >= 201112L */
#endif /* !defined(TOR_CTASSERT_H) */
diff --git a/src/lib/cc/include.am b/src/lib/cc/include.am
index 52cf8a9f72..1aa722dd82 100644
--- a/src/lib/cc/include.am
+++ b/src/lib/cc/include.am
@@ -1,4 +1,5 @@
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/cc/compat_compiler.h \
src/lib/cc/ctassert.h \
diff --git a/src/lib/cc/torint.h b/src/lib/cc/torint.h
index c9b2d329f2..523f378ed7 100644
--- a/src/lib/cc/torint.h
+++ b/src/lib/cc/torint.h
@@ -96,9 +96,9 @@ typedef int32_t ssize_t;
# else
# define TOR_PRIuSZ PRIu32
# endif
-#else
+#else /* !(defined(_WIN32)) */
# define TOR_PRIuSZ "zu"
-#endif
+#endif /* defined(_WIN32) */
#ifdef _WIN32
# ifdef _WIN64
@@ -106,9 +106,9 @@ typedef int32_t ssize_t;
# else
# define TOR_PRIdSZ PRId32
# endif
-#else
+#else /* !(defined(_WIN32)) */
# define TOR_PRIdSZ "zd"
-#endif
+#endif /* defined(_WIN32) */
#ifndef SSIZE_MAX
#if (SIZEOF_SIZE_T == 4)
@@ -125,4 +125,13 @@ typedef int32_t ssize_t;
/** Any size_t larger than this amount is likely to be an underflow. */
#define SIZE_T_CEILING ((size_t)(SSIZE_MAX-16))
+#if SIZEOF_INT > SIZEOF_VOID_P
+#error "sizeof(int) > sizeof(void *) - Tor cannot be built on this platform!"
+#endif
+
+#if SIZEOF_UNSIGNED_INT > SIZEOF_VOID_P
+#error "sizeof(unsigned int) > sizeof(void *) - Tor cannot be built on this \
+platform!"
+#endif
+
#endif /* !defined(TOR_TORINT_H) */
diff --git a/src/lib/compress/compress_zstd.c b/src/lib/compress/compress_zstd.c
index 45d0d4d602..a99ea67e0b 100644
--- a/src/lib/compress/compress_zstd.c
+++ b/src/lib/compress/compress_zstd.c
@@ -25,7 +25,7 @@
* all invocations of zstd's static-only functions in a check to make sure
* that the compile-time version matches the run-time version. */
#define ZSTD_STATIC_LINKING_ONLY
-#endif
+#endif /* defined(ENABLE_ZSTD_ADVANCED_APIS) */
#ifdef HAVE_ZSTD
#ifdef HAVE_CFLAG_WUNUSED_CONST_VARIABLE
@@ -35,7 +35,7 @@ DISABLE_GCC_WARNING(unused-const-variable)
#ifdef HAVE_CFLAG_WUNUSED_CONST_VARIABLE
ENABLE_GCC_WARNING(unused-const-variable)
#endif
-#endif
+#endif /* defined(HAVE_ZSTD) */
/** Total number of bytes allocated for Zstandard state. */
static atomic_counter_t total_zstd_allocation;
@@ -77,7 +77,7 @@ tor_zstd_format_version(char *buf, size_t buflen, unsigned version_number)
version_number / 100 % 100,
version_number % 100);
}
-#endif
+#endif /* defined(HAVE_ZSTD) */
#define VERSION_STR_MAX_LEN 16 /* more than enough space for 99.99.99 */
@@ -125,9 +125,9 @@ tor_zstd_can_use_static_apis(void)
}
#endif
return (ZSTD_VERSION_NUMBER == ZSTD_versionNumber());
-#else
+#else /* !(defined(ZSTD_STATIC_LINKING_ONLY) && defined(HAVE_ZSTD)) */
return 0;
-#endif
+#endif /* defined(ZSTD_STATIC_LINKING_ONLY) && defined(HAVE_ZSTD) */
}
/** Internal Zstandard state for incremental compression/decompression.
@@ -237,7 +237,7 @@ tor_zstd_state_size_precalc(int compress, int preset)
#endif
}
}
-#endif
+#endif /* defined(ZSTD_STATIC_LINKING_ONLY) */
return tor_zstd_state_size_precalc_fake(compress, preset);
}
#endif /* defined(HAVE_ZSTD) */
@@ -527,7 +527,7 @@ tor_zstd_warn_if_version_mismatched(void)
"For safety, we'll avoid using advanced zstd functionality.",
header_version, runtime_version);
}
-#endif
+#endif /* defined(HAVE_ZSTD) && defined(ENABLE_ZSTD_ADVANCED_APIS) */
}
#ifdef TOR_UNIT_TESTS
@@ -538,4 +538,4 @@ tor_zstd_set_static_apis_disabled_for_testing(int disabled)
{
static_apis_disable_for_testing = disabled;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/lib/compress/include.am b/src/lib/compress/include.am
index b952779578..60dd447d4e 100644
--- a/src/lib/compress/include.am
+++ b/src/lib/compress/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-compress-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_compress_a_SOURCES = \
src/lib/compress/compress.c \
src/lib/compress/compress_buf.c \
@@ -18,6 +19,7 @@ src_lib_libtor_compress_testing_a_SOURCES = \
src_lib_libtor_compress_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_compress_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/compress/compress.h \
src/lib/compress/compress_lzma.h \
diff --git a/src/lib/container/bitarray.h b/src/lib/container/bitarray.h
index 910d5fea65..45992796af 100644
--- a/src/lib/container/bitarray.h
+++ b/src/lib/container/bitarray.h
@@ -83,4 +83,4 @@ bitarray_is_set(bitarray_t *b, int bit)
return b[bit >> BITARRAY_SHIFT] & (1u << (bit & BITARRAY_MASK));
}
-#endif /* !defined(TOR_CONTAINER_H) */
+#endif /* !defined(TOR_BITARRAY_H) */
diff --git a/src/lib/container/include.am b/src/lib/container/include.am
index 032e4033da..00d7b8e587 100644
--- a/src/lib/container/include.am
+++ b/src/lib/container/include.am
@@ -5,9 +5,11 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-container-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_container_a_SOURCES = \
src/lib/container/bloomfilt.c \
src/lib/container/map.c \
+ src/lib/container/namemap.c \
src/lib/container/order.c \
src/lib/container/smartlist.c
@@ -16,10 +18,13 @@ src_lib_libtor_container_testing_a_SOURCES = \
src_lib_libtor_container_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_container_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/container/bitarray.h \
src/lib/container/bloomfilt.h \
src/lib/container/handles.h \
src/lib/container/map.h \
+ src/lib/container/namemap.h \
+ src/lib/container/namemap_st.h \
src/lib/container/order.h \
src/lib/container/smartlist.h
diff --git a/src/lib/container/map.h b/src/lib/container/map.h
index d61b1ec18f..9da1d3072c 100644
--- a/src/lib/container/map.h
+++ b/src/lib/container/map.h
@@ -258,4 +258,4 @@ void* strmap_remove_lc(strmap_t *map, const char *key);
return digestmap_iter_done((digestmap_iter_t*)iter); \
}
-#endif /* !defined(TOR_CONTAINER_H) */
+#endif /* !defined(TOR_MAP_H) */
diff --git a/src/lib/container/namemap.c b/src/lib/container/namemap.c
new file mode 100644
index 0000000000..a90057b32c
--- /dev/null
+++ b/src/lib/container/namemap.c
@@ -0,0 +1,184 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "lib/container/smartlist.h"
+#include "lib/container/namemap.h"
+#include "lib/container/namemap_st.h"
+#include "lib/log/util_bug.h"
+#include "lib/malloc/malloc.h"
+#include "lib/string/printf.h"
+
+#include "ext/siphash.h"
+
+#include <string.h>
+
+/** Helper for namemap hashtable implementation: compare two entries. */
+static inline int
+mapped_name_eq(const mapped_name_t *a, const mapped_name_t *b)
+{
+ return !strcmp(a->name, b->name);
+}
+
+/** Helper for namemap hashtable implementation: hash an entry. */
+static inline unsigned
+mapped_name_hash(const mapped_name_t *a)
+{
+ return (unsigned) siphash24g(a->name, strlen(a->name));
+}
+
+HT_PROTOTYPE(namemap_ht, mapped_name_t, node, mapped_name_hash,
+ mapped_name_eq)
+HT_GENERATE2(namemap_ht, mapped_name_t, node, mapped_name_hash,
+ mapped_name_eq, 0.6, tor_reallocarray_, tor_free_)
+
+/** Set up an uninitialized <b>map</b>. */
+void
+namemap_init(namemap_t *map)
+{
+ memset(map, 0, sizeof(*map));
+ HT_INIT(namemap_ht, &map->ht);
+ map->names = smartlist_new();
+}
+
+/** Return the name that <b>map</b> associates with a given <b>id</b>, or
+ * NULL if there is no such name. */
+const char *
+namemap_get_name(const namemap_t *map, unsigned id)
+{
+ if (map->names && id < (unsigned)smartlist_len(map->names)) {
+ mapped_name_t *name = smartlist_get(map->names, (int)id);
+ return name->name;
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * Return the name that <b>map</b> associates with a given <b>id</b>, or a
+ * pointer to a statically allocated string describing the value of <b>id</b>
+ * if no such name exists.
+ **/
+const char *
+namemap_fmt_name(const namemap_t *map, unsigned id)
+{
+ static char buf[32];
+
+ const char *name = namemap_get_name(map, id);
+ if (name)
+ return name;
+
+ tor_snprintf(buf, sizeof(buf), "{%u}", id);
+
+ return buf;
+}
+
+/**
+ * Helper: As namemap_get_id(), but requires that <b>name</b> is
+ * <b>namelen</b> charaters long, and that <b>namelen</b> is no more than
+ * MAX_NAMEMAP_NAME_LEN.
+ */
+static unsigned
+namemap_get_id_unchecked(const namemap_t *map,
+ const char *name,
+ size_t namelen)
+{
+ union {
+ mapped_name_t n;
+ char storage[MAX_NAMEMAP_NAME_LEN + sizeof(mapped_name_t) + 1];
+ } u;
+ memcpy(u.n.name, name, namelen);
+ u.n.name[namelen] = 0;
+ const mapped_name_t *found = HT_FIND(namemap_ht, &map->ht, &u.n);
+ if (found) {
+ tor_assert(map->names);
+ tor_assert(smartlist_get(map->names, found->intval) == found);
+ return found->intval;
+ }
+
+ return NAMEMAP_ERR;
+}
+
+/**
+ * Return the identifier currently associated by <b>map</b> with the name
+ * <b>name</b>, or NAMEMAP_ERR if no such identifier exists.
+ **/
+unsigned
+namemap_get_id(const namemap_t *map,
+ const char *name)
+{
+ size_t namelen = strlen(name);
+ if (namelen > MAX_NAMEMAP_NAME_LEN) {
+ return NAMEMAP_ERR;
+ }
+
+ return namemap_get_id_unchecked(map, name, namelen);
+}
+
+/**
+ * Return the identifier associated by <b>map</b> with the name
+ * <b>name</b>, allocating a new identifier in <b>map</b> if none exists.
+ *
+ * Return NAMEMAP_ERR if <b>name</b> is too long, or if there are no more
+ * identifiers we can allocate.
+ **/
+unsigned
+namemap_get_or_create_id(namemap_t *map,
+ const char *name)
+{
+ size_t namelen = strlen(name);
+ if (namelen > MAX_NAMEMAP_NAME_LEN) {
+ return NAMEMAP_ERR;
+ }
+
+ if (PREDICT_UNLIKELY(map->names == NULL))
+ map->names = smartlist_new();
+
+ unsigned found = namemap_get_id_unchecked(map, name, namelen);
+ if (found != NAMEMAP_ERR)
+ return found;
+
+ unsigned new_id = (unsigned)smartlist_len(map->names);
+ if (new_id == NAMEMAP_ERR)
+ return NAMEMAP_ERR; /* Can't allocate any more. */
+
+ mapped_name_t *insert = tor_malloc_zero(
+ offsetof(mapped_name_t, name) + namelen + 1);
+ memcpy(insert->name, name, namelen+1);
+ insert->intval = new_id;
+
+ HT_INSERT(namemap_ht, &map->ht, insert);
+ smartlist_add(map->names, insert);
+
+ return new_id;
+}
+
+/** Return the number of entries in 'names' */
+size_t
+namemap_get_size(const namemap_t *map)
+{
+ if (PREDICT_UNLIKELY(map->names == NULL))
+ return 0;
+
+ return smartlist_len(map->names);
+}
+
+/**
+ * Release all storage held in <b>map</b>.
+ */
+void
+namemap_clear(namemap_t *map)
+{
+ if (!map)
+ return;
+
+ HT_CLEAR(namemap_ht, &map->ht);
+ if (map->names) {
+ SMARTLIST_FOREACH(map->names, mapped_name_t *, n,
+ tor_free(n));
+ smartlist_free(map->names);
+ }
+ memset(map, 0, sizeof(*map));
+}
diff --git a/src/lib/container/namemap.h b/src/lib/container/namemap.h
new file mode 100644
index 0000000000..b96bc13f3a
--- /dev/null
+++ b/src/lib/container/namemap.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_NAMEMAP_H
+#define TOR_NAMEMAP_H
+
+/**
+ * \file namemap.h
+ *
+ * \brief Header for namemap.c
+ **/
+
+#include "lib/cc/compat_compiler.h"
+#include "ext/ht.h"
+
+#include <stddef.h>
+
+typedef struct namemap_t namemap_t;
+
+/** Returned in place of an identifier when an error occurs. */
+#define NAMEMAP_ERR UINT_MAX
+
+void namemap_init(namemap_t *map);
+const char *namemap_get_name(const namemap_t *map, unsigned id);
+const char *namemap_fmt_name(const namemap_t *map, unsigned id);
+unsigned namemap_get_id(const namemap_t *map,
+ const char *name);
+unsigned namemap_get_or_create_id(namemap_t *map,
+ const char *name);
+size_t namemap_get_size(const namemap_t *map);
+void namemap_clear(namemap_t *map);
+
+#endif /* !defined(TOR_NAMEMAP_H) */
diff --git a/src/lib/container/namemap_st.h b/src/lib/container/namemap_st.h
new file mode 100644
index 0000000000..5008fd5855
--- /dev/null
+++ b/src/lib/container/namemap_st.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef NAMEMAP_ST_H
+#define NAMEMAP_ST_H
+
+#include "lib/cc/compat_compiler.h"
+#include "ext/ht.h"
+
+struct smartlist_t;
+
+/** Longest allowed name that's allowed in a namemap_t. */
+#define MAX_NAMEMAP_NAME_LEN 128
+
+/** An entry inside a namemap_t. Maps a string to a numeric identifier. */
+typedef struct mapped_name_t {
+ HT_ENTRY(mapped_name_t) node;
+ unsigned intval;
+ char name[FLEXIBLE_ARRAY_MEMBER];
+} mapped_name_t;
+
+/** A structure that allocates small numeric identifiers for names and maps
+ * back and forth between them. */
+struct namemap_t {
+ HT_HEAD(namemap_ht, mapped_name_t) ht;
+ struct smartlist_t *names;
+};
+
+/** Macro to initialize a namemap. */
+#define NAMEMAP_INIT() { HT_INITIALIZER(), NULL }
+
+#endif /* !defined(NAMEMAP_ST_H) */
diff --git a/src/lib/container/order.h b/src/lib/container/order.h
index a176d6d8a6..3f2fd054a0 100644
--- a/src/lib/container/order.h
+++ b/src/lib/container/order.h
@@ -57,4 +57,4 @@ third_quartile_uint32(uint32_t *array, int n_elements)
return find_nth_uint32(array, n_elements, (n_elements*3)/4);
}
-#endif /* !defined(TOR_CONTAINER_H) */
+#endif /* !defined(TOR_ORDER_H) */
diff --git a/src/lib/container/smartlist.h b/src/lib/container/smartlist.h
index 77682db03e..81b0151433 100644
--- a/src/lib/container/smartlist.h
+++ b/src/lib/container/smartlist.h
@@ -165,4 +165,4 @@ char *smartlist_join_strings2(smartlist_t *sl, const char *join,
} \
STMT_END
-#endif /* !defined(TOR_CONTAINER_H) */
+#endif /* !defined(TOR_SMARTLIST_H) */
diff --git a/src/lib/crypt_ops/crypto_cipher.h b/src/lib/crypt_ops/crypto_cipher.h
index cc4fbf7a41..88d63c1df2 100644
--- a/src/lib/crypt_ops/crypto_cipher.h
+++ b/src/lib/crypt_ops/crypto_cipher.h
@@ -54,4 +54,4 @@ int crypto_cipher_decrypt_with_iv(const char *key,
char *to, size_t tolen,
const char *from, size_t fromlen);
-#endif /* !defined(TOR_CRYPTO_H) */
+#endif /* !defined(TOR_CRYPTO_CIPHER_H) */
diff --git a/src/lib/crypt_ops/crypto_curve25519.h b/src/lib/crypt_ops/crypto_curve25519.h
index 061a7a3505..cd23169cd5 100644
--- a/src/lib/crypt_ops/crypto_curve25519.h
+++ b/src/lib/crypt_ops/crypto_curve25519.h
@@ -76,8 +76,8 @@ STATIC int curve25519_basepoint_impl(uint8_t *output, const uint8_t *secret);
int curve25519_public_from_base64(curve25519_public_key_t *pkey,
const char *input);
-int curve25519_public_to_base64(char *output,
- const curve25519_public_key_t *pkey);
+void curve25519_public_to_base64(char *output,
+ const curve25519_public_key_t *pkey);
void curve25519_set_impl_params(int use_ed);
void curve25519_init(void);
diff --git a/src/lib/crypt_ops/crypto_dh_openssl.c b/src/lib/crypt_ops/crypto_dh_openssl.c
index 8c6388fd5d..75cee1b596 100644
--- a/src/lib/crypt_ops/crypto_dh_openssl.c
+++ b/src/lib/crypt_ops/crypto_dh_openssl.c
@@ -34,7 +34,7 @@ static int tor_check_dh_key(int severity, const BIGNUM *bn);
struct crypto_dh_t {
DH *dh; /**< The openssl DH object */
};
-#endif
+#endif /* !defined(ENABLE_NSS) */
static DH *new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g);
@@ -100,7 +100,7 @@ crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
DH_free(dh);
return ret;
}
-#endif
+#endif /* 0 */
/**
* Helper: convert <b>hex<b> to a bignum, and return it. Assert that the
@@ -202,7 +202,7 @@ crypto_dh_new(int dh_type)
tor_free(res); // sets res to NULL.
return res;
}
-#endif
+#endif /* !defined(ENABLE_NSS) */
/** Create and return a new openssl DH from a given prime and generator. */
static DH *
@@ -461,7 +461,7 @@ crypto_dh_free_(crypto_dh_t *dh)
DH_free(dh->dh);
tor_free(dh);
}
-#endif
+#endif /* !defined(ENABLE_NSS) */
void
crypto_dh_free_all_openssl(void)
diff --git a/src/lib/crypt_ops/crypto_digest.c b/src/lib/crypt_ops/crypto_digest.c
index 26f06c6c79..64a7d2d52c 100644
--- a/src/lib/crypt_ops/crypto_digest.c
+++ b/src/lib/crypt_ops/crypto_digest.c
@@ -23,171 +23,6 @@
#include "lib/arch/bytes.h"
-#ifdef ENABLE_NSS
-DISABLE_GCC_WARNING(strict-prototypes)
-#include <pk11pub.h>
-ENABLE_GCC_WARNING(strict-prototypes)
-#else
-
-#include "lib/crypt_ops/crypto_openssl_mgt.h"
-
-DISABLE_GCC_WARNING(redundant-decls)
-
-#include <openssl/hmac.h>
-#include <openssl/sha.h>
-
-ENABLE_GCC_WARNING(redundant-decls)
-#endif
-
-#ifdef ENABLE_NSS
-/**
- * Convert a digest_algorithm_t (used by tor) to a HashType (used by NSS).
- * On failure, return SEC_OID_UNKNOWN. */
-static SECOidTag
-digest_alg_to_nss_oid(digest_algorithm_t alg)
-{
- switch (alg) {
- case DIGEST_SHA1: return SEC_OID_SHA1;
- case DIGEST_SHA256: return SEC_OID_SHA256;
- case DIGEST_SHA512: return SEC_OID_SHA512;
- case DIGEST_SHA3_256: /* Fall through */
- case DIGEST_SHA3_512: /* Fall through */
- default:
- return SEC_OID_UNKNOWN;
- }
-}
-
-/* Helper: get an unkeyed digest via pk11wrap */
-static int
-digest_nss_internal(SECOidTag alg,
- char *digest, unsigned len_out,
- const char *msg, size_t msg_len)
-{
- if (alg == SEC_OID_UNKNOWN)
- return -1;
- tor_assert(msg_len <= UINT_MAX);
-
- int rv = -1;
- SECStatus s;
- PK11Context *ctx = PK11_CreateDigestContext(alg);
- if (!ctx)
- return -1;
-
- s = PK11_DigestBegin(ctx);
- if (s != SECSuccess)
- goto done;
-
- s = PK11_DigestOp(ctx, (const unsigned char *)msg, (unsigned int)msg_len);
- if (s != SECSuccess)
- goto done;
-
- unsigned int len = 0;
- s = PK11_DigestFinal(ctx, (unsigned char *)digest, &len, len_out);
- if (s != SECSuccess)
- goto done;
-
- rv = 0;
- done:
- PK11_DestroyContext(ctx, PR_TRUE);
- return rv;
-}
-
-/** True iff alg is implemented in our crypto library, and we want to use that
- * implementation */
-static bool
-library_supports_digest(digest_algorithm_t alg)
-{
- switch (alg) {
- case DIGEST_SHA1: /* Fall through */
- case DIGEST_SHA256: /* Fall through */
- case DIGEST_SHA512: /* Fall through */
- return true;
- case DIGEST_SHA3_256: /* Fall through */
- case DIGEST_SHA3_512: /* Fall through */
- default:
- return false;
- }
-}
-#endif
-
-/* Crypto digest functions */
-
-/** Compute the SHA1 digest of the <b>len</b> bytes on data stored in
- * <b>m</b>. Write the DIGEST_LEN byte result into <b>digest</b>.
- * Return 0 on success, -1 on failure.
- */
-MOCK_IMPL(int,
-crypto_digest,(char *digest, const char *m, size_t len))
-{
- tor_assert(m);
- tor_assert(digest);
-#ifdef ENABLE_NSS
- return digest_nss_internal(SEC_OID_SHA1, digest, DIGEST_LEN, m, len);
-#else
- if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL) {
- return -1;
- }
-#endif
- return 0;
-}
-
-/** Compute a 256-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
- * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN256-byte result
- * into <b>digest</b>. Return 0 on success, -1 on failure. */
-int
-crypto_digest256(char *digest, const char *m, size_t len,
- digest_algorithm_t algorithm)
-{
- tor_assert(m);
- tor_assert(digest);
- tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
-
- int ret = 0;
- if (algorithm == DIGEST_SHA256) {
-#ifdef ENABLE_NSS
- return digest_nss_internal(SEC_OID_SHA256, digest, DIGEST256_LEN, m, len);
-#else
- ret = (SHA256((const uint8_t*)m,len,(uint8_t*)digest) != NULL);
-#endif
- } else {
- ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len)
- > -1);
- }
-
- if (!ret)
- return -1;
- return 0;
-}
-
-/** Compute a 512-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
- * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN512-byte result
- * into <b>digest</b>. Return 0 on success, -1 on failure. */
-int
-crypto_digest512(char *digest, const char *m, size_t len,
- digest_algorithm_t algorithm)
-{
- tor_assert(m);
- tor_assert(digest);
- tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
-
- int ret = 0;
- if (algorithm == DIGEST_SHA512) {
-#ifdef ENABLE_NSS
- return digest_nss_internal(SEC_OID_SHA512, digest, DIGEST512_LEN, m, len);
-#else
- ret = (SHA512((const unsigned char*)m,len,(unsigned char*)digest)
- != NULL);
-#endif
- } else {
- ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len)
- > -1);
- }
-
- if (!ret)
- return -1;
- return 0;
-}
-
/** Set the common_digests_t in <b>ds_out</b> to contain every digest on the
* <b>len</b> bytes in <b>m</b> that we know how to compute. Return 0 on
* success, -1 on failure. */
@@ -267,485 +102,6 @@ crypto_digest_algorithm_get_length(digest_algorithm_t alg)
}
}
-/** Intermediate information about the digest of a stream of data. */
-struct crypto_digest_t {
- digest_algorithm_t algorithm; /**< Which algorithm is in use? */
- /** State for the digest we're using. Only one member of the
- * union is usable, depending on the value of <b>algorithm</b>. Note also
- * that space for other members might not even be allocated!
- */
- union {
-#ifdef ENABLE_NSS
- PK11Context *ctx;
-#else
- SHA_CTX sha1; /**< state for SHA1 */
- SHA256_CTX sha2; /**< state for SHA256 */
- SHA512_CTX sha512; /**< state for SHA512 */
-#endif
- keccak_state sha3; /**< state for SHA3-[256,512] */
- } d;
-};
-
-#ifdef TOR_UNIT_TESTS
-
-digest_algorithm_t
-crypto_digest_get_algorithm(crypto_digest_t *digest)
-{
- tor_assert(digest);
-
- return digest->algorithm;
-}
-
-#endif /* defined(TOR_UNIT_TESTS) */
-
-/**
- * Return the number of bytes we need to malloc in order to get a
- * crypto_digest_t for <b>alg</b>, or the number of bytes we need to wipe
- * when we free one.
- */
-static size_t
-crypto_digest_alloc_bytes(digest_algorithm_t alg)
-{
- /* Helper: returns the number of bytes in the 'f' field of 'st' */
-#define STRUCT_FIELD_SIZE(st, f) (sizeof( ((st*)0)->f ))
- /* Gives the length of crypto_digest_t through the end of the field 'd' */
-#define END_OF_FIELD(f) (offsetof(crypto_digest_t, f) + \
- STRUCT_FIELD_SIZE(crypto_digest_t, f))
- switch (alg) {
-#ifdef ENABLE_NSS
- case DIGEST_SHA1: /* Fall through */
- case DIGEST_SHA256: /* Fall through */
- case DIGEST_SHA512:
- return END_OF_FIELD(d.ctx);
-#else
- case DIGEST_SHA1:
- return END_OF_FIELD(d.sha1);
- case DIGEST_SHA256:
- return END_OF_FIELD(d.sha2);
- case DIGEST_SHA512:
- return END_OF_FIELD(d.sha512);
-#endif
- case DIGEST_SHA3_256:
- case DIGEST_SHA3_512:
- return END_OF_FIELD(d.sha3);
- default:
- 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)
- {
-#ifdef ENABLE_NSS
- case DIGEST_SHA1: /* fall through */
- case DIGEST_SHA256: /* fall through */
- case DIGEST_SHA512:
- r->d.ctx = PK11_CreateDigestContext(digest_alg_to_nss_oid(algorithm));
- if (BUG(!r->d.ctx)) {
- tor_free(r);
- return NULL;
- }
- if (BUG(SECSuccess != PK11_DigestBegin(r->d.ctx))) {
- crypto_digest_free(r);
- return NULL;
- }
- break;
-#else
- 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;
-#endif
- 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)
-{
- return crypto_digest_new_internal(DIGEST_SHA1);
-}
-
-/** Allocate and return a new digest object to compute 256-bit digests
- * using <b>algorithm</b>.
- *
- * C_RUST_COUPLED: `external::crypto_digest::crypto_digest256_new`
- * C_RUST_COUPLED: `crypto::digest::Sha256::default`
- */
-crypto_digest_t *
-crypto_digest256_new(digest_algorithm_t algorithm)
-{
- tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
- return crypto_digest_new_internal(algorithm);
-}
-
-/** Allocate and return a new digest object to compute 512-bit digests
- * using <b>algorithm</b>. */
-crypto_digest_t *
-crypto_digest512_new(digest_algorithm_t algorithm)
-{
- tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
- return crypto_digest_new_internal(algorithm);
-}
-
-/** Deallocate a digest object.
- */
-void
-crypto_digest_free_(crypto_digest_t *digest)
-{
- if (!digest)
- return;
-#ifdef ENABLE_NSS
- if (library_supports_digest(digest->algorithm)) {
- PK11_DestroyContext(digest->d.ctx, PR_TRUE);
- }
-#endif
- size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
- memwipe(digest, 0, bytes);
- tor_free(digest);
-}
-
-/** Add <b>len</b> bytes from <b>data</b> to the digest object.
- *
- * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_add_bytess`
- * C_RUST_COUPLED: `crypto::digest::Sha256::process`
- */
-void
-crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
- size_t len)
-{
- tor_assert(digest);
- tor_assert(data);
- /* Using the SHA*_*() calls directly means we don't support doing
- * SHA in hardware. But so far the delay of getting the question
- * to the hardware, and hearing the answer, is likely higher than
- * just doing it ourselves. Hashes are fast.
- */
- switch (digest->algorithm) {
-#ifdef ENABLE_NSS
- case DIGEST_SHA1: /* fall through */
- case DIGEST_SHA256: /* fall through */
- case DIGEST_SHA512:
- tor_assert(len <= UINT_MAX);
- SECStatus s = PK11_DigestOp(digest->d.ctx,
- (const unsigned char *)data,
- (unsigned int)len);
- tor_assert(s == SECSuccess);
- break;
-#else
- case DIGEST_SHA1:
- SHA1_Update(&digest->d.sha1, (void*)data, len);
- break;
- case DIGEST_SHA256:
- SHA256_Update(&digest->d.sha2, (void*)data, len);
- break;
- case DIGEST_SHA512:
- SHA512_Update(&digest->d.sha512, (void*)data, len);
- break;
-#endif
- case DIGEST_SHA3_256: /* FALLSTHROUGH */
- case DIGEST_SHA3_512:
- keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
- break;
- default:
- /* LCOV_EXCL_START */
- tor_fragile_assert();
- break;
- /* LCOV_EXCL_STOP */
- }
-}
-
-/** Compute the hash of the data that has been passed to the digest
- * object; write the first out_len bytes of the result to <b>out</b>.
- * <b>out_len</b> must be \<= DIGEST512_LEN.
- *
- * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_get_digest`
- * C_RUST_COUPLED: `impl digest::FixedOutput for Sha256`
- */
-void
-crypto_digest_get_digest(crypto_digest_t *digest,
- char *out, size_t out_len)
-{
- unsigned char r[DIGEST512_LEN];
- tor_assert(digest);
- tor_assert(out);
- tor_assert(out_len <= crypto_digest_algorithm_get_length(digest->algorithm));
-
- /* The SHA-3 code handles copying into a temporary ctx, and also can handle
- * short output buffers by truncating appropriately. */
- if (digest->algorithm == DIGEST_SHA3_256 ||
- digest->algorithm == DIGEST_SHA3_512) {
- keccak_digest_sum(&digest->d.sha3, (uint8_t *)out, out_len);
- return;
- }
-
-#ifdef ENABLE_NSS
- /* Copy into a temporary buffer since DigestFinal (alters) the context */
- unsigned char buf[1024];
- unsigned int saved_len = 0;
- unsigned rlen;
- unsigned char *saved = PK11_SaveContextAlloc(digest->d.ctx,
- buf, sizeof(buf),
- &saved_len);
- tor_assert(saved);
- SECStatus s = PK11_DigestFinal(digest->d.ctx, r, &rlen, sizeof(r));
- tor_assert(s == SECSuccess);
- tor_assert(rlen >= out_len);
- s = PK11_RestoreContext(digest->d.ctx, saved, saved_len);
- tor_assert(s == SECSuccess);
- if (saved != buf) {
- PORT_ZFree(saved, saved_len);
- }
-#else
- const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
- crypto_digest_t tmpenv;
- /* memcpy into a temporary ctx, since SHA*_Final clears the context */
- memcpy(&tmpenv, digest, alloc_bytes);
- switch (digest->algorithm) {
- case DIGEST_SHA1:
- SHA1_Final(r, &tmpenv.d.sha1);
- break;
- case DIGEST_SHA256:
- SHA256_Final(r, &tmpenv.d.sha2);
- break;
- case DIGEST_SHA512:
- SHA512_Final(r, &tmpenv.d.sha512);
- break;
-//LCOV_EXCL_START
- case DIGEST_SHA3_256: /* FALLSTHROUGH */
- case DIGEST_SHA3_512:
- default:
- 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
- }
-#endif
- memcpy(out, r, out_len);
- memwipe(r, 0, sizeof(r));
-}
-
-/** Allocate and return a new digest object with the same state as
- * <b>digest</b>
- *
- * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_dup`
- * C_RUST_COUPLED: `impl Clone for crypto::digest::Sha256`
- */
-crypto_digest_t *
-crypto_digest_dup(const crypto_digest_t *digest)
-{
- tor_assert(digest);
- const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
- crypto_digest_t *result = tor_memdup(digest, alloc_bytes);
-#ifdef ENABLE_NSS
- if (library_supports_digest(digest->algorithm)) {
- result->d.ctx = PK11_CloneContext(digest->d.ctx);
- }
-#endif
- return result;
-}
-
-/** Temporarily save the state of <b>digest</b> in <b>checkpoint</b>.
- * Asserts that <b>digest</b> is a SHA1 digest object.
- */
-void
-crypto_digest_checkpoint(crypto_digest_checkpoint_t *checkpoint,
- const crypto_digest_t *digest)
-{
- const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
- tor_assert(bytes <= sizeof(checkpoint->mem));
-#ifdef ENABLE_NSS
- if (library_supports_digest(digest->algorithm)) {
- unsigned char *allocated;
- allocated = PK11_SaveContextAlloc(digest->d.ctx,
- (unsigned char *)checkpoint->mem,
- sizeof(checkpoint->mem),
- &checkpoint->bytes_used);
- /* No allocation is allowed here. */
- tor_assert(allocated == checkpoint->mem);
- return;
- }
-#endif
- memcpy(checkpoint->mem, digest, bytes);
-}
-
-/** Restore the state of <b>digest</b> from <b>checkpoint</b>.
- * Asserts that <b>digest</b> is a SHA1 digest object. Requires that the
- * state was previously stored with crypto_digest_checkpoint() */
-void
-crypto_digest_restore(crypto_digest_t *digest,
- const crypto_digest_checkpoint_t *checkpoint)
-{
- const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
-#ifdef ENABLE_NSS
- if (library_supports_digest(digest->algorithm)) {
- SECStatus s = PK11_RestoreContext(digest->d.ctx,
- (unsigned char *)checkpoint->mem,
- checkpoint->bytes_used);
- tor_assert(s == SECSuccess);
- return;
- }
-#endif
- memcpy(digest, checkpoint->mem, bytes);
-}
-
-/** Replace the state of the digest object <b>into</b> with the state
- * of the digest object <b>from</b>. Requires that 'into' and 'from'
- * have the same digest type.
- */
-void
-crypto_digest_assign(crypto_digest_t *into,
- const crypto_digest_t *from)
-{
- tor_assert(into);
- tor_assert(from);
- tor_assert(into->algorithm == from->algorithm);
- const size_t alloc_bytes = crypto_digest_alloc_bytes(from->algorithm);
-#ifdef ENABLE_NSS
- if (library_supports_digest(from->algorithm)) {
- PK11_DestroyContext(into->d.ctx, PR_TRUE);
- into->d.ctx = PK11_CloneContext(from->d.ctx);
- return;
- }
-#endif
- memcpy(into,from,alloc_bytes);
-}
-
-/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
- * at <b>digest_out</b> to the hash of the concatenation of those strings,
- * plus the optional string <b>append</b>, computed with the algorithm
- * <b>alg</b>.
- * <b>out_len</b> must be \<= DIGEST512_LEN. */
-void
-crypto_digest_smartlist(char *digest_out, size_t len_out,
- const smartlist_t *lst,
- const char *append,
- digest_algorithm_t alg)
-{
- crypto_digest_smartlist_prefix(digest_out, len_out, NULL, lst, append, alg);
-}
-
-/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
- * at <b>digest_out</b> to the hash of the concatenation of: the
- * optional string <b>prepend</b>, those strings,
- * and the optional string <b>append</b>, computed with the algorithm
- * <b>alg</b>.
- * <b>len_out</b> must be \<= DIGEST512_LEN. */
-void
-crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
- const char *prepend,
- const smartlist_t *lst,
- const char *append,
- digest_algorithm_t alg)
-{
- 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,
- crypto_digest_add_bytes(d, cp, strlen(cp)));
- if (append)
- crypto_digest_add_bytes(d, append, strlen(append));
- crypto_digest_get_digest(d, digest_out, len_out);
- crypto_digest_free(d);
-}
-
-/** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using
- * the <b>key</b> of length <b>key_len</b>. Store the DIGEST256_LEN-byte
- * result in <b>hmac_out</b>. Asserts on failure.
- */
-void
-crypto_hmac_sha256(char *hmac_out,
- const char *key, size_t key_len,
- const char *msg, size_t msg_len)
-{
- /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
- tor_assert(key_len < INT_MAX);
- tor_assert(msg_len < INT_MAX);
- tor_assert(hmac_out);
-#ifdef ENABLE_NSS
- PK11SlotInfo *slot = NULL;
- PK11SymKey *symKey = NULL;
- PK11Context *hmac = NULL;
-
- int ok = 0;
- SECStatus s;
- SECItem keyItem, paramItem;
- keyItem.data = (unsigned char *)key;
- keyItem.len = (unsigned)key_len;
- paramItem.type = siBuffer;
- paramItem.data = NULL;
- paramItem.len = 0;
-
- slot = PK11_GetBestSlot(CKM_SHA256_HMAC, NULL);
- if (!slot)
- goto done;
- symKey = PK11_ImportSymKey(slot, CKM_SHA256_HMAC,
- PK11_OriginUnwrap, CKA_SIGN, &keyItem, NULL);
- if (!symKey)
- goto done;
-
- hmac = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, symKey,
- &paramItem);
- if (!hmac)
- goto done;
- s = PK11_DigestBegin(hmac);
- if (s != SECSuccess)
- goto done;
- s = PK11_DigestOp(hmac, (const unsigned char *)msg, (unsigned int)msg_len);
- if (s != SECSuccess)
- goto done;
- unsigned int len=0;
- s = PK11_DigestFinal(hmac, (unsigned char *)hmac_out, &len, DIGEST256_LEN);
- if (s != SECSuccess || len != DIGEST256_LEN)
- goto done;
- ok = 1;
-
- done:
- if (hmac)
- PK11_DestroyContext(hmac, PR_TRUE);
- if (symKey)
- PK11_FreeSymKey(symKey);
- if (slot)
- PK11_FreeSlot(slot);
-
- tor_assert(ok);
-#else
- unsigned char *rv = NULL;
- rv = HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
- (unsigned char*)hmac_out, NULL);
- tor_assert(rv);
-#endif
-}
-
/** Compute a MAC using SHA3-256 of <b>msg_len</b> bytes in <b>msg</b> using a
* <b>key</b> of length <b>key_len</b> and a <b>salt</b> of length
* <b>salt_len</b>. Store the result of <b>len_out</b> bytes in in
@@ -779,7 +135,23 @@ crypto_mac_sha3_256(uint8_t *mac_out, size_t len_out,
/** Internal state for a eXtendable-Output Function (XOF). */
struct crypto_xof_t {
+#ifdef OPENSSL_HAS_SHAKE3_EVP
+ /* XXXX We can't enable this yet, because OpenSSL's
+ * DigestFinalXOF function can't be called repeatedly on the same
+ * XOF.
+ *
+ * We could in theory use the undocumented SHA3_absorb and SHA3_squeeze
+ * functions, but let's not mess with undocumented OpenSSL internals any
+ * more than we have to.
+ *
+ * We could also revise our XOF code so that it only allows a single
+ * squeeze operation; we don't require streaming squeeze operations
+ * outside the tests yet.
+ */
+ EVP_MD_CTX *ctx;
+#else /* !(defined(OPENSSL_HAS_SHAKE3_EVP)) */
keccak_state s;
+#endif /* defined(OPENSSL_HAS_SHAKE3_EVP) */
};
/** Allocate a new XOF object backed by SHAKE-256. The security level
@@ -792,7 +164,14 @@ crypto_xof_new(void)
{
crypto_xof_t *xof;
xof = tor_malloc(sizeof(crypto_xof_t));
+#ifdef OPENSSL_HAS_SHAKE256
+ xof->ctx = EVP_MD_CTX_new();
+ tor_assert(xof->ctx);
+ int r = EVP_DigestInit(xof->ctx, EVP_shake256());
+ tor_assert(r == 1);
+#else /* !(defined(OPENSSL_HAS_SHAKE256)) */
keccak_xof_init(&xof->s, 256);
+#endif /* defined(OPENSSL_HAS_SHAKE256) */
return xof;
}
@@ -803,8 +182,13 @@ crypto_xof_new(void)
void
crypto_xof_add_bytes(crypto_xof_t *xof, const uint8_t *data, size_t len)
{
+#ifdef OPENSSL_HAS_SHAKE256
+ int r = EVP_DigestUpdate(xof->ctx, data, len);
+ tor_assert(r == 1);
+#else
int i = keccak_xof_absorb(&xof->s, data, len);
tor_assert(i == 0);
+#endif /* defined(OPENSSL_HAS_SHAKE256) */
}
/** Squeeze bytes out of a XOF object. Calling this routine will render
@@ -813,8 +197,13 @@ crypto_xof_add_bytes(crypto_xof_t *xof, const uint8_t *data, size_t len)
void
crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len)
{
+#ifdef OPENSSL_HAS_SHAKE256
+ int r = EVP_DigestFinalXOF(xof->ctx, out, len);
+ tor_assert(r == 1);
+#else
int i = keccak_xof_squeeze(&xof->s, out, len);
tor_assert(i == 0);
+#endif /* defined(OPENSSL_HAS_SHAKE256) */
}
/** Cleanse and deallocate a XOF object. */
@@ -823,6 +212,34 @@ crypto_xof_free_(crypto_xof_t *xof)
{
if (!xof)
return;
+#ifdef OPENSSL_HAS_SHAKE256
+ if (xof->ctx)
+ EVP_MD_CTX_free(xof->ctx);
+#endif
memwipe(xof, 0, sizeof(crypto_xof_t));
tor_free(xof);
}
+
+/** Compute the XOF (SHAKE256) of a <b>input_len</b> bytes at <b>input</b>,
+ * putting <b>output_len</b> bytes at <b>output</b>. */
+void
+crypto_xof(uint8_t *output, size_t output_len,
+ const uint8_t *input, size_t input_len)
+{
+#ifdef OPENSSL_HAS_SHA3
+ EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+ tor_assert(ctx);
+ int r = EVP_DigestInit(ctx, EVP_shake256());
+ tor_assert(r == 1);
+ r = EVP_DigestUpdate(ctx, input, input_len);
+ tor_assert(r == 1);
+ r = EVP_DigestFinalXOF(ctx, output, output_len);
+ tor_assert(r == 1);
+ EVP_MD_CTX_free(ctx);
+#else /* !(defined(OPENSSL_HAS_SHA3)) */
+ crypto_xof_t *xof = crypto_xof_new();
+ crypto_xof_add_bytes(xof, input, input_len);
+ crypto_xof_squeeze_bytes(xof, output, output_len);
+ crypto_xof_free(xof);
+#endif /* defined(OPENSSL_HAS_SHA3) */
+}
diff --git a/src/lib/crypt_ops/crypto_digest.h b/src/lib/crypt_ops/crypto_digest.h
index 47e60ce617..5869db7800 100644
--- a/src/lib/crypt_ops/crypto_digest.h
+++ b/src/lib/crypt_ops/crypto_digest.h
@@ -124,6 +124,8 @@ void crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len);
void crypto_xof_free_(crypto_xof_t *xof);
#define crypto_xof_free(xof) \
FREE_AND_NULL(crypto_xof_t, crypto_xof_free_, (xof))
+void crypto_xof(uint8_t *output, size_t output_len,
+ const uint8_t *input, size_t input_len);
#ifdef TOR_UNIT_TESTS
digest_algorithm_t crypto_digest_get_algorithm(crypto_digest_t *digest);
diff --git a/src/lib/crypt_ops/crypto_digest_nss.c b/src/lib/crypt_ops/crypto_digest_nss.c
new file mode 100644
index 0000000000..b73f0736fd
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_digest_nss.c
@@ -0,0 +1,560 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_digest_nss.c
+ * \brief Block of functions related with digest and xof utilities and
+ * operations (NSS specific implementations).
+ **/
+
+#include "lib/container/smartlist.h"
+#include "lib/crypt_ops/crypto_digest.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+
+#include "keccak-tiny/keccak-tiny.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib/arch/bytes.h"
+
+DISABLE_GCC_WARNING(strict-prototypes)
+#include <pk11pub.h>
+ENABLE_GCC_WARNING(strict-prototypes)
+
+/**
+ * Convert a digest_algorithm_t (used by tor) to a HashType (used by NSS).
+ * On failure, return SEC_OID_UNKNOWN. */
+static SECOidTag
+digest_alg_to_nss_oid(digest_algorithm_t alg)
+{
+ switch (alg) {
+ case DIGEST_SHA1: return SEC_OID_SHA1;
+ case DIGEST_SHA256: return SEC_OID_SHA256;
+ case DIGEST_SHA512: return SEC_OID_SHA512;
+ case DIGEST_SHA3_256: /* Fall through */
+ case DIGEST_SHA3_512: /* Fall through */
+ default:
+ return SEC_OID_UNKNOWN;
+ }
+}
+
+/* Helper: get an unkeyed digest via pk11wrap */
+static int
+digest_nss_internal(SECOidTag alg,
+ char *digest, unsigned len_out,
+ const char *msg, size_t msg_len)
+{
+ if (alg == SEC_OID_UNKNOWN)
+ return -1;
+ tor_assert(msg_len <= UINT_MAX);
+
+ int rv = -1;
+ SECStatus s;
+ PK11Context *ctx = PK11_CreateDigestContext(alg);
+ if (!ctx)
+ return -1;
+
+ s = PK11_DigestBegin(ctx);
+ if (s != SECSuccess)
+ goto done;
+
+ s = PK11_DigestOp(ctx, (const unsigned char *)msg, (unsigned int)msg_len);
+ if (s != SECSuccess)
+ goto done;
+
+ unsigned int len = 0;
+ s = PK11_DigestFinal(ctx, (unsigned char *)digest, &len, len_out);
+ if (s != SECSuccess)
+ goto done;
+
+ rv = 0;
+ done:
+ PK11_DestroyContext(ctx, PR_TRUE);
+ return rv;
+}
+
+/** True iff alg is implemented in our crypto library, and we want to use that
+ * implementation */
+static bool
+library_supports_digest(digest_algorithm_t alg)
+{
+ switch (alg) {
+ case DIGEST_SHA1: /* Fall through */
+ case DIGEST_SHA256: /* Fall through */
+ case DIGEST_SHA512: /* Fall through */
+ return true;
+ case DIGEST_SHA3_256: /* Fall through */
+ case DIGEST_SHA3_512: /* Fall through */
+ default:
+ return false;
+ }
+}
+
+/* Crypto digest functions */
+
+/** Compute the SHA1 digest of the <b>len</b> bytes on data stored in
+ * <b>m</b>. Write the DIGEST_LEN byte result into <b>digest</b>.
+ * Return 0 on success, -1 on failure.
+ */
+MOCK_IMPL(int,
+crypto_digest,(char *digest, const char *m, size_t len))
+{
+ tor_assert(m);
+ tor_assert(digest);
+ return digest_nss_internal(SEC_OID_SHA1, digest, DIGEST_LEN, m, len);
+}
+
+/** Compute a 256-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
+ * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN256-byte result
+ * into <b>digest</b>. Return 0 on success, -1 on failure. */
+int
+crypto_digest256(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm)
+{
+ tor_assert(m);
+ tor_assert(digest);
+ tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
+
+ int ret = 0;
+ if (algorithm == DIGEST_SHA256) {
+ return digest_nss_internal(SEC_OID_SHA256, digest, DIGEST256_LEN, m, len);
+ } else {
+ ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len)
+ > -1);
+ }
+
+ if (!ret)
+ return -1;
+ return 0;
+}
+
+/** Compute a 512-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
+ * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN512-byte result
+ * into <b>digest</b>. Return 0 on success, -1 on failure. */
+int
+crypto_digest512(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm)
+{
+ tor_assert(m);
+ tor_assert(digest);
+ tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
+
+ int ret = 0;
+ if (algorithm == DIGEST_SHA512) {
+ return digest_nss_internal(SEC_OID_SHA512, digest, DIGEST512_LEN, m, len);
+ } else {
+ ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len)
+ > -1);
+ }
+
+ if (!ret)
+ return -1;
+ return 0;
+}
+
+/** Intermediate information about the digest of a stream of data. */
+struct crypto_digest_t {
+ digest_algorithm_t algorithm; /**< Which algorithm is in use? */
+ /** State for the digest we're using. Only one member of the
+ * union is usable, depending on the value of <b>algorithm</b>. Note also
+ * that space for other members might not even be allocated!
+ */
+ union {
+ PK11Context *ctx;
+ keccak_state sha3; /**< state for SHA3-[256,512] */
+ } d;
+};
+
+#ifdef TOR_UNIT_TESTS
+
+digest_algorithm_t
+crypto_digest_get_algorithm(crypto_digest_t *digest)
+{
+ tor_assert(digest);
+
+ return digest->algorithm;
+}
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/**
+ * Return the number of bytes we need to malloc in order to get a
+ * crypto_digest_t for <b>alg</b>, or the number of bytes we need to wipe
+ * when we free one.
+ */
+static size_t
+crypto_digest_alloc_bytes(digest_algorithm_t alg)
+{
+ /* Helper: returns the number of bytes in the 'f' field of 'st' */
+#define STRUCT_FIELD_SIZE(st, f) (sizeof( ((st*)0)->f ))
+ /* Gives the length of crypto_digest_t through the end of the field 'd' */
+#define END_OF_FIELD(f) (offsetof(crypto_digest_t, f) + \
+ STRUCT_FIELD_SIZE(crypto_digest_t, f))
+ switch (alg) {
+ case DIGEST_SHA1: /* Fall through */
+ case DIGEST_SHA256: /* Fall through */
+ case DIGEST_SHA512:
+ return END_OF_FIELD(d.ctx);
+ case DIGEST_SHA3_256:
+ case DIGEST_SHA3_512:
+ return END_OF_FIELD(d.sha3);
+ default:
+ 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: /* fall through */
+ case DIGEST_SHA256: /* fall through */
+ case DIGEST_SHA512:
+ r->d.ctx = PK11_CreateDigestContext(digest_alg_to_nss_oid(algorithm));
+ if (BUG(!r->d.ctx)) {
+ tor_free(r);
+ return NULL;
+ }
+ if (BUG(SECSuccess != PK11_DigestBegin(r->d.ctx))) {
+ crypto_digest_free(r);
+ return NULL;
+ }
+ 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)
+{
+ return crypto_digest_new_internal(DIGEST_SHA1);
+}
+
+/** Allocate and return a new digest object to compute 256-bit digests
+ * using <b>algorithm</b>.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest256_new`
+ * C_RUST_COUPLED: `crypto::digest::Sha256::default`
+ */
+crypto_digest_t *
+crypto_digest256_new(digest_algorithm_t algorithm)
+{
+ tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
+ return crypto_digest_new_internal(algorithm);
+}
+
+/** Allocate and return a new digest object to compute 512-bit digests
+ * using <b>algorithm</b>. */
+crypto_digest_t *
+crypto_digest512_new(digest_algorithm_t algorithm)
+{
+ tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
+ return crypto_digest_new_internal(algorithm);
+}
+
+/** Deallocate a digest object.
+ */
+void
+crypto_digest_free_(crypto_digest_t *digest)
+{
+ if (!digest)
+ return;
+ if (library_supports_digest(digest->algorithm)) {
+ PK11_DestroyContext(digest->d.ctx, PR_TRUE);
+ }
+ size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ memwipe(digest, 0, bytes);
+ tor_free(digest);
+}
+
+/** Add <b>len</b> bytes from <b>data</b> to the digest object.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_add_bytess`
+ * C_RUST_COUPLED: `crypto::digest::Sha256::process`
+ */
+void
+crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
+ size_t len)
+{
+ tor_assert(digest);
+ tor_assert(data);
+ /* Using the SHA*_*() calls directly means we don't support doing
+ * SHA in hardware. But so far the delay of getting the question
+ * to the hardware, and hearing the answer, is likely higher than
+ * just doing it ourselves. Hashes are fast.
+ */
+ switch (digest->algorithm) {
+ case DIGEST_SHA1: /* fall through */
+ case DIGEST_SHA256: /* fall through */
+ case DIGEST_SHA512:
+ tor_assert(len <= UINT_MAX);
+ SECStatus s = PK11_DigestOp(digest->d.ctx,
+ (const unsigned char *)data,
+ (unsigned int)len);
+ tor_assert(s == SECSuccess);
+ break;
+ case DIGEST_SHA3_256: /* FALLSTHROUGH */
+ case DIGEST_SHA3_512:
+ keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
+ break;
+ default:
+ /* LCOV_EXCL_START */
+ tor_fragile_assert();
+ break;
+ /* LCOV_EXCL_STOP */
+ }
+}
+
+/** Compute the hash of the data that has been passed to the digest
+ * object; write the first out_len bytes of the result to <b>out</b>.
+ * <b>out_len</b> must be \<= DIGEST512_LEN.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_get_digest`
+ * C_RUST_COUPLED: `impl digest::FixedOutput for Sha256`
+ */
+void
+crypto_digest_get_digest(crypto_digest_t *digest,
+ char *out, size_t out_len)
+{
+ unsigned char r[DIGEST512_LEN];
+ tor_assert(digest);
+ tor_assert(out);
+ tor_assert(out_len <= crypto_digest_algorithm_get_length(digest->algorithm));
+
+ /* The SHA-3 code handles copying into a temporary ctx, and also can handle
+ * short output buffers by truncating appropriately. */
+ if (digest->algorithm == DIGEST_SHA3_256 ||
+ digest->algorithm == DIGEST_SHA3_512) {
+ keccak_digest_sum(&digest->d.sha3, (uint8_t *)out, out_len);
+ return;
+ }
+
+ /* Copy into a temporary buffer since DigestFinal (alters) the context */
+ unsigned char buf[1024];
+ unsigned int saved_len = 0;
+ unsigned rlen;
+ unsigned char *saved = PK11_SaveContextAlloc(digest->d.ctx,
+ buf, sizeof(buf),
+ &saved_len);
+ tor_assert(saved);
+ SECStatus s = PK11_DigestFinal(digest->d.ctx, r, &rlen, sizeof(r));
+ tor_assert(s == SECSuccess);
+ tor_assert(rlen >= out_len);
+ s = PK11_RestoreContext(digest->d.ctx, saved, saved_len);
+ tor_assert(s == SECSuccess);
+
+ if (saved != buf) {
+ PORT_ZFree(saved, saved_len);
+ }
+ memcpy(out, r, out_len);
+ memwipe(r, 0, sizeof(r));
+}
+
+/** Allocate and return a new digest object with the same state as
+ * <b>digest</b>
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_dup`
+ * C_RUST_COUPLED: `impl Clone for crypto::digest::Sha256`
+ */
+crypto_digest_t *
+crypto_digest_dup(const crypto_digest_t *digest)
+{
+ tor_assert(digest);
+ const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ crypto_digest_t *result = tor_memdup(digest, alloc_bytes);
+
+ if (library_supports_digest(digest->algorithm)) {
+ result->d.ctx = PK11_CloneContext(digest->d.ctx);
+ }
+
+ return result;
+}
+
+/** Temporarily save the state of <b>digest</b> in <b>checkpoint</b>.
+ * Asserts that <b>digest</b> is a SHA1 digest object.
+ */
+void
+crypto_digest_checkpoint(crypto_digest_checkpoint_t *checkpoint,
+ const crypto_digest_t *digest)
+{
+ const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ tor_assert(bytes <= sizeof(checkpoint->mem));
+ if (library_supports_digest(digest->algorithm)) {
+ unsigned char *allocated;
+ allocated = PK11_SaveContextAlloc(digest->d.ctx,
+ (unsigned char *)checkpoint->mem,
+ sizeof(checkpoint->mem),
+ &checkpoint->bytes_used);
+ /* No allocation is allowed here. */
+ tor_assert(allocated == checkpoint->mem);
+ return;
+ }
+ memcpy(checkpoint->mem, digest, bytes);
+}
+
+/** Restore the state of <b>digest</b> from <b>checkpoint</b>.
+ * Asserts that <b>digest</b> is a SHA1 digest object. Requires that the
+ * state was previously stored with crypto_digest_checkpoint() */
+void
+crypto_digest_restore(crypto_digest_t *digest,
+ const crypto_digest_checkpoint_t *checkpoint)
+{
+ const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ if (library_supports_digest(digest->algorithm)) {
+ SECStatus s = PK11_RestoreContext(digest->d.ctx,
+ (unsigned char *)checkpoint->mem,
+ checkpoint->bytes_used);
+ tor_assert(s == SECSuccess);
+ return;
+ }
+ memcpy(digest, checkpoint->mem, bytes);
+}
+
+/** Replace the state of the digest object <b>into</b> with the state
+ * of the digest object <b>from</b>. Requires that 'into' and 'from'
+ * have the same digest type.
+ */
+void
+crypto_digest_assign(crypto_digest_t *into,
+ const crypto_digest_t *from)
+{
+ tor_assert(into);
+ tor_assert(from);
+ tor_assert(into->algorithm == from->algorithm);
+ const size_t alloc_bytes = crypto_digest_alloc_bytes(from->algorithm);
+ if (library_supports_digest(from->algorithm)) {
+ PK11_DestroyContext(into->d.ctx, PR_TRUE);
+ into->d.ctx = PK11_CloneContext(from->d.ctx);
+ return;
+ }
+ memcpy(into,from,alloc_bytes);
+}
+
+/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
+ * at <b>digest_out</b> to the hash of the concatenation of those strings,
+ * plus the optional string <b>append</b>, computed with the algorithm
+ * <b>alg</b>.
+ * <b>out_len</b> must be \<= DIGEST512_LEN. */
+void
+crypto_digest_smartlist(char *digest_out, size_t len_out,
+ const smartlist_t *lst,
+ const char *append,
+ digest_algorithm_t alg)
+{
+ crypto_digest_smartlist_prefix(digest_out, len_out, NULL, lst, append, alg);
+}
+
+/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
+ * at <b>digest_out</b> to the hash of the concatenation of: the
+ * optional string <b>prepend</b>, those strings,
+ * and the optional string <b>append</b>, computed with the algorithm
+ * <b>alg</b>.
+ * <b>len_out</b> must be \<= DIGEST512_LEN. */
+void
+crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
+ const char *prepend,
+ const smartlist_t *lst,
+ const char *append,
+ digest_algorithm_t alg)
+{
+ 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,
+ crypto_digest_add_bytes(d, cp, strlen(cp)));
+ if (append)
+ crypto_digest_add_bytes(d, append, strlen(append));
+ crypto_digest_get_digest(d, digest_out, len_out);
+ crypto_digest_free(d);
+}
+
+/** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using
+ * the <b>key</b> of length <b>key_len</b>. Store the DIGEST256_LEN-byte
+ * result in <b>hmac_out</b>. Asserts on failure.
+ */
+void
+crypto_hmac_sha256(char *hmac_out,
+ const char *key, size_t key_len,
+ const char *msg, size_t msg_len)
+{
+ /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
+ tor_assert(key_len < INT_MAX);
+ tor_assert(msg_len < INT_MAX);
+ tor_assert(hmac_out);
+
+ PK11SlotInfo *slot = NULL;
+ PK11SymKey *symKey = NULL;
+ PK11Context *hmac = NULL;
+
+ int ok = 0;
+ SECStatus s;
+ SECItem keyItem, paramItem;
+ keyItem.data = (unsigned char *)key;
+ keyItem.len = (unsigned)key_len;
+ paramItem.type = siBuffer;
+ paramItem.data = NULL;
+ paramItem.len = 0;
+
+ slot = PK11_GetBestSlot(CKM_SHA256_HMAC, NULL);
+ if (!slot)
+ goto done;
+ symKey = PK11_ImportSymKey(slot, CKM_SHA256_HMAC,
+ PK11_OriginUnwrap, CKA_SIGN, &keyItem, NULL);
+ if (!symKey)
+ goto done;
+
+ hmac = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, symKey,
+ &paramItem);
+ if (!hmac)
+ goto done;
+ s = PK11_DigestBegin(hmac);
+ if (s != SECSuccess)
+ goto done;
+ s = PK11_DigestOp(hmac, (const unsigned char *)msg, (unsigned int)msg_len);
+ if (s != SECSuccess)
+ goto done;
+ unsigned int len=0;
+ s = PK11_DigestFinal(hmac, (unsigned char *)hmac_out, &len, DIGEST256_LEN);
+ if (s != SECSuccess || len != DIGEST256_LEN)
+ goto done;
+ ok = 1;
+
+ done:
+ if (hmac)
+ PK11_DestroyContext(hmac, PR_TRUE);
+ if (symKey)
+ PK11_FreeSymKey(symKey);
+ if (slot)
+ PK11_FreeSlot(slot);
+
+ tor_assert(ok);
+}
+
diff --git a/src/lib/crypt_ops/crypto_digest_openssl.c b/src/lib/crypt_ops/crypto_digest_openssl.c
new file mode 100644
index 0000000000..c631b0eac0
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_digest_openssl.c
@@ -0,0 +1,522 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_digest_openssl.c
+ * \brief Block of functions related with digest and xof utilities and
+ * operations (OpenSSL specific implementations).
+ **/
+
+#include "lib/container/smartlist.h"
+#include "lib/crypt_ops/crypto_digest.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+
+#include "keccak-tiny/keccak-tiny.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib/arch/bytes.h"
+
+#include "lib/crypt_ops/crypto_openssl_mgt.h"
+
+DISABLE_GCC_WARNING(redundant-decls)
+
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+ENABLE_GCC_WARNING(redundant-decls)
+
+/* Crypto digest functions */
+
+/** Compute the SHA1 digest of the <b>len</b> bytes on data stored in
+ * <b>m</b>. Write the DIGEST_LEN byte result into <b>digest</b>.
+ * Return 0 on success, -1 on failure.
+ */
+MOCK_IMPL(int,
+crypto_digest,(char *digest, const char *m, size_t len))
+{
+ tor_assert(m);
+ tor_assert(digest);
+ if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+/** Compute a 256-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
+ * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN256-byte result
+ * into <b>digest</b>. Return 0 on success, -1 on failure. */
+int
+crypto_digest256(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm)
+{
+ tor_assert(m);
+ tor_assert(digest);
+ tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
+
+ int ret = 0;
+ if (algorithm == DIGEST_SHA256) {
+ ret = (SHA256((const uint8_t*)m,len,(uint8_t*)digest) != NULL);
+ } else {
+#ifdef OPENSSL_HAS_SHA3
+ unsigned int dlen = DIGEST256_LEN;
+ ret = EVP_Digest(m, len, (uint8_t*)digest, &dlen, EVP_sha3_256(), NULL);
+#else
+ ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len)
+ > -1);
+#endif /* defined(OPENSSL_HAS_SHA3) */
+ }
+
+ if (!ret)
+ return -1;
+ return 0;
+}
+
+/** Compute a 512-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
+ * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN512-byte result
+ * into <b>digest</b>. Return 0 on success, -1 on failure. */
+int
+crypto_digest512(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm)
+{
+ tor_assert(m);
+ tor_assert(digest);
+ tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
+
+ int ret = 0;
+ if (algorithm == DIGEST_SHA512) {
+ ret = (SHA512((const unsigned char*)m,len,(unsigned char*)digest)
+ != NULL);
+ } else {
+#ifdef OPENSSL_HAS_SHA3
+ unsigned int dlen = DIGEST512_LEN;
+ ret = EVP_Digest(m, len, (uint8_t*)digest, &dlen, EVP_sha3_512(), NULL);
+#else
+ ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len)
+ > -1);
+#endif /* defined(OPENSSL_HAS_SHA3) */
+ }
+
+ if (!ret)
+ return -1;
+ return 0;
+}
+
+/** Intermediate information about the digest of a stream of data. */
+struct crypto_digest_t {
+ digest_algorithm_t algorithm; /**< Which algorithm is in use? */
+ /** State for the digest we're using. Only one member of the
+ * union is usable, depending on the value of <b>algorithm</b>. Note also
+ * that space for other members might not even be allocated!
+ */
+ union {
+ SHA_CTX sha1; /**< state for SHA1 */
+ SHA256_CTX sha2; /**< state for SHA256 */
+ SHA512_CTX sha512; /**< state for SHA512 */
+#ifdef OPENSSL_HAS_SHA3
+ EVP_MD_CTX *md;
+#else
+ keccak_state sha3; /**< state for SHA3-[256,512] */
+#endif
+ } d;
+};
+
+#ifdef TOR_UNIT_TESTS
+
+digest_algorithm_t
+crypto_digest_get_algorithm(crypto_digest_t *digest)
+{
+ tor_assert(digest);
+
+ return digest->algorithm;
+}
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/**
+ * Return the number of bytes we need to malloc in order to get a
+ * crypto_digest_t for <b>alg</b>, or the number of bytes we need to wipe
+ * when we free one.
+ */
+static size_t
+crypto_digest_alloc_bytes(digest_algorithm_t alg)
+{
+ /* Helper: returns the number of bytes in the 'f' field of 'st' */
+#define STRUCT_FIELD_SIZE(st, f) (sizeof( ((st*)0)->f ))
+ /* Gives the length of crypto_digest_t through the end of the field 'd' */
+#define END_OF_FIELD(f) (offsetof(crypto_digest_t, f) + \
+ STRUCT_FIELD_SIZE(crypto_digest_t, f))
+ switch (alg) {
+ case DIGEST_SHA1:
+ return END_OF_FIELD(d.sha1);
+ case DIGEST_SHA256:
+ return END_OF_FIELD(d.sha2);
+ case DIGEST_SHA512:
+ return END_OF_FIELD(d.sha512);
+#ifdef OPENSSL_HAS_SHA3
+ case DIGEST_SHA3_256: /* Fall through */
+ case DIGEST_SHA3_512:
+ return END_OF_FIELD(d.md);
+#else
+ case DIGEST_SHA3_256: /* Fall through */
+ case DIGEST_SHA3_512:
+ return END_OF_FIELD(d.sha3);
+#endif /* defined(OPENSSL_HAS_SHA3) */
+ default:
+ 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;
+#ifdef OPENSSL_HAS_SHA3
+ case DIGEST_SHA3_256:
+ r->d.md = EVP_MD_CTX_new();
+ if (!EVP_DigestInit(r->d.md, EVP_sha3_256())) {
+ crypto_digest_free(r);
+ return NULL;
+ }
+ break;
+ case DIGEST_SHA3_512:
+ r->d.md = EVP_MD_CTX_new();
+ if (!EVP_DigestInit(r->d.md, EVP_sha3_512())) {
+ crypto_digest_free(r);
+ return NULL;
+ }
+ break;
+#else /* !(defined(OPENSSL_HAS_SHA3)) */
+ case DIGEST_SHA3_256:
+ keccak_digest_init(&r->d.sha3, 256);
+ break;
+ case DIGEST_SHA3_512:
+ keccak_digest_init(&r->d.sha3, 512);
+ break;
+#endif /* defined(OPENSSL_HAS_SHA3) */
+ default:
+ tor_assert_unreached();
+ }
+
+ return r;
+}
+
+/** Allocate and return a new digest object to compute SHA1 digests.
+ */
+crypto_digest_t *
+crypto_digest_new(void)
+{
+ return crypto_digest_new_internal(DIGEST_SHA1);
+}
+
+/** Allocate and return a new digest object to compute 256-bit digests
+ * using <b>algorithm</b>.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest256_new`
+ * C_RUST_COUPLED: `crypto::digest::Sha256::default`
+ */
+crypto_digest_t *
+crypto_digest256_new(digest_algorithm_t algorithm)
+{
+ tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
+ return crypto_digest_new_internal(algorithm);
+}
+
+/** Allocate and return a new digest object to compute 512-bit digests
+ * using <b>algorithm</b>. */
+crypto_digest_t *
+crypto_digest512_new(digest_algorithm_t algorithm)
+{
+ tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
+ return crypto_digest_new_internal(algorithm);
+}
+
+/** Deallocate a digest object.
+ */
+void
+crypto_digest_free_(crypto_digest_t *digest)
+{
+ if (!digest)
+ return;
+#ifdef OPENSSL_HAS_SHA3
+ if (digest->algorithm == DIGEST_SHA3_256 ||
+ digest->algorithm == DIGEST_SHA3_512) {
+ if (digest->d.md) {
+ EVP_MD_CTX_free(digest->d.md);
+ }
+ }
+#endif /* defined(OPENSSL_HAS_SHA3) */
+ size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ memwipe(digest, 0, bytes);
+ tor_free(digest);
+}
+
+/** Add <b>len</b> bytes from <b>data</b> to the digest object.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_add_bytess`
+ * C_RUST_COUPLED: `crypto::digest::Sha256::process`
+ */
+void
+crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
+ size_t len)
+{
+ tor_assert(digest);
+ tor_assert(data);
+ /* Using the SHA*_*() calls directly means we don't support doing
+ * SHA in hardware. But so far the delay of getting the question
+ * to the hardware, and hearing the answer, is likely higher than
+ * just doing it ourselves. Hashes are fast.
+ */
+ switch (digest->algorithm) {
+ case DIGEST_SHA1:
+ SHA1_Update(&digest->d.sha1, (void*)data, len);
+ break;
+ case DIGEST_SHA256:
+ SHA256_Update(&digest->d.sha2, (void*)data, len);
+ break;
+ case DIGEST_SHA512:
+ SHA512_Update(&digest->d.sha512, (void*)data, len);
+ break;
+#ifdef OPENSSL_HAS_SHA3
+ case DIGEST_SHA3_256: /* FALLSTHROUGH */
+ case DIGEST_SHA3_512: {
+ int r = EVP_DigestUpdate(digest->d.md, data, len);
+ tor_assert(r);
+ }
+ break;
+#else /* !(defined(OPENSSL_HAS_SHA3)) */
+ case DIGEST_SHA3_256: /* FALLSTHROUGH */
+ case DIGEST_SHA3_512:
+ keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
+ break;
+#endif /* defined(OPENSSL_HAS_SHA3) */
+ default:
+ /* LCOV_EXCL_START */
+ tor_fragile_assert();
+ break;
+ /* LCOV_EXCL_STOP */
+ }
+}
+
+/** Compute the hash of the data that has been passed to the digest
+ * object; write the first out_len bytes of the result to <b>out</b>.
+ * <b>out_len</b> must be \<= DIGEST512_LEN.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_get_digest`
+ * C_RUST_COUPLED: `impl digest::FixedOutput for Sha256`
+ */
+void
+crypto_digest_get_digest(crypto_digest_t *digest,
+ char *out, size_t out_len)
+{
+ unsigned char r[DIGEST512_LEN];
+ tor_assert(digest);
+ tor_assert(out);
+ tor_assert(out_len <= crypto_digest_algorithm_get_length(digest->algorithm));
+
+ /* The SHA-3 code handles copying into a temporary ctx, and also can handle
+ * short output buffers by truncating appropriately. */
+ if (digest->algorithm == DIGEST_SHA3_256 ||
+ digest->algorithm == DIGEST_SHA3_512) {
+#ifdef OPENSSL_HAS_SHA3
+ unsigned dlen = (unsigned)
+ crypto_digest_algorithm_get_length(digest->algorithm);
+ EVP_MD_CTX *tmp = EVP_MD_CTX_new();
+ EVP_MD_CTX_copy(tmp, digest->d.md);
+ memset(r, 0xff, sizeof(r));
+ int res = EVP_DigestFinal(tmp, r, &dlen);
+ EVP_MD_CTX_free(tmp);
+ tor_assert(res == 1);
+ goto done;
+#else /* !(defined(OPENSSL_HAS_SHA3)) */
+ /* Tiny-Keccak handles copying into a temporary ctx, and also can handle
+ * short output buffers by truncating appropriately. */
+ keccak_digest_sum(&digest->d.sha3, (uint8_t *)out, out_len);
+ return;
+#endif /* defined(OPENSSL_HAS_SHA3) */
+ }
+
+ const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ crypto_digest_t tmpenv;
+ /* memcpy into a temporary ctx, since SHA*_Final clears the context */
+ memcpy(&tmpenv, digest, alloc_bytes);
+ switch (digest->algorithm) {
+ case DIGEST_SHA1:
+ SHA1_Final(r, &tmpenv.d.sha1);
+ break;
+ case DIGEST_SHA256:
+ SHA256_Final(r, &tmpenv.d.sha2);
+ break;
+ case DIGEST_SHA512:
+ SHA512_Final(r, &tmpenv.d.sha512);
+ break;
+//LCOV_EXCL_START
+ case DIGEST_SHA3_256: /* FALLSTHROUGH */
+ case DIGEST_SHA3_512:
+ default:
+ 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
+ }
+#ifdef OPENSSL_HAS_SHA3
+ done:
+#endif
+ memcpy(out, r, out_len);
+ memwipe(r, 0, sizeof(r));
+}
+
+/** Allocate and return a new digest object with the same state as
+ * <b>digest</b>
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_dup`
+ * C_RUST_COUPLED: `impl Clone for crypto::digest::Sha256`
+ */
+crypto_digest_t *
+crypto_digest_dup(const crypto_digest_t *digest)
+{
+ tor_assert(digest);
+ const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ crypto_digest_t *result = tor_memdup(digest, alloc_bytes);
+
+#ifdef OPENSSL_HAS_SHA3
+ if (digest->algorithm == DIGEST_SHA3_256 ||
+ digest->algorithm == DIGEST_SHA3_512) {
+ result->d.md = EVP_MD_CTX_new();
+ EVP_MD_CTX_copy(result->d.md, digest->d.md);
+ }
+#endif /* defined(OPENSSL_HAS_SHA3) */
+ return result;
+}
+
+/** Temporarily save the state of <b>digest</b> in <b>checkpoint</b>.
+ * Asserts that <b>digest</b> is a SHA1 digest object.
+ */
+void
+crypto_digest_checkpoint(crypto_digest_checkpoint_t *checkpoint,
+ const crypto_digest_t *digest)
+{
+ const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ tor_assert(bytes <= sizeof(checkpoint->mem));
+ memcpy(checkpoint->mem, digest, bytes);
+}
+
+/** Restore the state of <b>digest</b> from <b>checkpoint</b>.
+ * Asserts that <b>digest</b> is a SHA1 digest object. Requires that the
+ * state was previously stored with crypto_digest_checkpoint() */
+void
+crypto_digest_restore(crypto_digest_t *digest,
+ const crypto_digest_checkpoint_t *checkpoint)
+{
+ const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ memcpy(digest, checkpoint->mem, bytes);
+}
+
+/** Replace the state of the digest object <b>into</b> with the state
+ * of the digest object <b>from</b>. Requires that 'into' and 'from'
+ * have the same digest type.
+ */
+void
+crypto_digest_assign(crypto_digest_t *into,
+ const crypto_digest_t *from)
+{
+ tor_assert(into);
+ tor_assert(from);
+ tor_assert(into->algorithm == from->algorithm);
+ const size_t alloc_bytes = crypto_digest_alloc_bytes(from->algorithm);
+
+#ifdef OPENSSL_HAS_SHA3
+ if (from->algorithm == DIGEST_SHA3_256 ||
+ from->algorithm == DIGEST_SHA3_512) {
+ EVP_MD_CTX_copy(into->d.md, from->d.md);
+ return;
+ }
+#endif /* defined(OPENSSL_HAS_SHA3) */
+
+ memcpy(into,from,alloc_bytes);
+}
+
+/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
+ * at <b>digest_out</b> to the hash of the concatenation of those strings,
+ * plus the optional string <b>append</b>, computed with the algorithm
+ * <b>alg</b>.
+ * <b>out_len</b> must be \<= DIGEST512_LEN. */
+void
+crypto_digest_smartlist(char *digest_out, size_t len_out,
+ const smartlist_t *lst,
+ const char *append,
+ digest_algorithm_t alg)
+{
+ crypto_digest_smartlist_prefix(digest_out, len_out, NULL, lst, append, alg);
+}
+
+/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
+ * at <b>digest_out</b> to the hash of the concatenation of: the
+ * optional string <b>prepend</b>, those strings,
+ * and the optional string <b>append</b>, computed with the algorithm
+ * <b>alg</b>.
+ * <b>len_out</b> must be \<= DIGEST512_LEN. */
+void
+crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
+ const char *prepend,
+ const smartlist_t *lst,
+ const char *append,
+ digest_algorithm_t alg)
+{
+ 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,
+ crypto_digest_add_bytes(d, cp, strlen(cp)));
+ if (append)
+ crypto_digest_add_bytes(d, append, strlen(append));
+ crypto_digest_get_digest(d, digest_out, len_out);
+ crypto_digest_free(d);
+}
+
+/** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using
+ * the <b>key</b> of length <b>key_len</b>. Store the DIGEST256_LEN-byte
+ * result in <b>hmac_out</b>. Asserts on failure.
+ */
+void
+crypto_hmac_sha256(char *hmac_out,
+ const char *key, size_t key_len,
+ const char *msg, size_t msg_len)
+{
+ /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
+ tor_assert(key_len < INT_MAX);
+ tor_assert(msg_len < INT_MAX);
+ tor_assert(hmac_out);
+ unsigned char *rv = NULL;
+ rv = HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
+ (unsigned char*)hmac_out, NULL);
+ tor_assert(rv);
+}
+
diff --git a/src/lib/crypt_ops/crypto_ed25519.c b/src/lib/crypt_ops/crypto_ed25519.c
index 400f963898..0581529125 100644
--- a/src/lib/crypt_ops/crypto_ed25519.c
+++ b/src/lib/crypt_ops/crypto_ed25519.c
@@ -226,7 +226,7 @@ ed25519_keypair_generate(ed25519_keypair_t *keypair_out, int extra_strong)
int
ed25519_public_key_is_zero(const ed25519_public_key_t *pubkey)
{
- return tor_mem_is_zero((char*)pubkey->pubkey, ED25519_PUBKEY_LEN);
+ return safe_mem_is_zero((char*)pubkey->pubkey, ED25519_PUBKEY_LEN);
}
/* Return a heap-allocated array that contains <b>msg</b> prefixed by the
diff --git a/src/lib/crypt_ops/crypto_format.c b/src/lib/crypt_ops/crypto_format.c
index 84f73e5272..118cd79045 100644
--- a/src/lib/crypt_ops/crypto_format.c
+++ b/src/lib/crypt_ops/crypto_format.c
@@ -104,7 +104,7 @@ crypto_read_tagged_contents_from_file(const char *fname,
prefix[32] = 0;
/* Check type, extract tag. */
if (strcmpstart(prefix, "== ") || strcmpend(prefix, " ==") ||
- ! tor_mem_is_zero(prefix+strlen(prefix), 32-strlen(prefix))) {
+ ! fast_mem_is_zero(prefix+strlen(prefix), 32-strlen(prefix))) {
saved_errno = EINVAL;
goto end;
}
@@ -131,20 +131,27 @@ crypto_read_tagged_contents_from_file(const char *fname,
return r;
}
-/** Encode <b>pkey</b> as a base64-encoded string, without trailing "="
+/** Encode <b>pkey</b> as a base64-encoded string, including 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_BASE64_PADDED_LEN+1 bytes available.
+ * Can not fail.
+ *
+ * Careful! CURVE25519_BASE64_PADDED_LEN is one byte longer than
+ * ED25519_BASE64_LEN.
+ */
+void
curve25519_public_to_base64(char *output,
const curve25519_public_key_t *pkey)
{
char buf[128];
- base64_encode(buf, sizeof(buf),
- (const char*)pkey->public_key, CURVE25519_PUBKEY_LEN, 0);
- buf[CURVE25519_BASE64_PADDED_LEN] = '\0';
+ int n = base64_encode(buf, sizeof(buf),
+ (const char*)pkey->public_key,
+ CURVE25519_PUBKEY_LEN, 0);
+ /* These asserts should always succeed, unless there is a bug in
+ * base64_encode(). */
+ tor_assert(n == CURVE25519_BASE64_PADDED_LEN);
+ tor_assert(buf[CURVE25519_BASE64_PADDED_LEN] == '\0');
memcpy(output, buf, CURVE25519_BASE64_PADDED_LEN+1);
- return 0;
}
/** Try to decode a base64-encoded curve25519 public key from <b>input</b>
@@ -181,8 +188,7 @@ ed25519_fmt(const ed25519_public_key_t *pkey)
if (ed25519_public_key_is_zero(pkey)) {
strlcpy(formatted, "<unset>", sizeof(formatted));
} else {
- int r = ed25519_public_to_base64(formatted, pkey);
- tor_assert(!r);
+ ed25519_public_to_base64(formatted, pkey);
}
} else {
strlcpy(formatted, "<null>", sizeof(formatted));
@@ -202,28 +208,35 @@ ed25519_public_from_base64(ed25519_public_key_t *pkey,
/** Encode the public key <b>pkey</b> into the buffer at <b>output</b>,
* which must have space for ED25519_BASE64_LEN bytes of encoded key,
- * plus one byte for a terminating NUL. Return 0 on success, -1 on failure.
+ * plus one byte for a terminating NUL.
+ * Can not fail.
+ *
+ * Careful! ED25519_BASE64_LEN is one byte shorter than
+ * CURVE25519_BASE64_PADDED_LEN.
*/
-int
+void
ed25519_public_to_base64(char *output,
const ed25519_public_key_t *pkey)
{
- return digest256_to_base64(output, (const char *)pkey->pubkey);
+ digest256_to_base64(output, (const char *)pkey->pubkey);
}
/** Encode the signature <b>sig</b> into the buffer at <b>output</b>,
* which must have space for ED25519_SIG_BASE64_LEN bytes of encoded signature,
- * plus one byte for a terminating NUL. Return 0 on success, -1 on failure.
+ * plus one byte for a terminating NUL.
+ * Can not fail.
*/
-int
+void
ed25519_signature_to_base64(char *output,
const ed25519_signature_t *sig)
{
char buf[256];
int n = base64_encode_nopad(buf, sizeof(buf), sig->sig, ED25519_SIG_LEN);
+ /* These asserts should always succeed, unless there is a bug in
+ * base64_encode_nopad(). */
tor_assert(n == ED25519_SIG_BASE64_LEN);
+ tor_assert(buf[ED25519_SIG_BASE64_LEN] == '\0');
memcpy(output, buf, ED25519_SIG_BASE64_LEN+1);
- return 0;
}
/** Try to decode the string <b>input</b> into an ed25519 signature. On
@@ -233,16 +246,11 @@ int
ed25519_signature_from_base64(ed25519_signature_t *sig,
const char *input)
{
-
if (strlen(input) != ED25519_SIG_BASE64_LEN)
return -1;
- char buf[ED25519_SIG_BASE64_LEN+3];
- memcpy(buf, input, ED25519_SIG_BASE64_LEN);
- buf[ED25519_SIG_BASE64_LEN+0] = '=';
- buf[ED25519_SIG_BASE64_LEN+1] = '=';
- buf[ED25519_SIG_BASE64_LEN+2] = 0;
char decoded[128];
- int n = base64_decode(decoded, sizeof(decoded), buf, strlen(buf));
+ int n = base64_decode(decoded, sizeof(decoded), input,
+ ED25519_SIG_BASE64_LEN);
if (n < 0 || n != ED25519_SIG_LEN)
return -1;
memcpy(sig->sig, decoded, ED25519_SIG_LEN);
@@ -250,24 +258,26 @@ ed25519_signature_from_base64(ed25519_signature_t *sig,
return 0;
}
-/** Base64 encode DIGEST_LINE bytes from <b>digest</b>, remove the trailing =
+/** Base64 encode DIGEST_LEN bytes from <b>digest</b>, remove the trailing =
* characters, and store the nul-terminated result in the first
- * BASE64_DIGEST_LEN+1 bytes of <b>d64</b>. */
-/* XXXX unify with crypto_format.c code */
-int
+ * BASE64_DIGEST_LEN+1 bytes of <b>d64</b>.
+ * Can not fail. */
+void
digest_to_base64(char *d64, const char *digest)
{
char buf[256];
- base64_encode(buf, sizeof(buf), digest, DIGEST_LEN, 0);
- buf[BASE64_DIGEST_LEN] = '\0';
+ int n = base64_encode_nopad(buf, sizeof(buf),
+ (const uint8_t *)digest, DIGEST_LEN);
+ /* These asserts should always succeed, unless there is a bug in
+ * base64_encode_nopad(). */
+ tor_assert(n == BASE64_DIGEST_LEN);
+ tor_assert(buf[BASE64_DIGEST_LEN] == '\0');
memcpy(d64, buf, BASE64_DIGEST_LEN+1);
- return 0;
}
/** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without
* trailing newline or = characters), decode it and store the result in the
* first DIGEST_LEN bytes at <b>digest</b>. */
-/* XXXX unify with crypto_format.c code */
int
digest_from_base64(char *digest, const char *d64)
{
@@ -279,22 +289,24 @@ digest_from_base64(char *digest, const char *d64)
/** Base64 encode DIGEST256_LINE bytes from <b>digest</b>, remove the
* trailing = characters, and store the nul-terminated result in the first
- * BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>. */
- /* XXXX unify with crypto_format.c code */
-int
+ * BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>.
+ * Can not fail. */
+void
digest256_to_base64(char *d64, const char *digest)
{
char buf[256];
- base64_encode(buf, sizeof(buf), digest, DIGEST256_LEN, 0);
- buf[BASE64_DIGEST256_LEN] = '\0';
+ int n = base64_encode_nopad(buf, sizeof(buf),
+ (const uint8_t *)digest, DIGEST256_LEN);
+ /* These asserts should always succeed, unless there is a bug in
+ * base64_encode_nopad(). */
+ tor_assert(n == BASE64_DIGEST256_LEN);
+ tor_assert(buf[BASE64_DIGEST256_LEN] == '\0');
memcpy(d64, buf, BASE64_DIGEST256_LEN+1);
- return 0;
}
/** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without
* trailing newline or = characters), decode it and store the result in the
* first DIGEST256_LEN bytes at <b>digest</b>. */
-/* XXXX unify with crypto_format.c code */
int
digest256_from_base64(char *digest, const char *d64)
{
diff --git a/src/lib/crypt_ops/crypto_format.h b/src/lib/crypt_ops/crypto_format.h
index fe852e6a61..b4b3aa189c 100644
--- a/src/lib/crypt_ops/crypto_format.h
+++ b/src/lib/crypt_ops/crypto_format.h
@@ -33,18 +33,18 @@ ssize_t crypto_read_tagged_contents_from_file(const char *fname,
int ed25519_public_from_base64(struct ed25519_public_key_t *pkey,
const char *input);
-int ed25519_public_to_base64(char *output,
- const struct ed25519_public_key_t *pkey);
+void ed25519_public_to_base64(char *output,
+ const struct ed25519_public_key_t *pkey);
const char *ed25519_fmt(const struct ed25519_public_key_t *pkey);
int ed25519_signature_from_base64(struct ed25519_signature_t *sig,
const char *input);
-int ed25519_signature_to_base64(char *output,
- const struct ed25519_signature_t *sig);
+void ed25519_signature_to_base64(char *output,
+ const struct ed25519_signature_t *sig);
-int digest_to_base64(char *d64, const char *digest);
+void digest_to_base64(char *d64, const char *digest);
int digest_from_base64(char *digest, const char *d64);
-int digest256_to_base64(char *d64, const char *digest);
+void digest256_to_base64(char *d64, const char *digest);
int digest256_from_base64(char *digest, const char *d64);
#endif /* !defined(TOR_CRYPTO_FORMAT_H) */
diff --git a/src/lib/crypt_ops/crypto_hkdf.c b/src/lib/crypt_ops/crypto_hkdf.c
index fd2e701651..e0f3d65ad1 100644
--- a/src/lib/crypt_ops/crypto_hkdf.c
+++ b/src/lib/crypt_ops/crypto_hkdf.c
@@ -25,7 +25,7 @@
#include <openssl/kdf.h>
#define HAVE_OPENSSL_HKDF 1
#endif
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
#include <string.h>
@@ -109,7 +109,7 @@ crypto_expand_key_material_rfc5869_sha256_openssl(
return 0;
}
-#else
+#else /* !(defined(HAVE_OPENSSL_HKDF)) */
/**
* Perform RFC5869 HKDF computation using our own legacy implementation.
@@ -166,7 +166,7 @@ crypto_expand_key_material_rfc5869_sha256_legacy(
memwipe(mac, 0, sizeof(mac));
return 0;
}
-#endif
+#endif /* defined(HAVE_OPENSSL_HKDF) */
/** Expand some secret key material according to RFC5869, using SHA256 as the
* underlying hash. The <b>key_in_len</b> bytes at <b>key_in</b> are the
@@ -191,11 +191,11 @@ crypto_expand_key_material_rfc5869_sha256(
salt_in_len, info_in,
info_in_len,
key_out, key_out_len);
-#else
+#else /* !(defined(HAVE_OPENSSL_HKDF)) */
return crypto_expand_key_material_rfc5869_sha256_legacy(key_in,
key_in_len, salt_in,
salt_in_len, info_in,
info_in_len,
key_out, key_out_len);
-#endif
+#endif /* defined(HAVE_OPENSSL_HKDF) */
}
diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c
index 4040085c76..a16bf4e11a 100644
--- a/src/lib/crypt_ops/crypto_init.c
+++ b/src/lib/crypt_ops/crypto_init.c
@@ -12,6 +12,8 @@
#include "orconfig.h"
+#define CRYPTO_PRIVATE
+
#include "lib/crypt_ops/crypto_init.h"
#include "lib/crypt_ops/crypto_curve25519.h"
@@ -69,6 +71,8 @@ crypto_early_init(void)
if (crypto_init_siphash_key() < 0)
return -1;
+ crypto_rand_fast_init();
+
curve25519_init();
ed25519_init();
}
@@ -95,7 +99,7 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
(void)useAccel;
(void)accelName;
(void)accelDir;
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
#ifdef ENABLE_NSS
if (crypto_nss_late_init() < 0)
return -1;
@@ -111,6 +115,7 @@ crypto_thread_cleanup(void)
#ifdef ENABLE_OPENSSL
crypto_openssl_thread_cleanup();
#endif
+ destroy_thread_fast_rng();
}
/**
@@ -129,6 +134,8 @@ crypto_global_cleanup(void)
crypto_nss_global_cleanup();
#endif
+ crypto_rand_fast_shutdown();
+
crypto_early_initialized_ = 0;
crypto_global_initialized_ = 0;
have_seeded_siphash = 0;
@@ -145,6 +152,12 @@ crypto_prefork(void)
#ifdef ENABLE_NSS
crypto_nss_prefork();
#endif
+ /* It is not safe to share a fast_rng object across a fork boundary unless
+ * we actually have zero-on-fork support in map_anon.c. If we have
+ * drop-on-fork support, we will crash; if we have neither, we will yield
+ * a copy of the parent process's rng, which is scary and insecure.
+ */
+ destroy_thread_fast_rng();
}
/** Run operations that the crypto library requires to be happy again
diff --git a/src/lib/crypt_ops/crypto_init.h b/src/lib/crypt_ops/crypto_init.h
index 540d08eb56..8de3eb03ed 100644
--- a/src/lib/crypt_ops/crypto_init.h
+++ b/src/lib/crypt_ops/crypto_init.h
@@ -33,4 +33,4 @@ const char *crypto_get_header_version_string(void);
int tor_is_using_nss(void);
-#endif /* !defined(TOR_CRYPTO_H) */
+#endif /* !defined(TOR_CRYPTO_INIT_H) */
diff --git a/src/lib/crypt_ops/crypto_nss_mgt.h b/src/lib/crypt_ops/crypto_nss_mgt.h
index 72fd2a1229..4cfa9b42a4 100644
--- a/src/lib/crypt_ops/crypto_nss_mgt.h
+++ b/src/lib/crypt_ops/crypto_nss_mgt.h
@@ -29,6 +29,6 @@ void crypto_nss_global_cleanup(void);
void crypto_nss_prefork(void);
void crypto_nss_postfork(void);
-#endif
+#endif /* defined(ENABLE_NSS) */
-#endif /* !defined(TOR_CRYPTO_NSS_H) */
+#endif /* !defined(TOR_CRYPTO_NSS_MGT_H) */
diff --git a/src/lib/crypt_ops/crypto_ope.c b/src/lib/crypt_ops/crypto_ope.c
index 2186d2a939..4bd4b35706 100644
--- a/src/lib/crypt_ops/crypto_ope.c
+++ b/src/lib/crypt_ops/crypto_ope.c
@@ -57,9 +57,9 @@ ope_val_from_le(ope_val_t x)
((x) >> 8) |
(((x)&0xff) << 8);
}
-#else
+#else /* !(defined(WORDS_BIGENDIAN)) */
#define ope_val_from_le(x) (x)
-#endif
+#endif /* defined(WORDS_BIGENDIAN) */
/**
* Return a new AES256-CTR stream cipher object for <b>ope</b>, ready to yield
diff --git a/src/lib/crypt_ops/crypto_ope.h b/src/lib/crypt_ops/crypto_ope.h
index 610d956335..9778dfe0f0 100644
--- a/src/lib/crypt_ops/crypto_ope.h
+++ b/src/lib/crypt_ops/crypto_ope.h
@@ -41,6 +41,6 @@ struct aes_cnt_cipher;
STATIC struct aes_cnt_cipher *ope_get_cipher(const crypto_ope_t *ope,
uint32_t initial_idx);
STATIC uint64_t sum_values_from_cipher(struct aes_cnt_cipher *c, size_t n);
-#endif
+#endif /* defined(CRYPTO_OPE_PRIVATE) */
-#endif
+#endif /* !defined(CRYPTO_OPE_H) */
diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.c b/src/lib/crypt_ops/crypto_openssl_mgt.c
index c97815f9a4..9ec59e7c81 100644
--- a/src/lib/crypt_ops/crypto_openssl_mgt.c
+++ b/src/lib/crypt_ops/crypto_openssl_mgt.c
@@ -200,10 +200,10 @@ crypto_openssl_early_init(void)
OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
OPENSSL_INIT_ADD_ALL_CIPHERS |
OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
-#else
+#else /* !(defined(OPENSSL_1_1_API)) */
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
setup_openssl_threading();
diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.h b/src/lib/crypt_ops/crypto_openssl_mgt.h
index a3dd03aa04..111a2d12ed 100644
--- a/src/lib/crypt_ops/crypto_openssl_mgt.h
+++ b/src/lib/crypt_ops/crypto_openssl_mgt.h
@@ -84,6 +84,6 @@ int crypto_openssl_late_init(int useAccel, const char *accelName,
void crypto_openssl_thread_cleanup(void);
void crypto_openssl_global_cleanup(void);
-#endif /* ENABLE_OPENSSL */
+#endif /* defined(ENABLE_OPENSSL) */
#endif /* !defined(TOR_CRYPTO_OPENSSL_H) */
diff --git a/src/lib/crypt_ops/crypto_rand.c b/src/lib/crypt_ops/crypto_rand.c
index 0b1cb96c1b..a80a98f267 100644
--- a/src/lib/crypt_ops/crypto_rand.c
+++ b/src/lib/crypt_ops/crypto_rand.c
@@ -36,6 +36,7 @@
#include "lib/defs/digest_sizes.h"
#include "lib/crypt_ops/crypto_digest.h"
+#include "lib/ctime/di_ops.h"
#ifdef ENABLE_NSS
#include "lib/crypt_ops/crypto_nss_mgt.h"
@@ -46,7 +47,7 @@ DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/rand.h>
#include <openssl/sha.h>
ENABLE_GCC_WARNING(redundant-decls)
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
#ifdef ENABLE_NSS
#include <pk11pub.h>
@@ -314,7 +315,7 @@ crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
}
}
- if ((out_len < sanity_min_size) || !tor_mem_is_zero((char*)out, out_len))
+ if ((out_len < sanity_min_size) || !safe_mem_is_zero((char*)out, out_len))
return 0;
}
@@ -418,7 +419,7 @@ crypto_seed_openssl_rng(void)
else
return -1;
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
#ifdef ENABLE_NSS
/**
@@ -441,7 +442,7 @@ crypto_seed_nss_rng(void)
return load_entropy_ok ? 0 : -1;
}
-#endif
+#endif /* defined(ENABLE_NSS) */
/**
* Seed the RNG for any and all crypto libraries that we're using with bytes
@@ -519,13 +520,13 @@ crypto_rand_unmocked(char *to, size_t n)
#undef BUFLEN
}
-#else
+#else /* !(defined(ENABLE_NSS)) */
int r = RAND_bytes((unsigned char*)to, (int)n);
/* We consider a PRNG failure non-survivable. Let's assert so that we get a
* stack trace about where it happened.
*/
tor_assert(r >= 0);
-#endif
+#endif /* defined(ENABLE_NSS) */
}
/**
@@ -626,6 +627,6 @@ crypto_force_rand_ssleay(void)
RAND_set_rand_method(default_method);
return 1;
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
return 0;
}
diff --git a/src/lib/crypt_ops/crypto_rand.h b/src/lib/crypt_ops/crypto_rand.h
index 8a81a4acdc..a019287aa9 100644
--- a/src/lib/crypt_ops/crypto_rand.h
+++ b/src/lib/crypt_ops/crypto_rand.h
@@ -66,12 +66,37 @@ void crypto_fast_rng_free_(crypto_fast_rng_t *);
unsigned crypto_fast_rng_get_uint(crypto_fast_rng_t *rng, unsigned limit);
uint64_t crypto_fast_rng_get_uint64(crypto_fast_rng_t *rng, uint64_t limit);
+uint32_t crypto_fast_rng_get_u32(crypto_fast_rng_t *rng);
+uint64_t crypto_fast_rng_uint64_range(crypto_fast_rng_t *rng,
+ uint64_t min, uint64_t max);
double crypto_fast_rng_get_double(crypto_fast_rng_t *rng);
+/**
+ * Using the fast_rng <b>rng</b>, yield true with probability
+ * 1/<b>n</b>. Otherwise yield false.
+ *
+ * <b>n</b> must not be zero.
+ **/
+#define crypto_fast_rng_one_in_n(rng, n) \
+ (0 == (crypto_fast_rng_get_uint((rng), (n))))
+
+crypto_fast_rng_t *get_thread_fast_rng(void);
+
+#ifdef CRYPTO_PRIVATE
+/* These are only used from crypto_init.c */
+void destroy_thread_fast_rng(void);
+void crypto_rand_fast_init(void);
+void crypto_rand_fast_shutdown(void);
+#endif /* defined(CRYPTO_PRIVATE) */
+
#if defined(TOR_UNIT_TESTS)
/* Used for white-box testing */
size_t crypto_fast_rng_get_bytes_used_per_stream(void);
-#endif
+/* For deterministic prng implementations */
+void crypto_fast_rng_disable_reseed(crypto_fast_rng_t *rng);
+/* To override the prng for testing. */
+crypto_fast_rng_t *crypto_replace_thread_fast_rng(crypto_fast_rng_t *rng);
+#endif /* defined(TOR_UNIT_TESTS) */
#ifdef CRYPTO_RAND_PRIVATE
diff --git a/src/lib/crypt_ops/crypto_rand_fast.c b/src/lib/crypt_ops/crypto_rand_fast.c
index 34e763bf51..e6ceb42ccb 100644
--- a/src/lib/crypt_ops/crypto_rand_fast.c
+++ b/src/lib/crypt_ops/crypto_rand_fast.c
@@ -33,6 +33,7 @@
*/
#define CRYPTO_RAND_FAST_PRIVATE
+#define CRYPTO_PRIVATE
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_cipher.h"
@@ -41,11 +42,29 @@
#include "lib/intmath/cmp.h"
#include "lib/cc/ctassert.h"
#include "lib/malloc/map_anon.h"
+#include "lib/thread/threads.h"
#include "lib/log/util_bug.h"
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
#include <string.h>
+#ifdef NOINHERIT_CAN_FAIL
+#define CHECK_PID
+#endif
+
+#ifdef CHECK_PID
+#define PID_FIELD_LEN sizeof(pid_t)
+#else
+#define PID_FIELD_LEN 0
+#endif
+
/* Alias for CRYPTO_FAST_RNG_SEED_LEN to make our code shorter.
*/
#define SEED_LEN (CRYPTO_FAST_RNG_SEED_LEN)
@@ -57,7 +76,7 @@
/* The number of random bytes that we can yield to the user after each
* time we fill a crypto_fast_rng_t's buffer.
*/
-#define BUFLEN (MAPLEN - 2*sizeof(uint16_t) - SEED_LEN)
+#define BUFLEN (MAPLEN - 2*sizeof(uint16_t) - SEED_LEN - PID_FIELD_LEN)
/* The number of buffer refills after which we should fetch more
* entropy from crypto_strongest_rand().
@@ -76,10 +95,20 @@ CTASSERT(KEY_BITS == 128 || KEY_BITS == 192 || KEY_BITS == 256);
struct crypto_fast_rng_t {
/** How many more fills does this buffer have before we should mix
- * in the output of crypto_rand()? */
- uint16_t n_till_reseed;
+ * in the output of crypto_strongest_rand()?
+ *
+ * This value may be negative if unit tests are enabled. If so, it
+ * indicates that we should never mix in extra data from
+ * crypto_strongest_rand().
+ */
+ int16_t n_till_reseed;
/** How many bytes are remaining in cbuf.bytes? */
uint16_t bytes_left;
+#ifdef CHECK_PID
+ /** Which process owns this fast_rng? If this value is zero, we do not
+ * need to test the owner. */
+ pid_t owner;
+#endif
struct cbuf {
/** The seed (key and IV) that we will use the next time that we refill
* cbuf. */
@@ -122,24 +151,57 @@ crypto_fast_rng_new(void)
* long.
*
* Note that this object is NOT thread-safe. If you need a thread-safe
- * prng, use crypto_rand(), or wrap this in a mutex.
+ * prng, you should probably look at get_thread_fast_rng(). Alternatively,
+ * use crypto_rand(), wrap this in a mutex.
**/
crypto_fast_rng_t *
crypto_fast_rng_new_from_seed(const uint8_t *seed)
{
+ unsigned inherit = INHERIT_RES_KEEP;
/* We try to allocate this object as securely as we can, to avoid
* having it get dumped, swapped, or shared after fork.
*/
crypto_fast_rng_t *result = tor_mmap_anonymous(sizeof(*result),
- ANONMAP_PRIVATE | ANONMAP_NOINHERIT);
-
+ ANONMAP_PRIVATE | ANONMAP_NOINHERIT,
+ &inherit);
memcpy(result->buf.seed, seed, SEED_LEN);
/* Causes an immediate refill once the user asks for data. */
result->bytes_left = 0;
result->n_till_reseed = RESEED_AFTER;
+#ifdef CHECK_PID
+ if (inherit == INHERIT_RES_KEEP) {
+ /* This value will neither be dropped nor zeroed after fork, so we need to
+ * check our pid to make sure we are not sharing it across a fork. This
+ * can be expensive if the pid value isn't cached, sadly.
+ */
+ result->owner = getpid();
+ }
+#elif defined(_WIN32)
+ /* Windows can't fork(), so there's no need to noinherit. */
+#else
+ /* We decided above that noinherit would always do _something_. Assert here
+ * that we were correct. */
+ tor_assertf(inherit != INHERIT_RES_KEEP,
+ "We failed to create a non-inheritable memory region, even "
+ "though we believed such a failure to be impossible! This is "
+ "probably a bug in Tor support for your platform; please report "
+ "it.");
+#endif /* defined(CHECK_PID) || ... */
return result;
}
+#ifdef TOR_UNIT_TESTS
+/**
+ * Unit tests only: prevent a crypto_fast_rng_t from ever mixing in more
+ * entropy.
+ */
+void
+crypto_fast_rng_disable_reseed(crypto_fast_rng_t *rng)
+{
+ rng->n_till_reseed = -1;
+}
+#endif /* defined(TOR_UNIT_TESTS) */
+
/**
* Helper: create a crypto_cipher_t object from SEED_LEN bytes of
* input. The first KEY_LEN bytes are used as the stream cipher's key,
@@ -152,6 +214,26 @@ cipher_from_seed(const uint8_t *seed)
}
/**
+ * Helper: mix additional entropy into <b>rng</b> by using our XOF to mix the
+ * old value for the seed with some additional bytes from
+ * crypto_strongest_rand().
+ **/
+static void
+crypto_fast_rng_add_entopy(crypto_fast_rng_t *rng)
+{
+ crypto_xof_t *xof = crypto_xof_new();
+ crypto_xof_add_bytes(xof, rng->buf.seed, SEED_LEN);
+ {
+ uint8_t seedbuf[SEED_LEN];
+ crypto_strongest_rand(seedbuf, SEED_LEN);
+ crypto_xof_add_bytes(xof, seedbuf, SEED_LEN);
+ memwipe(seedbuf, 0, SEED_LEN);
+ }
+ crypto_xof_squeeze_bytes(xof, rng->buf.seed, SEED_LEN);
+ crypto_xof_free(xof);
+}
+
+/**
* Helper: refill the seed bytes and output buffer of <b>rng</b>, using
* the input seed bytes as input (key and IV) for the stream cipher.
*
@@ -161,22 +243,19 @@ cipher_from_seed(const uint8_t *seed)
static void
crypto_fast_rng_refill(crypto_fast_rng_t *rng)
{
- if (rng->n_till_reseed-- == 0) {
- /* It's time to reseed the RNG. We'll do this by using our XOF to mix the
- * old value for the seed with some additional bytes from
- * crypto_strongest_rand(). */
- crypto_xof_t *xof = crypto_xof_new();
- crypto_xof_add_bytes(xof, rng->buf.seed, SEED_LEN);
- {
- uint8_t seedbuf[SEED_LEN];
- crypto_strongest_rand(seedbuf, SEED_LEN);
- crypto_xof_add_bytes(xof, seedbuf, SEED_LEN);
- memwipe(seedbuf, 0, SEED_LEN);
- }
- crypto_xof_squeeze_bytes(xof, rng->buf.seed, SEED_LEN);
- crypto_xof_free(xof);
-
+ rng->n_till_reseed--;
+ if (rng->n_till_reseed == 0) {
+ /* It's time to reseed the RNG. */
+ crypto_fast_rng_add_entopy(rng);
rng->n_till_reseed = RESEED_AFTER;
+ } else if (rng->n_till_reseed < 0) {
+#ifdef TOR_UNIT_TESTS
+ /* Reseeding is disabled for testing; never do it on this prng. */
+ rng->n_till_reseed = -1;
+#else
+ /* If testing is disabled, this shouldn't be able to become negative. */
+ tor_assert_unreached();
+#endif /* defined(TOR_UNIT_TESTS) */
}
/* Now fill rng->buf with output from our stream cipher, initialized from
* that seed value. */
@@ -208,6 +287,27 @@ static void
crypto_fast_rng_getbytes_impl(crypto_fast_rng_t *rng, uint8_t *out,
const size_t n)
{
+#ifdef CHECK_PID
+ if (rng->owner) {
+ /* Note that we only need to do this check when we have owner set: that
+ * is, when our attempt to block inheriting failed, and the result was
+ * INHERIT_RES_KEEP.
+ *
+ * If the result was INHERIT_RES_DROP, then any attempt to access the rng
+ * memory after forking will crash.
+ *
+ * If the result was INHERIT_RES_ZERO, then forking will set the bytes_left
+ * and n_till_reseed fields to zero. This function will call
+ * crypto_fast_rng_refill(), which will in turn reseed the PRNG.
+ *
+ * So we only need to do this test in the case when mmap_anonymous()
+ * returned INHERIT_KEEP. We avoid doing it needlessly, since getpid() is
+ * often a system call, and that can be slow.
+ */
+ tor_assert(rng->owner == getpid());
+ }
+#endif /* defined(CHECK_PID) */
+
size_t bytes_to_yield = n;
while (bytes_to_yield) {
@@ -260,4 +360,80 @@ crypto_fast_rng_get_bytes_used_per_stream(void)
{
return BUFLEN;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/**
+ * Thread-local instance for our fast RNG.
+ **/
+static tor_threadlocal_t thread_rng;
+
+/**
+ * Return a per-thread fast RNG, initializing it if necessary.
+ *
+ * You do not need to free this yourself.
+ *
+ * It is NOT safe to share this value across threads.
+ **/
+crypto_fast_rng_t *
+get_thread_fast_rng(void)
+{
+ crypto_fast_rng_t *rng = tor_threadlocal_get(&thread_rng);
+
+ if (PREDICT_UNLIKELY(rng == NULL)) {
+ rng = crypto_fast_rng_new();
+ tor_threadlocal_set(&thread_rng, rng);
+ }
+
+ return rng;
+}
+
+/**
+ * Used when a thread is exiting: free the per-thread fast RNG if needed.
+ * Invoked from the crypto subsystem's thread-cleanup code.
+ **/
+void
+destroy_thread_fast_rng(void)
+{
+ crypto_fast_rng_t *rng = tor_threadlocal_get(&thread_rng);
+ if (!rng)
+ return;
+ crypto_fast_rng_free(rng);
+ tor_threadlocal_set(&thread_rng, NULL);
+}
+
+#ifdef TOR_UNIT_TESTS
+/**
+ * Replace the current thread's rng with <b>rng</b>. For use by the
+ * unit tests only. Returns the previous thread rng.
+ **/
+crypto_fast_rng_t *
+crypto_replace_thread_fast_rng(crypto_fast_rng_t *rng)
+{
+ crypto_fast_rng_t *old_rng = tor_threadlocal_get(&thread_rng);
+ tor_threadlocal_set(&thread_rng, rng);
+ return old_rng;
+}
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/**
+ * Initialize the global thread-local key that will be used to keep track
+ * of per-thread fast RNG instances. Called from the crypto subsystem's
+ * initialization code.
+ **/
+void
+crypto_rand_fast_init(void)
+{
+ tor_threadlocal_init(&thread_rng);
+}
+
+/**
+ * Initialize the global thread-local key that will be used to keep track
+ * of per-thread fast RNG instances. Called from the crypto subsystem's
+ * shutdown code.
+ **/
+void
+crypto_rand_fast_shutdown(void)
+{
+ destroy_thread_fast_rng();
+ tor_threadlocal_destroy(&thread_rng);
+}
diff --git a/src/lib/crypt_ops/crypto_rand_numeric.c b/src/lib/crypt_ops/crypto_rand_numeric.c
index d02c5cdcfa..ffbfa2d56c 100644
--- a/src/lib/crypt_ops/crypto_rand_numeric.c
+++ b/src/lib/crypt_ops/crypto_rand_numeric.c
@@ -155,7 +155,34 @@ crypto_fast_rng_get_uint64(crypto_fast_rng_t *rng, uint64_t limit)
}
/**
- * As crypto_rand_, but extract the result from a crypto_fast_rng_t.
+ * As crypto_rand_u32, but extract the result from a crypto_fast_rng_t.
+ */
+uint32_t
+crypto_fast_rng_get_u32(crypto_fast_rng_t *rng)
+{
+ uint32_t val;
+ crypto_fast_rng_getbytes(rng, (void*)&val, sizeof(val));
+ return val;
+}
+
+/**
+ * As crypto_rand_uint64_range(), but extract the result from a
+ * crypto_fast_rng_t.
+ */
+uint64_t
+crypto_fast_rng_uint64_range(crypto_fast_rng_t *rng,
+ uint64_t min, uint64_t max)
+{
+ /* Handle corrupted input */
+ if (BUG(min >= max)) {
+ return min;
+ }
+
+ return min + crypto_fast_rng_get_uint64(rng, max - min);
+}
+
+/**
+ * As crypto_rand_get_double() but extract the result from a crypto_fast_rng_t.
*/
double
crypto_fast_rng_get_double(crypto_fast_rng_t *rng)
@@ -164,3 +191,4 @@ crypto_fast_rng_get_double(crypto_fast_rng_t *rng)
crypto_fast_rng_getbytes(rng, (void*)&u, sizeof(u));
return ((double)u) / UINT_MAX_AS_DOUBLE;
}
+
diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c
index c9189b0dfc..c39d2e18d1 100644
--- a/src/lib/crypt_ops/crypto_rsa.c
+++ b/src/lib/crypt_ops/crypto_rsa.c
@@ -59,7 +59,7 @@ crypto_get_rsa_padding(int padding)
default: tor_assert(0); return -1; // LCOV_EXCL_LINE
}
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
/** Compare the public-key components of a and b. Return non-zero iff
* a==b. A NULL key is considered to be distinct from all non-NULL
diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h
index c1ea767f85..e9bfec2f85 100644
--- a/src/lib/crypt_ops/crypto_rsa.h
+++ b/src/lib/crypt_ops/crypto_rsa.h
@@ -119,7 +119,7 @@ struct rsa_st *crypto_pk_get_openssl_rsa_(crypto_pk_t *env);
crypto_pk_t *crypto_new_pk_from_openssl_rsa_(struct rsa_st *rsa);
MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,(
crypto_pk_t *env,int private));
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
#ifdef ENABLE_NSS
struct SECKEYPublicKeyStr;
@@ -129,7 +129,7 @@ const struct SECKEYPublicKeyStr *crypto_pk_get_nss_pubkey(
const crypto_pk_t *key);
const struct SECKEYPrivateKeyStr *crypto_pk_get_nss_privkey(
const crypto_pk_t *key);
-#endif
+#endif /* defined(ENABLE_NSS) */
void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src);
void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src);
@@ -140,6 +140,6 @@ struct SECItemStr;
STATIC int secitem_uint_cmp(const struct SECItemStr *a,
const struct SECItemStr *b);
#endif
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
-#endif
+#endif /* !defined(TOR_CRYPTO_RSA_H) */
diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c
index ad2ad38b66..612b7a0e64 100644
--- a/src/lib/crypt_ops/crypto_rsa_nss.c
+++ b/src/lib/crypt_ops/crypto_rsa_nss.c
@@ -156,7 +156,7 @@ crypto_pk_get_openssl_evp_pkey_,(crypto_pk_t *pk, int private))
tor_free(buf);
return result;
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
/** Allocate and return storage for a public key. The key itself will not yet
* be set.
diff --git a/src/lib/crypt_ops/crypto_s2k.c b/src/lib/crypt_ops/crypto_s2k.c
index 42276597d4..5cf98e3e64 100644
--- a/src/lib/crypt_ops/crypto_s2k.c
+++ b/src/lib/crypt_ops/crypto_s2k.c
@@ -285,7 +285,7 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
if (rv < 0)
return S2K_FAILED;
return (int)key_out_len;
-#else
+#else /* !(defined(ENABLE_OPENSSL)) */
SECItem passItem = { .type = siBuffer,
.data = (unsigned char *) secret,
.len = (int)secret_len };
@@ -325,7 +325,7 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
if (alg)
SECOID_DestroyAlgorithmID(alg, PR_TRUE);
return rv;
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
}
case S2K_TYPE_SCRYPT: {
diff --git a/src/lib/crypt_ops/crypto_util.c b/src/lib/crypt_ops/crypto_util.c
index 67a1a9eb92..5e3f4a87a1 100644
--- a/src/lib/crypt_ops/crypto_util.c
+++ b/src/lib/crypt_ops/crypto_util.c
@@ -30,7 +30,7 @@ DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/err.h>
#include <openssl/crypto.h>
ENABLE_GCC_WARNING(redundant-decls)
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
diff --git a/src/lib/crypt_ops/digestset.h b/src/lib/crypt_ops/digestset.h
index 91d53a0542..7d6d687342 100644
--- a/src/lib/crypt_ops/digestset.h
+++ b/src/lib/crypt_ops/digestset.h
@@ -26,4 +26,4 @@ void digestset_add(digestset_t *set, const char *addr);
int digestset_probably_contains(const digestset_t *set,
const char *addr);
-#endif
+#endif /* !defined(TOR_DIGESTSET_H) */
diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am
index 4730440143..1f58a33d38 100644
--- a/src/lib/crypt_ops/include.am
+++ b/src/lib/crypt_ops/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-crypt-ops-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_crypt_ops_a_SOURCES = \
src/lib/crypt_ops/crypto_cipher.c \
src/lib/crypt_ops/crypto_curve25519.c \
@@ -27,12 +28,14 @@ src_lib_libtor_crypt_ops_a_SOURCES = \
if USE_NSS
src_lib_libtor_crypt_ops_a_SOURCES += \
src/lib/crypt_ops/aes_nss.c \
+ src/lib/crypt_ops/crypto_digest_nss.c \
src/lib/crypt_ops/crypto_dh_nss.c \
src/lib/crypt_ops/crypto_nss_mgt.c \
src/lib/crypt_ops/crypto_rsa_nss.c
else
src_lib_libtor_crypt_ops_a_SOURCES += \
src/lib/crypt_ops/aes_openssl.c \
+ src/lib/crypt_ops/crypto_digest_openssl.c \
src/lib/crypt_ops/crypto_rsa_openssl.c
endif
@@ -50,6 +53,7 @@ src_lib_libtor_crypt_ops_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_crypt_ops_testing_a_CFLAGS = \
$(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/crypt_ops/aes.h \
src/lib/crypt_ops/compat_openssl.h \
diff --git a/src/lib/ctime/include.am b/src/lib/ctime/include.am
index b46c43ba0c..83942ca4e0 100644
--- a/src/lib/ctime/include.am
+++ b/src/lib/ctime/include.am
@@ -11,6 +11,7 @@ else
mulodi4_source=
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_ctime_a_SOURCES = \
$(mulodi4_source) \
src/ext/csiphash.c \
@@ -21,5 +22,6 @@ src_lib_libtor_ctime_testing_a_SOURCES = \
src_lib_libtor_ctime_a_CFLAGS = @CFLAGS_CONSTTIME@
src_lib_libtor_ctime_testing_a_CFLAGS = @CFLAGS_CONSTTIME@ $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/ctime/di_ops.h
diff --git a/src/lib/defs/dh_sizes.h b/src/lib/defs/dh_sizes.h
index a2ffbc51c2..b0d1eba0c5 100644
--- a/src/lib/defs/dh_sizes.h
+++ b/src/lib/defs/dh_sizes.h
@@ -19,4 +19,4 @@
/** Length of our legacy DH keys. */
#define DH1024_KEY_LEN (1024/8)
-#endif
+#endif /* !defined(TOR_DH_SIZES_H) */
diff --git a/src/lib/defs/digest_sizes.h b/src/lib/defs/digest_sizes.h
index 525e5209d6..a0dd97a74d 100644
--- a/src/lib/defs/digest_sizes.h
+++ b/src/lib/defs/digest_sizes.h
@@ -24,4 +24,4 @@
/** Length of the output of our 64-bit optimized message digests (SHA512). */
#define DIGEST512_LEN 64
-#endif
+#endif /* !defined(TOR_DIGEST_SIZES_H) */
diff --git a/src/lib/defs/include.am b/src/lib/defs/include.am
index 6a7f9114ea..84ee403771 100644
--- a/src/lib/defs/include.am
+++ b/src/lib/defs/include.am
@@ -1,6 +1,8 @@
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/defs/dh_sizes.h \
src/lib/defs/digest_sizes.h \
+ src/lib/defs/logging_types.h \
src/lib/defs/time.h \
src/lib/defs/x25519_sizes.h
diff --git a/src/lib/defs/logging_types.h b/src/lib/defs/logging_types.h
new file mode 100644
index 0000000000..57db818007
--- /dev/null
+++ b/src/lib/defs/logging_types.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file logging_types.h
+ *
+ * \brief Global definition for types used by logging systems.
+ **/
+
+#ifndef TOR_LOGGING_TYPES_H
+#define TOR_LOGGING_TYPES_H
+
+/* We define this here so that it can be used both by backtrace.h and
+ * log.h.
+ */
+
+/** Mask of zero or more log domains, OR'd together. */
+typedef uint64_t log_domain_mask_t;
+
+#endif
diff --git a/src/lib/defs/time.h b/src/lib/defs/time.h
index c25f5022c5..459afbf42d 100644
--- a/src/lib/defs/time.h
+++ b/src/lib/defs/time.h
@@ -20,4 +20,4 @@
/* How many nanoseconds per millisecond */
#define TOR_NSEC_PER_MSEC (1000*1000)
-#endif
+#endif /* !defined(TOR_TIME_DEFS_H) */
diff --git a/src/lib/defs/x25519_sizes.h b/src/lib/defs/x25519_sizes.h
index 8933a8866b..6431f0a2dd 100644
--- a/src/lib/defs/x25519_sizes.h
+++ b/src/lib/defs/x25519_sizes.h
@@ -33,4 +33,4 @@
#define ED25519_BASE64_LEN 43
#define ED25519_SIG_BASE64_LEN 86
-#endif
+#endif /* !defined(TOR_X25519_SIZES_H) */
diff --git a/src/lib/dispatch/.may_include b/src/lib/dispatch/.may_include
new file mode 100644
index 0000000000..7f2df5859f
--- /dev/null
+++ b/src/lib/dispatch/.may_include
@@ -0,0 +1,10 @@
+orconfig.h
+
+ext/tor_queue.h
+
+lib/cc/*.h
+lib/container/*.h
+lib/dispatch/*.h
+lib/intmath/*.h
+lib/log/*.h
+lib/malloc/*.h
diff --git a/src/lib/dispatch/dispatch.h b/src/lib/dispatch/dispatch.h
new file mode 100644
index 0000000000..a9e655409a
--- /dev/null
+++ b/src/lib/dispatch/dispatch.h
@@ -0,0 +1,114 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_DISPATCH_H
+#define TOR_DISPATCH_H
+
+#include "lib/dispatch/msgtypes.h"
+
+/**
+ * \file dispatch.h
+ * \brief Low-level APIs for message-passing system.
+ *
+ * This module implements message dispatch based on a set of short integer
+ * identifiers. For a higher-level interface, see pubsub.h.
+ *
+ * Each message is represented as a generic msg_t object, and is discriminated
+ * by its message_id_t. Messages are delivered by a dispatch_t object, which
+ * delivers each message to its recipients by a configured "channel".
+ *
+ * A "channel" is a means of delivering messages. Every message_id_t must
+ * be associated with exactly one channel, identified by channel_id_t.
+ * When a channel receives messages, a callback is invoked to either process
+ * the messages immediately, or to cause them to be processed later.
+ *
+ * Every message_id_t has zero or more associated receiver functions set up in
+ * the dispatch_t object. Once the dispatch_t object is created, receivers
+ * can be enabled or disabled [TODO], but not added or removed.
+ *
+ * Every message_id_t has an associated datatype, identified by a
+ * msg_type_id_t. These datatypes can be associated with functions to
+ * (for example) free them, or format them for debugging.
+ *
+ * To setup a dispatch_t object, first create a dispatch_cfg_t object, and
+ * configure messages with their types, channels, and receivers. Then, use
+ * dispatch_new() with that dispatch_cfg_t to create the dispatch_t object.
+ *
+ * (We use a two-phase contruction procedure here to enable better static
+ * reasoning about publish/subscribe relationships.)
+ *
+ * Once you have a dispatch_t, you can queue messages on it with
+ * dispatch_send*(), and cause those messages to be delivered with
+ * dispatch_flush().
+ **/
+
+/**
+ * A "dispatcher" is the highest-level object; it handles making sure that
+ * messages are received and delivered properly. Only the mainloop
+ * should handle this type directly.
+ */
+typedef struct dispatch_t dispatch_t;
+
+struct dispatch_cfg_t;
+
+dispatch_t *dispatch_new(const struct dispatch_cfg_t *cfg);
+
+/**
+ * Free a dispatcher. Tor does this at exit.
+ */
+#define dispatch_free(d) \
+ FREE_AND_NULL(dispatch_t, dispatch_free_, (d))
+
+void dispatch_free_(dispatch_t *);
+
+int dispatch_send(dispatch_t *d,
+ subsys_id_t sender,
+ channel_id_t channel,
+ message_id_t msg,
+ msg_type_id_t type,
+ msg_aux_data_t auxdata);
+
+int dispatch_send_msg(dispatch_t *d, msg_t *m);
+
+int dispatch_send_msg_unchecked(dispatch_t *d, msg_t *m);
+
+/* Flush up to <b>max_msgs</b> currently pending messages from the
+ * dispatcher. Messages that are not pending when this function are
+ * called, are not flushed by this call. Return 0 on success, -1 on
+ * unrecoverable error.
+ */
+int dispatch_flush(dispatch_t *, channel_id_t chan, int max_msgs);
+
+/**
+ * Function callback type used to alert some other module when a channel's
+ * queue changes from empty to nonempty.
+ *
+ * Ex 1: To cause messages to be processed immediately on-stack, this callback
+ * should invoke dispatch_flush() directly.
+ *
+ * Ex 2: To cause messages to be processed very soon, from the event queue,
+ * this callback should schedule an event callback to run dispatch_flush().
+ *
+ * Ex 3: To cause messages to be processed periodically, this function should
+ * do nothing, and a periodic event should invoke dispatch_flush().
+ **/
+typedef void (*dispatch_alertfn_t)(struct dispatch_t *,
+ channel_id_t, void *);
+
+int dispatch_set_alert_fn(dispatch_t *d, channel_id_t chan,
+ dispatch_alertfn_t fn, void *userdata);
+
+#define dispatch_free_msg(d,msg) \
+ STMT_BEGIN { \
+ msg_t **msg_tmp_ptr__ = &(msg); \
+ dispatch_free_msg_((d), *msg_tmp_ptr__); \
+ *msg_tmp_ptr__= NULL; \
+ } STMT_END
+void dispatch_free_msg_(const dispatch_t *d, msg_t *msg);
+
+char *dispatch_fmt_msg_data(const dispatch_t *d, const msg_t *msg);
+
+#endif /* !defined(TOR_DISPATCH_H) */
diff --git a/src/lib/dispatch/dispatch_cfg.c b/src/lib/dispatch/dispatch_cfg.c
new file mode 100644
index 0000000000..b3a72ec22f
--- /dev/null
+++ b/src/lib/dispatch/dispatch_cfg.c
@@ -0,0 +1,141 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dispatch_cfg.c
+ * \brief Create and configure a dispatch_cfg_t.
+ *
+ * A dispatch_cfg_t object is used to configure a set of messages and
+ * associated information before creating a dispatch_t.
+ */
+
+#define DISPATCH_PRIVATE
+
+#include "orconfig.h"
+#include "lib/dispatch/dispatch_cfg.h"
+#include "lib/dispatch/dispatch_cfg_st.h"
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_st.h"
+
+#include "lib/container/smartlist.h"
+#include "lib/malloc/malloc.h"
+
+/**
+ * Create and return a new dispatch_cfg_t.
+ **/
+dispatch_cfg_t *
+dcfg_new(void)
+{
+ dispatch_cfg_t *cfg = tor_malloc(sizeof(dispatch_cfg_t));
+ cfg->type_by_msg = smartlist_new();
+ cfg->chan_by_msg = smartlist_new();
+ cfg->fns_by_type = smartlist_new();
+ cfg->recv_by_msg = smartlist_new();
+ return cfg;
+}
+
+/**
+ * Associate a message with a datatype. Return 0 on success, -1 if a
+ * different type was previously associated with the message ID.
+ **/
+int
+dcfg_msg_set_type(dispatch_cfg_t *cfg, message_id_t msg,
+ msg_type_id_t type)
+{
+ smartlist_grow(cfg->type_by_msg, msg+1);
+ msg_type_id_t *oldval = smartlist_get(cfg->type_by_msg, msg);
+ if (oldval != NULL && *oldval != type) {
+ return -1;
+ }
+ if (!oldval)
+ smartlist_set(cfg->type_by_msg, msg, tor_memdup(&type, sizeof(type)));
+ return 0;
+}
+
+/**
+ * Associate a message with a channel. Return 0 on success, -1 if a
+ * different channel was previously associated with the message ID.
+ **/
+int
+dcfg_msg_set_chan(dispatch_cfg_t *cfg, message_id_t msg,
+ channel_id_t chan)
+{
+ smartlist_grow(cfg->chan_by_msg, msg+1);
+ channel_id_t *oldval = smartlist_get(cfg->chan_by_msg, msg);
+ if (oldval != NULL && *oldval != chan) {
+ return -1;
+ }
+ if (!oldval)
+ smartlist_set(cfg->chan_by_msg, msg, tor_memdup(&chan, sizeof(chan)));
+ return 0;
+}
+
+/**
+ * Associate a set of functions with a datatype. Return 0 on success, -1 if
+ * different functions were previously associated with the type.
+ **/
+int
+dcfg_type_set_fns(dispatch_cfg_t *cfg, msg_type_id_t type,
+ const dispatch_typefns_t *fns)
+{
+ smartlist_grow(cfg->fns_by_type, type+1);
+ dispatch_typefns_t *oldfns = smartlist_get(cfg->fns_by_type, type);
+ if (oldfns && (oldfns->free_fn != fns->free_fn ||
+ oldfns->fmt_fn != fns->fmt_fn))
+ return -1;
+ if (!oldfns)
+ smartlist_set(cfg->fns_by_type, type, tor_memdup(fns, sizeof(*fns)));
+ return 0;
+}
+
+/**
+ * Associate a receiver with a message ID. Multiple receivers may be
+ * associated with a single messasge ID.
+ *
+ * Return 0 on success, on failure.
+ **/
+int
+dcfg_add_recv(dispatch_cfg_t *cfg, message_id_t msg,
+ subsys_id_t sys, recv_fn_t fn)
+{
+ smartlist_grow(cfg->recv_by_msg, msg+1);
+ smartlist_t *receivers = smartlist_get(cfg->recv_by_msg, msg);
+ if (!receivers) {
+ receivers = smartlist_new();
+ smartlist_set(cfg->recv_by_msg, msg, receivers);
+ }
+
+ dispatch_rcv_t *rcv = tor_malloc(sizeof(dispatch_rcv_t));
+ rcv->sys = sys;
+ rcv->enabled = true;
+ rcv->fn = fn;
+ smartlist_add(receivers, (void*)rcv);
+ return 0;
+}
+
+/** Helper: release all storage held by <b>cfg</b>. */
+void
+dcfg_free_(dispatch_cfg_t *cfg)
+{
+ if (!cfg)
+ return;
+
+ SMARTLIST_FOREACH(cfg->type_by_msg, msg_type_id_t *, id, tor_free(id));
+ SMARTLIST_FOREACH(cfg->chan_by_msg, channel_id_t *, id, tor_free(id));
+ SMARTLIST_FOREACH(cfg->fns_by_type, dispatch_typefns_t *, f, tor_free(f));
+ smartlist_free(cfg->type_by_msg);
+ smartlist_free(cfg->chan_by_msg);
+ smartlist_free(cfg->fns_by_type);
+ SMARTLIST_FOREACH_BEGIN(cfg->recv_by_msg, smartlist_t *, receivers) {
+ if (!receivers)
+ continue;
+ SMARTLIST_FOREACH(receivers, dispatch_rcv_t *, rcv, tor_free(rcv));
+ smartlist_free(receivers);
+ } SMARTLIST_FOREACH_END(receivers);
+ smartlist_free(cfg->recv_by_msg);
+
+ tor_free(cfg);
+}
diff --git a/src/lib/dispatch/dispatch_cfg.h b/src/lib/dispatch/dispatch_cfg.h
new file mode 100644
index 0000000000..61fade7240
--- /dev/null
+++ b/src/lib/dispatch/dispatch_cfg.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_DISPATCH_CFG_H
+#define TOR_DISPATCH_CFG_H
+
+#include "lib/dispatch/msgtypes.h"
+
+/**
+ * A "dispatch_cfg" is the configuration used to set up a dispatcher.
+ * It is created and accessed with a set of dcfg_* functions, and then
+ * used with dispatcher_new() to make the dispatcher.
+ */
+typedef struct dispatch_cfg_t dispatch_cfg_t;
+
+dispatch_cfg_t *dcfg_new(void);
+
+int dcfg_msg_set_type(dispatch_cfg_t *cfg, message_id_t msg,
+ msg_type_id_t type);
+
+int dcfg_msg_set_chan(dispatch_cfg_t *cfg, message_id_t msg,
+ channel_id_t chan);
+
+int dcfg_type_set_fns(dispatch_cfg_t *cfg, msg_type_id_t type,
+ const dispatch_typefns_t *fns);
+
+int dcfg_add_recv(dispatch_cfg_t *cfg, message_id_t msg,
+ subsys_id_t sys, recv_fn_t fn);
+
+/** Free a dispatch_cfg_t. */
+#define dcfg_free(cfg) \
+ FREE_AND_NULL(dispatch_cfg_t, dcfg_free_, (cfg))
+
+void dcfg_free_(dispatch_cfg_t *cfg);
+
+#endif /* !defined(TOR_DISPATCH_CFG_H) */
diff --git a/src/lib/dispatch/dispatch_cfg_st.h b/src/lib/dispatch/dispatch_cfg_st.h
new file mode 100644
index 0000000000..57b6f0347f
--- /dev/null
+++ b/src/lib/dispatch/dispatch_cfg_st.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_DISPATCH_CFG_ST_H
+#define TOR_DISPATCH_CFG_ST_H
+
+struct smartlist_t;
+
+/* Information needed to create a dispatcher, but in a less efficient, more
+ * mutable format. */
+struct dispatch_cfg_t {
+ /** A list of msg_type_id_t (cast to void*), indexed by msg_t. */
+ struct smartlist_t *type_by_msg;
+ /** A list of channel_id_t (cast to void*), indexed by msg_t. */
+ struct smartlist_t *chan_by_msg;
+ /** A list of dispatch_rcv_t, indexed by msg_type_id_t. */
+ struct smartlist_t *fns_by_type;
+ /** A list of dispatch_typefns_t, indexed by msg_t. */
+ struct smartlist_t *recv_by_msg;
+};
+
+#endif /* !defined(TOR_DISPATCH_CFG_ST_H) */
diff --git a/src/lib/dispatch/dispatch_core.c b/src/lib/dispatch/dispatch_core.c
new file mode 100644
index 0000000000..da54f9b437
--- /dev/null
+++ b/src/lib/dispatch/dispatch_core.c
@@ -0,0 +1,260 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dispatch_core.c
+ * \brief Core module for sending and receiving messages.
+ */
+
+#define DISPATCH_PRIVATE
+#include "orconfig.h"
+
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_st.h"
+#include "lib/dispatch/dispatch_naming.h"
+
+#include "lib/malloc/malloc.h"
+#include "lib/log/util_bug.h"
+
+#include <string.h>
+
+/**
+ * Use <b>d</b> to drop all storage held for <b>msg</b>.
+ *
+ * (We need the dispatcher so we know how to free the auxiliary data.)
+ **/
+void
+dispatch_free_msg_(const dispatch_t *d, msg_t *msg)
+{
+ if (!msg)
+ return;
+
+ d->typefns[msg->type].free_fn(msg->aux_data__);
+ tor_free(msg);
+}
+
+/**
+ * Format the auxiliary data held by msg.
+ **/
+char *
+dispatch_fmt_msg_data(const dispatch_t *d, const msg_t *msg)
+{
+ if (!msg)
+ return NULL;
+
+ return d->typefns[msg->type].fmt_fn(msg->aux_data__);
+}
+
+/**
+ * Release all storage held by <b>d</b>.
+ **/
+void
+dispatch_free_(dispatch_t *d)
+{
+ if (d == NULL)
+ return;
+
+ size_t n_queues = d->n_queues;
+ for (size_t i = 0; i < n_queues; ++i) {
+ msg_t *m, *mtmp;
+ TOR_SIMPLEQ_FOREACH_SAFE(m, &d->queues[i].queue, next, mtmp) {
+ dispatch_free_msg(d, m);
+ }
+ }
+
+ size_t n_msgs = d->n_msgs;
+
+ for (size_t i = 0; i < n_msgs; ++i) {
+ tor_free(d->table[i]);
+ }
+ tor_free(d->table);
+ tor_free(d->typefns);
+ tor_free(d->queues);
+
+ // This is the only time we will treat d->cfg as non-const.
+ //dispatch_cfg_free_((dispatch_items_t *) d->cfg);
+
+ tor_free(d);
+}
+
+/**
+ * Tell the dispatcher to call <b>fn</b> with <b>userdata</b> whenever
+ * <b>chan</b> becomes nonempty. Return 0 on success, -1 on error.
+ **/
+int
+dispatch_set_alert_fn(dispatch_t *d, channel_id_t chan,
+ dispatch_alertfn_t fn, void *userdata)
+{
+ if (BUG(chan >= d->n_queues))
+ return -1;
+
+ dqueue_t *q = &d->queues[chan];
+ q->alert_fn = fn;
+ q->alert_fn_arg = userdata;
+ return 0;
+}
+
+/**
+ * Send a message on the appropriate channel notifying that channel if
+ * necessary.
+ *
+ * This function takes ownership of the auxiliary data; it can't be static or
+ * stack-allocated, and the caller is not allowed to use it afterwards.
+ *
+ * This function does not check the various vields of the message object for
+ * consistency.
+ **/
+int
+dispatch_send(dispatch_t *d,
+ subsys_id_t sender,
+ channel_id_t channel,
+ message_id_t msg,
+ msg_type_id_t type,
+ msg_aux_data_t auxdata)
+{
+ if (!d->table[msg]) {
+ /* Fast path: nobody wants this data. */
+
+ d->typefns[type].free_fn(auxdata);
+ return 0;
+ }
+
+ msg_t *m = tor_malloc(sizeof(msg_t));
+
+ m->sender = sender;
+ m->channel = channel;
+ m->msg = msg;
+ m->type = type;
+ memcpy(&m->aux_data__, &auxdata, sizeof(msg_aux_data_t));
+
+ return dispatch_send_msg(d, m);
+}
+
+int
+dispatch_send_msg(dispatch_t *d, msg_t *m)
+{
+ if (BUG(!d))
+ goto err;
+ if (BUG(!m))
+ goto err;
+ if (BUG(m->channel >= d->n_queues))
+ goto err;
+ if (BUG(m->msg >= d->n_msgs))
+ goto err;
+
+ dtbl_entry_t *ent = d->table[m->msg];
+ if (ent) {
+ if (BUG(m->type != ent->type))
+ goto err;
+ if (BUG(m->channel != ent->channel))
+ goto err;
+ }
+
+ return dispatch_send_msg_unchecked(d, m);
+ err:
+ /* Probably it isn't safe to free m, since type could be wrong. */
+ return -1;
+}
+
+/**
+ * Send a message on the appropriate queue, notifying that queue if necessary.
+ *
+ * This function takes ownership of the message object and its auxiliary data;
+ * it can't be static or stack-allocated, and the caller isn't allowed to use
+ * it afterwards.
+ *
+ * This function does not check the various fields of the message object for
+ * consistency, and can crash if they are out of range. Only functions that
+ * have already constructed the message in a safe way, or checked it for
+ * correctness themselves, should call this function.
+ **/
+int
+dispatch_send_msg_unchecked(dispatch_t *d, msg_t *m)
+{
+ /* Find the right queue. */
+ dqueue_t *q = &d->queues[m->channel];
+ bool was_empty = TOR_SIMPLEQ_EMPTY(&q->queue);
+
+ /* Append the message. */
+ TOR_SIMPLEQ_INSERT_TAIL(&q->queue, m, next);
+
+ if (debug_logging_enabled()) {
+ char *arg = dispatch_fmt_msg_data(d, m);
+ log_debug(LD_MESG,
+ "Queued: %s (%s) from %s, on %s.",
+ get_message_id_name(m->msg),
+ arg,
+ get_subsys_id_name(m->sender),
+ get_channel_id_name(m->channel));
+ tor_free(arg);
+ }
+
+ /* If we just made the queue nonempty for the first time, call the alert
+ * function. */
+ if (was_empty) {
+ q->alert_fn(d, m->channel, q->alert_fn_arg);
+ }
+
+ return 0;
+}
+
+/**
+ * Run all of the callbacks on <b>d</b> associated with <b>m</b>.
+ **/
+static void
+dispatcher_run_msg_cbs(const dispatch_t *d, msg_t *m)
+{
+ tor_assert(m->msg <= d->n_msgs);
+ dtbl_entry_t *ent = d->table[m->msg];
+ int n_fns = ent->n_fns;
+
+ if (debug_logging_enabled()) {
+ char *arg = dispatch_fmt_msg_data(d, m);
+ log_debug(LD_MESG,
+ "Delivering: %s (%s) from %s, on %s:",
+ get_message_id_name(m->msg),
+ arg,
+ get_subsys_id_name(m->sender),
+ get_channel_id_name(m->channel));
+ tor_free(arg);
+ }
+
+ int i;
+ for (i=0; i < n_fns; ++i) {
+ if (ent->rcv[i].enabled) {
+ log_debug(LD_MESG, " Delivering to %s.",
+ get_subsys_id_name(ent->rcv[i].sys));
+ ent->rcv[i].fn(m);
+ }
+ }
+}
+
+/**
+ * Run up to <b>max_msgs</b> callbacks for messages on the channel <b>ch</b>
+ * on the given dispatcher. Return 0 on success or recoverable failure,
+ * -1 on unrecoverable error.
+ **/
+int
+dispatch_flush(dispatch_t *d, channel_id_t ch, int max_msgs)
+{
+ if (BUG(ch >= d->n_queues))
+ return 0;
+
+ int n_flushed = 0;
+ dqueue_t *q = &d->queues[ch];
+
+ while (n_flushed < max_msgs) {
+ msg_t *m = TOR_SIMPLEQ_FIRST(&q->queue);
+ if (!m)
+ break;
+ TOR_SIMPLEQ_REMOVE_HEAD(&q->queue, next);
+ dispatcher_run_msg_cbs(d, m);
+ dispatch_free_msg(d, m);
+ ++n_flushed;
+ }
+
+ return 0;
+}
diff --git a/src/lib/dispatch/dispatch_naming.c b/src/lib/dispatch/dispatch_naming.c
new file mode 100644
index 0000000000..83d9a2d604
--- /dev/null
+++ b/src/lib/dispatch/dispatch_naming.c
@@ -0,0 +1,63 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#include "lib/cc/compat_compiler.h"
+
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/dispatch/msgtypes.h"
+
+#include "lib/container/namemap.h"
+#include "lib/container/namemap_st.h"
+
+#include "lib/log/util_bug.h"
+#include "lib/log/log.h"
+
+#include <stdlib.h>
+
+/** Global namemap for message IDs. */
+static namemap_t message_id_map = NAMEMAP_INIT();
+/** Global namemap for subsystem IDs. */
+static namemap_t subsys_id_map = NAMEMAP_INIT();
+/** Global namemap for channel IDs. */
+static namemap_t channel_id_map = NAMEMAP_INIT();
+/** Global namemap for message type IDs. */
+static namemap_t msg_type_id_map = NAMEMAP_INIT();
+
+void
+dispatch_naming_init(void)
+{
+}
+
+/* Helper macro: declare functions to map IDs to and from names for a given
+ * type in a namemap_t.
+ */
+#define DECLARE_ID_MAP_FNS(type) \
+ type##_id_t \
+ get_##type##_id(const char *name) \
+ { \
+ unsigned u = namemap_get_or_create_id(&type##_id_map, name); \
+ tor_assert(u != NAMEMAP_ERR); \
+ tor_assert(u != ERROR_ID); \
+ return (type##_id_t) u; \
+ } \
+ const char * \
+ get_##type##_id_name(type##_id_t id) \
+ { \
+ return namemap_fmt_name(&type##_id_map, id); \
+ } \
+ size_t \
+ get_num_##type##_ids(void) \
+ { \
+ return namemap_get_size(&type##_id_map); \
+ } \
+ EAT_SEMICOLON
+
+DECLARE_ID_MAP_FNS(message);
+DECLARE_ID_MAP_FNS(channel);
+DECLARE_ID_MAP_FNS(subsys);
+DECLARE_ID_MAP_FNS(msg_type);
diff --git a/src/lib/dispatch/dispatch_naming.h b/src/lib/dispatch/dispatch_naming.h
new file mode 100644
index 0000000000..fd6c83cc12
--- /dev/null
+++ b/src/lib/dispatch/dispatch_naming.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_DISPATCH_NAMING_H
+#define TOR_DISPATCH_NAMING_H
+
+#include "lib/dispatch/msgtypes.h"
+#include <stddef.h>
+
+/**
+ * Return an existing channel ID by name, allocating the channel ID if
+ * if necessary. Returns ERROR_ID if we have run out of
+ * channels
+ */
+channel_id_t get_channel_id(const char *);
+/**
+ * Return the name corresponding to a given channel ID.
+ **/
+const char *get_channel_id_name(channel_id_t);
+/**
+ * Return the total number of _named_ channel IDs.
+ **/
+size_t get_num_channel_ids(void);
+
+/* As above, but for messages. */
+message_id_t get_message_id(const char *);
+const char *get_message_id_name(message_id_t);
+size_t get_num_message_ids(void);
+
+/* As above, but for subsystems */
+subsys_id_t get_subsys_id(const char *);
+const char *get_subsys_id_name(subsys_id_t);
+size_t get_num_subsys_ids(void);
+
+/* As above, but for types. Note that types additionally must be
+ * "defined", if any message is to use them. */
+msg_type_id_t get_msg_type_id(const char *);
+const char *get_msg_type_id_name(msg_type_id_t);
+size_t get_num_msg_type_ids(void);
+
+void dispatch_naming_init(void);
+
+#endif /* !defined(TOR_DISPATCH_NAMING_H) */
diff --git a/src/lib/dispatch/dispatch_new.c b/src/lib/dispatch/dispatch_new.c
new file mode 100644
index 0000000000..b89ef43ea7
--- /dev/null
+++ b/src/lib/dispatch/dispatch_new.c
@@ -0,0 +1,174 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dispatch_new.c
+ * \brief Code to construct a dispatch_t from a dispatch_cfg_t.
+ **/
+
+#define DISPATCH_PRIVATE
+#include "orconfig.h"
+
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_st.h"
+#include "lib/dispatch/dispatch_cfg.h"
+#include "lib/dispatch/dispatch_cfg_st.h"
+
+#include "lib/cc/ctassert.h"
+#include "lib/intmath/cmp.h"
+#include "lib/malloc/malloc.h"
+#include "lib/log/util_bug.h"
+
+#include <string.h>
+
+/** Given a smartlist full of (possibly NULL) pointers to uint16_t values,
+ * return the largest value, or dflt if the list is empty. */
+static int
+max_in_sl(const smartlist_t *sl, int dflt)
+{
+ uint16_t *maxptr = NULL;
+ SMARTLIST_FOREACH_BEGIN(sl, uint16_t *, u) {
+ if (!maxptr)
+ maxptr = u;
+ else if (*u > *maxptr)
+ maxptr = u;
+ } SMARTLIST_FOREACH_END(u);
+
+ return maxptr ? *maxptr : dflt;
+}
+
+/* The above function is only safe to call if we are sure that channel_id_t
+ * and msg_type_id_t are really uint16_t. They should be so defined in
+ * msgtypes.h, but let's be extra cautious.
+ */
+CTASSERT(sizeof(uint16_t) == sizeof(msg_type_id_t));
+CTASSERT(sizeof(uint16_t) == sizeof(channel_id_t));
+
+/** Helper: Format an unformattable message auxiliary data item: just return a
+* copy of the string <>. */
+static char *
+type_fmt_nop(msg_aux_data_t arg)
+{
+ (void)arg;
+ return tor_strdup("<>");
+}
+
+/** Helper: Free an unfreeable message auxiliary data item: do nothing. */
+static void
+type_free_nop(msg_aux_data_t arg)
+{
+ (void)arg;
+}
+
+/** Type functions to use when no type functions are provided. */
+static dispatch_typefns_t nop_typefns = {
+ .free_fn = type_free_nop,
+ .fmt_fn = type_fmt_nop
+};
+
+/**
+ * Alert function to use when none is configured: do nothing.
+ **/
+static void
+alert_fn_nop(dispatch_t *d, channel_id_t ch, void *arg)
+{
+ (void)d;
+ (void)ch;
+ (void)arg;
+}
+
+/**
+ * Given a list of recvfn_t, create and return a new dtbl_entry_t mapping
+ * to each of those functions.
+ **/
+static dtbl_entry_t *
+dtbl_entry_from_lst(smartlist_t *receivers)
+{
+ if (!receivers)
+ return NULL;
+
+ size_t n_recv = smartlist_len(receivers);
+ dtbl_entry_t *ent;
+ ent = tor_malloc_zero(offsetof(dtbl_entry_t, rcv) +
+ sizeof(dispatch_rcv_t) * n_recv);
+
+ ent->n_fns = n_recv;
+
+ SMARTLIST_FOREACH_BEGIN(receivers, const dispatch_rcv_t *, rcv) {
+ memcpy(&ent->rcv[rcv_sl_idx], rcv, sizeof(*rcv));
+ if (rcv->enabled) {
+ ++ent->n_enabled;
+ }
+ } SMARTLIST_FOREACH_END(rcv);
+
+ return ent;
+}
+
+/** Create and return a new dispatcher from a given dispatch_cfg_t. */
+dispatch_t *
+dispatch_new(const dispatch_cfg_t *cfg)
+{
+ dispatch_t *d = tor_malloc_zero(sizeof(dispatch_t));
+
+ /* Any message that has a type or a receiver counts towards our messages */
+ const size_t n_msgs = MAX(smartlist_len(cfg->type_by_msg),
+ smartlist_len(cfg->recv_by_msg)) + 1;
+
+ /* Any channel that any message has counts towards the number of channels. */
+ const size_t n_chans = (size_t) MAX(1, max_in_sl(cfg->chan_by_msg,0)) + 1;
+
+ /* Any type that a message has, or that has functions, counts towards
+ * the number of types. */
+ const size_t n_types = (size_t) MAX(max_in_sl(cfg->type_by_msg,0),
+ smartlist_len(cfg->fns_by_type)) + 1;
+
+ d->n_msgs = n_msgs;
+ d->n_queues = n_chans;
+ d->n_types = n_types;
+
+ /* Initialize the array of type-functions. */
+ d->typefns = tor_calloc(n_types, sizeof(dispatch_typefns_t));
+ for (size_t i = 0; i < n_types; ++i) {
+ /* Default to no-op for everything... */
+ memcpy(&d->typefns[i], &nop_typefns, sizeof(dispatch_typefns_t));
+ }
+ SMARTLIST_FOREACH_BEGIN(cfg->fns_by_type, dispatch_typefns_t *, fns) {
+ /* Set the functions if they are provided. */
+ if (fns) {
+ if (fns->free_fn)
+ d->typefns[fns_sl_idx].free_fn = fns->free_fn;
+ if (fns->fmt_fn)
+ d->typefns[fns_sl_idx].fmt_fn = fns->fmt_fn;
+ }
+ } SMARTLIST_FOREACH_END(fns);
+
+ /* Initialize the message queues: one for each channel. */
+ d->queues = tor_calloc(d->n_queues, sizeof(dqueue_t));
+ for (size_t i = 0; i < d->n_queues; ++i) {
+ TOR_SIMPLEQ_INIT(&d->queues[i].queue);
+ d->queues[i].alert_fn = alert_fn_nop;
+ }
+
+ /* Build the dispatch tables mapping message IDs to receivers. */
+ d->table = tor_calloc(d->n_msgs, sizeof(dtbl_entry_t *));
+ SMARTLIST_FOREACH_BEGIN(cfg->recv_by_msg, smartlist_t *, rcv) {
+ d->table[rcv_sl_idx] = dtbl_entry_from_lst(rcv);
+ } SMARTLIST_FOREACH_END(rcv);
+
+ /* Fill in the empty entries in the dispatch tables:
+ * types and channels for each message. */
+ SMARTLIST_FOREACH_BEGIN(cfg->type_by_msg, msg_type_id_t *, type) {
+ if (d->table[type_sl_idx])
+ d->table[type_sl_idx]->type = *type;
+ } SMARTLIST_FOREACH_END(type);
+
+ SMARTLIST_FOREACH_BEGIN(cfg->chan_by_msg, channel_id_t *, chan) {
+ if (d->table[chan_sl_idx])
+ d->table[chan_sl_idx]->channel = *chan;
+ } SMARTLIST_FOREACH_END(chan);
+
+ return d;
+}
diff --git a/src/lib/dispatch/dispatch_st.h b/src/lib/dispatch/dispatch_st.h
new file mode 100644
index 0000000000..ee42518b5a
--- /dev/null
+++ b/src/lib/dispatch/dispatch_st.h
@@ -0,0 +1,108 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dispatch_st.h
+ *
+ * \brief private structures used for the dispatcher module
+ */
+
+#ifndef TOR_DISPATCH_ST_H
+#define TOR_DISPATCH_ST_H
+
+#ifdef DISPATCH_PRIVATE
+
+#include "lib/container/smartlist.h"
+
+/**
+ * Information about the recipient of a message.
+ **/
+typedef struct dispatch_rcv_t {
+ /** The subsystem receiving a message. */
+ subsys_id_t sys;
+ /** True iff this recipient is enabled. */
+ bool enabled;
+ /** The function that will handle the message. */
+ recv_fn_t fn;
+} dispatch_rcv_t;
+
+/**
+ * Information used by a dispatcher to handle and dispatch a single message
+ * ID. It maps that message ID to its type, channel, and list of receiver
+ * functions.
+ *
+ * This structure is used when the dispatcher is running.
+ **/
+typedef struct dtbl_entry_t {
+ /** The number of enabled non-stub subscribers for this message.
+ *
+ * Note that for now, this will be the same as <b>n_fns</b>, since there is
+ * no way to turn these subscribers on an off yet. */
+ uint16_t n_enabled;
+ /** The channel that handles this message. */
+ channel_id_t channel;
+ /** The associated C type for this message. */
+ msg_type_id_t type;
+ /**
+ * The number of functions pointers for subscribers that receive this
+ * message, in rcv. */
+ uint16_t n_fns;
+ /**
+ * The recipients for this message.
+ */
+ dispatch_rcv_t rcv[FLEXIBLE_ARRAY_MEMBER];
+} dtbl_entry_t;
+
+/**
+ * A queue of messages for a given channel, used by a live dispatcher.
+ */
+typedef struct dqueue_t {
+ /** The queue of messages itself. */
+ TOR_SIMPLEQ_HEAD( , msg_t) queue;
+ /** A function to be called when the queue becomes nonempty. */
+ dispatch_alertfn_t alert_fn;
+ /** An argument for the alert_fn. */
+ void *alert_fn_arg;
+} dqueue_t ;
+
+/**
+ * A single dispatcher for cross-module messages.
+ */
+struct dispatch_t {
+ /**
+ * The length of <b>table</b>: the number of message IDs that this
+ * dispatcher can handle.
+ */
+ size_t n_msgs;
+ /**
+ * The length of <b>queues</b>: the number of channels that this dispatcher
+ * has configured.
+ */
+ size_t n_queues;
+ /**
+ * The length of <b>typefns</b>: the number of C type IDs that this
+ * dispatcher has configured.
+ */
+ size_t n_types;
+ /**
+ * An array of message queues, indexed by channel ID.
+ */
+ dqueue_t *queues;
+ /**
+ * An array of entries about how to handle particular message types, indexed
+ * by message ID.
+ */
+ dtbl_entry_t **table;
+ /**
+ * An array of function tables for manipulating types, index by message
+ * type ID.
+ **/
+ dispatch_typefns_t *typefns;
+};
+
+#endif /* defined(DISPATCH_PRIVATE) */
+
+#endif /* !defined(TOR_DISPATCH_ST_H) */
diff --git a/src/lib/dispatch/include.am b/src/lib/dispatch/include.am
new file mode 100644
index 0000000000..4a0e0dfd90
--- /dev/null
+++ b/src/lib/dispatch/include.am
@@ -0,0 +1,27 @@
+
+noinst_LIBRARIES += src/lib/libtor-dispatch.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += src/lib/libtor-dispatch-testing.a
+endif
+
+# ADD_C_FILE: INSERT SOURCES HERE.
+src_lib_libtor_dispatch_a_SOURCES = \
+ src/lib/dispatch/dispatch_cfg.c \
+ src/lib/dispatch/dispatch_core.c \
+ src/lib/dispatch/dispatch_naming.c \
+ src/lib/dispatch/dispatch_new.c
+
+src_lib_libtor_dispatch_testing_a_SOURCES = \
+ $(src_lib_libtor_dispatch_a_SOURCES)
+src_lib_libtor_dispatch_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_lib_libtor_dispatch_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
+# ADD_C_FILE: INSERT HEADERS HERE.
+noinst_HEADERS += \
+ src/lib/dispatch/dispatch.h \
+ src/lib/dispatch/dispatch_cfg.h \
+ src/lib/dispatch/dispatch_cfg_st.h \
+ src/lib/dispatch/dispatch_naming.h \
+ src/lib/dispatch/dispatch_st.h \
+ src/lib/dispatch/msgtypes.h
diff --git a/src/lib/dispatch/msgtypes.h b/src/lib/dispatch/msgtypes.h
new file mode 100644
index 0000000000..b4c4a10248
--- /dev/null
+++ b/src/lib/dispatch/msgtypes.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file msgtypes.h
+ * \brief Types used for messages in the dispatcher code.
+ **/
+
+#ifndef TOR_DISPATCH_MSGTYPES_H
+#define TOR_DISPATCH_MSGTYPES_H
+
+#include <stdint.h>
+
+#include "ext/tor_queue.h"
+
+/**
+ * These types are aliases for subsystems, channels, and message IDs.
+ **/
+typedef uint16_t subsys_id_t;
+typedef uint16_t channel_id_t;
+typedef uint16_t message_id_t;
+
+/**
+ * This identifies a C type that can be sent along with a message.
+ **/
+typedef uint16_t msg_type_id_t;
+
+/**
+ * An ID value returned for *_type_t when none exists.
+ */
+#define ERROR_ID 65535
+
+/**
+ * Auxiliary (untyped) data sent along with a message.
+ *
+ * We define this as a union of a pointer and a u64, so that the integer
+ * types will have the same range across platforms.
+ **/
+typedef union {
+ void *ptr;
+ uint64_t u64;
+} msg_aux_data_t;
+
+/**
+ * Structure of a received message.
+ **/
+typedef struct msg_t {
+ TOR_SIMPLEQ_ENTRY(msg_t) next;
+ subsys_id_t sender;
+ channel_id_t channel;
+ message_id_t msg;
+ /** We could omit this field, since it is implicit in the message type, but
+ * IMO let's leave it in for safety. */
+ msg_type_id_t type;
+ /** Untyped auxiliary data. You shouldn't have to mess with this
+ * directly. */
+ msg_aux_data_t aux_data__;
+} msg_t;
+
+/**
+ * A function that a subscriber uses to receive a message.
+ **/
+typedef void (*recv_fn_t)(const msg_t *m);
+
+/**
+ * Table of functions to use for a given C type. Any omitted (NULL) functions
+ * will be treated as no-ops.
+ **/
+typedef struct dispatch_typefns_t {
+ /** Release storage held for the auxiliary data of this type. */
+ void (*free_fn)(msg_aux_data_t);
+ /** Format and return a newly allocated string describing the contents
+ * of this data element. */
+ char *(*fmt_fn)(msg_aux_data_t);
+} dispatch_typefns_t;
+
+#endif /* !defined(TOR_DISPATCH_MSGTYPES_H) */
diff --git a/src/lib/encoding/binascii.c b/src/lib/encoding/binascii.c
index de4d1648bb..fc64e014e7 100644
--- a/src/lib/encoding/binascii.c
+++ b/src/lib/encoding/binascii.c
@@ -84,7 +84,7 @@ base32_encode(char *dest, size_t destlen, const char *src, size_t srclen)
}
/** Implements base32 decoding as in RFC 4648.
- * Returns 0 if successful, -1 otherwise.
+ * Return the number of bytes decoded if successful; -1 otherwise.
*/
int
base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
@@ -147,7 +147,7 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
memset(tmp, 0, srclen); /* on the heap, this should be safe */
tor_free(tmp);
tmp = NULL;
- return 0;
+ return i;
}
#define BASE64_OPENSSL_LINELEN 64
@@ -321,8 +321,10 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
return (int) enclen;
}
-/** As base64_encode, but do not add any internal spaces or external padding
- * to the output stream. */
+/** As base64_encode, but do not add any internal spaces, and remove external
+ * padding from the output stream.
+ * dest must be at least base64_encode_size(srclen, 0), including space for
+ * the removed external padding. */
int
base64_encode_nopad(char *dest, size_t destlen,
const uint8_t *src, size_t srclen)
diff --git a/src/lib/encoding/binascii.h b/src/lib/encoding/binascii.h
index 44998bb85b..40c5593b11 100644
--- a/src/lib/encoding/binascii.h
+++ b/src/lib/encoding/binascii.h
@@ -58,4 +58,4 @@ size_t base32_encoded_size(size_t srclen);
void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);
int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen);
-#endif /* !defined(TOR_UTIL_FORMAT_H) */
+#endif /* !defined(TOR_BINASCII_H) */
diff --git a/src/lib/encoding/confline.c b/src/lib/encoding/confline.c
index 8110f3dd9c..fdb575e03f 100644
--- a/src/lib/encoding/confline.c
+++ b/src/lib/encoding/confline.c
@@ -82,6 +82,19 @@ config_line_find(const config_line_t *lines,
return NULL;
}
+/** As config_line_find(), but perform a case-insensitive comparison. */
+const config_line_t *
+config_line_find_case(const config_line_t *lines,
+ const char *key)
+{
+ const config_line_t *cl;
+ for (cl = lines; cl; cl = cl->next) {
+ if (!strcasecmp(cl->key, key))
+ return cl;
+ }
+ return NULL;
+}
+
/** Auxiliary function that does all the work of config_get_lines.
* <b>recursion_level</b> is the count of how many nested %includes we have.
* <b>opened_lst</b> will have a list of opened files if provided.
diff --git a/src/lib/encoding/confline.h b/src/lib/encoding/confline.h
index 3d9ae8a662..56ea36bf61 100644
--- a/src/lib/encoding/confline.h
+++ b/src/lib/encoding/confline.h
@@ -48,6 +48,8 @@ config_line_t *config_lines_dup_and_filter(const config_line_t *inp,
const char *key);
const config_line_t *config_line_find(const config_line_t *lines,
const char *key);
+const config_line_t *config_line_find_case(const config_line_t *lines,
+ const char *key);
int config_lines_eq(config_line_t *a, config_line_t *b);
int config_count_key(const config_line_t *a, const char *key);
void config_free_lines_(config_line_t *front);
diff --git a/src/lib/encoding/include.am b/src/lib/encoding/include.am
index 83e9211b6f..48d0120bfc 100644
--- a/src/lib/encoding/include.am
+++ b/src/lib/encoding/include.am
@@ -4,6 +4,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-encoding-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_encoding_a_SOURCES = \
src/lib/encoding/binascii.c \
src/lib/encoding/confline.c \
@@ -11,6 +12,7 @@ src_lib_libtor_encoding_a_SOURCES = \
src/lib/encoding/keyval.c \
src/lib/encoding/kvline.c \
src/lib/encoding/pem.c \
+ src/lib/encoding/qstring.c \
src/lib/encoding/time_fmt.c
src_lib_libtor_encoding_testing_a_SOURCES = \
@@ -18,6 +20,7 @@ src_lib_libtor_encoding_testing_a_SOURCES = \
src_lib_libtor_encoding_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_encoding_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/encoding/binascii.h \
src/lib/encoding/confline.h \
@@ -25,4 +28,5 @@ noinst_HEADERS += \
src/lib/encoding/keyval.h \
src/lib/encoding/kvline.h \
src/lib/encoding/pem.h \
+ src/lib/encoding/qstring.h \
src/lib/encoding/time_fmt.h
diff --git a/src/lib/encoding/keyval.h b/src/lib/encoding/keyval.h
index cd327b7a82..dcddfa3396 100644
--- a/src/lib/encoding/keyval.h
+++ b/src/lib/encoding/keyval.h
@@ -14,4 +14,4 @@
int string_is_key_value(int severity, const char *string);
-#endif
+#endif /* !defined(TOR_KEYVAL_H) */
diff --git a/src/lib/encoding/kvline.c b/src/lib/encoding/kvline.c
index 307adc3f12..d4a8f510ba 100644
--- a/src/lib/encoding/kvline.c
+++ b/src/lib/encoding/kvline.c
@@ -16,6 +16,7 @@
#include "lib/encoding/confline.h"
#include "lib/encoding/cstring.h"
#include "lib/encoding/kvline.h"
+#include "lib/encoding/qstring.h"
#include "lib/malloc/malloc.h"
#include "lib/string/compat_ctype.h"
#include "lib/string/printf.h"
@@ -54,6 +55,15 @@ line_has_no_key(const config_line_t *line)
}
/**
+ * Return true iff the value in <b>line</b> is not set.
+ **/
+static bool
+line_has_no_val(const config_line_t *line)
+{
+ return line->value == NULL || strlen(line->value) == 0;
+}
+
+/**
* Return true iff the all the lines in <b>line</b> can be encoded
* using <b>flags</b>.
**/
@@ -98,14 +108,25 @@ kvline_can_encode_lines(const config_line_t *line, unsigned flags)
* If KV_OMIT_KEYS is set in <b>flags</b>, then pairs with empty keys are
* allowed, and are encoded as 'Value'. Otherwise, such pairs are not
* allowed.
+ *
+ * If KV_OMIT_VALS is set in <b>flags</b>, then an empty value is
+ * encoded as 'Key', not as 'Key=' or 'Key=""'. Mutually exclusive with
+ * KV_OMIT_KEYS.
+ *
+ * KV_QUOTED_QSTRING is not supported.
*/
char *
kvline_encode(const config_line_t *line,
unsigned flags)
{
+ tor_assert(! (flags & KV_QUOTED_QSTRING));
+
if (!kvline_can_encode_lines(line, flags))
return NULL;
+ tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
+ (KV_OMIT_KEYS|KV_OMIT_VALS));
+
smartlist_t *elements = smartlist_new();
for (; line; line = line->next) {
@@ -126,7 +147,10 @@ kvline_encode(const config_line_t *line,
}
}
- if (esc) {
+ if ((flags & KV_OMIT_VALS) && line_has_no_val(line)) {
+ eq = "";
+ v = "";
+ } else if (esc) {
tmp = esc_for_log(line->value);
v = tmp;
} else {
@@ -151,17 +175,30 @@ kvline_encode(const config_line_t *line,
* allocated list of pairs on success, or NULL on failure.
*
* If KV_QUOTED is set in <b>flags</b>, then (double-)quoted values are
- * allowed. Otherwise, such values are not allowed.
+ * allowed and handled as C strings. Otherwise, such values are not allowed.
*
* If KV_OMIT_KEYS is set in <b>flags</b>, then values without keys are
* allowed. Otherwise, such values are not allowed.
+ *
+ * If KV_OMIT_VALS is set in <b>flags</b>, then keys without values are
+ * allowed. Otherwise, such keys are not allowed. Mutually exclusive with
+ * KV_OMIT_KEYS.
+ *
+ * If KV_QUOTED_QSTRING is set in <b>flags</b>, then double-quoted values
+ * are allowed and handled as QuotedStrings per qstring.c. Do not add
+ * new users of this flag.
*/
config_line_t *
kvline_parse(const char *line, unsigned flags)
{
+ tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
+ (KV_OMIT_KEYS|KV_OMIT_VALS));
+
const char *cp = line, *cplast = NULL;
- bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
- bool quoted = (flags & KV_QUOTED) != 0;
+ const bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
+ const bool omit_vals = (flags & KV_OMIT_VALS) != 0;
+ const bool quoted = (flags & (KV_QUOTED|KV_QUOTED_QSTRING)) != 0;
+ const bool c_quoted = (flags & (KV_QUOTED)) != 0;
config_line_t *result = NULL;
config_line_t **next_line = &result;
@@ -171,27 +208,33 @@ kvline_parse(const char *line, unsigned flags)
while (*cp) {
key = val = NULL;
+ /* skip all spaces */
{
size_t idx = strspn(cp, " \t\r\v\n");
cp += idx;
}
if (BUG(cp == cplast)) {
- /* If we didn't parse anything, this code is broken. */
+ /* If we didn't parse anything since the last loop, this code is
+ * broken. */
goto err; // LCOV_EXCL_LINE
}
cplast = cp;
if (! *cp)
break; /* End of string; we're done. */
- /* Possible formats are K=V, K="V", V, and "V", depending on flags. */
+ /* Possible formats are K=V, K="V", K, V, and "V", depending on flags. */
- /* Find the key. */
+ /* Find where the key ends */
if (*cp != '\"') {
size_t idx = strcspn(cp, " \t\r\v\n=");
if (cp[idx] == '=') {
key = tor_memdup_nulterm(cp, idx);
cp += idx + 1;
+ } else if (omit_vals) {
+ key = tor_memdup_nulterm(cp, idx);
+ cp += idx;
+ goto commit;
} else {
if (!omit_keys)
goto err;
@@ -203,7 +246,11 @@ kvline_parse(const char *line, unsigned flags)
if (!quoted)
goto err;
size_t len=0;
- cp = unescape_string(cp, &val, &len);
+ if (c_quoted) {
+ cp = unescape_string(cp, &val, &len);
+ } else {
+ cp = decode_qstring(cp, strlen(cp), &val, &len);
+ }
if (cp == NULL || len != strlen(val)) {
// The string contains a NUL or is badly coded.
goto err;
@@ -214,6 +261,7 @@ kvline_parse(const char *line, unsigned flags)
cp += idx;
}
+ commit:
if (key && strlen(key) == 0) {
/* We don't allow empty keys. */
goto err;
@@ -221,13 +269,15 @@ kvline_parse(const char *line, unsigned flags)
*next_line = tor_malloc_zero(sizeof(config_line_t));
(*next_line)->key = key ? key : tor_strdup("");
- (*next_line)->value = val;
+ (*next_line)->value = val ? val : tor_strdup("");
next_line = &(*next_line)->next;
key = val = NULL;
}
- if (!kvline_can_encode_lines(result, flags)) {
- goto err;
+ if (! (flags & KV_QUOTED_QSTRING)) {
+ if (!kvline_can_encode_lines(result, flags)) {
+ goto err;
+ }
}
return result;
diff --git a/src/lib/encoding/kvline.h b/src/lib/encoding/kvline.h
index 4eed30a223..dea2ce1809 100644
--- a/src/lib/encoding/kvline.h
+++ b/src/lib/encoding/kvline.h
@@ -17,6 +17,8 @@ struct config_line_t;
#define KV_QUOTED (1u<<0)
#define KV_OMIT_KEYS (1u<<1)
+#define KV_OMIT_VALS (1u<<2)
+#define KV_QUOTED_QSTRING (1u<<3)
struct config_line_t *kvline_parse(const char *line, unsigned flags);
char *kvline_encode(const struct config_line_t *line, unsigned flags);
diff --git a/src/lib/encoding/pem.h b/src/lib/encoding/pem.h
index 0bbb06a794..6b20350aa8 100644
--- a/src/lib/encoding/pem.h
+++ b/src/lib/encoding/pem.h
@@ -23,4 +23,4 @@ int pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen,
int pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen,
const char *objtype);
-#endif
+#endif /* !defined(TOR_PEM_H) */
diff --git a/src/lib/encoding/qstring.c b/src/lib/encoding/qstring.c
new file mode 100644
index 0000000000..a92d28c706
--- /dev/null
+++ b/src/lib/encoding/qstring.c
@@ -0,0 +1,90 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file qstring.c
+ * \brief Implement QuotedString parsing.
+ *
+ * Note that this is only used for controller authentication; do not
+ * create new users for this. Instead, prefer the cstring.c functions.
+ **/
+
+#include "orconfig.h"
+#include "lib/encoding/qstring.h"
+#include "lib/malloc/malloc.h"
+#include "lib/log/util_bug.h"
+
+/** If the first <b>in_len_max</b> characters in <b>start</b> contain a
+ * QuotedString, return the length of that
+ * string (as encoded, including quotes). Otherwise return -1. */
+static inline int
+get_qstring_length(const char *start, size_t in_len_max,
+ int *chars_out)
+{
+ const char *cp, *end;
+ int chars = 0;
+
+ if (*start != '\"')
+ return -1;
+
+ cp = start+1;
+ end = start+in_len_max;
+
+ /* Calculate length. */
+ while (1) {
+ if (cp >= end) {
+ return -1; /* Too long. */
+ } else if (*cp == '\\') {
+ if (++cp == end)
+ return -1; /* Can't escape EOS. */
+ ++cp;
+ ++chars;
+ } else if (*cp == '\"') {
+ break;
+ } else {
+ ++cp;
+ ++chars;
+ }
+ }
+ if (chars_out)
+ *chars_out = chars;
+ return (int)(cp - start+1);
+}
+
+/** Given a pointer to a string starting at <b>start</b> containing
+ * <b>in_len_max</b> characters, decode a string beginning with one double
+ * quote, containing any number of non-quote characters or characters escaped
+ * with a backslash, and ending with a final double quote. Place the resulting
+ * string (unquoted, unescaped) into a newly allocated string in *<b>out</b>;
+ * store its length in <b>out_len</b>. On success, return a pointer to the
+ * character immediately following the escaped string. On failure, return
+ * NULL. */
+const char *
+decode_qstring(const char *start, size_t in_len_max,
+ char **out, size_t *out_len)
+{
+ const char *cp, *end;
+ char *outp;
+ int len, n_chars = 0;
+
+ len = get_qstring_length(start, in_len_max, &n_chars);
+ if (len<0)
+ return NULL;
+
+ end = start+len-1; /* Index of last quote. */
+ tor_assert(*end == '\"');
+ outp = *out = tor_malloc(len+1);
+ *out_len = n_chars;
+
+ cp = start+1;
+ while (cp < end) {
+ if (*cp == '\\')
+ ++cp;
+ *outp++ = *cp++;
+ }
+ *outp = '\0';
+ tor_assert((outp - *out) == (int)*out_len);
+
+ return end+1;
+}
diff --git a/src/lib/encoding/qstring.h b/src/lib/encoding/qstring.h
new file mode 100644
index 0000000000..840e1044ce
--- /dev/null
+++ b/src/lib/encoding/qstring.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file qstring.h
+ * \brief Header for qstring.c
+ */
+
+#ifndef TOR_ENCODING_QSTRING_H
+#define TOR_ENCODING_QSTRING_H
+
+#include <stddef.h>
+
+const char *decode_qstring(const char *start, size_t in_len_max,
+ char **out, size_t *out_len);
+
+#endif /* !defined(TOR_ENCODING_QSTRING_H) */
diff --git a/src/lib/encoding/time_fmt.h b/src/lib/encoding/time_fmt.h
index 0ddeca57fc..d14bc1f902 100644
--- a/src/lib/encoding/time_fmt.h
+++ b/src/lib/encoding/time_fmt.h
@@ -41,4 +41,4 @@ int parse_iso_time_nospace(const char *cp, time_t *t);
int parse_http_time(const char *buf, struct tm *tm);
int format_time_interval(char *out, size_t out_len, long interval);
-#endif
+#endif /* !defined(TOR_TIME_FMT_H) */
diff --git a/src/lib/err/.may_include b/src/lib/err/.may_include
index daa1b6e4ca..314424545e 100644
--- a/src/lib/err/.may_include
+++ b/src/lib/err/.may_include
@@ -1,5 +1,6 @@
orconfig.h
lib/cc/*.h
+lib/defs/*.h
lib/err/*.h
lib/subsys/*.h
-lib/version/*.h \ No newline at end of file
+lib/version/*.h
diff --git a/src/lib/err/backtrace.c b/src/lib/err/backtrace.c
index 1d1b3bcfa3..e6cbe3d326 100644
--- a/src/lib/err/backtrace.c
+++ b/src/lib/err/backtrace.c
@@ -115,7 +115,7 @@ clean_backtrace(void **stack, size_t depth, const ucontext_t *ctx)
* that with a backtrace log. Send messages via the tor_log function at
* logger". */
void
-log_backtrace_impl(int severity, int domain, const char *msg,
+log_backtrace_impl(int severity, log_domain_mask_t domain, const char *msg,
tor_log_fn logger)
{
size_t depth;
@@ -240,7 +240,7 @@ remove_bt_handler(void)
#ifdef NO_BACKTRACE_IMPL
void
-log_backtrace_impl(int severity, int domain, const char *msg,
+log_backtrace_impl(int severity, log_domain_mask_t domain, const char *msg,
tor_log_fn logger)
{
logger(severity, domain, "%s. (Stack trace not available)", msg);
diff --git a/src/lib/err/backtrace.h b/src/lib/err/backtrace.h
index 9b313261e6..dcd22cfef2 100644
--- a/src/lib/err/backtrace.h
+++ b/src/lib/err/backtrace.h
@@ -12,11 +12,14 @@
#include "orconfig.h"
#include "lib/cc/compat_compiler.h"
+#include "lib/cc/torint.h"
+#include "lib/defs/logging_types.h"
-typedef void (*tor_log_fn)(int, unsigned, const char *fmt, ...)
+typedef void (*tor_log_fn)(int, log_domain_mask_t, const char *fmt, ...)
CHECK_PRINTF(3,4);
-void log_backtrace_impl(int severity, int domain, const char *msg,
+void log_backtrace_impl(int severity, log_domain_mask_t domain,
+ const char *msg,
tor_log_fn logger);
int configure_backtrace_handler(const char *tor_version);
void clean_up_backtrace_handler(void);
diff --git a/src/lib/err/include.am b/src/lib/err/include.am
index 43adcd2694..883ac91511 100644
--- a/src/lib/err/include.am
+++ b/src/lib/err/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-err-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_err_a_SOURCES = \
src/lib/err/backtrace.c \
src/lib/err/torerr.c \
@@ -15,6 +16,7 @@ src_lib_libtor_err_testing_a_SOURCES = \
src_lib_libtor_err_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_err_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/err/backtrace.h \
src/lib/err/torerr.h \
diff --git a/src/lib/err/torerr.h b/src/lib/err/torerr.h
index 0badaf7c6d..c2da6697a9 100644
--- a/src/lib/err/torerr.h
+++ b/src/lib/err/torerr.h
@@ -45,4 +45,4 @@ void tor_log_sigsafe_err_set_granularity(int ms);
int format_hex_number_sigsafe(unsigned long x, char *buf, int max_len);
int format_dec_number_sigsafe(unsigned long x, char *buf, int max_len);
-#endif /* !defined(TOR_TORLOG_H) */
+#endif /* !defined(TOR_TORERR_H) */
diff --git a/src/lib/evloop/include.am b/src/lib/evloop/include.am
index 6b0076272a..6595b3a34b 100644
--- a/src/lib/evloop/include.am
+++ b/src/lib/evloop/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-evloop-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_evloop_a_SOURCES = \
src/lib/evloop/compat_libevent.c \
src/lib/evloop/procmon.c \
@@ -12,12 +13,12 @@ src_lib_libtor_evloop_a_SOURCES = \
src/lib/evloop/token_bucket.c \
src/lib/evloop/workqueue.c
-
src_lib_libtor_evloop_testing_a_SOURCES = \
$(src_lib_libtor_evloop_a_SOURCES)
src_lib_libtor_evloop_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_evloop_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/evloop/compat_libevent.h \
src/lib/evloop/procmon.h \
diff --git a/src/lib/evloop/token_bucket.h b/src/lib/evloop/token_bucket.h
index 9398d2baa3..1ce6f1bf94 100644
--- a/src/lib/evloop/token_bucket.h
+++ b/src/lib/evloop/token_bucket.h
@@ -112,6 +112,6 @@ token_bucket_rw_get_write(const token_bucket_rw_t *bucket)
STATIC uint32_t rate_per_sec_to_rate_per_step(uint32_t rate);
-#endif
+#endif /* defined(TOKEN_BUCKET_PRIVATE) */
-#endif /* TOR_TOKEN_BUCKET_H */
+#endif /* !defined(TOR_TOKEN_BUCKET_H) */
diff --git a/src/lib/evloop/workqueue.c b/src/lib/evloop/workqueue.c
index b36a02da5e..015b694290 100644
--- a/src/lib/evloop/workqueue.c
+++ b/src/lib/evloop/workqueue.c
@@ -59,9 +59,6 @@ struct threadpool_s {
* <b>p</b> is work[p]. */
work_tailq_t work[WORKQUEUE_N_PRIORITIES];
- /** Weak RNG, used to decide when to ignore priority. */
- tor_weak_rng_t weak_rng;
-
/** The current 'update generation' of the threadpool. Any thread that is
* at an earlier generation needs to run the update function. */
unsigned generation;
@@ -238,7 +235,7 @@ worker_thread_extract_next_work(workerthread_t *thread)
this_queue = &pool->work[i];
if (!TOR_TAILQ_EMPTY(this_queue)) {
queue = this_queue;
- if (! tor_weak_random_one_in_n(&pool->weak_rng,
+ if (! crypto_fast_rng_one_in_n(get_thread_fast_rng(),
thread->lower_priority_chance)) {
/* Usually we'll just break now, so that we can get out of the loop
* and use the queue where we found work. But with a small
@@ -555,11 +552,6 @@ threadpool_new(int n_threads,
for (i = WORKQUEUE_PRIORITY_FIRST; i <= WORKQUEUE_PRIORITY_LAST; ++i) {
TOR_TAILQ_INIT(&pool->work[i]);
}
- {
- unsigned seed;
- crypto_rand((void*)&seed, sizeof(seed));
- tor_init_weak_random(&pool->weak_rng, seed);
- }
pool->new_thread_state_fn = new_thread_state_fn;
pool->new_thread_state_arg = arg;
diff --git a/src/lib/fdio/include.am b/src/lib/fdio/include.am
index 6c18f00a0d..545bbc929e 100644
--- a/src/lib/fdio/include.am
+++ b/src/lib/fdio/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-fdio-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_fdio_a_SOURCES = \
src/lib/fdio/fdio.c
@@ -13,5 +14,6 @@ src_lib_libtor_fdio_testing_a_SOURCES = \
src_lib_libtor_fdio_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_fdio_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/fdio/fdio.h
diff --git a/src/lib/fs/conffile.h b/src/lib/fs/conffile.h
index 7af9119dbb..29115e1085 100644
--- a/src/lib/fs/conffile.h
+++ b/src/lib/fs/conffile.h
@@ -20,4 +20,4 @@ int config_get_lines_include(const char *string, struct config_line_t **result,
int extended, int *has_include,
struct smartlist_t *opened_lst);
-#endif /* !defined(TOR_CONFLINE_H) */
+#endif /* !defined(TOR_CONFFILE_H) */
diff --git a/src/lib/fs/dir.h b/src/lib/fs/dir.h
index 826bc2dfc5..9ff81faa42 100644
--- a/src/lib/fs/dir.h
+++ b/src/lib/fs/dir.h
@@ -30,4 +30,4 @@ MOCK_DECL(int, check_private_dir, (const char *dirname, cpd_check_t check,
MOCK_DECL(struct smartlist_t *, tor_listdir, (const char *dirname));
-#endif
+#endif /* !defined(TOR_DIR_H) */
diff --git a/src/lib/fs/files.h b/src/lib/fs/files.h
index 52c94c914f..81dba8c140 100644
--- a/src/lib/fs/files.h
+++ b/src/lib/fs/files.h
@@ -27,7 +27,7 @@
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
-#endif
+#endif /* defined(_WIN32) */
#ifndef O_BINARY
#define O_BINARY 0
@@ -108,7 +108,7 @@ char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read,
* Tor is built for unit tests, or when Tor is built on an operating system
* without its own getdelim(). */
ssize_t compat_getdelim_(char **lineptr, size_t *n, int delim, FILE *stream);
-#endif
+#endif /* !defined(HAVE_GETDELIM) || defined(TOR_UNIT_TESTS) */
#ifdef HAVE_GETDELIM
/**
@@ -123,10 +123,10 @@ ssize_t compat_getdelim_(char **lineptr, size_t *n, int delim, FILE *stream);
*/
#define tor_getdelim(lineptr, n, delim, stream) \
getdelim((lineptr), (n), (delim), (stream))
-#else
+#else /* !(defined(HAVE_GETDELIM)) */
#define tor_getdelim(lineptr, n, delim, stream) \
compat_getdelim_((lineptr), (n), (delim), (stream))
-#endif
+#endif /* defined(HAVE_GETDELIM) */
#ifdef HAVE_GETLINE
/**
@@ -137,9 +137,9 @@ ssize_t compat_getdelim_(char **lineptr, size_t *n, int delim, FILE *stream);
*/
#define tor_getline(lineptr, n, stream) \
getline((lineptr), (n), (stream))
-#else
+#else /* !(defined(HAVE_GETLINE)) */
#define tor_getline(lineptr, n, stream) \
tor_getdelim((lineptr), (n), '\n', (stream))
-#endif
+#endif /* defined(HAVE_GETLINE) */
-#endif
+#endif /* !defined(TOR_FS_H) */
diff --git a/src/lib/fs/include.am b/src/lib/fs/include.am
index f33e4d6430..493db8f044 100644
--- a/src/lib/fs/include.am
+++ b/src/lib/fs/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-fs-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_fs_a_SOURCES = \
src/lib/fs/conffile.c \
src/lib/fs/dir.c \
@@ -25,6 +26,7 @@ src_lib_libtor_fs_testing_a_SOURCES = \
src_lib_libtor_fs_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_fs_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/fs/conffile.h \
src/lib/fs/dir.h \
diff --git a/src/lib/fs/lockfile.h b/src/lib/fs/lockfile.h
index 8aeee4cc7f..fc0281e253 100644
--- a/src/lib/fs/lockfile.h
+++ b/src/lib/fs/lockfile.h
@@ -17,4 +17,4 @@ tor_lockfile_t *tor_lockfile_lock(const char *filename, int blocking,
int *locked_out);
void tor_lockfile_unlock(tor_lockfile_t *lockfile);
-#endif
+#endif /* !defined(TOR_LOCKFILE_H) */
diff --git a/src/lib/fs/mmap.c b/src/lib/fs/mmap.c
index daaee1f9b1..f71c0cff7a 100644
--- a/src/lib/fs/mmap.c
+++ b/src/lib/fs/mmap.c
@@ -237,4 +237,4 @@ tor_munmap_file(tor_mmap_t *handle)
}
#else
#error "cannot implement tor_mmap_file"
-#endif /* defined(HAVE_MMAP) || ... || ... */
+#endif /* defined(HAVE_MMAP) || defined(RUNNING_DOXYGEN) || ... */
diff --git a/src/lib/fs/mmap.h b/src/lib/fs/mmap.h
index 18fb18a13c..61aad544b2 100644
--- a/src/lib/fs/mmap.h
+++ b/src/lib/fs/mmap.h
@@ -38,4 +38,4 @@ typedef struct tor_mmap_t {
tor_mmap_t *tor_mmap_file(const char *filename);
int tor_munmap_file(tor_mmap_t *handle);
-#endif
+#endif /* !defined(TOR_MMAP_H) */
diff --git a/src/lib/fs/path.h b/src/lib/fs/path.h
index 4675ac84e8..28a1838b88 100644
--- a/src/lib/fs/path.h
+++ b/src/lib/fs/path.h
@@ -27,4 +27,4 @@ void clean_fname_for_stat(char *name);
int get_parent_directory(char *fname);
char *make_path_absolute(char *fname);
-#endif
+#endif /* !defined(TOR_PATH_H) */
diff --git a/src/lib/fs/userdb.h b/src/lib/fs/userdb.h
index 5c39794873..5e5ddb89a3 100644
--- a/src/lib/fs/userdb.h
+++ b/src/lib/fs/userdb.h
@@ -21,6 +21,6 @@ struct passwd;
const struct passwd *tor_getpwnam(const char *username);
const struct passwd *tor_getpwuid(uid_t uid);
char *get_user_homedir(const char *username);
-#endif
+#endif /* !defined(_WIN32) */
-#endif
+#endif /* !defined(TOR_USERDB_H) */
diff --git a/src/lib/fs/winlib.h b/src/lib/fs/winlib.h
index 64a22439e5..7237226c76 100644
--- a/src/lib/fs/winlib.h
+++ b/src/lib/fs/winlib.h
@@ -17,6 +17,6 @@
#include <tchar.h>
HANDLE load_windows_system_library(const TCHAR *library_name);
-#endif
+#endif /* defined(_WIN32) */
-#endif
+#endif /* !defined(TOR_WINLIB_H) */
diff --git a/src/lib/geoip/country.h b/src/lib/geoip/country.h
index 9a8911d494..a24a1c4c0d 100644
--- a/src/lib/geoip/country.h
+++ b/src/lib/geoip/country.h
@@ -13,4 +13,4 @@ typedef int16_t country_t;
#define COUNTRY_MAX INT16_MAX
-#endif
+#endif /* !defined(TOR_COUNTRY_H) */
diff --git a/src/lib/geoip/include.am b/src/lib/geoip/include.am
index 9710d75ac7..ea426d14bc 100644
--- a/src/lib/geoip/include.am
+++ b/src/lib/geoip/include.am
@@ -4,6 +4,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-geoip-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_geoip_a_SOURCES = \
src/lib/geoip/geoip.c
@@ -12,6 +13,7 @@ src_lib_libtor_geoip_testing_a_SOURCES = \
src_lib_libtor_geoip_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_geoip_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/geoip/geoip.h \
src/lib/geoip/country.h
diff --git a/src/lib/intmath/addsub.h b/src/lib/intmath/addsub.h
index 83efa82919..3f745d457d 100644
--- a/src/lib/intmath/addsub.h
+++ b/src/lib/intmath/addsub.h
@@ -16,4 +16,4 @@
uint32_t tor_add_u32_nowrap(uint32_t a, uint32_t b);
-#endif /* !defined(TOR_INTMATH_MULDIV_H) */
+#endif /* !defined(TOR_INTMATH_ADDSUB_H) */
diff --git a/src/lib/intmath/include.am b/src/lib/intmath/include.am
index 45ee3bd53b..155ffa145a 100644
--- a/src/lib/intmath/include.am
+++ b/src/lib/intmath/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-intmath-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_intmath_a_SOURCES = \
src/lib/intmath/addsub.c \
src/lib/intmath/bits.c \
@@ -16,6 +17,7 @@ src_lib_libtor_intmath_testing_a_SOURCES = \
src_lib_libtor_intmath_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_intmath_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/intmath/addsub.h \
src/lib/intmath/cmp.h \
diff --git a/src/lib/intmath/logic.h b/src/lib/intmath/logic.h
index a4cecd69cc..b2f77462e1 100644
--- a/src/lib/intmath/logic.h
+++ b/src/lib/intmath/logic.h
@@ -17,4 +17,4 @@
/** Macro: true if two values have different boolean values. */
#define bool_neq(a,b) (!(a)!=!(b))
-#endif
+#endif /* !defined(HAVE_TOR_LOGIC_H) */
diff --git a/src/lib/intmath/weakrng.h b/src/lib/intmath/weakrng.h
index e26bf58cbb..40941e59b2 100644
--- a/src/lib/intmath/weakrng.h
+++ b/src/lib/intmath/weakrng.h
@@ -28,4 +28,4 @@ int32_t tor_weak_random_range(tor_weak_rng_t *rng, int32_t top);
* <b>n</b> */
#define tor_weak_random_one_in_n(rng, n) (0==tor_weak_random_range((rng),(n)))
-#endif
+#endif /* !defined(TOR_WEAKRNG_H) */
diff --git a/src/lib/lock/compat_mutex.h b/src/lib/lock/compat_mutex.h
index b63ce24024..e0c3d7cb78 100644
--- a/src/lib/lock/compat_mutex.h
+++ b/src/lib/lock/compat_mutex.h
@@ -48,7 +48,7 @@ typedef struct tor_mutex_t {
#else
/** No-threads only: Dummy variable so that tor_mutex_t takes up space. */
int _unused;
-#endif /* defined(USE_WIN32_MUTEX) || ... */
+#endif /* defined(USE_WIN32_THREADS) || ... */
} tor_mutex_t;
tor_mutex_t *tor_mutex_new(void);
diff --git a/src/lib/lock/include.am b/src/lib/lock/include.am
index 4e6f444347..1475b9911b 100644
--- a/src/lib/lock/include.am
+++ b/src/lib/lock/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-lock-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_lock_a_SOURCES = \
src/lib/lock/compat_mutex.c
@@ -20,5 +21,6 @@ src_lib_libtor_lock_testing_a_SOURCES = \
src_lib_libtor_lock_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_lock_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/lock/compat_mutex.h
diff --git a/src/lib/log/.may_include b/src/lib/log/.may_include
index 11c87f0a0d..54d96324db 100644
--- a/src/lib/log/.may_include
+++ b/src/lib/log/.may_include
@@ -1,6 +1,7 @@
orconfig.h
lib/cc/*.h
+lib/defs/*.h
lib/smartlist_core/*.h
lib/err/*.h
lib/fdio/*.h
diff --git a/src/lib/log/escape.h b/src/lib/log/escape.h
index 2f726186c5..0b9fc3406b 100644
--- a/src/lib/log/escape.h
+++ b/src/lib/log/escape.h
@@ -20,4 +20,4 @@ char *esc_for_log(const char *string) ATTR_MALLOC;
char *esc_for_log_len(const char *chars, size_t n) ATTR_MALLOC;
const char *escaped(const char *string);
-#endif /* !defined(TOR_TORLOG_H) */
+#endif /* !defined(TOR_ESCAPE_H) */
diff --git a/src/lib/log/include.am b/src/lib/log/include.am
index 9d3dbe3104..5b9f7113ba 100644
--- a/src/lib/log/include.am
+++ b/src/lib/log/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-log-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_log_a_SOURCES = \
src/lib/log/escape.c \
src/lib/log/ratelim.c \
@@ -21,6 +22,7 @@ src_lib_libtor_log_testing_a_SOURCES = \
src_lib_libtor_log_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_log_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/log/escape.h \
src/lib/log/ratelim.h \
diff --git a/src/lib/log/log.c b/src/lib/log/log.c
index d21d8d1d41..d95bf1ff6e 100644
--- a/src/lib/log/log.c
+++ b/src/lib/log/log.c
@@ -49,6 +49,7 @@
#include "lib/wallclock/approx_time.h"
#include "lib/wallclock/time_to_tm.h"
#include "lib/fdio/fdio.h"
+#include "lib/cc/ctassert.h"
#ifdef HAVE_ANDROID_LOG_H
#include <android/log.h>
@@ -154,7 +155,7 @@ severity_to_android_log_priority(int severity)
// LCOV_EXCL_STOP
}
}
-#endif // HAVE_ANDROID_LOG_H.
+#endif /* defined(HAVE_ANDROID_LOG_H) */
/** A mutex to guard changes to logfiles and logging. */
static tor_mutex_t log_mutex;
@@ -1021,7 +1022,7 @@ flush_pending_log_callbacks(void)
do {
SMARTLIST_FOREACH_BEGIN(messages, pending_log_message_t *, msg) {
const int severity = msg->severity;
- const int domain = msg->domain;
+ const log_domain_mask_t domain = msg->domain;
for (lf = logfiles; lf; lf = lf->next) {
if (! lf->callback || lf->seems_dead ||
! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) {
@@ -1232,7 +1233,7 @@ add_android_log(const log_severity_list_t *severity,
UNLOCK_LOGS();
return 0;
}
-#endif // HAVE_ANDROID_LOG_H.
+#endif /* defined(HAVE_ANDROID_LOG_H) */
/** If <b>level</b> is a valid log severity, return the corresponding
* numeric value. Otherwise, return -1. */
@@ -1268,9 +1269,14 @@ static const char *domain_list[] = {
"GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM",
"HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV",
"OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", "CHANNEL",
- "SCHED", "GUARD", "CONSDIFF", "DOS", "PROCESS", "PT", "BTRACK", NULL
+ "SCHED", "GUARD", "CONSDIFF", "DOS", "PROCESS", "PT", "BTRACK", "MESG",
+ NULL
};
+CTASSERT(ARRAY_LENGTH(domain_list) == N_LOGGING_DOMAINS + 1);
+
+CTASSERT((UINT64_C(1)<<(N_LOGGING_DOMAINS-1)) < LOWEST_RESERVED_LD_FLAG_);
+
/** Return a bitmask for the log domain for which <b>domain</b> is the name,
* or 0 if there is no such name. */
static log_domain_mask_t
@@ -1371,7 +1377,7 @@ parse_log_severity_config(const char **cfg_ptr,
if (!strcmp(domain, "*")) {
domains = ~0u;
} else {
- int d;
+ log_domain_mask_t d;
int negate=0;
if (*domain == '~') {
negate = 1;
diff --git a/src/lib/log/log.h b/src/lib/log/log.h
index dbc1c47021..c4a27782c3 100644
--- a/src/lib/log/log.h
+++ b/src/lib/log/log.h
@@ -11,10 +11,12 @@
**/
#ifndef TOR_TORLOG_H
+#define TOR_TORLOG_H
#include <stdarg.h>
#include "lib/cc/torint.h"
#include "lib/cc/compat_compiler.h"
+#include "lib/defs/logging_types.h"
#include "lib/testsupport/testsupport.h"
#ifdef HAVE_SYSLOG_H
@@ -55,81 +57,81 @@
/* Logging domains */
/** Catch-all for miscellaneous events and fatal errors. */
-#define LD_GENERAL (1u<<0)
+#define LD_GENERAL (UINT64_C(1)<<0)
/** The cryptography subsystem. */
-#define LD_CRYPTO (1u<<1)
+#define LD_CRYPTO (UINT64_C(1)<<1)
/** Networking. */
-#define LD_NET (1u<<2)
+#define LD_NET (UINT64_C(1)<<2)
/** Parsing and acting on our configuration. */
-#define LD_CONFIG (1u<<3)
+#define LD_CONFIG (UINT64_C(1)<<3)
/** Reading and writing from the filesystem. */
-#define LD_FS (1u<<4)
+#define LD_FS (UINT64_C(1)<<4)
/** Other servers' (non)compliance with the Tor protocol. */
-#define LD_PROTOCOL (1u<<5)
+#define LD_PROTOCOL (UINT64_C(1)<<5)
/** Memory management. */
-#define LD_MM (1u<<6)
+#define LD_MM (UINT64_C(1)<<6)
/** HTTP implementation. */
-#define LD_HTTP (1u<<7)
+#define LD_HTTP (UINT64_C(1)<<7)
/** Application (socks) requests. */
-#define LD_APP (1u<<8)
+#define LD_APP (UINT64_C(1)<<8)
/** Communication via the controller protocol. */
-#define LD_CONTROL (1u<<9)
+#define LD_CONTROL (UINT64_C(1)<<9)
/** Building, using, and managing circuits. */
-#define LD_CIRC (1u<<10)
+#define LD_CIRC (UINT64_C(1)<<10)
/** Hidden services. */
-#define LD_REND (1u<<11)
+#define LD_REND (UINT64_C(1)<<11)
/** Internal errors in this Tor process. */
-#define LD_BUG (1u<<12)
+#define LD_BUG (UINT64_C(1)<<12)
/** Learning and using information about Tor servers. */
-#define LD_DIR (1u<<13)
+#define LD_DIR (UINT64_C(1)<<13)
/** Learning and using information about Tor servers. */
-#define LD_DIRSERV (1u<<14)
+#define LD_DIRSERV (UINT64_C(1)<<14)
/** Onion routing protocol. */
-#define LD_OR (1u<<15)
+#define LD_OR (UINT64_C(1)<<15)
/** Generic edge-connection functionality. */
-#define LD_EDGE (1u<<16)
+#define LD_EDGE (UINT64_C(1)<<16)
#define LD_EXIT LD_EDGE
/** Bandwidth accounting. */
-#define LD_ACCT (1u<<17)
+#define LD_ACCT (UINT64_C(1)<<17)
/** Router history */
-#define LD_HIST (1u<<18)
+#define LD_HIST (UINT64_C(1)<<18)
/** OR handshaking */
-#define LD_HANDSHAKE (1u<<19)
+#define LD_HANDSHAKE (UINT64_C(1)<<19)
/** Heartbeat messages */
-#define LD_HEARTBEAT (1u<<20)
+#define LD_HEARTBEAT (UINT64_C(1)<<20)
/** Abstract channel_t code */
-#define LD_CHANNEL (1u<<21)
+#define LD_CHANNEL (UINT64_C(1)<<21)
/** Scheduler */
-#define LD_SCHED (1u<<22)
+#define LD_SCHED (UINT64_C(1)<<22)
/** Guard nodes */
-#define LD_GUARD (1u<<23)
+#define LD_GUARD (UINT64_C(1)<<23)
/** Generation and application of consensus diffs. */
-#define LD_CONSDIFF (1u<<24)
+#define LD_CONSDIFF (UINT64_C(1)<<24)
/** Denial of Service mitigation. */
-#define LD_DOS (1u<<25)
+#define LD_DOS (UINT64_C(1)<<25)
/** Processes */
-#define LD_PROCESS (1u<<26)
+#define LD_PROCESS (UINT64_C(1)<<26)
/** Pluggable Transports. */
-#define LD_PT (1u<<27)
+#define LD_PT (UINT64_C(1)<<27)
/** Bootstrap tracker. */
-#define LD_BTRACK (1u<<28)
-/** Number of logging domains in the code. */
-#define N_LOGGING_DOMAINS 29
-
-/** This log message is not safe to send to a callback-based logger
- * immediately. Used as a flag, not a log domain. */
-#define LD_NOCB (1u<<31)
-/** This log message should not include a function name, even if it otherwise
- * would. Used as a flag, not a log domain. */
-#define LD_NOFUNCNAME (1u<<30)
+#define LD_BTRACK (UINT64_C(1)<<28)
+/** Message-passing backend. */
+#define LD_MESG (UINT64_C(1)<<29)
+#define N_LOGGING_DOMAINS 30
+/** First bit that is reserved in log_domain_mask_t for non-domain flags. */
+#define LOWEST_RESERVED_LD_FLAG_ (UINT64_C(1)<<61)
#ifdef TOR_UNIT_TESTS
/** This log message should not be intercepted by mock_saving_logv */
-#define LD_NO_MOCK (1u<<29)
+#define LD_NO_MOCK (UINT64_C(1)<<61)
#endif
-/** Mask of zero or more log domains, OR'd together. */
-typedef uint32_t log_domain_mask_t;
+/** This log message is not safe to send to a callback-based logger
+ * immediately. Used as a flag, not a log domain. */
+#define LD_NOCB (UINT64_C(1)<<62)
+/** This log message should not include a function name, even if it otherwise
+ * would. Used as a flag, not a log domain. */
+#define LD_NOFUNCNAME (UINT64_C(1)<<63)
/** Configures which severities are logged for each logging domain for a given
* log target. */
@@ -140,7 +142,8 @@ typedef struct log_severity_list_t {
} log_severity_list_t;
/** Callback type used for add_callback_log. */
-typedef void (*log_callback)(int severity, uint32_t domain, const char *msg);
+typedef void (*log_callback)(int severity, log_domain_mask_t domain,
+ const char *msg);
void init_logging(int disable_startup_queue);
int parse_log_level(const char *level);
@@ -192,6 +195,21 @@ void tor_log_get_logfile_names(struct smartlist_t *out);
extern int log_global_min_severity_;
+#ifdef TOR_COVERAGE
+/* For coverage builds, we try to avoid our log_debug optimization, since it
+ * can have weird effects on internal macro coverage. */
+#define debug_logging_enabled() (1)
+#else
+static inline bool debug_logging_enabled(void);
+/**
+ * Return true iff debug logging is enabled for at least one domain.
+ */
+static inline bool debug_logging_enabled(void)
+{
+ return PREDICT_UNLIKELY(log_global_min_severity_ == LOG_DEBUG);
+}
+#endif /* defined(TOR_COVERAGE) */
+
void log_fn_(int severity, log_domain_mask_t domain,
const char *funcname, const char *format, ...)
CHECK_PRINTF(4,5);
@@ -221,8 +239,8 @@ void tor_log_string(int severity, log_domain_mask_t domain,
log_fn_ratelim_(ratelim, severity, domain, __FUNCTION__, args)
#define log_debug(domain, args...) \
STMT_BEGIN \
- if (PREDICT_UNLIKELY(log_global_min_severity_ == LOG_DEBUG)) \
- log_fn_(LOG_DEBUG, domain, __FUNCTION__, args); \
+ if (debug_logging_enabled()) \
+ log_fn_(LOG_DEBUG, domain, __FUNCTION__, args); \
STMT_END
#define log_info(domain, args...) \
log_fn_(LOG_INFO, domain, __FUNCTION__, args)
@@ -239,8 +257,8 @@ void tor_log_string(int severity, log_domain_mask_t domain,
#define log_debug(domain, args, ...) \
STMT_BEGIN \
- if (PREDICT_UNLIKELY(log_global_min_severity_ == LOG_DEBUG)) \
- log_fn_(LOG_DEBUG, domain, __FUNCTION__, args, ##__VA_ARGS__); \
+ if (debug_logging_enabled()) \
+ log_fn_(LOG_DEBUG, domain, __FUNCTION__, args, ##__VA_ARGS__); \
STMT_END
#define log_info(domain, args,...) \
log_fn_(LOG_INFO, domain, __FUNCTION__, args, ##__VA_ARGS__)
@@ -278,5 +296,4 @@ MOCK_DECL(STATIC void, logv, (int severity, log_domain_mask_t domain,
va_list ap) CHECK_PRINTF(5,0));
#endif
-# define TOR_TORLOG_H
#endif /* !defined(TOR_TORLOG_H) */
diff --git a/src/lib/log/ratelim.h b/src/lib/log/ratelim.h
index 48edd7c849..1db54ba726 100644
--- a/src/lib/log/ratelim.h
+++ b/src/lib/log/ratelim.h
@@ -50,4 +50,4 @@ typedef struct ratelim_t {
char *rate_limit_log(ratelim_t *lim, time_t now);
-#endif
+#endif /* !defined(TOR_RATELIM_H) */
diff --git a/src/lib/log/util_bug.c b/src/lib/log/util_bug.c
index c65a91ae9e..76b97c1a08 100644
--- a/src/lib/log/util_bug.c
+++ b/src/lib/log/util_bug.c
@@ -70,25 +70,45 @@ tor_set_failed_assertion_callback(void (*fn)(void))
/** Helper for tor_assert: report the assertion failure. */
void
+CHECK_PRINTF(5, 6)
tor_assertion_failed_(const char *fname, unsigned int line,
- const char *func, const char *expr)
+ const char *func, const char *expr,
+ const char *fmt, ...)
{
- char buf[256];
+ char *buf = NULL;
+ char *extra = NULL;
+ va_list ap;
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
+ if (fmt) {
+ va_start(ap,fmt);
+ tor_vasprintf(&extra, fmt, ap);
+ va_end(ap);
+ }
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
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);
+ tor_asprintf(&buf, "Assertion %s failed in %s at %s:%u: %s",
+ expr, func, fname, line, extra ? extra : "");
+ tor_free(extra);
log_backtrace(LOG_ERR, LD_BUG, buf);
+ tor_free(buf);
}
/** Helper for tor_assert_nonfatal: report the assertion failure. */
void
+CHECK_PRINTF(6, 7)
tor_bug_occurred_(const char *fname, unsigned int line,
const char *func, const char *expr,
- int once)
+ int once, const char *fmt, ...)
{
- char buf[256];
+ char *buf = NULL;
const char *once_str = once ?
" (Future instances of this warning will be silenced.)": "";
if (! expr) {
@@ -98,7 +118,7 @@ tor_bug_occurred_(const char *fname, unsigned int line,
}
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),
+ tor_asprintf(&buf,
"Line unexpectedly reached at %s at %s:%u",
func, fname, line);
} else {
@@ -106,13 +126,32 @@ tor_bug_occurred_(const char *fname, unsigned int line,
add_captured_bug(expr);
return;
}
+
+ va_list ap;
+ char *extra = NULL;
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
+ if (fmt) {
+ va_start(ap,fmt);
+ tor_vasprintf(&extra, fmt, ap);
+ va_end(ap);
+ }
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
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);
+ tor_asprintf(&buf, "Non-fatal assertion %s failed in %s at %s:%u%s%s",
+ expr, func, fname, line, fmt ? " : " : "",
+ extra ? extra : "");
+ tor_free(extra);
}
log_backtrace(LOG_WARN, LD_BUG, buf);
+ tor_free(buf);
#ifdef TOR_UNIT_TESTS
if (failed_assertion_cb) {
diff --git a/src/lib/log/util_bug.h b/src/lib/log/util_bug.h
index 2a4d68127e..546ae1e3ef 100644
--- a/src/lib/log/util_bug.h
+++ b/src/lib/log/util_bug.h
@@ -80,10 +80,10 @@
tor__assert_tmp_value__; \
} )
#define ASSERT_PREDICT_LIKELY_(e) ASSERT_PREDICT_UNLIKELY_(e)
-#else
+#else /* !(defined(TOR_UNIT_TESTS) && defined(__GNUC__)) */
#define ASSERT_PREDICT_UNLIKELY_(e) PREDICT_UNLIKELY(e)
#define ASSERT_PREDICT_LIKELY_(e) PREDICT_LIKELY(e)
-#endif
+#endif /* defined(TOR_UNIT_TESTS) && defined(__GNUC__) */
/* 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
@@ -92,21 +92,28 @@
#define tor_assert(a) STMT_BEGIN \
(void)(a); \
STMT_END
-#else
+#define tor_assertf(a, fmt, ...) STMT_BEGIN \
+ (void)(a); \
+ (void)(fmt); \
+ STMT_END
+#else /* !(defined(TOR_UNIT_TESTS) && ... */
/** Like assert(3), but send assertion failures to the log as well as to
* stderr. */
-#define tor_assert(expr) STMT_BEGIN \
+#define tor_assert(expr) tor_assertf(expr, NULL)
+
+#define tor_assertf(expr, fmt, ...) STMT_BEGIN \
if (ASSERT_PREDICT_LIKELY_(expr)) { \
} else { \
- tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \
- tor_abort_(); \
+ tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr, \
+ fmt, ##__VA_ARGS__); \
+ tor_abort_(); \
} STMT_END
#endif /* defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS) */
#define tor_assert_unreached() \
STMT_BEGIN { \
tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, \
- "line should be unreached"); \
+ "line should be unreached", NULL); \
tor_abort_(); \
} STMT_END
@@ -136,34 +143,47 @@
#ifdef ALL_BUGS_ARE_FATAL
#define tor_assert_nonfatal_unreached() tor_assert(0)
#define tor_assert_nonfatal(cond) tor_assert((cond))
+#define tor_assertf_nonfatal(cond, fmt, ...) \
+ tor_assertf(cond, fmt, ##__VA_ARGS__)
#define tor_assert_nonfatal_unreached_once() tor_assert(0)
#define tor_assert_nonfatal_once(cond) tor_assert((cond))
#define BUG(cond) \
(ASSERT_PREDICT_UNLIKELY_(cond) ? \
- (tor_assertion_failed_(SHORT_FILE__,__LINE__,__func__,"!("#cond")"), \
+ (tor_assertion_failed_(SHORT_FILE__,__LINE__,__func__,"!("#cond")",NULL), \
tor_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_assertf_nonfatal(cond, fmt, ...) STMT_BEGIN \
+ (void)cond; \
+ (void)fmt; \
+ STMT_END
#define tor_assert_nonfatal_unreached_once() STMT_NIL
#define tor_assert_nonfatal_once(cond) ((void)(cond))
#define BUG(cond) (ASSERT_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); \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, NULL, 0, NULL); \
STMT_END
#define tor_assert_nonfatal(cond) STMT_BEGIN \
if (ASSERT_PREDICT_LIKELY_(cond)) { \
} else { \
- tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 0); \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 0, NULL);\
+ } \
+ STMT_END
+#define tor_assertf_nonfatal(cond, fmt, ...) STMT_BEGIN \
+ if (ASSERT_PREDICT_UNLIKELY_(cond)) { \
+ } else { \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 0, \
+ fmt, ##__VA_ARGS__); \
} \
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); \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, NULL, 1, NULL); \
} \
STMT_END
#define tor_assert_nonfatal_once(cond) STMT_BEGIN \
@@ -171,12 +191,12 @@
if (ASSERT_PREDICT_LIKELY_(cond)) { \
} else if (!warning_logged__) { \
warning_logged__ = 1; \
- tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 1); \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 1, NULL);\
} \
STMT_END
#define BUG(cond) \
(ASSERT_PREDICT_UNLIKELY_(cond) ? \
- (tor_bug_occurred_(SHORT_FILE__,__LINE__,__func__,"!("#cond")",0), 1) \
+ (tor_bug_occurred_(SHORT_FILE__,__LINE__,__func__,"!("#cond")",1,NULL),1) \
: 0)
#endif /* defined(ALL_BUGS_ARE_FATAL) || ... */
@@ -188,7 +208,7 @@
if (bool_result && !var) { \
var = 1; \
tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, \
- "!("#cond")", 1); \
+ "!("#cond")", 1, NULL); \
} \
bool_result; } ))
#else /* !(defined(__GNUC__)) */
@@ -198,7 +218,7 @@
(var ? 1 : \
(var=1, \
tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, \
- "!("#cond")", 1), \
+ "!("#cond")", 1, NULL), \
1)) \
: 0)
#endif /* defined(__GNUC__) */
@@ -221,10 +241,11 @@
#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);
+ const char *func, const char *expr,
+ const char *fmt, ...);
void tor_bug_occurred_(const char *fname, unsigned int line,
const char *func, const char *expr,
- int once);
+ int once, const char *fmt, ...);
void tor_abort_(void) ATTR_NORETURN;
diff --git a/src/lib/log/win32err.h b/src/lib/log/win32err.h
index 33413dfd15..ecfa88792d 100644
--- a/src/lib/log/win32err.h
+++ b/src/lib/log/win32err.h
@@ -19,4 +19,4 @@
char *format_win32_error(DWORD err);
#endif
-#endif
+#endif /* !defined(TOR_WIN32ERR_H) */
diff --git a/src/lib/malloc/include.am b/src/lib/malloc/include.am
index 95d96168e1..b74292bc6e 100644
--- a/src/lib/malloc/include.am
+++ b/src/lib/malloc/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-malloc-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_malloc_a_SOURCES = \
src/lib/malloc/malloc.c \
src/lib/malloc/map_anon.c
@@ -18,6 +19,7 @@ src_lib_libtor_malloc_testing_a_SOURCES = \
src_lib_libtor_malloc_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_malloc_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/malloc/malloc.h \
src/lib/malloc/map_anon.h
diff --git a/src/lib/malloc/malloc.h b/src/lib/malloc/malloc.h
index ef6b509ca4..8c81d30dd5 100644
--- a/src/lib/malloc/malloc.h
+++ b/src/lib/malloc/malloc.h
@@ -48,12 +48,12 @@ void tor_free_(void *mem);
raw_free(*tor_free__tmpvar); \
*tor_free__tmpvar=NULL; \
STMT_END
-#else
+#else /* !(defined(__GNUC__)) */
#define tor_free(p) STMT_BEGIN \
raw_free(p); \
(p)=NULL; \
STMT_END
-#endif
+#endif /* defined(__GNUC__) */
#define tor_malloc(size) tor_malloc_(size)
#define tor_malloc_zero(size) tor_malloc_zero_(size)
diff --git a/src/lib/malloc/map_anon.c b/src/lib/malloc/map_anon.c
index 2430f7ad11..0f6a4150c7 100644
--- a/src/lib/malloc/map_anon.c
+++ b/src/lib/malloc/map_anon.c
@@ -59,7 +59,7 @@
#define FLAG_NOINHERIT VM_INHERIT_NONE
#elif defined(MAP_INHERIT_NONE)
#define FLAG_NOINHERIT MAP_INHERIT_NONE
-#endif
+#endif /* defined(INHERIT_NONE) || ... */
#elif defined(HAVE_MADVISE)
@@ -72,6 +72,11 @@
#define FLAG_NOINHERIT MADV_DONTFORK
#endif
+#endif /* defined(HAVE_MINHERIT) || ... */
+
+#if defined(HAVE_MINHERIT) && !defined(FLAG_ZERO) && !defined(FLAG_NOINHERIT)
+#warn "minherit() is defined, but we couldn't find the right flag for it."
+#warn "This is probably a bug in Tor's support for this platform."
#endif
/**
@@ -91,7 +96,7 @@ lock_mem(void *mem, size_t sz)
(void) sz;
return 0;
-#endif
+#endif /* defined(_WIN32) || ... */
}
/**
@@ -108,7 +113,7 @@ nodump_mem(void *mem, size_t sz)
(void) mem;
(void) sz;
return 0;
-#endif
+#endif /* defined(MADV_DONTDUMP) */
}
/**
@@ -117,22 +122,32 @@ nodump_mem(void *mem, size_t sz)
* fork, and if that doesn't work, by having them unmapped after a fork.
* Return 0 on success or if the facility is not available on this OS; return
* -1 on failure.
+ *
+ * If we successfully make the memory uninheritable, adjust the value of
+ * *<b>inherit_result_out</b>.
*/
static int
-noinherit_mem(void *mem, size_t sz)
+noinherit_mem(void *mem, size_t sz, inherit_res_t *inherit_result_out)
{
#ifdef FLAG_ZERO
int r = MINHERIT(mem, sz, FLAG_ZERO);
- if (r == 0)
+ if (r == 0) {
+ *inherit_result_out = INHERIT_RES_ZERO;
return 0;
-#endif
+ }
+#endif /* defined(FLAG_ZERO) */
#ifdef FLAG_NOINHERIT
- return MINHERIT(mem, sz, FLAG_NOINHERIT);
-#else
+ int r2 = MINHERIT(mem, sz, FLAG_NOINHERIT);
+ if (r2 == 0) {
+ *inherit_result_out = INHERIT_RES_DROP;
+ }
+ return r2;
+#else /* !(defined(FLAG_NOINHERIT)) */
+ (void)inherit_result_out;
(void)mem;
(void)sz;
return 0;
-#endif
+#endif /* defined(FLAG_NOINHERIT) */
}
/**
@@ -148,14 +163,25 @@ noinherit_mem(void *mem, size_t sz)
* Memory returned from this function must be released with
* tor_munmap_anonymous().
*
+ * If <b>inherit_result_out</b> is non-NULL, set it to one of
+ * INHERIT_RES_KEEP, INHERIT_RES_DROP, or INHERIT_RES_ZERO, depending on the
+ * properties of the returned memory.
+ *
* [Note: OS people use the word "anonymous" here to mean that the memory
* isn't associated with any file. This has *nothing* to do with the kind of
* anonymity that Tor is trying to provide.]
*/
void *
-tor_mmap_anonymous(size_t sz, unsigned flags)
+tor_mmap_anonymous(size_t sz, unsigned flags,
+ inherit_res_t *inherit_result_out)
{
void *ptr;
+ inherit_res_t itmp=0;
+ if (inherit_result_out == NULL) {
+ inherit_result_out = &itmp;
+ }
+ *inherit_result_out = INHERIT_RES_KEEP;
+
#if defined(_WIN32)
HANDLE mapping = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL, /*attributes*/
@@ -178,7 +204,7 @@ tor_mmap_anonymous(size_t sz, unsigned flags)
raw_assert(ptr != NULL);
#else
ptr = tor_malloc_zero(sz);
-#endif
+#endif /* defined(_WIN32) || ... */
if (flags & ANONMAP_PRIVATE) {
int lock_result = lock_mem(ptr, sz);
@@ -188,7 +214,7 @@ tor_mmap_anonymous(size_t sz, unsigned flags)
}
if (flags & ANONMAP_NOINHERIT) {
- int noinherit_result = noinherit_mem(ptr, sz);
+ int noinherit_result = noinherit_mem(ptr, sz, inherit_result_out);
raw_assert(noinherit_result == 0);
}
@@ -213,5 +239,5 @@ tor_munmap_anonymous(void *mapping, size_t sz)
#else
(void)sz;
tor_free(mapping);
-#endif
+#endif /* defined(_WIN32) || ... */
}
diff --git a/src/lib/malloc/map_anon.h b/src/lib/malloc/map_anon.h
index cc5797e4ec..4c4690e12f 100644
--- a/src/lib/malloc/map_anon.h
+++ b/src/lib/malloc/map_anon.h
@@ -31,7 +31,41 @@
*/
#define ANONMAP_NOINHERIT (1u<<1)
-void *tor_mmap_anonymous(size_t sz, unsigned flags);
+typedef enum {
+ /** Possible value for inherit_result_out: the memory will be kept
+ * by any child process. */
+ INHERIT_RES_KEEP=0,
+ /** Possible value for inherit_result_out: the memory will be dropped in the
+ * child process. Attempting to access it will likely cause a segfault. */
+ INHERIT_RES_DROP,
+ /** Possible value for inherit_result_out: the memory will be cleared in
+ * the child process. */
+ INHERIT_RES_ZERO
+} inherit_res_t;
+
+/* Here we define the NOINHERIT_CAN_FAIL macro if and only if
+ * it's possible that ANONMAP_NOINHERIT might yield inheritable memory.
+ */
+#ifdef _WIN32
+/* Windows can't fork, so NOINHERIT is never needed. */
+#elif defined(HAVE_MINHERIT)
+/* minherit() will always have a working MAP_INHERIT_NONE or MAP_INHERIT_ZERO.
+ * NOINHERIT should always work.
+ */
+#elif defined(HAVE_MADVISE)
+/* madvise() sometimes has neither MADV_DONTFORK and MADV_WIPEONFORK.
+ * We need to be ready for the possibility it failed.
+ *
+ * (Linux added DONTFORK in 2.6.16 and WIPEONFORK in 4.14. If we someday
+ * require 2.6.16 or later, we can assume that DONTFORK will work.)
+ */
+#define NOINHERIT_CAN_FAIL
+#else
+#define NOINHERIT_CAN_FAIL
+#endif /* defined(_WIN32) || ... */
+
+void *tor_mmap_anonymous(size_t sz, unsigned flags,
+ inherit_res_t *inherit_result_out);
void tor_munmap_anonymous(void *mapping, size_t sz);
#endif /* !defined(TOR_MAP_ANON_H) */
diff --git a/src/lib/math/fp.h b/src/lib/math/fp.h
index cb24649e6c..a73789c945 100644
--- a/src/lib/math/fp.h
+++ b/src/lib/math/fp.h
@@ -21,4 +21,4 @@ int64_t tor_llround(double d) ATTR_CONST;
int64_t clamp_double_to_int64(double number);
int tor_isinf(double x);
-#endif
+#endif /* !defined(TOR_FP_H) */
diff --git a/src/lib/math/include.am b/src/lib/math/include.am
index 6d65ce90a7..b2ca280f47 100644
--- a/src/lib/math/include.am
+++ b/src/lib/math/include.am
@@ -5,17 +5,18 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-math-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_math_a_SOURCES = \
src/lib/math/fp.c \
src/lib/math/laplace.c \
src/lib/math/prob_distr.c
-
src_lib_libtor_math_testing_a_SOURCES = \
$(src_lib_libtor_math_a_SOURCES)
src_lib_libtor_math_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_math_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/math/fp.h \
src/lib/math/laplace.h \
diff --git a/src/lib/math/laplace.h b/src/lib/math/laplace.h
index e8651e5197..02b0e890f0 100644
--- a/src/lib/math/laplace.h
+++ b/src/lib/math/laplace.h
@@ -19,4 +19,4 @@ 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);
-#endif
+#endif /* !defined(TOR_LAPLACE_H) */
diff --git a/src/lib/math/prob_distr.c b/src/lib/math/prob_distr.c
index c952dadc06..d44dc28265 100644
--- a/src/lib/math/prob_distr.c
+++ b/src/lib/math/prob_distr.c
@@ -46,26 +46,27 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/cc/ctassert.h"
+#include "lib/log/util_bug.h"
#include <float.h>
#include <math.h>
#include <stddef.h>
-/** Validators for downcasting macros below */
-#define validate_container_of(PTR, TYPE, FIELD) \
- (0 * sizeof((PTR) - &((TYPE *)(((char *)(PTR)) - \
- offsetof(TYPE, FIELD)))->FIELD))
-#define validate_const_container_of(PTR, TYPE, FIELD) \
- (0 * sizeof((PTR) - &((const TYPE *)(((const char *)(PTR)) - \
- offsetof(TYPE, FIELD)))->FIELD))
-/** Downcasting macro */
-#define container_of(PTR, TYPE, FIELD) \
- ((TYPE *)(((char *)(PTR)) - offsetof(TYPE, FIELD)) \
- + validate_container_of(PTR, TYPE, FIELD))
-/** Constified downcasting macro */
-#define const_container_of(PTR, TYPE, FIELD) \
- ((const TYPE *)(((const char *)(PTR)) - offsetof(TYPE, FIELD)) \
- + validate_const_container_of(PTR, TYPE, FIELD))
+/** Declare a function that downcasts from a generic dist struct to the actual
+ * subtype probablity distribution it represents. */
+#define DECLARE_PROB_DISTR_DOWNCAST_FN(name) \
+ static inline \
+ const struct name * \
+ dist_to_const_##name(const struct dist *obj) { \
+ tor_assert(obj->ops == &name##_ops); \
+ return SUBTYPE_P(obj, struct name, base); \
+ }
+DECLARE_PROB_DISTR_DOWNCAST_FN(uniform)
+DECLARE_PROB_DISTR_DOWNCAST_FN(geometric)
+DECLARE_PROB_DISTR_DOWNCAST_FN(logistic)
+DECLARE_PROB_DISTR_DOWNCAST_FN(log_logistic)
+DECLARE_PROB_DISTR_DOWNCAST_FN(genpareto)
+DECLARE_PROB_DISTR_DOWNCAST_FN(weibull)
/**
* Count number of one bits in 32-bit word.
@@ -458,7 +459,7 @@ random_uniform_01(void)
* system is broken.
*/
z = 0;
- while ((x = crypto_rand_u32()) == 0) {
+ while ((x = crypto_fast_rng_get_u32(get_thread_fast_rng())) == 0) {
if (z >= 1088)
/* Your bit sampler is broken. Go home. */
return 0;
@@ -472,8 +473,8 @@ random_uniform_01(void)
* occur only with measure zero in the uniform distribution on
* [0, 1].
*/
- hi = crypto_rand_u32() | UINT32_C(0x80000000);
- lo = crypto_rand_u32() | UINT32_C(0x00000001);
+ hi = crypto_fast_rng_get_u32(get_thread_fast_rng()) | UINT32_C(0x80000000);
+ lo = crypto_fast_rng_get_u32(get_thread_fast_rng()) | UINT32_C(0x00000001);
/* Round to nearest scaled significand in [2^63, 2^64]. */
s = hi*(double)4294967296 + lo;
@@ -1315,40 +1316,48 @@ sample_geometric(uint32_t s, double p0, double p)
/** Public API for probability distributions:
*
- * For each probability distribution we define each public functions
- * (sample/cdf/sf/icdf/isf) as part of its dist_ops structure.
+ * These are wrapper functions on top of the various probability distribution
+ * operations using the generic <b>dist</b> structure.
+
+ * These are the functions that should be used by consumers of this API.
*/
+/** Returns the name of the distribution in <b>dist</b>. */
const char *
dist_name(const struct dist *dist)
{
return dist->ops->name;
}
+/* Sample a value from <b>dist</b> and return it. */
double
dist_sample(const struct dist *dist)
{
return dist->ops->sample(dist);
}
+/** Compute the CDF of <b>dist</b> at <b>x</b>. */
double
dist_cdf(const struct dist *dist, double x)
{
return dist->ops->cdf(dist, x);
}
+/** Compute the SF (Survival function) of <b>dist</b> at <b>x</b>. */
double
dist_sf(const struct dist *dist, double x)
{
return dist->ops->sf(dist, x);
}
+/** Compute the iCDF (Inverse CDF) of <b>dist</b> at <b>x</b>. */
double
dist_icdf(const struct dist *dist, double p)
{
return dist->ops->icdf(dist, p);
}
+/** Compute the iSF (Inverse Survival function) of <b>dist</b> at <b>x</b>. */
double
dist_isf(const struct dist *dist, double p)
{
@@ -1360,8 +1369,7 @@ dist_isf(const struct dist *dist, double p)
static double
uniform_sample(const struct dist *dist)
{
- const struct uniform *U = const_container_of(dist, struct uniform,
- base);
+ const struct uniform *U = dist_to_const_uniform(dist);
double p0 = random_uniform_01();
return sample_uniform_interval(p0, U->a, U->b);
@@ -1370,9 +1378,7 @@ uniform_sample(const struct dist *dist)
static double
uniform_cdf(const struct dist *dist, double x)
{
- const struct uniform *U = const_container_of(dist, struct uniform,
- base);
-
+ const struct uniform *U = dist_to_const_uniform(dist);
if (x < U->a)
return 0;
else if (x < U->b)
@@ -1384,8 +1390,7 @@ uniform_cdf(const struct dist *dist, double x)
static double
uniform_sf(const struct dist *dist, double x)
{
- const struct uniform *U = const_container_of(dist, struct uniform,
- base);
+ const struct uniform *U = dist_to_const_uniform(dist);
if (x > U->b)
return 0;
@@ -1398,8 +1403,7 @@ uniform_sf(const struct dist *dist, double x)
static double
uniform_icdf(const struct dist *dist, double p)
{
- const struct uniform *U = const_container_of(dist, struct uniform,
- base);
+ const struct uniform *U = dist_to_const_uniform(dist);
double w = U->b - U->a;
return (p < 0.5 ? (U->a + w*p) : (U->b - w*(1 - p)));
@@ -1408,8 +1412,7 @@ uniform_icdf(const struct dist *dist, double p)
static double
uniform_isf(const struct dist *dist, double p)
{
- const struct uniform *U = const_container_of(dist, struct uniform,
- base);
+ const struct uniform *U = dist_to_const_uniform(dist);
double w = U->b - U->a;
return (p < 0.5 ? (U->b - w*p) : (U->a + w*(1 - p)));
@@ -1424,14 +1427,17 @@ const struct dist_ops uniform_ops = {
.isf = uniform_isf,
};
+/*******************************************************************/
+
+/** Private functions for each probability distribution. */
+
/** Functions for logistic distribution: */
static double
logistic_sample(const struct dist *dist)
{
- const struct logistic *L = const_container_of(dist, struct logistic,
- base);
- uint32_t s = crypto_rand_u32();
+ const struct logistic *L = dist_to_const_logistic(dist);
+ uint32_t s = crypto_fast_rng_get_u32(get_thread_fast_rng());
double t = random_uniform_01();
double p0 = random_uniform_01();
@@ -1441,36 +1447,28 @@ logistic_sample(const struct dist *dist)
static double
logistic_cdf(const struct dist *dist, double x)
{
- const struct logistic *L = const_container_of(dist, struct logistic,
- base);
-
+ const struct logistic *L = dist_to_const_logistic(dist);
return cdf_logistic(x, L->mu, L->sigma);
}
static double
logistic_sf(const struct dist *dist, double x)
{
- const struct logistic *L = const_container_of(dist, struct logistic,
- base);
-
+ const struct logistic *L = dist_to_const_logistic(dist);
return sf_logistic(x, L->mu, L->sigma);
}
static double
logistic_icdf(const struct dist *dist, double p)
{
- const struct logistic *L = const_container_of(dist, struct logistic,
- base);
-
+ const struct logistic *L = dist_to_const_logistic(dist);
return icdf_logistic(p, L->mu, L->sigma);
}
static double
logistic_isf(const struct dist *dist, double p)
{
- const struct logistic *L = const_container_of(dist, struct logistic,
- base);
-
+ const struct logistic *L = dist_to_const_logistic(dist);
return isf_logistic(p, L->mu, L->sigma);
}
@@ -1488,9 +1486,8 @@ const struct dist_ops logistic_ops = {
static double
log_logistic_sample(const struct dist *dist)
{
- const struct log_logistic *LL = const_container_of(dist, struct
- log_logistic, base);
- uint32_t s = crypto_rand_u32();
+ const struct log_logistic *LL = dist_to_const_log_logistic(dist);
+ uint32_t s = crypto_fast_rng_get_u32(get_thread_fast_rng());
double p0 = random_uniform_01();
return sample_log_logistic_scaleshape(s, p0, LL->alpha, LL->beta);
@@ -1499,36 +1496,28 @@ log_logistic_sample(const struct dist *dist)
static double
log_logistic_cdf(const struct dist *dist, double x)
{
- const struct log_logistic *LL = const_container_of(dist,
- struct log_logistic, base);
-
+ const struct log_logistic *LL = dist_to_const_log_logistic(dist);
return cdf_log_logistic(x, LL->alpha, LL->beta);
}
static double
log_logistic_sf(const struct dist *dist, double x)
{
- const struct log_logistic *LL = const_container_of(dist,
- struct log_logistic, base);
-
+ const struct log_logistic *LL = dist_to_const_log_logistic(dist);
return sf_log_logistic(x, LL->alpha, LL->beta);
}
static double
log_logistic_icdf(const struct dist *dist, double p)
{
- const struct log_logistic *LL = const_container_of(dist,
- struct log_logistic, base);
-
+ const struct log_logistic *LL = dist_to_const_log_logistic(dist);
return icdf_log_logistic(p, LL->alpha, LL->beta);
}
static double
log_logistic_isf(const struct dist *dist, double p)
{
- const struct log_logistic *LL = const_container_of(dist,
- struct log_logistic, base);
-
+ const struct log_logistic *LL = dist_to_const_log_logistic(dist);
return isf_log_logistic(p, LL->alpha, LL->beta);
}
@@ -1546,9 +1535,8 @@ const struct dist_ops log_logistic_ops = {
static double
weibull_sample(const struct dist *dist)
{
- const struct weibull *W = const_container_of(dist, struct weibull,
- base);
- uint32_t s = crypto_rand_u32();
+ const struct weibull *W = dist_to_const_weibull(dist);
+ uint32_t s = crypto_fast_rng_get_u32(get_thread_fast_rng());
double p0 = random_uniform_01();
return sample_weibull(s, p0, W->lambda, W->k);
@@ -1557,36 +1545,28 @@ weibull_sample(const struct dist *dist)
static double
weibull_cdf(const struct dist *dist, double x)
{
- const struct weibull *W = const_container_of(dist, struct weibull,
- base);
-
+ const struct weibull *W = dist_to_const_weibull(dist);
return cdf_weibull(x, W->lambda, W->k);
}
static double
weibull_sf(const struct dist *dist, double x)
{
- const struct weibull *W = const_container_of(dist, struct weibull,
- base);
-
+ const struct weibull *W = dist_to_const_weibull(dist);
return sf_weibull(x, W->lambda, W->k);
}
static double
weibull_icdf(const struct dist *dist, double p)
{
- const struct weibull *W = const_container_of(dist, struct weibull,
- base);
-
+ const struct weibull *W = dist_to_const_weibull(dist);
return icdf_weibull(p, W->lambda, W->k);
}
static double
weibull_isf(const struct dist *dist, double p)
{
- const struct weibull *W = const_container_of(dist, struct weibull,
- base);
-
+ const struct weibull *W = dist_to_const_weibull(dist);
return isf_weibull(p, W->lambda, W->k);
}
@@ -1604,9 +1584,8 @@ const struct dist_ops weibull_ops = {
static double
genpareto_sample(const struct dist *dist)
{
- const struct genpareto *GP = const_container_of(dist, struct genpareto,
- base);
- uint32_t s = crypto_rand_u32();
+ const struct genpareto *GP = dist_to_const_genpareto(dist);
+ uint32_t s = crypto_fast_rng_get_u32(get_thread_fast_rng());
double p0 = random_uniform_01();
return sample_genpareto_locscale(s, p0, GP->mu, GP->sigma, GP->xi);
@@ -1615,36 +1594,28 @@ genpareto_sample(const struct dist *dist)
static double
genpareto_cdf(const struct dist *dist, double x)
{
- const struct genpareto *GP = const_container_of(dist, struct genpareto,
- base);
-
+ const struct genpareto *GP = dist_to_const_genpareto(dist);
return cdf_genpareto(x, GP->mu, GP->sigma, GP->xi);
}
static double
genpareto_sf(const struct dist *dist, double x)
{
- const struct genpareto *GP = const_container_of(dist, struct genpareto,
- base);
-
+ const struct genpareto *GP = dist_to_const_genpareto(dist);
return sf_genpareto(x, GP->mu, GP->sigma, GP->xi);
}
static double
genpareto_icdf(const struct dist *dist, double p)
{
- const struct genpareto *GP = const_container_of(dist, struct genpareto,
- base);
-
+ const struct genpareto *GP = dist_to_const_genpareto(dist);
return icdf_genpareto(p, GP->mu, GP->sigma, GP->xi);
}
static double
genpareto_isf(const struct dist *dist, double p)
{
- const struct genpareto *GP = const_container_of(dist, struct genpareto,
- base);
-
+ const struct genpareto *GP = dist_to_const_genpareto(dist);
return isf_genpareto(p, GP->mu, GP->sigma, GP->xi);
}
@@ -1662,8 +1633,8 @@ const struct dist_ops genpareto_ops = {
static double
geometric_sample(const struct dist *dist)
{
- const struct geometric *G = const_container_of(dist, struct geometric, base);
- uint32_t s = crypto_rand_u32();
+ const struct geometric *G = dist_to_const_geometric(dist);
+ uint32_t s = crypto_fast_rng_get_u32(get_thread_fast_rng());
double p0 = random_uniform_01();
return sample_geometric(s, p0, G->p);
@@ -1672,7 +1643,7 @@ geometric_sample(const struct dist *dist)
static double
geometric_cdf(const struct dist *dist, double x)
{
- const struct geometric *G = const_container_of(dist, struct geometric, base);
+ const struct geometric *G = dist_to_const_geometric(dist);
if (x < 1)
return 0;
@@ -1683,7 +1654,7 @@ geometric_cdf(const struct dist *dist, double x)
static double
geometric_sf(const struct dist *dist, double x)
{
- const struct geometric *G = const_container_of(dist, struct geometric, base);
+ const struct geometric *G = dist_to_const_geometric(dist);
if (x < 1)
return 0;
@@ -1694,7 +1665,7 @@ geometric_sf(const struct dist *dist, double x)
static double
geometric_icdf(const struct dist *dist, double p)
{
- const struct geometric *G = const_container_of(dist, struct geometric, base);
+ const struct geometric *G = dist_to_const_geometric(dist);
return log1p(-p)/log1p(-G->p);
}
@@ -1702,7 +1673,7 @@ geometric_icdf(const struct dist *dist, double p)
static double
geometric_isf(const struct dist *dist, double p)
{
- const struct geometric *G = const_container_of(dist, struct geometric, base);
+ const struct geometric *G = dist_to_const_geometric(dist);
return log(p)/log1p(-G->p);
}
diff --git a/src/lib/math/prob_distr.h b/src/lib/math/prob_distr.h
index 66acb796fd..7254dc8623 100644
--- a/src/lib/math/prob_distr.h
+++ b/src/lib/math/prob_distr.h
@@ -19,10 +19,100 @@ struct dist {
const struct dist_ops *ops;
};
+/**
+ * Untyped initializer element for struct dist using the specified
+ * struct dist_ops pointer. Don't actually use this directly -- use
+ * the type-specific macro built out of DIST_BASE_TYPED below -- but if
+ * you did use this directly, it would be something like:
+ *
+ * struct weibull mydist = {
+ * DIST_BASE(&weibull_ops),
+ * .lambda = ...,
+ * .k = ...,
+ * };
+ *
+ * Note there is NO COMPILER FEEDBACK if you accidentally do something
+ * like
+ *
+ * struct geometric mydist = {
+ * DIST_BASE(&weibull_ops),
+ * ...
+ * };
+ */
#define DIST_BASE(OPS) { .ops = (OPS) }
+
+/** A compile-time type-checking macro for use with DIST_BASE_TYPED.
+ *
+ * This macro works by checking that &OBJ is a pointer type that is the same
+ * type (except for qualifiers) as (const TYPE *)&OBJ. It's a C constraint
+ * violation (which requires a diagnostic) if two pointers are different types
+ * and are subtracted. The sizeof() forces compile-time evaluation, and the
+ * multiplication by zero is to discard the result of the sizeof() from the
+ * expression.
+ *
+ * We define this conditionally to suppress false positives from
+ * Coverity, which gets confused by the sizeof business.
+ */
+#ifdef __COVERITY__
+#define TYPE_CHECK_OBJ(OPS, OBJ, TYPE) 0
+#else
+#define TYPE_CHECK_OBJ(OPS, OBJ, TYPE) \
+ (0*sizeof(&(OBJ) - (const TYPE *)&(OBJ)))
+#endif /* defined(__COVERITY__) */
+
+/**
+* Typed initializer element for struct dist using the specified struct
+* dist_ops pointer. Don't actually use this directly -- use a
+* type-specific macro built out of it -- but if you did use this
+* directly, it would be something like:
+*
+* struct weibull mydist = {
+* DIST_BASE_TYPED(&weibull_ops, mydist, struct weibull),
+* .lambda = ...,
+* .k = ...,
+* };
+*
+* If you want to define a distribution type, define a canonical set of
+* operations and define a type-specific initializer element like so:
+*
+* struct foo {
+* struct dist base;
+* int omega;
+* double tau;
+* double phi;
+* };
+*
+* struct dist_ops foo_ops = ...;
+*
+* #define FOO(OBJ) DIST_BASE_TYPED(&foo_ops, OBJ, struct foo)
+*
+* Then users can do:
+*
+* struct foo mydist = {
+* FOO(mydist),
+* .omega = ...,
+* .tau = ...,
+* .phi = ...,
+* };
+*
+* If you accidentally write
+*
+* struct bar mydist = {
+* FOO(mydist),
+* ...
+* };
+*
+* then the compiler will report a type mismatch in the sizeof
+* expression, which otherwise evaporates at runtime.
+*/
#define DIST_BASE_TYPED(OPS, OBJ, TYPE) \
- DIST_BASE((OPS) + 0*sizeof(&(OBJ) - (const TYPE *)&(OBJ)))
+ DIST_BASE((OPS) + TYPE_CHECK_OBJ(OPS,OBJ,TYPE))
+/**
+ * Generic operations on distributions. These simply defer to the
+ * corresponding dist_ops function. In the parlance of C++, these call
+ * virtual member functions.
+ */
const char *dist_name(const struct dist *);
double dist_sample(const struct dist *);
double dist_cdf(const struct dist *, double x);
@@ -30,6 +120,11 @@ double dist_sf(const struct dist *, double x);
double dist_icdf(const struct dist *, double p);
double dist_isf(const struct dist *, double p);
+/**
+ * Set of operations on a potentially parametric family of
+ * distributions. In the parlance of C++, this would be called a
+ * `vtable' and the members are virtual member functions.
+ */
struct dist_ops {
const char *name;
double (*sample)(const struct dist *);
@@ -153,6 +248,6 @@ STATIC double icdf_genpareto(double p, double mu, double sigma, double xi);
STATIC double isf_genpareto(double p, double mu, double sigma, double xi);
STATIC double sample_genpareto(uint32_t s, double p0, double xi);
-#endif
+#endif /* defined(PROB_DISTR_PRIVATE) */
-#endif
+#endif /* !defined(TOR_PROB_DISTR_H) */
diff --git a/src/lib/memarea/include.am b/src/lib/memarea/include.am
index 94343dcead..83fb99ec73 100644
--- a/src/lib/memarea/include.am
+++ b/src/lib/memarea/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-memarea-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_memarea_a_SOURCES = \
src/lib/memarea/memarea.c
@@ -13,5 +14,6 @@ src_lib_libtor_memarea_testing_a_SOURCES = \
src_lib_libtor_memarea_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_memarea_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/memarea/memarea.h
diff --git a/src/lib/meminfo/include.am b/src/lib/meminfo/include.am
index d1fdde6313..12c1bff72d 100644
--- a/src/lib/meminfo/include.am
+++ b/src/lib/meminfo/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-meminfo-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_meminfo_a_SOURCES = \
src/lib/meminfo/meminfo.c
@@ -13,5 +14,6 @@ src_lib_libtor_meminfo_testing_a_SOURCES = \
src_lib_libtor_meminfo_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_meminfo_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/meminfo/meminfo.h
diff --git a/src/lib/meminfo/meminfo.h b/src/lib/meminfo/meminfo.h
index 2d64e1ab06..9580640f4d 100644
--- a/src/lib/meminfo/meminfo.h
+++ b/src/lib/meminfo/meminfo.h
@@ -18,4 +18,4 @@
void tor_log_mallinfo(int severity);
MOCK_DECL(int, get_total_system_memory, (size_t *mem_out));
-#endif
+#endif /* !defined(TOR_MEMINFO_H) */
diff --git a/src/lib/net/address.c b/src/lib/net/address.c
index 214d8aa3eb..546af800a9 100644
--- a/src/lib/net/address.c
+++ b/src/lib/net/address.c
@@ -339,7 +339,7 @@ tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate)
break;
case AF_INET6:
/* Shortest addr [ :: ] + \0 */
- if (len < (3 + (decorate ? 2 : 0)))
+ if (len < (3u + (decorate ? 2 : 0)))
return NULL;
if (decorate)
@@ -2027,8 +2027,12 @@ string_is_valid_nonrfc_hostname(const char *string)
smartlist_split_string(components,string,".",0,0);
- if (BUG(smartlist_len(components) == 0))
- return 0; // LCOV_EXCL_LINE should be impossible given the earlier checks.
+ if (BUG(smartlist_len(components) == 0)) {
+ // LCOV_EXCL_START should be impossible given the earlier checks.
+ smartlist_free(components);
+ return 0;
+ // LCOV_EXCL_STOP
+ }
/* Allow a single terminating '.' used rarely to indicate domains
* are FQDNs rather than relative. */
diff --git a/src/lib/net/alertsock.h b/src/lib/net/alertsock.h
index c45f42be81..4d0d0dd57c 100644
--- a/src/lib/net/alertsock.h
+++ b/src/lib/net/alertsock.h
@@ -42,4 +42,4 @@ typedef struct alert_sockets_t {
int alert_sockets_create(alert_sockets_t *socks_out, uint32_t flags);
void alert_sockets_close(alert_sockets_t *socks);
-#endif
+#endif /* !defined(TOR_ALERTSOCK_H) */
diff --git a/src/lib/net/buffers_net.h b/src/lib/net/buffers_net.h
index a3a90172a1..5058dd0a26 100644
--- a/src/lib/net/buffers_net.h
+++ b/src/lib/net/buffers_net.h
@@ -31,4 +31,4 @@ int buf_read_from_pipe(struct buf_t *buf, int fd, size_t at_most,
int buf_flush_to_pipe(struct buf_t *buf, int fd, size_t sz,
size_t *buf_flushlen);
-#endif /* !defined(TOR_BUFFERS_H) */
+#endif /* !defined(TOR_BUFFERS_NET_H) */
diff --git a/src/lib/net/gethostname.h b/src/lib/net/gethostname.h
index 69b0528bc0..b3b77b0589 100644
--- a/src/lib/net/gethostname.h
+++ b/src/lib/net/gethostname.h
@@ -16,4 +16,4 @@
MOCK_DECL(int,tor_gethostname,(char *name, size_t namelen));
-#endif
+#endif /* !defined(TOR_GETHOSTNAME_H) */
diff --git a/src/lib/net/inaddr.h b/src/lib/net/inaddr.h
index 36352b65ea..602573944c 100644
--- a/src/lib/net/inaddr.h
+++ b/src/lib/net/inaddr.h
@@ -24,4 +24,4 @@ int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len);
const char *tor_inet_ntop(int af, const void *src, char *dst, size_t len);
int tor_inet_pton(int af, const char *src, void *dst);
-#endif
+#endif /* !defined(TOR_INADDR_H) */
diff --git a/src/lib/net/inaddr_st.h b/src/lib/net/inaddr_st.h
index 806f2c096a..230f29a63a 100644
--- a/src/lib/net/inaddr_st.h
+++ b/src/lib/net/inaddr_st.h
@@ -104,4 +104,4 @@ struct sockaddr_in6 {
};
#endif /* !defined(HAVE_STRUCT_SOCKADDR_IN6) */
-#endif /* TOR_INADDR_ST_H */
+#endif /* !defined(TOR_INADDR_ST_H) */
diff --git a/src/lib/net/include.am b/src/lib/net/include.am
index 8a88f0f2ae..485019f4b7 100644
--- a/src/lib/net/include.am
+++ b/src/lib/net/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-net-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_net_a_SOURCES = \
src/lib/net/address.c \
src/lib/net/alertsock.c \
@@ -21,6 +22,7 @@ src_lib_libtor_net_testing_a_SOURCES = \
src_lib_libtor_net_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_net_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/net/address.h \
src/lib/net/alertsock.h \
diff --git a/src/lib/net/nettypes.h b/src/lib/net/nettypes.h
index 6209bbe18a..0eb352c657 100644
--- a/src/lib/net/nettypes.h
+++ b/src/lib/net/nettypes.h
@@ -41,4 +41,4 @@ typedef int socklen_t;
#define TOR_INVALID_SOCKET (-1)
#endif /* defined(_WIN32) */
-#endif
+#endif /* !defined(TOR_NET_TYPES_H) */
diff --git a/src/lib/net/resolve.c b/src/lib/net/resolve.c
index 49c263faa2..2dda491d14 100644
--- a/src/lib/net/resolve.c
+++ b/src/lib/net/resolve.c
@@ -421,7 +421,7 @@ tor_make_getaddrinfo_cache_active(void)
{
sandbox_getaddrinfo_is_active = 1;
}
-#else
+#else /* !(defined(USE_SANDBOX_GETADDRINFO)) */
void
sandbox_disable_getaddrinfo_cache(void)
{
@@ -430,4 +430,4 @@ void
tor_make_getaddrinfo_cache_active(void)
{
}
-#endif
+#endif /* defined(USE_SANDBOX_GETADDRINFO) */
diff --git a/src/lib/net/resolve.h b/src/lib/net/resolve.h
index 0fb77f1661..d11c902a91 100644
--- a/src/lib/net/resolve.h
+++ b/src/lib/net/resolve.h
@@ -55,4 +55,4 @@ void tor_free_getaddrinfo_cache(void);
void sandbox_disable_getaddrinfo_cache(void);
void tor_make_getaddrinfo_cache_active(void);
-#endif
+#endif /* !defined(TOR_RESOLVE_H) */
diff --git a/src/lib/net/socket.c b/src/lib/net/socket.c
index f978deeab8..e824a05045 100644
--- a/src/lib/net/socket.c
+++ b/src/lib/net/socket.c
@@ -84,9 +84,9 @@ check_network_configuration(bool server_mode)
"so your relay makes it harder to figure out how busy it is.");
}
}
-#else
+#else /* !(defined(__FreeBSD__)) */
(void) server_mode;
-#endif
+#endif /* defined(__FreeBSD__) */
}
/* When set_max_file_sockets() is called, update this with the max file
@@ -487,11 +487,11 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
r = socketpair(family, type, protocol, fd);
if (r < 0)
return -errno;
-#else
+#else /* !(defined(HAVE_SOCKETPAIR) && !defined(_WIN32)) */
r = tor_ersatz_socketpair(family, type, protocol, fd);
if (r < 0)
return -r;
-#endif
+#endif /* defined(HAVE_SOCKETPAIR) && !defined(_WIN32) */
#if defined(FD_CLOEXEC)
if (SOCKET_OK(fd[0])) {
diff --git a/src/lib/net/socket.h b/src/lib/net/socket.h
index 86ae336dfb..193ad91e4c 100644
--- a/src/lib/net/socket.h
+++ b/src/lib/net/socket.h
@@ -116,4 +116,4 @@ const char *tor_socket_strerror(int e);
#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747b
#endif
-#endif
+#endif /* !defined(TOR_SOCKET_H) */
diff --git a/src/lib/net/socketpair.c b/src/lib/net/socketpair.c
index 15c706bec7..3be7b26f7f 100644
--- a/src/lib/net/socketpair.c
+++ b/src/lib/net/socketpair.c
@@ -22,11 +22,11 @@
#include <windows.h>
#define socket_errno() (WSAGetLastError())
#define SOCKET_EPROTONOSUPPORT WSAEPROTONOSUPPORT
-#else
+#else /* !(defined(_WIN32)) */
#define closesocket(x) close(x)
#define socket_errno() (errno)
#define SOCKET_EPROTONOSUPPORT EPROTONOSUPPORT
-#endif
+#endif /* defined(_WIN32) */
#ifdef NEED_ERSATZ_SOCKETPAIR
diff --git a/src/lib/net/socketpair.h b/src/lib/net/socketpair.h
index 6be0803881..5820606973 100644
--- a/src/lib/net/socketpair.h
+++ b/src/lib/net/socketpair.h
@@ -16,4 +16,4 @@ int tor_ersatz_socketpair(int family, int type, int protocol,
tor_socket_t fd[2]);
#endif
-#endif
+#endif /* !defined(TOR_SOCKETPAIR_H) */
diff --git a/src/lib/net/socks5_status.h b/src/lib/net/socks5_status.h
index e55242ce66..e55119e0b0 100644
--- a/src/lib/net/socks5_status.h
+++ b/src/lib/net/socks5_status.h
@@ -29,4 +29,4 @@ typedef enum {
SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED = 0x08,
} socks5_reply_status_t;
-#endif
+#endif /* !defined(TOR_SOCKS5_STATUS_H) */
diff --git a/src/lib/osinfo/include.am b/src/lib/osinfo/include.am
index 16c5812604..84bd7feb00 100644
--- a/src/lib/osinfo/include.am
+++ b/src/lib/osinfo/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-osinfo-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_osinfo_a_SOURCES = \
src/lib/osinfo/uname.c
@@ -13,5 +14,6 @@ src_lib_libtor_osinfo_testing_a_SOURCES = \
src_lib_libtor_osinfo_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_osinfo_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/osinfo/uname.h
diff --git a/src/lib/osinfo/uname.h b/src/lib/osinfo/uname.h
index fcce629074..443a30d358 100644
--- a/src/lib/osinfo/uname.h
+++ b/src/lib/osinfo/uname.h
@@ -15,4 +15,4 @@
MOCK_DECL(const char *, get_uname,(void));
-#endif
+#endif /* !defined(HAVE_TOR_UNAME_H) */
diff --git a/src/lib/process/daemon.h b/src/lib/process/daemon.h
index 20920e0aae..423c498837 100644
--- a/src/lib/process/daemon.h
+++ b/src/lib/process/daemon.h
@@ -18,4 +18,4 @@ int finish_daemon(const char *desired_cwd);
bool start_daemon_has_been_called(void);
-#endif
+#endif /* !defined(TOR_DAEMON_H) */
diff --git a/src/lib/process/env.h b/src/lib/process/env.h
index 15d59351e0..19c2235970 100644
--- a/src/lib/process/env.h
+++ b/src/lib/process/env.h
@@ -38,4 +38,4 @@ void set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
const char *new_var,
void (*free_old)(void*),
int free_p);
-#endif
+#endif /* !defined(TOR_ENV_H) */
diff --git a/src/lib/process/include.am b/src/lib/process/include.am
index 83b67bf029..af5f99617b 100644
--- a/src/lib/process/include.am
+++ b/src/lib/process/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-process-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_process_a_SOURCES = \
src/lib/process/daemon.c \
src/lib/process/env.c \
@@ -23,6 +24,7 @@ src_lib_libtor_process_testing_a_SOURCES = \
src_lib_libtor_process_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_process_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/process/daemon.h \
src/lib/process/env.h \
diff --git a/src/lib/process/pidfile.h b/src/lib/process/pidfile.h
index dfeb39e046..af59041f80 100644
--- a/src/lib/process/pidfile.h
+++ b/src/lib/process/pidfile.h
@@ -13,4 +13,4 @@
int write_pidfile(const char *filename);
-#endif
+#endif /* !defined(TOR_PIDFILE_H) */
diff --git a/src/lib/process/process.c b/src/lib/process/process.c
index 422942dc83..631c7169f1 100644
--- a/src/lib/process/process.c
+++ b/src/lib/process/process.c
@@ -83,7 +83,7 @@ struct process_t {
#else
/** Our Win32 process handle. */
process_win32_t *win32_process;
-#endif
+#endif /* !defined(_WIN32) */
};
/** Convert a given process status in <b>status</b> to its string
@@ -205,7 +205,7 @@ process_new(const char *command)
#else
/* Prepare our Win32 process handle. */
process->win32_process = process_win32_new();
-#endif
+#endif /* !defined(_WIN32) */
smartlist_add(processes, process);
@@ -240,7 +240,7 @@ process_free_(process_t *process)
#else
/* Cleanup our Win32 process handle. */
process_win32_free(process->win32_process);
-#endif
+#endif /* !defined(_WIN32) */
smartlist_remove(processes, process);
@@ -513,7 +513,7 @@ process_get_unix_process(const process_t *process)
tor_assert(process->unix_process);
return process->unix_process;
}
-#else
+#else /* !(!defined(_WIN32)) */
/** Get the internal handle for Windows backend. */
process_win32_t *
process_get_win32_process(const process_t *process)
@@ -522,7 +522,7 @@ process_get_win32_process(const process_t *process)
tor_assert(process->win32_process);
return process->win32_process;
}
-#endif
+#endif /* !defined(_WIN32) */
/** Write <b>size</b> bytes of <b>data</b> to the given process's standard
* input. */
diff --git a/src/lib/process/process.h b/src/lib/process/process.h
index 14069923a0..05c091a5bf 100644
--- a/src/lib/process/process.h
+++ b/src/lib/process/process.h
@@ -111,7 +111,7 @@ struct process_unix_t *process_get_unix_process(const process_t *process);
#else
struct process_win32_t;
struct process_win32_t *process_get_win32_process(const process_t *process);
-#endif
+#endif /* !defined(_WIN32) */
void process_write(process_t *process,
const uint8_t *data, size_t size);
@@ -140,6 +140,6 @@ STATIC void process_read_buffer(process_t *process,
STATIC void process_read_lines(process_t *process,
buf_t *buffer,
process_read_callback_t callback);
-#endif /* defined(PROCESS_PRIVATE). */
+#endif /* defined(PROCESS_PRIVATE) */
-#endif /* defined(TOR_PROCESS_H). */
+#endif /* !defined(TOR_PROCESS_H) */
diff --git a/src/lib/process/process_unix.c b/src/lib/process/process_unix.c
index 790ab897e9..17ade87463 100644
--- a/src/lib/process/process_unix.c
+++ b/src/lib/process/process_unix.c
@@ -702,4 +702,4 @@ process_unix_close_file_descriptors(process_unix_t *unix_process)
return success;
}
-#endif /* defined(_WIN32). */
+#endif /* !defined(_WIN32) */
diff --git a/src/lib/process/process_unix.h b/src/lib/process/process_unix.h
index a1d8f72993..da40b3e567 100644
--- a/src/lib/process/process_unix.h
+++ b/src/lib/process/process_unix.h
@@ -61,8 +61,8 @@ STATIC int process_unix_read_handle(process_t *,
process_unix_handle_t *,
buf_t *);
STATIC bool process_unix_close_file_descriptors(process_unix_t *);
-#endif /* defined(PROCESS_UNIX_PRIVATE). */
+#endif /* defined(PROCESS_UNIX_PRIVATE) */
-#endif /* defined(_WIN32). */
+#endif /* !defined(_WIN32) */
-#endif /* defined(TOR_PROCESS_UNIX_H). */
+#endif /* !defined(TOR_PROCESS_UNIX_H) */
diff --git a/src/lib/process/process_win32.c b/src/lib/process/process_win32.c
index ddbe76bfd9..624333d4a3 100644
--- a/src/lib/process/process_win32.c
+++ b/src/lib/process/process_win32.c
@@ -741,7 +741,7 @@ process_win32_cleanup_handle(process_win32_handle_t *handle)
format_win32_error(error_code));
}
}
-#endif
+#endif /* 0 */
/* Close our handle. */
if (handle->pipe != INVALID_HANDLE_VALUE) {
@@ -1084,4 +1084,4 @@ tor_join_win_cmdline(const char *argv[])
return joined_argv;
}
-#endif /* ! defined(_WIN32). */
+#endif /* defined(_WIN32) */
diff --git a/src/lib/process/process_win32.h b/src/lib/process/process_win32.h
index d79dde157e..a50d86df5b 100644
--- a/src/lib/process/process_win32.h
+++ b/src/lib/process/process_win32.h
@@ -90,8 +90,8 @@ STATIC bool process_win32_handle_read_completion(process_win32_handle_t *,
STATIC char *format_win_cmdline_argument(const char *arg);
STATIC char *tor_join_win_cmdline(const char *argv[]);
-#endif /* defined(PROCESS_WIN32_PRIVATE). */
+#endif /* defined(PROCESS_WIN32_PRIVATE) */
-#endif /* ! defined(_WIN32). */
+#endif /* defined(_WIN32) */
-#endif /* defined(TOR_PROCESS_WIN32_H). */
+#endif /* !defined(TOR_PROCESS_WIN32_H) */
diff --git a/src/lib/process/setuid.h b/src/lib/process/setuid.h
index 7d03e1f025..a2125d2d06 100644
--- a/src/lib/process/setuid.h
+++ b/src/lib/process/setuid.h
@@ -19,4 +19,4 @@ int have_capability_support(void);
#define SWITCH_ID_WARN_IF_NO_CAPS (1<<1)
int switch_id(const char *user, unsigned flags);
-#endif
+#endif /* !defined(TOR_SETUID_H) */
diff --git a/src/lib/process/winprocess_sys.c b/src/lib/process/winprocess_sys.c
index 407eeaaeed..ff9bc1ba04 100644
--- a/src/lib/process/winprocess_sys.c
+++ b/src/lib/process/winprocess_sys.c
@@ -51,7 +51,7 @@ subsys_winprocess_initialize(void)
return 0;
}
-#else /* !defined(_WIN32) */
+#else /* !(defined(_WIN32)) */
#define WINPROCESS_SYS_ENABLED false
#define subsys_winprocess_initialize NULL
#endif /* defined(_WIN32) */
diff --git a/src/lib/pubsub/.may_include b/src/lib/pubsub/.may_include
new file mode 100644
index 0000000000..5623492f00
--- /dev/null
+++ b/src/lib/pubsub/.may_include
@@ -0,0 +1,10 @@
+orconfig.h
+
+lib/cc/*.h
+lib/container/*.h
+lib/dispatch/*.h
+lib/intmath/*.h
+lib/log/*.h
+lib/malloc/*.h
+lib/pubsub/*.h
+lib/string/*.h
diff --git a/src/lib/pubsub/include.am b/src/lib/pubsub/include.am
new file mode 100644
index 0000000000..e2abebcd40
--- /dev/null
+++ b/src/lib/pubsub/include.am
@@ -0,0 +1,28 @@
+
+noinst_LIBRARIES += src/lib/libtor-pubsub.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += src/lib/libtor-pubsub-testing.a
+endif
+
+# ADD_C_FILE: INSERT SOURCES HERE.
+src_lib_libtor_pubsub_a_SOURCES = \
+ src/lib/pubsub/pubsub_build.c \
+ src/lib/pubsub/pubsub_check.c \
+ src/lib/pubsub/pubsub_publish.c
+
+src_lib_libtor_pubsub_testing_a_SOURCES = \
+ $(src_lib_libtor_pubsub_a_SOURCES)
+src_lib_libtor_pubsub_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_lib_libtor_pubsub_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
+# ADD_C_FILE: INSERT HEADERS HERE.
+noinst_HEADERS += \
+ src/lib/pubsub/pub_binding_st.h \
+ src/lib/pubsub/pubsub.h \
+ src/lib/pubsub/pubsub_build.h \
+ src/lib/pubsub/pubsub_builder_st.h \
+ src/lib/pubsub/pubsub_connect.h \
+ src/lib/pubsub/pubsub_flags.h \
+ src/lib/pubsub/pubsub_macros.h \
+ src/lib/pubsub/pubsub_publish.h
diff --git a/src/lib/pubsub/pub_binding_st.h b/src/lib/pubsub/pub_binding_st.h
new file mode 100644
index 0000000000..d841bf3f54
--- /dev/null
+++ b/src/lib/pubsub/pub_binding_st.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file pub_binding_st.h
+ * @brief Declaration of pub_binding_t.
+ *
+ * This is an internal type for the pubsub implementation.
+ */
+
+#ifndef TOR_PUB_BINDING_ST_H
+#define TOR_PUB_BINDING_ST_H
+
+#include "lib/dispatch/msgtypes.h"
+struct dispatch_t;
+
+/**
+ * A pub_binding_t is an opaque object that subsystems use to publish
+ * messages. The DISPATCH_ADD_PUB*() macros set it up.
+ **/
+typedef struct pub_binding_t {
+ /**
+ * A pointer to a configured dispatch_t object. This is filled in
+ * when the dispatch_t is finally constructed.
+ **/
+ struct dispatch_t *dispatch_ptr;
+ /**
+ * A template for the msg_t fields that are filled in for this message.
+ * This is copied into outgoing messages, ensuring that their fields are set
+ * corretly.
+ **/
+ msg_t msg_template;
+} pub_binding_t;
+
+#endif /* !defined(TOR_PUB_BINDING_ST_H) */
diff --git a/src/lib/pubsub/pubsub.h b/src/lib/pubsub/pubsub.h
new file mode 100644
index 0000000000..5346b07517
--- /dev/null
+++ b/src/lib/pubsub/pubsub.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file pubsub.h
+ * @brief Header for OO publish-subscribe functionality.
+ *
+ * This module provides a wrapper around the "dispatch" module,
+ * ensuring type-safety and allowing us to do static analysis on
+ * publication and subscriptions.
+ *
+ * With this module, we enforce:
+ * <ul>
+ * <li>that every message has (potential) publishers and subscribers;
+ * <li>that every message is published and subscribed from the correct
+ * channels, with the correct type ID, every time it is published.
+ * <li>that type IDs correspond to a single C type, and that the C types are
+ * used correctly.
+ * <li>that when a message is published or subscribed, it is done with
+ * a correct subsystem identifier
+ * </ul>
+ *
+ * We do this by making "publication requests" and "subscription requests"
+ * into objects, and doing some computation on them before we create
+ * a dispatch_t with them.
+ *
+ * Rather than using the dispatch module directly, a publishing module
+ * receives a "binding" object that it uses to send messages with the right
+ * settings.
+ *
+ * Most users of this module will want to use this header, and the
+ * pubsub_macros.h header for convenience.
+ */
+
+/*
+ *
+ * Overview: Messages are sent over channels. Before sending a message on a
+ * channel, or receiving a message on a channel, a subsystem needs to register
+ * that it publishes, or subscribes, to that message, on that channel.
+ *
+ * Messages, channels, and subsystems are represented internally as short
+ * integers, though they are associated with human-readable strings for
+ * initialization and debugging.
+ *
+ * When registering for a message, a subsystem must say whether it is an
+ * exclusive publisher/subscriber to that message type, or whether other
+ * subsystems may also publish/subscribe to it.
+ *
+ * All messages and their publishers/subscribers must be registered early in
+ * the initialization process.
+ *
+ * By default, it is an error for a message type to have publishers and no
+ * subscribers on a channel, or subscribers and no publishers on a channel.
+ *
+ * A subsystem may register for a message with a note that delivery or
+ * production is disabled -- for example, because the subsystem is
+ * disabled at compile-time. It is not an error for a message type to
+ * have all of its publishers or subscribers disabled.
+ *
+ * After a message is sent, it is delivered to every recipient. This
+ * delivery happens from the top level of the event loop; it may be
+ * interleaved with network events, timers, etc.
+ *
+ * Messages may have associated data. This data is typed, and is owned
+ * by the message. Strings, byte-arrays, and integers have built-in
+ * support. Other types may be added. If objects are to be sent,
+ * they should be identified by handle. If an object requires cleanup,
+ * it should be declared with an associated free function.
+ *
+ * Semantically, if two subsystems communicate only by this kind of
+ * message passing, neither is considered to depend on the other, though
+ * both are considered to have a dependency on the message and on any
+ * types it contains.
+ *
+ * (Or generational index?)
+ */
+#ifndef TOR_PUBSUB_PUBSUB_H
+#define TOR_PUBSUB_PUBSUB_H
+
+#include "lib/pubsub/pub_binding_st.h"
+#include "lib/pubsub/pubsub_connect.h"
+#include "lib/pubsub/pubsub_flags.h"
+#include "lib/pubsub/pubsub_macros.h"
+#include "lib/pubsub/pubsub_publish.h"
+
+#endif /* !defined(TOR_PUBSUB_PUBSUB_H) */
diff --git a/src/lib/pubsub/pubsub_build.c b/src/lib/pubsub/pubsub_build.c
new file mode 100644
index 0000000000..e44b7d76ec
--- /dev/null
+++ b/src/lib/pubsub/pubsub_build.c
@@ -0,0 +1,307 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file pubsub_build.c
+ * @brief Construct a dispatch_t in safer, more OO way.
+ **/
+
+#define PUBSUB_PRIVATE
+
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_cfg.h"
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/dispatch/msgtypes.h"
+#include "lib/pubsub/pubsub_flags.h"
+#include "lib/pubsub/pub_binding_st.h"
+#include "lib/pubsub/pubsub_build.h"
+#include "lib/pubsub/pubsub_builder_st.h"
+#include "lib/pubsub/pubsub_connect.h"
+
+#include "lib/container/smartlist.h"
+#include "lib/log/util_bug.h"
+#include "lib/malloc/malloc.h"
+
+ #include <string.h>
+
+/** Construct and return a new empty pubsub_items_t. */
+static pubsub_items_t *
+pubsub_items_new(void)
+{
+ pubsub_items_t *cfg = tor_malloc_zero(sizeof(*cfg));
+ cfg->items = smartlist_new();
+ cfg->type_items = smartlist_new();
+ return cfg;
+}
+
+/** Release all storage held in a pubsub_items_t. */
+void
+pubsub_items_free_(pubsub_items_t *cfg)
+{
+ if (! cfg)
+ return;
+ SMARTLIST_FOREACH(cfg->items, pubsub_cfg_t *, item, tor_free(item));
+ SMARTLIST_FOREACH(cfg->type_items,
+ pubsub_type_cfg_t *, item, tor_free(item));
+ smartlist_free(cfg->items);
+ smartlist_free(cfg->type_items);
+ tor_free(cfg);
+}
+
+/** Construct and return a new pubsub_builder_t. */
+pubsub_builder_t *
+pubsub_builder_new(void)
+{
+ dispatch_naming_init();
+
+ pubsub_builder_t *pb = tor_malloc_zero(sizeof(*pb));
+ pb->cfg = dcfg_new();
+ pb->items = pubsub_items_new();
+ return pb;
+}
+
+/**
+ * Release all storage held by a pubsub_builder_t.
+ *
+ * You'll (mostly) only want to call this function on an error case: if you're
+ * constructing a dispatch_t instead, you should call
+ * pubsub_builder_finalize() to consume the pubsub_builder_t.
+ */
+void
+pubsub_builder_free_(pubsub_builder_t *pb)
+{
+ if (pb == NULL)
+ return;
+ pubsub_items_free(pb->items);
+ dcfg_free(pb->cfg);
+ tor_free(pb);
+}
+
+/**
+ * Create and return a pubsub_connector_t for the subsystem with ID
+ * <b>subsys</b> to use in adding publications, subscriptions, and types to
+ * <b>builder</b>.
+ **/
+pubsub_connector_t *
+pubsub_connector_for_subsystem(pubsub_builder_t *builder,
+ subsys_id_t subsys)
+{
+ tor_assert(builder);
+ ++builder->n_connectors;
+
+ pubsub_connector_t *con = tor_malloc_zero(sizeof(*con));
+
+ con->builder = builder;
+ con->subsys_id = subsys;
+
+ return con;
+}
+
+/**
+ * Release all storage held by a pubsub_connector_t.
+ **/
+void
+pubsub_connector_free_(pubsub_connector_t *con)
+{
+ if (!con)
+ return;
+
+ if (con->builder) {
+ --con->builder->n_connectors;
+ tor_assert(con->builder->n_connectors >= 0);
+ }
+ tor_free(con);
+}
+
+/**
+ * Use <b>con</b> to add a request for being able to publish messages of type
+ * <b>msg</b> with auxiliary data of <b>type</b> on <b>channel</b>.
+ **/
+int
+pubsub_add_pub_(pubsub_connector_t *con,
+ pub_binding_t *out,
+ channel_id_t channel,
+ message_id_t msg,
+ msg_type_id_t type,
+ unsigned flags,
+ const char *file,
+ unsigned line)
+{
+ pubsub_cfg_t *cfg = tor_malloc_zero(sizeof(*cfg));
+
+ memset(out, 0, sizeof(*out));
+ cfg->is_publish = true;
+
+ out->msg_template.sender = cfg->subsys = con->subsys_id;
+ out->msg_template.channel = cfg->channel = channel;
+ out->msg_template.msg = cfg->msg = msg;
+ out->msg_template.type = cfg->type = type;
+
+ cfg->flags = flags;
+ cfg->added_by_file = file;
+ cfg->added_by_line = line;
+
+ /* We're grabbing a pointer to the pub_binding_t so we can tell it about
+ * the dispatcher later on.
+ */
+ cfg->pub_binding = out;
+
+ smartlist_add(con->builder->items->items, cfg);
+
+ if (dcfg_msg_set_type(con->builder->cfg, msg, type) < 0)
+ goto err;
+ if (dcfg_msg_set_chan(con->builder->cfg, msg, channel) < 0)
+ goto err;
+
+ return 0;
+ err:
+ ++con->builder->n_errors;
+ return -1;
+}
+
+/**
+ * Use <b>con</b> to add a request for being able to publish messages of type
+ * <b>msg</b> with auxiliary data of <b>type</b> on <b>channel</b>,
+ * passing them to the callback in <b>recv_fn</b>.
+ **/
+int
+pubsub_add_sub_(pubsub_connector_t *con,
+ recv_fn_t recv_fn,
+ channel_id_t channel,
+ message_id_t msg,
+ msg_type_id_t type,
+ unsigned flags,
+ const char *file,
+ unsigned line)
+{
+ pubsub_cfg_t *cfg = tor_malloc_zero(sizeof(*cfg));
+
+ cfg->is_publish = false;
+ cfg->subsys = con->subsys_id;
+ cfg->channel = channel;
+ cfg->msg = msg;
+ cfg->type = type;
+ cfg->flags = flags;
+ cfg->added_by_file = file;
+ cfg->added_by_line = line;
+
+ cfg->recv_fn = recv_fn;
+
+ smartlist_add(con->builder->items->items, cfg);
+
+ if (dcfg_msg_set_type(con->builder->cfg, msg, type) < 0)
+ goto err;
+ if (dcfg_msg_set_chan(con->builder->cfg, msg, channel) < 0)
+ goto err;
+ if (! (flags & DISP_FLAG_STUB)) {
+ if (dcfg_add_recv(con->builder->cfg, msg, cfg->subsys, recv_fn) < 0)
+ goto err;
+ }
+
+ return 0;
+ err:
+ ++con->builder->n_errors;
+ return -1;
+}
+
+/**
+ * Use <b>con</b> to define the functions to use for manipulating the type
+ * <b>type</b>. Any function pointers left as NULL will be implemented as
+ * no-ops.
+ **/
+int
+pubsub_connector_register_type_(pubsub_connector_t *con,
+ msg_type_id_t type,
+ dispatch_typefns_t *fns,
+ const char *file,
+ unsigned line)
+{
+ pubsub_type_cfg_t *cfg = tor_malloc_zero(sizeof(*cfg));
+ cfg->type = type;
+ memcpy(&cfg->fns, fns, sizeof(*fns));
+ cfg->subsys = con->subsys_id;
+ cfg->added_by_file = file;
+ cfg->added_by_line = line;
+
+ smartlist_add(con->builder->items->type_items, cfg);
+
+ if (dcfg_type_set_fns(con->builder->cfg, type, fns) < 0)
+ goto err;
+
+ return 0;
+ err:
+ ++con->builder->n_errors;
+ return -1;
+}
+
+/**
+ * Initialize the dispatch_ptr field in every relevant publish binding
+ * for <b>d</b>.
+ */
+static void
+pubsub_items_install_bindings(pubsub_items_t *items,
+ dispatch_t *d)
+{
+ SMARTLIST_FOREACH_BEGIN(items->items, pubsub_cfg_t *, cfg) {
+ if (cfg->pub_binding) {
+ // XXXX we could skip this for STUB publishers, and for any publishers
+ // XXXX where all subscribers are STUB.
+ cfg->pub_binding->dispatch_ptr = d;
+ }
+ } SMARTLIST_FOREACH_END(cfg);
+}
+
+/**
+ * Remove the dispatch_ptr fields for all the relevant publish bindings
+ * in <b>items</b>. The prevents subsequent dispatch_pub_() calls from
+ * sending messages to a dispatcher that has been freed.
+ **/
+void
+pubsub_items_clear_bindings(pubsub_items_t *items)
+{
+ SMARTLIST_FOREACH_BEGIN(items->items, pubsub_cfg_t *, cfg) {
+ if (cfg->pub_binding) {
+ cfg->pub_binding->dispatch_ptr = NULL;
+ }
+ } SMARTLIST_FOREACH_END(cfg);
+}
+
+/**
+ * Create a new dispatcher as configured in a pubsub_builder_t.
+ *
+ * Consumes and frees its input.
+ **/
+dispatch_t *
+pubsub_builder_finalize(pubsub_builder_t *builder,
+ pubsub_items_t **items_out)
+{
+ dispatch_t *dispatcher = NULL;
+ tor_assert_nonfatal(builder->n_connectors == 0);
+
+ if (pubsub_builder_check(builder) < 0)
+ goto err;
+
+ if (builder->n_errors) {
+ log_warn(LD_GENERAL, "At least one error occurred previously when "
+ "configuring the dispatcher.");
+ goto err;
+ }
+
+ dispatcher = dispatch_new(builder->cfg);
+
+ if (!dispatcher)
+ goto err;
+
+ pubsub_items_install_bindings(builder->items, dispatcher);
+ if (items_out) {
+ *items_out = builder->items;
+ builder->items = NULL; /* Prevent free */
+ }
+
+ err:
+ pubsub_builder_free(builder);
+ return dispatcher;
+}
diff --git a/src/lib/pubsub/pubsub_build.h b/src/lib/pubsub/pubsub_build.h
new file mode 100644
index 0000000000..5a0c5f5bd3
--- /dev/null
+++ b/src/lib/pubsub/pubsub_build.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file pubsub_build.h
+ * @brief Header used for constructing the OO publish-subscribe facility.
+ *
+ * (See pubsub.h for more general information on this API.)
+ **/
+
+#ifndef TOR_PUBSUB_BUILD_H
+#define TOR_PUBSUB_BUILD_H
+
+#include "lib/dispatch/msgtypes.h"
+
+struct dispatch_t;
+struct pubsub_connector_t;
+
+/**
+ * A "dispatch builder" is an incomplete dispatcher, used when
+ * registering messages. It does not have the same integrity guarantees
+ * as a dispatcher. It cannot actually handle messages itself: once all
+ * subsystems have registered, it is converted into a dispatch_t.
+ **/
+typedef struct pubsub_builder_t pubsub_builder_t;
+
+/**
+ * A "pubsub items" holds the configuration items used to configure a
+ * pubsub_builder. After the builder is finalized, this field is extracted,
+ * and used later to tear down pointers that enable publishing.
+ **/
+typedef struct pubsub_items_t pubsub_items_t;
+
+/**
+ * Create a new pubsub_builder. This should only happen in the
+ * main-init code.
+ */
+pubsub_builder_t *pubsub_builder_new(void);
+
+/** DOCDOC */
+int pubsub_builder_check(pubsub_builder_t *);
+
+/**
+ * Free a pubsub builder. This should only happen on error paths, where
+ * we have decided not to construct a dispatcher for some reason.
+ */
+#define pubsub_builder_free(db) \
+ FREE_AND_NULL(pubsub_builder_t, pubsub_builder_free_, (db))
+
+/** Internal implementation of pubsub_builder_free(). */
+void pubsub_builder_free_(pubsub_builder_t *);
+
+/**
+ * Create a pubsub connector that a single subsystem will use to
+ * register its messages. The main-init code does this during susbsystem
+ * initialization.
+ */
+struct pubsub_connector_t *pubsub_connector_for_subsystem(pubsub_builder_t *,
+ subsys_id_t);
+
+/**
+ * The main-init code does this after subsystem initialization.
+ */
+#define pubsub_connector_free(c) \
+ FREE_AND_NULL(struct pubsub_connector_t, pubsub_connector_free_, (c))
+
+void pubsub_connector_free_(struct pubsub_connector_t *);
+
+/**
+ * Constructs a dispatcher from a dispatch_builder, after checking that the
+ * invariances on the messages, channels, and connections have been
+ * respected.
+ *
+ * This should happen after every subsystem has initialized, and before
+ * entering the mainloop.
+ */
+struct dispatch_t *pubsub_builder_finalize(pubsub_builder_t *,
+ pubsub_items_t **items_out);
+
+/**
+ * Clear all pub_binding_t backpointers in <b>items</b>.
+ **/
+void pubsub_items_clear_bindings(pubsub_items_t *items);
+
+#define pubsub_items_free(cfg) \
+ FREE_AND_NULL(pubsub_items_t, pubsub_items_free_, (cfg))
+void pubsub_items_free_(pubsub_items_t *cfg);
+
+#endif /* !defined(TOR_PUBSUB_BUILD_H) */
diff --git a/src/lib/pubsub/pubsub_builder_st.h b/src/lib/pubsub/pubsub_builder_st.h
new file mode 100644
index 0000000000..545aa3f3ef
--- /dev/null
+++ b/src/lib/pubsub/pubsub_builder_st.h
@@ -0,0 +1,161 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file pubsub_builder_st.h
+ *
+ * @brief private structures used for configuring dispatchers and messages.
+ */
+
+#ifndef TOR_PUBSUB_BUILDER_ST_H
+#define TOR_PUBSUB_BUILDER_ST_H
+
+#ifdef PUBSUB_PRIVATE
+
+#include <stdbool.h>
+#include <stddef.h>
+
+struct dispatch_cfg_t;
+struct smartlist_t;
+struct pub_binding_t;
+
+/**
+ * Configuration for a single publication or subscription request.
+ *
+ * These can be stored while the dispatcher is in use, but are only used for
+ * setup, teardown, and debugging.
+ *
+ * There are various fields in this request describing the message; all of
+ * them must match other descriptions of the message, or a bug has occurred.
+ **/
+typedef struct pubsub_cfg_t {
+ /** True if this is a publishing request; false for a subscribing request. */
+ bool is_publish;
+ /** The system making this request. */
+ subsys_id_t subsys;
+ /** The channel on which the message is to be sent. */
+ channel_id_t channel;
+ /** The message ID to be sent or received. */
+ message_id_t msg;
+ /** The C type associated with the message. */
+ msg_type_id_t type;
+ /** One or more DISP_FLAGS_* items, combined with bitwise OR. */
+ unsigned flags;
+
+ /**
+ * Publishing only: a pub_binding object that will receive the binding for
+ * this request. We will finish filling this in when the dispatcher is
+ * constructed, so that the subsystem can publish then and not before.
+ */
+ struct pub_binding_t *pub_binding;
+
+ /**
+ * Subscribing only: a function to receive message objects for this request.
+ */
+ recv_fn_t recv_fn;
+
+ /** The file from which this message was configured */
+ const char *added_by_file;
+ /** The line at which this message was configured */
+ unsigned added_by_line;
+} pubsub_cfg_t;
+
+/**
+ * Configuration request for a single C type.
+ *
+ * These are stored while the dispatcher is in use, but are only used for
+ * setup, teardown, and debugging.
+ **/
+typedef struct pubsub_type_cfg_t {
+ /**
+ * The identifier for this type.
+ */
+ msg_type_id_t type;
+ /**
+ * Functions to use when manipulating the type.
+ */
+ dispatch_typefns_t fns;
+
+ /** The subsystem that configured this type. */
+ subsys_id_t subsys;
+ /** The file from which this type was configured */
+ const char *added_by_file;
+ /** The line at which this type was configured */
+ unsigned added_by_line;
+} pubsub_type_cfg_t;
+
+/**
+ * The set of configuration requests for a dispatcher, as made by various
+ * subsystems.
+ **/
+struct pubsub_items_t {
+ /** List of pubsub_cfg_t. */
+ struct smartlist_t *items;
+ /** List of pubsub_type_cfg_t. */
+ struct smartlist_t *type_items;
+};
+
+/**
+ * Type used to construct a dispatcher. We use this type to build up the
+ * configuration for a dispatcher, and then pass ownership of that
+ * configuration to the newly constructed dispatcher.
+ **/
+struct pubsub_builder_t {
+ /** Number of outstanding pubsub_connector_t objects pointing to this
+ * pubsub_builder_t. */
+ int n_connectors;
+ /** Number of errors encountered while constructing this object so far. */
+ int n_errors;
+ /** In-progress configuration that we're constructing, as a list of the
+ * requests that have been made. */
+ struct pubsub_items_t *items;
+ /** In-progress configuration that we're constructing, in a form that can
+ * be converted to a dispatch_t. */
+ struct dispatch_cfg_t *cfg;
+};
+
+/**
+ * Type given to a subsystem when adding connections to a pubsub_builder_t.
+ * We use this type to force each subsystem to get blamed for the
+ * publications, subscriptions, and types that it adds.
+ **/
+struct pubsub_connector_t {
+ /** The pubsub_builder that this connector refers to. */
+ struct pubsub_builder_t *builder;
+ /** The subsystem that has been given this connector. */
+ subsys_id_t subsys_id;
+};
+
+/**
+ * Helper structure used when constructing a dispatcher that sorts the
+ * pubsub_cfg_t objects in various ways.
+ **/
+typedef struct pubsub_adjmap_t {
+ /* XXXX The next three fields are currently constructed but not yet
+ * XXXX used. I believe we'll want them in the future, though. -nickm
+ */
+ /** Number of subsystems; length of the *_by_subsys arrays. */
+ size_t n_subsystems;
+ /** Array of lists of publisher pubsub_cfg_t objects, indexed by
+ * subsystem. */
+ struct smartlist_t **pub_by_subsys;
+ /** Array of lists of subscriber pubsub_cfg_t objects, indexed by
+ * subsystem. */
+ struct smartlist_t **sub_by_subsys;
+
+ /** Number of message IDs; length of the *_by_msg arrays. */
+ size_t n_msgs;
+ /** Array of lists of publisher pubsub_cfg_t objects, indexed by
+ * message ID. */
+ struct smartlist_t **pub_by_msg;
+ /** Array of lists of subscriber pubsub_cfg_t objects, indexed by
+ * message ID. */
+ struct smartlist_t **sub_by_msg;
+} pubsub_adjmap_t;
+
+#endif /* defined(PUBSUB_PRIVATE) */
+
+#endif /* !defined(TOR_PUBSUB_BUILDER_ST_H) */
diff --git a/src/lib/pubsub/pubsub_check.c b/src/lib/pubsub/pubsub_check.c
new file mode 100644
index 0000000000..a3c22d4f25
--- /dev/null
+++ b/src/lib/pubsub/pubsub_check.c
@@ -0,0 +1,428 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file pubsub_check.c
+ * @brief Enforce various requirements on a pubsub_builder.
+ **/
+
+#define PUBSUB_PRIVATE
+
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/dispatch/msgtypes.h"
+#include "lib/pubsub/pubsub_flags.h"
+#include "lib/pubsub/pubsub_builder_st.h"
+#include "lib/pubsub/pubsub_build.h"
+
+#include "lib/container/bitarray.h"
+#include "lib/container/smartlist.h"
+#include "lib/log/util_bug.h"
+#include "lib/malloc/malloc.h"
+#include "lib/string/compat_string.h"
+
+#include <string.h>
+
+static void pubsub_adjmap_add(pubsub_adjmap_t *map,
+ const pubsub_cfg_t *item);
+
+/**
+ * Helper: contruct and return a new pubsub_adjacency_map from <b>cfg</b>.
+ * Return NULL on error.
+ **/
+static pubsub_adjmap_t *
+pubsub_build_adjacency_map(const pubsub_items_t *cfg)
+{
+ pubsub_adjmap_t *map = tor_malloc_zero(sizeof(*map));
+ const size_t n_subsystems = get_num_subsys_ids();
+ const size_t n_msgs = get_num_message_ids();
+
+ map->n_subsystems = n_subsystems;
+ map->n_msgs = n_msgs;
+
+ map->pub_by_subsys = tor_calloc(n_subsystems, sizeof(smartlist_t*));
+ map->sub_by_subsys = tor_calloc(n_subsystems, sizeof(smartlist_t*));
+ map->pub_by_msg = tor_calloc(n_msgs, sizeof(smartlist_t*));
+ map->sub_by_msg = tor_calloc(n_msgs, sizeof(smartlist_t*));
+
+ SMARTLIST_FOREACH_BEGIN(cfg->items, const pubsub_cfg_t *, item) {
+ pubsub_adjmap_add(map, item);
+ } SMARTLIST_FOREACH_END(item);
+
+ return map;
+}
+
+/**
+ * Helper: add a single pubsub_cfg_t to an adjacency map.
+ **/
+static void
+pubsub_adjmap_add(pubsub_adjmap_t *map,
+ const pubsub_cfg_t *item)
+{
+ smartlist_t **by_subsys;
+ smartlist_t **by_msg;
+
+ tor_assert(item->subsys < map->n_subsystems);
+ tor_assert(item->msg < map->n_msgs);
+
+ if (item->is_publish) {
+ by_subsys = &map->pub_by_subsys[item->subsys];
+ by_msg = &map->pub_by_msg[item->msg];
+ } else {
+ by_subsys = &map->sub_by_subsys[item->subsys];
+ by_msg = &map->sub_by_msg[item->msg];
+ }
+
+ if (! *by_subsys)
+ *by_subsys = smartlist_new();
+ if (! *by_msg)
+ *by_msg = smartlist_new();
+ smartlist_add(*by_subsys, (void*) item);
+ smartlist_add(*by_msg, (void *) item);
+}
+
+/**
+ * Release all storage held by m and set m to NULL.
+ **/
+#define pubsub_adjmap_free(m) \
+ FREE_AND_NULL(pubsub_adjmap_t, pubsub_adjmap_free_, m)
+
+/**
+ * Free every element of an <b>n</b>-element array of smartlists, then
+ * free the array itself.
+ **/
+static void
+pubsub_adjmap_free_helper(smartlist_t **lsts, size_t n)
+{
+ if (!lsts)
+ return;
+
+ for (unsigned i = 0; i < n; ++i) {
+ smartlist_free(lsts[i]);
+ }
+ tor_free(lsts);
+}
+
+/**
+ * Release all storage held by <b>map</b>.
+ **/
+static void
+pubsub_adjmap_free_(pubsub_adjmap_t *map)
+{
+ if (!map)
+ return;
+ pubsub_adjmap_free_helper(map->pub_by_subsys, map->n_subsystems);
+ pubsub_adjmap_free_helper(map->sub_by_subsys, map->n_subsystems);
+ pubsub_adjmap_free_helper(map->pub_by_msg, map->n_msgs);
+ pubsub_adjmap_free_helper(map->sub_by_msg, map->n_msgs);
+ tor_free(map);
+}
+
+/**
+ * Helper: return the length of <b>sl</b>, or 0 if sl is NULL.
+ **/
+static int
+smartlist_len_opt(const smartlist_t *sl)
+{
+ if (sl)
+ return smartlist_len(sl);
+ else
+ return 0;
+}
+
+/** Return a pointer to a statically allocated string encoding the
+ * dispatcher flags in <b>flags</b>. */
+static const char *
+format_flags(unsigned flags)
+{
+ static char buf[32];
+ buf[0] = 0;
+ if (flags & DISP_FLAG_EXCL) {
+ strlcat(buf, " EXCL", sizeof(buf));
+ }
+ if (flags & DISP_FLAG_STUB) {
+ strlcat(buf, " STUB", sizeof(buf));
+ }
+ return buf[0] ? buf+1 : buf;
+}
+
+/**
+ * Log a message containing a description of <b>cfg</b> at severity, prefixed
+ * by the string <b>prefix</b>.
+ */
+static void
+pubsub_cfg_dump(const pubsub_cfg_t *cfg, int severity, const char *prefix)
+{
+ tor_assert(prefix);
+
+ tor_log(severity, LD_MESG,
+ "%s%s %s: %s{%s} on %s (%s) <%u %u %u %u %x> [%s:%d]",
+ prefix,
+ get_subsys_id_name(cfg->subsys),
+ cfg->is_publish ? "PUB" : "SUB",
+ get_message_id_name(cfg->msg),
+ get_msg_type_id_name(cfg->type),
+ get_channel_id_name(cfg->channel),
+ format_flags(cfg->flags),
+ cfg->subsys, cfg->msg, cfg->type, cfg->channel, cfg->flags,
+ cfg->added_by_file, cfg->added_by_line);
+}
+
+/**
+ * Helper: fill a bitarray <b>out</b> with entries corresponding to the
+ * subsystems listed in <b>items</b>. If any subsystem is listed more than
+ * once, log a warning. Return 0 on success, -1 on failure.
+ **/
+static int
+get_message_bitarray(const pubsub_adjmap_t *map,
+ message_id_t msg,
+ const smartlist_t *items,
+ const char *operation,
+ bitarray_t **out)
+{
+ bool ok = true;
+ *out = bitarray_init_zero((unsigned)map->n_subsystems);
+ if (! items)
+ return 0;
+
+ SMARTLIST_FOREACH_BEGIN(items, const pubsub_cfg_t *, cfg) {
+ if (bitarray_is_set(*out, cfg->subsys)) {
+ log_warn(LD_MESG|LD_BUG,
+ "Message \"%s\" is configured to be %s by subsystem "
+ "\"%s\" more than once.",
+ get_message_id_name(msg), operation,
+ get_subsys_id_name(cfg->subsys));
+ ok = false;
+ }
+ bitarray_set(*out, cfg->subsys);
+ } SMARTLIST_FOREACH_END(cfg);
+
+ return ok ? 0 : -1;
+}
+
+/**
+ * Helper for lint_message: check that all the pubsub_cfg_t items in the two
+ * respective smartlists obey our local graph topology rules.
+ *
+ * (Right now this is just a matter of "each subsystem only
+ * publishes/subscribes once; no subsystem is a publisher and subscriber for
+ * the same message.")
+ *
+ * Return 0 on success, -1 on failure.
+ **/
+static int
+lint_message_graph(const pubsub_adjmap_t *map,
+ message_id_t msg,
+ const smartlist_t *pub,
+ const smartlist_t *sub)
+{
+ bitarray_t *published_by = NULL;
+ bitarray_t *subscribed_by = NULL;
+ bool ok = true;
+
+ if (get_message_bitarray(map, msg, pub, "published", &published_by) < 0)
+ ok = false;
+ if (get_message_bitarray(map, msg, sub, "subscribed", &subscribed_by) < 0)
+ ok = false;
+
+ /* Check whether any subsystem is publishing and subscribing the same
+ * message. [??]
+ */
+ for (unsigned i = 0; i < map->n_subsystems; ++i) {
+ if (bitarray_is_set(published_by, i) &&
+ bitarray_is_set(subscribed_by, i)) {
+ log_warn(LD_MESG|LD_BUG,
+ "Message \"%s\" is published and subscribed by the same "
+ "subsystem \"%s\".",
+ get_message_id_name(msg),
+ get_subsys_id_name(i));
+ ok = false;
+ }
+ }
+
+ bitarray_free(published_by);
+ bitarray_free(subscribed_by);
+
+ return ok ? 0 : -1;
+}
+
+/**
+ * Helper for lint_message: check that all the pubsub_cfg_t items in the two
+ * respective smartlists have compatible flags, channels, and types.
+ **/
+static int
+lint_message_consistency(message_id_t msg,
+ const smartlist_t *pub,
+ const smartlist_t *sub)
+{
+ if (!smartlist_len_opt(pub) && !smartlist_len_opt(sub))
+ return 0; // LCOV_EXCL_LINE -- this was already checked.
+
+ /* The 'all' list has the publishers and the subscribers. */
+ smartlist_t *all = smartlist_new();
+ if (pub)
+ smartlist_add_all(all, pub);
+ if (sub)
+ smartlist_add_all(all, sub);
+
+ const pubsub_cfg_t *item0 = smartlist_get(all, 0);
+
+ /* Indicates which subsystems we've found publishing/subscribing here. */
+ bool pub_excl = false, sub_excl = false, chan_same = true, type_same = true;
+
+ /* Simple message consistency properties across messages.
+ */
+ SMARTLIST_FOREACH_BEGIN(all, const pubsub_cfg_t *, cfg) {
+ chan_same &= (cfg->channel == item0->channel);
+ type_same &= (cfg->type == item0->type);
+ if (cfg->is_publish)
+ pub_excl |= (cfg->flags & DISP_FLAG_EXCL) != 0;
+ else
+ sub_excl |= (cfg->flags & DISP_FLAG_EXCL) != 0;
+ } SMARTLIST_FOREACH_END(cfg);
+
+ bool ok = true;
+
+ if (! chan_same) {
+ log_warn(LD_MESG|LD_BUG,
+ "Message \"%s\" is associated with multiple inconsistent "
+ "channels.",
+ get_message_id_name(msg));
+ ok = false;
+ }
+ if (! type_same) {
+ log_warn(LD_MESG|LD_BUG,
+ "Message \"%s\" is associated with multiple inconsistent "
+ "message types.",
+ get_message_id_name(msg));
+ ok = false;
+ }
+
+ /* Enforce exclusive-ness for publishers and subscribers that have asked for
+ * it.
+ */
+ if (pub_excl && smartlist_len_opt(pub) > 1) {
+ log_warn(LD_MESG|LD_BUG,
+ "Message \"%s\" has multiple publishers, but at least one is "
+ "marked as exclusive.",
+ get_message_id_name(msg));
+ ok = false;
+ }
+ if (sub_excl && smartlist_len_opt(sub) > 1) {
+ log_warn(LD_MESG|LD_BUG,
+ "Message \"%s\" has multiple subscribers, but at least one is "
+ "marked as exclusive.",
+ get_message_id_name(msg));
+ ok = false;
+ }
+
+ smartlist_free(all);
+
+ return ok ? 0 : -1;
+}
+
+/**
+ * Check whether there are any errors or inconsistencies for the message
+ * described by <b>msg</b> in <b>map</b>. If there are problems, log about
+ * them, and return -1. Otherwise return 0.
+ **/
+static int
+lint_message(const pubsub_adjmap_t *map, message_id_t msg)
+{
+ /* NOTE: Some of the checks in this function are maybe over-zealous, and we
+ * might not want to have them forever. I've marked them with [?] below.
+ */
+ if (BUG(msg >= map->n_msgs))
+ return 0; // LCOV_EXCL_LINE
+
+ const smartlist_t *pub = map->pub_by_msg[msg];
+ const smartlist_t *sub = map->sub_by_msg[msg];
+
+ const size_t n_pub = smartlist_len_opt(pub);
+ const size_t n_sub = smartlist_len_opt(sub);
+
+ if (n_pub == 0 && n_sub == 0) {
+ log_info(LD_MESG, "Nobody is publishing or subscribing to message "
+ "\"%s\".",
+ get_message_id_name(msg));
+ return 0; // No publishers or subscribers: nothing to do.
+ }
+ /* We'll set this to false if there are any problems. */
+ bool ok = true;
+
+ /* First make sure that if there are publishers, there are subscribers. */
+ if (n_pub == 0) {
+ log_warn(LD_MESG|LD_BUG,
+ "Message \"%s\" has subscribers, but no publishers.",
+ get_message_id_name(msg));
+ ok = false;
+ } else if (n_sub == 0) {
+ log_warn(LD_MESG|LD_BUG,
+ "Message \"%s\" has publishers, but no subscribers.",
+ get_message_id_name(msg));
+ ok = false;
+ }
+
+ /* Check the message graph topology. */
+ if (lint_message_graph(map, msg, pub, sub) < 0)
+ ok = false;
+
+ /* Check whether the messages have the same fields set on them. */
+ if (lint_message_consistency(msg, pub, sub) < 0)
+ ok = false;
+
+ if (!ok) {
+ /* There was a problem -- let's log all the publishers and subscribers on
+ * this message */
+ if (pub) {
+ SMARTLIST_FOREACH(pub, pubsub_cfg_t *, cfg,
+ pubsub_cfg_dump(cfg, LOG_WARN, " "));
+ }
+ if (sub) {
+ SMARTLIST_FOREACH(sub, pubsub_cfg_t *, cfg,
+ pubsub_cfg_dump(cfg, LOG_WARN, " "));
+ }
+ }
+
+ return ok ? 0 : -1;
+}
+
+/**
+ * Check all the messages in <b>map</b> for consistency. Return 0 on success,
+ * -1 on problems.
+ **/
+static int
+pubsub_adjmap_check(const pubsub_adjmap_t *map)
+{
+ bool all_ok = true;
+ for (unsigned i = 0; i < map->n_msgs; ++i) {
+ if (lint_message(map, i) < 0) {
+ all_ok = false;
+ }
+ }
+ return all_ok ? 0 : -1;
+}
+
+/**
+ * Check builder for consistency and various constraints. Return 0 on success,
+ * -1 on failure.
+ **/
+int
+pubsub_builder_check(pubsub_builder_t *builder)
+{
+ pubsub_adjmap_t *map = pubsub_build_adjacency_map(builder->items);
+ int rv = -1;
+
+ if (!map)
+ goto err; // should be impossible
+
+ if (pubsub_adjmap_check(map) < 0)
+ goto err;
+
+ rv = 0;
+ err:
+ pubsub_adjmap_free(map);
+ return rv;
+}
diff --git a/src/lib/pubsub/pubsub_connect.h b/src/lib/pubsub/pubsub_connect.h
new file mode 100644
index 0000000000..0ad106044e
--- /dev/null
+++ b/src/lib/pubsub/pubsub_connect.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file pubsub_connect.h
+ * @brief Header for functions that add relationships to a pubsub builder.
+ *
+ * These functions are used by modules that need to add publication and
+ * subscription requests. Most users will want to call these functions
+ * indirectly, via the macros in pubsub_macros.h.
+ **/
+
+#ifndef TOR_PUBSUB_CONNECT_H
+#define TOR_PUBSUB_CONNECT_H
+
+#include "lib/dispatch/msgtypes.h"
+
+struct pub_binding_t;
+/**
+ * A "dispatch connector" is a view of the dispatcher that a subsystem
+ * uses while initializing itself. It is specific to the subsystem, and
+ * ensures that each subsystem doesn't need to identify itself
+ * repeatedly while registering its messages.
+ **/
+typedef struct pubsub_connector_t pubsub_connector_t;
+
+int pubsub_add_pub_(struct pubsub_connector_t *con,
+ struct pub_binding_t *out,
+ channel_id_t channel,
+ message_id_t msg,
+ msg_type_id_t type,
+ unsigned flags,
+ const char *file,
+ unsigned line);
+
+int pubsub_add_sub_(struct pubsub_connector_t *con,
+ recv_fn_t recv_fn,
+ channel_id_t channel,
+ message_id_t msg,
+ msg_type_id_t type,
+ unsigned flags,
+ const char *file,
+ unsigned line);
+
+int pubsub_connector_register_type_(struct pubsub_connector_t *,
+ msg_type_id_t,
+ dispatch_typefns_t *,
+ const char *file,
+ unsigned line);
+
+#endif /* !defined(TOR_PUBSUB_CONNECT_H) */
diff --git a/src/lib/pubsub/pubsub_flags.h b/src/lib/pubsub/pubsub_flags.h
new file mode 100644
index 0000000000..53c6e49565
--- /dev/null
+++ b/src/lib/pubsub/pubsub_flags.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file pubsub_flags.h
+ * @brief Flags that can be set on publish/subscribe messages.
+ **/
+
+#ifndef TOR_PUBSUB_FLAGS_H
+#define TOR_PUBSUB_FLAGS_H
+
+/**
+ * Flag for registering a message: declare that no other module is allowed to
+ * publish this message if we are publishing it, or subscribe to it if we are
+ * subscribing to it.
+ */
+#define DISP_FLAG_EXCL (1u<<0)
+
+/**
+ * Flag for registering a message: declare that this message is a stub, and we
+ * will not actually publish/subscribe it, but that the dispatcher should
+ * treat us as if we did when typechecking.
+ *
+ * We use this so that messages aren't treated as "dangling" if they are
+ * potentially used by some other build of Tor.
+ */
+#define DISP_FLAG_STUB (1u<<1)
+
+#endif /* !defined(TOR_PUBSUB_FLAGS_H) */
diff --git a/src/lib/pubsub/pubsub_macros.h b/src/lib/pubsub/pubsub_macros.h
new file mode 100644
index 0000000000..357e59fd54
--- /dev/null
+++ b/src/lib/pubsub/pubsub_macros.h
@@ -0,0 +1,373 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file pubsub_macros.h
+ * \brief Macros to help with the publish/subscribe dispatch API.
+ *
+ * The dispatch API allows different subsystems of Tor to communicate with
+ * another asynchronously via a shared "message" system. Some subsystems
+ * declare that they publish a given message, and others declare that they
+ * subscribe to it. Both subsystems depend on the message, but not upon one
+ * another.
+ *
+ * To declare a message, use DECLARE_MESSAGE() (for messages that take their
+ * data as a pointer) or DECLARE_MESSAGE_INT() (for messages that take their
+ * data as an integer. For example, you might say
+ *
+ * DECLARE_MESSAGE(new_circuit, circ, circuit_handle_t *);
+ * or
+ * DECLARE_MESSAGE_INT(shutdown_requested, boolean, bool);
+ *
+ * Every message has a unique name, a "type name" that the dispatch system
+ * uses to manage associated data, and a C type name. You'll want to put
+ * these declarations in a header, to be included by all publishers and all
+ * subscribers.
+ *
+ * When a subsystem wants to publish a message, it uses DECLARE_PUBLISH() at
+ * file scope to create necessary static functions. Then, in its subsystem
+ * initialization (in the "bind to dispatcher" callback) (TODO: name this
+ * properly!), it calls DISPATCH_ADD_PUB() to tell the dispatcher about its
+ * intent to publish. When it actually wants to publish, it uses the
+ * PUBLISH() macro. For example:
+ *
+ * // At file scope
+ * DECLARE_PUBLISH(shutdown_requested);
+ *
+ * static void bind_to_dispatcher(pubsub_connector_t *con)
+ * {
+ * DISPATCH_ADD_PUB(con, mainchannel, shutdown_requested);
+ * }
+ *
+ * // somewhere in a function
+ * {
+ * PUBLISH(shutdown_requested, true);
+ * }
+ *
+ * When a subsystem wants to subscribe to a message, it uses
+ * DECLARE_SUBSCRIBE() at file scope to declare static functions. It must
+ * declare a hook function that receives the message type. Then, in its "bind
+ * to dispatcher" function, it calls DISPATCHER_ADD_SUB() to tell the
+ * dispatcher about its intent to subscribe. When another module publishes
+ * the message, the dispatcher will call the provided hook function.
+ *
+ * // At file scope. The first argument is the message that you're
+ * // subscribing to; the second argument is the hook function to declare.
+ * DECLARE_SUBSCRIBE(shutdown_requested, on_shutdown_req_cb);
+ *
+ * // You need to declare this function.
+ * static void on_shutdown_req_cb(const msg_t *msg,
+ * bool value)
+ * {
+ * // (do something here.)
+ * }
+ *
+ * static void bind_to_dispatcher(pubsub_connector_t *con)
+ * {
+ * DISPATCH_ADD_SUB(con, mainchannel, shutdown_requested);
+ * }
+ *
+ * Where did these types come from? Somewhere in the code, you need to call
+ * DISPATCH_REGISTER_TYPE() to make sure that the dispatcher can manage the
+ * message auxiliary data. It associates a vtbl-like structure with the
+ * type name, so that the dispatcher knows how to manipulate the type you're
+ * giving it.
+ *
+ * For example, the "boolean" type we're using above could be defined as:
+ *
+ * static char *boolean_fmt(msg_aux_data_t d)
+ * {
+ * // This is used for debugging and dumping messages.
+ * if (d.u64)
+ * return tor_strdup("true");
+ * else
+ * return tor_strdup("false");
+ * }
+ *
+ * static void boolean_free(msg_aux_data_t d)
+ * {
+ * // We don't actually need to do anything to free a boolean.
+ * // We could use "NULL" instead of this function, but I'm including
+ * // it as an example.
+ * }
+ *
+ * static void bind_to_dispatcher(pubsub_connector_t *con)
+ * {
+ * dispatch_typefns_t boolean_fns = {
+ * .fmt_fn = boolean_fmt,
+ * .free_fn = boolean_free,
+ * };
+ * DISPATCH_REGISTER_TYPE(con, boolean, &boolean_fns);
+ * }
+ *
+ *
+ *
+ * So, how does this all work? (You can stop reading here, unless you're
+ * debugging something.)
+ *
+ * When you declare a message in a header with DECLARE_MESSAGE() or
+ * DECLARE_MESSAGE_INT(), it creates five things:
+ *
+ * * two typedefs for the message argument (constant and non-constant
+ * variants).
+ * * a constant string to hold the declared message type name
+ * * two inline functions, to coerce the message argument type to and from
+ * a "msg_aux_data_t" union.
+ *
+ * All of these declarations have names based on the message name.
+ *
+ * Later, when you say DECLARE_PUBLISH() or DECLARE_SUBSCRIBE(), we use the
+ * elements defined by DECLARE_MESSAGE() to make sure that the publish
+ * function takes the correct argument type, and that the subscription hook is
+ * declared with the right argument type.
+ **/
+
+#ifndef TOR_DISPATCH_MSG_H
+#define TOR_DISPATCH_MSG_H
+
+#include "lib/cc/compat_compiler.h"
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/pubsub/pub_binding_st.h"
+#include "lib/pubsub/pubsub_connect.h"
+#include "lib/pubsub/pubsub_flags.h"
+#include "lib/pubsub/pubsub_publish.h"
+
+/* Implemenation notes:
+ *
+ * For a messagename "foo", the DECLARE_MESSAGE*() macros must declare:
+ *
+ * msg_arg_type__foo -- a typedef for the argument type of the foo message.
+ * msg_arg_consttype__foo -- a typedef for the const argument type of the
+ * foo message.
+ * msg_arg_name__foo[] -- a static string constant holding the unique
+ * identifier for the type of the foo message.
+ * msg_arg_get__foo() -- an inline function taking a msg_aux_data_t and
+ * returning the C data type.
+ * msg_arg_set__foo() -- an inline function taking a msg_aux_data_t and
+ * the C type, setting the msg_aux_data_t to hold the C type.
+ *
+ * For a messagename "foo", the DECLARE_PUBLISH() macro must declare:
+ *
+ * pub_binding__foo -- A static pub_binding_t object used to send messages
+ * from this module.
+ * publish_fn__foo -- A function taking an argument of the appropriate
+ * C type, to be invoked by PUBLISH().
+ *
+ * For a messagename "foo", the DECLARE_SUBSCRIBE() macro must declare:
+ *
+ * hookfn -- A user-provided function name, with the correct signature.
+ * recv_fn__foo -- A wrapper callback that takes a msg_t *, and calls
+ * hookfn with the appropriate arguments.
+ */
+
+/* Macro to declare common elements shared by DECLARE_MESSAGE and
+ * DECLARE_MESSAGE_INT. Don't call this directly.
+ *
+ * Note that the "msg_arg_name" string constant is defined in each
+ * translation unit. This might be undesirable; we can tweak it in the
+ * future if need be.
+ */
+#define DECLARE_MESSAGE_COMMON__(messagename, typename, c_type) \
+ typedef c_type msg_arg_type__ ##messagename; \
+ typedef const c_type msg_arg_consttype__ ##messagename; \
+ ATTR_UNUSED static const char msg_arg_name__ ##messagename[] = # typename;
+
+/**
+ * Use this macro in a header to declare the existence of a given message,
+ * taking a pointer as auxiliary data.
+ *
+ * "messagename" is a unique identifier for the message; it must be a valid
+ * C identifier.
+ *
+ * "typename" is a unique identifier for the type of the auxiliary data.
+ * It needs to be defined somewhere in Tor, using
+ * "DISPATCH_REGISTER_TYPE."
+ *
+ * "c_ptr_type" is a C pointer type (like "char *" or "struct foo *").
+ * The "*" needs to be included.
+ */
+#define DECLARE_MESSAGE(messagename, typename, c_ptr_type) \
+ DECLARE_MESSAGE_COMMON__(messagename, typename, c_ptr_type) \
+ ATTR_UNUSED static inline c_ptr_type \
+ msg_arg_get__ ##messagename(msg_aux_data_t m) \
+ { \
+ return m.ptr; \
+ } \
+ ATTR_UNUSED static inline void \
+ msg_arg_set__ ##messagename(msg_aux_data_t *m, c_ptr_type v) \
+ { \
+ m->ptr = v; \
+ } \
+ EAT_SEMICOLON
+
+/**
+ * Use this macro in a header to declare the existence of a given message,
+ * taking an integer as auxiliary data.
+ *
+ * "messagename" is a unique identifier for the message; it must be a valid
+ * C identifier.
+ *
+ * "typename" is a unique identifier for the type of the auxiliary data. It
+ * needs to be defined somewhere in Tor, using "DISPATCH_REGISTER_TYPE."
+ *
+ * "c_type" is a C integer type, like "int" or "bool". It needs to fit inside
+ * a uint64_t.
+ */
+#define DECLARE_MESSAGE_INT(messagename, typename, c_type) \
+ DECLARE_MESSAGE_COMMON__(messagename, typename, c_type) \
+ ATTR_UNUSED static inline c_type \
+ msg_arg_get__ ##messagename(msg_aux_data_t m) \
+ { \
+ return (c_type)m.u64; \
+ } \
+ ATTR_UNUSED static inline void \
+ msg_arg_set__ ##messagename(msg_aux_data_t *m, c_type v) \
+ { \
+ m->u64 = (uint64_t)v; \
+ } \
+ EAT_SEMICOLON
+
+/**
+ * Use this macro inside a C module declare that we'll be publishing a given
+ * message type from within this module.
+ *
+ * It creates necessary functions and wrappers to publish a message whose
+ * unique identifier is "messagename".
+ *
+ * Before you use this, you need to include the header where DECLARE_MESSAGE*()
+ * was used for this message.
+ *
+ * You can only use this once per message in each subsystem.
+ */
+#define DECLARE_PUBLISH(messagename) \
+ static pub_binding_t pub_binding__ ##messagename; \
+ static void \
+ publish_fn__ ##messagename(msg_arg_type__ ##messagename arg) \
+ { \
+ msg_aux_data_t data; \
+ msg_arg_set__ ##messagename(&data, arg); \
+ pubsub_pub_(&pub_binding__ ##messagename, data); \
+ } \
+ EAT_SEMICOLON
+
+/**
+ * Use this macro inside a C file to declare that we're subscribing to a
+ * given message and associating it with a given "hook function". It
+ * declares the hook function static, and helps with strong typing.
+ *
+ * Before you use this, you need to include the header where
+ * DECLARE_MESSAGE*() was used for the message whose unique identifier is
+ * "messagename".
+ *
+ * You will need to define a function with the name that you provide for
+ * "hookfn". The type of this function will be:
+ * static void hookfn(const msg_t *, const c_type)
+ * where c_type is the c type that you declared in the header.
+ *
+ * You can only use this once per message in each subsystem.
+ */
+#define DECLARE_SUBSCRIBE(messagename, hookfn) \
+ static void hookfn(const msg_t *, \
+ const msg_arg_consttype__ ##messagename); \
+ static void recv_fn__ ## messagename(const msg_t *m) \
+ { \
+ msg_arg_type__ ## messagename arg; \
+ arg = msg_arg_get__ ##messagename(m->aux_data__); \
+ hookfn(m, arg); \
+ } \
+ EAT_SEMICOLON
+
+/**
+ * Add a fake use of the publish function for 'messagename', so that
+ * the compiler does not call it unused.
+ */
+#define DISPATCH__FAKE_USE_OF_PUBFN_(messagename) \
+ ( 0 ? (publish_fn__ ##messagename((msg_arg_type__##messagename)0), 1) \
+ : 1)
+
+/*
+ * This macro is for internal use. It backs DISPATCH_ADD_PUB*()
+ */
+#define DISPATCH_ADD_PUB_(connector, channel, messagename, flags) \
+ ( \
+ DISPATCH__FAKE_USE_OF_PUBFN_(messagename), \
+ pubsub_add_pub_((connector), \
+ &pub_binding__ ##messagename, \
+ get_channel_id(# channel), \
+ get_message_id(# messagename), \
+ get_msg_type_id(msg_arg_name__ ## messagename), \
+ (flags), \
+ __FILE__, \
+ __LINE__) \
+ )
+
+/**
+ * Use a given connector and channel name to declare that this subsystem will
+ * publish a given message type.
+ *
+ * Call this macro from within the add_subscriptions() function of a module.
+ */
+#define DISPATCH_ADD_PUB(connector, channel, messagename) \
+ DISPATCH_ADD_PUB_(connector, channel, messagename, 0)
+
+/**
+ * Use a given connector and channel name to declare that this subsystem will
+ * publish a given message type, and that no other subsystem is allowed to.
+ *
+ * Call this macro from within the add_subscriptions() function of a module.
+ */
+#define DISPATCH_ADD_PUB_EXCL(connector, channel, messagename) \
+ DISPATCH_ADD_PUB_(connector, channel, messagename, DISP_FLAG_EXCL)
+
+/*
+ * This macro is for internal use. It backs DISPATCH_ADD_SUB*()
+ */
+#define DISPATCH_ADD_SUB_(connector, channel, messagename, flags) \
+ pubsub_add_sub_((connector), \
+ recv_fn__ ##messagename, \
+ get_channel_id(#channel), \
+ get_message_id(# messagename), \
+ get_msg_type_id(msg_arg_name__ ##messagename), \
+ (flags), \
+ __FILE__, \
+ __LINE__)
+/*
+ * Use a given connector and channel name to declare that this subsystem will
+ * receive a given message type.
+ *
+ * Call this macro from within the add_subscriptions() function of a module.
+ */
+#define DISPATCH_ADD_SUB(connector, channel, messagename) \
+ DISPATCH_ADD_SUB_(connector, channel, messagename, 0)
+/**
+ * Use a given connector and channel name to declare that this subsystem will
+ * receive a given message type, and that no other subsystem is allowed to do
+ * so.
+ *
+ * Call this macro from within the add_subscriptions() function of a module.
+ */
+#define DISPATCH_ADD_SUB_EXCL(connector, channel, messagename) \
+ DISPATCH_ADD_SUB_(connector, channel, messagename, DISP_FLAG_EXCL)
+
+/**
+ * Publish a given message with a given argument. (Takes ownership of the
+ * argument if it is a pointer.)
+ */
+#define PUBLISH(messagename, arg) \
+ publish_fn__ ##messagename(arg)
+
+/**
+ * Use a given connector to declare that the functions to be used to manipuate
+ * a certain C type.
+ **/
+#define DISPATCH_REGISTER_TYPE(con, type, fns) \
+ pubsub_connector_register_type_((con), \
+ get_msg_type_id(#type), \
+ (fns), \
+ __FILE__, \
+ __LINE__)
+
+#endif /* !defined(TOR_DISPATCH_MSG_H) */
diff --git a/src/lib/pubsub/pubsub_publish.c b/src/lib/pubsub/pubsub_publish.c
new file mode 100644
index 0000000000..454a335a78
--- /dev/null
+++ b/src/lib/pubsub/pubsub_publish.c
@@ -0,0 +1,72 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file pubsub_publish.c
+ * @brief Header for functions to publish using a pub_binding_t.
+ **/
+
+#define PUBSUB_PRIVATE
+#define DISPATCH_PRIVATE
+#include "orconfig.h"
+
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_st.h"
+
+#include "lib/pubsub/pub_binding_st.h"
+#include "lib/pubsub/pubsub_publish.h"
+
+#include "lib/malloc/malloc.h"
+#include "lib/log/util_bug.h"
+
+#include <string.h>
+
+/**
+ * Publish a message from the publication binding <b>pub</b> using the
+ * auxiliary data <b>auxdata</b>.
+ *
+ * Return 0 on success, -1 on failure.
+ **/
+int
+pubsub_pub_(const pub_binding_t *pub, msg_aux_data_t auxdata)
+{
+ dispatch_t *d = pub->dispatch_ptr;
+ if (BUG(! d)) {
+ /* Tried to publish a message before the dispatcher was configured. */
+ /* (Without a dispatcher, we don't know how to free auxdata.) */
+ return -1;
+ }
+
+ if (BUG(pub->msg_template.type >= d->n_types)) {
+ /* The type associated with this message is not known to the dispatcher. */
+ /* (Without a correct type, we don't know how to free auxdata.) */
+ return -1;
+ }
+
+ if (BUG(pub->msg_template.msg >= d->n_msgs) ||
+ BUG(pub->msg_template.channel >= d->n_queues)) {
+ /* The message ID or channel ID was out of bounds. */
+ // LCOV_EXCL_START
+ d->typefns[pub->msg_template.type].free_fn(auxdata);
+ return -1;
+ // LCOV_EXCL_STOP
+ }
+
+ if (! d->table[pub->msg_template.msg]) {
+ /* Fast path: nobody wants this data. */
+
+ // XXXX Faster path: we could store this in the pub_binding_t.
+ d->typefns[pub->msg_template.type].free_fn(auxdata);
+ return 0;
+ }
+
+ /* Construct the message object */
+ msg_t *m = tor_malloc(sizeof(msg_t));
+ memcpy(m, &pub->msg_template, sizeof(msg_t));
+ m->aux_data__ = auxdata;
+
+ return dispatch_send_msg_unchecked(d, m);
+}
diff --git a/src/lib/pubsub/pubsub_publish.h b/src/lib/pubsub/pubsub_publish.h
new file mode 100644
index 0000000000..0686a465de
--- /dev/null
+++ b/src/lib/pubsub/pubsub_publish.h
@@ -0,0 +1,15 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PUBSUB_PUBLISH_H
+#define TOR_PUBSUB_PUBLISH_H
+
+#include "lib/dispatch/msgtypes.h"
+struct pub_binding_t;
+
+int pubsub_pub_(const struct pub_binding_t *pub, msg_aux_data_t auxdata);
+
+#endif /* !defined(TOR_PUBSUB_PUBLISH_H) */
diff --git a/src/lib/sandbox/include.am b/src/lib/sandbox/include.am
index adfda6bde5..e81f14b55f 100644
--- a/src/lib/sandbox/include.am
+++ b/src/lib/sandbox/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-sandbox-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_sandbox_a_SOURCES = \
src/lib/sandbox/sandbox.c
@@ -13,6 +14,7 @@ src_lib_libtor_sandbox_testing_a_SOURCES = \
src_lib_libtor_sandbox_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_sandbox_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/sandbox/linux_syscalls.inc \
src/lib/sandbox/sandbox.h
diff --git a/src/lib/smartlist_core/include.am b/src/lib/smartlist_core/include.am
index 99d65f0b23..548179bc4f 100644
--- a/src/lib/smartlist_core/include.am
+++ b/src/lib/smartlist_core/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-smartlist-core-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_smartlist_core_a_SOURCES = \
src/lib/smartlist_core/smartlist_core.c \
src/lib/smartlist_core/smartlist_split.c
@@ -15,6 +16,7 @@ src_lib_libtor_smartlist_core_testing_a_CPPFLAGS = \
$(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_smartlist_core_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/smartlist_core/smartlist_core.h \
src/lib/smartlist_core/smartlist_foreach.h \
diff --git a/src/lib/smartlist_core/smartlist_core.c b/src/lib/smartlist_core/smartlist_core.c
index ac85a6cc84..6b0a305a93 100644
--- a/src/lib/smartlist_core/smartlist_core.c
+++ b/src/lib/smartlist_core/smartlist_core.c
@@ -88,6 +88,30 @@ smartlist_ensure_capacity(smartlist_t *sl, size_t size)
#undef MAX_CAPACITY
}
+/** Expand <b>sl</b> so that its length is at least <b>new_size</b>,
+ * filling in previously unused entries with NULL>
+ *
+ * Do nothing if <b>sl</b> already had at least <b>new_size</b> elements.
+ */
+void
+smartlist_grow(smartlist_t *sl, size_t new_size)
+{
+ smartlist_ensure_capacity(sl, new_size);
+
+ if (new_size > (size_t)sl->num_used) {
+ /* This memset() should be a no-op: everything else in the smartlist code
+ * tries to make sure that unused entries are always NULL. Still, that is
+ * meant as a safety mechanism, so let's clear the memory here.
+ */
+ memset(sl->list + sl->num_used, 0,
+ sizeof(void *) * (new_size - sl->num_used));
+
+ /* This cast is safe, since we already asserted that we were below
+ * MAX_CAPACITY in smartlist_ensure_capacity(). */
+ sl->num_used = (int)new_size;
+ }
+}
+
/** Append element to the end of the list. */
void
smartlist_add(smartlist_t *sl, void *element)
@@ -153,6 +177,8 @@ smartlist_remove_keeporder(smartlist_t *sl, const void *element)
sl->list[i++] = sl->list[j];
}
}
+ memset(sl->list + sl->num_used, 0,
+ sizeof(void *) * (num_used_orig - sl->num_used));
}
/** If <b>sl</b> is nonempty, remove and return the final element. Otherwise,
diff --git a/src/lib/smartlist_core/smartlist_core.h b/src/lib/smartlist_core/smartlist_core.h
index a7fbaa099b..795741c447 100644
--- a/src/lib/smartlist_core/smartlist_core.h
+++ b/src/lib/smartlist_core/smartlist_core.h
@@ -43,6 +43,7 @@ void smartlist_clear(smartlist_t *sl);
void smartlist_add(smartlist_t *sl, void *element);
void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2);
void smartlist_add_strdup(struct smartlist_t *sl, const char *string);
+void smartlist_grow(smartlist_t *sl, size_t new_size);
void smartlist_remove(smartlist_t *sl, const void *element);
void smartlist_remove_keeporder(smartlist_t *sl, const void *element);
@@ -97,4 +98,4 @@ void smartlist_del(smartlist_t *sl, int idx);
void smartlist_del_keeporder(smartlist_t *sl, int idx);
void smartlist_insert(smartlist_t *sl, int idx, void *val);
-#endif /* !defined(TOR_CONTAINER_H) */
+#endif /* !defined(TOR_SMARTLIST_CORE_H) */
diff --git a/src/lib/smartlist_core/smartlist_split.h b/src/lib/smartlist_core/smartlist_split.h
index 4f72376125..e2cc7245b7 100644
--- a/src/lib/smartlist_core/smartlist_split.h
+++ b/src/lib/smartlist_core/smartlist_split.h
@@ -17,4 +17,4 @@
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep,
int flags, int max);
-#endif
+#endif /* !defined(TOR_SMARTLIST_SPLIT_H) */
diff --git a/src/lib/string/compat_string.h b/src/lib/string/compat_string.h
index a0e37bb6dc..4f30bf5392 100644
--- a/src/lib/string/compat_string.h
+++ b/src/lib/string/compat_string.h
@@ -25,14 +25,14 @@ static inline int strncasecmp(const char *a, const char *b, size_t n);
static inline int strncasecmp(const char *a, const char *b, size_t n) {
return _strnicmp(a,b,n);
}
-#endif
+#endif /* !defined(HAVE_STRNCASECMP) */
#ifndef HAVE_STRCASECMP
static inline int strcasecmp(const char *a, const char *b);
static inline int strcasecmp(const char *a, const char *b) {
return _stricmp(a,b);
}
-#endif
-#endif
+#endif /* !defined(HAVE_STRCASECMP) */
+#endif /* defined(_WIN32) */
#if defined __APPLE__
/* On OSX 10.9 and later, the overlap-checking code for strlcat would
@@ -59,4 +59,4 @@ char *tor_strtok_r_impl(char *str, const char *sep, char **lasts);
#define tor_strtok_r(str, sep, lasts) tor_strtok_r_impl(str, sep, lasts)
#endif
-#endif
+#endif /* !defined(TOR_COMPAT_STRING_H) */
diff --git a/src/lib/string/include.am b/src/lib/string/include.am
index edd74b8a3e..82d35cc5af 100644
--- a/src/lib/string/include.am
+++ b/src/lib/string/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-string-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_string_a_SOURCES = \
src/lib/string/compat_ctype.c \
src/lib/string/compat_string.c \
@@ -18,6 +19,7 @@ src_lib_libtor_string_testing_a_SOURCES = \
src_lib_libtor_string_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_string_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/string/compat_ctype.h \
src/lib/string/compat_string.h \
diff --git a/src/lib/string/parse_int.h b/src/lib/string/parse_int.h
index 925547942e..50d48b44c5 100644
--- a/src/lib/string/parse_int.h
+++ b/src/lib/string/parse_int.h
@@ -22,4 +22,4 @@ double tor_parse_double(const char *s, double min, double max, int *ok,
uint64_t tor_parse_uint64(const char *s, int base, uint64_t min,
uint64_t max, int *ok, char **next);
-#endif
+#endif /* !defined(TOR_PARSE_INT_H) */
diff --git a/src/lib/string/printf.c b/src/lib/string/printf.c
index a5cb71ce09..26203932e4 100644
--- a/src/lib/string/printf.c
+++ b/src/lib/string/printf.c
@@ -117,8 +117,8 @@ tor_vasprintf(char **strp, const char *fmt, va_list args)
*strp = NULL;
return -1;
}
- strp_tmp = tor_malloc(len + 1);
- r = _vsnprintf(strp_tmp, len+1, fmt, args);
+ strp_tmp = tor_malloc((size_t)len + 1);
+ r = _vsnprintf(strp_tmp, (size_t)len+1, fmt, args);
if (r != len) {
tor_free(strp_tmp);
*strp = NULL;
@@ -153,9 +153,9 @@ tor_vasprintf(char **strp, const char *fmt, va_list args)
*strp = tor_strdup(buf);
return len;
}
- strp_tmp = tor_malloc(len+1);
+ strp_tmp = tor_malloc((size_t)len+1);
/* use of tor_vsnprintf() will ensure string is null terminated */
- r = tor_vsnprintf(strp_tmp, len+1, fmt, args);
+ r = tor_vsnprintf(strp_tmp, (size_t)len+1, fmt, args);
if (r != len) {
tor_free(strp_tmp);
*strp = NULL;
diff --git a/src/lib/string/printf.h b/src/lib/string/printf.h
index 2cc13d6bee..6e90770f81 100644
--- a/src/lib/string/printf.h
+++ b/src/lib/string/printf.h
@@ -27,4 +27,4 @@ int tor_asprintf(char **strp, const char *fmt, ...)
int tor_vasprintf(char **strp, const char *fmt, va_list args)
CHECK_PRINTF(2,0);
-#endif /* !defined(TOR_UTIL_STRING_H) */
+#endif /* !defined(TOR_UTIL_PRINTF_H) */
diff --git a/src/lib/string/scanf.h b/src/lib/string/scanf.h
index 6673173de5..b642e242db 100644
--- a/src/lib/string/scanf.h
+++ b/src/lib/string/scanf.h
@@ -21,4 +21,4 @@ int tor_vsscanf(const char *buf, const char *pattern, va_list ap) \
int tor_sscanf(const char *buf, const char *pattern, ...)
CHECK_SCANF(2, 3);
-#endif /* !defined(TOR_UTIL_STRING_H) */
+#endif /* !defined(TOR_UTIL_SCANF_H) */
diff --git a/src/lib/string/util_string.c b/src/lib/string/util_string.c
index 0c4e399008..f5061a11d2 100644
--- a/src/lib/string/util_string.c
+++ b/src/lib/string/util_string.c
@@ -71,7 +71,7 @@ tor_memstr(const void *haystack, size_t hlen, const char *needle)
/** Return true iff the 'len' bytes at 'mem' are all zero. */
int
-tor_mem_is_zero(const char *mem, size_t len)
+fast_mem_is_zero(const char *mem, size_t len)
{
static const char ZERO[] = {
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
@@ -95,17 +95,14 @@ tor_mem_is_zero(const char *mem, size_t len)
int
tor_digest_is_zero(const char *digest)
{
- static const uint8_t ZERO_DIGEST[] = {
- 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
- };
- return tor_memeq(digest, ZERO_DIGEST, DIGEST_LEN);
+ return safe_mem_is_zero(digest, DIGEST_LEN);
}
/** Return true iff the DIGEST256_LEN bytes in digest are all zero. */
int
tor_digest256_is_zero(const char *digest)
{
- return tor_mem_is_zero(digest, DIGEST256_LEN);
+ return safe_mem_is_zero(digest, DIGEST256_LEN);
}
/** Remove from the string <b>s</b> every character which appears in
diff --git a/src/lib/string/util_string.h b/src/lib/string/util_string.h
index da4fab159c..b3c6841d41 100644
--- a/src/lib/string/util_string.h
+++ b/src/lib/string/util_string.h
@@ -20,7 +20,10 @@ const void *tor_memmem(const void *haystack, size_t hlen, const void *needle,
size_t nlen);
const void *tor_memstr(const void *haystack, size_t hlen,
const char *needle);
-int tor_mem_is_zero(const char *mem, size_t len);
+int fast_mem_is_zero(const char *mem, size_t len);
+#define fast_digest_is_zero(d) fast_mem_is_zero((d), DIGEST_LEN)
+#define fast_digetst256_is_zero(d) fast_mem_is_zero((d), DIGEST256_LEN)
+
int tor_digest_is_zero(const char *digest);
int tor_digest256_is_zero(const char *digest);
diff --git a/src/lib/subsys/include.am b/src/lib/subsys/include.am
index 4741126b14..c9ab54ca73 100644
--- a/src/lib/subsys/include.am
+++ b/src/lib/subsys/include.am
@@ -1,3 +1,4 @@
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/subsys/subsys.h
diff --git a/src/lib/subsys/subsys.h b/src/lib/subsys/subsys.h
index 241ad7829c..21f984f32d 100644
--- a/src/lib/subsys/subsys.h
+++ b/src/lib/subsys/subsys.h
@@ -8,7 +8,7 @@
#include <stdbool.h>
-struct dispatch_connector_t;
+struct pubsub_connector_t;
/**
* A subsystem is a part of Tor that is initialized, shut down, configured,
@@ -58,7 +58,7 @@ typedef struct subsys_fns_t {
/**
* Connect a subsystem to the message dispatch system.
**/
- int (*add_pubsub)(struct dispatch_connector_t *);
+ int (*add_pubsub)(struct pubsub_connector_t *);
/**
* Perform any necessary pre-fork cleanup. This function may not fail.
@@ -92,4 +92,4 @@ typedef struct subsys_fns_t {
* less than this value. */
#define SUBSYS_LEVEL_LIBS -10
-#endif
+#endif /* !defined(TOR_SUBSYS_T) */
diff --git a/src/lib/term/getpass.h b/src/lib/term/getpass.h
index a9c146ea8f..aa597ec423 100644
--- a/src/lib/term/getpass.h
+++ b/src/lib/term/getpass.h
@@ -15,4 +15,4 @@
ssize_t tor_getpass(const char *prompt, char *output, size_t buflen);
-#endif
+#endif /* !defined(TOR_GETPASS_H) */
diff --git a/src/lib/term/include.am b/src/lib/term/include.am
index 55fe548ebc..a120bba0cb 100644
--- a/src/lib/term/include.am
+++ b/src/lib/term/include.am
@@ -11,6 +11,7 @@ else
readpassphrase_source=
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_term_a_SOURCES = \
src/lib/term/getpass.c \
$(readpassphrase_source)
@@ -20,5 +21,6 @@ src_lib_libtor_term_testing_a_SOURCES = \
src_lib_libtor_term_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_term_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/term/getpass.h
diff --git a/src/lib/testsupport/include.am b/src/lib/testsupport/include.am
index b2aa620985..a5ed46eb67 100644
--- a/src/lib/testsupport/include.am
+++ b/src/lib/testsupport/include.am
@@ -1,3 +1,4 @@
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/testsupport/testsupport.h
diff --git a/src/lib/testsupport/testsupport.h b/src/lib/testsupport/testsupport.h
index 9363a9ba66..631ec0228c 100644
--- a/src/lib/testsupport/testsupport.h
+++ b/src/lib/testsupport/testsupport.h
@@ -21,7 +21,7 @@
* tests. */
#define STATIC
#define EXTERN(type, name) extern type name;
-#else
+#else /* !(defined(TOR_UNIT_TESTS)) */
#define STATIC static
#define EXTERN(type, name)
#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/lib/thread/include.am b/src/lib/thread/include.am
index 695795a2c8..cd8016b5df 100644
--- a/src/lib/thread/include.am
+++ b/src/lib/thread/include.am
@@ -12,6 +12,7 @@ if THREADS_WIN32
threads_impl_source=src/lib/thread/compat_winthreads.c
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_thread_a_SOURCES = \
src/lib/thread/compat_threads.c \
src/lib/thread/numcpus.c \
@@ -22,6 +23,7 @@ src_lib_libtor_thread_testing_a_SOURCES = \
src_lib_libtor_thread_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_thread_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/thread/numcpus.h \
src/lib/thread/thread_sys.h \
diff --git a/src/lib/thread/numcpus.h b/src/lib/thread/numcpus.h
index 3f0a29ce7c..2f1ea16eb9 100644
--- a/src/lib/thread/numcpus.h
+++ b/src/lib/thread/numcpus.h
@@ -13,4 +13,4 @@
int compute_num_cpus(void);
-#endif
+#endif /* !defined(TOR_NUMCPUS_H) */
diff --git a/src/lib/time/compat_time.c b/src/lib/time/compat_time.c
index a047d6c2cd..3f41500f3a 100644
--- a/src/lib/time/compat_time.c
+++ b/src/lib/time/compat_time.c
@@ -164,6 +164,8 @@ static int64_t last_tick_count = 0;
* to be monotonic; increments them as appropriate so that they actually
* _are_ monotonic.
*
+ * The returned time may be the same as the previous returned time.
+ *
* Caller must hold lock. */
STATIC int64_t
ratchet_performance_counter(int64_t count_raw)
@@ -202,6 +204,8 @@ static struct timeval timeofday_offset = { 0, 0 };
* supposed to be monotonic; increments them as appropriate so that they
* actually _are_ monotonic.
*
+ * The returned time may be the same as the previous returned time.
+ *
* Caller must hold lock. */
STATIC void
ratchet_timeval(const struct timeval *timeval_raw, struct timeval *out)
@@ -270,7 +274,9 @@ monotime_init_internal(void)
}
/**
- * Set "out" to the most recent monotonic time value
+ * Set "out" to the most recent monotonic time value.
+ *
+ * The returned time may be the same as the previous returned time.
*/
void
monotime_get(monotime_t *out)
@@ -298,10 +304,12 @@ monotime_coarse_get(monotime_coarse_t *out)
#endif /* defined(TOR_UNIT_TESTS) */
out->abstime_ = mach_approximate_time();
}
-#endif
+#endif /* defined(HAVE_MACH_APPROXIMATE_TIME) */
/**
* Return the number of nanoseconds between <b>start</b> and <b>end</b>.
+ *
+ * The returned value may be equal to zero.
*/
int64_t
monotime_diff_nsec(const monotime_t *start,
@@ -759,7 +767,7 @@ monotime_coarse_zero(monotime_coarse_t *out)
{
memset(out, 0, sizeof(*out));
}
-#endif
+#endif /* defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */
int64_t
monotime_diff_usec(const monotime_t *start,
@@ -825,7 +833,7 @@ monotime_coarse_absolute_msec(void)
{
return monotime_coarse_absolute_nsec() / ONE_MILLION;
}
-#else
+#else /* !(defined(MONOTIME_COARSE_FN_IS_DIFFERENT)) */
#define initialized_at_coarse initialized_at
#endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */
@@ -857,7 +865,7 @@ monotime_msec_to_approx_coarse_stamp_units(uint64_t msec)
mach_time_info.numer;
return abstime_val >> monotime_shift;
}
-#else
+#else /* !(defined(__APPLE__)) */
uint64_t
monotime_coarse_stamp_units_to_approx_msec(uint64_t units)
{
@@ -868,4 +876,4 @@ monotime_msec_to_approx_coarse_stamp_units(uint64_t msec)
{
return (msec * STAMP_TICKS_PER_SECOND) / 1000;
}
-#endif
+#endif /* defined(__APPLE__) */
diff --git a/src/lib/time/compat_time.h b/src/lib/time/compat_time.h
index 2cd4b3bee3..8c7661d7cb 100644
--- a/src/lib/time/compat_time.h
+++ b/src/lib/time/compat_time.h
@@ -15,11 +15,29 @@
* of tens of milliseconds.
*/
-/* Q: Should you use monotime or monotime_coarse as your source?
+/* Q: When should I use monotonic time?
+ *
+ * A: If you need a time that never decreases, use monotonic time. If you need
+ * to send a time to a user or another process, or store a time, use the
+ * wall-clock time.
+ *
+ * Q: Should you use monotime or monotime_coarse as your source?
*
* A: Generally, you get better precision with monotime, but better
* performance with monotime_coarse.
*
+ * Q: What is a "monotonic" time, exactly?
+ *
+ * A: Monotonic times are strictly non-decreasing. The difference between any
+ * previous monotonic time, and the current monotonic time, is always greater
+ * than *or equal to* zero.
+ * Zero deltas happen more often:
+ * - on Windows (due to an OS bug),
+ * - when using monotime_coarse, or on systems with low-resolution timers,
+ * - on platforms where we emulate monotonic time using wall-clock time, and
+ * - when using time units that are larger than nanoseconds (due to
+ * truncation on division).
+ *
* Q: Should you use monotime_t or monotime_coarse_t directly? Should you use
* usec? msec? "stamp units?"
*
@@ -95,7 +113,7 @@
* All, "timestamp units": Cheap everywhere: it never divides.
*
* Q: This is only somewhat related, but how much precision could I hope for
- * from a libevent time.?
+ * from a libevent time?
*
* A: Actually, it's _very_ related if you're timing in order to have a
* timeout happen.
@@ -182,26 +200,36 @@ void monotime_init(void);
void monotime_get(monotime_t *out);
/**
* Return the number of nanoseconds between <b>start</b> and <b>end</b>.
+ * The returned value may be equal to zero.
*/
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>.
+ * The returned value may be equal to zero.
+ * Fractional units are truncated, not rounded.
*/
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>.
+ * The returned value may be equal to zero.
+ * Fractional units are truncated, not rounded.
*/
int64_t monotime_diff_msec(const monotime_t *start, const monotime_t *end);
/**
* Return the number of nanoseconds since the timer system was initialized.
+ * The returned value may be equal to zero.
*/
uint64_t monotime_absolute_nsec(void);
/**
* Return the number of microseconds since the timer system was initialized.
+ * The returned value may be equal to zero.
+ * Fractional units are truncated, not rounded.
*/
MOCK_DECL(uint64_t, monotime_absolute_usec,(void));
/**
* Return the number of milliseconds since the timer system was initialized.
+ * The returned value may be equal to zero.
+ * Fractional units are truncated, not rounded.
*/
uint64_t monotime_absolute_msec(void);
@@ -225,6 +253,9 @@ void monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec);
* Set <b>out</b> to the current coarse time.
*/
void monotime_coarse_get(monotime_coarse_t *out);
+/**
+ * Like monotime_absolute_*(), but faster on some platforms.
+ */
uint64_t monotime_coarse_absolute_nsec(void);
uint64_t monotime_coarse_absolute_usec(void);
uint64_t monotime_coarse_absolute_msec(void);
@@ -248,18 +279,27 @@ uint32_t monotime_coarse_to_stamp(const monotime_coarse_t *t);
/**
* Convert a difference, expressed in the units of monotime_coarse_to_stamp,
* into an approximate number of milliseconds.
+ *
+ * The returned value may be equal to zero.
+ * Fractional units are truncated, not rounded.
*/
uint64_t monotime_coarse_stamp_units_to_approx_msec(uint64_t units);
uint64_t monotime_msec_to_approx_coarse_stamp_units(uint64_t msec);
uint32_t monotime_coarse_get_stamp(void);
#if defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)
+/**
+ * Like monotime_diff_*(), but faster on some platforms.
+ */
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);
+/**
+ * Like monotime_*(), but faster on some platforms.
+ */
void monotime_coarse_zero(monotime_coarse_t *out);
int monotime_coarse_is_zero(const monotime_coarse_t *val);
void monotime_coarse_add_msec(monotime_coarse_t *out,
@@ -278,6 +318,9 @@ void monotime_coarse_add_msec(monotime_coarse_t *out,
*
* Requires that the difference fit into an int32_t; not for use with
* large time differences.
+ *
+ * The returned value may be equal to zero.
+ * Fractional units are truncated, not rounded.
*/
int32_t monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
const monotime_coarse_t *end);
@@ -287,6 +330,9 @@ int32_t monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
*
* Requires that the difference fit into an int32_t; not for use with
* large time differences.
+ *
+ * The returned value may be equal to zero.
+ * Fractional units are truncated, not rounded.
*/
static inline int32_t
monotime_coarse_diff_msec32(const monotime_coarse_t *start,
@@ -298,7 +344,7 @@ monotime_coarse_diff_msec32(const monotime_coarse_t *start,
#else
#define USING_32BIT_MSEC_HACK
return monotime_coarse_diff_msec32_(start, end);
-#endif
+#endif /* SIZEOF_VOID_P == 8 */
}
#ifdef TOR_UNIT_TESTS
diff --git a/src/lib/time/include.am b/src/lib/time/include.am
index dae16f49ac..dcb199b142 100644
--- a/src/lib/time/include.am
+++ b/src/lib/time/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-time-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_time_a_SOURCES = \
src/lib/time/compat_time.c \
src/lib/time/time_sys.c \
@@ -15,6 +16,7 @@ src_lib_libtor_time_testing_a_SOURCES = \
src_lib_libtor_time_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_time_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/time/compat_time.h \
src/lib/time/time_sys.h \
diff --git a/src/lib/time/tvdiff.h b/src/lib/time/tvdiff.h
index 724af1528a..657cb99553 100644
--- a/src/lib/time/tvdiff.h
+++ b/src/lib/time/tvdiff.h
@@ -20,4 +20,4 @@ int64_t tv_to_msec(const struct timeval *tv);
time_t time_diff(const time_t from, const time_t to);
-#endif
+#endif /* !defined(TOR_TVDIFF_H) */
diff --git a/src/lib/tls/include.am b/src/lib/tls/include.am
index 1817739eef..7e05ef4f8c 100644
--- a/src/lib/tls/include.am
+++ b/src/lib/tls/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-tls-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_tls_a_SOURCES = \
src/lib/tls/buffers_tls.c \
src/lib/tls/tortls.c \
@@ -29,6 +30,7 @@ src_lib_libtor_tls_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_tls_testing_a_CFLAGS = \
$(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/tls/ciphers.inc \
src/lib/tls/buffers_tls.h \
diff --git a/src/lib/tls/nss_countbytes.h b/src/lib/tls/nss_countbytes.h
index 8b31603923..47f220c4c1 100644
--- a/src/lib/tls/nss_countbytes.h
+++ b/src/lib/tls/nss_countbytes.h
@@ -22,4 +22,4 @@ int tor_get_prfiledesc_byte_counts(struct PRFileDesc *fd,
uint64_t *n_read_out,
uint64_t *n_written_out);
-#endif
+#endif /* !defined(TOR_NSS_COUNTBYTES_H) */
diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h
index 8efc7a1c98..9e195c6af2 100644
--- a/src/lib/tls/tortls.h
+++ b/src/lib/tls/tortls.h
@@ -25,12 +25,12 @@ struct ssl_ctx_st;
struct ssl_session_st;
typedef struct ssl_ctx_st tor_tls_context_impl_t;
typedef struct ssl_st tor_tls_impl_t;
-#else
+#else /* !(defined(ENABLE_OPENSSL)) */
struct PRFileDesc;
typedef struct PRFileDesc tor_tls_context_impl_t;
typedef struct PRFileDesc tor_tls_impl_t;
-#endif
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
+#endif /* defined(TORTLS_PRIVATE) */
struct tor_x509_cert_t;
@@ -144,9 +144,9 @@ 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);
-#else
+#else /* !(defined(ENABLE_OPENSSL)) */
#define check_no_tls_errors() STMT_NIL
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
int tor_tls_get_my_certs(int server,
const struct tor_x509_cert_t **link_cert_out,
diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h
index 071c506561..866483a94c 100644
--- a/src/lib/tls/tortls_internal.h
+++ b/src/lib/tls/tortls_internal.h
@@ -61,8 +61,8 @@ STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret,
void *arg);
STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
uint16_t cipher);
-#endif
-#endif
+#endif /* defined(TORTLS_OPENSSL_PRIVATE) */
+#endif /* defined(ENABLE_OPENSSL) */
#ifdef TOR_UNIT_TESTS
extern int tor_tls_object_ex_data_index;
@@ -73,4 +73,4 @@ extern uint64_t total_bytes_written_over_tls;
extern uint64_t total_bytes_written_by_tls;
#endif /* defined(TOR_UNIT_TESTS) */
-#endif /* defined(TORTLS_INTERNAL_H) */
+#endif /* !defined(TORTLS_INTERNAL_H) */
diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c
index b40f948a3b..86f0ac42cc 100644
--- a/src/lib/tls/tortls_openssl.c
+++ b/src/lib/tls/tortls_openssl.c
@@ -25,7 +25,7 @@
* <winsock.h> and mess things up, in at least some openssl versions. */
#include <winsock2.h>
#include <ws2tcpip.h>
-#endif
+#endif /* defined(_WIN32) */
#include "lib/crypt_ops/crypto_cipher.h"
#include "lib/crypt_ops/crypto_rand.h"
@@ -318,7 +318,7 @@ tor_tls_init(void)
#else
SSL_library_init();
SSL_load_error_strings();
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
#if (SIZEOF_VOID_P >= 8 && \
OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1))
@@ -383,7 +383,7 @@ static const char SERVER_CIPHER_LIST[] =
* conclude that it has no valid ciphers if it's running with TLS1.3.
*/
TLS1_3_TXT_AES_128_GCM_SHA256 ":"
-#endif
+#endif /* defined(TLS1_3_TXT_AES_128_GCM_SHA256) */
TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
TLS1_TXT_DHE_RSA_WITH_AES_128_SHA;
@@ -657,7 +657,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
if (r < 0)
goto error;
}
-#else
+#else /* !(defined(SSL_CTX_set1_groups_list) || ...) */
if (! is_client) {
int nid;
EC_KEY *ec_key;
@@ -673,7 +673,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
SSL_CTX_set_tmp_ecdh(result->ctx, ec_key);
EC_KEY_free(ec_key);
}
-#endif
+#endif /* defined(SSL_CTX_set1_groups_list) || ...) */
SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
always_accept_verify_cb);
/* let us realloc bufs that we're writing from */
@@ -1062,7 +1062,7 @@ tor_tls_new(tor_socket_t sock, int isServer)
/* We can't actually use TLS 1.3 until this bug is fixed. */
SSL_set_max_proto_version(result->ssl, TLS1_2_VERSION);
}
-#endif
+#endif /* defined(SSL_CTRL_SET_MAX_PROTO_VERSION) */
if (!SSL_set_cipher_list(result->ssl,
isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) {
@@ -1728,7 +1728,7 @@ tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out,
else
return -1;
}
-#endif
+#endif /* defined(TLS1_3_VERSION) */
return (r == 1) ? 0 : -1;
}
diff --git a/src/lib/tls/tortls_st.h b/src/lib/tls/tortls_st.h
index 3f7ea8ac6a..73f6e6ecca 100644
--- a/src/lib/tls/tortls_st.h
+++ b/src/lib/tls/tortls_st.h
@@ -64,7 +64,7 @@ struct tor_tls_t {
void (*negotiated_callback)(tor_tls_t *tls, void *arg);
/** Argument to pass to negotiated_callback. */
void *callback_arg;
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
#ifdef ENABLE_NSS
/** Last values retried from tor_get_prfiledesc_byte_counts(). */
uint64_t last_write_count;
@@ -72,4 +72,4 @@ struct tor_tls_t {
#endif
};
-#endif
+#endif /* !defined(TOR_TORTLS_ST_H) */
diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h
index 5e6660de5c..0390a5464d 100644
--- a/src/lib/tls/x509.h
+++ b/src/lib/tls/x509.h
@@ -35,7 +35,7 @@ struct tor_x509_cert_t {
common_digests_t cert_digests;
common_digests_t pkey_digests;
};
-#endif
+#endif /* defined(TOR_X509_PRIVATE) */
void tor_tls_pick_certificate_lifetime(time_t now,
unsigned cert_lifetime,
@@ -47,7 +47,7 @@ tor_x509_cert_t *tor_x509_cert_replace_expiration(
const tor_x509_cert_t *inp,
time_t new_expiration_time,
crypto_pk_t *signing_key);
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert);
@@ -72,4 +72,4 @@ int tor_tls_cert_is_valid(int severity,
time_t now,
int check_rsa_1024);
-#endif
+#endif /* !defined(TOR_X509_H) */
diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h
index bf2bec9689..f858baae98 100644
--- a/src/lib/tls/x509_internal.h
+++ b/src/lib/tls/x509_internal.h
@@ -50,4 +50,4 @@ int tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert);
#define tor_x509_cert_set_cached_der_encoding(cert) (0)
#endif
-#endif
+#endif /* !defined(TOR_X509_INTERNAL_H) */
diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c
index fb4af54c52..e04afaf07b 100644
--- a/src/lib/tls/x509_nss.c
+++ b/src/lib/tls/x509_nss.c
@@ -120,13 +120,13 @@ tor_tls_create_certificate_internal(crypto_pk_t *rsa,
der.data, der.len,
(SECKEYPrivateKey *)signing_key,//const
&cert->signature);
-#else
+#else /* !(0) */
s = SEC_DerSignData(cert->arena,
&signed_der,
der.data, der.len,
(SECKEYPrivateKey *)signing_key,//const
SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION);
-#endif
+#endif /* 0 */
if (s != SECSuccess)
goto err;
@@ -145,7 +145,7 @@ tor_tls_create_certificate_internal(crypto_pk_t *rsa,
&result_cert->signatureWrap, issuer_pk, NULL);
tor_assert(cert_ok == SECSuccess);
}
-#endif
+#endif /* 1 */
err:
if (subject_spki)
@@ -455,4 +455,4 @@ tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp,
return newcert ? tor_x509_cert_new(newcert) : NULL;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c
index a344279c22..03f65049cf 100644
--- a/src/lib/tls/x509_openssl.c
+++ b/src/lib/tls/x509_openssl.c
@@ -59,12 +59,12 @@ ENABLE_GCC_WARNING(redundant-decls)
#define X509_get_notAfter(cert) \
X509_getm_notAfter(cert)
#endif
-#else /* ! OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */
+#else /* !(defined(OPENSSL_1_1_API)) */
#define X509_get_notBefore_const(cert) \
((const ASN1_TIME*) X509_get_notBefore((X509 *)cert))
#define X509_get_notAfter_const(cert) \
((const ASN1_TIME*) X509_get_notAfter((X509 *)cert))
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
/** Return a newly allocated X509 name with commonName <b>cname</b>. */
static X509_NAME *
diff --git a/src/lib/trace/debug.h b/src/lib/trace/debug.h
index e35616cf50..92bb95c883 100644
--- a/src/lib/trace/debug.h
+++ b/src/lib/trace/debug.h
@@ -27,4 +27,4 @@
"\"" XSTR(subsystem) "\" hit. " \
"(line "XSTR(__LINE__) ")")
-#endif /* TOR_TRACE_LOG_DEBUG_H */
+#endif /* !defined(TOR_TRACE_LOG_DEBUG_H) */
diff --git a/src/lib/trace/events.h b/src/lib/trace/events.h
index 1e1e7b9d16..0674f7d501 100644
--- a/src/lib/trace/events.h
+++ b/src/lib/trace/events.h
@@ -34,12 +34,12 @@
#include "lib/trace/debug.h"
#endif
-#else /* TOR_EVENT_TRACING_ENABLED */
+#else /* !(defined(TOR_EVENT_TRACING_ENABLED)) */
/* Reaching this point, we NOP every event declaration because event tracing
* is not been enabled at compile time. */
#define tor_trace(subsystem, name, args...)
-#endif /* TOR_EVENT_TRACING_ENABLED */
+#endif /* defined(TOR_EVENT_TRACING_ENABLED) */
-#endif /* TOR_TRACE_EVENTS_H */
+#endif /* !defined(TOR_TRACE_EVENTS_H) */
diff --git a/src/lib/trace/include.am b/src/lib/trace/include.am
index 6f10c98744..98098c87f4 100644
--- a/src/lib/trace/include.am
+++ b/src/lib/trace/include.am
@@ -2,6 +2,7 @@
noinst_LIBRARIES += \
src/lib/libtor-trace.a
+# ADD_C_FILE: INSERT HEADERS HERE.
TRACEHEADERS = \
src/lib/trace/trace.h \
src/lib/trace/events.h
@@ -11,7 +12,7 @@ TRACEHEADERS += \
src/lib/trace/debug.h
endif
-# Library source files.
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_trace_a_SOURCES = \
src/lib/trace/trace.c
diff --git a/src/lib/trace/trace.h b/src/lib/trace/trace.h
index 606d435568..5001b28a1d 100644
--- a/src/lib/trace/trace.h
+++ b/src/lib/trace/trace.h
@@ -11,4 +11,4 @@
void tor_trace_init(void);
-#endif // TOR_TRACE_TRACE_H
+#endif /* !defined(TOR_TRACE_TRACE_H) */
diff --git a/src/lib/version/include.am b/src/lib/version/include.am
index 6944eb05e3..0ae31be1b2 100644
--- a/src/lib/version/include.am
+++ b/src/lib/version/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-version-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_version_a_SOURCES = \
src/lib/version/git_revision.c \
src/lib/version/version.c
@@ -20,6 +21,7 @@ src/lib/version/git_revision.$(OBJEXT) \
src/lib/version/src_lib_libtor_version_testing_a-git_revision.$(OBJEXT): \
micro-revision.i
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/version/git_revision.h \
src/lib/version/torversion.h
diff --git a/src/lib/wallclock/approx_time.h b/src/lib/wallclock/approx_time.h
index e6b53f2c27..e7da160122 100644
--- a/src/lib/wallclock/approx_time.h
+++ b/src/lib/wallclock/approx_time.h
@@ -22,4 +22,4 @@ time_t approx_time(void);
void update_approx_time(time_t now);
#endif /* defined(TIME_IS_FAST) */
-#endif
+#endif /* !defined(TOR_APPROX_TIME_H) */
diff --git a/src/lib/wallclock/include.am b/src/lib/wallclock/include.am
index 2351252e0c..2b50d6ccbb 100644
--- a/src/lib/wallclock/include.am
+++ b/src/lib/wallclock/include.am
@@ -5,6 +5,7 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-wallclock-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_wallclock_a_SOURCES = \
src/lib/wallclock/approx_time.c \
src/lib/wallclock/time_to_tm.c \
@@ -15,6 +16,7 @@ src_lib_libtor_wallclock_testing_a_SOURCES = \
src_lib_libtor_wallclock_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_wallclock_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/wallclock/approx_time.h \
src/lib/wallclock/timeval.h \
diff --git a/src/lib/wallclock/time_to_tm.h b/src/lib/wallclock/time_to_tm.h
index abe78c0efe..da27fcaba1 100644
--- a/src/lib/wallclock/time_to_tm.h
+++ b/src/lib/wallclock/time_to_tm.h
@@ -19,4 +19,4 @@ struct tm *tor_localtime_r_msg(const time_t *timep, struct tm *result,
struct tm *tor_gmtime_r_msg(const time_t *timep, struct tm *result,
char **err_out);
-#endif
+#endif /* !defined(TOR_WALLCLOCK_TIME_TO_TM_H) */
diff --git a/src/lib/wallclock/timeval.h b/src/lib/wallclock/timeval.h
index 4967e939bf..e632d04a04 100644
--- a/src/lib/wallclock/timeval.h
+++ b/src/lib/wallclock/timeval.h
@@ -20,6 +20,27 @@
#include <sys/time.h>
#endif
+#ifdef TOR_COVERAGE
+/* For coverage builds, we use a slower definition of these macros without
+ * branches, to make coverage consistent. */
+#undef timeradd
+#undef timersub
+#define timeradd(tv1,tv2,tvout) \
+ do { \
+ (tvout)->tv_sec = (tv1)->tv_sec + (tv2)->tv_sec; \
+ (tvout)->tv_usec = (tv1)->tv_usec + (tv2)->tv_usec; \
+ (tvout)->tv_sec += (tvout)->tv_usec / 1000000; \
+ (tvout)->tv_usec %= 1000000; \
+ } while (0)
+#define timersub(tv1,tv2,tvout) \
+ do { \
+ (tvout)->tv_sec = (tv1)->tv_sec - (tv2)->tv_sec - 1; \
+ (tvout)->tv_usec = (tv1)->tv_usec - (tv2)->tv_usec + 1000000; \
+ (tvout)->tv_sec += (tvout)->tv_usec / 1000000; \
+ (tvout)->tv_usec %= 1000000; \
+ } while (0)
+#endif /* defined(TOR_COVERAGE) */
+
#ifndef timeradd
/** Replacement for timeradd on platforms that do not have it: sets tvout to
* the sum of tv1 and tv2. */
@@ -62,4 +83,4 @@
((tv1)->tv_sec op (tv2)->tv_sec))
#endif /* !defined(timercmp) */
-#endif
+#endif /* !defined(TOR_TIMEVAL_H) */
diff --git a/src/lib/wallclock/tor_gettimeofday.h b/src/lib/wallclock/tor_gettimeofday.h
index c7fff9747a..6fec2fc893 100644
--- a/src/lib/wallclock/tor_gettimeofday.h
+++ b/src/lib/wallclock/tor_gettimeofday.h
@@ -17,4 +17,4 @@ struct timeval;
MOCK_DECL(void, tor_gettimeofday, (struct timeval *timeval));
-#endif
+#endif /* !defined(TOR_GETTIMEOFDAY_H) */
diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs
index 066b08eddb..14170d0353 100644
--- a/src/rust/protover/ffi.rs
+++ b/src/rust/protover/ffi.rs
@@ -31,6 +31,7 @@ fn translate_to_rust(c_proto: uint32_t) -> Result<Protocol, ProtoverError> {
8 => Ok(Protocol::Microdesc),
9 => Ok(Protocol::Cons),
10 => Ok(Protocol::Padding),
+ 11 => Ok(Protocol::FlowCtrl),
_ => Err(ProtoverError::UnknownProtocol),
}
}
diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs
index 74158d9f6d..7a76fcdd94 100644
--- a/src/rust/protover/protover.rs
+++ b/src/rust/protover/protover.rs
@@ -47,6 +47,7 @@ pub enum Protocol {
Microdesc,
Relay,
Padding,
+ FlowCtrl,
}
impl fmt::Display for Protocol {
@@ -75,6 +76,7 @@ impl FromStr for Protocol {
"Microdesc" => Ok(Protocol::Microdesc),
"Relay" => Ok(Protocol::Relay),
"Padding" => Ok(Protocol::Padding),
+ "FlowCtrl" => Ok(Protocol::FlowCtrl),
_ => Err(ProtoverError::UnknownProtocol),
}
}
@@ -166,7 +168,8 @@ pub(crate) fn get_supported_protocols_cstr() -> &'static CStr {
LinkAuth=3 \
Microdesc=1-2 \
Relay=1-2 \
- Padding=1"
+ Padding=2 \
+ FlowCtrl=1"
)
} else {
cstr!(
@@ -180,7 +183,8 @@ pub(crate) fn get_supported_protocols_cstr() -> &'static CStr {
LinkAuth=1,3 \
Microdesc=1-2 \
Relay=1-2 \
- Padding=1"
+ Padding=2 \
+ FlowCtrl=1"
)
}
}
diff --git a/src/rust/tor_log/tor_log.rs b/src/rust/tor_log/tor_log.rs
index 98fccba5a9..bbaf97129c 100644
--- a/src/rust/tor_log/tor_log.rs
+++ b/src/rust/tor_log/tor_log.rs
@@ -99,14 +99,14 @@ pub mod log {
/// Domain log types. These mirror definitions in src/lib/log/log.h
/// C_RUST_COUPLED: src/lib/log/log.c, log severity types
extern "C" {
- static LD_NET_: u32;
- static LD_GENERAL_: u32;
+ static LD_NET_: u64;
+ static LD_GENERAL_: u64;
}
/// Translate Rust defintions of log domain levels to C. This exposes a 1:1
/// mapping between types.
#[inline]
- pub unsafe fn translate_domain(domain: LogDomain) -> u32 {
+ pub unsafe fn translate_domain(domain: LogDomain) -> u64 {
match domain {
LogDomain::Net => LD_NET_,
LogDomain::General => LD_GENERAL_,
@@ -128,7 +128,7 @@ pub mod log {
extern "C" {
pub fn tor_log_string(
severity: c_int,
- domain: u32,
+ domain: u64,
function: *const c_char,
string: *const c_char,
);
diff --git a/src/test/bench.c b/src/test/bench.c
index 65fa617cbd..cf732df593 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -22,7 +22,7 @@
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/obj_mac.h>
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
#include "core/or/circuitlist.h"
#include "app/config/config.h"
@@ -701,7 +701,7 @@ bench_ecdh_p224(void)
{
bench_ecdh_impl(NID_secp224r1, "P-224");
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
static void
bench_md_parse(void)
diff --git a/src/test/fuzz/fixup_filenames.sh b/src/test/fuzz/fixup_filenames.sh
index 68efc1abc5..f730d532a5 100755
--- a/src/test/fuzz/fixup_filenames.sh
+++ b/src/test/fuzz/fixup_filenames.sh
@@ -8,9 +8,9 @@ if [ ! -d "$1" ] ; then
fi
for fn in "$1"/* ; do
- prev=`basename "$fn"`
- post=`sha256sum "$fn" | sed -e 's/ .*//;'`
- if [ "$prev" == "$post" ] ; then
+ prev=$(basename "$fn")
+ post=$(sha256sum "$fn" | sed -e 's/ .*//;')
+ if [ "$prev" = "$post" ] ; then
echo "OK $prev"
else
echo "mv $prev $post"
diff --git a/src/test/fuzz/fuzz_multi.sh b/src/test/fuzz/fuzz_multi.sh
index b4a17ed8cb..406ab498d9 100755
--- a/src/test/fuzz/fuzz_multi.sh
+++ b/src/test/fuzz/fuzz_multi.sh
@@ -1,3 +1,5 @@
+#!/bin/sh
+
MEMLIMIT_BYTES=21990500990976
N_CPUS=1
@@ -6,9 +8,9 @@ if [ $# -ge 1 ]; then
shift
fi
-FILTER=echo
+FILTER="echo"
-for i in `seq -w "$N_CPUS"`; do
+for i in $(seq -w "$N_CPUS"); do
if [ "$i" -eq 1 ]; then
if [ "$N_CPUS" -eq 1 ]; then
INSTANCE=""
diff --git a/src/test/fuzz/fuzz_strops.c b/src/test/fuzz/fuzz_strops.c
index 64a6453050..459b4e21aa 100644
--- a/src/test/fuzz/fuzz_strops.c
+++ b/src/test/fuzz/fuzz_strops.c
@@ -86,15 +86,13 @@ b16_enc(const chunk_t *inp)
return ch;
}
-#if 0
static chunk_t *
b32_dec(const chunk_t *inp)
{
chunk_t *ch = chunk_new(inp->len);//XXXX
int r = base32_decode((char *)ch->buf, ch->len, (char *)inp->buf, inp->len);
if (r >= 0) {
- ch->len = r; // XXXX we need some way to get the actual length of
- // XXXX the output here.
+ ch->len = r;
} else {
chunk_free(ch);
}
@@ -108,7 +106,6 @@ b32_enc(const chunk_t *inp)
ch->len = strlen((char *) ch->buf);
return ch;
}
-#endif
static chunk_t *
b64_dec(const chunk_t *inp)
@@ -222,10 +219,7 @@ fuzz_main(const uint8_t *stdin_buf, size_t data_size)
ENCODE_ROUNDTRIP(b16_enc, b16_dec, chunk_free_);
break;
case 1:
- /*
- XXXX see notes above about our base-32 functions.
ENCODE_ROUNDTRIP(b32_enc, b32_dec, chunk_free_);
- */
break;
case 2:
ENCODE_ROUNDTRIP(b64_enc, b64_dec, chunk_free_);
@@ -241,6 +235,18 @@ fuzz_main(const uint8_t *stdin_buf, size_t data_size)
kv_flags = 0;
ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
break;
+ case 7:
+ kv_flags = KV_OMIT_VALS;
+ ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
+ break;
+ case 8:
+ kv_flags = KV_QUOTED;
+ ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
+ break;
+ case 9:
+ kv_flags = KV_QUOTED|KV_OMIT_VALS;
+ ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
+ break;
}
return 0;
diff --git a/src/test/fuzz/fuzzing.h b/src/test/fuzz/fuzzing.h
index 150ac4aa7d..2d278825ec 100644
--- a/src/test/fuzz/fuzzing.h
+++ b/src/test/fuzz/fuzzing.h
@@ -9,5 +9,5 @@ int fuzz_main(const uint8_t *data, size_t sz);
void disable_signature_checking(void);
-#endif /* FUZZING_H */
+#endif /* !defined(FUZZING_H) */
diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c
index 387c865a9b..6d0f9d7d60 100644
--- a/src/test/fuzz/fuzzing_common.c
+++ b/src/test/fuzz/fuzzing_common.c
@@ -137,7 +137,7 @@ LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
return fuzz_main(Data, Size);
}
-#else /* Not LLVM_FUZZ, so AFL. */
+#else /* !(defined(LLVM_FUZZ)) */
int
main(int argc, char **argv)
@@ -194,4 +194,4 @@ main(int argc, char **argv)
return 0;
}
-#endif
+#endif /* defined(LLVM_FUZZ) */
diff --git a/src/test/fuzz/minimize.sh b/src/test/fuzz/minimize.sh
index 87d3dda13c..ce43812bb8 100755
--- a/src/test/fuzz/minimize.sh
+++ b/src/test/fuzz/minimize.sh
@@ -7,7 +7,7 @@ if [ ! -d "$1" ] ; then
exit 1
fi
-which=`basename "$1"`
+which=$(basename "$1")
mkdir "$1.out"
afl-cmin -i "$1" -o "$1.out" -m none "./src/test/fuzz/fuzz-${which}"
diff --git a/src/test/fuzz_static_testcases.sh b/src/test/fuzz_static_testcases.sh
index f7b3adffb1..b883352402 100755
--- a/src/test/fuzz_static_testcases.sh
+++ b/src/test/fuzz_static_testcases.sh
@@ -14,7 +14,7 @@ fi
for fuzzer in "${builddir:-.}"/src/test/fuzz/fuzz-* ; do
- f=`basename $fuzzer`
+ f=$(basename "$fuzzer")
case="${f#fuzz-}"
if [ -d "${TOR_FUZZ_CORPORA}/${case}" ]; then
echo "Running tests for ${case}"
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index f2ae8398df..0a21fe576b 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -21,26 +21,35 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
/* For a usable intro point we need at least two link specifiers: One legacy
* keyid and one ipv4 */
{
- hs_desc_link_specifier_t *ls_legacy = tor_malloc_zero(sizeof(*ls_legacy));
- hs_desc_link_specifier_t *ls_v4 = tor_malloc_zero(sizeof(*ls_v4));
- ls_legacy->type = LS_LEGACY_ID;
- memcpy(ls_legacy->u.legacy_id, "0299F268FCA9D55CD157976D39AE92B4B455B3A8",
- DIGEST_LEN);
- ls_v4->u.ap.port = 9001;
- int family = tor_addr_parse(&ls_v4->u.ap.addr, addr);
+ tor_addr_t a;
+ tor_addr_make_unspec(&a);
+ link_specifier_t *ls_legacy = link_specifier_new();
+ link_specifier_t *ls_ip = link_specifier_new();
+ link_specifier_set_ls_type(ls_legacy, LS_LEGACY_ID);
+ memset(link_specifier_getarray_un_legacy_id(ls_legacy), 'C',
+ link_specifier_getlen_un_legacy_id(ls_legacy));
+ int family = tor_addr_parse(&a, addr);
switch (family) {
case AF_INET:
- ls_v4->type = LS_IPV4;
+ link_specifier_set_ls_type(ls_ip, LS_IPV4);
+ link_specifier_set_un_ipv4_addr(ls_ip, tor_addr_to_ipv4h(&a));
+ link_specifier_set_un_ipv4_port(ls_ip, 9001);
break;
case AF_INET6:
- ls_v4->type = LS_IPV6;
+ link_specifier_set_ls_type(ls_ip, LS_IPV6);
+ memcpy(link_specifier_getarray_un_ipv6_addr(ls_ip),
+ tor_addr_to_in6_addr8(&a),
+ link_specifier_getlen_un_ipv6_addr(ls_ip));
+ link_specifier_set_un_ipv6_port(ls_ip, 9001);
break;
default:
- /* Stop the test, not suppose to have an error. */
- tt_int_op(family, OP_EQ, AF_INET);
+ /* Stop the test, not supposed to have an error.
+ * Compare with -1 to show the actual family.
+ */
+ tt_int_op(family, OP_EQ, -1);
}
smartlist_add(ip->link_specifiers, ls_legacy);
- smartlist_add(ip->link_specifiers, ls_v4);
+ smartlist_add(ip->link_specifiers, ls_ip);
}
ret = ed25519_keypair_generate(&auth_kp, 0);
@@ -202,7 +211,6 @@ void
hs_helper_desc_equal(const hs_descriptor_t *desc1,
const hs_descriptor_t *desc2)
{
- char *addr1 = NULL, *addr2 = NULL;
/* Plaintext data section. */
tt_int_op(desc1->plaintext_data.version, OP_EQ,
desc2->plaintext_data.version);
@@ -291,35 +299,57 @@ hs_helper_desc_equal(const hs_descriptor_t *desc1,
tt_int_op(smartlist_len(ip1->link_specifiers), ==,
smartlist_len(ip2->link_specifiers));
for (int j = 0; j < smartlist_len(ip1->link_specifiers); j++) {
- hs_desc_link_specifier_t *ls1 = smartlist_get(ip1->link_specifiers, j),
- *ls2 = smartlist_get(ip2->link_specifiers, j);
- tt_int_op(ls1->type, ==, ls2->type);
- switch (ls1->type) {
+ link_specifier_t *ls1 = smartlist_get(ip1->link_specifiers, j),
+ *ls2 = smartlist_get(ip2->link_specifiers, j);
+ tt_int_op(link_specifier_get_ls_type(ls1), ==,
+ link_specifier_get_ls_type(ls2));
+ switch (link_specifier_get_ls_type(ls1)) {
case LS_IPV4:
+ {
+ uint32_t addr1 = link_specifier_get_un_ipv4_addr(ls1);
+ uint32_t addr2 = link_specifier_get_un_ipv4_addr(ls2);
+ tt_int_op(addr1, OP_EQ, addr2);
+ uint16_t port1 = link_specifier_get_un_ipv4_port(ls1);
+ uint16_t port2 = link_specifier_get_un_ipv4_port(ls2);
+ tt_int_op(port1, ==, port2);
+ }
+ break;
case LS_IPV6:
{
- addr1 = tor_addr_to_str_dup(&ls1->u.ap.addr);
- addr2 = tor_addr_to_str_dup(&ls2->u.ap.addr);
- tt_str_op(addr1, OP_EQ, addr2);
- tor_free(addr1);
- tor_free(addr2);
- tt_int_op(ls1->u.ap.port, ==, ls2->u.ap.port);
+ const uint8_t *addr1 =
+ link_specifier_getconstarray_un_ipv6_addr(ls1);
+ const uint8_t *addr2 =
+ link_specifier_getconstarray_un_ipv6_addr(ls2);
+ tt_int_op(link_specifier_getlen_un_ipv6_addr(ls1), OP_EQ,
+ link_specifier_getlen_un_ipv6_addr(ls2));
+ tt_mem_op(addr1, OP_EQ, addr2,
+ link_specifier_getlen_un_ipv6_addr(ls1));
+ uint16_t port1 = link_specifier_get_un_ipv6_port(ls1);
+ uint16_t port2 = link_specifier_get_un_ipv6_port(ls2);
+ tt_int_op(port1, ==, port2);
}
break;
case LS_LEGACY_ID:
- tt_mem_op(ls1->u.legacy_id, OP_EQ, ls2->u.legacy_id,
- sizeof(ls1->u.legacy_id));
+ {
+ const uint8_t *id1 =
+ link_specifier_getconstarray_un_legacy_id(ls1);
+ const uint8_t *id2 =
+ link_specifier_getconstarray_un_legacy_id(ls2);
+ tt_int_op(link_specifier_getlen_un_legacy_id(ls1), OP_EQ,
+ link_specifier_getlen_un_legacy_id(ls2));
+ tt_mem_op(id1, OP_EQ, id2,
+ link_specifier_getlen_un_legacy_id(ls1));
+ }
break;
default:
/* Unknown type, caught it and print its value. */
- tt_int_op(ls1->type, OP_EQ, -1);
+ tt_int_op(link_specifier_get_ls_type(ls1), OP_EQ, -1);
}
}
}
}
done:
- tor_free(addr1);
- tor_free(addr2);
+ ;
}
diff --git a/src/test/include.am b/src/test/include.am
index d585c2a38a..85f9c9f880 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -32,8 +32,15 @@ endif
if USEPYTHON
TESTSCRIPTS += src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh
+
+if COVERAGE_ENABLED
+# ...
+else
+# Only do this when coverage is not on, since it invokes lots of code
+# in a kind of unpredictable way.
TESTSCRIPTS += src/test/test_rebind.sh
endif
+endif
TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
src/test/test_workqueue \
@@ -46,10 +53,8 @@ TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-v2-min hs-v3-min \
single-onion-v23
# only run if we can ping6 ::1 (localhost)
-# IPv6-only v3 single onion services don't work yet, so we don't test the
-# single-onion-v23-ipv6-md flavor
TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-v23-ipv6-md \
- single-onion-ipv6-md
+ single-onion-v23-ipv6-md
# only run if we can find a stable (or simply another) version of tor
TEST_CHUTNEY_FLAVORS_MIXED = mixed+hs-v2
@@ -85,10 +90,13 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
src_test_test_SOURCES =
if UNITTESTS_ENABLED
+
+# ADD_C_FILE: INSERT SOURCES HERE.
src_test_test_SOURCES += \
src/test/log_test_helpers.c \
src/test/hs_test_helpers.c \
src/test/rend_test_helpers.c \
+ src/test/rng_test_helpers.c \
src/test/test.c \
src/test/test_accounting.c \
src/test/test_addr.c \
@@ -126,6 +134,7 @@ src_test_test_SOURCES += \
src/test/test_dir.c \
src/test/test_dir_common.c \
src/test/test_dir_handle_get.c \
+ src/test/test_dispatch.c \
src/test/test_dos.c \
src/test/test_entryconn.c \
src/test/test_entrynodes.c \
@@ -150,6 +159,7 @@ src_test_test_SOURCES += \
src/test/test_logging.c \
src/test/test_mainloop.c \
src/test/test_microdesc.c \
+ src/test/test_namemap.c \
src/test/test_netinfo.c \
src/test/test_nodelist.c \
src/test/test_oom.c \
@@ -165,6 +175,8 @@ src_test_test_SOURCES += \
src/test/test_proto_misc.c \
src/test/test_protover.c \
src/test/test_pt.c \
+ src/test/test_pubsub_build.c \
+ src/test/test_pubsub_msg.c \
src/test/test_relay.c \
src/test/test_relaycell.c \
src/test/test_relaycrypt.c \
@@ -175,6 +187,7 @@ src_test_test_SOURCES += \
src/test/test_routerlist.c \
src/test/test_routerset.c \
src/test/test_scheduler.c \
+ src/test/test_sendme.c \
src/test/test_shared_random.c \
src/test/test_socks.c \
src/test/test_status.c \
@@ -207,10 +220,13 @@ endif
src_test_test_slow_SOURCES =
if UNITTESTS_ENABLED
src_test_test_slow_SOURCES += \
+ src/test/rng_test_helpers.c \
src/test/test_slow.c \
src/test/test_crypto_slow.c \
src/test/test_process_slow.c \
src/test/test_prob_distr.c \
+ src/test/ptr_helpers.c \
+ src/test/test_ptr_slow.c \
src/test/testing_common.c \
src/test/testing_rsakeys.c \
src/ext/tinytest.c
@@ -308,12 +324,15 @@ src_test_test_timers_LDADD = \
@TOR_LZMA_LIBS@
src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS+= \
src/test/fakechans.h \
src/test/hs_test_helpers.h \
src/test/log_test_helpers.h \
src/test/rend_test_helpers.h \
+ src/test/rng_test_helpers.h \
src/test/test.h \
+ src/test/ptr_helpers.h \
src/test/test_helpers.h \
src/test/test_dir_common.h \
src/test/test_connection.h \
diff --git a/src/test/ope_ref.py b/src/test/ope_ref.py
index f9bd97c546..b2f7012563 100644
--- a/src/test/ope_ref.py
+++ b/src/test/ope_ref.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
# Copyright 2018-2019, The Tor Project, Inc. See LICENSE for licensing info.
# Reference implementation for our rudimentary OPE code, used to
diff --git a/src/test/ptr_helpers.c b/src/test/ptr_helpers.c
new file mode 100644
index 0000000000..a55ab437fa
--- /dev/null
+++ b/src/test/ptr_helpers.c
@@ -0,0 +1,50 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "test/ptr_helpers.h"
+
+/**
+ * Cast <b> (inptr_t value) to a void pointer.
+ */
+void *
+cast_intptr_to_voidstar(intptr_t x)
+{
+ void *r = (void *)x;
+
+ return r;
+}
+
+/**
+ * Cast x (void pointer) to inptr_t value.
+ */
+intptr_t
+cast_voidstar_to_intptr(void *x)
+{
+ intptr_t r = (intptr_t)x;
+
+ return r;
+}
+
+/**
+ * Cast x (uinptr_t value) to void pointer.
+ */
+void *
+cast_uintptr_to_voidstar(uintptr_t x)
+{
+ void *r = (void *)x;
+
+ return r;
+}
+
+/**
+ * Cast x (void pointer) to uinptr_t value.
+ */
+uintptr_t
+cast_voidstar_to_uintptr(void *x)
+{
+ uintptr_t r = (uintptr_t)x;
+
+ return r;
+}
diff --git a/src/test/ptr_helpers.h b/src/test/ptr_helpers.h
new file mode 100644
index 0000000000..7349bddd51
--- /dev/null
+++ b/src/test/ptr_helpers.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PTR_HELPERS_H
+#define TOR_PTR_HELPERS_H
+
+#include <stdint.h>
+
+void *
+cast_intptr_to_voidstar(intptr_t x);
+
+intptr_t
+cast_voidstar_to_intptr(void *x);
+
+void *
+cast_uintptr_to_voidstar(uintptr_t x);
+
+uintptr_t
+cast_voidstar_to_uintptr(void *x);
+
+#endif /* !defined(TOR_PTR_HELPERS_H) */
diff --git a/src/test/rng_test_helpers.c b/src/test/rng_test_helpers.c
new file mode 100644
index 0000000000..7024fb5793
--- /dev/null
+++ b/src/test/rng_test_helpers.c
@@ -0,0 +1,259 @@
+/* Copyright (c) 2018-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file rng_test_helpers.c
+ * \brief Helpers for overriding PRNGs during unit tests.
+ *
+ * We define two PRNG overrides: a "reproducible PRNG" where the seed is
+ * chosen randomly but the stream can be replayed later on in case a bug is
+ * found, and a "deterministic PRNG" where the seed is fixed in the unit
+ * tests.
+ *
+ * Obviously, this code is testing-only.
+ */
+
+#include "orconfig.h"
+#include "core/or/or.h"
+
+#include "lib/crypt_ops/crypto_rand.h"
+#include "ext/tinytest.h"
+
+#include "test/rng_test_helpers.h"
+
+#ifndef TOR_UNIT_TESTS
+#error "No. Never link this code into Tor proper."
+#endif
+
+/**
+ * True iff the RNG is currently replaced. Prevents double-replacement.
+ **/
+static bool rng_is_replaced = false;
+
+/**
+ * Mutex to protect deterministic prng.
+ *
+ * Note that if you actually _use_ the prng from two threads at the same time,
+ * the results will probably be nondeterministic anyway.
+ */
+static tor_mutex_t *rng_mutex = NULL;
+
+/**
+ * Cached old value for the thread prng.
+ **/
+static crypto_fast_rng_t *stored_fast_rng = NULL;
+
+/** replacement for crypto_strongest_rand that delegates to crypto_rand. */
+static void
+mock_crypto_strongest_rand(uint8_t *out, size_t len)
+{
+ crypto_rand((char *)out, len);
+}
+
+/* This is the seed of the deterministic randomness. */
+static uint8_t rng_seed[16];
+static crypto_xof_t *rng_xof = NULL;
+
+/**
+ * Print the seed for our PRNG to stdout. We use this when we're failed
+ * test that had a reproducible RNG set.
+ **/
+void
+testing_dump_reproducible_rng_seed(void)
+{
+ printf("\n"
+ "Seed: %s\n",
+ hex_str((const char*)rng_seed, sizeof(rng_seed)));
+}
+
+/** Produce deterministic randomness for the stochastic tests using the global
+ * rng_xof output.
+ *
+ * This function produces deterministic data over multiple calls iff it's
+ * called in the same call order with the same 'n' parameter.
+ * If not, outputs will deviate. */
+static void
+crypto_rand_deterministic(char *out, size_t n)
+{
+ tor_assert(rng_xof);
+ tor_mutex_acquire(rng_mutex);
+ crypto_xof_squeeze_bytes(rng_xof, (uint8_t*)out, n);
+ tor_mutex_release(rng_mutex);
+}
+
+/**
+ * Implementation helper: override our crypto_rand() PRNG with a given seed of
+ * length <b>seed_len</b>. Overlong seeds are truncated; short ones are
+ * padded.
+ **/
+static void
+enable_deterministic_rng_impl(const uint8_t *seed, size_t seed_len)
+{
+ tor_assert(!rng_is_replaced);
+ tor_assert(crypto_rand == crypto_rand__real);
+
+ memset(rng_seed, 0, sizeof(rng_seed));
+ memcpy(rng_seed, seed, MIN(seed_len, sizeof(rng_seed)));
+
+ rng_mutex = tor_mutex_new();
+
+ crypto_xof_free(rng_xof);
+ rng_xof = crypto_xof_new();
+ crypto_xof_add_bytes(rng_xof, rng_seed, sizeof(rng_seed));
+ MOCK(crypto_rand, crypto_rand_deterministic);
+ MOCK(crypto_strongest_rand_, mock_crypto_strongest_rand);
+
+ uint8_t fast_rng_seed[CRYPTO_FAST_RNG_SEED_LEN];
+ memset(fast_rng_seed, 0xff, sizeof(fast_rng_seed));
+ memcpy(fast_rng_seed, rng_seed, MIN(sizeof(rng_seed),
+ sizeof(fast_rng_seed)));
+ crypto_fast_rng_t *fast_rng = crypto_fast_rng_new_from_seed(fast_rng_seed);
+ crypto_fast_rng_disable_reseed(fast_rng);
+ stored_fast_rng = crypto_replace_thread_fast_rng(fast_rng);
+
+ rng_is_replaced = true;
+}
+
+/**
+ * Replace our get_thread_fast_rng(), crypto_rand() and
+ * crypto_strongest_rand() prngs with a variant that generates all of its
+ * output deterministically from a randomly chosen seed. In the event of an
+ * error, you can log the seed later on with
+ * testing_dump_reproducible_rng_seed.
+ **/
+void
+testing_enable_reproducible_rng(void)
+{
+ const char *provided_seed = getenv("TOR_TEST_RNG_SEED");
+ if (provided_seed) {
+ size_t hexlen = strlen(provided_seed);
+ size_t seedlen = hexlen / 2;
+ uint8_t *seed = tor_malloc(hexlen / 2);
+ if (base16_decode((char*)seed, seedlen, provided_seed, hexlen) < 0) {
+ puts("Cannot decode value in TOR_TEST_RNG_SEED");
+ exit(1);
+ }
+ enable_deterministic_rng_impl(seed, seedlen);
+ tor_free(seed);
+ } else {
+ uint8_t seed[16];
+ crypto_rand((char*)seed, sizeof(seed));
+ enable_deterministic_rng_impl(seed, sizeof(seed));
+ }
+}
+
+/**
+ * Replace our get_thread_fast_rng(), crypto_rand() and
+ * crypto_strongest_rand() prngs with a variant that generates all of its
+ * output deterministically from a fixed seed. This variant is mainly useful
+ * for cases when we don't want coverage to change between runs.
+ *
+ * USAGE NOTE: Test correctness SHOULD NOT depend on the specific output of
+ * this "rng". If you need a specific output, use
+ * testing_enable_prefilled_rng() instead.
+ **/
+void
+testing_enable_deterministic_rng(void)
+{
+ static const uint8_t quotation[] =
+ "What will it be? A tree? A weed? "
+ "Each one is started from a seed."; // -- Mary Ann Hoberman
+ enable_deterministic_rng_impl(quotation, sizeof(quotation));
+}
+
+static uint8_t *prefilled_rng_buffer = NULL;
+static size_t prefilled_rng_buflen;
+static size_t prefilled_rng_idx;
+
+/**
+ * crypto_rand() replacement that returns canned data.
+ **/
+static void
+crypto_rand_prefilled(char *out, size_t n)
+{
+ tor_mutex_acquire(rng_mutex);
+ while (n) {
+ size_t n_to_copy = MIN(prefilled_rng_buflen - prefilled_rng_idx, n);
+ memcpy(out, prefilled_rng_buffer + prefilled_rng_idx, n_to_copy);
+ out += n_to_copy;
+ n -= n_to_copy;
+ prefilled_rng_idx += n_to_copy;
+
+ if (prefilled_rng_idx == prefilled_rng_buflen) {
+ prefilled_rng_idx = 0;
+ }
+ }
+ tor_mutex_release(rng_mutex);
+}
+
+/**
+ * Replace our crypto_rand() and crypto_strongest_rand() prngs with a variant
+ * that yields output from a buffer. If it reaches the end of the buffer, it
+ * starts over.
+ *
+ * Note: the get_thread_fast_rng() prng is not replaced by this; we'll need
+ * more code to support that.
+ **/
+void
+testing_enable_prefilled_rng(const void *buffer, size_t buflen)
+{
+ tor_assert(buflen > 0);
+ tor_assert(!rng_mutex);
+ rng_mutex = tor_mutex_new();
+
+ tor_mutex_acquire(rng_mutex);
+
+ prefilled_rng_buffer = tor_memdup(buffer, buflen);
+ prefilled_rng_buflen = buflen;
+ prefilled_rng_idx = 0;
+
+ tor_mutex_release(rng_mutex);
+
+ MOCK(crypto_rand, crypto_rand_prefilled);
+ MOCK(crypto_strongest_rand_, mock_crypto_strongest_rand);
+}
+
+/**
+ * Reset the position in the prefilled RNG buffer to the start.
+ */
+void
+testing_prefilled_rng_reset(void)
+{
+ tor_mutex_acquire(rng_mutex);
+ prefilled_rng_idx = 0;
+ tor_mutex_release(rng_mutex);
+}
+
+/**
+ * Undo the overrides for our PRNG. To be used at the end of testing.
+ *
+ * Note that this function should be safe to call even if the rng has not
+ * yet been replaced.
+ **/
+void
+testing_disable_rng_override(void)
+{
+ crypto_xof_free(rng_xof);
+ tor_free(prefilled_rng_buffer);
+ UNMOCK(crypto_rand);
+ UNMOCK(crypto_strongest_rand_);
+ tor_mutex_free(rng_mutex);
+
+ crypto_fast_rng_t *rng = crypto_replace_thread_fast_rng(stored_fast_rng);
+ crypto_fast_rng_free(rng);
+
+ rng_is_replaced = false;
+}
+
+/**
+ * As testing_disable_rng_override(), but dump the seed if the current
+ * test has failed.
+ */
+void
+testing_disable_reproducible_rng(void)
+{
+ if (tinytest_cur_test_has_failed()) {
+ testing_dump_reproducible_rng_seed();
+ }
+ testing_disable_rng_override();
+}
diff --git a/src/test/rng_test_helpers.h b/src/test/rng_test_helpers.h
new file mode 100644
index 0000000000..d7925148ae
--- /dev/null
+++ b/src/test/rng_test_helpers.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2017-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_RNG_TEST_HELPERS_H
+#define TOR_RNG_TEST_HELPERS_H
+
+#include "core/or/or.h"
+
+void testing_enable_deterministic_rng(void);
+void testing_enable_reproducible_rng(void);
+void testing_enable_prefilled_rng(const void *buffer, size_t buflen);
+
+void testing_prefilled_rng_reset(void);
+
+void testing_disable_rng_override(void);
+
+void testing_disable_reproducible_rng(void);
+#define testing_disable_deterministic_rng() \
+ testing_disable_rng_override()
+#define testing_disable_prefilled_rng() \
+ testing_disable_rng_override()
+
+void testing_dump_reproducible_rng_seed(void);
+
+#endif /* !defined(TOR_RNG_TEST_HELPERS_H) */
diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c
index 43754ed1c2..3f952e484f 100644
--- a/src/test/test-memwipe.c
+++ b/src/test/test-memwipe.c
@@ -49,7 +49,7 @@ const char *s = NULL;
* us do bad things, such as access freed buffers, without crashing. */
extern const char *malloc_options;
const char *malloc_options = "sufjj";
-#endif
+#endif /* defined(OpenBSD) */
static unsigned
fill_a_buffer_memset(void)
diff --git a/src/test/test-network.sh b/src/test/test-network.sh
index b7a9f1b3c0..5ef995f1a4 100755
--- a/src/test/test-network.sh
+++ b/src/test/test-network.sh
@@ -5,7 +5,7 @@
# If we already know CHUTNEY_PATH, don't bother with argument parsing
TEST_NETWORK="$CHUTNEY_PATH/tools/test-network.sh"
# Call the chutney version of this script, if it exists, and we can find it
-if [ -d "$CHUTNEY_PATH" -a -x "$TEST_NETWORK" ]; then
+if [ -d "$CHUTNEY_PATH" ] && [ -x "$TEST_NETWORK" ]; then
# we can't produce any output, because we might be --quiet
# this preserves arguments with spaces correctly
exec "$TEST_NETWORK" "$@"
@@ -16,34 +16,16 @@ fi
# Do we output anything at all?
ECHO="${ECHO:-echo}"
# Output is prefixed with the name of the script
-myname=$(basename $0)
-
-# Save the arguments before we destroy them
-# This might not preserve arguments with spaces in them
-ORIGINAL_ARGS="$@"
+myname=$(basename "$0")
# We need to find CHUTNEY_PATH, so that we can call the version of this script
# in chutney/tools with the same arguments. We also need to respect --quiet.
-until [ -z "$1" ]
-do
- case "$1" in
- --chutney-path)
- CHUTNEY_PATH="$2"
- shift
- ;;
- --tor-path)
- TOR_DIR="$2"
- shift
- ;;
- --quiet)
- ECHO=true
- ;;
- *)
- # maybe chutney's test-network.sh can handle it
- ;;
- esac
- shift
-done
+CHUTNEY_PATH=$(echo "$@" | awk -F '--chutney-path ' '{sub(" .*","",$2); print $2}')
+TOR_DIR=$(echo "$@" | awk -F '--tor-dir ' '{sub(" .*","",$2); print $2}')
+
+if echo "$@" | grep -e "--quiet" > /dev/null; then
+ ECHO=true
+fi
# optional: $TOR_DIR is the tor build directory
# it's used to find the location of tor binaries
@@ -52,12 +34,12 @@ done
# - 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/core/or" -a -d "$BUILDDIR/src/tools" ]; then
+ if [ -d "$BUILDDIR/src/core/or" ] && [ -d "$BUILDDIR/src/tools" ]; then
# Choose the build directory
# But only if it looks like one
$ECHO "$myname: \$TOR_DIR not set, trying \$BUILDDIR"
TOR_DIR="$BUILDDIR"
- elif [ -d "$PWD/src/core/or" -a -d "$PWD/src/tools" ]; then
+ elif [ -d "$PWD/src/core/or" ] && [ -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"
@@ -73,12 +55,12 @@ fi
# - 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 [ ! -d "$CHUTNEY_PATH" ] || [ ! -x "$CHUTNEY_PATH/chutney" ]; then
if [ -x "$PWD/chutney" ]; then
$ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$PWD"
CHUTNEY_PATH="$PWD"
- elif [ -d "$TOR_DIR" -a -d "$TOR_DIR/../chutney" -a \
- -x "$TOR_DIR/../chutney/chutney" ]; then
+ elif [ -d "$TOR_DIR" ] && [ -d "$TOR_DIR/../chutney" ] && \
+ [ -x "$TOR_DIR/../chutney/chutney" ]; then
$ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$TOR_DIR/../chutney"
CHUTNEY_PATH="$TOR_DIR/../chutney"
else
@@ -94,12 +76,12 @@ fi
TEST_NETWORK="$CHUTNEY_PATH/tools/test-network.sh"
# Call the chutney version of this script, if it exists, and we can find it
-if [ -d "$CHUTNEY_PATH" -a -x "$TEST_NETWORK" ]; then
+if [ -d "$CHUTNEY_PATH" ] && [ -x "$TEST_NETWORK" ]; then
$ECHO "$myname: Calling newer chutney script $TEST_NETWORK"
# this may fail if some arguments have spaces in them
# if so, set CHUTNEY_PATH before calling test-network.sh, and spaces
# will be handled correctly
- exec "$TEST_NETWORK" $ORIGINAL_ARGS
+ exec "$TEST_NETWORK" "$@"
else
$ECHO "$myname: Could not find tools/test-network.sh in CHUTNEY_PATH."
$ECHO "$myname: Please update your chutney using 'git pull'."
diff --git a/src/test/test.c b/src/test/test.c
index 25e9da5591..cac98dd839 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -12,6 +12,7 @@
#include "lib/crypt_ops/crypto_dh.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "app/config/or_state_st.h"
+#include "test/rng_test_helpers.h"
#include <stdio.h>
#ifdef HAVE_FCNTL_H
@@ -283,7 +284,7 @@ test_fast_handshake(void *arg)
/* 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,
+ tt_assert(! fast_mem_is_zero((char*)client_handshake,
sizeof(client_handshake)));
tt_int_op(0, OP_EQ,
@@ -354,18 +355,6 @@ test_onion_queues(void *arg)
tor_free(onionskin);
}
-static crypto_cipher_t *crypto_rand_aes_cipher = NULL;
-
-// Mock replacement for crypto_rand: Generates bytes from a provided AES_CTR
-// cipher in <b>crypto_rand_aes_cipher</b>.
-static void
-crypto_rand_deterministic_aes(char *out, size_t n)
-{
- tor_assert(crypto_rand_aes_cipher);
- memset(out, 0, n);
- crypto_cipher_crypt_inplace(crypto_rand_aes_cipher, out, n);
-}
-
static void
test_circuit_timeout(void *arg)
{
@@ -397,8 +386,7 @@ test_circuit_timeout(void *arg)
// Use a deterministic RNG here, or else we'll get nondeterministic
// coverage in some of the circuitstats functions.
- MOCK(crypto_rand, crypto_rand_deterministic_aes);
- crypto_rand_aes_cipher = crypto_cipher_new("xyzzyplughplover");
+ testing_enable_deterministic_rng();
circuitbuild_running_unit_tests();
#define timeout0 (build_time_t)(30*1000.0)
@@ -534,8 +522,8 @@ test_circuit_timeout(void *arg)
circuit_build_times_free_timeouts(&final);
or_state_free(state);
teardown_periodic_events();
- UNMOCK(crypto_rand);
- crypto_cipher_free(crypto_rand_aes_cipher);
+
+ testing_disable_deterministic_rng();
}
/** Test encoding and parsing of rendezvous service descriptors. */
@@ -857,6 +845,7 @@ struct testgroup_t testgroups[] = {
{ "consdiff/", consdiff_tests },
{ "consdiffmgr/", consdiffmgr_tests },
{ "container/", container_tests },
+ { "container/namemap/", namemap_tests },
{ "control/", controller_tests },
{ "control/btrack/", btrack_tests },
{ "control/event/", controller_event_tests },
@@ -872,6 +861,7 @@ struct testgroup_t testgroups[] = {
{ "dir/voting/flags/", voting_flags_tests },
{ "dir/voting/schedule/", voting_schedule_tests },
{ "dir_handle_get/", dir_handle_get_tests },
+ { "dispatch/", dispatch_tests, },
{ "dns/", dns_tests },
{ "dos/", dos_tests },
{ "entryconn/", entryconn_tests },
@@ -909,6 +899,8 @@ struct testgroup_t testgroups[] = {
{ "proto/misc/", proto_misc_tests },
{ "protover/", protover_tests },
{ "pt/", pt_tests },
+ { "pubsub/build/", pubsub_build_tests },
+ { "pubsub/msg/", pubsub_msg_tests },
{ "relay/" , relay_tests },
{ "relaycell/", relaycell_tests },
{ "relaycrypt/", relaycrypt_tests },
@@ -919,6 +911,7 @@ struct testgroup_t testgroups[] = {
{ "routerlist/", routerlist_tests },
{ "routerset/" , routerset_tests },
{ "scheduler/", scheduler_tests },
+ { "sendme/", sendme_tests },
{ "shared-random/", sr_tests },
{ "socks/", socks_tests },
{ "status/" , status_tests },
diff --git a/src/test/test.h b/src/test/test.h
index 2564432985..167fd090ac 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -210,6 +210,7 @@ extern struct testcase_t crypto_rng_tests[];
extern struct testcase_t crypto_tests[];
extern struct testcase_t dir_handle_get_tests[];
extern struct testcase_t dir_tests[];
+extern struct testcase_t dispatch_tests[];
extern struct testcase_t dns_tests[];
extern struct testcase_t dos_tests[];
extern struct testcase_t entryconn_tests[];
@@ -235,6 +236,7 @@ extern struct testcase_t link_handshake_tests[];
extern struct testcase_t logging_tests[];
extern struct testcase_t mainloop_tests[];
extern struct testcase_t microdesc_tests[];
+extern struct testcase_t namemap_tests[];
extern struct testcase_t netinfo_tests[];
extern struct testcase_t nodelist_tests[];
extern struct testcase_t oom_tests[];
@@ -252,6 +254,8 @@ extern struct testcase_t proto_http_tests[];
extern struct testcase_t proto_misc_tests[];
extern struct testcase_t protover_tests[];
extern struct testcase_t pt_tests[];
+extern struct testcase_t pubsub_build_tests[];
+extern struct testcase_t pubsub_msg_tests[];
extern struct testcase_t relay_tests[];
extern struct testcase_t relaycell_tests[];
extern struct testcase_t relaycrypt_tests[];
@@ -262,6 +266,7 @@ 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 sendme_tests[];
extern struct testcase_t socks_tests[];
extern struct testcase_t sr_tests[];
extern struct testcase_t status_tests[];
@@ -278,6 +283,7 @@ extern struct testcase_t x509_tests[];
extern struct testcase_t slow_crypto_tests[];
extern struct testcase_t slow_process_tests[];
+extern struct testcase_t slow_ptr_tests[];
extern struct testgroup_t testgroups[];
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index fb8df5f0fb..05d8bf6c7b 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -11,6 +11,7 @@
#include "feature/client/addressmap.h"
#include "test/log_test_helpers.h"
#include "lib/net/resolve.h"
+#include "test/rng_test_helpers.h"
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
@@ -239,7 +240,7 @@ test_addr_ip6_helpers(void *arg)
tt_int_op(0,OP_EQ, tor_addr_lookup("9000::5", AF_UNSPEC, &t1));
tt_int_op(AF_INET6,OP_EQ, tor_addr_family(&t1));
tt_int_op(0x90,OP_EQ, tor_addr_to_in6_addr8(&t1)[0]);
- tt_assert(tor_mem_is_zero((char*)tor_addr_to_in6_addr8(&t1)+1, 14));
+ tt_assert(fast_mem_is_zero((char*)tor_addr_to_in6_addr8(&t1)+1, 14));
tt_int_op(0x05,OP_EQ, tor_addr_to_in6_addr8(&t1)[15]);
/* === Test pton: valid af_inet6 */
@@ -696,7 +697,7 @@ test_addr_ip6_helpers(void *arg)
&t1,&mask,&port1,&port2);
tt_int_op(r,OP_EQ,AF_INET6);
tt_int_op(tor_addr_family(&t1),OP_EQ,AF_INET6);
- tt_assert(tor_mem_is_zero((const char*)tor_addr_to_in6_addr32(&t1), 16));
+ tt_assert(fast_mem_is_zero((const char*)tor_addr_to_in6_addr32(&t1), 16));
tt_int_op(mask,OP_EQ,0);
tt_int_op(port1,OP_EQ,1);
tt_int_op(port2,OP_EQ,65535);
@@ -945,27 +946,6 @@ test_virtaddrmap(void *data)
;
}
-static const char *canned_data = NULL;
-static size_t canned_data_len = 0;
-
-/* Mock replacement for crypto_rand() that returns canned data from
- * canned_data above. */
-static void
-crypto_canned(char *ptr, size_t n)
-{
- if (canned_data_len) {
- size_t to_copy = MIN(n, canned_data_len);
- memcpy(ptr, canned_data, to_copy);
- canned_data += to_copy;
- canned_data_len -= to_copy;
- n -= to_copy;
- ptr += to_copy;
- }
- if (n) {
- crypto_rand_unmocked(ptr, n);
- }
-}
-
static void
test_virtaddrmap_persist(void *data)
{
@@ -973,6 +953,8 @@ test_virtaddrmap_persist(void *data)
const char *a, *b, *c;
tor_addr_t addr;
char *ones = NULL;
+ const char *canned_data;
+ size_t canned_data_len;
addressmap_init();
@@ -991,7 +973,7 @@ test_virtaddrmap_persist(void *data)
"1234567890" // the second call returns this.
"abcdefghij"; // the third call returns this.
canned_data_len = 30;
- MOCK(crypto_rand, crypto_canned);
+ testing_enable_prefilled_rng(canned_data, canned_data_len);
a = addressmap_register_virtual_address(RESOLVED_TYPE_HOSTNAME,
tor_strdup("quuxit.baz"));
@@ -1001,9 +983,9 @@ test_virtaddrmap_persist(void *data)
tt_assert(b);
tt_str_op(a, OP_EQ, "gezdgnbvgy3tqojq.virtual");
tt_str_op(b, OP_EQ, "mfrggzdfmztwq2lk.virtual");
+ testing_disable_prefilled_rng();
// Now try something to get us an ipv4 address
- UNMOCK(crypto_rand);
tt_int_op(0,OP_EQ, parse_virtual_addr_network("192.168.0.0/16",
AF_INET, 0, NULL));
a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
@@ -1020,22 +1002,23 @@ test_virtaddrmap_persist(void *data)
// Try some canned entropy and verify all the we discard duplicates,
// addresses that end with 0, and addresses that end with 255.
- MOCK(crypto_rand, crypto_canned);
canned_data = "\x01\x02\x03\x04" // okay
"\x01\x02\x03\x04" // duplicate
"\x03\x04\x00\x00" // bad ending 1
"\x05\x05\x00\xff" // bad ending 2
"\x05\x06\x07\xf0"; // okay
canned_data_len = 20;
+ testing_enable_prefilled_rng(canned_data, canned_data_len);
+
a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
tor_strdup("wumble.onion"));
b = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
tor_strdup("wumpus.onion"));
tt_str_op(a, OP_EQ, "192.168.3.4");
tt_str_op(b, OP_EQ, "192.168.7.240");
+ testing_disable_prefilled_rng();
// Now try IPv6!
- UNMOCK(crypto_rand);
tt_int_op(0,OP_EQ, parse_virtual_addr_network("1010:F000::/20",
AF_INET6, 0, NULL));
a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
@@ -1051,7 +1034,7 @@ test_virtaddrmap_persist(void *data)
tt_assert(!strcmpstart(b, "[1010:f"));
// Try IPv6 with canned entropy, to make sure we detect duplicates.
- MOCK(crypto_rand, crypto_canned);
+
canned_data = "acanthopterygian" // okay
"cinematographist" // okay
"acanthopterygian" // duplicate
@@ -1060,6 +1043,8 @@ test_virtaddrmap_persist(void *data)
"cinematographist" // duplicate
"coadministration"; // okay
canned_data_len = 16 * 7;
+ testing_enable_prefilled_rng(canned_data, canned_data_len);
+
a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
tor_strdup("wuffle.baz"));
b = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
@@ -1072,9 +1057,11 @@ test_virtaddrmap_persist(void *data)
// Try address exhaustion: make sure we can actually fail if we
// get too many already-existing addresses.
+ testing_disable_prefilled_rng();
canned_data_len = 128*1024;
canned_data = ones = tor_malloc(canned_data_len);
memset(ones, 1, canned_data_len);
+ testing_enable_prefilled_rng(canned_data, canned_data_len);
// There is some chance this one will fail if a previous random
// allocation gave out the address already.
a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
@@ -1091,7 +1078,7 @@ test_virtaddrmap_persist(void *data)
expect_single_log_msg_containing("Ran out of virtual addresses!");
done:
- UNMOCK(crypto_rand);
+ testing_disable_prefilled_rng();
tor_free(ones);
addressmap_free_all();
teardown_capture_of_logs();
diff --git a/src/test/test_bt.sh b/src/test/test_bt.sh
index df8bcb8eda..312905a4e2 100755
--- a/src/test/test_bt.sh
+++ b/src/test/test_bt.sh
@@ -3,8 +3,6 @@
exitcode=0
-ulimit -c 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 2>&1 | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?"
diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c
index 0c15a02ee4..b29c2c6cbc 100644
--- a/src/test/test_bt_cl.c
+++ b/src/test/test_bt_cl.c
@@ -4,6 +4,9 @@
#include "orconfig.h"
#include <stdio.h>
#include <stdlib.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
/* To prevent 'assert' from going away. */
#undef TOR_COVERAGE
@@ -43,7 +46,7 @@ crash(int x)
*(volatile int *)0 = 0;
#endif /* defined(__clang_analyzer__) || defined(__COVERITY__) */
} else if (crashtype == 1) {
- tor_assert(1 == 0);
+ tor_assertf(1 == 0, "%d != %d", 1, 0);
} else if (crashtype == -1) {
;
}
@@ -88,6 +91,11 @@ main(int argc, char **argv)
return 1;
}
+#ifdef HAVE_SYS_RESOURCE_H
+ struct rlimit rlim = { .rlim_cur = 0, .rlim_max = 0 };
+ setrlimit(RLIMIT_CORE, &rlim);
+#endif
+
#if !(defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \
defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION))
puts("Backtrace reporting is not supported on this platform");
diff --git a/src/test/test_channel.c b/src/test/test_channel.c
index e55b9b0750..6a6bc9d810 100644
--- a/src/test/test_channel.c
+++ b/src/test/test_channel.c
@@ -598,7 +598,6 @@ test_channel_outbound_cell(void *arg)
circuit_set_n_circid_chan(TO_CIRCUIT(circ), 42, chan);
tt_int_op(channel_num_circuits(chan), OP_EQ, 1);
/* Test the cmux state. */
- tt_ptr_op(TO_CIRCUIT(circ)->n_mux, OP_EQ, chan->cmux);
tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)),
OP_EQ, 1);
@@ -1541,6 +1540,10 @@ test_channel_listener(void *arg)
channel_listener_dump_statistics(chan, LOG_INFO);
done:
+ if (chan) {
+ channel_listener_unregister(chan);
+ tor_free(chan);
+ }
channel_free_all();
}
@@ -1567,4 +1570,3 @@ struct testcase_t channel_tests[] = {
NULL, NULL },
END_OF_TESTCASES
};
-
diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c
index ccbaa02552..0c23091594 100644
--- a/src/test/test_circuitbuild.c
+++ b/src/test/test_circuitbuild.c
@@ -27,7 +27,7 @@ static smartlist_t dummy_nodes;
static extend_info_t dummy_ei;
static int
-mock_count_acceptable_nodes(smartlist_t *nodes, int direct)
+mock_count_acceptable_nodes(const smartlist_t *nodes, int direct)
{
(void)nodes;
diff --git a/src/test/test_circuitpadding.c b/src/test/test_circuitpadding.c
index 09a4c9a0ca..25f8fd311b 100644
--- a/src/test/test_circuitpadding.c
+++ b/src/test/test_circuitpadding.c
@@ -1,14 +1,17 @@
#define TOR_CHANNEL_INTERNAL_
#define TOR_TIMERS_PRIVATE
#define CIRCUITPADDING_PRIVATE
+#define CIRCUITPADDING_MACHINES_PRIVATE
#define NETWORKSTATUS_PRIVATE
+#define CRYPT_PATH_PRIVATE
#include "core/or/or.h"
-#include "test.h"
+#include "test/test.h"
#include "lib/testsupport/testsupport.h"
#include "core/or/connection_or.h"
#include "core/or/channel.h"
#include "core/or/channeltls.h"
+#include "core/or/crypt_path.h"
#include <event.h>
#include "lib/evloop/compat_libevent.h"
#include "lib/time/compat_time.h"
@@ -17,6 +20,8 @@
#include "core/or/circuitlist.h"
#include "core/or/circuitbuild.h"
#include "core/or/circuitpadding.h"
+#include "core/or/circuitpadding_machines.h"
+#include "core/mainloop/netstatus.h"
#include "core/crypto/relay_crypto.h"
#include "core/or/protover.h"
#include "feature/nodelist/nodelist.h"
@@ -31,6 +36,8 @@
#include "core/or/or_circuit_st.h"
#include "core/or/origin_circuit_st.h"
+#include "test/rng_test_helpers.h"
+
/* Start our monotime mocking at 1 second past whatever monotime_init()
* thought the actual wall clock time was, for platforms with bad resolution
* and weird timevalues during monotime_init() before mocking. */
@@ -38,6 +45,7 @@
TOR_NSEC_PER_USEC*TOR_USEC_PER_SEC)
extern smartlist_t *connection_array;
+void circuit_expire_old_circuits_clientside(void);
circid_t get_unique_circ_id_by_chan(channel_t *chan);
void helper_create_basic_machine(void);
@@ -52,6 +60,7 @@ void test_circuitpadding_conditions(void *arg);
void test_circuitpadding_serialize(void *arg);
void test_circuitpadding_rtt(void *arg);
void test_circuitpadding_tokens(void *arg);
+void test_circuitpadding_state_length(void *arg);
static void
simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay,
@@ -81,10 +90,10 @@ static void
nodes_init(void)
{
padding_node.rs = tor_malloc_zero(sizeof(routerstatus_t));
- padding_node.rs->pv.supports_padding = 1;
+ padding_node.rs->pv.supports_hs_setup_padding = 1;
non_padding_node.rs = tor_malloc_zero(sizeof(routerstatus_t));
- non_padding_node.rs->pv.supports_padding = 0;
+ non_padding_node.rs->pv.supports_hs_setup_padding = 0;
}
static void
@@ -107,6 +116,15 @@ node_get_by_id_mock(const char *identity_digest)
return NULL;
}
+static const node_t *
+circuit_get_nth_node_mock(origin_circuit_t *circ, int hop)
+{
+ (void) circ;
+ (void) hop;
+
+ return &padding_node;
+}
+
static or_circuit_t *
new_fake_orcirc(channel_t *nchan, channel_t *pchan)
{
@@ -121,7 +139,6 @@ new_fake_orcirc(channel_t *nchan, channel_t *pchan)
//circ->n_chan = nchan;
circ->n_circ_id = get_unique_circ_id_by_chan(nchan);
- circ->n_mux = NULL; /* ?? */
cell_queue_init(&(circ->n_chan_cells));
circ->n_hop = NULL;
circ->streams_blocked_on_n_chan = 0;
@@ -143,12 +160,12 @@ new_fake_orcirc(channel_t *nchan, channel_t *pchan)
circuit_set_n_circid_chan(circ, circ->n_circ_id, nchan);
memset(&tmp_cpath, 0, sizeof(tmp_cpath));
- if (circuit_init_cpath_crypto(&tmp_cpath, whatevs_key,
+ if (cpath_init_circuit_crypto(&tmp_cpath, whatevs_key,
sizeof(whatevs_key), 0, 0)<0) {
log_warn(LD_BUG,"Circuit initialization failed");
return NULL;
}
- orcirc->crypto = tmp_cpath.crypto;
+ orcirc->crypto = tmp_cpath.pvt_crypto;
return orcirc;
}
@@ -298,6 +315,7 @@ test_circuitpadding_rtt(void *arg)
MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
MOCK(circpad_send_command_to_hop, circpad_send_command_to_hop_mock);
+ testing_enable_reproducible_rng();
dummy_channel.cmux = circuitmux_alloc();
relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
@@ -327,12 +345,12 @@ test_circuitpadding_rtt(void *arg)
relay_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side,0);
/* Test 1: Test measuring RTT */
- circpad_cell_event_nonpadding_received((circuit_t*)relay_side);
+ circpad_cell_event_nonpadding_received(relay_side);
tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_NE, 0);
timers_advance_and_run(20);
- circpad_cell_event_nonpadding_sent((circuit_t*)relay_side);
+ circpad_cell_event_nonpadding_sent(relay_side);
tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_EQ, 0);
tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_GE, 19000);
@@ -341,14 +359,14 @@ test_circuitpadding_rtt(void *arg)
OP_EQ,
relay_side->padding_info[0]->rtt_estimate_usec+
circpad_machine_current_state(
- relay_side->padding_info[0])->start_usec);
+ relay_side->padding_info[0])->histogram_edges[0]);
- circpad_cell_event_nonpadding_received((circuit_t*)relay_side);
- circpad_cell_event_nonpadding_received((circuit_t*)relay_side);
+ circpad_cell_event_nonpadding_received(relay_side);
+ circpad_cell_event_nonpadding_received(relay_side);
tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_NE, 0);
timers_advance_and_run(20);
- circpad_cell_event_nonpadding_sent((circuit_t*)relay_side);
- circpad_cell_event_nonpadding_sent((circuit_t*)relay_side);
+ circpad_cell_event_nonpadding_sent(relay_side);
+ circpad_cell_event_nonpadding_sent(relay_side);
tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_EQ, 0);
tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_GE, 20000);
@@ -357,15 +375,15 @@ test_circuitpadding_rtt(void *arg)
OP_EQ,
relay_side->padding_info[0]->rtt_estimate_usec+
circpad_machine_current_state(
- relay_side->padding_info[0])->start_usec);
+ relay_side->padding_info[0])->histogram_edges[0]);
/* Test 2: Termination of RTT measurement (from the previous test) */
tt_int_op(relay_side->padding_info[0]->stop_rtt_update, OP_EQ, 1);
rtt_estimate = relay_side->padding_info[0]->rtt_estimate_usec;
- circpad_cell_event_nonpadding_received((circuit_t*)relay_side);
+ circpad_cell_event_nonpadding_received(relay_side);
timers_advance_and_run(4);
- circpad_cell_event_nonpadding_sent((circuit_t*)relay_side);
+ circpad_cell_event_nonpadding_sent(relay_side);
tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_EQ,
rtt_estimate);
@@ -375,14 +393,14 @@ test_circuitpadding_rtt(void *arg)
OP_EQ,
relay_side->padding_info[0]->rtt_estimate_usec+
circpad_machine_current_state(
- relay_side->padding_info[0])->start_usec);
+ relay_side->padding_info[0])->histogram_edges[0]);
/* Test 3: Make sure client side machine properly ignores RTT */
- circpad_cell_event_nonpadding_received((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_received(client_side);
tt_u64_op(client_side->padding_info[0]->last_received_time_usec, OP_EQ, 0);
timers_advance_and_run(20);
- circpad_cell_event_nonpadding_sent((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_u64_op(client_side->padding_info[0]->last_received_time_usec, OP_EQ, 0);
tt_int_op(client_side->padding_info[0]->rtt_estimate_usec, OP_EQ, 0);
@@ -391,7 +409,7 @@ test_circuitpadding_rtt(void *arg)
tt_int_op(circpad_histogram_bin_to_usec(client_side->padding_info[0], 0),
OP_EQ,
circpad_machine_current_state(
- client_side->padding_info[0])->start_usec);
+ client_side->padding_info[0])->histogram_edges[0]);
done:
free_fake_orcirc(relay_side);
circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
@@ -401,6 +419,7 @@ test_circuitpadding_rtt(void *arg)
UNMOCK(circuit_package_relay_cell);
UNMOCK(circuitmux_attach_circuit);
tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
return;
}
@@ -411,8 +430,11 @@ helper_create_basic_machine(void)
/* Start, burst */
circpad_machine_states_init(&circ_client_machine, 2);
+ circ_client_machine.name = "basic";
+
circ_client_machine.states[CIRCPAD_STATE_START].
next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_BURST;
+ circ_client_machine.states[CIRCPAD_STATE_START].use_rtt_estimate = 1;
circ_client_machine.states[CIRCPAD_STATE_BURST].
next_state[CIRCPAD_EVENT_PADDING_RECV] = CIRCPAD_STATE_BURST;
@@ -422,19 +444,23 @@ helper_create_basic_machine(void)
circ_client_machine.states[CIRCPAD_STATE_BURST].
next_state[CIRCPAD_EVENT_NONPADDING_SENT] = CIRCPAD_STATE_CANCEL;
- // FIXME: Is this what we want?
circ_client_machine.states[CIRCPAD_STATE_BURST].token_removal =
CIRCPAD_TOKEN_REMOVAL_HIGHER;
- // FIXME: Tune this histogram
circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_len = 5;
- circ_client_machine.states[CIRCPAD_STATE_BURST].start_usec = 500;
- circ_client_machine.states[CIRCPAD_STATE_BURST].range_usec = 1000000;
+
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 500;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[1] = 2500;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[2] = 5000;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[3] = 10000;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[4] = 20000;
+
circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[0] = 1;
circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[1] = 0;
circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[2] = 2;
circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[3] = 2;
circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[4] = 2;
+
circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_total_tokens = 7;
circ_client_machine.states[CIRCPAD_STATE_BURST].use_rtt_estimate = 1;
@@ -466,15 +492,25 @@ helper_create_machine_with_big_histogram(circpad_removal_t removal_strategy)
burst_state->token_removal = CIRCPAD_TOKEN_REMOVAL_HIGHER;
burst_state->histogram_len = BIG_HISTOGRAM_LEN;
- burst_state->start_usec = 0;
- burst_state->range_usec = 1000;
int n_tokens = 0;
- for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ int i;
+ for (i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
burst_state->histogram[i] = tokens_per_bin;
n_tokens += tokens_per_bin;
}
+ burst_state->histogram_edges[0] = 0;
+ burst_state->histogram_edges[1] = 1;
+ burst_state->histogram_edges[2] = 7;
+ burst_state->histogram_edges[3] = 15;
+ burst_state->histogram_edges[4] = 31;
+ burst_state->histogram_edges[5] = 62;
+ burst_state->histogram_edges[6] = 125;
+ burst_state->histogram_edges[7] = 250;
+ burst_state->histogram_edges[8] = 500;
+ burst_state->histogram_edges[9] = 1000;
+
burst_state->histogram_total_tokens = n_tokens;
burst_state->length_dist.type = CIRCPAD_DIST_UNIFORM;
burst_state->length_dist.param1 = n_tokens;
@@ -486,7 +522,7 @@ helper_create_machine_with_big_histogram(circpad_removal_t removal_strategy)
}
static circpad_decision_t
-circpad_machine_schedule_padding_mock(circpad_machine_state_t *mi)
+circpad_machine_schedule_padding_mock(circpad_machine_runtime_t *mi)
{
(void)mi;
return 0;
@@ -502,15 +538,16 @@ mock_monotime_absolute_usec(void)
static void
test_circuitpadding_token_removal_higher(void *arg)
{
- circpad_machine_state_t *mi;
+ circpad_machine_runtime_t *mi;
(void)arg;
/* Mock it up */
MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
/* Setup test environment (time etc.) */
- client_side = (circuit_t *)origin_circuit_new();
+ client_side = TO_CIRCUIT(origin_circuit_new());
client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
monotime_enable_test_mocking();
@@ -521,7 +558,7 @@ test_circuitpadding_token_removal_higher(void *arg)
circpad_circuit_machineinfo_new(client_side, 0);
/* move the machine to the right state */
- circpad_cell_event_nonpadding_received((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_received(client_side);
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
@@ -535,12 +572,20 @@ test_circuitpadding_token_removal_higher(void *arg)
/* Test left boundaries of each histogram bin: */
const circpad_delay_t bin_left_bounds[] =
- {0, 1, 7, 15, 31, 62, 125, 250, 500, CIRCPAD_DELAY_INFINITE};
- for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE};
+ for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) {
tt_uint_op(bin_left_bounds[i], OP_EQ,
circpad_histogram_bin_to_usec(mi, i));
}
+ /* Test right boundaries of each histogram bin: */
+ const circpad_delay_t bin_right_bounds[] =
+ {0, 6, 14, 30, 61, 124, 249, 499, 999, CIRCPAD_DELAY_INFINITE-1};
+ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ tt_uint_op(bin_right_bounds[i], OP_EQ,
+ histogram_get_bin_upper_bound(mi, i));
+ }
+
/* Check that all bins have two tokens right now */
for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
tt_int_op(mi->histogram[i], OP_EQ, 2);
@@ -562,12 +607,12 @@ test_circuitpadding_token_removal_higher(void *arg)
tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2);
mi->padding_scheduled_at_usec = current_time - 57;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1);
mi->padding_scheduled_at_usec = current_time - 57;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
/* Test that we cleaned out this bin. Don't do this in the case of the last
bin since the tokens will get refilled */
@@ -584,30 +629,32 @@ test_circuitpadding_token_removal_higher(void *arg)
/* Test below the lowest bin, for coverage */
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
- circ_client_machine.states[CIRCPAD_STATE_BURST].start_usec = 100;
- mi->padding_scheduled_at_usec = current_time - 1;
- circpad_machine_remove_token(mi);
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 100;
+ mi->padding_scheduled_at_usec = current_time;
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[0], OP_EQ, 1);
done:
free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
monotime_disable_test_mocking();
tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
}
/** Test lower token removal strategy by bin */
static void
test_circuitpadding_token_removal_lower(void *arg)
{
- circpad_machine_state_t *mi;
+ circpad_machine_runtime_t *mi;
(void)arg;
/* Mock it up */
MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
/* Setup test environment (time etc.) */
- client_side = (circuit_t *)origin_circuit_new();
+ client_side = TO_CIRCUIT(origin_circuit_new());
client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
monotime_enable_test_mocking();
@@ -618,7 +665,7 @@ test_circuitpadding_token_removal_lower(void *arg)
circpad_circuit_machineinfo_new(client_side, 0);
/* move the machine to the right state */
- circpad_cell_event_nonpadding_received((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_received(client_side);
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
@@ -632,8 +679,8 @@ test_circuitpadding_token_removal_lower(void *arg)
/* Test left boundaries of each histogram bin: */
const circpad_delay_t bin_left_bounds[] =
- {0, 1, 7, 15, 31, 62, 125, 250, 500, CIRCPAD_DELAY_INFINITE};
- for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE};
+ for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) {
tt_uint_op(bin_left_bounds[i], OP_EQ,
circpad_histogram_bin_to_usec(mi, i));
}
@@ -659,12 +706,12 @@ test_circuitpadding_token_removal_lower(void *arg)
tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2);
mi->padding_scheduled_at_usec = current_time - 57;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1);
mi->padding_scheduled_at_usec = current_time - 57;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
/* Test that we cleaned out this bin. Don't do this in the case of the last
bin since the tokens will get refilled */
@@ -681,30 +728,33 @@ test_circuitpadding_token_removal_lower(void *arg)
/* Test above the highest bin, for coverage */
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
- circ_client_machine.states[CIRCPAD_STATE_BURST].start_usec = 100;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].
+ histogram_edges[BIG_HISTOGRAM_LEN-2] = 100;
mi->padding_scheduled_at_usec = current_time - 29202;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[BIG_HISTOGRAM_LEN-2], OP_EQ, 1);
done:
free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
monotime_disable_test_mocking();
tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
}
/** Test closest token removal strategy by bin */
static void
test_circuitpadding_closest_token_removal(void *arg)
{
- circpad_machine_state_t *mi;
+ circpad_machine_runtime_t *mi;
(void)arg;
/* Mock it up */
MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
/* Setup test environment (time etc.) */
- client_side = (circuit_t *)origin_circuit_new();
+ client_side = TO_CIRCUIT(origin_circuit_new());
client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
monotime_enable_test_mocking();
@@ -715,7 +765,7 @@ test_circuitpadding_closest_token_removal(void *arg)
circpad_circuit_machineinfo_new(client_side, 0);
/* move the machine to the right state */
- circpad_cell_event_nonpadding_received((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_received(client_side);
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
@@ -729,8 +779,8 @@ test_circuitpadding_closest_token_removal(void *arg)
/* Test left boundaries of each histogram bin: */
const circpad_delay_t bin_left_bounds[] =
- {0, 1, 7, 15, 31, 62, 125, 250, 500, CIRCPAD_DELAY_INFINITE};
- for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE};
+ for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) {
tt_uint_op(bin_left_bounds[i], OP_EQ,
circpad_histogram_bin_to_usec(mi, i));
}
@@ -755,12 +805,12 @@ test_circuitpadding_closest_token_removal(void *arg)
tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2);
mi->padding_scheduled_at_usec = current_time - 57;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1);
mi->padding_scheduled_at_usec = current_time - 57;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
/* Test that we cleaned out this bin. Don't do this in the case of the last
bin since the tokens will get refilled */
@@ -777,39 +827,42 @@ test_circuitpadding_closest_token_removal(void *arg)
/* Test below the lowest bin, for coverage */
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
- circ_client_machine.states[CIRCPAD_STATE_BURST].start_usec = 100;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 100;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[1] = 101;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[2] = 120;
mi->padding_scheduled_at_usec = current_time - 102;
mi->histogram[0] = 0;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[1], OP_EQ, 1);
/* Test above the highest bin, for coverage */
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
- circ_client_machine.states[CIRCPAD_STATE_BURST].start_usec = 100;
mi->padding_scheduled_at_usec = current_time - 29202;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[BIG_HISTOGRAM_LEN-2], OP_EQ, 1);
done:
free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
monotime_disable_test_mocking();
tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
}
/** Test closest token removal strategy with usec */
static void
test_circuitpadding_closest_token_removal_usec(void *arg)
{
- circpad_machine_state_t *mi;
+ circpad_machine_runtime_t *mi;
(void)arg;
/* Mock it up */
MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
/* Setup test environment (time etc.) */
- client_side = (circuit_t *)origin_circuit_new();
+ client_side = TO_CIRCUIT(origin_circuit_new());
client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
monotime_enable_test_mocking();
@@ -820,7 +873,7 @@ test_circuitpadding_closest_token_removal_usec(void *arg)
circpad_circuit_machineinfo_new(client_side, 0);
/* move the machine to the right state */
- circpad_cell_event_nonpadding_received((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_received(client_side);
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
@@ -834,8 +887,8 @@ test_circuitpadding_closest_token_removal_usec(void *arg)
/* Test left boundaries of each histogram bin: */
const circpad_delay_t bin_left_bounds[] =
- {0, 1, 7, 15, 31, 62, 125, 250, 500, CIRCPAD_DELAY_INFINITE};
- for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE};
+ for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) {
tt_uint_op(bin_left_bounds[i], OP_EQ,
circpad_histogram_bin_to_usec(mi, i));
}
@@ -863,12 +916,12 @@ test_circuitpadding_closest_token_removal_usec(void *arg)
tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2);
mi->padding_scheduled_at_usec = current_time - 57;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1);
mi->padding_scheduled_at_usec = current_time - 57;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
/* Test that we cleaned out this bin. Don't do this in the case of the last
bin since the tokens will get refilled */
@@ -885,39 +938,44 @@ test_circuitpadding_closest_token_removal_usec(void *arg)
/* Test below the lowest bin, for coverage */
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
- circ_client_machine.states[CIRCPAD_STATE_BURST].start_usec = 100;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 100;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[1] = 101;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[2] = 120;
mi->padding_scheduled_at_usec = current_time - 102;
mi->histogram[0] = 0;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[1], OP_EQ, 1);
/* Test above the highest bin, for coverage */
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
- circ_client_machine.states[CIRCPAD_STATE_BURST].start_usec = 100;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].
+ histogram_edges[BIG_HISTOGRAM_LEN-2] = 100;
mi->padding_scheduled_at_usec = current_time - 29202;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[BIG_HISTOGRAM_LEN-2], OP_EQ, 1);
done:
free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
monotime_disable_test_mocking();
tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
}
/** Test closest token removal strategy with usec */
static void
test_circuitpadding_token_removal_exact(void *arg)
{
- circpad_machine_state_t *mi;
+ circpad_machine_runtime_t *mi;
(void)arg;
/* Mock it up */
MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
/* Setup test environment (time etc.) */
- client_side = (circuit_t *)origin_circuit_new();
+ client_side = TO_CIRCUIT(origin_circuit_new());
client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
monotime_enable_test_mocking();
@@ -928,7 +986,7 @@ test_circuitpadding_token_removal_exact(void *arg)
circpad_circuit_machineinfo_new(client_side, 0);
/* move the machine to the right state */
- circpad_cell_event_nonpadding_received((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_received(client_side);
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_BURST);
@@ -942,16 +1000,16 @@ test_circuitpadding_token_removal_exact(void *arg)
/* Ensure that we will clear out bin #4 with this usec */
mi->padding_scheduled_at_usec = current_time - 57;
tt_int_op(mi->histogram[4], OP_EQ, 2);
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
mi->padding_scheduled_at_usec = current_time - 57;
tt_int_op(mi->histogram[4], OP_EQ, 1);
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
tt_int_op(mi->histogram[4], OP_EQ, 0);
/* Ensure that we will not remove any other tokens even tho we try to, since
* this is what the exact strategy dictates */
mi->padding_scheduled_at_usec = current_time - 57;
- circpad_machine_remove_token(mi);
+ circpad_cell_event_nonpadding_sent(client_side);
for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
if (i != 4) {
tt_int_op(mi->histogram[i], OP_EQ, 2);
@@ -962,6 +1020,7 @@ test_circuitpadding_token_removal_exact(void *arg)
free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
monotime_disable_test_mocking();
tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
}
#undef BIG_HISTOGRAM_LEN
@@ -970,10 +1029,12 @@ void
test_circuitpadding_tokens(void *arg)
{
const circpad_state_t *state;
- circpad_machine_state_t *mi;
+ circpad_machine_runtime_t *mi;
int64_t actual_mocked_monotime_start;
(void)arg;
+ testing_enable_reproducible_rng();
+
/** Test plan:
*
* 1. Test symmetry between bin_to_usec and usec_to_bin
@@ -1004,6 +1065,9 @@ test_circuitpadding_tokens(void *arg)
monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
curr_mocked_time = actual_mocked_monotime_start;
+ /* This is needed so that we are not considered to be dormant */
+ note_user_activity(20);
+
timers_initialize();
helper_create_basic_machine();
@@ -1014,8 +1078,8 @@ test_circuitpadding_tokens(void *arg)
mi = client_side->padding_info[0];
// Pretend a non-padding cell was sent
- circpad_cell_event_nonpadding_received((circuit_t*)client_side);
- circpad_cell_event_nonpadding_sent((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_received(client_side);
+ circpad_cell_event_nonpadding_sent(client_side);
/* We have to save the infinity bin because one inf delay
* could have been chosen when we transition to burst */
circpad_hist_token_t inf_bin = mi->histogram[4];
@@ -1052,7 +1116,7 @@ test_circuitpadding_tokens(void *arg)
// Test 1: converting usec->bin->usec->bin
// Bin 0+1 have different semantics.
- for (circpad_delay_t i = 0; i <= state->start_usec+1; i++) {
+ for (circpad_delay_t i = 0; i <= state->histogram_edges[0]; i++) {
int bin = circpad_histogram_usec_to_bin(client_side->padding_info[0],
i);
circpad_delay_t usec =
@@ -1062,8 +1126,9 @@ test_circuitpadding_tokens(void *arg)
tt_int_op(bin, OP_EQ, bin2);
tt_int_op(i, OP_LE, usec);
}
- for (circpad_delay_t i = state->start_usec+1;
- i <= state->start_usec + state->range_usec; i++) {
+ for (circpad_delay_t i = state->histogram_edges[0]+1;
+ i <= state->histogram_edges[0] +
+ state->histogram_edges[state->histogram_len-2]; i++) {
int bin = circpad_histogram_usec_to_bin(client_side->padding_info[0],
i);
circpad_delay_t usec =
@@ -1116,19 +1181,18 @@ test_circuitpadding_tokens(void *arg)
{
tt_int_op(mi->histogram[0], OP_EQ, 0);
mi->histogram[0] = 1;
- circpad_machine_remove_higher_token(mi,
- state->start_usec/2);
+ circpad_machine_remove_higher_token(mi, state->histogram_edges[0]/2);
tt_int_op(mi->histogram[0], OP_EQ, 0);
}
/* Drain the infinity bin and cause a refill */
while (inf_bin != 0) {
tt_int_op(mi->histogram[4], OP_EQ, inf_bin);
- circpad_cell_event_nonpadding_received((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_received(client_side);
inf_bin--;
}
- circpad_cell_event_nonpadding_sent((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_sent(client_side);
// We should have refilled here.
tt_int_op(mi->histogram[4], OP_EQ, 2);
@@ -1136,8 +1200,7 @@ test_circuitpadding_tokens(void *arg)
/* 3.a. Bin 0 */
{
tt_int_op(mi->histogram[0], OP_EQ, 1);
- circpad_machine_remove_higher_token(mi,
- state->start_usec/2);
+ circpad_machine_remove_higher_token(mi, state->histogram_edges[0]/2);
tt_int_op(mi->histogram[0], OP_EQ, 0);
}
@@ -1225,6 +1288,7 @@ test_circuitpadding_tokens(void *arg)
free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
monotime_disable_test_mocking();
tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
}
void
@@ -1252,11 +1316,12 @@ test_circuitpadding_wronghop(void *arg)
/* Mock this function so that our cell counting tests don't get confused by
* padding that gets sent by scheduled timers. */
MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
- client_side = (circuit_t *)origin_circuit_new();
+ client_side = TO_CIRCUIT(origin_circuit_new());
dummy_channel.cmux = circuitmux_alloc();
- relay_side = (circuit_t *)new_fake_orcirc(&dummy_channel,
- &dummy_channel);
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel,
+ &dummy_channel));
orig_client = TO_ORIGIN_CIRCUIT(client_side);
relay_side->purpose = CIRCUIT_PURPOSE_OR;
@@ -1374,9 +1439,9 @@ test_circuitpadding_wronghop(void *arg)
free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
free_fake_orcirc(relay_side);
- client_side = (circuit_t *)origin_circuit_new();
- relay_side = (circuit_t *)new_fake_orcirc(&dummy_channel,
- &dummy_channel);
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel,
+ &dummy_channel));
relay_side->purpose = CIRCUIT_PURPOSE_OR;
client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
@@ -1425,6 +1490,7 @@ test_circuitpadding_wronghop(void *arg)
UNMOCK(circuit_package_relay_cell);
UNMOCK(circuitmux_attach_circuit);
nodes_free();
+ testing_disable_reproducible_rng();
}
void
@@ -1570,10 +1636,10 @@ simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay,
tor_addr_t addr;
// Pretend a non-padding cell was sent
- circpad_cell_event_nonpadding_sent((circuit_t*)client);
+ circpad_cell_event_nonpadding_sent(client);
// Receive extend cell at middle
- circpad_cell_event_nonpadding_received((circuit_t*)mid_relay);
+ circpad_cell_event_nonpadding_received(mid_relay);
// Advance time a tiny bit so we can calculate an RTT
curr_mocked_time += 10 * TOR_NSEC_PER_MSEC;
@@ -1581,14 +1647,14 @@ simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay,
monotime_set_mock_time_nsec(curr_mocked_time);
// Receive extended cell at middle
- circpad_cell_event_nonpadding_sent((circuit_t*)mid_relay);
+ circpad_cell_event_nonpadding_sent(mid_relay);
// Receive extended cell at first hop
- circpad_cell_event_nonpadding_received((circuit_t*)client);
+ circpad_cell_event_nonpadding_received(client);
// Add a hop to cpath
crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t));
- onion_append_to_cpath(&TO_ORIGIN_CIRCUIT(client)->cpath, hop);
+ cpath_extend_linked_list(&TO_ORIGIN_CIRCUIT(client)->cpath, hop);
hop->magic = CRYPT_PATH_MAGIC;
hop->state = CPATH_STATE_OPEN;
@@ -1602,7 +1668,7 @@ simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay,
digest, NULL, NULL, NULL,
&addr, padding);
- circuit_init_cpath_crypto(hop, whatevs_key, sizeof(whatevs_key), 0, 0);
+ cpath_init_circuit_crypto(hop, whatevs_key, sizeof(whatevs_key), 0, 0);
hop->package_window = circuit_initial_package_window();
hop->deliver_window = CIRCWINDOW_START;
@@ -1612,7 +1678,7 @@ simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay,
}
static circpad_machine_spec_t *
-helper_create_conditional_machine(void)
+helper_create_length_machine(void)
{
circpad_machine_spec_t *ret =
tor_malloc_zero(sizeof(circpad_machine_spec_t));
@@ -1629,15 +1695,69 @@ helper_create_conditional_machine(void)
ret->states[CIRCPAD_STATE_BURST].
next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END;
+ ret->states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_BINS_EMPTY] = CIRCPAD_STATE_END;
+
+ /* No token removal.. end via state_length only */
ret->states[CIRCPAD_STATE_BURST].token_removal =
CIRCPAD_TOKEN_REMOVAL_NONE;
+ /* Let's have this one end after 12 packets */
+ ret->states[CIRCPAD_STATE_BURST].length_dist.type = CIRCPAD_DIST_UNIFORM;
+ ret->states[CIRCPAD_STATE_BURST].length_dist.param1 = 12;
+ ret->states[CIRCPAD_STATE_BURST].length_dist.param2 = 13;
+ ret->states[CIRCPAD_STATE_BURST].max_length = 12;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram_len = 4;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[0] = 0;
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[1] = 1;
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[2] = 1000000;
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[3] = 10000000;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram[0] = 0;
+ ret->states[CIRCPAD_STATE_BURST].histogram[1] = 0;
+ ret->states[CIRCPAD_STATE_BURST].histogram[2] = 6;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram_total_tokens = 6;
+ ret->states[CIRCPAD_STATE_BURST].use_rtt_estimate = 0;
+ ret->states[CIRCPAD_STATE_BURST].length_includes_nonpadding = 0;
+
+ return ret;
+}
+
+static circpad_machine_spec_t *
+helper_create_conditional_machine(void)
+{
+ circpad_machine_spec_t *ret =
+ tor_malloc_zero(sizeof(circpad_machine_spec_t));
+
+ /* Start, burst */
+ circpad_machine_states_init(ret, 2);
+
+ ret->states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_BURST;
+
+ ret->states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_BURST;
+
+ ret->states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END;
+
+ /* Use EXACT removal strategy, otherwise setup_tokens() does not work */
+ ret->states[CIRCPAD_STATE_BURST].token_removal =
+ CIRCPAD_TOKEN_REMOVAL_EXACT;
+
ret->states[CIRCPAD_STATE_BURST].histogram_len = 3;
- ret->states[CIRCPAD_STATE_BURST].start_usec = 0;
- ret->states[CIRCPAD_STATE_BURST].range_usec = 1000000;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[0] = 0;
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[1] = 1;
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[2] = 1000000;
+
ret->states[CIRCPAD_STATE_BURST].histogram[0] = 6;
ret->states[CIRCPAD_STATE_BURST].histogram[1] = 0;
- ret->states[CIRCPAD_STATE_BURST].histogram[1] = 0;
+ ret->states[CIRCPAD_STATE_BURST].histogram[2] = 0;
+
ret->states[CIRCPAD_STATE_BURST].histogram_total_tokens = 6;
ret->states[CIRCPAD_STATE_BURST].use_rtt_estimate = 0;
ret->states[CIRCPAD_STATE_BURST].length_includes_nonpadding = 1;
@@ -1649,8 +1769,11 @@ static void
helper_create_conditional_machines(void)
{
circpad_machine_spec_t *add = helper_create_conditional_machine();
- origin_padding_machines = smartlist_new();
- relay_padding_machines = smartlist_new();
+
+ if (!origin_padding_machines)
+ origin_padding_machines = smartlist_new();
+ if (!relay_padding_machines)
+ relay_padding_machines = smartlist_new();
add->machine_num = 2;
add->is_origin_side = 1;
@@ -1668,8 +1791,7 @@ helper_create_conditional_machines(void)
add->conditions.state_mask = CIRCPAD_CIRC_BUILDING|
CIRCPAD_CIRC_NO_STREAMS|CIRCPAD_CIRC_HAS_RELAY_EARLY;
add->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL;
-
- smartlist_add(origin_padding_machines, add);
+ circpad_register_padding_machine(add, origin_padding_machines);
add = helper_create_conditional_machine();
add->machine_num = 3;
@@ -1688,15 +1810,144 @@ helper_create_conditional_machines(void)
add->conditions.state_mask = CIRCPAD_CIRC_OPENED|
CIRCPAD_CIRC_STREAMS|CIRCPAD_CIRC_HAS_NO_RELAY_EARLY;
add->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL;
- smartlist_add(origin_padding_machines, add);
+ circpad_register_padding_machine(add, origin_padding_machines);
add = helper_create_conditional_machine();
add->machine_num = 2;
- smartlist_add(relay_padding_machines, add);
+ circpad_register_padding_machine(add, relay_padding_machines);
add = helper_create_conditional_machine();
add->machine_num = 3;
- smartlist_add(relay_padding_machines, add);
+ circpad_register_padding_machine(add, relay_padding_machines);
+}
+
+void
+test_circuitpadding_state_length(void *arg)
+{
+ /**
+ * Test plan:
+ * * Explicitly test that with no token removal enabled, we hit
+ * the state length limit due to either padding, or non-padding.
+ * * Repeat test with an arbitrary token removal strategy, and
+ * verify that if we run out of tokens due to padding before we
+ * hit the state length, we still go to state end (all our
+ * token removal tests only test nonpadding token removal).
+ */
+ int64_t actual_mocked_monotime_start;
+ (void)arg;
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ MOCK(circpad_send_command_to_hop, circpad_send_command_to_hop_mock);
+
+ nodes_init();
+ dummy_channel.cmux = circuitmux_alloc();
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel,
+ &dummy_channel));
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+
+ /* This is needed so that we are not considered to be dormant */
+ note_user_activity(20);
+
+ timers_initialize();
+ circpad_machine_spec_t *client_machine =
+ helper_create_length_machine();
+
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+ MOCK(node_get_by_id,
+ node_get_by_id_mock);
+
+ client_side->padding_machine[0] = client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+ circpad_machine_runtime_t *mi = client_side->padding_info[0];
+
+ circpad_cell_event_padding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, 12);
+ tt_ptr_op(mi->histogram, OP_EQ, NULL);
+
+ /* Verify that non-padding does not change our state length */
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, 12);
+
+ /* verify that sending padding changes our state length */
+ for (uint64_t i = mi->state_length-1; i > 0; i--) {
+ circpad_send_padding_cell_for_callback(mi);
+ tt_i64_op(mi->state_length, OP_EQ, i);
+ }
+ circpad_send_padding_cell_for_callback(mi);
+
+ tt_i64_op(mi->state_length, OP_EQ, -1);
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END);
+
+ /* Restart machine */
+ mi->current_state = CIRCPAD_STATE_START;
+
+ /* Now, count nonpadding as part of the state length */
+ client_machine->states[CIRCPAD_STATE_BURST].length_includes_nonpadding = 1;
+
+ circpad_cell_event_padding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, 12);
+
+ /* Verify that non-padding does change our state length now */
+ for (uint64_t i = mi->state_length-1; i > 0; i--) {
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, i);
+ }
+
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, -1);
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END);
+
+ /* Now, just test token removal when we send padding */
+ client_machine->states[CIRCPAD_STATE_BURST].token_removal =
+ CIRCPAD_TOKEN_REMOVAL_EXACT;
+
+ /* Restart machine */
+ mi->current_state = CIRCPAD_STATE_START;
+ circpad_cell_event_padding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, 12);
+ tt_ptr_op(mi->histogram, OP_NE, NULL);
+ tt_int_op(mi->chosen_bin, OP_EQ, 2);
+
+ /* verify that sending padding changes our state length and
+ * our histogram now */
+ for (uint32_t i = mi->histogram[2]-1; i > 0; i--) {
+ circpad_send_padding_cell_for_callback(mi);
+ tt_int_op(mi->chosen_bin, OP_EQ, 2);
+ tt_int_op(mi->histogram[2], OP_EQ, i);
+ }
+
+ tt_i64_op(mi->state_length, OP_EQ, 7);
+ tt_int_op(mi->histogram[2], OP_EQ, 1);
+
+ circpad_send_padding_cell_for_callback(mi);
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END);
+
+ done:
+ tor_free(client_machine->states);
+ tor_free(client_machine);
+
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ free_fake_orcirc(relay_side);
+
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ timers_shutdown();
+ monotime_disable_test_mocking();
+ UNMOCK(circuit_package_relay_cell);
+ UNMOCK(circuitmux_attach_circuit);
+ UNMOCK(node_get_by_id);
+
+ return;
}
void
@@ -1720,12 +1971,13 @@ test_circuitpadding_conditions(void *arg)
int64_t actual_mocked_monotime_start;
(void)arg;
MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ testing_enable_reproducible_rng();
nodes_init();
dummy_channel.cmux = circuitmux_alloc();
- relay_side = (circuit_t *)new_fake_orcirc(&dummy_channel,
- &dummy_channel);
- client_side = (circuit_t *)origin_circuit_new();
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel,
+ &dummy_channel));
+ client_side = TO_CIRCUIT(origin_circuit_new());
relay_side->purpose = CIRCUIT_PURPOSE_OR;
client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
@@ -1736,6 +1988,9 @@ test_circuitpadding_conditions(void *arg)
monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
curr_mocked_time = actual_mocked_monotime_start;
+ /* This is needed so that we are not considered to be dormant */
+ note_user_activity(20);
+
timers_initialize();
helper_create_conditional_machines();
@@ -1821,9 +2076,261 @@ test_circuitpadding_conditions(void *arg)
done:
/* XXX: Free everything */
+ testing_disable_reproducible_rng();
return;
}
+/** Disabled unstable test until #29298 is implemented (see #29122) */
+#if 0
+void
+test_circuitpadding_circuitsetup_machine(void *arg)
+{
+ int64_t actual_mocked_monotime_start;
+ /**
+ * Test case plan:
+ *
+ * 1. Simulate a normal circuit setup pattern
+ * a. Application traffic
+ *
+ * FIXME: This should focus more on exercising the machine
+ * features rather than actual traffic patterns. For example,
+ * test cancellation and bins empty/refill
+ */
+ (void)arg;
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+
+ dummy_channel.cmux = circuitmux_alloc();
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
+
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ nodes_init();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+
+ timers_initialize();
+ circpad_machines_init();
+
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+ MOCK(node_get_by_id,
+ node_get_by_id_mock);
+
+ /* Test case #1: Build a 3 hop circuit, then wait and let pad */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ tt_int_op(n_client_cells, OP_EQ, 1);
+ tt_int_op(n_relay_cells, OP_EQ, 1);
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_int_op(relay_side->padding_info[0]->is_padding_timer_scheduled,
+ OP_EQ, 0);
+ timers_advance_and_run(2000);
+ tt_int_op(n_client_cells, OP_EQ, 2);
+ tt_int_op(n_relay_cells, OP_EQ, 1);
+
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_GAP);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ timers_advance_and_run(5000);
+ tt_int_op(n_client_cells, OP_EQ, 2);
+ tt_int_op(n_relay_cells, OP_EQ, 2);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ timers_advance_and_run(2000);
+ tt_int_op(n_client_cells, OP_EQ, 3);
+ tt_int_op(n_relay_cells, OP_EQ, 2);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ timers_advance_and_run(5000);
+ tt_int_op(n_client_cells, OP_EQ, 3);
+ tt_int_op(n_relay_cells, OP_EQ, 3);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ timers_advance_and_run(2000);
+ tt_int_op(n_client_cells, OP_EQ, 4);
+ tt_int_op(n_relay_cells, OP_EQ, 3);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ timers_advance_and_run(5000);
+ tt_int_op(n_client_cells, OP_EQ, 4);
+ tt_int_op(n_relay_cells, OP_EQ, 4);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ timers_advance_and_run(2000);
+ tt_int_op(n_client_cells, OP_EQ, 5);
+ tt_int_op(n_relay_cells, OP_EQ, 4);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ timers_advance_and_run(5000);
+ tt_int_op(n_client_cells, OP_EQ, 5);
+ tt_int_op(n_relay_cells, OP_EQ, 5);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ timers_advance_and_run(2000);
+ tt_int_op(n_client_cells, OP_EQ, 6);
+ tt_int_op(n_relay_cells, OP_EQ, 5);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ timers_advance_and_run(5000);
+ tt_int_op(n_client_cells, OP_EQ, 6);
+ tt_int_op(n_relay_cells, OP_EQ, 6);
+
+ tt_int_op(client_side->padding_info[0]->current_state,
+ OP_EQ, CIRCPAD_STATE_END);
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_int_op(relay_side->padding_info[0]->current_state,
+ OP_EQ, CIRCPAD_STATE_GAP);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+
+ /* Verify we can't schedule padding in END state */
+ circpad_decision_t ret =
+ circpad_machine_schedule_padding(client_side->padding_info[0]);
+ tt_int_op(ret, OP_EQ, CIRCPAD_STATE_UNCHANGED);
+
+ /* Simulate application traffic */
+ circpad_cell_event_nonpadding_sent(client_side);
+ circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_OUT);
+ circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_IN);
+ circpad_deliver_recognized_relay_cell_events(client_side, RELAY_COMMAND_DATA,
+ TO_ORIGIN_CIRCUIT(client_side)->cpath->next);
+
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+ tt_int_op(n_client_cells, OP_EQ, 6);
+ tt_int_op(n_relay_cells, OP_EQ, 7);
+
+ // Test timer cancellation
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ timers_advance_and_run(5000);
+ circpad_cell_event_padding_received(client_side);
+
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_GAP);
+
+ tt_int_op(n_client_cells, OP_EQ, 8);
+ tt_int_op(n_relay_cells, OP_EQ, 8);
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+
+ /* Test timer cancel due to state rules */
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ circpad_cell_event_padding_received(client_side);
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+
+ /* Simulate application traffic to cancel timer */
+ circpad_cell_event_nonpadding_sent(client_side);
+ circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_OUT);
+ circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_IN);
+ circpad_deliver_recognized_relay_cell_events(client_side, RELAY_COMMAND_DATA,
+ TO_ORIGIN_CIRCUIT(client_side)->cpath->next);
+
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+
+ /* No cells sent, except negotiate end from relay */
+ tt_int_op(n_client_cells, OP_EQ, 8);
+ tt_int_op(n_relay_cells, OP_EQ, 9);
+
+ /* Test mark for close and free */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ timers_advance_and_run(5000);
+ circpad_cell_event_padding_received(client_side);
+
+ tt_int_op(n_client_cells, OP_EQ, 10);
+ tt_int_op(n_relay_cells, OP_EQ, 10);
+
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_GAP);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ circuit_mark_for_close(client_side, END_CIRC_REASON_FLAG_REMOTE);
+ free_fake_orcirc(relay_side);
+ timers_advance_and_run(5000);
+
+ /* No cells sent */
+ tt_int_op(n_client_cells, OP_EQ, 10);
+ tt_int_op(n_relay_cells, OP_EQ, 10);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ timers_shutdown();
+ monotime_disable_test_mocking();
+ UNMOCK(circuit_package_relay_cell);
+ UNMOCK(circuitmux_attach_circuit);
+
+ return;
+}
+#endif /* 0 */
+
/** Helper function: Initializes a padding machine where every state uses the
* uniform probability distribution. */
static void
@@ -1834,60 +2341,63 @@ helper_circpad_circ_distribution_machine_setup(int min, int max)
circpad_state_t *zero_st = &circ_client_machine.states[0];
zero_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 1;
zero_st->iat_dist.type = CIRCPAD_DIST_UNIFORM;
+ /* param2 is upper bound, param1 is lower */
zero_st->iat_dist.param1 = min;
zero_st->iat_dist.param2 = max;
- zero_st->start_usec = min;
- zero_st->range_usec = max;
+ zero_st->dist_added_shift_usec = min;
+ zero_st->dist_max_sample_usec = max;
circpad_state_t *first_st = &circ_client_machine.states[1];
first_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 2;
first_st->iat_dist.type = CIRCPAD_DIST_LOGISTIC;
- first_st->iat_dist.param1 = min;
- first_st->iat_dist.param2 = max;
- first_st->start_usec = min;
- first_st->range_usec = max;
+ /* param1 is Mu, param2 is sigma. */
+ first_st->iat_dist.param1 = 9;
+ first_st->iat_dist.param2 = 3;
+ first_st->dist_added_shift_usec = min;
+ first_st->dist_max_sample_usec = max;
circpad_state_t *second_st = &circ_client_machine.states[2];
second_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 3;
second_st->iat_dist.type = CIRCPAD_DIST_LOG_LOGISTIC;
- second_st->iat_dist.param1 = min;
- second_st->iat_dist.param2 = max;
- second_st->start_usec = min;
- second_st->range_usec = max;
+ /* param1 is Alpha, param2 is 1.0/Beta */
+ second_st->iat_dist.param1 = 1;
+ second_st->iat_dist.param2 = 0.5;
+ second_st->dist_added_shift_usec = min;
+ second_st->dist_max_sample_usec = max;
circpad_state_t *third_st = &circ_client_machine.states[3];
third_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 4;
third_st->iat_dist.type = CIRCPAD_DIST_GEOMETRIC;
- third_st->iat_dist.param1 = min;
- third_st->iat_dist.param2 = max;
- third_st->start_usec = min;
- third_st->range_usec = max;
+ /* param1 is 'p' (success probability) */
+ third_st->iat_dist.param1 = 0.2;
+ third_st->dist_added_shift_usec = min;
+ third_st->dist_max_sample_usec = max;
circpad_state_t *fourth_st = &circ_client_machine.states[4];
fourth_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 5;
fourth_st->iat_dist.type = CIRCPAD_DIST_WEIBULL;
- fourth_st->iat_dist.param1 = min;
- fourth_st->iat_dist.param2 = max;
- fourth_st->start_usec = min;
- fourth_st->range_usec = max;
+ /* param1 is k, param2 is Lambda */
+ fourth_st->iat_dist.param1 = 1.5;
+ fourth_st->iat_dist.param2 = 1;
+ fourth_st->dist_added_shift_usec = min;
+ fourth_st->dist_max_sample_usec = max;
circpad_state_t *fifth_st = &circ_client_machine.states[5];
fifth_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 6;
fifth_st->iat_dist.type = CIRCPAD_DIST_PARETO;
- fifth_st->iat_dist.param1 = min;
- fifth_st->iat_dist.param2 = max;
- fifth_st->start_usec = min;
- fifth_st->range_usec = max;
+ /* param1 is sigma, param2 is xi */
+ fifth_st->iat_dist.param1 = 1;
+ fifth_st->iat_dist.param2 = 5;
+ fifth_st->dist_added_shift_usec = min;
+ fifth_st->dist_max_sample_usec = max;
}
/** Simple test that the padding delays sampled from a uniform distribution
* actually faill within the uniform distribution range. */
-/* TODO: Upgrade this test so that each state tests a different prob
- * distribution */
static void
test_circuitpadding_sample_distribution(void *arg)
{
- circpad_machine_state_t *mi;
+ circpad_machine_runtime_t *mi;
int n_samples;
int n_states;
@@ -1896,9 +2406,9 @@ test_circuitpadding_sample_distribution(void *arg)
/* mock this function so that we dont actually schedule any padding */
MOCK(circpad_machine_schedule_padding,
circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
- /* Initialize a machine with multiple probability distributions that should
- * return values between 0 and 5 */
+ /* Initialize a machine with multiple probability distributions */
circpad_machines_init();
helper_circpad_circ_distribution_machine_setup(0, 10);
@@ -1923,16 +2433,17 @@ test_circuitpadding_sample_distribution(void *arg)
}
/* send a non-padding cell to move to the next machine state */
- circpad_cell_event_nonpadding_received((circuit_t*)client_side);
+ circpad_cell_event_nonpadding_received(client_side);
}
done:
free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
UNMOCK(circpad_machine_schedule_padding);
+ testing_disable_reproducible_rng();
}
static circpad_decision_t
-circpad_machine_spec_transition_mock(circpad_machine_state_t *mi,
+circpad_machine_spec_transition_mock(circpad_machine_runtime_t *mi,
circpad_event_t event)
{
(void) mi;
@@ -1947,13 +2458,14 @@ test_circuitpadding_machine_rate_limiting(void *arg)
{
(void) arg;
bool retval;
- circpad_machine_state_t *mi;
+ circpad_machine_runtime_t *mi;
int i;
/* Ignore machine transitions for the purposes of this function, we only
* really care about padding counts */
MOCK(circpad_machine_spec_transition, circpad_machine_spec_transition_mock);
MOCK(circpad_send_command_to_hop, circpad_send_command_to_hop_mock);
+ testing_enable_reproducible_rng();
/* Setup machine and circuits */
client_side = TO_CIRCUIT(origin_circuit_new());
@@ -2007,6 +2519,7 @@ test_circuitpadding_machine_rate_limiting(void *arg)
done:
free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ testing_disable_reproducible_rng();
}
/* Test global padding rate limits */
@@ -2015,7 +2528,7 @@ test_circuitpadding_global_rate_limiting(void *arg)
{
(void) arg;
bool retval;
- circpad_machine_state_t *mi;
+ circpad_machine_runtime_t *mi;
int i;
int64_t actual_mocked_monotime_start;
@@ -2026,6 +2539,7 @@ test_circuitpadding_global_rate_limiting(void *arg)
MOCK(circuit_package_relay_cell,
circuit_package_relay_cell_mock);
MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
+ testing_enable_reproducible_rng();
monotime_init();
monotime_enable_test_mocking();
@@ -2035,12 +2549,12 @@ test_circuitpadding_global_rate_limiting(void *arg)
curr_mocked_time = actual_mocked_monotime_start;
timers_initialize();
- client_side = (circuit_t *)origin_circuit_new();
+ client_side = TO_CIRCUIT(origin_circuit_new());
client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
dummy_channel.cmux = circuitmux_alloc();
/* Setup machine and circuits */
- relay_side = (circuit_t *)new_fake_orcirc(&dummy_channel, &dummy_channel);
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
relay_side->purpose = CIRCUIT_PURPOSE_OR;
helper_create_basic_machine();
relay_side->padding_machine[0] = &circ_client_machine;
@@ -2105,6 +2619,537 @@ test_circuitpadding_global_rate_limiting(void *arg)
circuitmux_free(dummy_channel.cmux);
SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp));
smartlist_free(vote1.net_params);
+ testing_disable_reproducible_rng();
+}
+
+/* Test reduced and disabled padding */
+static void
+test_circuitpadding_reduce_disable(void *arg)
+{
+ (void) arg;
+ int64_t actual_mocked_monotime_start;
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ testing_enable_reproducible_rng();
+
+ nodes_init();
+ dummy_channel.cmux = circuitmux_alloc();
+ relay_side = (circuit_t *)new_fake_orcirc(&dummy_channel,
+ &dummy_channel);
+ client_side = (circuit_t *)origin_circuit_new();
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ circpad_machines_init();
+ helper_create_conditional_machines();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+ timers_initialize();
+
+ /* This is needed so that we are not considered to be dormant */
+ note_user_activity(20);
+
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+ MOCK(node_get_by_id,
+ node_get_by_id_mock);
+
+ /* Simulate extend. This should result in the original machine getting
+ * added, since the circuit is not built */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Verify that machine #2 is added */
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ /* Deliver a padding cell to the client, to trigger burst state */
+ circpad_cell_event_padding_sent(client_side);
+
+ /* This should have trigger length shutdown condition on client.. */
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+
+ /* Verify machine is gone from both sides */
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+
+ /* Now test the reduced padding machine by setting up the consensus */
+ networkstatus_t vote1;
+ vote1.net_params = smartlist_new();
+ smartlist_split_string(vote1.net_params,
+ "circpad_padding_reduced=1", NULL, 0, 0);
+
+ /* Register reduced padding machine with the padding subsystem */
+ circpad_new_consensus_params(&vote1);
+
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Verify that machine #0 is added */
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ tt_int_op(
+ circpad_machine_reached_padding_limit(client_side->padding_info[0]),
+ OP_EQ, 0);
+ tt_int_op(
+ circpad_machine_reached_padding_limit(relay_side->padding_info[0]),
+ OP_EQ, 0);
+
+ /* Test that machines get torn down when padding is disabled */
+ SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp));
+ smartlist_free(vote1.net_params);
+ vote1.net_params = smartlist_new();
+ smartlist_split_string(vote1.net_params,
+ "circpad_padding_disabled=1", NULL, 0, 0);
+
+ /* Register reduced padding machine with the padding subsystem */
+ circpad_new_consensus_params(&vote1);
+
+ tt_int_op(
+ circpad_machine_schedule_padding(client_side->padding_info[0]),
+ OP_EQ, CIRCPAD_STATE_UNCHANGED);
+ tt_int_op(
+ circpad_machine_schedule_padding(relay_side->padding_info[0]),
+ OP_EQ, CIRCPAD_STATE_UNCHANGED);
+
+ /* Signal that circuit is built: this event causes us to re-evaluate
+ * machine conditions (which don't apply because padding is disabled). */
+ circpad_machine_event_circ_built(TO_ORIGIN_CIRCUIT(client_side));
+
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+
+ SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp));
+ smartlist_free(vote1.net_params);
+ vote1.net_params = NULL;
+ circpad_new_consensus_params(&vote1);
+
+ get_options_mutable()->ReducedCircuitPadding = 1;
+
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Verify that machine #0 is added */
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ tt_int_op(
+ circpad_machine_reached_padding_limit(client_side->padding_info[0]),
+ OP_EQ, 0);
+ tt_int_op(
+ circpad_machine_reached_padding_limit(relay_side->padding_info[0]),
+ OP_EQ, 0);
+
+ get_options_mutable()->CircuitPadding = 0;
+
+ tt_int_op(
+ circpad_machine_schedule_padding(client_side->padding_info[0]),
+ OP_EQ, CIRCPAD_STATE_UNCHANGED);
+ tt_int_op(
+ circpad_machine_schedule_padding(relay_side->padding_info[0]),
+ OP_EQ, CIRCPAD_STATE_UNCHANGED);
+
+ /* Signal that circuit is built: this event causes us to re-evaluate
+ * machine conditions (which don't apply because padding is disabled). */
+
+ circpad_machine_event_circ_built(TO_ORIGIN_CIRCUIT(client_side));
+
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+
+ done:
+ free_fake_orcirc(relay_side);
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ testing_disable_reproducible_rng();
+}
+
+/** Just a basic machine whose whole purpose is to reach the END state */
+static void
+helper_create_ender_machine(void)
+{
+ /* Start, burst */
+ circpad_machine_states_init(&circ_client_machine, 2);
+
+ circ_client_machine.states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_END;
+
+ circ_client_machine.conditions.state_mask = CIRCPAD_STATE_ALL;
+ circ_client_machine.conditions.purpose_mask = CIRCPAD_PURPOSE_ALL;
+}
+
+static time_t mocked_timeofday;
+/** Set timeval to a mock date and time. This is necessary
+ * to make tor_gettimeofday() mockable. */
+static void
+mock_tor_gettimeofday(struct timeval *timeval)
+{
+ timeval->tv_sec = mocked_timeofday;
+ timeval->tv_usec = 0;
+}
+
+/** Test manual managing of circuit lifetimes by the circuitpadding
+ * subsystem. In particular this test goes through all the cases of the
+ * circpad_marked_circuit_for_padding() function, via
+ * circuit_mark_for_close() as well as
+ * circuit_expire_old_circuits_clientside(). */
+static void
+test_circuitpadding_manage_circuit_lifetime(void *arg)
+{
+ circpad_machine_runtime_t *mi;
+
+ (void) arg;
+
+ client_side = (circuit_t *)origin_circuit_new();
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ monotime_enable_test_mocking();
+ MOCK(tor_gettimeofday, mock_tor_gettimeofday);
+ mocked_timeofday = 23;
+
+ helper_create_ender_machine();
+
+ /* Enable manual circuit lifetime manage for this test */
+ circ_client_machine.manage_circ_lifetime = 1;
+
+ /* Test setup */
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+ mi = client_side->padding_info[0];
+
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_START);
+
+ /* Check that the circuit is not marked for close */
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL);
+
+ /* Mark this circuit for close due to a remote reason */
+ circuit_mark_for_close(client_side,
+ END_CIRC_REASON_FLAG_REMOTE|END_CIRC_REASON_NONE);
+ tt_ptr_op(client_side->padding_info[0], OP_NE, NULL);
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL);
+ client_side->marked_for_close = 0;
+
+ /* Mark this circuit for close due to a protocol issue */
+ circuit_mark_for_close(client_side, END_CIRC_REASON_TORPROTOCOL);
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL);
+ client_side->marked_for_close = 0;
+
+ /* Mark a measurement circuit for close */
+ client_side->purpose = CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT;
+ circuit_mark_for_close(client_side, END_CIRC_REASON_NONE);
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT);
+ client_side->marked_for_close = 0;
+
+ /* Mark a general circuit for close */
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ circuit_mark_for_close(client_side, END_CIRC_REASON_NONE);
+
+ /* Check that this circuit is still not marked for close since we are
+ * managing the lifetime manually, but the circuit was tagged as such by the
+ * circpadding subsystem */
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_CIRCUIT_PADDING);
+
+ /* We just tested case (1) from the comments of
+ * circpad_circuit_should_be_marked_for_close() */
+
+ /* Transition the machine to the END state but did not delete its machine */
+ tt_ptr_op(client_side->padding_info[0], OP_NE, NULL);
+ circpad_cell_event_nonpadding_received(client_side);
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END);
+
+ /* We just tested case (3) from the comments of
+ * circpad_circuit_should_be_marked_for_close().
+ * Now let's go for case (2). */
+
+ /* Reset the close mark */
+ client_side->marked_for_close = 0;
+
+ /* Mark this circuit for close */
+ circuit_mark_for_close(client_side, 0);
+
+ /* See that the circ got closed since we are already in END state */
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+
+ /* We just tested case (2). Now let's see that case (4) is unreachable as
+ that comment claims */
+
+ /* First, reset all close marks and tags */
+ client_side->marked_for_close = 0;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ /* Now re-create the ender machine so that we can transition to END again */
+ /* Free up some stuff first */
+ circpad_circuit_free_all_machineinfos(client_side);
+ tor_free(circ_client_machine.states);
+ helper_create_ender_machine();
+
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+ mi = client_side->padding_info[0];
+
+ /* Check we are in START. */
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_START);
+
+ /* Test that we don't expire this circuit yet */
+ client_side->timestamp_dirty = 0;
+ client_side->state = CIRCUIT_STATE_OPEN;
+ tor_gettimeofday(&client_side->timestamp_began);
+ TO_ORIGIN_CIRCUIT(client_side)->circuit_idle_timeout = 23;
+ mocked_timeofday += 24;
+ circuit_expire_old_circuits_clientside();
+ circuit_expire_old_circuits_clientside();
+ circuit_expire_old_circuits_clientside();
+ tt_int_op(client_side->timestamp_dirty, OP_NE, 0);
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_CIRCUIT_PADDING);
+
+ /* Runaway circpad test: if the machine does not transition to end,
+ * test that after CIRCPAD_DELAY_MAX_SECS, we get marked anyway */
+ mocked_timeofday = client_side->timestamp_dirty
+ + get_options()->MaxCircuitDirtiness + 2;
+ client_side->padding_info[0]->last_cell_time_sec =
+ approx_time()-(CIRCPAD_DELAY_MAX_SECS+10);
+ circuit_expire_old_circuits_clientside();
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+
+ /* Test back to normal: if we had activity, we won't close */
+ client_side->padding_info[0]->last_cell_time_sec = approx_time();
+ client_side->marked_for_close = 0;
+ circuit_expire_old_circuits_clientside();
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+
+ /* Transition to END, but before we're past the dirty timer */
+ mocked_timeofday = client_side->timestamp_dirty;
+ circpad_cell_event_nonpadding_received(client_side);
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END);
+
+ /* Verify that the circuit was not closed. */
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+
+ /* Now that we are in END state, we can be closed by expiry, but via
+ * the timestamp_dirty path, not the idle path. So first test not dirty
+ * enough. */
+ mocked_timeofday = client_side->timestamp_dirty;
+ circuit_expire_old_circuits_clientside();
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+ mocked_timeofday = client_side->timestamp_dirty
+ + get_options()->MaxCircuitDirtiness + 2;
+ circuit_expire_old_circuits_clientside();
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ tor_free(circ_client_machine.states);
+ monotime_disable_test_mocking();
+ UNMOCK(tor_gettimeofday);
+}
+
+/** Helper for the test_circuitpadding_hs_machines test:
+ *
+ * - Create a client and relay circuit.
+ * - Setup right circuit purpose and attach a machine to the client circuit.
+ * - Verify that state transitions work as intended and state length gets
+ * enforced.
+ *
+ * This function is able to do this test both for intro and rend circuits
+ * depending on the value of <b>test_intro_circs</b>.
+ */
+static void
+helper_test_hs_machines(bool test_intro_circs)
+{
+ /* Setup the circuits */
+ origin_circuit_t *origin_client_side = origin_circuit_new();
+ client_side = TO_CIRCUIT(origin_client_side);
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ dummy_channel.cmux = circuitmux_alloc();
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+
+ /* extend the client circ to two hops */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* machines only apply on opened circuits */
+ origin_client_side->has_opened = 1;
+
+ /************************************/
+
+ /* Attaching the client machine now won't work here because of a wrong
+ * purpose */
+ tt_assert(!client_side->padding_machine[0]);
+ circpad_add_matching_machines(origin_client_side, origin_padding_machines);
+ tt_assert(!client_side->padding_machine[0]);
+
+ /* Change the purpose, see the machine getting attached */
+ client_side->purpose = test_intro_circs ?
+ CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT : CIRCUIT_PURPOSE_C_REND_JOINED;
+ circpad_add_matching_machines(origin_client_side, origin_padding_machines);
+ tt_ptr_op(client_side->padding_info[0], OP_NE, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_NE, NULL);
+
+ tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL);
+
+ /* Verify that the right machine is attached */
+ tt_str_op(client_side->padding_machine[0]->name, OP_EQ,
+ test_intro_circs ? "client_ip_circ" : "client_rp_circ");
+ tt_str_op(relay_side->padding_machine[0]->name, OP_EQ,
+ test_intro_circs ? "relay_ip_circ": "relay_rp_circ");
+
+ /***********************************/
+
+ /* Intro machines are at START state, but rend machines have already skipped
+ * to OBFUSCATE_CIRC_SETUP because of the sent PADDING_NEGOTIATE. */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
+
+ /*Send non-padding to move the machines from START to OBFUSCATE_CIRC_SETUP */
+ circpad_cell_event_nonpadding_received(client_side);
+ circpad_cell_event_nonpadding_received(relay_side);
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
+
+ /* Check that the state lengths have been sampled and are within range */
+ circpad_machine_runtime_t *client_machine_runtime =
+ client_side->padding_info[0];
+ circpad_machine_runtime_t *relay_machine_runtime =
+ relay_side->padding_info[0];
+
+ if (test_intro_circs) {
+ /* on the client side, we don't send any padding so
+ * state length is not set */
+ tt_i64_op(client_machine_runtime->state_length, OP_EQ, -1);
+ /* relay side has state limits. check them */
+ tt_i64_op(relay_machine_runtime->state_length, OP_GE,
+ INTRO_MACHINE_MINIMUM_PADDING);
+ tt_i64_op(relay_machine_runtime->state_length, OP_LT,
+ INTRO_MACHINE_MAXIMUM_PADDING);
+ } else {
+ tt_i64_op(client_machine_runtime->state_length, OP_EQ, 1);
+ tt_i64_op(relay_machine_runtime->state_length, OP_EQ, 1);
+ }
+
+ if (test_intro_circs) {
+ int i;
+ /* Send state_length worth of padding from the relay and see that the
+ * client state goes to END */
+ for (i = (int) relay_machine_runtime->state_length ; i > 0 ; i--) {
+ circpad_send_padding_cell_for_callback(relay_machine_runtime);
+ }
+ /* See that the machine has been teared down after all the length has been
+ * exhausted (the padding info should now be null on both sides) */
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ } else {
+ int i;
+ /* Send state_length worth of padding and see that the state goes to END */
+ for (i = (int) client_machine_runtime->state_length ; i > 0 ; i--) {
+ circpad_send_padding_cell_for_callback(client_machine_runtime);
+ }
+ /* See that the machine has been teared down after all the length has been
+ * exhausted. */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_END);
+ }
+
+ done:
+ free_fake_orcirc(relay_side);
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+}
+
+/** Test that the HS circuit padding machines work as intended. */
+static void
+test_circuitpadding_hs_machines(void *arg)
+{
+ (void)arg;
+
+ /* Test logic:
+ *
+ * 1) Register the HS machines, which aim to hide the presense of
+ * onion service traffic on the client-side
+ *
+ * 2) Call helper_test_hs_machines() to perform tests for the intro circuit
+ * machines and for the rend circuit machines.
+ */
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock);
+ MOCK(circuit_get_nth_node, circuit_get_nth_node_mock);
+ MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+
+ origin_padding_machines = smartlist_new();
+ relay_padding_machines = smartlist_new();
+
+ nodes_init();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ monotime_set_mock_time_nsec(1*TOR_NSEC_PER_USEC);
+ monotime_coarse_set_mock_time_nsec(1*TOR_NSEC_PER_USEC);
+ curr_mocked_time = 1*TOR_NSEC_PER_USEC;
+
+ timers_initialize();
+
+ /* This is needed so that we are not considered to be dormant */
+ note_user_activity(20);
+
+ /************************************/
+
+ /* Register the HS machines */
+ circpad_machine_client_hide_intro_circuits(origin_padding_machines);
+ circpad_machine_client_hide_rend_circuits(origin_padding_machines);
+ circpad_machine_relay_hide_intro_circuits(relay_padding_machines);
+ circpad_machine_relay_hide_rend_circuits(relay_padding_machines);
+
+ /***********************************/
+
+ /* Do the tests for the intro circuit machines */
+ helper_test_hs_machines(true);
+ /* Do the tests for the rend circuit machines */
+ helper_test_hs_machines(false);
+
+ timers_shutdown();
+ monotime_disable_test_mocking();
+
+ SMARTLIST_FOREACH_BEGIN(origin_padding_machines,
+ circpad_machine_spec_t *, m) {
+ machine_spec_free(m);
+ } SMARTLIST_FOREACH_END(m);
+
+ SMARTLIST_FOREACH_BEGIN(relay_padding_machines,
+ circpad_machine_spec_t *, m) {
+ machine_spec_free(m);
+ } SMARTLIST_FOREACH_END(m);
+
+ smartlist_free(origin_padding_machines);
+ smartlist_free(relay_padding_machines);
+
+ UNMOCK(circuitmux_attach_circuit);
+ UNMOCK(circuit_package_relay_cell);
+ UNMOCK(circuit_get_nth_node);
+ UNMOCK(circpad_machine_schedule_padding);
}
#define TEST_CIRCUITPADDING(name, flags) \
@@ -2112,17 +3157,23 @@ test_circuitpadding_global_rate_limiting(void *arg)
struct testcase_t circuitpadding_tests[] = {
TEST_CIRCUITPADDING(circuitpadding_tokens, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_state_length, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_negotiation, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_wronghop, TT_FORK),
+ /** Disabled unstable test until #29298 is implemented (see #29122) */
+ // TEST_CIRCUITPADDING(circuitpadding_circuitsetup_machine, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_conditions, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_rtt, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_sample_distribution, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_machine_rate_limiting, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_global_rate_limiting, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_reduce_disable, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_token_removal_lower, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_token_removal_higher, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_closest_token_removal, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_closest_token_removal_usec, TT_FORK),
TEST_CIRCUITPADDING(circuitpadding_token_removal_exact, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_manage_circuit_lifetime, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_hs_machines, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_circuitstats.c b/src/test/test_circuitstats.c
index 1cbcb14f2b..2a09622f09 100644
--- a/src/test/test_circuitstats.c
+++ b/src/test/test_circuitstats.c
@@ -28,7 +28,7 @@ origin_circuit_t *subtest_fourhop_circuit(struct timeval, int);
origin_circuit_t *add_opened_threehop(void);
origin_circuit_t *build_unopened_fourhop(struct timeval);
-int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
+int cpath_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
static int marked_for_close;
/* Mock function because we are not trying to test the close circuit that does
@@ -57,9 +57,9 @@ add_opened_threehop(void)
or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
or_circ->build_state->desired_path_len = DEFAULT_ROUTE_LEN;
- onion_append_hop(&or_circ->cpath, &fakehop);
- onion_append_hop(&or_circ->cpath, &fakehop);
- onion_append_hop(&or_circ->cpath, &fakehop);
+ cpath_append_hop(&or_circ->cpath, &fakehop);
+ cpath_append_hop(&or_circ->cpath, &fakehop);
+ cpath_append_hop(&or_circ->cpath, &fakehop);
or_circ->has_opened = 1;
TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
@@ -82,10 +82,10 @@ build_unopened_fourhop(struct timeval circ_start_time)
or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
or_circ->build_state->desired_path_len = 4;
- onion_append_hop(&or_circ->cpath, fakehop);
- onion_append_hop(&or_circ->cpath, fakehop);
- onion_append_hop(&or_circ->cpath, fakehop);
- onion_append_hop(&or_circ->cpath, fakehop);
+ cpath_append_hop(&or_circ->cpath, fakehop);
+ cpath_append_hop(&or_circ->cpath, fakehop);
+ cpath_append_hop(&or_circ->cpath, fakehop);
+ cpath_append_hop(&or_circ->cpath, fakehop);
tor_free(fakehop);
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 3078e68665..a415ca4480 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -5064,7 +5064,7 @@ test_config_include_no_permission(void *data)
chmod(dir, 0700);
tor_free(dir);
}
-#endif
+#endif /* !defined(_WIN32) */
static void
test_config_include_recursion_before_after(void *data)
@@ -5696,7 +5696,7 @@ test_config_compute_max_mem_in_queues(void *data)
#else
/* We are on a 32-bit system. */
tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ, GIGABYTE(1));
-#endif
+#endif /* SIZEOF_VOID_P >= 8 */
/* We are able to detect the amount of RAM on the system. */
total_system_memory_return = 0;
@@ -5737,7 +5737,7 @@ test_config_compute_max_mem_in_queues(void *data)
/* We will at maximum get MAX_DEFAULT_MEMORY_QUEUE_SIZE here. */
tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ,
MAX_DEFAULT_MEMORY_QUEUE_SIZE);
-#endif
+#endif /* SIZEOF_SIZE_T > 4 */
done:
UNMOCK(get_total_system_memory);
@@ -5884,6 +5884,61 @@ test_config_kvline_parse(void *arg)
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "AB");
tt_str_op(lines->value, OP_EQ, "");
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB=", KV_OMIT_VALS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "");
+ config_free_lines(lines);
+
+ lines = kvline_parse(" AB ", KV_OMIT_VALS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "");
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB", KV_OMIT_VALS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "");
+ enc = kvline_encode(lines, KV_OMIT_VALS);
+ tt_str_op(enc, OP_EQ, "AB");
+ tor_free(enc);
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB=CD", KV_OMIT_VALS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "CD");
+ enc = kvline_encode(lines, KV_OMIT_VALS);
+ tt_str_op(enc, OP_EQ, "AB=CD");
+ tor_free(enc);
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB=CD DE FGH=I", KV_OMIT_VALS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "CD");
+ tt_str_op(lines->next->key, OP_EQ, "DE");
+ tt_str_op(lines->next->value, OP_EQ, "");
+ tt_str_op(lines->next->next->key, OP_EQ, "FGH");
+ tt_str_op(lines->next->next->value, OP_EQ, "I");
+ enc = kvline_encode(lines, KV_OMIT_VALS);
+ tt_str_op(enc, OP_EQ, "AB=CD DE FGH=I");
+ tor_free(enc);
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB=\"CD E\" DE FGH=\"I\"", KV_OMIT_VALS|KV_QUOTED);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "CD E");
+ tt_str_op(lines->next->key, OP_EQ, "DE");
+ tt_str_op(lines->next->value, OP_EQ, "");
+ tt_str_op(lines->next->next->key, OP_EQ, "FGH");
+ tt_str_op(lines->next->next->value, OP_EQ, "I");
+ enc = kvline_encode(lines, KV_OMIT_VALS|KV_QUOTED);
+ tt_str_op(enc, OP_EQ, "AB=\"CD E\" DE FGH=I");
done:
config_free_lines(lines);
diff --git a/src/test/test_connection.h b/src/test/test_connection.h
index 47a5599e5f..40121e6d38 100644
--- a/src/test/test_connection.h
+++ b/src/test/test_connection.h
@@ -1,6 +1,9 @@
/* Copyright (c) 2014-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#ifndef TOR_TEST_CONNECTION_H
+#define TOR_TEST_CONNECTION_H
+
/** Some constants used by test_connection and helpers */
#define TEST_CONN_FAMILY (AF_INET)
#define TEST_CONN_ADDRESS "127.0.0.1"
@@ -11,3 +14,4 @@
void test_conn_lookup_addr_helper(const char *address,
int family, tor_addr_t *addr);
+#endif /* !defined(TOR_TEST_CONNECTION_H) */
diff --git a/src/test/test_containers.c b/src/test/test_containers.c
index a0832f868e..67ba457975 100644
--- a/src/test/test_containers.c
+++ b/src/test/test_containers.c
@@ -606,6 +606,66 @@ test_container_smartlist_ints_eq(void *arg)
smartlist_free(sl2);
}
+static void
+test_container_smartlist_grow(void *arg)
+{
+ (void)arg;
+ smartlist_t *sl = smartlist_new();
+ int i;
+ const char *s[] = { "first", "2nd", "3rd" };
+
+ /* case 1: starting from empty. */
+ smartlist_grow(sl, 10);
+ tt_int_op(10, OP_EQ, smartlist_len(sl));
+ for (i = 0; i < 10; ++i) {
+ tt_ptr_op(smartlist_get(sl, i), OP_EQ, NULL);
+ }
+
+ /* case 2: starting with a few elements, probably not reallocating. */
+ smartlist_free(sl);
+ sl = smartlist_new();
+ smartlist_add(sl, (char*)s[0]);
+ smartlist_add(sl, (char*)s[1]);
+ smartlist_add(sl, (char*)s[2]);
+ smartlist_grow(sl, 5);
+ tt_int_op(5, OP_EQ, smartlist_len(sl));
+ for (i = 0; i < 3; ++i) {
+ tt_ptr_op(smartlist_get(sl, i), OP_EQ, s[i]);
+ }
+ tt_ptr_op(smartlist_get(sl, 3), OP_EQ, NULL);
+ tt_ptr_op(smartlist_get(sl, 4), OP_EQ, NULL);
+
+ /* case 3: starting with a few elements, but reallocating. */
+ smartlist_free(sl);
+ sl = smartlist_new();
+ smartlist_add(sl, (char*)s[0]);
+ smartlist_add(sl, (char*)s[1]);
+ smartlist_add(sl, (char*)s[2]);
+ smartlist_grow(sl, 100);
+ tt_int_op(100, OP_EQ, smartlist_len(sl));
+ for (i = 0; i < 3; ++i) {
+ tt_ptr_op(smartlist_get(sl, i), OP_EQ, s[i]);
+ }
+ for (i = 3; i < 100; ++i) {
+ tt_ptr_op(smartlist_get(sl, i), OP_EQ, NULL);
+ }
+
+ /* case 4: shrinking doesn't happen. */
+ smartlist_free(sl);
+ sl = smartlist_new();
+ smartlist_add(sl, (char*)s[0]);
+ smartlist_add(sl, (char*)s[1]);
+ smartlist_add(sl, (char*)s[2]);
+ smartlist_grow(sl, 1);
+ tt_int_op(3, OP_EQ, smartlist_len(sl));
+ for (i = 0; i < 3; ++i) {
+ tt_ptr_op(smartlist_get(sl, i), OP_EQ, s[i]);
+ }
+
+ done:
+ smartlist_free(sl);
+}
+
/** Run unit tests for bitarray code */
static void
test_container_bitarray(void *arg)
@@ -946,6 +1006,10 @@ test_container_smartlist_remove(void *arg)
tt_ptr_op(smartlist_get(sl, 1), OP_EQ, &array[2]);
tt_ptr_op(smartlist_get(sl, 2), OP_EQ, &array[1]);
tt_ptr_op(smartlist_get(sl, 3), OP_EQ, &array[2]);
+ /* Ordinary code should never look at this pointer; we're doing it here
+ * to make sure that we really cleared the pointer we removed.
+ */
+ tt_ptr_op(sl->list[4], OP_EQ, NULL);
done:
smartlist_free(sl);
@@ -1312,6 +1376,7 @@ struct testcase_t container_tests[] = {
CONTAINER_LEGACY(smartlist_pos),
CONTAINER(smartlist_remove, 0),
CONTAINER(smartlist_ints_eq, 0),
+ CONTAINER(smartlist_grow, 0),
CONTAINER_LEGACY(bitarray),
CONTAINER_LEGACY(digestset),
CONTAINER_LEGACY(strmap),
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 5b406e159b..ee48d656bd 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -1,11 +1,14 @@
/* Copyright (c) 2015-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-#define CONTROL_PRIVATE
+#define CONTROL_CMD_PRIVATE
+#define CONTROL_GETINFO_PRIVATE
#include "core/or/or.h"
#include "lib/crypt_ops/crypto_ed25519.h"
#include "feature/client/bridges.h"
#include "feature/control/control.h"
+#include "feature/control/control_cmd.h"
+#include "feature/control/control_getinfo.h"
#include "feature/client/entrynodes.h"
#include "feature/hs/hs_common.h"
#include "feature/nodelist/networkstatus.h"
@@ -15,12 +18,189 @@
#include "test/test.h"
#include "test/test_helpers.h"
#include "lib/net/resolve.h"
+#include "lib/encoding/confline.h"
+#include "lib/encoding/kvline.h"
#include "feature/control/control_connection_st.h"
+#include "feature/control/control_cmd_args_st.h"
#include "feature/dirclient/download_status_st.h"
#include "feature/nodelist/microdesc_st.h"
#include "feature/nodelist/node_st.h"
+typedef struct {
+ const char *input;
+ const char *expected_parse;
+ const char *expected_error;
+} parser_testcase_t;
+
+typedef struct {
+ const control_cmd_syntax_t *syntax;
+ size_t n_testcases;
+ const parser_testcase_t *testcases;
+} parse_test_params_t;
+
+static char *
+control_cmd_dump_args(const control_cmd_args_t *result)
+{
+ buf_t *buf = buf_new();
+ buf_add_string(buf, "{ args=[");
+ if (result->args) {
+ if (smartlist_len(result->args)) {
+ buf_add_string(buf, " ");
+ }
+ SMARTLIST_FOREACH_BEGIN(result->args, const char *, s) {
+ const bool last = (s_sl_idx == smartlist_len(result->args)-1);
+ buf_add_printf(buf, "%s%s ",
+ escaped(s),
+ last ? "" : ",");
+ } SMARTLIST_FOREACH_END(s);
+ }
+ buf_add_string(buf, "]");
+ if (result->cmddata) {
+ buf_add_string(buf, ", obj=");
+ buf_add_string(buf, escaped(result->cmddata));
+ }
+ if (result->kwargs) {
+ buf_add_string(buf, ", { ");
+ const config_line_t *line;
+ for (line = result->kwargs; line; line = line->next) {
+ const bool last = (line->next == NULL);
+ buf_add_printf(buf, "%s=%s%s ", line->key, escaped(line->value),
+ last ? "" : ",");
+ }
+ buf_add_string(buf, "}");
+ }
+ buf_add_string(buf, " }");
+
+ char *encoded = buf_extract(buf, NULL);
+ buf_free(buf);
+ return encoded;
+}
+
+static void
+test_controller_parse_cmd(void *arg)
+{
+ const parse_test_params_t *params = arg;
+ control_cmd_args_t *result = NULL;
+ char *error = NULL;
+ char *encoded = NULL;
+
+ for (size_t i = 0; i < params->n_testcases; ++i) {
+ const parser_testcase_t *t = &params->testcases[i];
+ result = control_cmd_parse_args("EXAMPLE",
+ params->syntax,
+ strlen(t->input),
+ t->input,
+ &error);
+ // A valid test should expect exactly one parse or error.
+ tt_int_op((t->expected_parse == NULL), OP_NE,
+ (t->expected_error == NULL));
+ // We get a result or an error, not both.
+ tt_int_op((result == NULL), OP_EQ, (error != NULL));
+ // We got the one we expected.
+ tt_int_op((result == NULL), OP_EQ, (t->expected_parse == NULL));
+
+ if (result) {
+ encoded = control_cmd_dump_args(result);
+ tt_str_op(encoded, OP_EQ, t->expected_parse);
+ } else {
+ tt_str_op(error, OP_EQ, t->expected_error);
+ }
+
+ tor_free(error);
+ tor_free(encoded);
+ control_cmd_args_free(result);
+ }
+
+ done:
+ tor_free(error);
+ tor_free(encoded);
+ control_cmd_args_free(result);
+}
+
+#define OK(inp, out) \
+ { inp "\r\n", out, NULL }
+#define ERR(inp, err) \
+ { inp "\r\n", NULL, err }
+
+#define TESTPARAMS(syntax, array) \
+ { &syntax, \
+ ARRAY_LENGTH(array), \
+ array }
+
+static const parser_testcase_t one_to_three_tests[] = {
+ ERR("", "Need at least 1 argument(s)"),
+ ERR(" \t", "Need at least 1 argument(s)"),
+ OK("hello", "{ args=[ \"hello\" ] }"),
+ OK("hello world", "{ args=[ \"hello\", \"world\" ] }"),
+ OK("hello world", "{ args=[ \"hello\", \"world\" ] }"),
+ OK(" hello world", "{ args=[ \"hello\", \"world\" ] }"),
+ OK(" hello world ", "{ args=[ \"hello\", \"world\" ] }"),
+ OK("hello there world", "{ args=[ \"hello\", \"there\", \"world\" ] }"),
+ ERR("why hello there world", "Cannot accept more than 3 argument(s)"),
+ ERR("hello\r\nworld.\r\n.", "Unexpected body"),
+};
+
+static const control_cmd_syntax_t one_to_three_syntax = {
+ .min_args=1, .max_args=3
+};
+
+static const parse_test_params_t parse_one_to_three_params =
+ TESTPARAMS( one_to_three_syntax, one_to_three_tests );
+
+// =
+static const parser_testcase_t no_args_one_obj_tests[] = {
+ ERR("Hi there!\r\n.", "Cannot accept more than 0 argument(s)"),
+ ERR("", "Empty body"),
+ OK("\r\n", "{ args=[], obj=\"\\n\" }"),
+ OK("\r\nHello world\r\n", "{ args=[], obj=\"Hello world\\n\\n\" }"),
+ OK("\r\nHello\r\nworld\r\n", "{ args=[], obj=\"Hello\\nworld\\n\\n\" }"),
+ OK("\r\nHello\r\n..\r\nworld\r\n",
+ "{ args=[], obj=\"Hello\\n.\\nworld\\n\\n\" }"),
+};
+static const control_cmd_syntax_t no_args_one_obj_syntax = {
+ .min_args=0, .max_args=0,
+ .want_cmddata=true,
+};
+static const parse_test_params_t parse_no_args_one_obj_params =
+ TESTPARAMS( no_args_one_obj_syntax, no_args_one_obj_tests );
+
+static const parser_testcase_t no_args_kwargs_tests[] = {
+ OK("", "{ args=[] }"),
+ OK(" ", "{ args=[] }"),
+ OK("hello there=world", "{ args=[], { hello=\"\", there=\"world\" } }"),
+ OK("hello there=world today",
+ "{ args=[], { hello=\"\", there=\"world\", today=\"\" } }"),
+ ERR("=Foo", "Cannot parse keyword argument(s)"),
+};
+static const control_cmd_syntax_t no_args_kwargs_syntax = {
+ .min_args=0, .max_args=0,
+ .accept_keywords=true,
+ .kvline_flags=KV_OMIT_VALS
+};
+static const parse_test_params_t parse_no_args_kwargs_params =
+ TESTPARAMS( no_args_kwargs_syntax, no_args_kwargs_tests );
+
+static const char *one_arg_kwargs_allow_keywords[] = {
+ "Hello", "world", NULL
+};
+static const parser_testcase_t one_arg_kwargs_tests[] = {
+ ERR("", "Need at least 1 argument(s)"),
+ OK("Hi", "{ args=[ \"Hi\" ] }"),
+ ERR("hello there=world", "Unrecognized keyword argument \"there\""),
+ OK("Hi HELLO=foo", "{ args=[ \"Hi\" ], { HELLO=\"foo\" } }"),
+ OK("Hi world=\"bar baz\" hello ",
+ "{ args=[ \"Hi\" ], { world=\"bar baz\", hello=\"\" } }"),
+};
+static const control_cmd_syntax_t one_arg_kwargs_syntax = {
+ .min_args=1, .max_args=1,
+ .accept_keywords=true,
+ .allowed_keywords=one_arg_kwargs_allow_keywords,
+ .kvline_flags=KV_OMIT_VALS|KV_QUOTED,
+};
+static const parse_test_params_t parse_one_arg_kwargs_params =
+ TESTPARAMS( one_arg_kwargs_syntax, one_arg_kwargs_tests );
+
static void
test_add_onion_helper_keyarg_v3(void *arg)
{
@@ -1543,7 +1723,7 @@ test_current_time(void *arg)
static size_t n_nodelist_get_list = 0;
static smartlist_t *nodes = NULL;
-static smartlist_t *
+static const smartlist_t *
mock_nodelist_get_list(void)
{
n_nodelist_get_list++;
@@ -1614,7 +1794,15 @@ test_getinfo_md_all(void *arg)
return;
}
+#define PARSER_TEST(type) \
+ { "parse/" #type, test_controller_parse_cmd, 0, &passthrough_setup, \
+ (void*)&parse_ ## type ## _params }
+
struct testcase_t controller_tests[] = {
+ PARSER_TEST(one_to_three),
+ PARSER_TEST(no_args_one_obj),
+ PARSER_TEST(no_args_kwargs),
+ PARSER_TEST(one_arg_kwargs),
{ "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0,
NULL, NULL },
{ "add_onion_helper_keyarg_v3", test_add_onion_helper_keyarg_v3, 0,
diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c
index 647eac43c7..910aacace3 100644
--- a/src/test/test_controller_events.c
+++ b/src/test/test_controller_events.c
@@ -4,6 +4,7 @@
#define CONNECTION_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#define CONTROL_PRIVATE
+#define CONTROL_EVENTS_PRIVATE
#define OCIRC_EVENT_PRIVATE
#define ORCONN_EVENT_PRIVATE
#include "core/or/or.h"
@@ -13,7 +14,7 @@
#include "core/or/ocirc_event.h"
#include "core/or/orconn_event.h"
#include "core/mainloop/connection.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "test/test.h"
#include "core/or/or_circuit_st.h"
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 0b57448bcf..178a9a5097 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -32,7 +32,7 @@
DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/dh.h>
ENABLE_GCC_WARNING(redundant-decls)
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
/** Run unit tests for Diffie-Hellman functionality. */
static void
@@ -190,7 +190,7 @@ test_crypto_dh(void *arg)
DH_get0_key(dh4, &pk, &sk);
#else
pk = dh4->pub_key;
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
tt_assert(pk);
tt_int_op(BN_num_bytes(pk), OP_LE, DH1024_KEY_LEN);
tt_int_op(BN_num_bytes(pk), OP_GT, 0);
@@ -207,7 +207,7 @@ test_crypto_dh(void *arg)
tt_int_op(s1len, OP_GT, 0);
tt_mem_op(s1, OP_EQ, s2, s1len);
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
done:
crypto_dh_free(dh1);
@@ -219,7 +219,7 @@ test_crypto_dh(void *arg)
DH_free(dh4);
if (pubkey_tmp)
BN_free(pubkey_tmp);
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
}
static void
@@ -248,7 +248,7 @@ test_crypto_openssl_version(void *arg)
tt_int_op(a, OP_GE, 0);
tt_int_op(b, OP_GE, 0);
tt_int_op(c, OP_GE, 0);
-#endif
+#endif /* defined(ENABLE_NSS) */
done:
;
@@ -389,7 +389,7 @@ test_crypto_aes128(void *arg)
"\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff");
crypto_cipher_crypt_inplace(env1, data2, 64);
- tt_assert(tor_mem_is_zero(data2, 64));
+ tt_assert(fast_mem_is_zero(data2, 64));
done:
tor_free(mem_op_hex_tmp);
@@ -1011,13 +1011,19 @@ test_crypto_sha3_xof(void *arg)
crypto_xof_free(xof);
memset(out, 0, sizeof(out));
+ /* Test one-function absorb/squeeze. */
+ crypto_xof(out, sizeof(out), msg, sizeof(msg));
+ test_memeq_hex(out, squeezed_hex);
+ memset(out, 0, sizeof(out));
+
/* Test incremental absorb/squeeze. */
xof = crypto_xof_new();
tt_assert(xof);
for (size_t i = 0; i < sizeof(msg); i++)
crypto_xof_add_bytes(xof, msg + i, 1);
- for (size_t i = 0; i < sizeof(out); i++)
+ for (size_t i = 0; i < sizeof(out); i++) {
crypto_xof_squeeze_bytes(xof, out + i, 1);
+ }
test_memeq_hex(out, squeezed_hex);
done:
@@ -1703,13 +1709,13 @@ test_crypto_base32_decode(void *arg)
/* Encode and decode a random string. */
base32_encode(encoded, 96 + 1, plain, 60);
res = base32_decode(decoded, 60, encoded, 96);
- tt_int_op(res,OP_EQ, 0);
+ tt_int_op(res, OP_EQ, 60);
tt_mem_op(plain,OP_EQ, decoded, 60);
/* Encode, uppercase, and decode a random string. */
base32_encode(encoded, 96 + 1, plain, 60);
tor_strupper(encoded);
res = base32_decode(decoded, 60, encoded, 96);
- tt_int_op(res,OP_EQ, 0);
+ tt_int_op(res, OP_EQ, 60);
tt_mem_op(plain,OP_EQ, decoded, 60);
/* Change encoded string and decode. */
if (encoded[0] == 'A' || encoded[0] == 'a')
@@ -1717,12 +1723,12 @@ test_crypto_base32_decode(void *arg)
else
encoded[0] = 'A';
res = base32_decode(decoded, 60, encoded, 96);
- tt_int_op(res,OP_EQ, 0);
+ tt_int_op(res, OP_EQ, 60);
tt_mem_op(plain,OP_NE, decoded, 60);
/* Bad encodings. */
encoded[0] = '!';
res = base32_decode(decoded, 60, encoded, 96);
- tt_int_op(0, OP_GT, res);
+ tt_int_op(res, OP_LT, 0);
done:
;
@@ -2069,7 +2075,7 @@ test_crypto_curve25519_encode(void *arg)
curve25519_secret_key_generate(&seckey, 0);
curve25519_public_key_generate(&key1, &seckey);
- tt_int_op(0, OP_EQ, curve25519_public_to_base64(buf, &key1));
+ curve25519_public_to_base64(buf, &key1);
tt_int_op(CURVE25519_BASE64_PADDED_LEN, OP_EQ, strlen(buf));
tt_int_op(0, OP_EQ, curve25519_public_from_base64(&key2, buf));
@@ -2128,7 +2134,7 @@ test_crypto_curve25519_persist(void *arg)
tt_u64_op((uint64_t)st.st_size, OP_EQ,
32+CURVE25519_PUBKEY_LEN+CURVE25519_SECKEY_LEN);
tt_assert(fast_memeq(content, "== c25519v1: testing ==", taglen));
- tt_assert(tor_mem_is_zero(content+taglen, 32-taglen));
+ tt_assert(fast_mem_is_zero(content+taglen, 32-taglen));
cp = content + 32;
tt_mem_op(keypair.seckey.secret_key,OP_EQ,
cp,
@@ -2449,13 +2455,13 @@ test_crypto_ed25519_encode(void *arg)
/* Test roundtrip. */
tt_int_op(0, OP_EQ, ed25519_keypair_generate(&kp, 0));
- tt_int_op(0, OP_EQ, ed25519_public_to_base64(buf, &kp.pubkey));
+ ed25519_public_to_base64(buf, &kp.pubkey);
tt_int_op(ED25519_BASE64_LEN, OP_EQ, strlen(buf));
tt_int_op(0, OP_EQ, ed25519_public_from_base64(&pk, buf));
tt_mem_op(kp.pubkey.pubkey, OP_EQ, pk.pubkey, ED25519_PUBKEY_LEN);
tt_int_op(0, OP_EQ, ed25519_sign(&sig1, (const uint8_t*)"ABC", 3, &kp));
- tt_int_op(0, OP_EQ, ed25519_signature_to_base64(buf, &sig1));
+ ed25519_signature_to_base64(buf, &sig1);
tt_int_op(0, OP_EQ, ed25519_signature_from_base64(&sig2, buf));
tt_mem_op(sig1.sig, OP_EQ, sig2.sig, ED25519_SIG_LEN);
diff --git a/src/test/test_crypto_rng.c b/src/test/test_crypto_rng.c
index 23b0c66514..6b7749a889 100644
--- a/src/test/test_crypto_rng.c
+++ b/src/test/test_crypto_rng.c
@@ -218,6 +218,14 @@ test_crypto_rng_fast(void *arg)
tt_int_op(counts[i], OP_GT, 0);
}
+ /* per-thread rand_fast shouldn't crash or leak. */
+ crypto_fast_rng_t *t_rng = get_thread_fast_rng();
+ for (int i = 0; i < N; ++i) {
+ uint64_t u64 = crypto_fast_rng_get_uint64(t_rng, UINT64_C(1)<<40);
+ tt_u64_op(u64, OP_GE, 0);
+ tt_u64_op(u64, OP_LT, UINT64_C(1)<<40);
+ }
+
done:
crypto_fast_rng_free(rng);
}
diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c
index e24aee8930..3b20dfa587 100644
--- a/src/test/test_crypto_slow.c
+++ b/src/test/test_crypto_slow.c
@@ -109,7 +109,7 @@ run_s2k_tests(const unsigned flags, const unsigned type,
secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
pw1, strlen(pw1)));
tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3));
- tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
+ tt_assert(!fast_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
done:
;
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 07a2641c9f..17d6db1e4d 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -8,7 +8,7 @@
#define BWAUTH_PRIVATE
#define CONFIG_PRIVATE
-#define CONTROL_PRIVATE
+#define CONTROL_GETINFO_PRIVATE
#define DIRCACHE_PRIVATE
#define DIRCLIENT_PRIVATE
#define DIRSERV_PRIVATE
@@ -32,7 +32,7 @@
#include "core/or/versions.h"
#include "feature/client/bridges.h"
#include "feature/client/entrynodes.h"
-#include "feature/control/control.h"
+#include "feature/control/control_getinfo.h"
#include "feature/dirauth/bwauth.h"
#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/dsigs_parse.h"
@@ -162,6 +162,269 @@ test_dir_nicknames(void *arg)
;
}
+/* Allocate and return a new routerinfo, with the fields set from the
+ * arguments to this function.
+ *
+ * Also sets:
+ * - random RSA identity and onion keys,
+ * - the platform field using get_platform_str(), and
+ * - supports_tunnelled_dir_requests to 1.
+ *
+ * If rsa_onion_keypair_out is not NULL, it is set to the onion keypair.
+ * The caller must free this keypair.
+ */
+static routerinfo_t *
+basic_routerinfo_new(const char *nickname, uint32_t ipv4_addr,
+ uint16_t or_port, uint16_t dir_port,
+ uint32_t bandwidthrate, uint32_t bandwidthburst,
+ uint32_t bandwidthcapacity,
+ time_t published_on,
+ crypto_pk_t **rsa_onion_keypair_out)
+{
+ char platform[256];
+
+ tor_assert(nickname);
+
+ crypto_pk_t *pk1 = NULL, *pk2 = NULL;
+ /* These keys are random: idx is ignored. */
+ pk1 = pk_generate(0);
+ pk2 = pk_generate(1);
+
+ tor_assert(pk1);
+ tor_assert(pk2);
+
+ get_platform_str(platform, sizeof(platform));
+
+ routerinfo_t *r1 = tor_malloc_zero(sizeof(routerinfo_t));
+
+ r1->nickname = tor_strdup(nickname);
+ r1->platform = tor_strdup(platform);
+
+ r1->addr = ipv4_addr;
+ r1->or_port = or_port;
+ r1->dir_port = dir_port;
+ r1->supports_tunnelled_dir_requests = 1;
+
+ router_set_rsa_onion_pkey(pk1, &r1->onion_pkey, &r1->onion_pkey_len);
+ r1->identity_pkey = pk2;
+
+ r1->bandwidthrate = bandwidthrate;
+ r1->bandwidthburst = bandwidthburst;
+ r1->bandwidthcapacity = bandwidthcapacity;
+
+ r1->cache_info.published_on = published_on;
+
+ if (rsa_onion_keypair_out) {
+ *rsa_onion_keypair_out = pk1;
+ } else {
+ crypto_pk_free(pk1);
+ }
+
+ return r1;
+}
+
+/* Allocate and return a new string containing a "router" line for r1. */
+static char *
+get_new_router_line(const routerinfo_t *r1)
+{
+ char *line = NULL;
+
+ tor_assert(r1);
+
+ tor_asprintf(&line,
+ "router %s %s %d 0 %d\n",
+ r1->nickname, fmt_addr32(r1->addr),
+ r1->or_port, r1->dir_port);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing a "platform" line for the
+ * current Tor version and OS. */
+static char *
+get_new_platform_line(void)
+{
+ char *line = NULL;
+
+ tor_asprintf(&line,
+ "platform Tor %s on %s\n",
+ VERSION, get_uname());
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing a "published" line for r1.
+ * r1->cache_info.published_on must be between 0 and 59 seconds. */
+static char *
+get_new_published_line(const routerinfo_t *r1)
+{
+ char *line = NULL;
+
+ tor_assert(r1);
+
+ tor_assert(r1->cache_info.published_on >= 0);
+ tor_assert(r1->cache_info.published_on <= 59);
+
+ tor_asprintf(&line,
+ "published 1970-01-01 00:00:%02u\n",
+ (unsigned)r1->cache_info.published_on);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing a "fingerprint" line for r1. */
+static char *
+get_new_fingerprint_line(const routerinfo_t *r1)
+{
+ char *line = NULL;
+ char fingerprint[FINGERPRINT_LEN+1];
+
+ tor_assert(r1);
+
+ tor_assert(!crypto_pk_get_fingerprint(r1->identity_pkey, fingerprint, 1));
+ tor_assert(strlen(fingerprint) > 0);
+
+ tor_asprintf(&line,
+ "fingerprint %s\n",
+ fingerprint);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing an "uptime" line with uptime t.
+ *
+ * You should pass a hard-coded value to this function, because even if we made
+ * it reflect uptime, that still wouldn't make it right, because the two
+ * descriptors might be made on different seconds.
+ */
+static char *
+get_new_uptime_line(time_t t)
+{
+ char *line = NULL;
+
+ tor_asprintf(&line,
+ "uptime %u\n",
+ (unsigned)t);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing an "bandwidth" line for r1.
+ */
+static char *
+get_new_bandwidth_line(const routerinfo_t *r1)
+{
+ char *line = NULL;
+
+ tor_assert(r1);
+
+ tor_asprintf(&line,
+ "bandwidth %u %u %u\n",
+ r1->bandwidthrate,
+ r1->bandwidthburst,
+ r1->bandwidthcapacity);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing a key_name block for the
+ * RSA key pk1.
+ */
+static char *
+get_new_rsa_key_block(const char *key_name, crypto_pk_t *pk1)
+{
+ char *block = NULL;
+ char *pk1_str = NULL;
+ size_t pk1_str_len = 0;
+
+ tor_assert(key_name);
+ tor_assert(pk1);
+
+ tor_assert(!crypto_pk_write_public_key_to_string(pk1, &pk1_str,
+ &pk1_str_len));
+ tor_assert(pk1_str);
+ tor_assert(pk1_str_len);
+
+ tor_asprintf(&block,
+ "%s\n%s",
+ key_name,
+ pk1_str);
+ tor_free(pk1_str);
+
+ tor_assert(block);
+ return block;
+}
+
+/* Allocate and return a new string containing an "onion-key" block for the
+ * router r1.
+ */
+static char *
+get_new_onion_key_block(const routerinfo_t *r1)
+{
+ char *block = NULL;
+ tor_assert(r1);
+ crypto_pk_t *pk_tmp = router_get_rsa_onion_pkey(r1->onion_pkey,
+ r1->onion_pkey_len);
+ block = get_new_rsa_key_block("onion-key", pk_tmp);
+ crypto_pk_free(pk_tmp);
+ return block;
+}
+
+/* Allocate and return a new string containing an "signing-key" block for the
+ * router r1.
+ */
+static char *
+get_new_signing_key_block(const routerinfo_t *r1)
+{
+ tor_assert(r1);
+ return get_new_rsa_key_block("signing-key", r1->identity_pkey);
+}
+
+/* Allocate and return a new string containing an "ntor-onion-key" line for
+ * the curve25519 public key ntor_onion_pubkey.
+ */
+static char *
+get_new_ntor_onion_key_line(const curve25519_public_key_t *ntor_onion_pubkey)
+{
+ char *line = NULL;
+ char cert_buf[256];
+ int rv = 0;
+
+ tor_assert(ntor_onion_pubkey);
+
+ rv = base64_encode(cert_buf, sizeof(cert_buf),
+ (const char*)ntor_onion_pubkey->public_key, 32,
+ BASE64_ENCODE_MULTILINE);
+ tor_assert(rv > 0);
+ tor_assert(strlen(cert_buf) > 0);
+
+ tor_asprintf(&line,
+ "ntor-onion-key %s",
+ cert_buf);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing a "bridge-distribution-request"
+ * line for options.
+ */
+static char *
+get_new_bridge_distribution_request_line(const or_options_t *options)
+{
+ if (options->BridgeRelay) {
+ return tor_strdup("bridge-distribution-request any\n");
+ } else {
+ return tor_strdup("");
+ }
+}
+
static smartlist_t *mocked_configured_ports = NULL;
/** Returns mocked_configured_ports */
@@ -171,71 +434,510 @@ mock_get_configured_ports(void)
return mocked_configured_ports;
}
-/** Run unit tests for router descriptor generation logic. */
+static tor_cert_t *
+mock_tor_cert_dup_null(const tor_cert_t *cert)
+{
+ (void)cert;
+ return NULL;
+}
+
+static crypto_pk_t *mocked_server_identitykey = NULL;
+
+/* Returns mocked_server_identitykey with no checks. */
+static crypto_pk_t *
+mock_get_server_identity_key(void)
+{
+ return mocked_server_identitykey;
+}
+
+static crypto_pk_t *mocked_onionkey = NULL;
+
+/* Returns mocked_onionkey with no checks. */
+static crypto_pk_t *
+mock_get_onion_key(void)
+{
+ return mocked_onionkey;
+}
+
+static routerinfo_t *mocked_routerinfo = NULL;
+
+/* Returns 0 and sets ri_out to mocked_routerinfo.
+ * ri_out must not be NULL. There are no other checks. */
+static int
+mock_router_build_fresh_unsigned_routerinfo(routerinfo_t **ri_out)
+{
+ tor_assert(ri_out);
+ *ri_out = mocked_routerinfo;
+ return 0;
+}
+
+static ed25519_keypair_t *mocked_master_signing_key = NULL;
+
+/* Returns mocked_master_signing_key with no checks. */
+static const ed25519_keypair_t *
+mock_get_master_signing_keypair(void)
+{
+ return mocked_master_signing_key;
+}
+
+static struct tor_cert_st *mocked_signing_key_cert = NULL;
+
+/* Returns mocked_signing_key_cert with no checks. */
+static const struct tor_cert_st *
+mock_get_master_signing_key_cert(void)
+{
+ return mocked_signing_key_cert;
+}
+
+static curve25519_keypair_t *mocked_curve25519_onion_key = NULL;
+
+/* Returns mocked_curve25519_onion_key with no checks. */
+static const curve25519_keypair_t *
+mock_get_current_curve25519_keypair(void)
+{
+ return mocked_curve25519_onion_key;
+}
+
+/* Unmock get_configured_ports() and free mocked_configured_ports. */
+static void
+cleanup_mock_configured_ports(void)
+{
+ UNMOCK(get_configured_ports);
+
+ if (mocked_configured_ports) {
+ SMARTLIST_FOREACH(mocked_configured_ports, port_cfg_t *, p, tor_free(p));
+ smartlist_free(mocked_configured_ports);
+ }
+}
+
+/* Mock get_configured_ports() with a list containing or_port and dir_port.
+ * If a port is 0, don't set it.
+ * Only sets the minimal data required for the tests to pass. */
+static void
+setup_mock_configured_ports(uint16_t or_port, uint16_t dir_port)
+{
+ cleanup_mock_configured_ports();
+
+ /* Fake just enough of an ORPort and DirPort to get by */
+ MOCK(get_configured_ports, mock_get_configured_ports);
+ mocked_configured_ports = smartlist_new();
+
+ if (or_port) {
+ port_cfg_t *or_port_cfg = tor_malloc_zero(sizeof(*or_port_cfg));
+ or_port_cfg->type = CONN_TYPE_OR_LISTENER;
+ or_port_cfg->addr.family = AF_INET;
+ or_port_cfg->port = or_port;
+ smartlist_add(mocked_configured_ports, or_port_cfg);
+ }
+
+ if (dir_port) {
+ port_cfg_t *dir_port_cfg = tor_malloc_zero(sizeof(*dir_port_cfg));
+ dir_port_cfg->type = CONN_TYPE_DIR_LISTENER;
+ dir_port_cfg->addr.family = AF_INET;
+ dir_port_cfg->port = dir_port;
+ smartlist_add(mocked_configured_ports, dir_port_cfg);
+ }
+}
+
+/* Clean up the data structures and unmock the functions needed for generating
+ * a fresh descriptor. */
+static void
+cleanup_mocks_for_fresh_descriptor(void)
+{
+ tor_free(get_options_mutable()->Nickname);
+
+ mocked_server_identitykey = NULL;
+ UNMOCK(get_server_identity_key);
+
+ crypto_pk_free(mocked_onionkey);
+ UNMOCK(get_onion_key);
+}
+
+/* Mock the data structures and functions needed for generating a fresh
+ * descriptor.
+ *
+ * Sets options->Nickname from r1->nickname.
+ * Mocks get_server_identity_key() with r1->identity_pkey.
+ *
+ * If rsa_onion_keypair is not NULL, it is used to mock get_onion_key().
+ * Otherwise, the public key in r1->onion_pkey is used to mock get_onion_key().
+ */
static void
-test_dir_formats(void *arg)
+setup_mocks_for_fresh_descriptor(const routerinfo_t *r1,
+ crypto_pk_t *rsa_onion_keypair)
+{
+ cleanup_mocks_for_fresh_descriptor();
+
+ tor_assert(r1);
+
+ /* router_build_fresh_signed_extrainfo() requires options->Nickname */
+ get_options_mutable()->Nickname = tor_strdup(r1->nickname);
+
+ /* router_build_fresh_signed_extrainfo() requires get_server_identity_key().
+ * Use the same one as the call to router_dump_router_to_string() above.
+ */
+ mocked_server_identitykey = r1->identity_pkey;
+ MOCK(get_server_identity_key, mock_get_server_identity_key);
+
+ /* router_dump_and_sign_routerinfo_descriptor_body() requires
+ * get_onion_key(). Use the same one as r1.
+ */
+ if (rsa_onion_keypair) {
+ mocked_onionkey = crypto_pk_dup_key(rsa_onion_keypair);
+ } else {
+ mocked_onionkey = router_get_rsa_onion_pkey(r1->onion_pkey,
+ r1->onion_pkey_len);
+ }
+ MOCK(get_onion_key, mock_get_onion_key);
+}
+
+/* Set options based on arg.
+ *
+ * b: BridgeRelay 1
+ * e: ExtraInfoStatistics 1
+ * s: sets all the individual statistics options to 1
+ *
+ * Always sets AssumeReachable to 1.
+ *
+ * Does not set ServerTransportPlugin, because it's parsed before use.
+ *
+ * Does not set BridgeRecordUsageByCountry, because the tests don't have access
+ * to a GeoIPFile or GeoIPv6File. */
+static void
+setup_dir_formats_options(const char *arg, or_options_t *options)
+{
+ /* Skip reachability checks for DirPort, ORPort, and tunnelled-dir-server */
+ options->AssumeReachable = 1;
+
+ if (strchr(arg, 'b')) {
+ options->BridgeRelay = 1;
+ }
+
+ if (strchr(arg, 'e')) {
+ options->ExtraInfoStatistics = 1;
+ }
+
+ if (strchr(arg, 's')) {
+ options->DirReqStatistics = 1;
+ options->HiddenServiceStatistics = 1;
+ options->EntryStatistics = 1;
+ options->CellStatistics = 1;
+ options->ExitPortStatistics = 1;
+ options->ConnDirectionStatistics = 1;
+ options->PaddingStatistics = 1;
+ }
+}
+
+/* Check that routerinfos r1 and rp1 are consistent.
+ * Only performs some basic checks.
+ */
+#define CHECK_ROUTERINFO_CONSISTENCY(r1, rp1) \
+STMT_BEGIN \
+ tt_assert(r1); \
+ tt_assert(rp1); \
+\
+ tt_int_op(rp1->addr,OP_EQ, r1->addr); \
+ tt_int_op(rp1->or_port,OP_EQ, r1->or_port); \
+ tt_int_op(rp1->dir_port,OP_EQ, r1->dir_port); \
+ tt_int_op(rp1->bandwidthrate,OP_EQ, r1->bandwidthrate); \
+ tt_int_op(rp1->bandwidthburst,OP_EQ, r1->bandwidthburst); \
+ tt_int_op(rp1->bandwidthcapacity,OP_EQ, r1->bandwidthcapacity); \
+ crypto_pk_t *rp1_onion_pkey = router_get_rsa_onion_pkey(rp1->onion_pkey, \
+ rp1->onion_pkey_len); \
+ crypto_pk_t *r1_onion_pkey = router_get_rsa_onion_pkey(r1->onion_pkey, \
+ r1->onion_pkey_len); \
+ tt_int_op(crypto_pk_cmp_keys(rp1_onion_pkey, r1_onion_pkey), OP_EQ, 0); \
+ crypto_pk_free(rp1_onion_pkey); \
+ crypto_pk_free(r1_onion_pkey); \
+ tt_int_op(crypto_pk_cmp_keys(rp1->identity_pkey, r1->identity_pkey), \
+ OP_EQ, 0); \
+ tt_int_op(rp1->supports_tunnelled_dir_requests, OP_EQ, \
+ r1->supports_tunnelled_dir_requests); \
+STMT_END
+
+/* Check that routerinfo r1 and extrainfo e1 are consistent.
+ * Only performs some basic checks.
+ */
+#define CHECK_EXTRAINFO_CONSISTENCY(r1, e1) \
+STMT_BEGIN \
+ tt_assert(r1); \
+ tt_assert(e1); \
+\
+ tt_str_op(e1->nickname, OP_EQ, r1->nickname); \
+STMT_END
+
+/** Run unit tests for router descriptor generation logic for a RSA-only
+ * router. Tor versions without ed25519 (0.2.6 and earlier) are no longer
+ * officially supported, but the authorities still accept their descriptors.
+ */
+static void
+test_dir_formats_rsa(void *arg)
{
char *buf = NULL;
- char buf2[8192];
- char platform[256];
- char fingerprint[FINGERPRINT_LEN+1];
- char *pk1_str = NULL, *pk2_str = NULL, *cp;
- size_t pk1_str_len, pk2_str_len;
- routerinfo_t *r1=NULL, *r2=NULL;
- crypto_pk_t *pk1 = NULL, *pk2 = NULL;
- routerinfo_t *rp1 = NULL, *rp2 = NULL;
- addr_policy_t *ex1, *ex2;
- routerlist_t *dir1 = NULL, *dir2 = NULL;
+ char *buf2 = NULL;
+ char *cp = NULL;
+
uint8_t *rsa_cc = NULL;
- or_options_t *options = get_options_mutable();
- 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);
- pk2 = pk_generate(1);
+ routerinfo_t *r1 = NULL;
+ extrainfo_t *e1 = NULL;
+ routerinfo_t *rp1 = NULL;
+ extrainfo_t *ep1 = NULL;
- tt_assert(pk1 && pk2);
+ smartlist_t *chunks = NULL;
+ const char *msg = NULL;
+ int rv = -1;
+
+ or_options_t *options = get_options_mutable();
+ setup_dir_formats_options((const char *)arg, options);
hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE);
- get_platform_str(platform, sizeof(platform));
- r1 = tor_malloc_zero(sizeof(routerinfo_t));
- r1->addr = 0xc0a80001u; /* 192.168.0.1 */
- r1->cache_info.published_on = 0;
- r1->or_port = 9000;
- r1->dir_port = 9003;
- r1->supports_tunnelled_dir_requests = 1;
- tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
- r1->ipv6_orport = 9999;
- router_set_rsa_onion_pkey(pk1, &r1->onion_pkey, &r1->onion_pkey_len);
- /* Fake just enough of an ntor key to get by */
+ /* r1 is a minimal, RSA-only descriptor, with DirPort and IPv6 */
+ r1 = basic_routerinfo_new("Magri", 0xc0a80001u /* 192.168.0.1 */,
+ 9000, 9003,
+ 1000, 5000, 10000,
+ 0,
+ NULL);
+
+ /* 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;
- r1->bandwidthcapacity = 10000;
+
+ /* Now add IPv6 */
+ tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
+ r1->ipv6_orport = 9999;
+
r1->exit_policy = NULL;
- r1->nickname = tor_strdup("Magri");
- r1->platform = tor_strdup(platform);
- ex1 = tor_malloc_zero(sizeof(addr_policy_t));
- ex2 = tor_malloc_zero(sizeof(addr_policy_t));
- ex1->policy_type = ADDR_POLICY_ACCEPT;
- tor_addr_from_ipv4h(&ex1->addr, 0);
- ex1->maskbits = 0;
- ex1->prt_min = ex1->prt_max = 80;
- ex2->policy_type = ADDR_POLICY_REJECT;
- tor_addr_from_ipv4h(&ex2->addr, 18<<24);
- ex2->maskbits = 8;
- ex2->prt_min = ex2->prt_max = 24;
- r2 = tor_malloc_zero(sizeof(routerinfo_t));
- r2->addr = 0x0a030201u; /* 10.3.2.1 */
+ /* XXXX+++ router_dump_to_string should really take this from ri. */
+ options->ContactInfo = tor_strdup("Magri White "
+ "<magri@elsewhere.example.com>");
+
+ setup_mock_configured_ports(r1->or_port, r1->dir_port);
+
+ buf = router_dump_router_to_string(r1, r1->identity_pkey, NULL, NULL, NULL);
+ tt_assert(buf);
+
+ tor_free(options->ContactInfo);
+ cleanup_mock_configured_ports();
+
+ /* Synthesise a router descriptor, without the signature */
+ chunks = smartlist_new();
+
+ smartlist_add(chunks, get_new_router_line(r1));
+ smartlist_add_strdup(chunks, "or-address [1:2:3:4::]:9999\n");
+
+ smartlist_add(chunks, get_new_platform_line());
+ smartlist_add(chunks, get_new_published_line(r1));
+ smartlist_add(chunks, get_new_fingerprint_line(r1));
+
+ smartlist_add(chunks, get_new_uptime_line(0));
+ smartlist_add(chunks, get_new_bandwidth_line(r1));
+
+ smartlist_add(chunks, get_new_onion_key_block(r1));
+ smartlist_add(chunks, get_new_signing_key_block(r1));
+
+ smartlist_add_strdup(chunks, "hidden-service-dir\n");
+
+ smartlist_add_strdup(chunks, "contact Magri White "
+ "<magri@elsewhere.example.com>\n");
+
+ smartlist_add(chunks, get_new_bridge_distribution_request_line(options));
+ smartlist_add(chunks, get_new_ntor_onion_key_line(&r1_onion_keypair.pubkey));
+ smartlist_add_strdup(chunks, "reject *:*\n");
+ smartlist_add_strdup(chunks, "tunnelled-dir-server\n");
+
+ smartlist_add_strdup(chunks, "router-signature\n");
+
+ size_t len_out = 0;
+ buf2 = smartlist_join_strings(chunks, "", 0, &len_out);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+
+ tt_assert(len_out > 0);
+
+ buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
+ * twice */
+
+ tt_str_op(buf,OP_EQ, buf2);
+ tor_free(buf);
+
+ setup_mock_configured_ports(r1->or_port, r1->dir_port);
+
+ buf = router_dump_router_to_string(r1, r1->identity_pkey, NULL, NULL, NULL);
+ tt_assert(buf);
+
+ cleanup_mock_configured_ports();
+
+ /* Now, try to parse buf */
+ cp = buf;
+ rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
+
+ CHECK_ROUTERINFO_CONSISTENCY(r1, rp1);
+
+ tt_assert(rp1->policy_is_reject_star);
+
+ tor_free(buf);
+ routerinfo_free(rp1);
+
+ /* Test extrainfo creation.
+ * We avoid calling router_build_fresh_unsigned_routerinfo(), because it's
+ * too complex. Instead, we re-use the manually-created routerinfos.
+ */
+
+ /* Set up standard mocks and data */
+ setup_mocks_for_fresh_descriptor(r1, NULL);
+
+ /* router_build_fresh_signed_extrainfo() passes the result of
+ * get_master_signing_key_cert() directly to tor_cert_dup(), which fails on
+ * NULL. But we want a NULL ei->cache_info.signing_key_cert to test the
+ * non-ed key path.
+ */
+ MOCK(tor_cert_dup, mock_tor_cert_dup_null);
+
+ /* Fake just enough of an ORPort and DirPort to get by */
+ setup_mock_configured_ports(r1->or_port, r1->dir_port);
+
+ /* Test some of the low-level static functions. */
+ e1 = router_build_fresh_signed_extrainfo(r1);
+ tt_assert(e1);
+ router_update_routerinfo_from_extrainfo(r1, e1);
+ rv = router_dump_and_sign_routerinfo_descriptor_body(r1);
+ tt_assert(rv == 0);
+ msg = "";
+ rv = routerinfo_incompatible_with_extrainfo(r1->identity_pkey, e1,
+ &r1->cache_info, &msg);
+ /* If they are incompatible, fail and show the msg string */
+ tt_str_op(msg, OP_EQ, "");
+ tt_assert(rv == 0);
+
+ /* Now cleanup */
+ cleanup_mocks_for_fresh_descriptor();
+
+ UNMOCK(tor_cert_dup);
+
+ cleanup_mock_configured_ports();
+
+ CHECK_EXTRAINFO_CONSISTENCY(r1, e1);
+
+ /* Test that the signed ri is parseable */
+ tt_assert(r1->cache_info.signed_descriptor_body);
+ cp = r1->cache_info.signed_descriptor_body;
+ rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
+
+ CHECK_ROUTERINFO_CONSISTENCY(r1, rp1);
+
+ tt_assert(rp1->policy_is_reject_star);
+
+ routerinfo_free(rp1);
+
+ /* Test that the signed ei is parseable */
+ tt_assert(e1->cache_info.signed_descriptor_body);
+ cp = e1->cache_info.signed_descriptor_body;
+ ep1 = extrainfo_parse_entry_from_string((const char*)cp,NULL,1,NULL,NULL);
+
+ CHECK_EXTRAINFO_CONSISTENCY(r1, ep1);
+
+ /* In future tests, we could check the actual extrainfo statistics. */
+
+ extrainfo_free(ep1);
+
+ done:
+ dirserv_free_fingerprint_list();
+
+ tor_free(options->ContactInfo);
+ tor_free(options->Nickname);
+
+ cleanup_mock_configured_ports();
+ cleanup_mocks_for_fresh_descriptor();
+
+ if (chunks) {
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+ }
+
+ routerinfo_free(r1);
+ routerinfo_free(rp1);
+
+ extrainfo_free(e1);
+ extrainfo_free(ep1);
+
+ tor_free(rsa_cc);
+
+ tor_free(buf);
+ tor_free(buf2);
+}
+
+/* Check that the exit policy in rp2 is as expected. */
+#define CHECK_PARSED_EXIT_POLICY(rp2) \
+STMT_BEGIN \
+ tt_int_op(smartlist_len(rp2->exit_policy),OP_EQ, 2); \
+ \
+ p = smartlist_get(rp2->exit_policy, 0); \
+ tt_int_op(p->policy_type,OP_EQ, ADDR_POLICY_ACCEPT); \
+ tt_assert(tor_addr_is_null(&p->addr)); \
+ tt_int_op(p->maskbits,OP_EQ, 0); \
+ tt_int_op(p->prt_min,OP_EQ, 80); \
+ tt_int_op(p->prt_max,OP_EQ, 80); \
+ \
+ p = smartlist_get(rp2->exit_policy, 1); \
+ tt_int_op(p->policy_type,OP_EQ, ADDR_POLICY_REJECT); \
+ tt_assert(tor_addr_eq(&p->addr, &ex2->addr)); \
+ tt_int_op(p->maskbits,OP_EQ, 8); \
+ tt_int_op(p->prt_min,OP_EQ, 24); \
+ tt_int_op(p->prt_max,OP_EQ, 24); \
+STMT_END
+
+/** Run unit tests for router descriptor generation logic for a RSA + ed25519
+ * router.
+ */
+static void
+test_dir_formats_rsa_ed25519(void *arg)
+{
+ char *buf = NULL;
+ char *buf2 = NULL;
+ char *cp = NULL;
+
+ crypto_pk_t *r2_onion_pkey = NULL;
+ char cert_buf[256];
+ uint8_t *rsa_cc = NULL;
+ time_t now = time(NULL);
+
+ routerinfo_t *r2 = NULL;
+ extrainfo_t *e2 = NULL;
+ routerinfo_t *r2_out = NULL;
+ routerinfo_t *rp2 = NULL;
+ extrainfo_t *ep2 = NULL;
+ addr_policy_t *ex1, *ex2;
+ const addr_policy_t *p;
+
+ smartlist_t *chunks = NULL;
+ int rv = -1;
+
+ or_options_t *options = get_options_mutable();
+ setup_dir_formats_options((const char *)arg, options);
+
+ hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE);
+
+ /* r2 is a RSA + ed25519 descriptor, with an exit policy, but no DirPort or
+ * IPv6 */
+ r2 = basic_routerinfo_new("Fred", 0x0a030201u /* 10.3.2.1 */,
+ 9005, 0,
+ 3000, 3000, 3000,
+ 5,
+ &r2_onion_pkey);
+
+ /* Fake just enough of an ntor key to get by */
+ curve25519_keypair_t r2_onion_keypair;
+ curve25519_keypair_generate(&r2_onion_keypair, 0);
+ r2->onion_curve25519_pkey = tor_memdup(&r2_onion_keypair.pubkey,
+ sizeof(curve25519_public_key_t));
+
+ /* Now add relay ed25519 keys
+ * We can't use init_mock_ed_keys() here, because the keys are seeded */
ed25519_keypair_t kp1, kp2;
ed25519_secret_key_from_seed(&kp1.seckey,
(const uint8_t*)"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY");
@@ -248,157 +950,78 @@ test_dir_formats(void *arg)
&kp2.pubkey,
now, 86400,
CERT_FLAG_INCLUDE_SIGNING_KEY);
- r2->platform = tor_strdup(platform);
- r2->cache_info.published_on = 5;
- r2->or_port = 9005;
- r2->dir_port = 0;
- r2->supports_tunnelled_dir_requests = 1;
- router_set_rsa_onion_pkey(pk2, &r2->onion_pkey, &r2->onion_pkey_len);
- curve25519_keypair_t r2_onion_keypair;
- curve25519_keypair_generate(&r2_onion_keypair, 0);
- r2->onion_curve25519_pkey = tor_memdup(&r2_onion_keypair.pubkey,
- sizeof(curve25519_public_key_t));
- r2->identity_pkey = crypto_pk_dup_key(pk1);
- r2->bandwidthrate = r2->bandwidthburst = r2->bandwidthcapacity = 3000;
+
+ /* Now add an exit policy */
+ ex1 = tor_malloc_zero(sizeof(addr_policy_t));
+ ex2 = tor_malloc_zero(sizeof(addr_policy_t));
+ ex1->policy_type = ADDR_POLICY_ACCEPT;
+ tor_addr_from_ipv4h(&ex1->addr, 0);
+ ex1->maskbits = 0;
+ ex1->prt_min = ex1->prt_max = 80;
+ ex2->policy_type = ADDR_POLICY_REJECT;
+ tor_addr_from_ipv4h(&ex2->addr, 18<<24);
+ ex2->maskbits = 8;
+ ex2->prt_min = ex2->prt_max = 24;
+
r2->exit_policy = smartlist_new();
smartlist_add(r2->exit_policy, ex1);
smartlist_add(r2->exit_policy, ex2);
- r2->nickname = tor_strdup("Fred");
-
- tt_assert(!crypto_pk_write_public_key_to_string(pk1, &pk1_str,
- &pk1_str_len));
- tt_assert(!crypto_pk_write_public_key_to_string(pk2 , &pk2_str,
- &pk2_str_len));
-
- /* 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 */
- options->AssumeReachable = 1;
-
- /* Fake just enough of an ORPort and DirPort to get by */
- MOCK(get_configured_ports, mock_get_configured_ports);
- mocked_configured_ports = smartlist_new();
-
- memset(&orport, 0, sizeof(orport));
- orport.type = CONN_TYPE_OR_LISTENER;
- orport.addr.family = AF_INET;
- orport.port = 9000;
- smartlist_add(mocked_configured_ports, &orport);
-
- memset(&dirport, 0, sizeof(dirport));
- dirport.type = CONN_TYPE_DIR_LISTENER;
- dirport.addr.family = AF_INET;
- dirport.port = 9003;
- smartlist_add(mocked_configured_ports, &dirport);
- buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL);
-
- UNMOCK(get_configured_ports);
- smartlist_free(mocked_configured_ports);
- mocked_configured_ports = NULL;
+ /* Fake just enough of an ORPort to get by */
+ setup_mock_configured_ports(r2->or_port, 0);
- tor_free(options->ContactInfo);
+ buf = router_dump_router_to_string(r2,
+ r2->identity_pkey, r2_onion_pkey,
+ &r2_onion_keypair, &kp2);
tt_assert(buf);
- strlcpy(buf2, "router Magri 192.168.0.1 9000 0 9003\n"
- "or-address [1:2:3:4::]:9999\n"
- "platform Tor "VERSION" on ", sizeof(buf2));
- strlcat(buf2, get_uname(), sizeof(buf2));
- strlcat(buf2, "\n"
- "published 1970-01-01 00:00:00\n"
- "fingerprint ", sizeof(buf2));
- tt_assert(!crypto_pk_get_fingerprint(pk2, fingerprint, 1));
- strlcat(buf2, fingerprint, sizeof(buf2));
- strlcat(buf2, "\nuptime 0\n"
- /* XXX the "0" above is hard-coded, but even if we made it reflect
- * uptime, that still wouldn't make it right, because the two
- * descriptors might be made on different seconds... hm. */
- "bandwidth 1000 5000 10000\n"
- "onion-key\n", sizeof(buf2));
- strlcat(buf2, pk1_str, sizeof(buf2));
- strlcat(buf2, "signing-key\n", sizeof(buf2));
- strlcat(buf2, pk2_str, sizeof(buf2));
- 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
- * twice */
+ cleanup_mock_configured_ports();
- tt_str_op(buf,OP_EQ, buf2);
- tor_free(buf);
+ chunks = smartlist_new();
- buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL);
- tt_assert(buf);
- cp = buf;
- rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
- tt_assert(rp1);
- tt_int_op(rp1->addr,OP_EQ, r1->addr);
- tt_int_op(rp1->or_port,OP_EQ, r1->or_port);
- tt_int_op(rp1->dir_port,OP_EQ, r1->dir_port);
- tt_int_op(rp1->bandwidthrate,OP_EQ, r1->bandwidthrate);
- tt_int_op(rp1->bandwidthburst,OP_EQ, r1->bandwidthburst);
- tt_int_op(rp1->bandwidthcapacity,OP_EQ, r1->bandwidthcapacity);
- crypto_pk_t *onion_pkey = router_get_rsa_onion_pkey(rp1->onion_pkey,
- rp1->onion_pkey_len);
- tt_int_op(crypto_pk_cmp_keys(onion_pkey, pk1), OP_EQ, 0);
- crypto_pk_free(onion_pkey);
- tt_int_op(crypto_pk_cmp_keys(rp1->identity_pkey, pk2), OP_EQ, 0);
- tt_assert(rp1->supports_tunnelled_dir_requests);
- //tt_assert(rp1->exit_policy == NULL);
- tor_free(buf);
+ /* Synthesise a router descriptor, without the signatures */
+ smartlist_add(chunks, get_new_router_line(r2));
- strlcpy(buf2,
- "router Fred 10.3.2.1 9005 0 0\n"
- "identity-ed25519\n"
- "-----BEGIN ED25519 CERT-----\n", sizeof(buf2));
+ smartlist_add_strdup(chunks,
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n");
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));
+ smartlist_add_strdup(chunks, cert_buf);
+ smartlist_add_strdup(chunks, "-----END ED25519 CERT-----\n");
+
+ smartlist_add_strdup(chunks, "master-key-ed25519 ");
{
char k[ED25519_BASE64_LEN+1];
- tt_int_op(ed25519_public_to_base64(k,
- &r2->cache_info.signing_key_cert->signing_key),
- OP_GE, 0);
- strlcat(buf2, k, sizeof(buf2));
- strlcat(buf2, "\n", sizeof(buf2));
+ ed25519_public_to_base64(k, &r2->cache_info.signing_key_cert->signing_key);
+ smartlist_add_strdup(chunks, k);
+ smartlist_add_strdup(chunks, "\n");
}
- strlcat(buf2, "platform Tor "VERSION" on ", sizeof(buf2));
- strlcat(buf2, get_uname(), sizeof(buf2));
- strlcat(buf2, "\n"
- "published 1970-01-01 00:00:05\n"
- "fingerprint ", sizeof(buf2));
- tt_assert(!crypto_pk_get_fingerprint(pk1, fingerprint, 1));
- strlcat(buf2, fingerprint, sizeof(buf2));
- strlcat(buf2, "\nuptime 0\n"
- "bandwidth 3000 3000 3000\n", sizeof(buf2));
- strlcat(buf2, "onion-key\n", sizeof(buf2));
- strlcat(buf2, pk2_str, sizeof(buf2));
- strlcat(buf2, "signing-key\n", sizeof(buf2));
- strlcat(buf2, pk1_str, sizeof(buf2));
+
+ smartlist_add(chunks, get_new_platform_line());
+ smartlist_add(chunks, get_new_published_line(r2));
+ smartlist_add(chunks, get_new_fingerprint_line(r2));
+
+ smartlist_add(chunks, get_new_uptime_line(0));
+ smartlist_add(chunks, get_new_bandwidth_line(r2));
+
+ smartlist_add(chunks, get_new_onion_key_block(r2));
+ smartlist_add(chunks, get_new_signing_key_block(r2));
+
int rsa_cc_len;
- rsa_cc = make_tap_onion_key_crosscert(pk2,
+ rsa_cc = make_tap_onion_key_crosscert(r2_onion_pkey,
&kp1.pubkey,
- pk1,
+ r2->identity_pkey,
&rsa_cc_len);
tt_assert(rsa_cc);
base64_encode(cert_buf, sizeof(cert_buf), (char*)rsa_cc, rsa_cc_len,
BASE64_ENCODE_MULTILINE);
- strlcat(buf2, "onion-key-crosscert\n"
- "-----BEGIN CROSSCERT-----\n", sizeof(buf2));
- strlcat(buf2, cert_buf, sizeof(buf2));
- strlcat(buf2, "-----END CROSSCERT-----\n", sizeof(buf2));
+ smartlist_add_strdup(chunks, "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n");
+ smartlist_add_strdup(chunks, cert_buf);
+ smartlist_add_strdup(chunks, "-----END CROSSCERT-----\n");
int ntor_cc_sign;
{
tor_cert_t *ntor_cc = NULL;
@@ -413,112 +1036,165 @@ test_dir_formats(void *arg)
BASE64_ENCODE_MULTILINE);
tor_cert_free(ntor_cc);
}
- tor_snprintf(buf2+strlen(buf2), sizeof(buf2)-strlen(buf2),
+ smartlist_add_asprintf(chunks,
"ntor-onion-key-crosscert %d\n"
"-----BEGIN ED25519 CERT-----\n"
"%s"
"-----END ED25519 CERT-----\n", ntor_cc_sign, cert_buf);
- strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
- strlcat(buf2, "ntor-onion-key ", sizeof(buf2));
- base64_encode(cert_buf, sizeof(cert_buf),
- (const char*)r2_onion_keypair.pubkey.public_key, 32,
- BASE64_ENCODE_MULTILINE);
- strlcat(buf2, cert_buf, sizeof(buf2));
- strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2));
- strlcat(buf2, "tunnelled-dir-server\n", sizeof(buf2));
- strlcat(buf2, "router-sig-ed25519 ", sizeof(buf2));
+ smartlist_add_strdup(chunks, "hidden-service-dir\n");
- /* Fake just enough of an ORPort to get by */
- MOCK(get_configured_ports, mock_get_configured_ports);
- mocked_configured_ports = smartlist_new();
+ smartlist_add(chunks, get_new_bridge_distribution_request_line(options));
+ smartlist_add(chunks, get_new_ntor_onion_key_line(&r2_onion_keypair.pubkey));
+ smartlist_add_strdup(chunks, "accept *:80\nreject 18.0.0.0/8:24\n");
+ smartlist_add_strdup(chunks, "tunnelled-dir-server\n");
- memset(&orport, 0, sizeof(orport));
- orport.type = CONN_TYPE_OR_LISTENER;
- orport.addr.family = AF_INET;
- orport.port = 9005;
- smartlist_add(mocked_configured_ports, &orport);
+ smartlist_add_strdup(chunks, "router-sig-ed25519 ");
- buf = router_dump_router_to_string(r2, pk1, pk2, &r2_onion_keypair, &kp2);
- tt_assert(buf);
- buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
+ size_t len_out = 0;
+ buf2 = smartlist_join_strings(chunks, "", 0, &len_out);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+
+ tt_assert(len_out > 0);
+
+ buf[strlen(buf2)] = '\0'; /* Don't compare either sig; they're never the same
* twice */
tt_str_op(buf, OP_EQ, buf2);
tor_free(buf);
- buf = router_dump_router_to_string(r2, pk1, NULL, NULL, NULL);
+ setup_mock_configured_ports(r2->or_port, 0);
- UNMOCK(get_configured_ports);
- smartlist_free(mocked_configured_ports);
- mocked_configured_ports = NULL;
+ buf = router_dump_router_to_string(r2, r2->identity_pkey, NULL, NULL, NULL);
+ tt_assert(buf);
- /* Reset for later */
+ cleanup_mock_configured_ports();
+
+ /* Now, try to parse buf */
cp = buf;
rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
- tt_assert(rp2);
- tt_int_op(rp2->addr,OP_EQ, r2->addr);
- tt_int_op(rp2->or_port,OP_EQ, r2->or_port);
- tt_int_op(rp2->dir_port,OP_EQ, r2->dir_port);
- tt_int_op(rp2->bandwidthrate,OP_EQ, r2->bandwidthrate);
- tt_int_op(rp2->bandwidthburst,OP_EQ, r2->bandwidthburst);
- tt_int_op(rp2->bandwidthcapacity,OP_EQ, r2->bandwidthcapacity);
+
+ CHECK_ROUTERINFO_CONSISTENCY(r2, rp2);
+
tt_mem_op(rp2->onion_curve25519_pkey->public_key,OP_EQ,
r2->onion_curve25519_pkey->public_key,
CURVE25519_PUBKEY_LEN);
- onion_pkey = router_get_rsa_onion_pkey(rp2->onion_pkey,
- rp2->onion_pkey_len);
- tt_int_op(crypto_pk_cmp_keys(onion_pkey, pk2), OP_EQ, 0);
- crypto_pk_free(onion_pkey);
- tt_int_op(crypto_pk_cmp_keys(rp2->identity_pkey, pk1), OP_EQ, 0);
- tt_assert(rp2->supports_tunnelled_dir_requests);
-
- tt_int_op(smartlist_len(rp2->exit_policy),OP_EQ, 2);
-
- p = smartlist_get(rp2->exit_policy, 0);
- tt_int_op(p->policy_type,OP_EQ, ADDR_POLICY_ACCEPT);
- tt_assert(tor_addr_is_null(&p->addr));
- tt_int_op(p->maskbits,OP_EQ, 0);
- tt_int_op(p->prt_min,OP_EQ, 80);
- tt_int_op(p->prt_max,OP_EQ, 80);
-
- p = smartlist_get(rp2->exit_policy, 1);
- tt_int_op(p->policy_type,OP_EQ, ADDR_POLICY_REJECT);
- tt_assert(tor_addr_eq(&p->addr, &ex2->addr));
- tt_int_op(p->maskbits,OP_EQ, 8);
- tt_int_op(p->prt_min,OP_EQ, 24);
- tt_int_op(p->prt_max,OP_EQ, 24);
-
-#if 0
- /* Okay, now for the directories. */
- {
- fingerprint_list = smartlist_new();
- crypto_pk_get_fingerprint(pk2, buf, 1);
- add_fingerprint_to_dir(buf, fingerprint_list, 0);
- crypto_pk_get_fingerprint(pk1, buf, 1);
- add_fingerprint_to_dir(buf, fingerprint_list, 0);
+
+ CHECK_PARSED_EXIT_POLICY(rp2);
+
+ tor_free(buf);
+ routerinfo_free(rp2);
+
+ /* Test extrainfo creation. */
+
+ /* Set up standard mocks and data */
+ setup_mocks_for_fresh_descriptor(r2, r2_onion_pkey);
+
+ /* router_build_fresh_descriptor() requires
+ * router_build_fresh_unsigned_routerinfo(), but the implementation is
+ * too complex. Instead, we re-use r2.
+ */
+ mocked_routerinfo = r2;
+ MOCK(router_build_fresh_unsigned_routerinfo,
+ mock_router_build_fresh_unsigned_routerinfo);
+
+ /* r2 uses ed25519, so we need to mock the ed key functions */
+ mocked_master_signing_key = &kp2;
+ MOCK(get_master_signing_keypair, mock_get_master_signing_keypair);
+
+ mocked_signing_key_cert = r2->cache_info.signing_key_cert;
+ MOCK(get_master_signing_key_cert, mock_get_master_signing_key_cert);
+
+ mocked_curve25519_onion_key = &r2_onion_keypair;
+ MOCK(get_current_curve25519_keypair, mock_get_current_curve25519_keypair);
+
+ /* Fake just enough of an ORPort to get by */
+ setup_mock_configured_ports(r2->or_port, 0);
+
+ /* Test the high-level interface. */
+ rv = router_build_fresh_descriptor(&r2_out, &e2);
+ if (rv < 0) {
+ /* router_build_fresh_descriptor() frees r2 on failure. */
+ r2 = NULL;
+ /* Get rid of an alias to rp2 */
+ r2_out = NULL;
}
+ tt_assert(rv == 0);
+ tt_assert(r2_out);
+ tt_assert(e2);
+ /* Guaranteed by mock_router_build_fresh_unsigned_routerinfo() */
+ tt_ptr_op(r2_out, OP_EQ, r2);
+ /* Get rid of an alias to r2 */
+ r2_out = NULL;
+
+ /* Now cleanup */
+ cleanup_mocks_for_fresh_descriptor();
+
+ mocked_routerinfo = NULL;
+ UNMOCK(router_build_fresh_unsigned_routerinfo);
+ mocked_master_signing_key = NULL;
+ UNMOCK(get_master_signing_keypair);
+ mocked_signing_key_cert = NULL;
+ UNMOCK(get_master_signing_key_cert);
+ mocked_curve25519_onion_key = NULL;
+ UNMOCK(get_current_curve25519_keypair);
+
+ cleanup_mock_configured_ports();
+
+ CHECK_EXTRAINFO_CONSISTENCY(r2, e2);
+
+ /* Test that the signed ri is parseable */
+ tt_assert(r2->cache_info.signed_descriptor_body);
+ cp = r2->cache_info.signed_descriptor_body;
+ rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
-#endif /* 0 */
- dirserv_free_fingerprint_list();
+ CHECK_ROUTERINFO_CONSISTENCY(r2, rp2);
+
+ tt_mem_op(rp2->onion_curve25519_pkey->public_key,OP_EQ,
+ r2->onion_curve25519_pkey->public_key,
+ CURVE25519_PUBKEY_LEN);
+
+ CHECK_PARSED_EXIT_POLICY(rp2);
+
+ routerinfo_free(rp2);
+
+ /* Test that the signed ei is parseable */
+ tt_assert(e2->cache_info.signed_descriptor_body);
+ cp = e2->cache_info.signed_descriptor_body;
+ ep2 = extrainfo_parse_entry_from_string((const char*)cp,NULL,1,NULL,NULL);
+
+ CHECK_EXTRAINFO_CONSISTENCY(r2, ep2);
+
+ /* In future tests, we could check the actual extrainfo statistics. */
+
+ extrainfo_free(ep2);
done:
- if (r1)
- routerinfo_free(r1);
- if (r2)
- routerinfo_free(r2);
- if (rp2)
- routerinfo_free(rp2);
+ dirserv_free_fingerprint_list();
+
+ tor_free(options->Nickname);
+
+ cleanup_mock_configured_ports();
+ cleanup_mocks_for_fresh_descriptor();
+
+ if (chunks) {
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+ }
+
+ routerinfo_free(r2);
+ routerinfo_free(r2_out);
+ routerinfo_free(rp2);
+
+ extrainfo_free(e2);
+ extrainfo_free(ep2);
tor_free(rsa_cc);
+ crypto_pk_free(r2_onion_pkey);
+
tor_free(buf);
- tor_free(pk1_str);
- tor_free(pk2_str);
- if (pk1) crypto_pk_free(pk1);
- if (pk2) crypto_pk_free(pk2);
- if (rp1) routerinfo_free(rp1);
- tor_free(dir1); /* XXXX And more !*/
- tor_free(dir2); /* And more !*/
+ tor_free(buf2);
}
#include "failing_routerdescs.inc"
@@ -6546,7 +7222,22 @@ test_dir_format_versions_list(void *arg)
struct testcase_t dir_tests[] = {
DIR_LEGACY(nicknames),
- DIR_LEGACY(formats),
+ /* extrainfo without any stats */
+ DIR_ARG(formats_rsa, TT_FORK, ""),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, ""),
+ /* on a bridge */
+ DIR_ARG(formats_rsa, TT_FORK, "b"),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, "b"),
+ /* extrainfo with basic stats */
+ DIR_ARG(formats_rsa, TT_FORK, "e"),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, "e"),
+ DIR_ARG(formats_rsa, TT_FORK, "be"),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, "be"),
+ /* extrainfo with all stats */
+ DIR_ARG(formats_rsa, TT_FORK, "es"),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, "es"),
+ DIR_ARG(formats_rsa, TT_FORK, "bes"),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, "bes"),
DIR(routerinfo_parsing, 0),
DIR(extrainfo_parsing, 0),
DIR(parse_router_list, TT_FORK),
diff --git a/src/test/test_dir_common.h b/src/test/test_dir_common.h
index d6c5241b14..619dc83eb9 100644
--- a/src/test/test_dir_common.h
+++ b/src/test/test_dir_common.h
@@ -3,6 +3,9 @@
* Copyright (c) 2007-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#ifndef TOR_TEST_DIR_COMMON_H
+#define TOR_TEST_DIR_COMMON_H
+
#include "core/or/or.h"
#include "feature/nodelist/networkstatus.h"
@@ -49,3 +52,4 @@ int dir_common_construct_vote_3(networkstatus_t **vote,
networkstatus_t **vote_out, int *n_vrs, time_t now,
int clear_rl);
+#endif /* !defined(TOR_TEST_DIR_COMMON_H) */
diff --git a/src/test/test_dispatch.c b/src/test/test_dispatch.c
new file mode 100644
index 0000000000..d6fe7e781a
--- /dev/null
+++ b/src/test/test_dispatch.c
@@ -0,0 +1,249 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define DISPATCH_PRIVATE
+
+#include "test/test.h"
+
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_cfg.h"
+#include "lib/dispatch/dispatch_st.h"
+#include "lib/dispatch/msgtypes.h"
+
+#include "lib/log/escape.h"
+#include "lib/malloc/malloc.h"
+#include "lib/string/printf.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static dispatch_t *dispatcher_in_use=NULL;
+
+/* Construct an empty dispatch_t. */
+static void
+test_dispatch_empty(void *arg)
+{
+ (void)arg;
+
+ dispatch_t *d=NULL;
+ dispatch_cfg_t *cfg=NULL;
+
+ cfg = dcfg_new();
+ d = dispatch_new(cfg);
+ tt_assert(d);
+
+ done:
+ dispatch_free(d);
+ dcfg_free(cfg);
+}
+
+static int total_recv1_simple = 0;
+static int total_recv2_simple = 0;
+
+static void
+simple_recv1(const msg_t *m)
+{
+ total_recv1_simple += m->aux_data__.u64;
+}
+
+static char *recv2_received = NULL;
+
+static void
+simple_recv2(const msg_t *m)
+{
+ tor_free(recv2_received);
+ recv2_received = dispatch_fmt_msg_data(dispatcher_in_use, m);
+
+ total_recv2_simple += m->aux_data__.u64*10;
+}
+
+/* Construct a dispatch_t with two messages, make sure that they both get
+ * delivered. */
+static void
+test_dispatch_simple(void *arg)
+{
+ (void)arg;
+
+ dispatch_t *d=NULL;
+ dispatch_cfg_t *cfg=NULL;
+ int r;
+
+ cfg = dcfg_new();
+ r = dcfg_msg_set_type(cfg,0,0);
+ r += dcfg_msg_set_chan(cfg,0,0);
+ r += dcfg_add_recv(cfg,0,1,simple_recv1);
+ r += dcfg_msg_set_type(cfg,1,0);
+ r += dcfg_msg_set_chan(cfg,1,0);
+ r += dcfg_add_recv(cfg,1,1,simple_recv2);
+ r += dcfg_add_recv(cfg,1,1,simple_recv2); /* second copy */
+ tt_int_op(r, OP_EQ, 0);
+
+ d = dispatch_new(cfg);
+ tt_assert(d);
+ dispatcher_in_use = d;
+
+ msg_aux_data_t data = {.u64 = 7};
+ r = dispatch_send(d, 99, 0, 0, 0, data);
+ tt_int_op(r, OP_EQ, 0);
+ tt_int_op(total_recv1_simple, OP_EQ, 0);
+
+ r = dispatch_flush(d, 0, INT_MAX);
+ tt_int_op(r, OP_EQ, 0);
+ tt_int_op(total_recv1_simple, OP_EQ, 7);
+ tt_int_op(total_recv2_simple, OP_EQ, 0);
+
+ total_recv1_simple = 0;
+ r = dispatch_send(d, 99, 0, 1, 0, data);
+ tt_int_op(r, OP_EQ, 0);
+ r = dispatch_flush(d, 0, INT_MAX);
+ tt_int_op(total_recv1_simple, OP_EQ, 0);
+ tt_int_op(total_recv2_simple, OP_EQ, 140);
+
+ tt_str_op(recv2_received, OP_EQ, "<>"); // no format function was set.
+
+ done:
+ dispatch_free(d);
+ dcfg_free(cfg);
+ tor_free(recv2_received);
+}
+
+/* Construct a dispatch_t with a message and no reciever; make sure that it
+ * gets dropped properly. */
+static void
+test_dispatch_no_recipient(void *arg)
+{
+ (void)arg;
+
+ dispatch_t *d=NULL;
+ dispatch_cfg_t *cfg=NULL;
+ int r;
+
+ cfg = dcfg_new();
+ r = dcfg_msg_set_type(cfg,0,0);
+ r += dcfg_msg_set_chan(cfg,0,0);
+ tt_int_op(r, OP_EQ, 0);
+
+ d = dispatch_new(cfg);
+ tt_assert(d);
+ dispatcher_in_use = d;
+
+ msg_aux_data_t data = { .u64 = 7};
+ r = dispatch_send(d, 99, 0, 0, 0, data);
+ tt_int_op(r, OP_EQ, 0);
+
+ r = dispatch_flush(d, 0, INT_MAX);
+ tt_int_op(r, OP_EQ, 0);
+
+ done:
+ dispatch_free(d);
+ dcfg_free(cfg);
+}
+
+struct coord { int x; int y; };
+static void
+free_coord(msg_aux_data_t d)
+{
+ tor_free(d.ptr);
+}
+static char *
+fmt_coord(msg_aux_data_t d)
+{
+ char *v;
+ struct coord *c = d.ptr;
+ tor_asprintf(&v, "[%d, %d]", c->x, c->y);
+ return v;
+}
+static dispatch_typefns_t coord_fns = {
+ .fmt_fn = fmt_coord,
+ .free_fn = free_coord,
+};
+static void
+alert_run_immediate(dispatch_t *d, channel_id_t ch, void *arg)
+{
+ (void)arg;
+ dispatch_flush(d, ch, INT_MAX);
+}
+
+static char *received_data=NULL;
+
+static void
+recv_typed_data(const msg_t *m)
+{
+ tor_free(received_data);
+ received_data = dispatch_fmt_msg_data(dispatcher_in_use, m);
+}
+
+static void
+test_dispatch_with_types(void *arg)
+{
+ (void)arg;
+
+ dispatch_t *d=NULL;
+ dispatch_cfg_t *cfg=NULL;
+ int r;
+
+ cfg = dcfg_new();
+ r = dcfg_msg_set_type(cfg,5,3);
+ r += dcfg_msg_set_chan(cfg,5,2);
+ r += dcfg_add_recv(cfg,5,0,recv_typed_data);
+ r += dcfg_type_set_fns(cfg,3,&coord_fns);
+ tt_int_op(r, OP_EQ, 0);
+
+ d = dispatch_new(cfg);
+ tt_assert(d);
+ dispatcher_in_use = d;
+
+ /* Make this message get run immediately. */
+ r = dispatch_set_alert_fn(d, 2, alert_run_immediate, NULL);
+ tt_int_op(r, OP_EQ, 0);
+
+ struct coord *xy = tor_malloc(sizeof(*xy));
+ xy->x = 13;
+ xy->y = 37;
+ msg_aux_data_t data = {.ptr = xy};
+ r = dispatch_send(d, 99/*sender*/, 2/*channel*/, 5/*msg*/, 3/*type*/, data);
+ tt_int_op(r, OP_EQ, 0);
+ tt_str_op(received_data, OP_EQ, "[13, 37]");
+
+ done:
+ dispatch_free(d);
+ dcfg_free(cfg);
+ tor_free(received_data);
+ dispatcher_in_use = NULL;
+}
+
+static void
+test_dispatch_bad_type_setup(void *arg)
+{
+ (void)arg;
+ static dispatch_typefns_t fns;
+ dispatch_cfg_t *cfg = dcfg_new();
+
+ tt_int_op(0, OP_EQ, dcfg_type_set_fns(cfg, 7, &coord_fns));
+
+ fns = coord_fns;
+ fns.fmt_fn = NULL;
+ tt_int_op(-1, OP_EQ, dcfg_type_set_fns(cfg, 7, &fns));
+
+ fns = coord_fns;
+ fns.free_fn = NULL;
+ tt_int_op(-1, OP_EQ, dcfg_type_set_fns(cfg, 7, &fns));
+
+ fns = coord_fns;
+ tt_int_op(0, OP_EQ, dcfg_type_set_fns(cfg, 7, &fns));
+
+ done:
+ dcfg_free(cfg);
+}
+
+#define T(name) \
+ { #name, test_dispatch_ ## name, TT_FORK, NULL, NULL }
+
+struct testcase_t dispatch_tests[] = {
+ T(empty),
+ T(simple),
+ T(no_recipient),
+ T(with_types),
+ T(bad_type_setup),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index 231e6965f7..51ff8729d0 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -67,7 +67,7 @@ NS(test_main)(void *arg)
tt_assert(tor_addr_family(nameserver_addr) == AF_INET);
tt_assert(tor_addr_eq_ipv4h(nameserver_addr, 0x7f000001));
-#endif
+#endif /* !defined(_WIN32) */
UNMOCK(get_options);
@@ -77,7 +77,7 @@ NS(test_main)(void *arg)
}
#undef NS_SUBMODULE
-#endif
+#endif /* defined(HAVE_EVDNS_BASE_GET_NAMESERVER_ADDR) */
#define NS_SUBMODULE clip_ttl
diff --git a/src/test/test_dos.c b/src/test/test_dos.c
index 4756c5014e..bda9908e6c 100644
--- a/src/test/test_dos.c
+++ b/src/test/test_dos.c
@@ -411,7 +411,7 @@ test_dos_bucket_refill(void *arg)
}
tt_uint_op(current_circ_count, OP_EQ, 0);
tt_uint_op(dos_stats->cc_stats.circuit_bucket, OP_EQ, current_circ_count);
-#endif
+#endif /* SIZEOF_TIME_T == 8 */
done:
tor_free(chan);
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index 729795b674..c43b21c673 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -67,7 +67,7 @@ static networkstatus_t *dummy_consensus = NULL;
static smartlist_t *big_fake_net_nodes = NULL;
-static smartlist_t *
+static const smartlist_t *
bfn_mock_nodelist_get_list(void)
{
return big_fake_net_nodes;
@@ -197,6 +197,7 @@ big_fake_network_setup(const struct testcase_t *testcase)
n->md->exit_policy = parse_short_policy("accept 443");
}
+ n->nodelist_idx = smartlist_len(big_fake_net_nodes);
smartlist_add(big_fake_net_nodes, n);
}
diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c
index aeb71ec583..38aca90266 100644
--- a/src/test/test_extorport.c
+++ b/src/test/test_extorport.c
@@ -9,7 +9,7 @@
#include "core/mainloop/connection.h"
#include "core/or/connection_or.h"
#include "app/config/config.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/relay/ext_orport.h"
#include "core/mainloop/mainloop.h"
@@ -18,6 +18,7 @@
#include "test/test.h"
#include "test/test_helpers.h"
+#include "test/rng_test_helpers.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
@@ -176,7 +177,7 @@ test_ext_or_init_auth(void *arg)
/* Shouldn't be initialized already, or our tests will be a bit
* meaningless */
ext_or_auth_cookie = tor_malloc_zero(32);
- tt_assert(tor_mem_is_zero((char*)ext_or_auth_cookie, 32));
+ tt_assert(fast_mem_is_zero((char*)ext_or_auth_cookie, 32));
/* Now make sure we use a temporary file */
fn = get_fname("ext_cookie_file");
@@ -201,7 +202,7 @@ test_ext_or_init_auth(void *arg)
tt_mem_op(cp,OP_EQ, "! Extended ORPort Auth Cookie !\x0a", 32);
tt_mem_op(cp+32,OP_EQ, ext_or_auth_cookie, 32);
memcpy(cookie0, ext_or_auth_cookie, 32);
- tt_assert(!tor_mem_is_zero((char*)ext_or_auth_cookie, 32));
+ tt_assert(!fast_mem_is_zero((char*)ext_or_auth_cookie, 32));
/* Operation should be idempotent. */
tt_int_op(0, OP_EQ, init_ext_or_cookie_authentication(1));
@@ -303,16 +304,6 @@ test_ext_or_cookie_auth(void *arg)
}
static void
-crypto_rand_return_tse_str(char *to, size_t n)
-{
- if (n != 32) {
- TT_FAIL(("Asked for %d bytes, not 32", (int)n));
- return;
- }
- memcpy(to, "te road There is always another ", 32);
-}
-
-static void
test_ext_or_cookie_auth_testvec(void *arg)
{
char *reply=NULL, *client_hash=NULL;
@@ -326,7 +317,7 @@ test_ext_or_cookie_auth_testvec(void *arg)
memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32);
ext_or_auth_cookie_is_set = 1;
- MOCK(crypto_rand, crypto_rand_return_tse_str);
+ testing_enable_prefilled_rng("te road There is always another ", 32);
tt_int_op(0, OP_EQ,
handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply,
@@ -351,7 +342,7 @@ test_ext_or_cookie_auth_testvec(void *arg)
"33b3cd77ff79bd80c2074bbf438119a2");
done:
- UNMOCK(crypto_rand);
+ testing_disable_prefilled_rng();
tor_free(reply);
tor_free(client_hash);
tor_free(mem_op_hex_tmp);
@@ -414,9 +405,9 @@ do_ext_or_handshake(or_connection_t *conn)
CONTAINS("\x01\x00", 2);
WRITE("\x01", 1);
WRITE("But when I look ahead up the whi", 32);
- MOCK(crypto_rand, crypto_rand_return_tse_str);
+ testing_enable_prefilled_rng("te road There is always another ", 32);
tt_int_op(0, OP_EQ, connection_ext_or_process_inbuf(conn));
- UNMOCK(crypto_rand);
+ testing_disable_prefilled_rng();
tt_int_op(TO_CONN(conn)->state, OP_EQ,
EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH);
CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
@@ -481,9 +472,9 @@ test_ext_or_handshake(void *arg)
tt_int_op(0, OP_EQ, connection_ext_or_process_inbuf(conn));
/* send the rest of the nonce. */
WRITE("ahead up the whi", 16);
- MOCK(crypto_rand, crypto_rand_return_tse_str);
+ testing_enable_prefilled_rng("te road There is always another ", 32);
tt_int_op(0, OP_EQ, connection_ext_or_process_inbuf(conn));
- UNMOCK(crypto_rand);
+ testing_disable_prefilled_rng();
/* We should get the right reply from the server. */
CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
"\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21"
@@ -582,7 +573,7 @@ test_ext_or_handshake(void *arg)
done:
UNMOCK(connection_write_to_buf_impl_);
- UNMOCK(crypto_rand);
+ testing_disable_prefilled_rng();
if (conn)
connection_free_minimal(TO_CONN(conn));
#undef CONTAINS
diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c
index 13de1e154b..489c257761 100644
--- a/src/test/test_helpers.c
+++ b/src/test/test_helpers.c
@@ -78,7 +78,7 @@ helper_setup_fake_routerlist(void)
{
int retval;
routerlist_t *our_routerlist = NULL;
- smartlist_t *our_nodelist = NULL;
+ const smartlist_t *our_nodelist = NULL;
/* Read the file that contains our test descriptors. */
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
index a611b46ca6..2b69aae547 100644
--- a/src/test/test_hs.c
+++ b/src/test/test_hs.c
@@ -6,7 +6,7 @@
* \brief Unit tests for hidden service.
**/
-#define CONTROL_PRIVATE
+#define CONTROL_EVENTS_PRIVATE
#define CIRCUITBUILD_PRIVATE
#define RENDCOMMON_PRIVATE
#define RENDSERVICE_PRIVATE
@@ -15,6 +15,8 @@
#include "core/or/or.h"
#include "test/test.h"
#include "feature/control/control.h"
+#include "feature/control/control_events.h"
+#include "feature/control/control_fmt.h"
#include "app/config/config.h"
#include "feature/hs/hs_common.h"
#include "feature/rend/rendcommon.h"
@@ -321,6 +323,16 @@ test_hs_desc_event(void *arg)
tt_str_op(received_msg,OP_EQ, expected_msg);
tor_free(received_msg);
+ /* test HSDir rate limited */
+ rend_query.auth_type = REND_NO_AUTH;
+ control_event_hsv2_descriptor_failed(&rend_query.base_, NULL,
+ "QUERY_RATE_LIMITED");
+ expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" NO_AUTH " \
+ "UNKNOWN REASON=QUERY_RATE_LIMITED\r\n";
+ tt_assert(received_msg);
+ tt_str_op(received_msg,OP_EQ, expected_msg);
+ tor_free(received_msg);
+
/* Test invalid content with no HSDir fingerprint. */
char *exp_msg;
control_event_hs_descriptor_content(rend_query.onion_address,
@@ -436,7 +448,7 @@ test_hs_rend_data(void *arg)
tt_int_op(client_v2->auth_type, OP_EQ, REND_BASIC_AUTH);
tt_int_op(strlen(client_v2->onion_address), OP_EQ, 0);
tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id));
- tt_int_op(tor_mem_is_zero(client_v2->descriptor_cookie,
+ tt_int_op(fast_mem_is_zero(client_v2->descriptor_cookie,
sizeof(client_v2->descriptor_cookie)), OP_EQ, 1);
tt_assert(client->hsdirs_fp);
tt_int_op(smartlist_len(client->hsdirs_fp), OP_EQ, 0);
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index 9182829116..d71f8b6b18 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -238,14 +238,13 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key)
{
char hsdir_cache_key[ED25519_BASE64_LEN+1];
- retval = ed25519_public_to_base64(hsdir_cache_key,
- blinded_key);
- tt_int_op(retval, OP_EQ, 0);
+ ed25519_public_to_base64(hsdir_cache_key, blinded_key);
tor_asprintf(&hsdir_query_str, GET("/tor/hs/3/%s"), hsdir_cache_key);
}
/* Simulate an HTTP GET request to the HSDir */
conn = dir_connection_new(AF_INET);
+ tt_assert(conn);
tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001);
TO_CONN(conn)->linked = 1;/* Pretend the conn is encrypted :) */
retval = directory_handle_command_get(conn, hsdir_query_str,
@@ -487,7 +486,7 @@ test_client_cache(void *arg)
NULL, &published_desc_str);
tt_int_op(retval, OP_EQ, 0);
memcpy(wanted_subcredential, published_desc->subcredential, DIGEST256_LEN);
- tt_assert(!tor_mem_is_zero((char*)wanted_subcredential, DIGEST256_LEN));
+ tt_assert(!fast_mem_is_zero((char*)wanted_subcredential, DIGEST256_LEN));
}
/* Test handle_response_fetch_hsdesc_v3() */
diff --git a/src/test/test_hs_cell.c b/src/test/test_hs_cell.c
index f8af631c8b..cdcbe23e69 100644
--- a/src/test/test_hs_cell.c
+++ b/src/test/test_hs_cell.c
@@ -39,7 +39,7 @@ test_gen_establish_intro_cell(void *arg)
attempt to parse it. */
{
/* We only need the auth key pair here. */
- hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0, 0);
+ hs_service_intro_point_t *ip = service_intro_point_new(NULL);
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
ret = hs_cell_build_establish_intro(circ_nonce, ip, buf);
@@ -107,7 +107,7 @@ test_gen_establish_intro_cell_bad(void *arg)
ed25519_sign_prefixed() function and make it fail. */
cell = trn_cell_establish_intro_new();
tt_assert(cell);
- ip = service_intro_point_new(NULL, 0, 0);
+ ip = service_intro_point_new(NULL);
cell_len = hs_cell_build_establish_intro(circ_nonce, ip, NULL);
service_intro_point_free(ip);
expect_log_msg_containing("Unable to make signature for "
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index 2f2bb45581..0d25a98bb3 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -14,6 +14,7 @@
#define CIRCUITBUILD_PRIVATE
#define CIRCUITLIST_PRIVATE
#define CONNECTION_PRIVATE
+#define CRYPT_PATH_PRIVATE
#include "test/test.h"
#include "test/test_helpers.h"
@@ -44,6 +45,7 @@
#include "core/or/cpath_build_state_st.h"
#include "core/or/crypt_path_st.h"
+#include "core/or/crypt_path.h"
#include "feature/dircommon/dir_connection_st.h"
#include "core/or/entry_connection_st.h"
#include "core/or/extend_info_st.h"
@@ -241,12 +243,14 @@ test_e2e_rend_circuit_setup_legacy(void *arg)
tt_int_op(retval, OP_EQ, 1);
/* Check the digest algo */
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.f_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.f_digest),
OP_EQ, DIGEST_SHA1);
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.b_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.b_digest),
OP_EQ, DIGEST_SHA1);
- tt_assert(or_circ->cpath->crypto.f_crypto);
- tt_assert(or_circ->cpath->crypto.b_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.f_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.b_crypto);
/* Ensure that circ purpose was changed */
tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
@@ -311,12 +315,14 @@ test_e2e_rend_circuit_setup(void *arg)
tt_int_op(retval, OP_EQ, 1);
/* Check that the crypt path has prop224 algorithm parameters */
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.f_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.f_digest),
OP_EQ, DIGEST_SHA3_256);
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.b_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.b_digest),
OP_EQ, DIGEST_SHA3_256);
- tt_assert(or_circ->cpath->crypto.f_crypto);
- tt_assert(or_circ->cpath->crypto.b_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.f_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.b_crypto);
/* Ensure that circ purpose was changed */
tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
@@ -395,7 +401,7 @@ test_client_pick_intro(void *arg)
tt_assert(fetched_desc);
tt_mem_op(fetched_desc->subcredential, OP_EQ, desc->subcredential,
DIGEST256_LEN);
- tt_assert(!tor_mem_is_zero((char*)fetched_desc->subcredential,
+ tt_assert(!fast_mem_is_zero((char*)fetched_desc->subcredential,
DIGEST256_LEN));
tor_free(encoded);
}
@@ -403,6 +409,9 @@ test_client_pick_intro(void *arg)
/* 2) Mark all intro points except _the chosen one_ as failed. Then query the
* desc and get a random intro: check that we got _the chosen one_. */
{
+ /* Tell hs_get_extend_info_from_lspecs() to skip the private address check.
+ */
+ get_options_mutable()->ExtendAllowPrivateAddresses = 1;
/* Pick the chosen intro point and get its ei */
hs_desc_intro_point_t *chosen_intro_point =
smartlist_get(desc->encrypted_data.intro_points, 0);
@@ -430,7 +439,7 @@ test_client_pick_intro(void *arg)
for (int i = 0; i < 64; ++i) {
extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
tor_assert(ip);
- tt_assert(!tor_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN));
+ tt_assert(!fast_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN));
tt_mem_op(ip->identity_digest, OP_EQ, chosen_intro_ei->identity_digest,
DIGEST_LEN);
extend_info_free(ip);
@@ -476,6 +485,18 @@ test_client_pick_intro(void *arg)
SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
hs_desc_intro_point_t *, ip) {
extend_info_t *intro_ei = desc_intro_point_to_extend_info(ip);
+ /* desc_intro_point_to_extend_info() doesn't return IPv6 intro points
+ * yet, because we can't extend to them. See #24404, #24451, and #24181.
+ */
+ if (intro_ei == NULL) {
+ /* Pretend we're making a direct connection, and that we can use IPv6
+ */
+ get_options_mutable()->ClientUseIPv6 = 1;
+ intro_ei = hs_get_extend_info_from_lspecs(ip->link_specifiers,
+ &ip->onion_key, 1);
+ tt_assert(tor_addr_family(&intro_ei->addr) == AF_INET6);
+ }
+ tt_assert(intro_ei);
if (intro_ei) {
const char *ptr;
char ip_addr[TOR_ADDR_BUF_LEN];
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
index eb7f3bfbb0..abded6021e 100644
--- a/src/test/test_hs_common.c
+++ b/src/test/test_hs_common.c
@@ -275,7 +275,7 @@ test_start_time_of_next_time_period(void *arg)
static void
cleanup_nodelist(void)
{
- smartlist_t *nodelist = nodelist_get_list();
+ const smartlist_t *nodelist = nodelist_get_list();
SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) {
tor_free(node->md);
node->md = NULL;
@@ -603,6 +603,10 @@ test_desc_reupload_logic(void *arg)
SMARTLIST_FOREACH(ns->routerstatus_list,
routerstatus_t *, rs, routerstatus_free(rs));
smartlist_clear(ns->routerstatus_list);
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
networkstatus_vote_free(ns);
cleanup_nodelist();
hs_free_all();
diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c
index ba67712f1b..7cedc987bb 100644
--- a/src/test/test_hs_control.c
+++ b/src/test/test_hs_control.c
@@ -6,11 +6,13 @@
* \brief Unit tests for hidden service control port event and command.
**/
-#define CONTROL_PRIVATE
+#define CONTROL_EVENTS_PRIVATE
#include "core/or/or.h"
#include "test/test.h"
#include "feature/control/control.h"
+#include "feature/control/control_events.h"
+#include "feature/control/control_fmt.h"
#include "app/config/config.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_control.h"
@@ -105,8 +107,7 @@ test_hs_desc_event(void *arg)
memset(&blinded_pk, 'B', sizeof(blinded_pk));
memset(&hsdir_rs, 0, sizeof(hsdir_rs));
memcpy(hsdir_rs.identity_digest, HSDIR_EXIST_ID, DIGEST_LEN);
- ret = ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
- tt_int_op(ret, OP_EQ, 0);
+ ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
memcpy(&ident.identity_pk, &identity_kp.pubkey,
sizeof(ed25519_public_key_t));
memcpy(&ident.blinded_pk, &blinded_pk, sizeof(blinded_pk));
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index de584ed47a..6fe5573c0f 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -21,6 +21,7 @@
#include "test/hs_test_helpers.h"
#include "test/test_helpers.h"
#include "test/log_test_helpers.h"
+#include "test/rng_test_helpers.h"
#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
DISABLE_GCC_WARNING(overlength-strings)
@@ -30,13 +31,6 @@ DISABLE_GCC_WARNING(overlength-strings)
#include "test_hs_descriptor.inc"
ENABLE_GCC_WARNING(overlength-strings)
-/* Mock function to fill all bytes with 1 */
-static void
-mock_crypto_strongest_rand(uint8_t *out, size_t out_len)
-{
- memset(out, 1, out_len);
-}
-
/* Test certificate encoding put in a descriptor. */
static void
test_cert_encoding(void *arg)
@@ -132,7 +126,7 @@ test_descriptor_padding(void *arg)
tt_assert(padded_plaintext);
tor_free(plaintext);
/* Make sure our padding has been zeroed. */
- tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len,
+ tt_int_op(fast_mem_is_zero((char *) padded_plaintext + plaintext_len,
padded_len - plaintext_len), OP_EQ, 1);
tor_free(padded_plaintext);
/* Never never have a padded length smaller than the plaintext. */
@@ -149,7 +143,7 @@ test_descriptor_padding(void *arg)
tt_assert(padded_plaintext);
tor_free(plaintext);
/* Make sure our padding has been zeroed. */
- tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len,
+ tt_int_op(fast_mem_is_zero((char *) padded_plaintext + plaintext_len,
padded_len - plaintext_len), OP_EQ, 1);
tor_free(padded_plaintext);
/* Never never have a padded length smaller than the plaintext. */
@@ -166,7 +160,7 @@ test_descriptor_padding(void *arg)
tt_assert(padded_plaintext);
tor_free(plaintext);
/* Make sure our padding has been zeroed. */
- tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len,
+ tt_int_op(fast_mem_is_zero((char *) padded_plaintext + plaintext_len,
padded_len - plaintext_len), OP_EQ, 1);
tor_free(padded_plaintext);
/* Never never have a padded length smaller than the plaintext. */
@@ -179,115 +173,6 @@ test_descriptor_padding(void *arg)
}
static void
-test_link_specifier(void *arg)
-{
- ssize_t ret;
- hs_desc_link_specifier_t spec;
- smartlist_t *link_specifiers = smartlist_new();
- char buf[256];
- char *b64 = NULL;
- link_specifier_t *ls = NULL;
-
- (void) arg;
-
- /* Always this port. */
- spec.u.ap.port = 42;
- smartlist_add(link_specifiers, &spec);
-
- /* Test IPv4 for starter. */
- {
- uint32_t ipv4;
-
- spec.type = LS_IPV4;
- ret = tor_addr_parse(&spec.u.ap.addr, "1.2.3.4");
- tt_int_op(ret, OP_EQ, AF_INET);
- b64 = encode_link_specifiers(link_specifiers);
- tt_assert(b64);
-
- /* Decode it and validate the format. */
- ret = base64_decode(buf, sizeof(buf), b64, strlen(b64));
- tt_int_op(ret, OP_GT, 0);
- /* First byte is the number of link specifier. */
- tt_int_op(get_uint8(buf), OP_EQ, 1);
- ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1);
- tt_int_op(ret, OP_EQ, 8);
- /* Should be 2 bytes for port and 4 bytes for IPv4. */
- tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, 6);
- ipv4 = link_specifier_get_un_ipv4_addr(ls);
- tt_int_op(tor_addr_to_ipv4h(&spec.u.ap.addr), OP_EQ, ipv4);
- tt_int_op(link_specifier_get_un_ipv4_port(ls), OP_EQ, spec.u.ap.port);
-
- link_specifier_free(ls);
- ls = NULL;
- tor_free(b64);
- }
-
- /* Test IPv6. */
- {
- uint8_t ipv6[16];
-
- spec.type = LS_IPV6;
- ret = tor_addr_parse(&spec.u.ap.addr, "[1:2:3:4::]");
- tt_int_op(ret, OP_EQ, AF_INET6);
- b64 = encode_link_specifiers(link_specifiers);
- tt_assert(b64);
-
- /* Decode it and validate the format. */
- ret = base64_decode(buf, sizeof(buf), b64, strlen(b64));
- tt_int_op(ret, OP_GT, 0);
- /* First byte is the number of link specifier. */
- tt_int_op(get_uint8(buf), OP_EQ, 1);
- ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1);
- tt_int_op(ret, OP_EQ, 20);
- /* Should be 2 bytes for port and 16 bytes for IPv6. */
- tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, 18);
- for (unsigned int i = 0; i < sizeof(ipv6); i++) {
- ipv6[i] = link_specifier_get_un_ipv6_addr(ls, i);
- }
- tt_mem_op(tor_addr_to_in6_addr8(&spec.u.ap.addr), OP_EQ, ipv6,
- sizeof(ipv6));
- tt_int_op(link_specifier_get_un_ipv6_port(ls), OP_EQ, spec.u.ap.port);
-
- link_specifier_free(ls);
- ls = NULL;
- tor_free(b64);
- }
-
- /* Test legacy. */
- {
- uint8_t *id;
-
- spec.type = LS_LEGACY_ID;
- memset(spec.u.legacy_id, 'Y', sizeof(spec.u.legacy_id));
- b64 = encode_link_specifiers(link_specifiers);
- tt_assert(b64);
-
- /* Decode it and validate the format. */
- ret = base64_decode(buf, sizeof(buf), b64, strlen(b64));
- tt_int_op(ret, OP_GT, 0);
- /* First byte is the number of link specifier. */
- tt_int_op(get_uint8(buf), OP_EQ, 1);
- ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1);
- /* 20 bytes digest + 1 byte type + 1 byte len. */
- tt_int_op(ret, OP_EQ, 22);
- tt_int_op(link_specifier_getlen_un_legacy_id(ls), OP_EQ, DIGEST_LEN);
- /* Digest length is 20 bytes. */
- tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, DIGEST_LEN);
- id = link_specifier_getarray_un_legacy_id(ls);
- tt_mem_op(spec.u.legacy_id, OP_EQ, id, DIGEST_LEN);
-
- link_specifier_free(ls);
- ls = NULL;
- tor_free(b64);
- }
-
- done:
- link_specifier_free(ls);
- tor_free(b64);
- smartlist_free(link_specifiers);
-}
-
-static void
test_encode_descriptor(void *arg)
{
int ret;
@@ -848,8 +733,7 @@ test_desc_signature(void *arg)
ret = ed25519_sign_prefixed(&sig, (const uint8_t *) data, strlen(data),
"Tor onion service descriptor sig v3", &kp);
tt_int_op(ret, OP_EQ, 0);
- ret = ed25519_signature_to_base64(sig_b64, &sig);
- tt_int_op(ret, OP_EQ, 0);
+ ed25519_signature_to_base64(sig_b64, &sig);
/* Build the descriptor that should be valid. */
tor_asprintf(&desc, "%ssignature %s\n", data, sig_b64);
ret = desc_sig_is_valid(sig_b64, &kp.pubkey, desc, strlen(desc));
@@ -909,7 +793,7 @@ test_build_authorized_client(void *arg)
client_pubkey_b16,
strlen(client_pubkey_b16));
- MOCK(crypto_strongest_rand_, mock_crypto_strongest_rand);
+ testing_enable_prefilled_rng("\x01", 1);
hs_desc_build_authorized_client(subcredential,
&client_auth_pk, &auth_ephemeral_sk,
@@ -925,15 +809,13 @@ test_build_authorized_client(void *arg)
done:
tor_free(desc_client);
tor_free(mem_op_hex_tmp);
- UNMOCK(crypto_strongest_rand_);
+ testing_disable_prefilled_rng();
}
struct testcase_t hs_descriptor[] = {
/* Encoding tests. */
{ "cert_encoding", test_cert_encoding, TT_FORK,
NULL, NULL },
- { "link_specifier", test_link_specifier, TT_FORK,
- NULL, NULL },
{ "encode_descriptor", test_encode_descriptor, TT_FORK,
NULL, NULL },
{ "descriptor_padding", test_descriptor_padding, TT_FORK,
diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c
index 558fc32c54..732836fb5b 100644
--- a/src/test/test_hs_intropoint.c
+++ b/src/test/test_hs_intropoint.c
@@ -50,7 +50,7 @@ new_establish_intro_cell(const char *circ_nonce,
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
- ip = service_intro_point_new(NULL, 0, 0);
+ ip = service_intro_point_new(NULL);
tt_assert(ip);
cell_len = hs_cell_build_establish_intro(circ_nonce, ip, buf);
tt_i64_op(cell_len, OP_GT, 0);
@@ -76,7 +76,7 @@ new_establish_intro_encoded_cell(const char *circ_nonce, uint8_t *cell_out)
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
- ip = service_intro_point_new(NULL, 0, 0);
+ ip = service_intro_point_new(NULL);
tt_assert(ip);
cell_len = hs_cell_build_establish_intro(circ_nonce, ip, cell_out);
tt_i64_op(cell_len, OP_GT, 0);
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index 43bf894383..a303f10411 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -21,6 +21,7 @@
#define STATEFILE_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#define HS_CLIENT_PRIVATE
+#define CRYPT_PATH_PRIVATE
#include "test/test.h"
#include "test/test_helpers.h"
@@ -60,6 +61,7 @@
#include "core/or/cpath_build_state_st.h"
#include "core/or/crypt_path_st.h"
+#include "core/or/crypt_path.h"
#include "feature/nodelist/networkstatus_st.h"
#include "feature/nodelist/node_st.h"
#include "core/or/origin_circuit_st.h"
@@ -193,12 +195,14 @@ test_e2e_rend_circuit_setup(void *arg)
tt_int_op(retval, OP_EQ, 1);
/* Check the digest algo */
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.f_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.f_digest),
OP_EQ, DIGEST_SHA3_256);
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.b_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.b_digest),
OP_EQ, DIGEST_SHA3_256);
- tt_assert(or_circ->cpath->crypto.f_crypto);
- tt_assert(or_circ->cpath->crypto.b_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.f_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.b_crypto);
/* Ensure that circ purpose was changed */
tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED);
@@ -328,17 +332,18 @@ helper_create_service_with_clients(int num_clients)
static hs_service_intro_point_t *
helper_create_service_ip(void)
{
- hs_desc_link_specifier_t *ls;
- hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0, 0);
+ link_specifier_t *ls;
+ hs_service_intro_point_t *ip = service_intro_point_new(NULL);
tor_assert(ip);
/* Add a first unused link specifier. */
- ls = tor_malloc_zero(sizeof(*ls));
- ls->type = LS_IPV4;
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_IPV4);
smartlist_add(ip->base.link_specifiers, ls);
/* Add a second link specifier used by a test. */
- ls = tor_malloc_zero(sizeof(*ls));
- ls->type = LS_LEGACY_ID;
- memset(ls->u.legacy_id, 'A', sizeof(ls->u.legacy_id));
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_LEGACY_ID);
+ memset(link_specifier_getarray_un_legacy_id(ls), 'A',
+ link_specifier_getlen_un_legacy_id(ls));
smartlist_add(ip->base.link_specifiers, ls);
return ip;
@@ -392,11 +397,11 @@ test_load_keys(void *arg)
tt_assert(s);
/* Ok we have the service object. Validate few things. */
- tt_assert(!tor_mem_is_zero(s->onion_address, sizeof(s->onion_address)));
+ tt_assert(!fast_mem_is_zero(s->onion_address, sizeof(s->onion_address)));
tt_int_op(hs_address_is_valid(s->onion_address), OP_EQ, 1);
- tt_assert(!tor_mem_is_zero((char *) s->keys.identity_sk.seckey,
+ tt_assert(!fast_mem_is_zero((char *) s->keys.identity_sk.seckey,
ED25519_SECKEY_LEN));
- tt_assert(!tor_mem_is_zero((char *) s->keys.identity_pk.pubkey,
+ tt_assert(!fast_mem_is_zero((char *) s->keys.identity_pk.pubkey,
ED25519_PUBKEY_LEN));
/* Check onion address from identity key. */
hs_build_address(&s->keys.identity_pk, s->config.version, addr);
@@ -676,7 +681,7 @@ test_service_intro_point(void *arg)
ip = helper_create_service_ip();
tt_assert(ip);
/* Make sure the authentication keypair is not zeroes. */
- tt_int_op(tor_mem_is_zero((const char *) &ip->auth_key_kp,
+ tt_int_op(fast_mem_is_zero((const char *) &ip->auth_key_kp,
sizeof(ed25519_keypair_t)), OP_EQ, 0);
/* The introduce2_max MUST be in that range. */
tt_u64_op(ip->introduce2_max, OP_GE,
@@ -811,10 +816,11 @@ test_helper_functions(void *arg)
const node_t *node = get_node_from_intro_point(ip);
tt_ptr_op(node, OP_EQ, &mock_node);
SMARTLIST_FOREACH_BEGIN(ip->base.link_specifiers,
- hs_desc_link_specifier_t *, ls) {
- if (ls->type == LS_LEGACY_ID) {
+ link_specifier_t *, ls) {
+ if (link_specifier_get_ls_type(ls) == LS_LEGACY_ID) {
/* Change legacy id in link specifier which is not the mock node. */
- memset(ls->u.legacy_id, 'B', sizeof(ls->u.legacy_id));
+ memset(link_specifier_getarray_un_legacy_id(ls), 'B',
+ link_specifier_getlen_un_legacy_id(ls));
}
} SMARTLIST_FOREACH_END(ls);
node = get_node_from_intro_point(ip);
@@ -872,6 +878,10 @@ test_helper_functions(void *arg)
done:
/* This will free the service and all objects associated to it. */
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_service_free_all();
UNMOCK(node_get_by_id);
}
@@ -881,7 +891,7 @@ static void
test_intro_circuit_opened(void *arg)
{
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
- hs_service_t *service;
+ hs_service_t *service = NULL;
origin_circuit_t *circ = NULL;
(void) arg;
@@ -929,6 +939,10 @@ test_intro_circuit_opened(void *arg)
done:
circuit_free_(TO_CIRCUIT(circ));
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(circuit_mark_for_close_);
UNMOCK(relay_send_command_from_edge_);
@@ -943,7 +957,7 @@ test_intro_established(void *arg)
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
origin_circuit_t *circ = NULL;
- hs_service_t *service;
+ hs_service_t *service = NULL;
hs_service_intro_point_t *ip = NULL;
(void) arg;
@@ -1004,6 +1018,10 @@ test_intro_established(void *arg)
done:
if (circ)
circuit_free_(TO_CIRCUIT(circ));
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(circuit_mark_for_close_);
}
@@ -1015,7 +1033,7 @@ test_rdv_circuit_opened(void *arg)
{
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
origin_circuit_t *circ = NULL;
- hs_service_t *service;
+ hs_service_t *service = NULL;
(void) arg;
@@ -1046,6 +1064,10 @@ test_rdv_circuit_opened(void *arg)
done:
circuit_free_(TO_CIRCUIT(circ));
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(circuit_mark_for_close_);
UNMOCK(relay_send_command_from_edge_);
@@ -1133,6 +1155,10 @@ test_closing_intro_circs(void *arg)
circuit_free_(TO_CIRCUIT(intro_circ));
}
/* Frees the service object. */
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(assert_circuit_ok);
}
@@ -1145,7 +1171,7 @@ test_introduce2(void *arg)
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
origin_circuit_t *circ = NULL;
- hs_service_t *service;
+ hs_service_t *service = NULL;
hs_service_intro_point_t *ip = NULL;
(void) arg;
@@ -1212,6 +1238,10 @@ test_introduce2(void *arg)
dummy_state = NULL;
if (circ)
circuit_free_(TO_CIRCUIT(circ));
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(circuit_mark_for_close_);
}
@@ -1296,6 +1326,10 @@ test_service_event(void *arg)
done:
hs_circuitmap_remove_circuit(TO_CIRCUIT(circ));
circuit_free_(TO_CIRCUIT(circ));
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(circuit_mark_for_close_);
}
@@ -1306,7 +1340,7 @@ test_rotate_descriptors(void *arg)
{
int ret;
time_t next_rotation_time, now;
- hs_service_t *service;
+ hs_service_t *service = NULL;
hs_service_descriptor_t *desc_next;
(void) arg;
@@ -1398,6 +1432,10 @@ test_rotate_descriptors(void *arg)
tt_assert(service->desc_next);
done:
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(get_or_state);
UNMOCK(circuit_mark_for_close_);
@@ -1411,7 +1449,7 @@ test_build_update_descriptors(void *arg)
{
int ret;
node_t *node;
- hs_service_t *service;
+ hs_service_t *service = NULL;
hs_service_intro_point_t *ip_cur, *ip_next;
routerinfo_t ri;
@@ -1560,9 +1598,9 @@ test_build_update_descriptors(void *arg)
tt_int_op(smartlist_len(ip_cur->base.link_specifiers), OP_EQ, 3);
/* Make sure we have a valid encryption keypair generated when we pick an
* intro point in the update process. */
- tt_assert(!tor_mem_is_zero((char *) ip_cur->enc_key_kp.seckey.secret_key,
+ tt_assert(!fast_mem_is_zero((char *) ip_cur->enc_key_kp.seckey.secret_key,
CURVE25519_SECKEY_LEN));
- tt_assert(!tor_mem_is_zero((char *) ip_cur->enc_key_kp.pubkey.public_key,
+ tt_assert(!fast_mem_is_zero((char *) ip_cur->enc_key_kp.pubkey.public_key,
CURVE25519_PUBKEY_LEN));
tt_u64_op(ip_cur->time_to_expire, OP_GE, now +
INTRO_POINT_LIFETIME_MIN_SECONDS);
@@ -1628,6 +1666,10 @@ test_build_update_descriptors(void *arg)
tt_u64_op(service->desc_next->next_upload_time, OP_EQ, 0);
done:
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
nodelist_free_all();
}
@@ -1882,9 +1924,9 @@ test_rendezvous1_parsing(void *arg)
}
/* Send out the RENDEZVOUS1 and make sure that our mock func worked */
- tt_assert(tor_mem_is_zero(rend1_payload, 32));
+ tt_assert(fast_mem_is_zero(rend1_payload, 32));
hs_circ_service_rp_has_opened(service, service_circ);
- tt_assert(!tor_mem_is_zero(rend1_payload, 32));
+ tt_assert(!fast_mem_is_zero(rend1_payload, 32));
tt_int_op(rend1_payload_len, OP_EQ, HS_LEGACY_RENDEZVOUS_CELL_SIZE);
/******************************/
diff --git a/src/test/test_key_expiration.sh b/src/test/test_key_expiration.sh
index 3474210607..54abb4a2fa 100755
--- a/src/test/test_key_expiration.sh
+++ b/src/test/test_key_expiration.sh
@@ -6,14 +6,14 @@
umask 077
set -e
-if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then
+if [ $# -eq 0 ] || [ ! -f "${1}" ] || [ ! -x "${1}" ]; then
if [ "$TESTING_TOR_BINARY" = "" ] ; then
echo "Usage: ${0} PATH_TO_TOR [case-number]"
exit 1
fi
fi
-UNAME_OS=`uname -s | cut -d_ -f1`
+UNAME_OS=$(uname -s | cut -d_ -f1)
if test "$UNAME_OS" = 'CYGWIN' || \
test "$UNAME_OS" = 'MSYS' || \
test "$UNAME_OS" = 'MINGW'; then
@@ -47,11 +47,11 @@ dump() { xxd -p "$1" | tr -d '\n '; }
die() { echo "$1" >&2 ; exit 5; }
check_dir() { [ -d "$1" ] || die "$1 did not exist"; }
check_file() { [ -e "$1" ] || die "$1 did not exist"; }
-check_no_file() { [ -e "$1" ] && die "$1 was not supposed to exist" || true; }
-check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match: `dump $1` vs `dump $2`"; }
+check_no_file() { if [ -e "$1" ]; then die "$1 was not supposed to exist"; fi }
+check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match: $(dump "$1") vs $(dump "$2")"; }
check_keys_eq() { check_files_eq "${SRC}/keys/${1}" "${ME}/keys/${1}"; }
-DATA_DIR=`mktemp -d -t tor_key_expiration_tests.XXXXXX`
+DATA_DIR=$(mktemp -d -t tor_key_expiration_tests.XXXXXX)
if [ -z "$DATA_DIR" ]; then
echo "Failure: mktemp invocation returned empty string" >&2
exit 3
@@ -60,10 +60,10 @@ if [ ! -d "$DATA_DIR" ]; then
echo "Failure: mktemp invocation result doesn't point to directory" >&2
exit 3
fi
-trap "rm -rf '$DATA_DIR'" 0
+trap 'rm -rf "$DATA_DIR"' 0
# Use an absolute path for this or Tor will complain
-DATA_DIR=`cd "${DATA_DIR}" && pwd`
+DATA_DIR=$(cd "${DATA_DIR}" && pwd)
touch "${DATA_DIR}/empty_torrc"
touch "${DATA_DIR}/empty_defaults_torrc"
diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh
index 7afff271cb..cbdfd1909c 100755
--- a/src/test/test_keygen.sh
+++ b/src/test/test_keygen.sh
@@ -6,14 +6,14 @@
umask 077
set -e
-if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then
+if [ $# -eq 0 ] || [ ! -f "${1}" ] || [ ! -x "${1}" ]; then
if [ "$TESTING_TOR_BINARY" = "" ] ; then
echo "Usage: ${0} PATH_TO_TOR [case-number]"
exit 1
fi
fi
-UNAME_OS=`uname -s | cut -d_ -f1`
+UNAME_OS=$(uname -s | cut -d_ -f1)
if test "$UNAME_OS" = 'CYGWIN' || \
test "$UNAME_OS" = 'MSYS' || \
test "$UNAME_OS" = 'MINGW'; then
@@ -64,11 +64,11 @@ dump() { xxd -p "$1" | tr -d '\n '; }
die() { echo "$1" >&2 ; exit 5; }
check_dir() { [ -d "$1" ] || die "$1 did not exist"; }
check_file() { [ -e "$1" ] || die "$1 did not exist"; }
-check_no_file() { [ -e "$1" ] && die "$1 was not supposed to exist" || true; }
-check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match: `dump $1` vs `dump $2`"; }
+check_no_file() { if [ -e "$1" ]; then die "$1 was not supposed to exist"; fi }
+check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match: $(dump "$1") vs $(dump "$2")"; }
check_keys_eq() { check_files_eq "${SRC}/keys/${1}" "${ME}/keys/${1}"; }
-DATA_DIR=`mktemp -d -t tor_keygen_tests.XXXXXX`
+DATA_DIR=$(mktemp -d -t tor_keygen_tests.XXXXXX)
if [ -z "$DATA_DIR" ]; then
echo "Failure: mktemp invocation returned empty string" >&2
exit 3
@@ -77,10 +77,10 @@ if [ ! -d "$DATA_DIR" ]; then
echo "Failure: mktemp invocation result doesn't point to directory" >&2
exit 3
fi
-trap "rm -rf '$DATA_DIR'" 0
+trap 'rm -rf "$DATA_DIR"' 0
# Use an absolute path for this or Tor will complain
-DATA_DIR=`cd "${DATA_DIR}" && pwd`
+DATA_DIR=$(cd "${DATA_DIR}" && pwd)
touch "${DATA_DIR}/empty_torrc"
touch "${DATA_DIR}/empty_defaults_torrc"
@@ -144,7 +144,9 @@ ME="${DATA_DIR}/case2a"
SRC="${DATA_DIR}/orig"
mkdir -p "${ME}/keys"
cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout" && die "Somehow succeeded when missing secret key, certs: `cat ${ME}/stdout`" || true
+if ${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout"; then
+ die "Somehow succeeded when missing secret key, certs: $(cat "${ME}/stdout")"
+fi
check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key"
grep "We needed to load a secret key.*but couldn't find it" "${ME}/stdout" >/dev/null || die "Tor didn't declare that it was missing a secret key"
@@ -281,7 +283,9 @@ SRC="${DATA_DIR}/encrypted"
mkdir -p "${ME}/keys"
cp "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/"
cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout" && die "Tor started with encrypted secret key and no certs" || true
+if ${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout"; then
+ die "Tor started with encrypted secret key and no certs"
+fi
check_no_file "${ME}/keys/ed25519_signing_cert"
check_no_file "${ME}/keys/ed25519_signing_secret_key"
@@ -370,7 +374,9 @@ mkdir -p "${ME}/keys"
cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/"
cp "${OTHER}/keys/ed25519_master_id_secret_key" "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint >"${ME}/stdout" && die "Successfully started with mismatched keys!?" || true
+if ${TOR} --DataDirectory "${ME}" --list-fingerprint >"${ME}/stdout"; then
+ die "Successfully started with mismatched keys!?"
+fi
grep "public_key does not match.*secret_key" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a key mismatch"
@@ -386,7 +392,9 @@ ME="${DATA_DIR}/case11a"
mkdir -p "${ME}/keys"
-${TOR} --DataDirectory "${ME}" --passphrase-fd 1 > "${ME}/stdout" && die "Successfully started with passphrase-fd but no keygen?" || true
+if ${TOR} --DataDirectory "${ME}" --passphrase-fd 1 > "${ME}/stdout"; then
+ die "Successfully started with passphrase-fd but no keygen?"
+fi
grep "passphrase-fd specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
@@ -402,7 +410,9 @@ ME="${DATA_DIR}/case11b"
mkdir -p "${ME}/keys"
-${TOR} --DataDirectory "${ME}" --no-passphrase > "${ME}/stdout" && die "Successfully started with no-passphrase but no keygen?" || true
+if ${TOR} --DataDirectory "${ME}" --no-passphrase > "${ME}/stdout"; then
+ die "Successfully started with no-passphrase but no keygen?"
+fi
grep "no-passphrase specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
@@ -418,7 +428,9 @@ ME="${DATA_DIR}/case11C"
mkdir -p "${ME}/keys"
-${TOR} --DataDirectory "${ME}" --newpass > "${ME}/stdout" && die "Successfully started with newpass but no keygen?" || true
+if ${TOR} --DataDirectory "${ME}" --newpass > "${ME}/stdout"; then
+ die "Successfully started with newpass but no keygen?"
+fi
grep "newpass specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
@@ -456,7 +468,9 @@ ME="${DATA_DIR}/case11E"
mkdir -p "${ME}/keys"
-${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd ewigeblumenkraft > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd?" || true
+if ${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd ewigeblumenkraft > "${ME}/stdout"; then
+ die "Successfully started with bogus passphrase-fd?"
+fi
grep "Invalid --passphrase-fd value" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
@@ -473,7 +487,9 @@ ME="${DATA_DIR}/case11F"
mkdir -p "${ME}/keys"
-${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd 1 --no-passphrase > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd combination?" || true
+if ${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd 1 --no-passphrase > "${ME}/stdout"; then
+ die "Successfully started with bogus passphrase-fd combination?"
+fi
grep "no-passphrase specified with --passphrase-fd" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
index 34f59f26cd..5e78e1ce4d 100644
--- a/src/test/test_link_handshake.c
+++ b/src/test/test_link_handshake.c
@@ -263,7 +263,7 @@ test_link_handshake_certs_ok(void *arg)
tt_assert(c1->handshake_state->authenticated_rsa);
tt_assert(! c1->handshake_state->authenticated_ed25519);
}
- tt_assert(! tor_mem_is_zero(
+ tt_assert(! fast_mem_is_zero(
(char*)c1->handshake_state->authenticated_rsa_peer_id, 20));
chan2 = tor_malloc_zero(sizeof(*chan2));
@@ -290,7 +290,7 @@ test_link_handshake_certs_ok(void *arg)
tt_ptr_op(c2->handshake_state->certs->ed_id_sign, OP_EQ, NULL);
}
tt_assert(c2->handshake_state->certs->id_cert);
- tt_assert(tor_mem_is_zero(
+ tt_assert(fast_mem_is_zero(
(char*)c2->handshake_state->authenticated_rsa_peer_id, 20));
/* no authentication has happened yet, since we haen't gotten an AUTH cell.
*/
@@ -948,7 +948,7 @@ test_link_handshake_send_authchallenge(void *arg)
#else
tt_int_op(36, OP_EQ, cell1->payload_len);
tt_int_op(36, OP_EQ, cell2->payload_len);
-#endif
+#endif /* defined(HAVE_WORKING_TOR_TLS_GET_TLSSECRETS) */
tt_int_op(0, OP_EQ, cell1->circ_id);
tt_int_op(0, OP_EQ, cell2->circ_id);
tt_int_op(CELL_AUTH_CHALLENGE, OP_EQ, cell1->command);
@@ -960,7 +960,7 @@ test_link_handshake_send_authchallenge(void *arg)
#else
tt_mem_op("\x00\x01\x00\x03", OP_EQ, cell1->payload + 32, 4);
tt_mem_op("\x00\x01\x00\x03", OP_EQ, cell2->payload + 32, 4);
-#endif
+#endif /* defined(HAVE_WORKING_TOR_TLS_GET_TLSSECRETS) */
tt_mem_op(cell1->payload, OP_NE, cell2->payload, 32);
done:
diff --git a/src/test/test_logging.c b/src/test/test_logging.c
index 6416e98a4e..bb7018fe1c 100644
--- a/src/test/test_logging.c
+++ b/src/test/test_logging.c
@@ -15,7 +15,7 @@
#endif
static void
-dummy_cb_fn(int severity, uint32_t domain, const char *msg)
+dummy_cb_fn(int severity, log_domain_mask_t domain, const char *msg)
{
(void)severity; (void)domain; (void)msg;
}
diff --git a/src/test/test_namemap.c b/src/test/test_namemap.c
new file mode 100644
index 0000000000..df77d4e2de
--- /dev/null
+++ b/src/test/test_namemap.c
@@ -0,0 +1,174 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "test/test.h"
+
+#include "lib/cc/torint.h"
+#include "lib/container/namemap.h"
+#include "lib/container/namemap_st.h"
+#include "lib/malloc/malloc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static void
+test_namemap_empty(void *arg)
+{
+ (void)arg;
+
+ namemap_t m;
+ namemap_init(&m);
+ namemap_t m2 = NAMEMAP_INIT();
+
+ tt_uint_op(0, OP_EQ, namemap_get_size(&m));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, "hello"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, "hello"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, "hello128"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, ""));
+ tt_uint_op(0, OP_EQ, namemap_get_size(&m));
+
+ tt_uint_op(0, OP_EQ, namemap_get_size(&m2));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello128"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, ""));
+ tt_uint_op(0, OP_EQ, namemap_get_size(&m));
+
+ done:
+ namemap_clear(&m);
+ namemap_clear(&m2);
+}
+
+static void
+test_namemap_toolong(void *arg)
+{
+ (void)arg;
+ namemap_t m;
+ char *ok = NULL;
+ char *toolong = NULL;
+ namemap_init(&m);
+
+ ok = tor_malloc_zero(MAX_NAMEMAP_NAME_LEN+1);
+ memset(ok, 'x', MAX_NAMEMAP_NAME_LEN);
+
+ toolong = tor_malloc_zero(MAX_NAMEMAP_NAME_LEN+2);
+ memset(toolong, 'x', MAX_NAMEMAP_NAME_LEN+1);
+
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, ok));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, toolong));
+ unsigned u1 = namemap_get_or_create_id(&m, toolong);
+ unsigned u2 = namemap_get_or_create_id(&m, ok);
+ tt_uint_op(u1, OP_EQ, NAMEMAP_ERR);
+ tt_uint_op(u2, OP_NE, NAMEMAP_ERR);
+ tt_uint_op(u2, OP_EQ, namemap_get_id(&m, ok));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, toolong));
+
+ tt_str_op(ok, OP_EQ, namemap_get_name(&m, u2));
+ tt_ptr_op(NULL, OP_EQ, namemap_get_name(&m, u1));
+
+ done:
+ tor_free(ok);
+ tor_free(toolong);
+ namemap_clear(&m);
+}
+
+static void
+test_namemap_blackbox(void *arg)
+{
+ (void)arg;
+
+ namemap_t m1, m2;
+ namemap_init(&m1);
+ namemap_init(&m2);
+
+ unsigned u1 = namemap_get_or_create_id(&m1, "hello");
+ unsigned u2 = namemap_get_or_create_id(&m1, "world");
+ tt_uint_op(u1, OP_NE, NAMEMAP_ERR);
+ tt_uint_op(u2, OP_NE, NAMEMAP_ERR);
+ tt_uint_op(u1, OP_NE, u2);
+
+ tt_uint_op(u1, OP_EQ, namemap_get_id(&m1, "hello"));
+ tt_uint_op(u1, OP_EQ, namemap_get_or_create_id(&m1, "hello"));
+ tt_uint_op(u2, OP_EQ, namemap_get_id(&m1, "world"));
+ tt_uint_op(u2, OP_EQ, namemap_get_or_create_id(&m1, "world"));
+
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m1, "HELLO"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello"));
+
+ unsigned u3 = namemap_get_or_create_id(&m2, "hola");
+ tt_uint_op(u3, OP_NE, NAMEMAP_ERR);
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m1, "hola"));
+ tt_uint_op(u3, OP_EQ, namemap_get_or_create_id(&m2, "hola"));
+ tt_uint_op(u3, OP_EQ, namemap_get_id(&m2, "hola"));
+
+ unsigned int u4 = namemap_get_or_create_id(&m1, "hola");
+ tt_uint_op(u4, OP_NE, NAMEMAP_ERR);
+ tt_uint_op(u4, OP_EQ, namemap_get_id(&m1, "hola"));
+ tt_uint_op(u3, OP_EQ, namemap_get_id(&m2, "hola"));
+
+ tt_str_op("hello", OP_EQ, namemap_get_name(&m1, u1));
+ tt_str_op("world", OP_EQ, namemap_get_name(&m1, u2));
+ tt_str_op("hola", OP_EQ, namemap_get_name(&m2, u3));
+ tt_str_op("hola", OP_EQ, namemap_get_name(&m1, u4));
+
+ tt_ptr_op(NULL, OP_EQ, namemap_get_name(&m2, u3 + 10));
+
+ done:
+ namemap_clear(&m1);
+ namemap_clear(&m2);
+}
+
+static void
+test_namemap_internals(void *arg)
+{
+ (void)arg;
+ // This test actually assumes know something about the identity layout.
+ namemap_t m;
+ namemap_init(&m);
+
+ tt_uint_op(0, OP_EQ, namemap_get_or_create_id(&m, "that"));
+ tt_uint_op(0, OP_EQ, namemap_get_or_create_id(&m, "that"));
+ tt_uint_op(1, OP_EQ, namemap_get_or_create_id(&m, "is"));
+ tt_uint_op(1, OP_EQ, namemap_get_or_create_id(&m, "is"));
+
+ tt_uint_op(0, OP_EQ, namemap_get_id(&m, "that"));
+ tt_uint_op(0, OP_EQ, namemap_get_id(&m, "that"));
+ tt_uint_op(1, OP_EQ, namemap_get_id(&m, "is"));
+ tt_uint_op(2, OP_EQ, namemap_get_or_create_id(&m, "not"));
+ tt_uint_op(1, OP_EQ, namemap_get_or_create_id(&m, "is"));
+ tt_uint_op(2, OP_EQ, namemap_get_or_create_id(&m, "not"));
+
+ done:
+ namemap_clear(&m);
+}
+
+static void
+test_namemap_fmt(void *arg)
+{
+ (void)arg;
+ namemap_t m = NAMEMAP_INIT();
+
+ unsigned a = namemap_get_or_create_id(&m, "greetings");
+ unsigned b = namemap_get_or_create_id(&m, "earthlings");
+
+ tt_str_op(namemap_fmt_name(&m, a), OP_EQ, "greetings");
+ tt_str_op(namemap_fmt_name(&m, b), OP_EQ, "earthlings");
+ tt_int_op(a, OP_NE, 100);
+ tt_int_op(b, OP_NE, 100);
+ tt_str_op(namemap_fmt_name(&m, 100), OP_EQ, "{100}");
+
+ done:
+ namemap_clear(&m);
+}
+
+#define T(name) \
+ { #name, test_namemap_ ## name , 0, NULL, NULL }
+
+struct testcase_t namemap_tests[] = {
+ T(empty),
+ T(toolong),
+ T(blackbox),
+ T(internals),
+ T(fmt),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_options.c b/src/test/test_options.c
index f12e6b6763..7009910b0f 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -31,14 +31,14 @@
typedef struct {
int severity;
- uint32_t domain;
+ log_domain_mask_t domain;
char *msg;
} logmsg_t;
static smartlist_t *messages = NULL;
static void
-log_cback(int severity, uint32_t domain, const char *msg)
+log_cback(int severity, log_domain_mask_t domain, const char *msg)
{
logmsg_t *x = tor_malloc(sizeof(*x));
x->severity = severity;
@@ -430,6 +430,7 @@ get_options_test_data(const char *conf)
// Being kinda lame and just fixing the immedate breakage for now..
result->opt->ConnectionPadding = -1; // default must be "auto"
result->opt->DormantClientTimeout = 1800; // must be over 600.
+ result->opt->CircuitPadding = 1; // default must be "1"
rv = config_get_lines(conf, &cl, 1);
tt_int_op(rv, OP_EQ, 0);
diff --git a/src/test/test_periodic_event.c b/src/test/test_periodic_event.c
index ebac20838f..267156a908 100644
--- a/src/test/test_periodic_event.c
+++ b/src/test/test_periodic_event.c
@@ -51,12 +51,13 @@ test_pe_initialize(void *arg)
* need to run the main loop and then wait for a second delaying the unit
* tests. Instead, we'll test the callback work indepedently elsewhere. */
initialize_periodic_events();
+ periodic_events_connect_all();
set_network_participation(false);
rescan_periodic_events(get_options());
/* Validate that all events have been set up. */
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
tt_assert(item->ev);
tt_assert(item->fn);
tt_u64_op(item->last_action_time, OP_EQ, 0);
@@ -89,8 +90,8 @@ test_pe_launch(void *arg)
/* Hack: We'll set a dumb fn() of each events so they don't get called when
* dispatching them. We just want to test the state of the callbacks, not
* the whole code path. */
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
item->fn = dumb_event_fn;
}
@@ -107,17 +108,18 @@ test_pe_launch(void *arg)
periodic_event_item_t *item = &periodic_events[i];
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
}
-#endif
+#endif /* 0 */
initialize_periodic_events();
+ periodic_events_connect_all();
/* Now that we've initialized, rescan the list to launch. */
periodic_events_on_new_options(options);
int mask = PERIODIC_EVENT_ROLE_CLIENT|PERIODIC_EVENT_ROLE_ALL|
PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
int should_be_enabled = !!(item->roles & mask);
tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
// enabled or not, the event has not yet been run.
@@ -134,8 +136,8 @@ test_pe_launch(void *arg)
PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER|
PERIODIC_EVENT_ROLE_ALL|PERIODIC_EVENT_ROLE_NET_PARTICIPANT);
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
/* Only Client role should be disabled. */
if (item->roles == PERIODIC_EVENT_ROLE_CLIENT) {
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
@@ -156,8 +158,8 @@ test_pe_launch(void *arg)
set_network_participation(false);
periodic_events_on_new_options(options);
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
!(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
@@ -177,8 +179,8 @@ test_pe_launch(void *arg)
* trigger a rescan of the event disabling the HS service event. */
to_remove = &service;
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
tt_int_op(periodic_event_is_enabled(item), OP_EQ,
(item->roles != PERIODIC_EVENT_ROLE_CONTROLEV));
}
@@ -300,12 +302,13 @@ test_pe_hs_service(void *arg)
consider_hibernation(time(NULL));
/* Initialize the events so we can enable them */
initialize_periodic_events();
+ periodic_events_connect_all();
/* Hack: We'll set a dumb fn() of each events so they don't get called when
* dispatching them. We just want to test the state of the callbacks, not
* the whole code path. */
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
item->fn = dumb_event_fn;
}
@@ -318,8 +321,8 @@ test_pe_hs_service(void *arg)
* trigger a rescan of the event disabling the HS service event. */
to_remove = &service;
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
}
@@ -329,8 +332,8 @@ test_pe_hs_service(void *arg)
/* Remove the service from the global map, it should trigger a rescan and
* disable the HS service events. */
remove_service(get_hs_service_map(), &service);
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
}
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index 46d4a1b94a..e58bb3d174 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -6,13 +6,18 @@
#include "core/or/or.h"
#include "app/config/config.h"
+#include "core/or/circuitbuild.h"
#include "core/or/policies.h"
#include "feature/dirparse/policy_parse.h"
+#include "feature/hs/hs_common.h"
+#include "feature/hs/hs_descriptor.h"
#include "feature/relay/router.h"
#include "lib/encoding/confline.h"
#include "test/test.h"
+#include "test/log_test_helpers.h"
#include "core/or/addr_policy_st.h"
+#include "core/or/extend_info_st.h"
#include "core/or/port_cfg_st.h"
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/routerinfo_st.h"
@@ -2024,6 +2029,101 @@ test_policies_fascist_firewall_allows_address(void *arg)
expect_ap); \
STMT_END
+/* Check that fascist_firewall_choose_address_ls() returns the expected
+ * results. */
+#define CHECK_CHOSEN_ADDR_NULL_LS() \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_ls_ap; \
+ tor_addr_make_null(&chosen_ls_ap.addr, AF_UNSPEC); \
+ chosen_ls_ap.port = 0; \
+ setup_full_capture_of_logs(LOG_WARN); \
+ fascist_firewall_choose_address_ls(NULL, 1, &chosen_ls_ap); \
+ expect_single_log_msg("Unknown or missing link specifiers"); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_CHOSEN_ADDR_LS(fake_ls, pref_only, expect_rv, expect_ap) \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_ls_ap; \
+ tor_addr_make_null(&chosen_ls_ap.addr, AF_UNSPEC); \
+ chosen_ls_ap.port = 0; \
+ setup_full_capture_of_logs(LOG_WARN); \
+ fascist_firewall_choose_address_ls(fake_ls, pref_only, &chosen_ls_ap); \
+ if (smartlist_len(fake_ls) == 0) { \
+ expect_single_log_msg("Link specifiers are empty"); \
+ } else { \
+ expect_no_log_entry(); \
+ tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_ls_ap.addr)); \
+ tt_int_op((expect_ap).port, OP_EQ, chosen_ls_ap.port); \
+ } \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_LS_LEGACY_ONLY(fake_ls) \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_ls_ap; \
+ tor_addr_make_null(&chosen_ls_ap.addr, AF_UNSPEC); \
+ chosen_ls_ap.port = 0; \
+ setup_full_capture_of_logs(LOG_WARN); \
+ fascist_firewall_choose_address_ls(fake_ls, 0, &chosen_ls_ap); \
+ expect_single_log_msg("None of our link specifiers have IPv4 or IPv6"); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS(fake_ls, direct_conn, expect_ap) \
+ STMT_BEGIN \
+ curve25519_secret_key_t seckey; \
+ curve25519_secret_key_generate(&seckey, 0); \
+ curve25519_public_key_t pubkey; \
+ curve25519_public_key_generate(&pubkey, &seckey); \
+ setup_full_capture_of_logs(LOG_WARN); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, &pubkey, \
+ direct_conn); \
+ if (fake_ls == NULL) { \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ expect_single_log_msg("Specified link specifiers is null"); \
+ } else { \
+ expect_no_log_entry(); \
+ tt_assert(tor_addr_eq(&(expect_ap).addr, &ei->addr)); \
+ tt_int_op((expect_ap).port, OP_EQ, ei->port); \
+ extend_info_free(ei); \
+ } \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS_NULL_KEY(fake_ls) \
+ STMT_BEGIN \
+ setup_full_capture_of_logs(LOG_WARN); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, NULL, 0); \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ expect_single_log_msg("Specified onion key is null"); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(fake_ls, direct_conn) \
+ STMT_BEGIN \
+ curve25519_secret_key_t seckey; \
+ curve25519_secret_key_generate(&seckey, 0); \
+ curve25519_public_key_t pubkey; \
+ curve25519_public_key_generate(&pubkey, &seckey); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, &pubkey, \
+ direct_conn); \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_MSG(fake_ls, msg_level, msg) \
+ STMT_BEGIN \
+ curve25519_secret_key_t seckey; \
+ curve25519_secret_key_generate(&seckey, 0); \
+ curve25519_public_key_t pubkey; \
+ curve25519_public_key_generate(&pubkey, &seckey); \
+ setup_full_capture_of_logs(msg_level); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, &pubkey, 0); \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ expect_single_log_msg(msg); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
/** Mock the preferred address function to return zero (prefer IPv4). */
static int
mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv4(void)
@@ -2472,6 +2572,141 @@ test_policies_fascist_firewall_choose_address(void *arg)
UNMOCK(fascist_firewall_rand_prefer_ipv6_addr);
+ /* Test firewall_choose_address_ls(). To do this, we make a fake link
+ * specifier. */
+ smartlist_t *lspecs = smartlist_new(),
+ *lspecs_blank = smartlist_new(),
+ *lspecs_v4 = smartlist_new(),
+ *lspecs_v6 = smartlist_new(),
+ *lspecs_no_legacy = smartlist_new(),
+ *lspecs_legacy_only = smartlist_new();
+ link_specifier_t *fake_ls;
+
+ /* IPv4 link specifier */
+ fake_ls = link_specifier_new();
+ link_specifier_set_ls_type(fake_ls, LS_IPV4);
+ link_specifier_set_un_ipv4_addr(fake_ls,
+ tor_addr_to_ipv4h(&ipv4_or_ap.addr));
+ link_specifier_set_un_ipv4_port(fake_ls, ipv4_or_ap.port);
+ link_specifier_set_ls_len(fake_ls, sizeof(ipv4_or_ap.addr.addr.in_addr) +
+ sizeof(ipv4_or_ap.port));
+ smartlist_add(lspecs, fake_ls);
+ smartlist_add(lspecs_v4, fake_ls);
+ smartlist_add(lspecs_no_legacy, fake_ls);
+
+ /* IPv6 link specifier */
+ fake_ls = link_specifier_new();
+ link_specifier_set_ls_type(fake_ls, LS_IPV6);
+ size_t addr_len = link_specifier_getlen_un_ipv6_addr(fake_ls);
+ const uint8_t *in6_addr = tor_addr_to_in6_addr8(&ipv6_or_ap.addr);
+ uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(fake_ls);
+ memcpy(ipv6_array, in6_addr, addr_len);
+ link_specifier_set_un_ipv6_port(fake_ls, ipv6_or_ap.port);
+ link_specifier_set_ls_len(fake_ls, addr_len + sizeof(ipv6_or_ap.port));
+ smartlist_add(lspecs, fake_ls);
+ smartlist_add(lspecs_v6, fake_ls);
+
+ /* Legacy ID link specifier */
+ fake_ls = link_specifier_new();
+ link_specifier_set_ls_type(fake_ls, LS_LEGACY_ID);
+ uint8_t *legacy_id = link_specifier_getarray_un_legacy_id(fake_ls);
+ memset(legacy_id, 'A', sizeof(*legacy_id));
+ link_specifier_set_ls_len(fake_ls,
+ link_specifier_getlen_un_legacy_id(fake_ls));
+ smartlist_add(lspecs, fake_ls);
+ smartlist_add(lspecs_legacy_only, fake_ls);
+ smartlist_add(lspecs_v4, fake_ls);
+ smartlist_add(lspecs_v6, fake_ls);
+
+ /* Check with bogus requests. */
+ tor_addr_port_t null_ap; \
+ tor_addr_make_null(&null_ap.addr, AF_UNSPEC); \
+ null_ap.port = 0; \
+
+ /* Check for a null link state. */
+ CHECK_CHOSEN_ADDR_NULL_LS();
+ CHECK_HS_EXTEND_INFO_ADDR_LS(NULL, 1, null_ap);
+
+ /* Check for a blank link state. */
+ CHECK_CHOSEN_ADDR_LS(lspecs_blank, 0, 0, null_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_blank, 0);
+
+ /* Check for a link state with only a Legacy ID. */
+ CHECK_LS_LEGACY_ONLY(lspecs_legacy_only);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_legacy_only, 0);
+ smartlist_free(lspecs_legacy_only);
+
+ /* Check with a null onion_key. */
+ CHECK_HS_EXTEND_INFO_ADDR_LS_NULL_KEY(lspecs_blank);
+ smartlist_free(lspecs_blank);
+
+ /* Check with a null onion_key. */
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_MSG(lspecs_no_legacy, LOG_WARN,
+ "Missing Legacy ID in link state");
+ smartlist_free(lspecs_no_legacy);
+
+ /* Enable both IPv4 and IPv6. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+
+ /* Prefer IPv4, enable both IPv4 and IPv6. */
+ mock_options.ClientPreferIPv6ORPort = 0;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv4_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv4_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ /* Prefer IPv6, enable both IPv4 and IPv6. */
+ mock_options.ClientPreferIPv6ORPort = 1;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv6_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv6_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ /* IPv4-only. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 0;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv4_or_ap);
+
+ CHECK_CHOSEN_ADDR_LS(lspecs_v6, 0, 0, null_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv4_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v6, 0);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v6, 1);
+
+ /* IPv6-only. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 1;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv6_or_ap);
+
+ CHECK_CHOSEN_ADDR_LS(lspecs_v4, 0, 0, null_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv6_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v4, 1);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v6, 0);
+
+ smartlist_free(lspecs_v4);
+ smartlist_free(lspecs_v6);
+
+ SMARTLIST_FOREACH(lspecs, link_specifier_t *, lspec, \
+ link_specifier_free(lspec)); \
+ smartlist_free(lspecs);
+
done:
UNMOCK(get_options);
}
diff --git a/src/test/test_prob_distr.c b/src/test/test_prob_distr.c
index 37cfdae7d9..0ecbf65f41 100644
--- a/src/test/test_prob_distr.c
+++ b/src/test/test_prob_distr.c
@@ -33,6 +33,7 @@
#include "lib/math/prob_distr.h"
#include "lib/math/fp.h"
#include "lib/crypt_ops/crypto_rand.h"
+#include "test/rng_test_helpers.h"
#include <float.h>
#include <math.h>
@@ -1117,49 +1118,15 @@ test_psi_dist_sample(const struct dist *dist)
}
}
-/* This is the seed of the deterministic randomness */
-static uint8_t rng_seed[16];
-static crypto_xof_t *rng_xof = NULL;
-
-/** Initialize the seed of the deterministic randomness. */
-static void
-init_deterministic_rand(void)
-{
- crypto_rand((char*)rng_seed, sizeof(rng_seed));
- crypto_xof_free(rng_xof);
- rng_xof = crypto_xof_new();
- crypto_xof_add_bytes(rng_xof, rng_seed, sizeof(rng_seed));
-}
-
static void
-teardown_deterministic_rand(void)
+write_stochastic_warning(void)
{
- crypto_xof_free(rng_xof);
-}
-
-static void
-dump_seed(void)
-{
- printf("\n"
+ if (tinytest_cur_test_has_failed()) {
+ printf("\n"
"NOTE: This is a stochastic test, and we expect it to fail from\n"
"time to time, with some low probability. If you see it fail more\n"
- "than one trial in 100, though, please tell us.\n\n"
- "Seed: %s\n",
- hex_str((const char*)rng_seed, sizeof(rng_seed)));
-}
-
-/** Produce deterministic randomness for the stochastic tests using the global
- * deterministic_rand_counter seed
- *
- * This function produces deterministic data over multiple calls iff it's
- * called in the same call order with the same 'n' parameter (which is the
- * case for the psi test). If not, outputs will deviate. */
-static void
-crypto_rand_deterministic(char *out, size_t n)
-{
- /* Use a XOF to squeeze bytes out of that silly counter */
- tor_assert(rng_xof);
- crypto_xof_squeeze_bytes(rng_xof, (uint8_t*)out, n);
+ "than one trial in 100, though, please tell us.\n\n");
+ }
}
static void
@@ -1199,8 +1166,7 @@ test_stochastic_uniform(void *arg)
};
bool ok = true, tests_failed = true;
- init_deterministic_rand();
- MOCK(crypto_rand, crypto_rand_deterministic);
+ testing_enable_reproducible_rng();
ok &= test_psi_dist_sample(&uniform01.base);
ok &= test_psi_dist_sample(&uniform_pos.base);
@@ -1215,10 +1181,9 @@ test_stochastic_uniform(void *arg)
done:
if (tests_failed) {
- dump_seed();
+ write_stochastic_warning();
}
- teardown_deterministic_rand();
- UNMOCK(crypto_rand);
+ testing_disable_reproducible_rng();
}
static bool
@@ -1288,8 +1253,7 @@ test_stochastic_genpareto(void *arg)
bool tests_failed = true;
(void) arg;
- init_deterministic_rand();
- MOCK(crypto_rand, crypto_rand_deterministic);
+ testing_enable_reproducible_rng();
ok = test_stochastic_genpareto_impl(0, 1, -0.25);
tt_assert(ok);
@@ -1310,10 +1274,9 @@ test_stochastic_genpareto(void *arg)
done:
if (tests_failed) {
- dump_seed();
+ write_stochastic_warning();
}
- teardown_deterministic_rand();
- UNMOCK(crypto_rand);
+ testing_disable_reproducible_rng();
}
static void
@@ -1324,8 +1287,7 @@ test_stochastic_geometric(void *arg)
(void) arg;
- init_deterministic_rand();
- MOCK(crypto_rand, crypto_rand_deterministic);
+ testing_enable_reproducible_rng();
ok = test_stochastic_geometric_impl(0.1);
tt_assert(ok);
@@ -1340,10 +1302,9 @@ test_stochastic_geometric(void *arg)
done:
if (tests_failed) {
- dump_seed();
+ write_stochastic_warning();
}
- teardown_deterministic_rand();
- UNMOCK(crypto_rand);
+ testing_disable_reproducible_rng();
}
static void
@@ -1353,8 +1314,7 @@ test_stochastic_logistic(void *arg)
bool tests_failed = true;
(void) arg;
- init_deterministic_rand();
- MOCK(crypto_rand, crypto_rand_deterministic);
+ testing_enable_reproducible_rng();
ok = test_stochastic_logistic_impl(0, 1);
tt_assert(ok);
@@ -1369,21 +1329,18 @@ test_stochastic_logistic(void *arg)
done:
if (tests_failed) {
- dump_seed();
+ write_stochastic_warning();
}
- teardown_deterministic_rand();
- UNMOCK(crypto_rand);
+ testing_disable_reproducible_rng();
}
static void
test_stochastic_log_logistic(void *arg)
{
bool ok = 0;
- bool tests_failed = true;
(void) arg;
- init_deterministic_rand();
- MOCK(crypto_rand, crypto_rand_deterministic);
+ testing_enable_reproducible_rng();
ok = test_stochastic_log_logistic_impl(1, 1);
tt_assert(ok);
@@ -1394,25 +1351,18 @@ test_stochastic_log_logistic(void *arg)
ok = test_stochastic_log_logistic_impl(exp(-10), 1e-2);
tt_assert(ok);
- tests_failed = false;
-
done:
- if (tests_failed) {
- dump_seed();
- }
- teardown_deterministic_rand();
- UNMOCK(crypto_rand);
+ write_stochastic_warning();
+ testing_disable_reproducible_rng();
}
static void
test_stochastic_weibull(void *arg)
{
bool ok = 0;
- bool tests_failed = true;
(void) arg;
- init_deterministic_rand();
- MOCK(crypto_rand, crypto_rand_deterministic);
+ testing_enable_reproducible_rng();
ok = test_stochastic_weibull_impl(1, 0.5);
tt_assert(ok);
@@ -1425,13 +1375,9 @@ test_stochastic_weibull(void *arg)
ok = test_stochastic_weibull_impl(10, 1);
tt_assert(ok);
- tests_failed = false;
-
done:
- if (tests_failed) {
- dump_seed();
- }
- teardown_deterministic_rand();
+ write_stochastic_warning();
+ testing_disable_reproducible_rng();
UNMOCK(crypto_rand);
}
diff --git a/src/test/test_process.c b/src/test/test_process.c
index 7cc01d2442..7836312761 100644
--- a/src/test/test_process.c
+++ b/src/test/test_process.c
@@ -594,7 +594,7 @@ test_unix(void *arg)
done:
process_free(process);
-#endif
+#endif /* !defined(_WIN32) */
}
static void
@@ -649,7 +649,7 @@ test_win32(void *arg)
done:
tor_free(joined_argv);
process_free(process);
-#endif
+#endif /* defined(_WIN32) */
}
struct testcase_t process_tests[] = {
diff --git a/src/test/test_process_slow.c b/src/test/test_process_slow.c
index 1322d7b833..91252c725d 100644
--- a/src/test/test_process_slow.c
+++ b/src/test/test_process_slow.c
@@ -135,7 +135,7 @@ get_win32_test_binary_path(void)
done:
return NULL;
}
-#endif
+#endif /* defined(_WIN32) */
static void
main_loop_timeout_cb(periodic_timer_t *timer, void *data)
diff --git a/src/test/test_protover.c b/src/test/test_protover.c
index 63c508bd13..1759aef97d 100644
--- a/src/test/test_protover.c
+++ b/src/test/test_protover.c
@@ -22,7 +22,7 @@ test_protover_parse(void *arg)
tt_skip();
done:
;
-#else
+#else /* !(defined(HAVE_RUST)) */
char *re_encoded = NULL;
const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900";
@@ -89,7 +89,7 @@ test_protover_parse(void *arg)
SMARTLIST_FOREACH(elts, proto_entry_t *, ent, proto_entry_free(ent));
smartlist_free(elts);
tor_free(re_encoded);
-#endif
+#endif /* defined(HAVE_RUST) */
}
static void
@@ -133,7 +133,7 @@ test_protover_parse_fail(void *arg)
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
tt_ptr_op(elts, OP_EQ, NULL);
-#endif
+#endif /* defined(HAVE_RUST) */
done:
;
}
@@ -335,7 +335,7 @@ test_protover_all_supported(void *arg)
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaa=1-65536", &msg));
tor_end_capture_bugs_();
-#endif
+#endif /* !defined(HAVE_RUST) */
done:
tor_end_capture_bugs_();
@@ -459,7 +459,7 @@ test_protover_supported_protocols(void *arg)
tt_assert(protocol_list_supports_protocol(supported_protocols,
PRT_LINKAUTH,
PROTOVER_LINKAUTH_V1));
-#endif
+#endif /* defined(HAVE_WORKING_TOR_TLS_GET_TLSSECRETS) */
/* Latest LinkAuth is not exposed in the headers. */
tt_assert(protocol_list_supports_protocol(supported_protocols,
PRT_LINKAUTH,
diff --git a/src/test/test_pt.c b/src/test/test_pt.c
index d2996f4cc3..87e3ba356c 100644
--- a/src/test/test_pt.c
+++ b/src/test/test_pt.c
@@ -7,12 +7,13 @@
#define PT_PRIVATE
#define UTIL_PRIVATE
#define STATEFILE_PRIVATE
-#define CONTROL_PRIVATE
+#define CONTROL_EVENTS_PRIVATE
#define PROCESS_PRIVATE
#include "core/or/or.h"
#include "app/config/config.h"
#include "app/config/confparse.h"
#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/client/transports.h"
#include "core/or/circuitbuild.h"
#include "app/config/statefile.h"
diff --git a/src/test/test_ptr_slow.c b/src/test/test_ptr_slow.c
new file mode 100644
index 0000000000..76bdbf1891
--- /dev/null
+++ b/src/test/test_ptr_slow.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "core/or/or.h"
+#include "test/test.h"
+#include "test/ptr_helpers.h"
+
+#include <stdint.h>
+#include <limits.h>
+
+/** Assert that <b>a</b> can be cast to void * and back. */
+static void
+assert_int_voidptr_roundtrip(int a)
+{
+ intptr_t ap = (intptr_t)a;
+ void *b = cast_intptr_to_voidstar(ap);
+ intptr_t c = cast_voidstar_to_intptr(b);
+ void *d = cast_intptr_to_voidstar(c);
+
+ tt_assert(ap == c);
+ tt_assert(b == d);
+
+ done:
+ return;
+}
+
+/** Test for possibility of casting `int` to `void *` and back. */
+static void
+test_int_voidstar_interop(void *arg)
+{
+ int a;
+ (void)arg;
+
+ for (a = -1024; a <= 1024; a++) {
+ assert_int_voidptr_roundtrip(a);
+ }
+
+ for (a = INT_MIN; a <= INT_MIN+1024; a++) {
+ assert_int_voidptr_roundtrip(a);
+ }
+
+ for (a = INT_MAX-1024; a < INT_MAX; a++) {
+ assert_int_voidptr_roundtrip(a);
+ }
+
+ a = 1;
+ for (unsigned long i = 0; i < sizeof(int) * 8; i++) {
+ assert_int_voidptr_roundtrip(a);
+ a = (a << 1);
+ }
+}
+
+/** Assert that <b>a</b> can be cast to void * and back. */
+static void
+assert_uint_voidptr_roundtrip(unsigned int a)
+{
+ uintptr_t ap = (uintptr_t)a;
+ void *b = cast_uintptr_to_voidstar(ap);
+ uintptr_t c = cast_voidstar_to_uintptr(b);
+ void *d = cast_uintptr_to_voidstar(c);
+
+ tt_assert(ap == c);
+ tt_assert(b == d);
+
+ done:
+ return;
+}
+
+/** Test for possibility of casting `int` to `void *` and back. */
+static void
+test_uint_voidstar_interop(void *arg)
+{
+ unsigned int a;
+ (void)arg;
+
+ for (a = 0; a <= 1024; a++) {
+ assert_uint_voidptr_roundtrip(a);
+ }
+
+ for (a = UINT_MAX-1024; a < UINT_MAX; a++) {
+ assert_uint_voidptr_roundtrip(a);
+ }
+
+ a = 1;
+ for (unsigned long i = 0; i < sizeof(int) * 8; i++) {
+ assert_uint_voidptr_roundtrip(a);
+ a = (a << 1);
+ }
+}
+
+struct testcase_t slow_ptr_tests[] = {
+ { .name = "int_voidstar_interop",
+ .fn = test_int_voidstar_interop,
+ .flags = 0,
+ .setup = NULL,
+ .setup_data = NULL },
+ { .name = "uint_voidstar_interop",
+ .fn = test_uint_voidstar_interop,
+ .flags = 0,
+ .setup = NULL,
+ .setup_data = NULL },
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_pubsub_build.c b/src/test/test_pubsub_build.c
new file mode 100644
index 0000000000..ce5bf60080
--- /dev/null
+++ b/src/test/test_pubsub_build.c
@@ -0,0 +1,621 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define DISPATCH_PRIVATE
+#define PUBSUB_PRIVATE
+
+#include "test/test.h"
+
+#include "lib/cc/torint.h"
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/dispatch/dispatch_st.h"
+#include "lib/dispatch/msgtypes.h"
+#include "lib/pubsub/pubsub_macros.h"
+#include "lib/pubsub/pubsub_build.h"
+#include "lib/pubsub/pubsub_builder_st.h"
+
+#include "lib/log/escape.h"
+#include "lib/malloc/malloc.h"
+#include "lib/string/printf.h"
+
+#include "test/log_test_helpers.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static char *
+ex_int_fmt(msg_aux_data_t aux)
+{
+ int val = (int) aux.u64;
+ char *r=NULL;
+ tor_asprintf(&r, "%d", val);
+ return r;
+}
+
+static char *
+ex_str_fmt(msg_aux_data_t aux)
+{
+ return esc_for_log(aux.ptr);
+}
+
+static void
+ex_str_free(msg_aux_data_t aux)
+{
+ tor_free_(aux.ptr);
+}
+
+static dispatch_typefns_t intfns = {
+ .fmt_fn = ex_int_fmt
+};
+
+static dispatch_typefns_t stringfns = {
+ .free_fn = ex_str_free,
+ .fmt_fn = ex_str_fmt
+};
+
+DECLARE_MESSAGE_INT(bunch_of_coconuts, int, int);
+DECLARE_PUBLISH(bunch_of_coconuts);
+DECLARE_SUBSCRIBE(bunch_of_coconuts, coconut_recipient_cb);
+
+DECLARE_MESSAGE(yes_we_have_no, string, char *);
+DECLARE_PUBLISH(yes_we_have_no);
+DECLARE_SUBSCRIBE(yes_we_have_no, absent_item_cb);
+
+static void
+coconut_recipient_cb(const msg_t *m, int n_coconuts)
+{
+ (void)m;
+ (void)n_coconuts;
+}
+
+static void
+absent_item_cb(const msg_t *m, const char *fruitname)
+{
+ (void)m;
+ (void)fruitname;
+}
+
+#define FLAG_SKIP 99999
+
+static void
+seed_dispatch_builder(pubsub_builder_t *b,
+ unsigned fl1, unsigned fl2, unsigned fl3, unsigned fl4)
+{
+ pubsub_connector_t *c = NULL;
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys1"));
+ DISPATCH_REGISTER_TYPE(c, int, &intfns);
+ if (fl1 != FLAG_SKIP)
+ DISPATCH_ADD_PUB_(c, main, bunch_of_coconuts, fl1);
+ if (fl2 != FLAG_SKIP)
+ DISPATCH_ADD_SUB_(c, main, yes_we_have_no, fl2);
+ pubsub_connector_free(c);
+ }
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys2"));
+ DISPATCH_REGISTER_TYPE(c, string, &stringfns);
+ if (fl3 != FLAG_SKIP)
+ DISPATCH_ADD_PUB_(c, main, yes_we_have_no, fl3);
+ if (fl4 != FLAG_SKIP)
+ DISPATCH_ADD_SUB_(c, main, bunch_of_coconuts, fl4);
+ pubsub_connector_free(c);
+ }
+}
+
+static void
+seed_pubsub_builder_basic(pubsub_builder_t *b)
+{
+ seed_dispatch_builder(b, 0, 0, 0, 0);
+}
+
+/* Regular builder with valid types and messages.
+ */
+static void
+test_pubsub_build_types_ok(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+ pubsub_items_t *items = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+
+ dispatcher = pubsub_builder_finalize(b, &items);
+ b = NULL;
+ tt_assert(dispatcher);
+ tt_assert(items);
+ tt_int_op(smartlist_len(items->items), OP_EQ, 4);
+
+ // Make sure that the bindings got build correctly.
+ SMARTLIST_FOREACH_BEGIN(items->items, pubsub_cfg_t *, item) {
+ if (item->is_publish) {
+ tt_assert(item->pub_binding);
+ tt_ptr_op(item->pub_binding->dispatch_ptr, OP_EQ, dispatcher);
+ }
+ } SMARTLIST_FOREACH_END(item);
+
+ tt_int_op(dispatcher->n_types, OP_GE, 2);
+ tt_assert(dispatcher->typefns);
+
+ tt_assert(dispatcher->typefns[get_msg_type_id("int")].fmt_fn == ex_int_fmt);
+ tt_assert(dispatcher->typefns[get_msg_type_id("string")].fmt_fn ==
+ ex_str_fmt);
+
+ // Now clear the bindings, like we would do before freeing the
+ // the dispatcher.
+ pubsub_items_clear_bindings(items);
+ SMARTLIST_FOREACH_BEGIN(items->items, pubsub_cfg_t *, item) {
+ if (item->is_publish) {
+ tt_assert(item->pub_binding);
+ tt_ptr_op(item->pub_binding->dispatch_ptr, OP_EQ, NULL);
+ }
+ } SMARTLIST_FOREACH_END(item);
+
+ done:
+ pubsub_connector_free(c);
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ pubsub_items_free(items);
+}
+
+/* We fail if the same type is defined in two places with different functions.
+ */
+static void
+test_pubsub_build_types_decls_conflict(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
+ // Extra declaration of int: we don't allow this.
+ DISPATCH_REGISTER_TYPE(c, int, &stringfns);
+ pubsub_connector_free(c);
+ }
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+ // expect_log_msg_containing("(int) declared twice"); // XXXX
+
+ done:
+ pubsub_connector_free(c);
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* If a message ID exists but nobody is publishing or subscribing to it,
+ * that's okay. */
+static void
+test_pubsub_build_unused_message(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+
+ // This message isn't actually generated by anyone, but that will be fine:
+ // we just log it at info.
+ get_message_id("unused");
+ setup_capture_of_logs(LOG_INFO);
+
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher);
+ expect_log_msg_containing(
+ "Nobody is publishing or subscribing to message");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* Publishing or subscribing to a message with no subscribers / publishers
+ * should fail and warn. */
+static void
+test_pubsub_build_missing_pubsub(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+
+ b = pubsub_builder_new();
+ seed_dispatch_builder(b, 0, 0, FLAG_SKIP, FLAG_SKIP);
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing(
+ "Message \"bunch_of_coconuts\" has publishers, but no subscribers.");
+ expect_log_msg_containing(
+ "Message \"yes_we_have_no\" has subscribers, but no publishers.");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* Make sure that a stub publisher or subscriber prevents an error from
+ * happening even if there are no other publishers/subscribers for a message
+ */
+static void
+test_pubsub_build_stub_pubsub(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+
+ b = pubsub_builder_new();
+ seed_dispatch_builder(b, 0, 0, DISP_FLAG_STUB, DISP_FLAG_STUB);
+
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher);
+
+ // 1 subscriber.
+ tt_int_op(1, OP_EQ,
+ dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
+ // no subscribers
+ tt_ptr_op(NULL, OP_EQ,
+ dispatcher->table[get_message_id("bunch_of_coconuts")]);
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+}
+
+/* Only one channel per msg id. */
+static void
+test_pubsub_build_channels_conflict(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+ pub_binding_t btmp;
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("problems"));
+ /* Usually the DISPATCH_ADD_PUB macro would keep us from using
+ * the wrong channel */
+ pubsub_add_pub_(c, &btmp, get_channel_id("hithere"),
+ get_message_id("bunch_of_coconuts"),
+ get_msg_type_id("int"),
+ 0 /* flags */,
+ "somewhere.c", 22);
+ pubsub_connector_free(c);
+ };
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing("Message \"bunch_of_coconuts\" is associated "
+ "with multiple inconsistent channels.");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* Only one type per msg id. */
+static void
+test_pubsub_build_types_conflict(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+ pub_binding_t btmp;
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("problems"));
+ /* Usually the DISPATCH_ADD_PUB macro would keep us from using
+ * the wrong channel */
+ pubsub_add_pub_(c, &btmp, get_channel_id("hithere"),
+ get_message_id("bunch_of_coconuts"),
+ get_msg_type_id("string"),
+ 0 /* flags */,
+ "somewhere.c", 22);
+ pubsub_connector_free(c);
+ };
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing("Message \"bunch_of_coconuts\" is associated "
+ "with multiple inconsistent message types.");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* The same module can't publish and subscribe the same message */
+static void
+test_pubsub_build_pubsub_same(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys1"));
+ // already publishing this.
+ DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
+ pubsub_connector_free(c);
+ };
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing("Message \"bunch_of_coconuts\" is published "
+ "and subscribed by the same subsystem \"sys1\".");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* More than one subsystem may publish or subscribe, and that's okay. */
+static void
+test_pubsub_build_pubsub_multi(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+ pub_binding_t btmp;
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
+ DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
+ pubsub_add_pub_(c, &btmp, get_channel_id("main"),
+ get_message_id("yes_we_have_no"),
+ get_msg_type_id("string"),
+ 0 /* flags */,
+ "somewhere.c", 22);
+ pubsub_connector_free(c);
+ };
+
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher);
+
+ // 1 subscribers
+ tt_int_op(1, OP_EQ,
+ dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
+ // 2 subscribers.
+ dtbl_entry_t *ent =
+ dispatcher->table[get_message_id("bunch_of_coconuts")];
+ tt_int_op(2, OP_EQ, ent->n_enabled);
+ tt_int_op(2, OP_EQ, ent->n_fns);
+ tt_ptr_op(ent->rcv[0].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+ tt_ptr_op(ent->rcv[1].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+}
+
+static void
+some_other_coconut_hook(const msg_t *m)
+{
+ (void)m;
+}
+
+/* Subscribe hooks should be build correctly when there are a bunch of
+ * them. */
+static void
+test_pubsub_build_sub_many(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+ char *sysname = NULL;
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+
+ int i;
+ for (i = 1; i < 100; ++i) {
+ tor_asprintf(&sysname, "system%d",i);
+ c = pubsub_connector_for_subsystem(b, get_subsys_id(sysname));
+ if (i % 7) {
+ DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
+ } else {
+ pubsub_add_sub_(c, some_other_coconut_hook,
+ get_channel_id("main"),
+ get_message_id("bunch_of_coconuts"),
+ get_msg_type_id("int"),
+ 0 /* flags */,
+ "somewhere.c", 22);
+ }
+ pubsub_connector_free(c);
+ tor_free(sysname);
+ };
+
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher);
+
+ dtbl_entry_t *ent =
+ dispatcher->table[get_message_id("bunch_of_coconuts")];
+ tt_int_op(100, OP_EQ, ent->n_enabled);
+ tt_int_op(100, OP_EQ, ent->n_fns);
+ tt_ptr_op(ent->rcv[0].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+ tt_ptr_op(ent->rcv[1].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+ tt_ptr_op(ent->rcv[76].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+ tt_ptr_op(ent->rcv[77].fn, OP_EQ, some_other_coconut_hook);
+ tt_ptr_op(ent->rcv[78].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ tor_free(sysname);
+}
+
+/* The same subsystem can only declare one publish or subscribe. */
+static void
+test_pubsub_build_pubsub_redundant(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+ pub_binding_t btmp;
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys2"));
+ DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
+ pubsub_add_pub_(c, &btmp, get_channel_id("main"),
+ get_message_id("yes_we_have_no"),
+ get_msg_type_id("string"),
+ 0 /* flags */,
+ "somewhere.c", 22);
+ pubsub_connector_free(c);
+ };
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing(
+ "Message \"yes_we_have_no\" is configured to be published by "
+ "subsystem \"sys2\" more than once.");
+ expect_log_msg_containing(
+ "Message \"bunch_of_coconuts\" is configured to be subscribed by "
+ "subsystem \"sys2\" more than once.");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* It's fine to declare the excl flag. */
+static void
+test_pubsub_build_excl_ok(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+
+ b = pubsub_builder_new();
+ // Try one excl/excl pair and one excl/non pair.
+ seed_dispatch_builder(b, DISP_FLAG_EXCL, 0,
+ DISP_FLAG_EXCL, DISP_FLAG_EXCL);
+
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher);
+
+ // 1 subscribers
+ tt_int_op(1, OP_EQ,
+ dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
+ // 1 subscriber.
+ tt_int_op(1, OP_EQ,
+ dispatcher->table[get_message_id("bunch_of_coconuts")]->n_enabled);
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+}
+
+/* but if you declare the excl flag, you need to mean it. */
+static void
+test_pubsub_build_excl_bad(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_dispatch_builder(b, DISP_FLAG_EXCL, DISP_FLAG_EXCL,
+ 0, 0);
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
+ DISPATCH_ADD_PUB_(c, main, bunch_of_coconuts, 0);
+ DISPATCH_ADD_SUB_(c, main, yes_we_have_no, 0);
+ pubsub_connector_free(c);
+ };
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing("has multiple publishers, but at least one is "
+ "marked as exclusive.");
+ expect_log_msg_containing("has multiple subscribers, but at least one is "
+ "marked as exclusive.");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+#define T(name, flags) \
+ { #name, test_pubsub_build_ ## name , (flags), NULL, NULL }
+
+struct testcase_t pubsub_build_tests[] = {
+ T(types_ok, TT_FORK),
+ T(types_decls_conflict, TT_FORK),
+ T(unused_message, TT_FORK),
+ T(missing_pubsub, TT_FORK),
+ T(stub_pubsub, TT_FORK),
+ T(channels_conflict, TT_FORK),
+ T(types_conflict, TT_FORK),
+ T(pubsub_same, TT_FORK),
+ T(pubsub_multi, TT_FORK),
+ T(sub_many, TT_FORK),
+ T(pubsub_redundant, TT_FORK),
+ T(excl_ok, TT_FORK),
+ T(excl_bad, TT_FORK),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_pubsub_msg.c b/src/test/test_pubsub_msg.c
new file mode 100644
index 0000000000..73c7c9f540
--- /dev/null
+++ b/src/test/test_pubsub_msg.c
@@ -0,0 +1,305 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define DISPATCH_PRIVATE
+
+#include "test/test.h"
+
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/dispatch/dispatch_st.h"
+#include "lib/dispatch/msgtypes.h"
+#include "lib/pubsub/pubsub_flags.h"
+#include "lib/pubsub/pub_binding_st.h"
+#include "lib/pubsub/pubsub_build.h"
+#include "lib/pubsub/pubsub_builder_st.h"
+#include "lib/pubsub/pubsub_connect.h"
+#include "lib/pubsub/pubsub_publish.h"
+
+#include "lib/log/escape.h"
+#include "lib/malloc/malloc.h"
+#include "lib/string/printf.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static char *
+ex_str_fmt(msg_aux_data_t aux)
+{
+ return esc_for_log(aux.ptr);
+}
+static void
+ex_str_free(msg_aux_data_t aux)
+{
+ tor_free_(aux.ptr);
+}
+static dispatch_typefns_t stringfns = {
+ .free_fn = ex_str_free,
+ .fmt_fn = ex_str_fmt
+};
+
+// We're using the lowest-level publish/subscribe logic here, to avoid the
+// pubsub_macros.h macros and just test the dispatch core. We'll use a string
+// type for everything.
+
+#define DECLARE_MESSAGE(suffix) \
+ static pub_binding_t pub_binding_##suffix; \
+ static int msg_received_##suffix = 0; \
+ static void recv_msg_##suffix(const msg_t *m) { \
+ (void)m; \
+ ++msg_received_##suffix; \
+ } \
+ EAT_SEMICOLON
+
+#define ADD_PUBLISH(binding_suffix, subsys, channel, msg, flags) \
+ STMT_BEGIN { \
+ con = pubsub_connector_for_subsystem(builder, \
+ get_subsys_id(#subsys)); \
+ pubsub_add_pub_(con, &pub_binding_##binding_suffix, \
+ get_channel_id(#channel), \
+ get_message_id(#msg), get_msg_type_id("string"), \
+ (flags), __FILE__, __LINE__); \
+ pubsub_connector_free(con); \
+ } STMT_END
+
+#define ADD_SUBSCRIBE(hook_suffix, subsys, channel, msg, flags) \
+ STMT_BEGIN { \
+ con = pubsub_connector_for_subsystem(builder, \
+ get_subsys_id(#subsys)); \
+ pubsub_add_sub_(con, recv_msg_##hook_suffix, \
+ get_channel_id(#channel), \
+ get_message_id(#msg), get_msg_type_id("string"), \
+ (flags), __FILE__, __LINE__); \
+ pubsub_connector_free(con); \
+ } STMT_END
+
+#define SEND(binding_suffix, val) \
+ STMT_BEGIN { \
+ msg_aux_data_t data_; \
+ data_.ptr = tor_strdup(val); \
+ pubsub_pub_(&pub_binding_##binding_suffix, data_); \
+ } STMT_END
+
+DECLARE_MESSAGE(msg1);
+DECLARE_MESSAGE(msg2);
+DECLARE_MESSAGE(msg3);
+DECLARE_MESSAGE(msg4);
+DECLARE_MESSAGE(msg5);
+
+static smartlist_t *strings_received = NULL;
+static void
+recv_msg_copy_string(const msg_t *m)
+{
+ const char *s = m->aux_data__.ptr;
+ smartlist_add(strings_received, tor_strdup(s));
+}
+
+static void *
+setup_dispatcher(const struct testcase_t *testcase)
+{
+ (void)testcase;
+ pubsub_builder_t *builder = pubsub_builder_new();
+ pubsub_connector_t *con;
+
+ {
+ con = pubsub_connector_for_subsystem(builder, get_subsys_id("types"));
+ pubsub_connector_register_type_(con,
+ get_msg_type_id("string"),
+ &stringfns,
+ "nowhere.c", 99);
+ pubsub_connector_free(con);
+ }
+ // message1 has one publisher and one subscriber.
+ ADD_PUBLISH(msg1, sys1, main, message1, 0);
+ ADD_SUBSCRIBE(msg1, sys2, main, message1, 0);
+
+ // message2 has a publisher and a stub subscriber.
+ ADD_PUBLISH(msg2, sys1, main, message2, 0);
+ ADD_SUBSCRIBE(msg2, sys2, main, message2, DISP_FLAG_STUB);
+
+ // message3 has a publisher and three subscribers.
+ ADD_PUBLISH(msg3, sys1, main, message3, 0);
+ ADD_SUBSCRIBE(msg3, sys2, main, message3, 0);
+ ADD_SUBSCRIBE(msg3, sys3, main, message3, 0);
+ ADD_SUBSCRIBE(msg3, sys4, main, message3, 0);
+
+ // message4 has one publisher and two subscribers, but it's on another
+ // channel.
+ ADD_PUBLISH(msg4, sys2, other, message4, 0);
+ ADD_SUBSCRIBE(msg4, sys1, other, message4, 0);
+ ADD_SUBSCRIBE(msg4, sys3, other, message4, 0);
+
+ // message5 has a huge number of recipients.
+ ADD_PUBLISH(msg5, sys3, main, message5, 0);
+ ADD_SUBSCRIBE(msg5, sys4, main, message5, 0);
+ ADD_SUBSCRIBE(msg5, sys5, main, message5, 0);
+ ADD_SUBSCRIBE(msg5, sys6, main, message5, 0);
+ ADD_SUBSCRIBE(msg5, sys7, main, message5, 0);
+ ADD_SUBSCRIBE(msg5, sys8, main, message5, 0);
+ for (int i = 0; i < 1000-5; ++i) {
+ char *sys;
+ tor_asprintf(&sys, "xsys-%d", i);
+ con = pubsub_connector_for_subsystem(builder, get_subsys_id(sys));
+ pubsub_add_sub_(con, recv_msg_copy_string,
+ get_channel_id("main"),
+ get_message_id("message5"),
+ get_msg_type_id("string"), 0, "here", 100);
+ pubsub_connector_free(con);
+ tor_free(sys);
+ }
+
+ return pubsub_builder_finalize(builder, NULL);
+}
+
+static int
+cleanup_dispatcher(const struct testcase_t *testcase, void *dispatcher_)
+{
+ (void)testcase;
+ dispatch_t *dispatcher = dispatcher_;
+ dispatch_free(dispatcher);
+ return 1;
+}
+
+static const struct testcase_setup_t dispatcher_setup = {
+ setup_dispatcher, cleanup_dispatcher
+};
+
+static void
+test_pubsub_msg_minimal(void *arg)
+{
+ dispatch_t *d = arg;
+
+ tt_int_op(0, OP_EQ, msg_received_msg1);
+ SEND(msg1, "hello world");
+ tt_int_op(0, OP_EQ, msg_received_msg1); // hasn't actually arrived yet.
+
+ tt_int_op(0, OP_EQ, dispatch_flush(d, get_channel_id("main"), 1000));
+ tt_int_op(1, OP_EQ, msg_received_msg1); // we got the message!
+
+ done:
+ ;
+}
+
+static void
+test_pubsub_msg_send_to_stub(void *arg)
+{
+ dispatch_t *d = arg;
+
+ tt_int_op(0, OP_EQ, msg_received_msg2);
+ SEND(msg2, "hello silence");
+ tt_int_op(0, OP_EQ, msg_received_msg2); // hasn't actually arrived yet.
+
+ tt_int_op(0, OP_EQ, dispatch_flush(d, get_channel_id("main"), 1000));
+ tt_int_op(0, OP_EQ, msg_received_msg2); // doesn't arrive -- stub hook.
+
+ done:
+ ;
+}
+
+static void
+test_pubsub_msg_cancel_msgs(void *arg)
+{
+ dispatch_t *d = arg;
+
+ tt_int_op(0, OP_EQ, msg_received_msg1);
+ for (int i = 0; i < 100; ++i) {
+ SEND(msg1, "hello world");
+ }
+ tt_int_op(0, OP_EQ, msg_received_msg1); // hasn't actually arrived yet.
+
+ tt_int_op(0, OP_EQ, dispatch_flush(d, get_channel_id("main"), 10));
+ tt_int_op(10, OP_EQ, msg_received_msg1); // we got the message 10 times.
+
+ // At this point, the dispatcher will be freed with queued, undelivered
+ // messages.
+ done:
+ ;
+}
+
+struct alertfn_target {
+ dispatch_t *d;
+ channel_id_t ch;
+ int count;
+};
+static void
+alertfn_generic(dispatch_t *d, channel_id_t ch, void *arg)
+{
+ struct alertfn_target *t = arg;
+ tt_ptr_op(d, OP_EQ, t->d);
+ tt_int_op(ch, OP_EQ, t->ch);
+ ++t->count;
+ done:
+ ;
+}
+
+static void
+test_pubsub_msg_alertfns(void *arg)
+{
+ dispatch_t *d = arg;
+ struct alertfn_target ch1_a = { d, get_channel_id("main"), 0 };
+ struct alertfn_target ch2_a = { d, get_channel_id("other"), 0 };
+
+ tt_int_op(0, OP_EQ,
+ dispatch_set_alert_fn(d, get_channel_id("main"),
+ alertfn_generic, &ch1_a));
+ tt_int_op(0, OP_EQ,
+ dispatch_set_alert_fn(d, get_channel_id("other"),
+ alertfn_generic, &ch2_a));
+
+ SEND(msg3, "hello");
+ tt_int_op(ch1_a.count, OP_EQ, 1);
+ SEND(msg3, "world");
+ tt_int_op(ch1_a.count, OP_EQ, 1); // only the first message sends an alert
+ tt_int_op(ch2_a.count, OP_EQ, 0); // no alert for 'other'
+
+ SEND(msg4, "worse things happen in C");
+ tt_int_op(ch2_a.count, OP_EQ, 1);
+
+ // flush the first (main) channel...
+ tt_int_op(0, OP_EQ, dispatch_flush(d, get_channel_id("main"), 1000));
+ tt_int_op(6, OP_EQ, msg_received_msg3); // 3 subscribers, 2 instances.
+
+ // now that the main channel is flushed, sending another message on it
+ // starts another alert.
+ tt_int_op(ch1_a.count, OP_EQ, 1);
+ SEND(msg1, "plover");
+ tt_int_op(ch1_a.count, OP_EQ, 2);
+ tt_int_op(ch2_a.count, OP_EQ, 1);
+
+ done:
+ ;
+}
+
+/* try more than N_FAST_FNS hooks on msg5 */
+static void
+test_pubsub_msg_many_hooks(void *arg)
+{
+ dispatch_t *d = arg;
+ strings_received = smartlist_new();
+
+ tt_int_op(0, OP_EQ, msg_received_msg5);
+ SEND(msg5, "hello world");
+ tt_int_op(0, OP_EQ, msg_received_msg5);
+ tt_int_op(0, OP_EQ, smartlist_len(strings_received));
+
+ tt_int_op(0, OP_EQ, dispatch_flush(d, get_channel_id("main"), 100000));
+ tt_int_op(5, OP_EQ, msg_received_msg5);
+ tt_int_op(995, OP_EQ, smartlist_len(strings_received));
+
+ done:
+ SMARTLIST_FOREACH(strings_received, char *, s, tor_free(s));
+ smartlist_free(strings_received);
+}
+
+#define T(name) \
+ { #name, test_pubsub_msg_ ## name , TT_FORK, \
+ &dispatcher_setup, NULL }
+
+struct testcase_t pubsub_msg_tests[] = {
+ T(minimal),
+ T(send_to_stub),
+ T(cancel_msgs),
+ T(alertfns),
+ T(many_hooks),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_rebind.sh b/src/test/test_rebind.sh
index ea2012957e..e0d8394d38 100755
--- a/src/test/test_rebind.sh
+++ b/src/test/test_rebind.sh
@@ -7,7 +7,7 @@ if test "$UNAME_OS" = 'CYGWIN' || \
test "$UNAME_OS" = 'MSYS' || \
test "$UNAME_OS" = 'MINGW'; then
if test "$APPVEYOR" = 'True'; then
- echo "This test is disabled on Windows CI, as it requires firewall examptions. Skipping." >&2
+ echo "This test is disabled on Windows CI, as it requires firewall exemptions. Skipping." >&2
exit 77
fi
fi
@@ -15,10 +15,15 @@ fi
exitcode=0
tmpdir=
-clean () { test -n "$tmpdir" && test -d "$tmpdir" && rm -rf "$tmpdir" || :; }
+clean () {
+ if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then
+ rm -rf "$tmpdir"
+ fi
+}
+
trap clean EXIT HUP INT TERM
-tmpdir="`mktemp -d -t tor_rebind_test.XXXXXX`"
+tmpdir="$(mktemp -d -t tor_rebind_test.XXXXXX)"
if [ -z "$tmpdir" ]; then
echo >&2 mktemp failed
exit 2
diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c
index 0623583511..c65279fb25 100644
--- a/src/test/test_relaycell.c
+++ b/src/test/test_relaycell.c
@@ -17,6 +17,7 @@
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
#include "core/or/connection_edge.h"
+#include "core/or/sendme.h"
#include "core/or/relay.h"
#include "test/test.h"
#include "test/log_test_helpers.h"
@@ -812,7 +813,11 @@ test_circbw_relay(void *arg)
ASSERT_UNCOUNTED_BW();
/* Sendme on circuit with non-full window: counted */
- PACK_CELL(0, RELAY_COMMAND_SENDME, "Data1234");
+ PACK_CELL(0, RELAY_COMMAND_SENDME, "");
+ /* Recording a cell, the window is updated after decryption so off by one in
+ * order to record and then we process it with the proper window. */
+ circ->cpath->package_window = 901;
+ sendme_record_cell_digest_on_circ(TO_CIRCUIT(circ), circ->cpath);
circ->cpath->package_window = 900;
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
diff --git a/src/test/test_relaycrypt.c b/src/test/test_relaycrypt.c
index fe6889e521..4bbf07c3ec 100644
--- a/src/test/test_relaycrypt.c
+++ b/src/test/test_relaycrypt.c
@@ -3,6 +3,8 @@
* Copyright (c) 2007-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#define CRYPT_PATH_PRIVATE
+
#include "core/or/or.h"
#include "core/or/circuitbuild.h"
#define CIRCUITLIST_PRIVATE
@@ -10,7 +12,7 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "core/or/relay.h"
#include "core/crypto/relay_crypto.h"
-
+#include "core/or/crypt_path.h"
#include "core/or/cell_st.h"
#include "core/or/or_circuit_st.h"
#include "core/or/origin_circuit_st.h"
@@ -49,10 +51,10 @@ testing_circuitset_setup(const struct testcase_t *testcase)
cs->origin_circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL;
for (i=0; i<3; ++i) {
crypt_path_t *hop = tor_malloc_zero(sizeof(*hop));
- relay_crypto_init(&hop->crypto, KEY_MATERIAL[i], sizeof(KEY_MATERIAL[i]),
- 0, 0);
+ relay_crypto_init(&hop->pvt_crypto, KEY_MATERIAL[i],
+ sizeof(KEY_MATERIAL[i]), 0, 0);
hop->state = CPATH_STATE_OPEN;
- onion_append_to_cpath(&cs->origin_circ->cpath, hop);
+ cpath_extend_linked_list(&cs->origin_circ->cpath, hop);
tt_ptr_op(hop, OP_EQ, cs->origin_circ->cpath->prev);
}
diff --git a/src/test/test_rng.c b/src/test/test_rng.c
index c749de112a..dcf08fff1d 100644
--- a/src/test/test_rng.c
+++ b/src/test/test_rng.c
@@ -46,7 +46,7 @@ main(int argc, char **argv)
return 1;
}
}
-#endif
+#endif /* 0 */
crypto_fast_rng_t *rng = crypto_fast_rng_new();
while (1) {
diff --git a/src/test/test_router.c b/src/test/test_router.c
index ea0ee3e84c..5477ab51e9 100644
--- a/src/test/test_router.c
+++ b/src/test/test_router.c
@@ -100,6 +100,9 @@ test_router_dump_router_to_string_no_bridge_distribution_method(void *arg)
router = (routerinfo_t*)router_get_my_routerinfo();
tt_ptr_op(router, !=, NULL);
+ /* The real router_get_my_routerinfo() looks up onion_curve25519_pkey using
+ * get_current_curve25519_keypair(), but we don't initialise static data in
+ * this test. */
router->onion_curve25519_pkey = &ntor_keypair.pubkey;
/* Generate our server descriptor and ensure that the substring
diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c
index 727fa5660f..0c6b533698 100644
--- a/src/test/test_routerkeys.c
+++ b/src/test/test_routerkeys.c
@@ -399,7 +399,7 @@ test_routerkeys_ed_key_init_split(void *arg)
tt_assert(kp2 != NULL);
tt_assert(cert == NULL);
tt_mem_op(&kp1->pubkey, OP_EQ, &kp2->pubkey, sizeof(kp2->pubkey));
- tt_assert(tor_mem_is_zero((char*)kp2->seckey.seckey,
+ tt_assert(fast_mem_is_zero((char*)kp2->seckey.seckey,
sizeof(kp2->seckey.seckey)));
ed25519_keypair_free(kp2); kp2 = NULL;
@@ -409,7 +409,7 @@ test_routerkeys_ed_key_init_split(void *arg)
tt_assert(kp2 != NULL);
tt_assert(cert == NULL);
tt_mem_op(&kp1->pubkey, OP_EQ, &kp2->pubkey, sizeof(kp2->pubkey));
- tt_assert(tor_mem_is_zero((char*)kp2->seckey.seckey,
+ tt_assert(fast_mem_is_zero((char*)kp2->seckey.seckey,
sizeof(kp2->seckey.seckey)));
ed25519_keypair_free(kp2); kp2 = NULL;
@@ -455,11 +455,11 @@ test_routerkeys_ed_keys_init_all(void *arg)
options->TestingLinkKeySlop = 2*3600;
#ifdef _WIN32
- mkdir(dir);
- mkdir(keydir);
+ tt_int_op(0, OP_EQ, mkdir(dir));
+ tt_int_op(0, OP_EQ, mkdir(keydir));
#else
- mkdir(dir, 0700);
- mkdir(keydir, 0700);
+ tt_int_op(0, OP_EQ, mkdir(dir, 0700));
+ tt_int_op(0, OP_EQ, mkdir(keydir, 0700));
#endif /* defined(_WIN32) */
options->DataDirectory = dir;
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 84ec8cc462..6d596e87ea 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -631,7 +631,7 @@ mock_clock_skew_warning(const connection_t *conn, long apparent_skew,
(void)conn;
mock_apparent_skew = apparent_skew;
tt_int_op(trusted, OP_EQ, 1);
- tt_int_op(domain, OP_EQ, LD_GENERAL);
+ tt_i64_op(domain, OP_EQ, LD_GENERAL);
tt_str_op(received, OP_EQ, "microdesc flavor consensus");
tt_str_op(source, OP_EQ, "CONSENSUS");
done:
diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c
index c45f0e1595..cc73e6c20a 100644
--- a/src/test/test_routerset.c
+++ b/src/test/test_routerset.c
@@ -1765,7 +1765,7 @@ NS(node_get_by_nickname)(const char *nickname, unsigned flags)
* Structural test for routerset_get_all_nodes, when the nodelist has no nodes.
*/
-NS_DECL(smartlist_t *, nodelist_get_list, (void));
+NS_DECL(const smartlist_t *, nodelist_get_list, (void));
static smartlist_t *NS(mock_smartlist);
@@ -1795,7 +1795,7 @@ NS(test_main)(void *arg)
;
}
-smartlist_t *
+const smartlist_t *
NS(nodelist_get_list)(void)
{
CALLED(nodelist_get_list)++;
@@ -1811,7 +1811,7 @@ NS(nodelist_get_list)(void)
* the running_only flag is set, but the nodes are not running.
*/
-NS_DECL(smartlist_t *, nodelist_get_list, (void));
+NS_DECL(const smartlist_t *, nodelist_get_list, (void));
static smartlist_t *NS(mock_smartlist);
static node_t NS(mock_node);
@@ -1844,7 +1844,7 @@ NS(test_main)(void *arg)
;
}
-smartlist_t *
+const smartlist_t *
NS(nodelist_get_list)(void)
{
CALLED(nodelist_get_list)++;
diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh
index 00b3e88d37..804d2ada36 100755
--- a/src/test/test_rust.sh
+++ b/src/test/test_rust.sh
@@ -14,11 +14,12 @@ rustc_host=$(rustc -vV | grep host | sed 's/host: //')
for cargo_toml_dir in "${abs_top_srcdir:-../../..}"/src/rust/*; do
if [ -e "${cargo_toml_dir}/Cargo.toml" ]; then
+ # shellcheck disable=SC2086
cd "${abs_top_builddir:-../../..}/src/rust" && \
CARGO_TARGET_DIR="${abs_top_builddir:-../../..}/src/rust/target" \
- "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} \
+ "${CARGO:-cargo}" test "${CARGO_ONLINE-'--frozen'}" \
--features "test_linking_hack" \
- --target $rustc_host \
+ --target "$rustc_host" \
${EXTRA_CARGO_OPTIONS} \
--manifest-path "${cargo_toml_dir}/Cargo.toml" || exitcode=1
fi
diff --git a/src/test/test_sendme.c b/src/test/test_sendme.c
new file mode 100644
index 0000000000..eb402232bc
--- /dev/null
+++ b/src/test/test_sendme.c
@@ -0,0 +1,365 @@
+/* Copyright (c) 2014-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/* Unit tests for handling different kinds of relay cell */
+
+#define CIRCUITLIST_PRIVATE
+#define NETWORKSTATUS_PRIVATE
+#define SENDME_PRIVATE
+#define RELAY_PRIVATE
+
+#include "core/or/circuit_st.h"
+#include "core/or/or_circuit_st.h"
+#include "core/or/origin_circuit_st.h"
+#include "core/or/circuitlist.h"
+#include "core/or/relay.h"
+#include "core/or/sendme.h"
+
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/networkstatus_st.h"
+
+#include "lib/crypt_ops/crypto_digest.h"
+
+#include "test/test.h"
+#include "test/log_test_helpers.h"
+
+static void
+setup_mock_consensus(void)
+{
+ current_md_consensus = current_ns_consensus =
+ tor_malloc_zero(sizeof(networkstatus_t));
+ current_md_consensus->net_params = smartlist_new();
+ current_md_consensus->routerstatus_list = smartlist_new();
+}
+
+static void
+free_mock_consensus(void)
+{
+ SMARTLIST_FOREACH(current_md_consensus->routerstatus_list, void *, r,
+ tor_free(r));
+ smartlist_free(current_md_consensus->routerstatus_list);
+ smartlist_free(current_ns_consensus->net_params);
+ tor_free(current_ns_consensus);
+}
+
+static void
+test_v1_record_digest(void *arg)
+{
+ or_circuit_t *or_circ = NULL;
+ circuit_t *circ = NULL;
+
+ (void) arg;
+
+ /* Create our dummy circuit. */
+ or_circ = or_circuit_new(1, NULL);
+ /* Points it to the OR circuit now. */
+ circ = TO_CIRCUIT(or_circ);
+
+ /* The package window has to be a multiple of CIRCWINDOW_INCREMENT minus 1
+ * in order to catched the CIRCWINDOW_INCREMENT-nth cell. Try something that
+ * shouldn't be noted. */
+ circ->package_window = CIRCWINDOW_INCREMENT;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_assert(!circ->sendme_last_digests);
+
+ /* This should work now. Package window at CIRCWINDOW_INCREMENT + 1. */
+ circ->package_window++;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_assert(circ->sendme_last_digests);
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
+
+ /* Next cell in the package window shouldn't do anything. */
+ circ->package_window++;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
+
+ /* The next CIRCWINDOW_INCREMENT should add one more digest. */
+ circ->package_window = (CIRCWINDOW_INCREMENT * 2) + 1;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 2);
+
+ done:
+ circuit_free_(circ);
+}
+
+static void
+test_v1_consensus_params(void *arg)
+{
+ (void) arg;
+
+ setup_mock_consensus();
+ tt_assert(current_md_consensus);
+
+ /* Both zeroes. */
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_emit_min_version=0");
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_accept_min_version=0");
+ tt_int_op(get_emit_min_version(), OP_EQ, 0);
+ tt_int_op(get_accept_min_version(), OP_EQ, 0);
+ smartlist_clear(current_md_consensus->net_params);
+
+ /* Both ones. */
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_emit_min_version=1");
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_accept_min_version=1");
+ tt_int_op(get_emit_min_version(), OP_EQ, 1);
+ tt_int_op(get_accept_min_version(), OP_EQ, 1);
+ smartlist_clear(current_md_consensus->net_params);
+
+ /* Different values from each other. */
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_emit_min_version=1");
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_accept_min_version=0");
+ tt_int_op(get_emit_min_version(), OP_EQ, 1);
+ tt_int_op(get_accept_min_version(), OP_EQ, 0);
+ smartlist_clear(current_md_consensus->net_params);
+
+ /* Validate is the cell version is coherent with our internal default value
+ * and the one in the consensus. */
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_accept_min_version=1");
+ /* Minimum acceptable value is 1. */
+ tt_int_op(cell_version_can_be_handled(1), OP_EQ, true);
+ /* Minimum acceptable value is 1 so a cell version of 0 is refused. */
+ tt_int_op(cell_version_can_be_handled(0), OP_EQ, false);
+
+ done:
+ free_mock_consensus();
+}
+
+static void
+test_v1_build_cell(void *arg)
+{
+ uint8_t payload[RELAY_PAYLOAD_SIZE], digest[DIGEST_LEN];
+ ssize_t ret;
+ crypto_digest_t *cell_digest = NULL;
+ or_circuit_t *or_circ = NULL;
+ circuit_t *circ = NULL;
+
+ (void) arg;
+
+ or_circ = or_circuit_new(1, NULL);
+ circ = TO_CIRCUIT(or_circ);
+ circ->sendme_last_digests = smartlist_new();
+
+ cell_digest = crypto_digest_new();
+ tt_assert(cell_digest);
+ crypto_digest_add_bytes(cell_digest, "AAAAAAAAAAAAAAAAAAAA", 20);
+ crypto_digest_get_digest(cell_digest, (char *) digest, sizeof(digest));
+ smartlist_add(circ->sendme_last_digests, tor_memdup(digest, sizeof(digest)));
+
+ /* SENDME v1 payload is 3 bytes + 20 bytes digest. See spec. */
+ ret = build_cell_payload_v1(digest, payload);
+ tt_int_op(ret, OP_EQ, 23);
+
+ /* Validation. */
+
+ /* An empty payload means SENDME version 0 thus valid. */
+ tt_int_op(sendme_is_valid(circ, payload, 0), OP_EQ, true);
+ /* Current phoney digest should have been popped. */
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
+
+ /* An unparseable cell means invalid. */
+ setup_full_capture_of_logs(LOG_INFO);
+ tt_int_op(sendme_is_valid(circ, (const uint8_t *) "A", 1), OP_EQ, false);
+ expect_log_msg_containing("Unparseable SENDME cell received. "
+ "Closing circuit.");
+ teardown_capture_of_logs();
+
+ /* No cell digest recorded for this. */
+ setup_full_capture_of_logs(LOG_INFO);
+ tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false);
+ expect_log_msg_containing("We received a SENDME but we have no cell digests "
+ "to match. Closing circuit.");
+ teardown_capture_of_logs();
+
+ /* Note the wrong digest in the circuit, cell should fail validation. */
+ circ->package_window = CIRCWINDOW_INCREMENT + 1;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
+ setup_full_capture_of_logs(LOG_INFO);
+ tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false);
+ /* After a validation, the last digests is always popped out. */
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
+ expect_log_msg_containing("SENDME v1 cell digest do not match.");
+ teardown_capture_of_logs();
+
+ /* Record the cell digest into the circuit, cell should validate. */
+ memcpy(or_circ->crypto.sendme_digest, digest, sizeof(digest));
+ circ->package_window = CIRCWINDOW_INCREMENT + 1;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
+ tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, true);
+ /* After a validation, the last digests is always popped out. */
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
+
+ done:
+ crypto_digest_free(cell_digest);
+ circuit_free_(circ);
+}
+
+static void
+test_cell_payload_pad(void *arg)
+{
+ size_t pad_offset, payload_len, expected_offset;
+
+ (void) arg;
+
+ /* Offset should be 0, not enough room for padding. */
+ payload_len = RELAY_PAYLOAD_SIZE;
+ pad_offset = get_pad_cell_offset(payload_len);
+ tt_int_op(pad_offset, OP_EQ, 0);
+ tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
+
+ /* Still no room because we keep 4 extra bytes. */
+ pad_offset = get_pad_cell_offset(payload_len - 4);
+ tt_int_op(pad_offset, OP_EQ, 0);
+ tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
+
+ /* We should have 1 byte of padding. Meaning, the offset should be the
+ * CELL_PAYLOAD_SIZE minus 1 byte. */
+ expected_offset = CELL_PAYLOAD_SIZE - 1;
+ pad_offset = get_pad_cell_offset(payload_len - 5);
+ tt_int_op(pad_offset, OP_EQ, expected_offset);
+ tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
+
+ /* Now some arbitrary small payload length. The cell size is header + 10 +
+ * extra 4 bytes we keep so the offset should be there. */
+ expected_offset = RELAY_HEADER_SIZE + 10 + 4;
+ pad_offset = get_pad_cell_offset(10);
+ tt_int_op(pad_offset, OP_EQ, expected_offset);
+ tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
+
+ /* Data length of 0. */
+ expected_offset = RELAY_HEADER_SIZE + 4;
+ pad_offset = get_pad_cell_offset(0);
+ tt_int_op(pad_offset, OP_EQ, expected_offset);
+ tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
+
+ done:
+ ;
+}
+
+static void
+test_cell_version_validation(void *arg)
+{
+ (void) arg;
+
+ /* We currently only support up to SENDME_MAX_SUPPORTED_VERSION so we are
+ * going to test the boundaries there. */
+
+ tt_assert(cell_version_can_be_handled(SENDME_MAX_SUPPORTED_VERSION));
+
+ /* Version below our supported should pass. */
+ tt_assert(cell_version_can_be_handled(SENDME_MAX_SUPPORTED_VERSION - 1));
+
+ /* Extra version from our supported should fail. */
+ tt_assert(!cell_version_can_be_handled(SENDME_MAX_SUPPORTED_VERSION + 1));
+
+ /* Simple check for version 0. */
+ tt_assert(cell_version_can_be_handled(0));
+
+ /* We MUST handle the default cell version that we emit or accept. */
+ tt_assert(cell_version_can_be_handled(SENDME_EMIT_MIN_VERSION_DEFAULT));
+ tt_assert(cell_version_can_be_handled(SENDME_ACCEPT_MIN_VERSION_DEFAULT));
+
+ done:
+ ;
+}
+
+/* check our decisions about how much stuff to put into relay cells. */
+static void
+test_package_payload_len(void *arg)
+{
+ (void)arg;
+ /* this is not a real circuit: it only has the fields needed for this
+ * test. */
+ circuit_t *c = tor_malloc_zero(sizeof(circuit_t));
+
+ /* check initial conditions. */
+ circuit_reset_sendme_randomness(c);
+ tt_assert(! c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_GE, CIRCWINDOW_INCREMENT / 2);
+ tt_int_op(c->send_randomness_after_n_cells, OP_LT, CIRCWINDOW_INCREMENT);
+
+ /* We have a bunch of cells before we need to send randomness, so the first
+ * few can be packaged full. */
+ int initial = c->send_randomness_after_n_cells;
+ size_t n = connection_edge_get_inbuf_bytes_to_package(10000, 0, c);
+ tt_uint_op(RELAY_PAYLOAD_SIZE, OP_EQ, n);
+ n = connection_edge_get_inbuf_bytes_to_package(95000, 1, c);
+ tt_uint_op(RELAY_PAYLOAD_SIZE, OP_EQ, n);
+ tt_int_op(c->send_randomness_after_n_cells, OP_EQ, initial - 2);
+
+ /* If package_partial isn't set, we won't package a partially full cell at
+ * all. */
+ n = connection_edge_get_inbuf_bytes_to_package(RELAY_PAYLOAD_SIZE-1, 0, c);
+ tt_int_op(n, OP_EQ, 0);
+ /* no change in our state, since nothing was sent. */
+ tt_assert(! c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_EQ, initial - 2);
+
+ /* If package_partial is set and the partial cell is not going to have
+ * _enough_ randomness, we package it, but we don't consider ourselves to
+ * have sent a sufficiently random cell. */
+ n = connection_edge_get_inbuf_bytes_to_package(RELAY_PAYLOAD_SIZE-1, 1, c);
+ tt_int_op(n, OP_EQ, RELAY_PAYLOAD_SIZE-1);
+ tt_assert(! c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_EQ, initial - 3);
+
+ /* Make sure we set have_set_sufficiently_random_cell as appropriate. */
+ n = connection_edge_get_inbuf_bytes_to_package(RELAY_PAYLOAD_SIZE-64, 1, c);
+ tt_int_op(n, OP_EQ, RELAY_PAYLOAD_SIZE-64);
+ tt_assert(c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_EQ, initial - 4);
+
+ /* Now let's look at what happens when we get down to zero. Since we have
+ * sent a sufficiently random cell, we will not force this one to have a gap.
+ */
+ c->send_randomness_after_n_cells = 0;
+ n = connection_edge_get_inbuf_bytes_to_package(10000, 1, c);
+ tt_int_op(n, OP_EQ, RELAY_PAYLOAD_SIZE);
+ /* Now these will be reset. */
+ tt_assert(! c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_GE,
+ CIRCWINDOW_INCREMENT / 2 - 1);
+
+ /* What would happen if we hadn't sent a sufficiently random cell? */
+ c->send_randomness_after_n_cells = 0;
+ n = connection_edge_get_inbuf_bytes_to_package(10000, 1, c);
+ const size_t reduced_payload_size = RELAY_PAYLOAD_SIZE - 4 - 16;
+ tt_int_op(n, OP_EQ, reduced_payload_size);
+ /* Now these will be reset. */
+ tt_assert(! c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_GE,
+ CIRCWINDOW_INCREMENT / 2 - 1);
+
+ /* Here is a fun case: if it's time to package a small cell, then
+ * package_partial==0 should mean we accept that many bytes.
+ */
+ c->send_randomness_after_n_cells = 0;
+ n = connection_edge_get_inbuf_bytes_to_package(reduced_payload_size, 0, c);
+ tt_int_op(n, OP_EQ, reduced_payload_size);
+
+ done:
+ tor_free(c);
+}
+
+struct testcase_t sendme_tests[] = {
+ { "v1_record_digest", test_v1_record_digest, TT_FORK,
+ NULL, NULL },
+ { "v1_consensus_params", test_v1_consensus_params, TT_FORK,
+ NULL, NULL },
+ { "v1_build_cell", test_v1_build_cell, TT_FORK,
+ NULL, NULL },
+ { "cell_payload_pad", test_cell_payload_pad, TT_FORK,
+ NULL, NULL },
+ { "cell_version_validation", test_cell_version_validation, TT_FORK,
+ NULL, NULL },
+ { "package_payload_len", test_package_payload_len, 0, NULL, NULL },
+
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index 5fa7e80d07..9c8703fa6f 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -449,12 +449,12 @@ test_sr_commit(void *arg)
/* 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,
+ tt_assert(!fast_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, OP_EQ, our_commit->reveal_ts);
/* We should have a hashed reveal. */
- tt_assert(!tor_mem_is_zero(our_commit->hashed_reveal,
+ tt_assert(!fast_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
@@ -1081,70 +1081,85 @@ test_sr_get_majority_srv_from_votes(void *arg)
smartlist_free(votes);
}
-/* Test utils that don't depend on authority state */
+/* Testing sr_srv_dup(). */
static void
-test_utils_general(void *arg)
+test_sr_svr_dup(void *arg)
{
- (void) arg;
+ (void)arg;
- /* Testing sr_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 = sr_srv_dup(srv);
- tt_assert(dup_srv);
- tt_u64_op(dup_srv->num_reveals, OP_EQ, srv->num_reveals);
- tt_mem_op(dup_srv->value, OP_EQ, srv->value, sizeof(srv->value));
- tor_free(srv);
- tor_free(dup_srv);
- }
+ 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 = sr_srv_dup(srv);
+ tt_assert(dup_srv);
+ tt_u64_op(dup_srv->num_reveals, OP_EQ, srv->num_reveals);
+ tt_mem_op(dup_srv->value, OP_EQ, srv->value, sizeof(srv->value));
- /* 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), OP_EQ, 1);
- /* Let's corrupt one of them. */
- memset(commit1.encoded_commit, 'A', sizeof(commit1.encoded_commit));
- tt_int_op(commitments_are_the_same(&commit1, &commit2), OP_EQ, 0);
- }
+ done:
+ tor_free(srv);
+ tor_free(dup_srv);
+}
- /* Testing commit_is_authoritative(). */
- {
- crypto_pk_t *k = crypto_pk_new();
- char digest[DIGEST_LEN];
- sr_commit_t commit;
+/* 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. */
+static void
+test_commitments_are_the_same(void *arg)
+{
+ (void)arg;
- tt_assert(!crypto_pk_generate_key(k));
+ /* 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), OP_EQ, 1);
+ /* Let's corrupt one of them. */
+ memset(commit1.encoded_commit, 'A', sizeof(commit1.encoded_commit));
+ tt_int_op(commitments_are_the_same(&commit1, &commit2), OP_EQ, 0);
- tt_int_op(0, OP_EQ, crypto_pk_get_digest(k, digest));
- memcpy(commit.rsa_identity, digest, sizeof(commit.rsa_identity));
- tt_int_op(commit_is_authoritative(&commit, digest), OP_EQ, 1);
- /* Change the pubkey. */
- memset(commit.rsa_identity, 0, sizeof(commit.rsa_identity));
- tt_int_op(commit_is_authoritative(&commit, digest), OP_EQ, 0);
- crypto_pk_free(k);
- }
+ done:
+ return;
+}
- /* Testing get_phase_str(). */
- {
- tt_str_op(get_phase_str(SR_PHASE_REVEAL), OP_EQ, "reveal");
- tt_str_op(get_phase_str(SR_PHASE_COMMIT), OP_EQ, "commit");
- }
+/* Testing commit_is_authoritative(). */
+static void
+test_commit_is_authoritative(void *arg)
+{
+ (void)arg;
+
+ 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, OP_EQ, crypto_pk_get_digest(k, digest));
+ memcpy(commit.rsa_identity, digest, sizeof(commit.rsa_identity));
+ tt_int_op(commit_is_authoritative(&commit, digest), OP_EQ, 1);
+ /* Change the pubkey. */
+ memset(commit.rsa_identity, 0, sizeof(commit.rsa_identity));
+ tt_int_op(commit_is_authoritative(&commit, digest), OP_EQ, 0);
+
+ done:
+ crypto_pk_free(k);
+}
+
+static void
+test_get_phase_str(void *arg)
+{
+ (void)arg;
+
+ tt_str_op(get_phase_str(SR_PHASE_REVEAL), OP_EQ, "reveal");
+ tt_str_op(get_phase_str(SR_PHASE_COMMIT), OP_EQ, "commit");
done:
return;
@@ -1352,7 +1367,7 @@ test_utils_auth(void *arg)
sr_state_set_current_srv(sr_state_get_current_srv());
sr_state_set_previous_srv(sr_state_get_previous_srv());
}
-#endif
+#endif /* 0 */
done:
sr_state_free_all();
@@ -1649,7 +1664,12 @@ struct testcase_t sr_tests[] = {
{ "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_general", test_utils_general, TT_FORK, NULL, NULL },
+ { "sr_svr_dup", test_sr_svr_dup, TT_FORK, NULL, NULL },
+ { "commitments_are_the_same", test_commitments_are_the_same, TT_FORK, NULL,
+ NULL },
+ { "commit_is_authoritative", test_commit_is_authoritative, TT_FORK, NULL,
+ NULL },
+ { "get_phase_str", test_get_phase_str, TT_FORK, NULL, NULL },
{ "utils_auth", test_utils_auth, TT_FORK, NULL, NULL },
{ "state_transition", test_state_transition, TT_FORK, NULL, NULL },
{ "state_update", test_state_update, TT_FORK,
diff --git a/src/test/test_slow.c b/src/test/test_slow.c
index c3e7edd408..d4d5b755a5 100644
--- a/src/test/test_slow.c
+++ b/src/test/test_slow.c
@@ -22,6 +22,7 @@ struct testgroup_t testgroups[] = {
{ "slow/crypto/", slow_crypto_tests },
{ "slow/process/", slow_process_tests },
{ "slow/prob_distr/", slow_stochastic_prob_distr_tests },
+ { "slow/ptr/", slow_ptr_tests },
END_OF_GROUPS
};
diff --git a/src/test/test_status.c b/src/test/test_status.c
index 9c47469975..2fb2a7b24f 100644
--- a/src/test/test_status.c
+++ b/src/test/test_status.c
@@ -404,7 +404,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
{
case 0:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -412,7 +412,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
break;
case 1:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -429,7 +429,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
break;
case 3:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "rep_hist_log_circuit_handshake_stats"),
OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
@@ -442,13 +442,13 @@ NS(logv)(int severity, log_domain_mask_t domain,
break;
case 4:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "rep_hist_log_link_protocol_counts"),
OP_NE, NULL);
break;
case 5:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_str_op(format, OP_EQ, "DoS mitigation since startup:%s%s%s%s");
tt_str_op(va_arg(ap, char *), OP_EQ,
" 0 circuits killed with too many cells.");
@@ -574,7 +574,7 @@ NS(logv)(int severity, log_domain_mask_t domain, const char *funcname,
++NS(n_msgs);
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -709,7 +709,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
{
case 0:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -723,7 +723,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
break;
case 1:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_accounting"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -889,7 +889,7 @@ NS(logv)(int severity, log_domain_mask_t domain, const char *funcname,
{
case 0:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -903,7 +903,7 @@ NS(logv)(int severity, log_domain_mask_t domain, const char *funcname,
break;
case 1:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -1038,7 +1038,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
{
case 0:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -1052,7 +1052,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
break;
case 1:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
diff --git a/src/test/test_switch_id.sh b/src/test/test_switch_id.sh
index 79c44f2eb1..b13bf7602f 100755
--- a/src/test/test_switch_id.sh
+++ b/src/test/test_switch_id.sh
@@ -1,11 +1,11 @@
#!/bin/sh
-if test "`id -u`" != '0'; then
+if test "$(id -u)" != '0'; then
echo "This test only works when run as root. Skipping." >&2
exit 77
fi
-if test "`id -u nobody`" = ""; then
+if test "$(id -u nobody)" = ""; then
echo "This test requires that your system have a 'nobody' user. Sorry." >&2
exit 1
fi
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
index 11e35be2fa..5f87434f6a 100644
--- a/src/test/test_tortls.c
+++ b/src/test/test_tortls.c
@@ -219,7 +219,7 @@ test_tortls_tor_tls_get_error(void *data)
crypto_pk_free(key2);
tor_tls_free(tls);
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
static void
test_tortls_x509_cert_get_id_digests(void *ignored)
@@ -336,7 +336,7 @@ test_tortls_server_got_renegotiate(void *ignored)
done:
tor_free(tls);
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
static void
test_tortls_evaluate_ecgroup_for_tls(void *ignored)
@@ -526,7 +526,7 @@ struct testcase_t tortls_tests[] = {
LOCAL_TEST_CASE(get_forced_write_size, 0),
LOCAL_TEST_CASE(used_v1_handshake, TT_FORK),
LOCAL_TEST_CASE(server_got_renegotiate, 0),
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
LOCAL_TEST_CASE(evaluate_ecgroup_for_tls, 0),
LOCAL_TEST_CASE(double_init, TT_FORK),
LOCAL_TEST_CASE(address, TT_FORK),
diff --git a/src/test/test_tortls.h b/src/test/test_tortls.h
index 1a8b117d0f..4567b9f6a0 100644
--- a/src/test/test_tortls.h
+++ b/src/test/test_tortls.h
@@ -10,4 +10,4 @@ extern const char *notCompletelyValidCertString;
extern const char *validCertString;
extern const char *caCertString;
-#endif
+#endif /* !defined(TEST_TORTLS_H) */
diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c
index 73041a871c..81e2d3aa4f 100644
--- a/src/test/test_tortls_openssl.c
+++ b/src/test/test_tortls_openssl.c
@@ -133,7 +133,7 @@ library_init(void)
#else
SSL_library_init();
SSL_load_error_strings();
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
}
static void
@@ -477,7 +477,7 @@ fake_x509_free(X509 *cert)
tor_free(cert);
}
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static tor_x509_cert_t *fixed_x509_cert = NULL;
static tor_x509_cert_t *
diff --git a/src/test/test_util.c b/src/test/test_util.c
index f1ffae7af8..2faadd4e19 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -6,7 +6,6 @@
#include "orconfig.h"
#define COMPAT_PRIVATE
#define COMPAT_TIME_PRIVATE
-#define CONTROL_PRIVATE
#define UTIL_PRIVATE
#define UTIL_MALLOC_PRIVATE
#define SOCKET_PRIVATE
@@ -16,6 +15,7 @@
#include "lib/buf/buffers.h"
#include "app/config/config.h"
#include "feature/control/control.h"
+#include "feature/control/control_proto.h"
#include "feature/client/transports.h"
#include "lib/crypt_ops/crypto_format.h"
#include "lib/crypt_ops/crypto_rand.h"
@@ -2087,14 +2087,14 @@ test_util_strmisc(void *arg)
/* Test mem_is_zero */
memset(buf,0,128);
buf[128] = 'x';
- tt_assert(tor_mem_is_zero(buf, 10));
- tt_assert(tor_mem_is_zero(buf, 20));
- tt_assert(tor_mem_is_zero(buf, 128));
- tt_assert(!tor_mem_is_zero(buf, 129));
+ tt_assert(fast_mem_is_zero(buf, 10));
+ tt_assert(fast_mem_is_zero(buf, 20));
+ tt_assert(fast_mem_is_zero(buf, 128));
+ tt_assert(!fast_mem_is_zero(buf, 129));
buf[60] = (char)255;
- tt_assert(!tor_mem_is_zero(buf, 128));
+ tt_assert(!fast_mem_is_zero(buf, 128));
buf[0] = (char)1;
- tt_assert(!tor_mem_is_zero(buf, 10));
+ tt_assert(!fast_mem_is_zero(buf, 10));
/* Test 'escaped' */
tt_ptr_op(escaped(NULL), OP_EQ, NULL);
@@ -3789,7 +3789,7 @@ test_util_memarea(void *arg)
tt_int_op(((uintptr_t)p3) % sizeof(void*),OP_EQ, 0);
tt_assert(!memarea_owns_ptr(area, p3+8192));
tt_assert(!memarea_owns_ptr(area, p3+30));
- tt_assert(tor_mem_is_zero(p2, 52));
+ tt_assert(fast_mem_is_zero(p2, 52));
/* Make sure we don't overalign. */
p1 = memarea_alloc(area, 1);
p2 = memarea_alloc(area, 1);
@@ -6116,9 +6116,9 @@ test_util_log_mallinfo(void *arg)
} else {
tt_u64_op(mem1, OP_LT, mem2);
}
-#else
+#else /* !(defined(HAVE_MALLINFO)) */
tt_skip();
-#endif
+#endif /* defined(HAVE_MALLINFO) */
done:
teardown_capture_of_logs();
tor_free(log1);
@@ -6132,10 +6132,12 @@ test_util_map_anon(void *arg)
(void)arg;
char *ptr = NULL;
size_t sz = 16384;
+ unsigned inherit=0;
/* Basic checks. */
- ptr = tor_mmap_anonymous(sz, 0);
+ ptr = tor_mmap_anonymous(sz, 0, &inherit);
tt_ptr_op(ptr, OP_NE, 0);
+ tt_int_op(inherit, OP_EQ, INHERIT_RES_KEEP);
ptr[sz-1] = 3;
tt_int_op(ptr[0], OP_EQ, 0);
tt_int_op(ptr[sz-2], OP_EQ, 0);
@@ -6143,8 +6145,9 @@ test_util_map_anon(void *arg)
/* Try again, with a private (non-swappable) mapping. */
tor_munmap_anonymous(ptr, sz);
- ptr = tor_mmap_anonymous(sz, ANONMAP_PRIVATE);
+ ptr = tor_mmap_anonymous(sz, ANONMAP_PRIVATE, &inherit);
tt_ptr_op(ptr, OP_NE, 0);
+ tt_int_op(inherit, OP_EQ, INHERIT_RES_KEEP);
ptr[sz-1] = 10;
tt_int_op(ptr[0], OP_EQ, 0);
tt_int_op(ptr[sz/2], OP_EQ, 0);
@@ -6152,7 +6155,7 @@ test_util_map_anon(void *arg)
/* Now let's test a drop-on-fork mapping. */
tor_munmap_anonymous(ptr, sz);
- ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT);
+ ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT, &inherit);
tt_ptr_op(ptr, OP_NE, 0);
ptr[sz-1] = 10;
tt_int_op(ptr[0], OP_EQ, 0);
@@ -6167,12 +6170,12 @@ static void
test_util_map_anon_nofork(void *arg)
{
(void)arg;
-#if !defined(HAVE_MADVISE) && !defined(HAVE_MINHERIT)
- /* The operating system doesn't support this. */
+#ifdef _WIN32
+ /* The operating system doesn't support forking. */
tt_skip();
done:
;
-#else
+#else /* !(defined(_WIN32)) */
/* We have the right OS support. We're going to try marking the buffer as
* either zero-on-fork or as drop-on-fork, whichever is supported. Then we
* will fork and send a byte back to the parent process. This will either
@@ -6181,9 +6184,10 @@ test_util_map_anon_nofork(void *arg)
char *ptr = NULL;
size_t sz = 16384;
int pipefd[2] = {-1, -1};
+ unsigned inherit=0;
tor_munmap_anonymous(ptr, sz);
- ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT);
+ ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT, &inherit);
tt_ptr_op(ptr, OP_NE, 0);
memset(ptr, 0xd0, sz);
@@ -6204,15 +6208,36 @@ test_util_map_anon_nofork(void *arg)
pipefd[1] = -1;
char buf[1];
ssize_t r = read(pipefd[0], buf, 1);
-#if defined(INHERIT_ZERO) || defined(MADV_WIPEONFORK)
- tt_int_op((int)r, OP_EQ, 1); // child should send us a byte.
- tt_int_op(buf[0], OP_EQ, 0);
-#else
- tt_int_op(r, OP_LE, 0); // child said nothing; it should have crashed.
-#endif
+
+ if (inherit == INHERIT_RES_ZERO) {
+ // We should be seeing clear-on-fork behavior.
+ tt_int_op((int)r, OP_EQ, 1); // child should send us a byte.
+ tt_int_op(buf[0], OP_EQ, 0); // that byte should be zero.
+ } else if (inherit == INHERIT_RES_DROP) {
+ // We should be seeing noinherit behavior.
+ tt_int_op(r, OP_LE, 0); // child said nothing; it should have crashed.
+ } else {
+ // noinherit isn't implemented.
+ tt_int_op(inherit, OP_EQ, INHERIT_RES_KEEP);
+ tt_int_op((int)r, OP_EQ, 1); // child should send us a byte.
+ tt_int_op(buf[0], OP_EQ, 0xd0); // that byte should what we set it to.
+ }
+
int ws;
waitpid(child, &ws, 0);
+#ifndef NOINHERIT_CAN_FAIL
+ /* Only if NOINHERIT_CAN_FAIL should it be possible for us to get
+ * INHERIT_KEEP behavior in this case. */
+ tt_int_op(inherit, OP_NE, INHERIT_RES_KEEP);
+#else
+ if (inherit == INHERIT_RES_KEEP) {
+ /* Call this test "skipped", not "passed", since noinherit wasn't
+ * implemented. */
+ tt_skip();
+ }
+#endif /* !defined(NOINHERIT_CAN_FAIL) */
+
done:
tor_munmap_anonymous(ptr, sz);
if (pipefd[0] >= 0) {
@@ -6221,7 +6246,7 @@ test_util_map_anon_nofork(void *arg)
if (pipefd[1] >= 0) {
close(pipefd[1]);
}
-#endif
+#endif /* defined(_WIN32) */
}
#define UTIL_LEGACY(name) \
@@ -6362,6 +6387,6 @@ struct testcase_t util_tests[] = {
UTIL_TEST(get_unquoted_path, 0),
UTIL_TEST(log_mallinfo, 0),
UTIL_TEST(map_anon, 0),
- UTIL_TEST(map_anon_nofork, TT_SKIP /* See bug #29535 */),
+ UTIL_TEST(map_anon_nofork, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c
index 3a0b41faa5..2859da66b2 100644
--- a/src/test/test_util_format.c
+++ b/src/test/test_util_format.c
@@ -346,7 +346,7 @@ test_util_format_base32_decode(void *arg)
const char *src = "mjwgc2dcnrswqmjs";
ret = base32_decode(dst, strlen(expected), src, strlen(src));
- tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(ret, OP_EQ, 10);
tt_str_op(expected, OP_EQ, dst);
}
@@ -357,7 +357,7 @@ test_util_format_base32_decode(void *arg)
const char *src = "mjwgc2dcnrswq";
ret = base32_decode(dst, strlen(expected), src, strlen(src));
- tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(ret, OP_EQ, 8);
tt_mem_op(expected, OP_EQ, dst, strlen(expected));
}
@@ -367,7 +367,7 @@ test_util_format_base32_decode(void *arg)
ret = base32_decode(dst, real_dstlen, "#abcde", 6);
tt_int_op(ret, OP_EQ, -1);
/* Make sure the destination buffer has been zeroed even on error. */
- tt_int_op(tor_mem_is_zero(dst, real_dstlen), OP_EQ, 1);
+ tt_int_op(fast_mem_is_zero(dst, real_dstlen), OP_EQ, 1);
}
done:
diff --git a/src/test/test_voting_flags.c b/src/test/test_voting_flags.c
index 5c9eebd00e..c8111ea5df 100644
--- a/src/test/test_voting_flags.c
+++ b/src/test/test_voting_flags.c
@@ -60,7 +60,7 @@ check_result(flag_vote_test_cfg_t *c)
bool result = false;
routerstatus_t rs;
memset(&rs, 0, sizeof(rs));
- set_routerstatus_from_routerinfo(&rs, &c->node, &c->ri, c->now, 0);
+ dirauth_set_routerstatus_from_routerinfo(&rs, &c->node, &c->ri, c->now, 0);
tt_i64_op(rs.published_on, OP_EQ, c->expected.published_on);
tt_str_op(rs.nickname, OP_EQ, c->expected.nickname);
diff --git a/src/test/test_workqueue_cancel.sh b/src/test/test_workqueue_cancel.sh
index f7c663171e..e50b884f26 100755
--- a/src/test/test_workqueue_cancel.sh
+++ b/src/test/test_workqueue_cancel.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue -C 1
+"${builddir:-.}/src/test/test_workqueue" -C 1
diff --git a/src/test/test_workqueue_efd.sh b/src/test/test_workqueue_efd.sh
index 4d89396819..592841fc91 100755
--- a/src/test/test_workqueue_efd.sh
+++ b/src/test/test_workqueue_efd.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue \
+"${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
index 7cfff45ff3..4cf1b76cbe 100755
--- a/src/test/test_workqueue_efd2.sh
+++ b/src/test/test_workqueue_efd2.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue \
+"${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
index afcef87853..fc3ef34c6c 100755
--- a/src/test/test_workqueue_pipe.sh
+++ b/src/test/test_workqueue_pipe.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue \
+"${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
index a20a1427e0..7f19ea880d 100755
--- a/src/test/test_workqueue_pipe2.sh
+++ b/src/test/test_workqueue_pipe2.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue \
+"${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
index 76af79746d..1ee1776447 100755
--- a/src/test/test_workqueue_socketpair.sh
+++ b/src/test/test_workqueue_socketpair.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue \
+"${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 8fc8ef7830..ad22898ce5 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -12,6 +12,7 @@
#include "orconfig.h"
#include "core/or/or.h"
#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "app/config/config.h"
#include "lib/crypt_ops/crypto_dh.h"
#include "lib/crypt_ops/crypto_ed25519.h"
@@ -242,7 +243,7 @@ tinytest_postfork(void)
}
static void
-log_callback_failure(int severity, uint32_t domain, const char *msg)
+log_callback_failure(int severity, log_domain_mask_t domain, const char *msg)
{
(void)msg;
if (severity == LOG_ERR || (domain & LD_BUG)) {
diff --git a/src/test/testing_rsakeys.c b/src/test/testing_rsakeys.c
index 0f22d4e01b..8ba6bf9fe4 100644
--- a/src/test/testing_rsakeys.c
+++ b/src/test/testing_rsakeys.c
@@ -448,7 +448,8 @@ static int next_key_idx_2048;
static crypto_pk_t *
pk_generate_internal(int bits)
{
- tor_assert(bits == 2048 || bits == 1024);
+ tor_assertf(bits == 2048 || bits == 1024,
+ "Wrong key size: %d", bits);
#ifdef USE_PREGENERATED_RSA_KEYS
int *idxp;
diff --git a/src/test/zero_length_keys.sh b/src/test/zero_length_keys.sh
index 5635bdfd89..1702d11245 100755
--- a/src/test/zero_length_keys.sh
+++ b/src/test/zero_length_keys.sh
@@ -19,7 +19,7 @@
# 3: a command failed - the test could not be completed
#
-if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then
+if [ $# -eq 0 ] || [ ! -f "${1}" ] || [ ! -x "${1}" ]; then
echo "Usage: ${0} PATH_TO_TOR [-z|-d|-e]"
exit 1
elif [ $# -eq 1 ]; then
@@ -31,7 +31,7 @@ else #[$# -gt 1 ]; then
shift
fi
-DATA_DIR=`mktemp -d -t tor_zero_length_keys.XXXXXX`
+DATA_DIR=$(mktemp -d -t tor_zero_length_keys.XXXXXX)
if [ -z "$DATA_DIR" ]; then
echo "Failure: mktemp invocation returned empty string" >&2
exit 3
@@ -40,7 +40,7 @@ if [ ! -d "$DATA_DIR" ]; then
echo "Failure: mktemp invocation result doesn't point to directory" >&2
exit 3
fi
-trap "rm -rf '$DATA_DIR'" 0
+trap 'rm -rf "$DATA_DIR"' 0
touch "$DATA_DIR"/empty_torrc
touch "$DATA_DIR"/empty_defaults_torrc
diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c
index 25113420df..ea96f41dbf 100644
--- a/src/tools/tor-gencert.c
+++ b/src/tools/tor-gencert.c
@@ -31,7 +31,7 @@ DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/err.h>
ENABLE_GCC_WARNING(redundant-decls)
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
#include <errno.h>
diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c
index 98b3a4a74c..5d97696c18 100644
--- a/src/tools/tor-resolve.c
+++ b/src/tools/tor-resolve.c
@@ -424,6 +424,7 @@ do_resolve(const char *hostname,
if (parsed < 2) {
log_err(LD_NET, "Failed to parse SOCKS5 method selection "
"message");
+ socks5_server_method_free(m);
goto err;
}
diff --git a/src/trunnel/ed25519_cert.trunnel b/src/trunnel/ed25519_cert.trunnel
index 8d6483d558..e424ce5464 100644
--- a/src/trunnel/ed25519_cert.trunnel
+++ b/src/trunnel/ed25519_cert.trunnel
@@ -28,12 +28,6 @@ const LS_IPV6 = 0x01;
const LS_LEGACY_ID = 0x02;
const LS_ED25519_ID = 0x03;
-// XXX hs_link_specifier_dup() violates the opaqueness of link_specifier_t by
-// taking its sizeof(). If we ever want to turn on TRUNNEL_OPAQUE, or
-// if we ever make link_specifier contain other types, we will
-// need to refactor that function to do the copy by encoding and decoding the
-// object.
-
// amended from tor.trunnel
struct link_specifier {
u8 ls_type;
diff --git a/src/trunnel/include.am b/src/trunnel/include.am
index 4f4f1d3624..ce15570b15 100644
--- a/src/trunnel/include.am
+++ b/src/trunnel/include.am
@@ -11,6 +11,7 @@ TRUNNELINPUTS = \
src/trunnel/link_handshake.trunnel \
src/trunnel/pwbox.trunnel \
src/trunnel/channelpadding_negotiation.trunnel \
+ src/trunnel/sendme.trunnel \
src/trunnel/socks5.trunnel \
src/trunnel/circpad_negotiation.trunnel
@@ -24,6 +25,7 @@ TRUNNELSOURCES = \
src/trunnel/hs/cell_introduce1.c \
src/trunnel/hs/cell_rendezvous.c \
src/trunnel/channelpadding_negotiation.c \
+ src/trunnel/sendme.c \
src/trunnel/socks5.c \
src/trunnel/netinfo.c \
src/trunnel/circpad_negotiation.c
@@ -40,6 +42,7 @@ TRUNNELHEADERS = \
src/trunnel/hs/cell_introduce1.h \
src/trunnel/hs/cell_rendezvous.h \
src/trunnel/channelpadding_negotiation.h \
+ src/trunnel/sendme.h \
src/trunnel/socks5.h \
src/trunnel/netinfo.h \
src/trunnel/circpad_negotiation.h
diff --git a/src/trunnel/sendme.c b/src/trunnel/sendme.c
new file mode 100644
index 0000000000..262b915234
--- /dev/null
+++ b/src/trunnel/sendme.c
@@ -0,0 +1,347 @@
+/* sendme.c -- generated by Trunnel v1.5.2.
+ * https://gitweb.torproject.org/trunnel.git
+ * You probably shouldn't edit this file.
+ */
+#include <stdlib.h>
+#include "trunnel-impl.h"
+
+#include "sendme.h"
+
+#define TRUNNEL_SET_ERROR_CODE(obj) \
+ do { \
+ (obj)->trunnel_error_code_ = 1; \
+ } while (0)
+
+#if defined(__COVERITY__) || defined(__clang_analyzer__)
+/* If we're running a static analysis tool, we don't want it to complain
+ * that some of our remaining-bytes checks are dead-code. */
+int sendme_deadcode_dummy__ = 0;
+#define OR_DEADCODE_DUMMY || sendme_deadcode_dummy__
+#else
+#define OR_DEADCODE_DUMMY
+#endif
+
+#define CHECK_REMAINING(nbytes, label) \
+ do { \
+ if (remaining < (nbytes) OR_DEADCODE_DUMMY) { \
+ goto label; \
+ } \
+ } while (0)
+
+sendme_cell_t *
+sendme_cell_new(void)
+{
+ sendme_cell_t *val = trunnel_calloc(1, sizeof(sendme_cell_t));
+ if (NULL == val)
+ return NULL;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+sendme_cell_clear(sendme_cell_t *obj)
+{
+ (void) obj;
+}
+
+void
+sendme_cell_free(sendme_cell_t *obj)
+{
+ if (obj == NULL)
+ return;
+ sendme_cell_clear(obj);
+ trunnel_memwipe(obj, sizeof(sendme_cell_t));
+ trunnel_free_(obj);
+}
+
+uint8_t
+sendme_cell_get_version(const sendme_cell_t *inp)
+{
+ return inp->version;
+}
+int
+sendme_cell_set_version(sendme_cell_t *inp, uint8_t val)
+{
+ if (! ((val == 0 || val == 1))) {
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+ }
+ inp->version = val;
+ return 0;
+}
+uint16_t
+sendme_cell_get_data_len(const sendme_cell_t *inp)
+{
+ return inp->data_len;
+}
+int
+sendme_cell_set_data_len(sendme_cell_t *inp, uint16_t val)
+{
+ inp->data_len = val;
+ return 0;
+}
+size_t
+sendme_cell_getlen_data_v1_digest(const sendme_cell_t *inp)
+{
+ (void)inp; return TRUNNEL_SENDME_V1_DIGEST_LEN;
+}
+
+uint8_t
+sendme_cell_get_data_v1_digest(sendme_cell_t *inp, size_t idx)
+{
+ trunnel_assert(idx < TRUNNEL_SENDME_V1_DIGEST_LEN);
+ return inp->data_v1_digest[idx];
+}
+
+uint8_t
+sendme_cell_getconst_data_v1_digest(const sendme_cell_t *inp, size_t idx)
+{
+ return sendme_cell_get_data_v1_digest((sendme_cell_t*)inp, idx);
+}
+int
+sendme_cell_set_data_v1_digest(sendme_cell_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < TRUNNEL_SENDME_V1_DIGEST_LEN);
+ inp->data_v1_digest[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+sendme_cell_getarray_data_v1_digest(sendme_cell_t *inp)
+{
+ return inp->data_v1_digest;
+}
+const uint8_t *
+sendme_cell_getconstarray_data_v1_digest(const sendme_cell_t *inp)
+{
+ return (const uint8_t *)sendme_cell_getarray_data_v1_digest((sendme_cell_t*)inp);
+}
+const char *
+sendme_cell_check(const sendme_cell_t *obj)
+{
+ if (obj == NULL)
+ return "Object was NULL";
+ if (obj->trunnel_error_code_)
+ return "A set function failed on this object";
+ if (! (obj->version == 0 || obj->version == 1))
+ return "Integer out of bounds";
+ switch (obj->version) {
+
+ case 0:
+ break;
+
+ case 1:
+ break;
+
+ default:
+ return "Bad tag for union";
+ break;
+ }
+ return NULL;
+}
+
+ssize_t
+sendme_cell_encoded_len(const sendme_cell_t *obj)
+{
+ ssize_t result = 0;
+
+ if (NULL != sendme_cell_check(obj))
+ return -1;
+
+
+ /* Length of u8 version IN [0, 1] */
+ result += 1;
+
+ /* Length of u16 data_len */
+ result += 2;
+ switch (obj->version) {
+
+ case 0:
+ break;
+
+ case 1:
+
+ /* Length of u8 data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN] */
+ result += TRUNNEL_SENDME_V1_DIGEST_LEN;
+ break;
+
+ default:
+ trunnel_assert(0);
+ break;
+ }
+ return result;
+}
+int
+sendme_cell_clear_errors(sendme_cell_t *obj)
+{
+ int r = obj->trunnel_error_code_;
+ obj->trunnel_error_code_ = 0;
+ return r;
+}
+ssize_t
+sendme_cell_encode(uint8_t *output, const size_t avail, const sendme_cell_t *obj)
+{
+ ssize_t result = 0;
+ size_t written = 0;
+ uint8_t *ptr = output;
+ const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ const ssize_t encoded_len = sendme_cell_encoded_len(obj);
+#endif
+
+ uint8_t *backptr_data_len = NULL;
+
+ if (NULL != (msg = sendme_cell_check(obj)))
+ goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ trunnel_assert(encoded_len >= 0);
+#endif
+
+ /* Encode u8 version IN [0, 1] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 1)
+ goto truncated;
+ trunnel_set_uint8(ptr, (obj->version));
+ written += 1; ptr += 1;
+
+ /* Encode u16 data_len */
+ backptr_data_len = ptr;
+ trunnel_assert(written <= avail);
+ if (avail - written < 2)
+ goto truncated;
+ trunnel_set_uint16(ptr, trunnel_htons(obj->data_len));
+ written += 2; ptr += 2;
+ {
+ size_t written_before_union = written;
+
+ /* Encode union data[version] */
+ trunnel_assert(written <= avail);
+ switch (obj->version) {
+
+ case 0:
+ break;
+
+ case 1:
+
+ /* Encode u8 data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN] */
+ trunnel_assert(written <= avail);
+ if (avail - written < TRUNNEL_SENDME_V1_DIGEST_LEN)
+ goto truncated;
+ memcpy(ptr, obj->data_v1_digest, TRUNNEL_SENDME_V1_DIGEST_LEN);
+ written += TRUNNEL_SENDME_V1_DIGEST_LEN; ptr += TRUNNEL_SENDME_V1_DIGEST_LEN;
+ break;
+
+ default:
+ trunnel_assert(0);
+ break;
+ }
+ /* Write the length field back to data_len */
+ trunnel_assert(written >= written_before_union);
+#if UINT16_MAX < SIZE_MAX
+ if (written - written_before_union > UINT16_MAX)
+ goto check_failed;
+#endif
+ trunnel_set_uint16(backptr_data_len, trunnel_htons(written - written_before_union));
+ }
+
+
+ trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ {
+ trunnel_assert(encoded_len >= 0);
+ trunnel_assert((size_t)encoded_len == written);
+ }
+
+#endif
+
+ return written;
+
+ truncated:
+ result = -2;
+ goto fail;
+ check_failed:
+ (void)msg;
+ result = -1;
+ goto fail;
+ fail:
+ trunnel_assert(result < 0);
+ return result;
+}
+
+/** As sendme_cell_parse(), but do not allocate the output object.
+ */
+static ssize_t
+sendme_cell_parse_into(sendme_cell_t *obj, const uint8_t *input, const size_t len_in)
+{
+ const uint8_t *ptr = input;
+ size_t remaining = len_in;
+ ssize_t result = 0;
+ (void)result;
+
+ /* Parse u8 version IN [0, 1] */
+ CHECK_REMAINING(1, truncated);
+ obj->version = (trunnel_get_uint8(ptr));
+ remaining -= 1; ptr += 1;
+ if (! (obj->version == 0 || obj->version == 1))
+ goto fail;
+
+ /* Parse u16 data_len */
+ CHECK_REMAINING(2, truncated);
+ obj->data_len = trunnel_ntohs(trunnel_get_uint16(ptr));
+ remaining -= 2; ptr += 2;
+ {
+ size_t remaining_after;
+ CHECK_REMAINING(obj->data_len, truncated);
+ remaining_after = remaining - obj->data_len;
+ remaining = obj->data_len;
+
+ /* Parse union data[version] */
+ switch (obj->version) {
+
+ case 0:
+ /* Skip to end of union */
+ ptr += remaining; remaining = 0;
+ break;
+
+ case 1:
+
+ /* Parse u8 data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN] */
+ CHECK_REMAINING(TRUNNEL_SENDME_V1_DIGEST_LEN, fail);
+ memcpy(obj->data_v1_digest, ptr, TRUNNEL_SENDME_V1_DIGEST_LEN);
+ remaining -= TRUNNEL_SENDME_V1_DIGEST_LEN; ptr += TRUNNEL_SENDME_V1_DIGEST_LEN;
+ break;
+
+ default:
+ goto fail;
+ break;
+ }
+ if (remaining != 0)
+ goto fail;
+ remaining = remaining_after;
+ }
+ trunnel_assert(ptr + remaining == input + len_in);
+ return len_in - remaining;
+
+ truncated:
+ return -2;
+ fail:
+ result = -1;
+ return result;
+}
+
+ssize_t
+sendme_cell_parse(sendme_cell_t **output, const uint8_t *input, const size_t len_in)
+{
+ ssize_t result;
+ *output = sendme_cell_new();
+ if (NULL == *output)
+ return -1;
+ result = sendme_cell_parse_into(*output, input, len_in);
+ if (result < 0) {
+ sendme_cell_free(*output);
+ *output = NULL;
+ }
+ return result;
+}
diff --git a/src/trunnel/sendme.h b/src/trunnel/sendme.h
new file mode 100644
index 0000000000..f3c3dd78c4
--- /dev/null
+++ b/src/trunnel/sendme.h
@@ -0,0 +1,101 @@
+/* sendme.h -- generated by Trunnel v1.5.2.
+ * https://gitweb.torproject.org/trunnel.git
+ * You probably shouldn't edit this file.
+ */
+#ifndef TRUNNEL_SENDME_H
+#define TRUNNEL_SENDME_H
+
+#include <stdint.h>
+#include "trunnel.h"
+
+#define TRUNNEL_SENDME_V1_DIGEST_LEN 20
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SENDME_CELL)
+struct sendme_cell_st {
+ uint8_t version;
+ uint16_t data_len;
+ uint8_t data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN];
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct sendme_cell_st sendme_cell_t;
+/** Return a newly allocated sendme_cell with all elements set to
+ * zero.
+ */
+sendme_cell_t *sendme_cell_new(void);
+/** Release all storage held by the sendme_cell in 'victim'. (Do
+ * nothing if 'victim' is NULL.)
+ */
+void sendme_cell_free(sendme_cell_t *victim);
+/** Try to parse a sendme_cell from the buffer in 'input', using up to
+ * 'len_in' bytes from the input buffer. On success, return the number
+ * of bytes consumed and set *output to the newly allocated
+ * sendme_cell_t. On failure, return -2 if the input appears
+ * truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t sendme_cell_parse(sendme_cell_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * sendme_cell in 'obj'. On failure, return a negative value. Note
+ * that this value may be an overestimate, and can even be an
+ * underestimate for certain unencodeable objects.
+ */
+ssize_t sendme_cell_encoded_len(const sendme_cell_t *obj);
+/** Try to encode the sendme_cell from 'input' into the buffer at
+ * 'output', using up to 'avail' bytes of the output buffer. On
+ * success, return the number of bytes used. On failure, return -2 if
+ * the buffer was not long enough, and -1 if the input was invalid.
+ */
+ssize_t sendme_cell_encode(uint8_t *output, size_t avail, const sendme_cell_t *input);
+/** Check whether the internal state of the sendme_cell in 'obj' is
+ * consistent. Return NULL if it is, and a short message if it is not.
+ */
+const char *sendme_cell_check(const sendme_cell_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int sendme_cell_clear_errors(sendme_cell_t *obj);
+/** Return the value of the version field of the sendme_cell_t in
+ * 'inp'
+ */
+uint8_t sendme_cell_get_version(const sendme_cell_t *inp);
+/** Set the value of the version field of the sendme_cell_t in 'inp'
+ * to 'val'. Return 0 on success; return -1 and set the error code on
+ * 'inp' on failure.
+ */
+int sendme_cell_set_version(sendme_cell_t *inp, uint8_t val);
+/** Return the value of the data_len field of the sendme_cell_t in
+ * 'inp'
+ */
+uint16_t sendme_cell_get_data_len(const sendme_cell_t *inp);
+/** Set the value of the data_len field of the sendme_cell_t in 'inp'
+ * to 'val'. Return 0 on success; return -1 and set the error code on
+ * 'inp' on failure.
+ */
+int sendme_cell_set_data_len(sendme_cell_t *inp, uint16_t val);
+/** Return the (constant) length of the array holding the
+ * data_v1_digest field of the sendme_cell_t in 'inp'.
+ */
+size_t sendme_cell_getlen_data_v1_digest(const sendme_cell_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * data_v1_digest of the sendme_cell_t in 'inp'.
+ */
+uint8_t sendme_cell_get_data_v1_digest(sendme_cell_t *inp, size_t idx);
+/** As sendme_cell_get_data_v1_digest, but take and return a const
+ * pointer
+ */
+uint8_t sendme_cell_getconst_data_v1_digest(const sendme_cell_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * data_v1_digest of the sendme_cell_t in 'inp', so that it will hold
+ * the value 'elt'.
+ */
+int sendme_cell_set_data_v1_digest(sendme_cell_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the TRUNNEL_SENDME_V1_DIGEST_LEN-element array
+ * field data_v1_digest of 'inp'.
+ */
+uint8_t * sendme_cell_getarray_data_v1_digest(sendme_cell_t *inp);
+/** As sendme_cell_get_data_v1_digest, but take and return a const
+ * pointer
+ */
+const uint8_t * sendme_cell_getconstarray_data_v1_digest(const sendme_cell_t *inp);
+
+
+#endif
diff --git a/src/trunnel/sendme.trunnel b/src/trunnel/sendme.trunnel
new file mode 100644
index 0000000000..300963e679
--- /dev/null
+++ b/src/trunnel/sendme.trunnel
@@ -0,0 +1,19 @@
+/* This file contains the SENDME cell definition. */
+
+/* v1 digest length in bytes. */
+const TRUNNEL_SENDME_V1_DIGEST_LEN = 20;
+
+/* SENDME cell declaration. */
+struct sendme_cell {
+ /* Version field. */
+ u8 version IN [0x00, 0x01];
+
+ /* Length of data contained in this cell. */
+ u16 data_len;
+
+ /* The data content depends on the version. */
+ union data[version] with length data_len {
+ 0x00: ignore;
+ 0x01: u8 v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN];
+ };
+}
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index adac422687..bb1f42ef19 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -218,7 +218,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.4.0.5-dev"
+#define VERSION "0.4.1.5-dev"