diff options
Diffstat (limited to 'src/app/config')
-rw-r--r-- | src/app/config/auth_dirs.inc | 34 | ||||
-rw-r--r-- | src/app/config/config.c | 8521 | ||||
-rw-r--r-- | src/app/config/config.h | 300 | ||||
-rw-r--r-- | src/app/config/confparse.c | 1207 | ||||
-rw-r--r-- | src/app/config/confparse.h | 233 | ||||
-rw-r--r-- | src/app/config/fallback_dirs.inc | 856 | ||||
-rw-r--r-- | src/app/config/or_options_st.h | 1077 | ||||
-rw-r--r-- | src/app/config/or_state_st.h | 92 | ||||
-rw-r--r-- | src/app/config/statefile.c | 728 | ||||
-rw-r--r-- | src/app/config/statefile.h | 36 |
10 files changed, 13084 insertions, 0 deletions
diff --git a/src/app/config/auth_dirs.inc b/src/app/config/auth_dirs.inc new file mode 100644 index 0000000000..278f08bfcf --- /dev/null +++ b/src/app/config/auth_dirs.inc @@ -0,0 +1,34 @@ +"moria1 orport=9101 " + "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 " + "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31", +"tor26 orport=443 " + "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 " + "ipv6=[2001:858:2:2:aabb:0:563b:1526]:443 " + "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D", +"dizum orport=443 " + "v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 " + "45.66.33.45:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755", +"Serge orport=9001 bridge " + "66.111.2.131:9030 BA44 A889 E64B 93FA A2B1 14E0 2C2A 279A 8555 C533", +"gabelmoo orport=443 " + "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 " + "ipv6=[2001:638:a000:4140::ffff:189]:443 " + "131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281", +"dannenberg orport=443 " + "v3ident=0232AF901C31A04EE9848595AF9BB7620D4C5B2E " + "ipv6=[2001:678:558:1000::244]:443 " + "193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123", +"maatuska orport=80 " + "v3ident=49015F787433103580E3B66A1707A00E60F2D15B " + "ipv6=[2001:67c:289c::9]:80 " + "171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810", +"Faravahar orport=443 " + "v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 " + "154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC", +"longclaw orport=443 " + "v3ident=23D15D965BC35114467363C165C4F724B64B4F66 " + "199.58.81.140:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145", +"bastet orport=443 " + "v3ident=27102BC123E7AF1D4741AE047E160C91ADC76B21 " + "ipv6=[2620:13:4000:6000::1000:118]:443 " + "204.13.164.118:80 24E2 F139 121D 4394 C54B 5BCC 368B 3B41 1857 C413", diff --git a/src/app/config/config.c b/src/app/config/config.c new file mode 100644 index 0000000000..0b1b758d96 --- /dev/null +++ b/src/app/config/config.c @@ -0,0 +1,8521 @@ +/* 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 config.c + * \brief Code to interpret the user's configuration of Tor. + * + * This module handles torrc configuration file, including parsing it, + * combining it with torrc.defaults and the command line, allowing + * user changes to it (via editing and SIGHUP or via the control port), + * writing it back to disk (because of SAVECONF from the control port), + * and -- most importantly, acting on it. + * + * The module additionally has some tools for manipulating and + * inspecting values that are calculated as a result of the + * configured options. + * + * <h3>How to add new options</h3> + * + * To add new items to the torrc, there are a minimum of three places to edit: + * <ul> + * <li>The or_options_t structure in or.h, where the options are stored. + * <li>The option_vars_ array below in this module, which configures + * the names of the torrc options, their types, their multiplicities, + * and their mappings to fields in or_options_t. + * <li>The manual in doc/tor.1.txt, to document what the new option + * is, and how it works. + * </ul> + * + * Additionally, you might need to edit these places too: + * <ul> + * <li>options_validate() below, in case you want to reject some possible + * values of the new configuration option. + * <li>options_transition_allowed() below, in case you need to + * forbid some or all changes in the option while Tor is + * running. + * <li>options_transition_affects_workers(), in case changes in the option + * might require Tor to relaunch or reconfigure its worker threads. + * <li>options_transition_affects_descriptor(), in case changes in the + * option might require a Tor relay to build and publish a new server + * descriptor. + * <li>options_act() and/or options_act_reversible(), in case there's some + * action that needs to be taken immediately based on the option's + * value. + * </ul> + * + * <h3>Changing the value of an option</h3> + * + * Because of the SAVECONF command from the control port, it's a bad + * idea to change the value of any user-configured option in the + * or_options_t. If you want to sometimes do this anyway, we recommend + * that you create a secondary field in or_options_t; that you have the + * user option linked only to the secondary field; that you use the + * secondary field to initialize the one that Tor actually looks at; and that + * you use the one Tor looks as the one that you modify. + **/ + +#define CONFIG_PRIVATE +#include "core/or/or.h" +#include "app/config/config.h" +#include "app/config/confparse.h" +#include "app/config/statefile.h" +#include "app/main/main.h" +#include "core/mainloop/connection.h" +#include "core/mainloop/cpuworker.h" +#include "core/mainloop/mainloop.h" +#include "core/mainloop/netstatus.h" +#include "core/or/channel.h" +#include "core/or/circuitbuild.h" +#include "core/or/circuitlist.h" +#include "core/or/circuitmux.h" +#include "core/or/circuitmux_ewma.h" +#include "core/or/circuitstats.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/relay.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/dirauth/bwauth.h" +#include "feature/dirauth/guardfraction.h" +#include "feature/dircache/consdiffmgr.h" +#include "feature/dircache/dirserv.h" +#include "feature/dircommon/voting_schedule.h" +#include "feature/hibernate/hibernate.h" +#include "feature/hs/hs_config.h" +#include "feature/nodelist/dirlist.h" +#include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/nickname.h" +#include "feature/nodelist/nodelist.h" +#include "feature/nodelist/routerlist.h" +#include "feature/nodelist/routerset.h" +#include "feature/relay/dns.h" +#include "feature/relay/ext_orport.h" +#include "feature/relay/routermode.h" +#include "feature/rend/rendclient.h" +#include "feature/rend/rendservice.h" +#include "lib/geoip/geoip.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/crypt_ops/crypto_init.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/encoding/confline.h" +#include "lib/log/git_revision.h" +#include "lib/net/resolve.h" +#include "lib/sandbox/sandbox.h" + +#ifdef ENABLE_NSS +#include "lib/crypt_ops/crypto_nss_mgt.h" +#else +#include "lib/crypt_ops/crypto_openssl_mgt.h" +#endif + +#ifdef _WIN32 +#include <shlobj.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "lib/meminfo/meminfo.h" +#include "lib/osinfo/uname.h" +#include "lib/process/daemon.h" +#include "lib/process/pidfile.h" +#include "lib/process/restrict.h" +#include "lib/process/setuid.h" +#include "lib/process/subprocess.h" +#include "lib/net/gethostname.h" +#include "lib/thread/numcpus.h" + +#include "lib/encoding/keyval.h" +#include "lib/fs/conffile.h" +#include "lib/evloop/procmon.h" + +#include "feature/dirauth/dirvote.h" +#include "feature/dirauth/recommend_pkg.h" +#include "feature/dirauth/authmode.h" + +#include "core/or/connection_st.h" +#include "core/or/port_cfg_st.h" + +#ifdef HAVE_SYSTEMD +# if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) +/* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse + * Coverity. Here's a kludge to unconfuse it. + */ +# define __INCLUDE_LEVEL__ 2 +#endif /* defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) */ +#include <systemd/sd-daemon.h> +#endif /* defined(HAVE_SYSTEMD) */ + +/* Prefix used to indicate a Unix socket in a FooPort configuration. */ +static const char unix_socket_prefix[] = "unix:"; +/* Prefix used to indicate a Unix socket with spaces in it, in a FooPort + * configuration. */ +static const char unix_q_socket_prefix[] = "unix:\""; + +/* limits for TCP send and recv buffer size used for constrained sockets */ +#define MIN_CONSTRAINED_TCP_BUFFER 2048 +#define MAX_CONSTRAINED_TCP_BUFFER 262144 /* 256k */ + +/** macro to help with the bulk rename of *DownloadSchedule to + * *DowloadInitialDelay . */ +#define DOWNLOAD_SCHEDULE(name) \ + { #name "DownloadSchedule", #name "DownloadInitialDelay", 0, 1 } + +/** A list of abbreviations and aliases to map command-line options, obsolete + * option names, or alternative option names, to their current values. */ +static config_abbrev_t option_abbrevs_[] = { + PLURAL(AuthDirBadDirCC), + PLURAL(AuthDirBadExitCC), + PLURAL(AuthDirInvalidCC), + PLURAL(AuthDirRejectCC), + PLURAL(EntryNode), + PLURAL(ExcludeNode), + PLURAL(FirewallPort), + PLURAL(LongLivedPort), + PLURAL(HiddenServiceNode), + PLURAL(HiddenServiceExcludeNode), + PLURAL(NumCPU), + PLURAL(RendNode), + PLURAL(RecommendedPackage), + PLURAL(RendExcludeNode), + PLURAL(StrictEntryNode), + PLURAL(StrictExitNode), + PLURAL(StrictNode), + { "l", "Log", 1, 0}, + { "AllowUnverifiedNodes", "AllowInvalidNodes", 0, 0}, + { "AutomapHostSuffixes", "AutomapHostsSuffixes", 0, 0}, + { "AutomapHostOnResolve", "AutomapHostsOnResolve", 0, 0}, + { "BandwidthRateBytes", "BandwidthRate", 0, 0}, + { "BandwidthBurstBytes", "BandwidthBurst", 0, 0}, + { "DirFetchPostPeriod", "StatusFetchPeriod", 0, 0}, + { "DirServer", "DirAuthority", 0, 0}, /* XXXX later, make this warn? */ + { "MaxConn", "ConnLimit", 0, 1}, + { "MaxMemInCellQueues", "MaxMemInQueues", 0, 0}, + { "ORBindAddress", "ORListenAddress", 0, 0}, + { "DirBindAddress", "DirListenAddress", 0, 0}, + { "SocksBindAddress", "SocksListenAddress", 0, 0}, + { "UseHelperNodes", "UseEntryGuards", 0, 0}, + { "NumHelperNodes", "NumEntryGuards", 0, 0}, + { "UseEntryNodes", "UseEntryGuards", 0, 0}, + { "NumEntryNodes", "NumEntryGuards", 0, 0}, + { "ResolvConf", "ServerDNSResolvConfFile", 0, 1}, + { "SearchDomains", "ServerDNSSearchDomains", 0, 1}, + { "ServerDNSAllowBrokenResolvConf", "ServerDNSAllowBrokenConfig", 0, 0}, + { "PreferTunnelledDirConns", "PreferTunneledDirConns", 0, 0}, + { "BridgeAuthoritativeDirectory", "BridgeAuthoritativeDir", 0, 0}, + { "HashedControlPassword", "__HashedControlSessionPassword", 1, 0}, + { "VirtualAddrNetwork", "VirtualAddrNetworkIPv4", 0, 0}, + { "SocksSocketsGroupWritable", "UnixSocksGroupWritable", 0, 1}, + { "_HSLayer2Nodes", "HSLayer2Nodes", 0, 1 }, + { "_HSLayer3Nodes", "HSLayer3Nodes", 0, 1 }, + + DOWNLOAD_SCHEDULE(ClientBootstrapConsensusAuthority), + DOWNLOAD_SCHEDULE(ClientBootstrapConsensusAuthorityOnly), + DOWNLOAD_SCHEDULE(ClientBootstrapConsensusFallback), + DOWNLOAD_SCHEDULE(TestingBridge), + DOWNLOAD_SCHEDULE(TestingBridgeBootstrap), + DOWNLOAD_SCHEDULE(TestingClient), + DOWNLOAD_SCHEDULE(TestingClientConsensus), + DOWNLOAD_SCHEDULE(TestingServer), + DOWNLOAD_SCHEDULE(TestingServerConsensus), + + { NULL, NULL, 0, 0}, +}; + +/** dummy instance of or_options_t, used for type-checking its + * members with CONF_CHECK_VAR_TYPE. */ +DUMMY_TYPECHECK_INSTANCE(or_options_t); + +/** An entry for config_vars: "The option <b>name</b> has type + * CONFIG_TYPE_<b>conftype</b>, and corresponds to + * or_options_t.<b>member</b>" + */ +#define VAR(name,conftype,member,initvalue) \ + { name, CONFIG_TYPE_ ## conftype, offsetof(or_options_t, member), \ + initvalue CONF_TEST_MEMBERS(or_options_t, conftype, member) } +/** As VAR, but the option name and member name are the same. */ +#define V(member,conftype,initvalue) \ + VAR(#member, conftype, member, initvalue) +/** An entry for config_vars: "The option <b>name</b> is obsolete." */ +#ifdef TOR_UNIT_TESTS +#define OBSOLETE(name) { name, CONFIG_TYPE_OBSOLETE, 0, NULL, {.INT=NULL} } +#else +#define OBSOLETE(name) { name, CONFIG_TYPE_OBSOLETE, 0, NULL } +#endif + +/** + * Macro to declare *Port options. Each one comes in three entries. + * For example, most users should use "SocksPort" to configure the + * socks port, but TorBrowser wants to use __SocksPort so that it + * isn't stored by SAVECONF. The SocksPortLines virtual option is + * used to query both options from the controller. + */ +#define VPORT(member) \ + VAR(#member "Lines", LINELIST_V, member ## _lines, NULL), \ + VAR(#member, LINELIST_S, member ## _lines, NULL), \ + VAR("__" #member, LINELIST_S, member ## _lines, NULL) + +/** UINT64_MAX as a decimal string */ +#define UINT64_MAX_STRING "18446744073709551615" + +/** Array of configuration options. Until we disallow nonstandard + * abbreviations, order is significant, since the first matching option will + * be chosen first. + */ +static config_var_t option_vars_[] = { + V(AccountingMax, MEMUNIT, "0 bytes"), + VAR("AccountingRule", STRING, AccountingRule_option, "max"), + V(AccountingStart, STRING, NULL), + V(Address, STRING, NULL), + OBSOLETE("AllowDotExit"), + OBSOLETE("AllowInvalidNodes"), + V(AllowNonRFC953Hostnames, BOOL, "0"), + OBSOLETE("AllowSingleHopCircuits"), + OBSOLETE("AllowSingleHopExits"), + V(AlternateBridgeAuthority, LINELIST, NULL), + V(AlternateDirAuthority, LINELIST, NULL), + OBSOLETE("AlternateHSAuthority"), + V(AssumeReachable, BOOL, "0"), + OBSOLETE("AuthDirBadDir"), + OBSOLETE("AuthDirBadDirCCs"), + V(AuthDirBadExit, LINELIST, NULL), + V(AuthDirBadExitCCs, CSV, ""), + V(AuthDirInvalid, LINELIST, NULL), + V(AuthDirInvalidCCs, CSV, ""), + V(AuthDirFastGuarantee, MEMUNIT, "100 KB"), + V(AuthDirGuardBWGuarantee, MEMUNIT, "2 MB"), + V(AuthDirPinKeys, BOOL, "1"), + V(AuthDirReject, LINELIST, NULL), + V(AuthDirRejectCCs, CSV, ""), + OBSOLETE("AuthDirRejectUnlisted"), + OBSOLETE("AuthDirListBadDirs"), + V(AuthDirListBadExits, BOOL, "0"), + V(AuthDirMaxServersPerAddr, UINT, "2"), + OBSOLETE("AuthDirMaxServersPerAuthAddr"), + V(AuthDirHasIPv6Connectivity, BOOL, "0"), + VAR("AuthoritativeDirectory", BOOL, AuthoritativeDir, "0"), + V(AutomapHostsOnResolve, BOOL, "0"), + V(AutomapHostsSuffixes, CSV, ".onion,.exit"), + V(AvoidDiskWrites, BOOL, "0"), + V(BandwidthBurst, MEMUNIT, "1 GB"), + V(BandwidthRate, MEMUNIT, "1 GB"), + V(BridgeAuthoritativeDir, BOOL, "0"), + VAR("Bridge", LINELIST, Bridges, NULL), + V(BridgePassword, STRING, NULL), + V(BridgeRecordUsageByCountry, BOOL, "1"), + V(BridgeRelay, BOOL, "0"), + V(BridgeDistribution, STRING, NULL), + VAR("CacheDirectory", FILENAME, CacheDirectory_option, NULL), + V(CacheDirectoryGroupReadable, AUTOBOOL, "auto"), + V(CellStatistics, BOOL, "0"), + V(PaddingStatistics, BOOL, "1"), + V(LearnCircuitBuildTimeout, BOOL, "1"), + V(CircuitBuildTimeout, INTERVAL, "0"), + OBSOLETE("CircuitIdleTimeout"), + V(CircuitsAvailableTimeout, INTERVAL, "0"), + V(CircuitStreamTimeout, INTERVAL, "0"), + V(CircuitPriorityHalflife, DOUBLE, "-1.0"), /*negative:'Use default'*/ + V(ClientDNSRejectInternalAddresses, BOOL,"1"), + V(ClientOnly, BOOL, "0"), + V(ClientPreferIPv6ORPort, AUTOBOOL, "auto"), + V(ClientPreferIPv6DirPort, AUTOBOOL, "auto"), + V(ClientRejectInternalAddresses, BOOL, "1"), + V(ClientTransportPlugin, LINELIST, NULL), + V(ClientUseIPv6, BOOL, "0"), + V(ClientUseIPv4, BOOL, "1"), + V(ConsensusParams, STRING, NULL), + V(ConnLimit, UINT, "1000"), + V(ConnDirectionStatistics, BOOL, "0"), + V(ConstrainedSockets, BOOL, "0"), + V(ConstrainedSockSize, MEMUNIT, "8192"), + V(ContactInfo, STRING, NULL), + OBSOLETE("ControlListenAddress"), + VPORT(ControlPort), + V(ControlPortFileGroupReadable,BOOL, "0"), + V(ControlPortWriteToFile, FILENAME, NULL), + V(ControlSocket, LINELIST, NULL), + V(ControlSocketsGroupWritable, BOOL, "0"), + V(UnixSocksGroupWritable, BOOL, "0"), + V(CookieAuthentication, BOOL, "0"), + V(CookieAuthFileGroupReadable, BOOL, "0"), + V(CookieAuthFile, STRING, NULL), + V(CountPrivateBandwidth, BOOL, "0"), + VAR("DataDirectory", FILENAME, DataDirectory_option, NULL), + V(DataDirectoryGroupReadable, BOOL, "0"), + V(DisableOOSCheck, BOOL, "1"), + V(DisableNetwork, BOOL, "0"), + V(DirAllowPrivateAddresses, BOOL, "0"), + V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), + OBSOLETE("DirListenAddress"), + V(DirPolicy, LINELIST, NULL), + VPORT(DirPort), + V(DirPortFrontPage, FILENAME, NULL), + VAR("DirReqStatistics", BOOL, DirReqStatistics_option, "1"), + VAR("DirAuthority", LINELIST, DirAuthorities, NULL), + V(DirCache, BOOL, "1"), + /* A DirAuthorityFallbackRate of 0.1 means that 0.5% of clients try an + * authority when all fallbacks are up, and 2% try an authority when 25% of + * fallbacks are down. (We rebuild the list when 25% of fallbacks are down). + * + * We want to reduce load on authorities, but keep these two figures within + * an order of magnitude, so there isn't too much load shifting to + * authorities when fallbacks go down. */ + V(DirAuthorityFallbackRate, DOUBLE, "0.1"), + V(DisableAllSwap, BOOL, "0"), + V(DisableDebuggerAttachment, BOOL, "1"), + OBSOLETE("DisableIOCP"), + OBSOLETE("DisableV2DirectoryInfo_"), + OBSOLETE("DynamicDHGroups"), + VPORT(DNSPort), + OBSOLETE("DNSListenAddress"), + /* DoS circuit creation options. */ + V(DoSCircuitCreationEnabled, AUTOBOOL, "auto"), + V(DoSCircuitCreationMinConnections, UINT, "0"), + V(DoSCircuitCreationRate, UINT, "0"), + V(DoSCircuitCreationBurst, UINT, "0"), + V(DoSCircuitCreationDefenseType, INT, "0"), + V(DoSCircuitCreationDefenseTimePeriod, INTERVAL, "0"), + /* DoS connection options. */ + V(DoSConnectionEnabled, AUTOBOOL, "auto"), + V(DoSConnectionMaxConcurrentCount, UINT, "0"), + V(DoSConnectionDefenseType, INT, "0"), + /* DoS single hop client options. */ + V(DoSRefuseSingleHopClientRendezvous, AUTOBOOL, "auto"), + V(DownloadExtraInfo, BOOL, "0"), + V(TestingEnableConnBwEvent, BOOL, "0"), + V(TestingEnableCellStatsEvent, BOOL, "0"), + OBSOLETE("TestingEnableTbEmptyEvent"), + V(EnforceDistinctSubnets, BOOL, "1"), + V(EntryNodes, ROUTERSET, NULL), + V(EntryStatistics, BOOL, "0"), + V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "10 minutes"), + V(ExcludeNodes, ROUTERSET, NULL), + V(ExcludeExitNodes, ROUTERSET, NULL), + OBSOLETE("ExcludeSingleHopRelays"), + V(ExitNodes, ROUTERSET, NULL), + V(ExitPolicy, LINELIST, NULL), + V(ExitPolicyRejectPrivate, BOOL, "1"), + V(ExitPolicyRejectLocalInterfaces, BOOL, "0"), + V(ExitPortStatistics, BOOL, "0"), + V(ExtendAllowPrivateAddresses, BOOL, "0"), + V(ExitRelay, AUTOBOOL, "auto"), + VPORT(ExtORPort), + V(ExtORPortCookieAuthFile, STRING, NULL), + V(ExtORPortCookieAuthFileGroupReadable, BOOL, "0"), + V(ExtraInfoStatistics, BOOL, "1"), + V(ExtendByEd25519ID, AUTOBOOL, "auto"), + V(FallbackDir, LINELIST, NULL), + + V(UseDefaultFallbackDirs, BOOL, "1"), + + OBSOLETE("FallbackNetworkstatusFile"), + V(FascistFirewall, BOOL, "0"), + V(FirewallPorts, CSV, ""), + OBSOLETE("FastFirstHopPK"), + V(FetchDirInfoEarly, BOOL, "0"), + V(FetchDirInfoExtraEarly, BOOL, "0"), + V(FetchServerDescriptors, BOOL, "1"), + V(FetchHidServDescriptors, BOOL, "1"), + V(FetchUselessDescriptors, BOOL, "0"), + OBSOLETE("FetchV2Networkstatus"), + V(GeoIPExcludeUnknown, AUTOBOOL, "auto"), +#ifdef _WIN32 + V(GeoIPFile, FILENAME, "<default>"), + V(GeoIPv6File, FILENAME, "<default>"), +#else + V(GeoIPFile, FILENAME, + SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip"), + V(GeoIPv6File, FILENAME, + SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip6"), +#endif /* defined(_WIN32) */ + OBSOLETE("Group"), + V(GuardLifetime, INTERVAL, "0 minutes"), + V(HardwareAccel, BOOL, "0"), + V(HeartbeatPeriod, INTERVAL, "6 hours"), + V(MainloopStats, BOOL, "0"), + V(AccelName, STRING, NULL), + V(AccelDir, FILENAME, NULL), + V(HashedControlPassword, LINELIST, NULL), + OBSOLETE("HidServDirectoryV2"), + VAR("HiddenServiceDir", LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceDirGroupReadable", LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceOptions",LINELIST_V, RendConfigLines, NULL), + VAR("HiddenServicePort", LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL), + VAR("HiddenServiceAllowUnknownPorts",LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceNumIntroductionPoints", LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceExportCircuitID", LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"), + V(HidServAuth, LINELIST, NULL), + V(ClientOnionAuthDir, FILENAME, NULL), + OBSOLETE("CloseHSClientCircuitsImmediatelyOnTimeout"), + OBSOLETE("CloseHSServiceRendCircuitsImmediatelyOnTimeout"), + V(HiddenServiceSingleHopMode, BOOL, "0"), + V(HiddenServiceNonAnonymousMode,BOOL, "0"), + V(HTTPProxy, STRING, NULL), + V(HTTPProxyAuthenticator, STRING, NULL), + V(HTTPSProxy, STRING, NULL), + V(HTTPSProxyAuthenticator, STRING, NULL), + VPORT(HTTPTunnelPort), + V(IPv6Exit, BOOL, "0"), + VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL), + V(ServerTransportListenAddr, LINELIST, NULL), + V(ServerTransportOptions, LINELIST, NULL), + V(SigningKeyLifetime, INTERVAL, "30 days"), + V(Socks4Proxy, STRING, NULL), + V(Socks5Proxy, STRING, NULL), + V(Socks5ProxyUsername, STRING, NULL), + V(Socks5ProxyPassword, STRING, NULL), + VAR("KeyDirectory", FILENAME, KeyDirectory_option, NULL), + V(KeyDirectoryGroupReadable, BOOL, "0"), + VAR("HSLayer2Nodes", ROUTERSET, HSLayer2Nodes, NULL), + VAR("HSLayer3Nodes", ROUTERSET, HSLayer3Nodes, NULL), + V(KeepalivePeriod, INTERVAL, "5 minutes"), + V(KeepBindCapabilities, AUTOBOOL, "auto"), + VAR("Log", LINELIST, Logs, NULL), + V(LogMessageDomains, BOOL, "0"), + V(LogTimeGranularity, MSEC_INTERVAL, "1 second"), + V(TruncateLogFile, BOOL, "0"), + V(SyslogIdentityTag, STRING, NULL), + V(AndroidIdentityTag, STRING, NULL), + V(LongLivedPorts, CSV, + "21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"), + VAR("MapAddress", LINELIST, AddressMap, NULL), + V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"), + V(MaxCircuitDirtiness, INTERVAL, "10 minutes"), + V(MaxClientCircuitsPending, UINT, "32"), + V(MaxConsensusAgeForDiffs, INTERVAL, "0 seconds"), + VAR("MaxMemInQueues", MEMUNIT, MaxMemInQueues_raw, "0"), + OBSOLETE("MaxOnionsPending"), + V(MaxOnionQueueDelay, MSEC_INTERVAL, "1750 msec"), + V(MaxUnparseableDescSizeToLog, MEMUNIT, "10 MB"), + V(MinMeasuredBWsForAuthToIgnoreAdvertised, INT, "500"), + VAR("MyFamily", LINELIST, MyFamily_lines, NULL), + V(NewCircuitPeriod, INTERVAL, "30 seconds"), + OBSOLETE("NamingAuthoritativeDirectory"), + OBSOLETE("NATDListenAddress"), + VPORT(NATDPort), + V(Nickname, STRING, NULL), + OBSOLETE("PredictedPortsRelevanceTime"), + OBSOLETE("WarnUnsafeSocks"), + VAR("NodeFamily", LINELIST, NodeFamilies, NULL), + V(NoExec, BOOL, "0"), + V(NumCPUs, UINT, "0"), + V(NumDirectoryGuards, UINT, "0"), + V(NumEntryGuards, UINT, "0"), + V(NumPrimaryGuards, UINT, "0"), + V(OfflineMasterKey, BOOL, "0"), + OBSOLETE("ORListenAddress"), + VPORT(ORPort), + V(OutboundBindAddress, LINELIST, NULL), + V(OutboundBindAddressOR, LINELIST, NULL), + V(OutboundBindAddressExit, LINELIST, NULL), + + OBSOLETE("PathBiasDisableRate"), + V(PathBiasCircThreshold, INT, "-1"), + V(PathBiasNoticeRate, DOUBLE, "-1"), + V(PathBiasWarnRate, DOUBLE, "-1"), + V(PathBiasExtremeRate, DOUBLE, "-1"), + V(PathBiasScaleThreshold, INT, "-1"), + OBSOLETE("PathBiasScaleFactor"), + OBSOLETE("PathBiasMultFactor"), + V(PathBiasDropGuards, AUTOBOOL, "0"), + OBSOLETE("PathBiasUseCloseCounts"), + + V(PathBiasUseThreshold, INT, "-1"), + V(PathBiasNoticeUseRate, DOUBLE, "-1"), + V(PathBiasExtremeUseRate, DOUBLE, "-1"), + V(PathBiasScaleUseThreshold, INT, "-1"), + + V(PathsNeededToBuildCircuits, DOUBLE, "-1"), + V(PerConnBWBurst, MEMUNIT, "0"), + V(PerConnBWRate, MEMUNIT, "0"), + V(PidFile, STRING, NULL), + V(TestingTorNetwork, BOOL, "0"), + V(TestingMinExitFlagThreshold, MEMUNIT, "0"), + V(TestingMinFastFlagThreshold, MEMUNIT, "0"), + + V(TestingLinkCertLifetime, INTERVAL, "2 days"), + V(TestingAuthKeyLifetime, INTERVAL, "2 days"), + V(TestingLinkKeySlop, INTERVAL, "3 hours"), + V(TestingAuthKeySlop, INTERVAL, "3 hours"), + V(TestingSigningKeySlop, INTERVAL, "1 day"), + + V(OptimisticData, AUTOBOOL, "auto"), + OBSOLETE("PortForwarding"), + OBSOLETE("PortForwardingHelper"), + OBSOLETE("PreferTunneledDirConns"), + V(ProtocolWarnings, BOOL, "0"), + V(PublishServerDescriptor, CSV, "1"), + V(PublishHidServDescriptors, BOOL, "1"), + V(ReachableAddresses, LINELIST, NULL), + V(ReachableDirAddresses, LINELIST, NULL), + V(ReachableORAddresses, LINELIST, NULL), + V(RecommendedVersions, LINELIST, NULL), + V(RecommendedClientVersions, LINELIST, NULL), + V(RecommendedServerVersions, LINELIST, NULL), + V(RecommendedPackages, LINELIST, NULL), + V(ReducedConnectionPadding, BOOL, "0"), + V(ConnectionPadding, AUTOBOOL, "auto"), + V(RefuseUnknownExits, AUTOBOOL, "auto"), + V(RejectPlaintextPorts, CSV, ""), + V(RelayBandwidthBurst, MEMUNIT, "0"), + V(RelayBandwidthRate, MEMUNIT, "0"), + V(RendPostPeriod, INTERVAL, "1 hour"), + V(RephistTrackTime, INTERVAL, "24 hours"), + V(RunAsDaemon, BOOL, "0"), + V(ReducedExitPolicy, BOOL, "0"), + OBSOLETE("RunTesting"), // currently unused + V(Sandbox, BOOL, "0"), + V(SafeLogging, STRING, "1"), + V(SafeSocks, BOOL, "0"), + V(ServerDNSAllowBrokenConfig, BOOL, "1"), + V(ServerDNSAllowNonRFC953Hostnames, BOOL,"0"), + V(ServerDNSDetectHijacking, BOOL, "1"), + V(ServerDNSRandomizeCase, BOOL, "1"), + V(ServerDNSResolvConfFile, STRING, NULL), + V(ServerDNSSearchDomains, BOOL, "0"), + V(ServerDNSTestAddresses, CSV, + "www.google.com,www.mit.edu,www.yahoo.com,www.slashdot.org"), + OBSOLETE("SchedulerLowWaterMark__"), + OBSOLETE("SchedulerHighWaterMark__"), + OBSOLETE("SchedulerMaxFlushCells__"), + V(KISTSchedRunInterval, MSEC_INTERVAL, "0 msec"), + V(KISTSockBufSizeFactor, DOUBLE, "1.0"), + V(Schedulers, CSV, "KIST,KISTLite,Vanilla"), + V(ShutdownWaitLength, INTERVAL, "30 seconds"), + OBSOLETE("SocksListenAddress"), + V(SocksPolicy, LINELIST, NULL), + VPORT(SocksPort), + V(SocksTimeout, INTERVAL, "2 minutes"), + V(SSLKeyLifetime, INTERVAL, "0"), + OBSOLETE("StrictEntryNodes"), + OBSOLETE("StrictExitNodes"), + V(StrictNodes, BOOL, "0"), + OBSOLETE("Support022HiddenServices"), + V(TestSocks, BOOL, "0"), + V(TokenBucketRefillInterval, MSEC_INTERVAL, "100 msec"), + OBSOLETE("Tor2webMode"), + OBSOLETE("Tor2webRendezvousPoints"), + OBSOLETE("TLSECGroup"), + V(TrackHostExits, CSV, NULL), + V(TrackHostExitsExpire, INTERVAL, "30 minutes"), + OBSOLETE("TransListenAddress"), + VPORT(TransPort), + V(TransProxyType, STRING, "default"), + OBSOLETE("TunnelDirConns"), + V(UpdateBridgesFromAuthority, BOOL, "0"), + V(UseBridges, BOOL, "0"), + VAR("UseEntryGuards", BOOL, UseEntryGuards_option, "1"), + OBSOLETE("UseEntryGuardsAsDirGuards"), + V(UseGuardFraction, AUTOBOOL, "auto"), + V(UseMicrodescriptors, AUTOBOOL, "auto"), + OBSOLETE("UseNTorHandshake"), + V(User, STRING, NULL), + OBSOLETE("UserspaceIOCPBuffers"), + V(AuthDirSharedRandomness, BOOL, "1"), + V(AuthDirTestEd25519LinkKeys, BOOL, "1"), + OBSOLETE("V1AuthoritativeDirectory"), + OBSOLETE("V2AuthoritativeDirectory"), + VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"), + V(TestingV3AuthInitialVotingInterval, INTERVAL, "30 minutes"), + V(TestingV3AuthInitialVoteDelay, INTERVAL, "5 minutes"), + V(TestingV3AuthInitialDistDelay, INTERVAL, "5 minutes"), + V(TestingV3AuthVotingStartOffset, INTERVAL, "0"), + V(V3AuthVotingInterval, INTERVAL, "1 hour"), + V(V3AuthVoteDelay, INTERVAL, "5 minutes"), + V(V3AuthDistDelay, INTERVAL, "5 minutes"), + V(V3AuthNIntervalsValid, UINT, "3"), + V(V3AuthUseLegacyKey, BOOL, "0"), + V(V3BandwidthsFile, FILENAME, NULL), + V(GuardfractionFile, FILENAME, NULL), + VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"), + OBSOLETE("VoteOnHidServDirectoriesV2"), + V(VirtualAddrNetworkIPv4, STRING, "127.192.0.0/10"), + V(VirtualAddrNetworkIPv6, STRING, "[FE80::]/10"), + V(WarnPlaintextPorts, CSV, "23,109,110,143"), + OBSOLETE("UseFilteringSSLBufferevents"), + OBSOLETE("__UseFilteringSSLBufferevents"), + VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"), + VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"), + VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"), + VAR("__DisableSignalHandlers", BOOL, DisableSignalHandlers, "0"), + VAR("__LeaveStreamsUnattached",BOOL, LeaveStreamsUnattached, "0"), + VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword, + NULL), + VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), + VAR("__OwningControllerFD", UINT64, OwningControllerFD, UINT64_MAX_STRING), + V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"), + V(TestingServerDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingServerConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), + /* With the ClientBootstrapConsensus*Download* below: + * Clients with only authorities will try: + * - at least 3 authorities over 10 seconds, then exponentially backoff, + * with the next attempt 3-21 seconds later, + * Clients with authorities and fallbacks will try: + * - at least 2 authorities and 4 fallbacks over 21 seconds, then + * exponentially backoff, with the next attempts 4-33 seconds later, + * Clients will also retry when an application request arrives. + * After a number of failed requests, clients retry every 3 days + 1 hour. + * + * Clients used to try 2 authorities over 10 seconds, then wait for + * 60 minutes or an application request. + * + * When clients have authorities and fallbacks available, they use these + * schedules: (we stagger the times to avoid thundering herds) */ + V(ClientBootstrapConsensusAuthorityDownloadInitialDelay, CSV_INTERVAL, "6"), + V(ClientBootstrapConsensusFallbackDownloadInitialDelay, CSV_INTERVAL, "0"), + /* When clients only have authorities available, they use this schedule: */ + V(ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay, CSV_INTERVAL, + "0"), + /* We don't want to overwhelm slow networks (or mirrors whose replies are + * blocked), but we also don't want to fail if only some mirrors are + * blackholed. Clients will try 3 directories simultaneously. + * (Relays never use simultaneous connections.) */ + V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"), + /* When a client has any running bridges, check each bridge occasionally, + * whether or not that bridge is actually up. */ + V(TestingBridgeDownloadInitialDelay, CSV_INTERVAL,"10800"), + /* When a client is just starting, or has no running bridges, check each + * bridge a few times quickly, and then try again later. These schedules + * are much longer than the other schedules, because we try each and every + * configured bridge with this schedule. */ + V(TestingBridgeBootstrapDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"), + V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"), + OBSOLETE("TestingConsensusMaxDownloadTries"), + OBSOLETE("ClientBootstrapConsensusMaxDownloadTries"), + OBSOLETE("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries"), + OBSOLETE("TestingDescriptorMaxDownloadTries"), + OBSOLETE("TestingMicrodescMaxDownloadTries"), + OBSOLETE("TestingCertMaxDownloadTries"), + V(TestingDirAuthVoteExit, ROUTERSET, NULL), + V(TestingDirAuthVoteExitIsStrict, BOOL, "0"), + V(TestingDirAuthVoteGuard, ROUTERSET, NULL), + V(TestingDirAuthVoteGuardIsStrict, BOOL, "0"), + V(TestingDirAuthVoteHSDir, ROUTERSET, NULL), + V(TestingDirAuthVoteHSDirIsStrict, BOOL, "0"), + VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "0"), + + END_OF_CONFIG_VARS +}; + +/** Override default values with these if the user sets the TestingTorNetwork + * option. */ +static const config_var_t testing_tor_network_defaults[] = { + V(DirAllowPrivateAddresses, BOOL, "1"), + V(EnforceDistinctSubnets, BOOL, "0"), + V(AssumeReachable, BOOL, "1"), + V(AuthDirMaxServersPerAddr, UINT, "0"), + V(ClientBootstrapConsensusAuthorityDownloadInitialDelay, CSV_INTERVAL, "0"), + V(ClientBootstrapConsensusFallbackDownloadInitialDelay, CSV_INTERVAL, "0"), + V(ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay, CSV_INTERVAL, + "0"), + V(ClientDNSRejectInternalAddresses, BOOL,"0"), + V(ClientRejectInternalAddresses, BOOL, "0"), + V(CountPrivateBandwidth, BOOL, "1"), + V(ExitPolicyRejectPrivate, BOOL, "0"), + V(ExtendAllowPrivateAddresses, BOOL, "1"), + V(V3AuthVotingInterval, INTERVAL, "5 minutes"), + V(V3AuthVoteDelay, INTERVAL, "20 seconds"), + V(V3AuthDistDelay, INTERVAL, "20 seconds"), + V(TestingV3AuthInitialVotingInterval, INTERVAL, "150 seconds"), + V(TestingV3AuthInitialVoteDelay, INTERVAL, "20 seconds"), + V(TestingV3AuthInitialDistDelay, INTERVAL, "20 seconds"), + V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"), + V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"), + V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"), + V(TestingServerDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingServerConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingBridgeDownloadInitialDelay, CSV_INTERVAL, "10"), + V(TestingBridgeBootstrapDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"), + V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"), + V(TestingEnableConnBwEvent, BOOL, "1"), + V(TestingEnableCellStatsEvent, BOOL, "1"), + VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"), + V(RendPostPeriod, INTERVAL, "2 minutes"), + + END_OF_CONFIG_VARS +}; + +#undef VAR +#undef V +#undef OBSOLETE + +static const config_deprecation_t option_deprecation_notes_[] = { + /* Deprecated since 0.3.2.0-alpha. */ + { "HTTPProxy", "It only applies to direct unencrypted HTTP connections " + "to your directory server, which your Tor probably wasn't using." }, + { "HTTPProxyAuthenticator", "HTTPProxy is deprecated in favor of HTTPSProxy " + "which should be used with HTTPSProxyAuthenticator." }, + /* End of options deprecated since 0.3.2.1-alpha */ + + /* Options deprecated since 0.3.2.2-alpha */ + { "ReachableDirAddresses", "It has no effect on relays, and has had no " + "effect on clients since 0.2.8." }, + { "ClientPreferIPv6DirPort", "It has no effect on relays, and has had no " + "effect on clients since 0.2.8." }, + /* End of options deprecated since 0.3.2.2-alpha. */ + + { NULL, NULL } +}; + +#ifdef _WIN32 +static char *get_windows_conf_root(void); +#endif +static int options_act_reversible(const or_options_t *old_options, char **msg); +static int options_transition_allowed(const or_options_t *old, + const or_options_t *new, + char **msg); +static int options_transition_affects_workers( + const or_options_t *old_options, const or_options_t *new_options); +static int options_transition_affects_descriptor( + const or_options_t *old_options, const or_options_t *new_options); +static int options_transition_affects_dirauth_timing( + const or_options_t *old_options, const or_options_t *new_options); +static int normalize_nickname_list(config_line_t **normalized_out, + const config_line_t *lst, const char *name, + char **msg); +static char *get_bindaddr_from_transport_listen_line(const char *line, + const char *transport); +static int parse_ports(or_options_t *options, int validate_only, + char **msg_out, int *n_ports_out, + int *world_writable_control_socket); +static int check_server_ports(const smartlist_t *ports, + const or_options_t *options, + int *num_low_ports_out); +static int validate_data_directories(or_options_t *options); +static int write_configuration_file(const char *fname, + const or_options_t *options); +static int options_init_logs(const or_options_t *old_options, + or_options_t *options, int validate_only); + +static void init_libevent(const or_options_t *options); +static int opt_streq(const char *s1, const char *s2); +static int parse_outbound_addresses(or_options_t *options, int validate_only, + char **msg); +static void config_maybe_load_geoip_files_(const or_options_t *options, + const or_options_t *old_options); +static int options_validate_cb(void *old_options, void *options, + void *default_options, + int from_setconf, char **msg); +static void options_free_cb(void *options); +static void cleanup_protocol_warning_severity_level(void); +static void set_protocol_warning_severity_level(int warning_severity); + +/** Magic value for or_options_t. */ +#define OR_OPTIONS_MAGIC 9090909 + +/** Configuration format for or_options_t. */ +STATIC config_format_t options_format = { + sizeof(or_options_t), + OR_OPTIONS_MAGIC, + offsetof(or_options_t, magic_), + option_abbrevs_, + option_deprecation_notes_, + option_vars_, + options_validate_cb, + options_free_cb, + NULL +}; + +/* + * Functions to read and write the global options pointer. + */ + +/** Command-line and config-file options. */ +static or_options_t *global_options = NULL; +/** The fallback options_t object; this is where we look for options not + * in torrc before we fall back to Tor's defaults. */ +static or_options_t *global_default_options = NULL; +/** Name of most recently read torrc file. */ +static char *torrc_fname = NULL; +/** Name of the most recently read torrc-defaults file.*/ +static char *torrc_defaults_fname = NULL; +/** Configuration options set by command line. */ +static config_line_t *global_cmdline_options = NULL; +/** Non-configuration options set by the command line */ +static config_line_t *global_cmdline_only_options = NULL; +/** Boolean: Have we parsed the command line? */ +static int have_parsed_cmdline = 0; +/** Contents of most recently read DirPortFrontPage file. */ +static char *global_dirfrontpagecontents = NULL; +/** List of port_cfg_t for all configured ports. */ +static smartlist_t *configured_ports = NULL; +/** True iff we're currently validating options, and any calls to + * get_options() are likely to be bugs. */ +static int in_option_validation = 0; +/* True iff we've initialized libevent */ +static int libevent_initialized = 0; + +/** Return the contents of our frontpage string, or NULL if not configured. */ +MOCK_IMPL(const char*, +get_dirportfrontpage, (void)) +{ + return global_dirfrontpagecontents; +} + +/** Returns the currently configured options. */ +MOCK_IMPL(or_options_t *, +get_options_mutable, (void)) +{ + tor_assert(global_options); + tor_assert_nonfatal(! in_option_validation); + return global_options; +} + +/** Returns the currently configured options */ +MOCK_IMPL(const or_options_t *, +get_options,(void)) +{ + return get_options_mutable(); +} + +/** Change the current global options to contain <b>new_val</b> instead of + * their current value; take action based on the new value; free the old value + * as necessary. Returns 0 on success, -1 on failure. + */ +int +set_options(or_options_t *new_val, char **msg) +{ + int i; + smartlist_t *elements; + config_line_t *line; + or_options_t *old_options = global_options; + global_options = new_val; + /* Note that we pass the *old* options below, for comparison. It + * pulls the new options directly out of global_options. */ + if (options_act_reversible(old_options, msg)<0) { + tor_assert(*msg); + global_options = old_options; + return -1; + } + if (options_act(old_options) < 0) { /* acting on the options failed. die. */ + if (! tor_event_loop_shutdown_is_pending()) { + log_err(LD_BUG, + "Acting on config options left us in a broken state. Dying."); + tor_shutdown_event_loop_and_exit(1); + } + global_options = old_options; + return -1; + } + /* Issues a CONF_CHANGED event to notify controller of the change. If Tor is + * just starting up then the old_options will be undefined. */ + if (old_options && old_options != global_options) { + elements = smartlist_new(); + for (i=0; options_format.vars[i].name; ++i) { + const config_var_t *var = &options_format.vars[i]; + const char *var_name = var->name; + if (var->type == CONFIG_TYPE_LINELIST_S || + var->type == CONFIG_TYPE_OBSOLETE) { + continue; + } + if (!config_is_same(&options_format, new_val, old_options, var_name)) { + line = config_get_assigned_option(&options_format, new_val, + var_name, 1); + + if (line) { + config_line_t *next; + for (; line; line = next) { + next = line->next; + smartlist_add(elements, line->key); + smartlist_add(elements, line->value); + tor_free(line); + } + } else { + smartlist_add_strdup(elements, options_format.vars[i].name); + smartlist_add(elements, NULL); + } + } + } + control_event_conf_changed(elements); + SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); + smartlist_free(elements); + } + + if (old_options != global_options) { + or_options_free(old_options); + /* If we are here it means we've successfully applied the new options and + * that the global options have been changed to the new values. We'll + * check if we need to remove or add periodic events. */ + periodic_events_on_new_options(global_options); + } + + return 0; +} + +/** The version of this Tor process, as parsed. */ +static char *the_tor_version = NULL; +/** A shorter version of this Tor process's version, for export in our router + * descriptor. (Does not include the git version, if any.) */ +static char *the_short_tor_version = NULL; + +/** Return the current Tor version. */ +const char * +get_version(void) +{ + if (the_tor_version == NULL) { + if (strlen(tor_git_revision)) { + tor_asprintf(&the_tor_version, "%s (git-%s)", get_short_version(), + tor_git_revision); + } else { + the_tor_version = tor_strdup(get_short_version()); + } + } + return the_tor_version; +} + +/** Return the current Tor version, without any git tag. */ +const char * +get_short_version(void) +{ + + if (the_short_tor_version == NULL) { +#ifdef TOR_BUILD_TAG + tor_asprintf(&the_short_tor_version, "%s (%s)", VERSION, TOR_BUILD_TAG); +#else + the_short_tor_version = tor_strdup(VERSION); +#endif + } + return the_short_tor_version; +} + +/** Release additional memory allocated in options + */ +STATIC void +or_options_free_(or_options_t *options) +{ + if (!options) + return; + + routerset_free(options->ExcludeExitNodesUnion_); + if (options->NodeFamilySets) { + SMARTLIST_FOREACH(options->NodeFamilySets, routerset_t *, + rs, routerset_free(rs)); + smartlist_free(options->NodeFamilySets); + } + if (options->SchedulerTypes_) { + SMARTLIST_FOREACH(options->SchedulerTypes_, int *, i, tor_free(i)); + smartlist_free(options->SchedulerTypes_); + } + if (options->FilesOpenedByIncludes) { + SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, tor_free(f)); + smartlist_free(options->FilesOpenedByIncludes); + } + tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); + tor_free(options->KeyDirectory); + tor_free(options->BridgePassword_AuthDigest_); + tor_free(options->command_arg); + tor_free(options->master_key_fname); + config_free_lines(options->MyFamily); + config_free(&options_format, options); +} + +/** Release all memory and resources held by global configuration structures. + */ +void +config_free_all(void) +{ + or_options_free(global_options); + global_options = NULL; + or_options_free(global_default_options); + global_default_options = NULL; + + config_free_lines(global_cmdline_options); + global_cmdline_options = NULL; + + config_free_lines(global_cmdline_only_options); + global_cmdline_only_options = NULL; + + if (configured_ports) { + SMARTLIST_FOREACH(configured_ports, + port_cfg_t *, p, port_cfg_free(p)); + smartlist_free(configured_ports); + configured_ports = NULL; + } + + tor_free(torrc_fname); + tor_free(torrc_defaults_fname); + tor_free(global_dirfrontpagecontents); + + tor_free(the_short_tor_version); + tor_free(the_tor_version); + + cleanup_protocol_warning_severity_level(); + + have_parsed_cmdline = 0; + libevent_initialized = 0; +} + +/** Make <b>address</b> -- a piece of information related to our operation as + * a client -- safe to log according to the settings in options->SafeLogging, + * and return it. + * + * (We return "[scrubbed]" if SafeLogging is "1", and address otherwise.) + */ +const char * +safe_str_client_opts(const or_options_t *options, const char *address) +{ + tor_assert(address); + if (!options) { + options = get_options(); + } + + if (options->SafeLogging_ == SAFELOG_SCRUB_ALL) + return "[scrubbed]"; + else + return address; +} + +/** Make <b>address</b> -- a piece of information of unspecified sensitivity + * -- safe to log according to the settings in options->SafeLogging, and + * return it. + * + * (We return "[scrubbed]" if SafeLogging is anything besides "0", and address + * otherwise.) + */ +const char * +safe_str_opts(const or_options_t *options, const char *address) +{ + tor_assert(address); + if (!options) { + options = get_options(); + } + + if (options->SafeLogging_ != SAFELOG_SCRUB_NONE) + return "[scrubbed]"; + else + return address; +} + +/** Equivalent to escaped(safe_str_client(address)). See reentrancy note on + * escaped(): don't use this outside the main thread, or twice in the same + * log statement. */ +const char * +escaped_safe_str_client(const char *address) +{ + if (get_options()->SafeLogging_ == SAFELOG_SCRUB_ALL) + return "[scrubbed]"; + else + return escaped(address); +} + +/** Equivalent to escaped(safe_str(address)). See reentrancy note on + * escaped(): don't use this outside the main thread, or twice in the same + * log statement. */ +const char * +escaped_safe_str(const char *address) +{ + if (get_options()->SafeLogging_ != SAFELOG_SCRUB_NONE) + return "[scrubbed]"; + else + return escaped(address); +} + +/** + * The severity level that should be used for warnings of severity + * LOG_PROTOCOL_WARN. + * + * We keep this outside the options, and we use an atomic_counter_t, in case + * one thread needs to use LOG_PROTOCOL_WARN while an option transition is + * happening in the main thread. + */ +static atomic_counter_t protocol_warning_severity_level; + +/** Return the severity level that should be used for warnings of severity + * LOG_PROTOCOL_WARN. */ +int +get_protocol_warning_severity_level(void) +{ + return (int) atomic_counter_get(&protocol_warning_severity_level); +} + +/** Set the protocol warning severity level to <b>severity</b>. */ +static void +set_protocol_warning_severity_level(int warning_severity) +{ + atomic_counter_exchange(&protocol_warning_severity_level, + warning_severity); +} + +/** + * Initialize the log warning severity level for protocol warnings. Call + * only once at startup. + */ +void +init_protocol_warning_severity_level(void) +{ + atomic_counter_init(&protocol_warning_severity_level); + set_protocol_warning_severity_level(LOG_WARN); +} + +/** + * Tear down protocol_warning_severity_level. + */ +static void +cleanup_protocol_warning_severity_level(void) +{ + atomic_counter_destroy(&protocol_warning_severity_level); +} + +/** List of default directory authorities */ + +static const char *default_authorities[] = { +#include "auth_dirs.inc" + NULL +}; + +/** List of fallback directory authorities. The list is generated by opt-in of + * relays that meet certain stability criteria. + */ +static const char *default_fallbacks[] = { +#include "fallback_dirs.inc" + NULL +}; + +/** Add the default directory authorities directly into the trusted dir list, + * but only add them insofar as they share bits with <b>type</b>. + * Each authority's bits are restricted to the bits shared with <b>type</b>. + * If <b>type</b> is ALL_DIRINFO or NO_DIRINFO (zero), add all authorities. */ +STATIC void +add_default_trusted_dir_authorities(dirinfo_type_t type) +{ + int i; + for (i=0; default_authorities[i]; i++) { + if (parse_dir_authority_line(default_authorities[i], type, 0)<0) { + log_err(LD_BUG, "Couldn't parse internal DirAuthority line %s", + default_authorities[i]); + } + } +} + +/** Add the default fallback directory servers into the fallback directory + * server list. */ +MOCK_IMPL(void, +add_default_fallback_dir_servers,(void)) +{ + int i; + for (i=0; default_fallbacks[i]; i++) { + if (parse_dir_fallback_line(default_fallbacks[i], 0)<0) { + log_err(LD_BUG, "Couldn't parse internal FallbackDir line %s", + default_fallbacks[i]); + } + } +} + +/** Look at all the config options for using alternate directory + * authorities, and make sure none of them are broken. Also, warn the + * user if we changed any dangerous ones. + */ +static int +validate_dir_servers(or_options_t *options, or_options_t *old_options) +{ + config_line_t *cl; + + if (options->DirAuthorities && + (options->AlternateDirAuthority || options->AlternateBridgeAuthority)) { + log_warn(LD_CONFIG, + "You cannot set both DirAuthority and Alternate*Authority."); + return -1; + } + + /* do we want to complain to the user about being partitionable? */ + if ((options->DirAuthorities && + (!old_options || + !config_lines_eq(options->DirAuthorities, + old_options->DirAuthorities))) || + (options->AlternateDirAuthority && + (!old_options || + !config_lines_eq(options->AlternateDirAuthority, + old_options->AlternateDirAuthority)))) { + log_warn(LD_CONFIG, + "You have used DirAuthority or AlternateDirAuthority to " + "specify alternate directory authorities in " + "your configuration. This is potentially dangerous: it can " + "make you look different from all other Tor users, and hurt " + "your anonymity. Even if you've specified the same " + "authorities as Tor uses by default, the defaults could " + "change in the future. Be sure you know what you're doing."); + } + + /* Now go through the four ways you can configure an alternate + * set of directory authorities, and make sure none are broken. */ + for (cl = options->DirAuthorities; cl; cl = cl->next) + if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0) + return -1; + for (cl = options->AlternateBridgeAuthority; cl; cl = cl->next) + if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0) + return -1; + for (cl = options->AlternateDirAuthority; cl; cl = cl->next) + if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0) + return -1; + for (cl = options->FallbackDir; cl; cl = cl->next) + if (parse_dir_fallback_line(cl->value, 1)<0) + return -1; + return 0; +} + +/** Look at all the config options and assign new dir authorities + * as appropriate. + */ +int +consider_adding_dir_servers(const or_options_t *options, + const or_options_t *old_options) +{ + config_line_t *cl; + int need_to_update = + !smartlist_len(router_get_trusted_dir_servers()) || + !smartlist_len(router_get_fallback_dir_servers()) || !old_options || + !config_lines_eq(options->DirAuthorities, old_options->DirAuthorities) || + !config_lines_eq(options->FallbackDir, old_options->FallbackDir) || + (options->UseDefaultFallbackDirs != old_options->UseDefaultFallbackDirs) || + !config_lines_eq(options->AlternateBridgeAuthority, + old_options->AlternateBridgeAuthority) || + !config_lines_eq(options->AlternateDirAuthority, + old_options->AlternateDirAuthority); + + if (!need_to_update) + return 0; /* all done */ + + /* "You cannot set both DirAuthority and Alternate*Authority." + * Checking that this restriction holds allows us to simplify + * the unit tests. */ + tor_assert(!(options->DirAuthorities && + (options->AlternateDirAuthority + || options->AlternateBridgeAuthority))); + + /* Start from a clean slate. */ + clear_dir_servers(); + + if (!options->DirAuthorities) { + /* then we may want some of the defaults */ + dirinfo_type_t type = NO_DIRINFO; + if (!options->AlternateBridgeAuthority) { + type |= BRIDGE_DIRINFO; + } + if (!options->AlternateDirAuthority) { + type |= V3_DIRINFO | EXTRAINFO_DIRINFO | MICRODESC_DIRINFO; + /* Only add the default fallback directories when the DirAuthorities, + * AlternateDirAuthority, and FallbackDir directory config options + * are set to their defaults, and when UseDefaultFallbackDirs is 1. */ + if (!options->FallbackDir && options->UseDefaultFallbackDirs) { + add_default_fallback_dir_servers(); + } + } + /* if type == NO_DIRINFO, we don't want to add any of the + * default authorities, because we've replaced them all */ + if (type != NO_DIRINFO) + add_default_trusted_dir_authorities(type); + } + + for (cl = options->DirAuthorities; cl; cl = cl->next) + if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0) + return -1; + for (cl = options->AlternateBridgeAuthority; cl; cl = cl->next) + if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0) + return -1; + for (cl = options->AlternateDirAuthority; cl; cl = cl->next) + if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0) + return -1; + for (cl = options->FallbackDir; cl; cl = cl->next) + if (parse_dir_fallback_line(cl->value, 0)<0) + return -1; + return 0; +} + +/** + * Make sure that <b>directory</b> exists, with appropriate ownership and + * permissions (as modified by <b>group_readable</b>). If <b>create</b>, + * create the directory if it is missing. Return 0 on success. + * On failure, return -1 and set *<b>msg_out</b>. + */ +static int +check_and_create_data_directory(int create, + const char *directory, + int group_readable, + const char *owner, + char **msg_out) +{ + cpd_check_t cpd_opts = create ? CPD_CREATE : CPD_CHECK; + if (group_readable) + cpd_opts |= CPD_GROUP_READ; + if (check_private_dir(directory, + cpd_opts, + owner) < 0) { + tor_asprintf(msg_out, + "Couldn't %s private data directory \"%s\"", + create ? "create" : "access", + directory); + return -1; + } + +#ifndef _WIN32 + if (group_readable) { + /* Only new dirs created get new opts, also enforce group read. */ + if (chmod(directory, 0750)) { + log_warn(LD_FS,"Unable to make %s group-readable: %s", + directory, strerror(errno)); + } + } +#endif /* !defined(_WIN32) */ + + return 0; +} + +/** + * Ensure that our keys directory exists, with appropriate permissions. + * Return 0 on success, -1 on failure. + */ +int +create_keys_directory(const or_options_t *options) +{ + /* Make sure DataDirectory exists, and is private. */ + cpd_check_t cpd_opts = CPD_CREATE; + if (options->DataDirectoryGroupReadable) + cpd_opts |= CPD_GROUP_READ; + if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { + log_err(LD_OR, "Can't create/check datadirectory %s", + options->DataDirectory); + return -1; + } + + /* Check the key directory. */ + if (check_private_dir(options->KeyDirectory, CPD_CREATE, options->User)) { + return -1; + } + return 0; +} + +/* Helps determine flags to pass to switch_id. */ +static int have_low_ports = -1; + +/** Fetch the active option list, and take actions based on it. All of the + * things we do should survive being done repeatedly. If present, + * <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if things went badly. + */ +static int +options_act_reversible(const or_options_t *old_options, char **msg) +{ + smartlist_t *new_listeners = smartlist_new(); + or_options_t *options = get_options_mutable(); + int running_tor = options->command == CMD_RUN_TOR; + int set_conn_limit = 0; + int r = -1; + int logs_marked = 0, logs_initialized = 0; + int old_min_log_level = get_min_log_level(); + + /* Daemonize _first_, since we only want to open most of this stuff in + * the subprocess. Libevent bases can't be reliably inherited across + * processes. */ + if (running_tor && options->RunAsDaemon) { + if (! start_daemon_has_been_called()) + crypto_prefork(); + /* No need to roll back, since you can't change the value. */ + if (start_daemon()) + crypto_postfork(); + } + +#ifdef HAVE_SYSTEMD + /* Our PID may have changed, inform supervisor */ + sd_notifyf(0, "MAINPID=%ld\n", (long int)getpid()); +#endif + +#ifndef HAVE_SYS_UN_H + if (options->ControlSocket || options->ControlSocketsGroupWritable) { + *msg = tor_strdup("Unix domain sockets (ControlSocket) not supported " + "on this OS/with this build."); + goto rollback; + } +#else /* !(!defined(HAVE_SYS_UN_H)) */ + if (options->ControlSocketsGroupWritable && !options->ControlSocket) { + *msg = tor_strdup("Setting ControlSocketGroupWritable without setting" + "a ControlSocket makes no sense."); + goto rollback; + } +#endif /* !defined(HAVE_SYS_UN_H) */ + + if (running_tor) { + int n_ports=0; + /* We need to set the connection limit before we can open the listeners. */ + if (! sandbox_is_active()) { + if (set_max_file_descriptors((unsigned)options->ConnLimit, + &options->ConnLimit_) < 0) { + *msg = tor_strdup("Problem with ConnLimit value. " + "See logs for details."); + goto rollback; + } + set_conn_limit = 1; + } else { + tor_assert(old_options); + options->ConnLimit_ = old_options->ConnLimit_; + } + + /* Set up libevent. (We need to do this before we can register the + * listeners as listeners.) */ + if (running_tor && !libevent_initialized) { + init_libevent(options); + libevent_initialized = 1; + + /* This has to come up after libevent is initialized. */ + control_initialize_event_queue(); + + /* + * Initialize the scheduler - this has to come after + * options_init_from_torrc() sets up libevent - why yes, that seems + * completely sensible to hide the libevent setup in the option parsing + * code! It also needs to happen before init_keys(), so it needs to + * happen here too. How yucky. */ + scheduler_init(); + } + + /* Adjust the port configuration so we can launch listeners. */ + if (parse_ports(options, 0, msg, &n_ports, NULL)) { + if (!*msg) + *msg = tor_strdup("Unexpected problem parsing port config"); + goto rollback; + } + + /* Set the hibernation state appropriately.*/ + consider_hibernation(time(NULL)); + + /* Launch the listeners. (We do this before we setuid, so we can bind to + * ports under 1024.) We don't want to rebind if we're hibernating or + * shutting down. If networking is disabled, this will close all but the + * control listeners, but disable those. */ + if (!we_are_hibernating()) { + if (retry_all_listeners(new_listeners, options->DisableNetwork) < 0) { + *msg = tor_strdup("Failed to bind one of the listener ports."); + goto rollback; + } + } + if (options->DisableNetwork) { + /* Aggressively close non-controller stuff, NOW */ + log_notice(LD_NET, "DisableNetwork is set. Tor will not make or accept " + "non-control network connections. Shutting down all existing " + "connections."); + connection_mark_all_noncontrol_connections(); + /* We can't complete circuits until the network is re-enabled. */ + note_that_we_maybe_cant_complete_circuits(); + } + } + +#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) + /* Open /dev/pf before dropping privileges. */ + if (options->TransPort_set && + options->TransProxyType_parsed == TPT_DEFAULT) { + if (get_pf_socket() < 0) { + *msg = tor_strdup("Unable to open /dev/pf for transparent proxy."); + goto rollback; + } + } +#endif /* defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) */ + + /* Attempt to lock all current and future memory with mlockall() only once */ + if (options->DisableAllSwap) { + if (tor_mlockall() == -1) { + *msg = tor_strdup("DisableAllSwap failure. Do you have proper " + "permissions?"); + goto done; + } + } + + /* Setuid/setgid as appropriate */ + if (options->User) { + tor_assert(have_low_ports != -1); + unsigned switch_id_flags = 0; + if (options->KeepBindCapabilities == 1) { + switch_id_flags |= SWITCH_ID_KEEP_BINDLOW; + switch_id_flags |= SWITCH_ID_WARN_IF_NO_CAPS; + } + if (options->KeepBindCapabilities == -1 && have_low_ports) { + switch_id_flags |= SWITCH_ID_KEEP_BINDLOW; + } + if (switch_id(options->User, switch_id_flags) != 0) { + /* No need to roll back, since you can't change the value. */ + *msg = tor_strdup("Problem with User value. See logs for details."); + goto done; + } + } + + /* Ensure data directory is private; create if possible. */ + /* It's okay to do this in "options_act_reversible()" even though it isn't + * actually reversible, since you can't change the DataDirectory while + * Tor is running. */ + if (check_and_create_data_directory(running_tor /* create */, + options->DataDirectory, + options->DataDirectoryGroupReadable, + options->User, + msg) < 0) { + goto done; + } + if (check_and_create_data_directory(running_tor /* create */, + options->KeyDirectory, + options->KeyDirectoryGroupReadable, + options->User, + msg) < 0) { + goto done; + } + + /* We need to handle the group-readable flag for the cache directory + * specially, since the directory defaults to being the same as the + * DataDirectory. */ + int cache_dir_group_readable; + if (options->CacheDirectoryGroupReadable != -1) { + /* If the user specified a value, use their setting */ + cache_dir_group_readable = options->CacheDirectoryGroupReadable; + } else if (!strcmp(options->CacheDirectory, options->DataDirectory)) { + /* If the user left the value as "auto", and the cache is the same as the + * datadirectory, use the datadirectory setting. + */ + cache_dir_group_readable = options->DataDirectoryGroupReadable; + } else { + /* Otherwise, "auto" means "not group readable". */ + cache_dir_group_readable = 0; + } + if (check_and_create_data_directory(running_tor /* create */, + options->CacheDirectory, + cache_dir_group_readable, + options->User, + msg) < 0) { + goto done; + } + + /* Bail out at this point if we're not going to be a client or server: + * we don't run Tor itself. */ + if (!running_tor) + goto commit; + + mark_logs_temp(); /* Close current logs once new logs are open. */ + logs_marked = 1; + /* Configure the tor_log(s) */ + if (options_init_logs(old_options, options, 0)<0) { + *msg = tor_strdup("Failed to init Log options. See logs for details."); + goto rollback; + } + logs_initialized = 1; + + commit: + r = 0; + if (logs_marked) { + log_severity_list_t *severity = + tor_malloc_zero(sizeof(log_severity_list_t)); + close_temp_logs(); + add_callback_log(severity, control_event_logmsg); + logs_set_pending_callback_callback(control_event_logmsg_pending); + control_adjust_event_log_severity(); + tor_free(severity); + tor_log_update_sigsafe_err_fds(); + } + if (logs_initialized) { + flush_log_messages_from_startup(); + } + + { + const char *badness = NULL; + int bad_safelog = 0, bad_severity = 0, new_badness = 0; + if (options->SafeLogging_ != SAFELOG_SCRUB_ALL) { + bad_safelog = 1; + if (!old_options || old_options->SafeLogging_ != options->SafeLogging_) + new_badness = 1; + } + if (get_min_log_level() >= LOG_INFO) { + bad_severity = 1; + if (get_min_log_level() != old_min_log_level) + new_badness = 1; + } + if (bad_safelog && bad_severity) + badness = "you disabled SafeLogging, and " + "you're logging more than \"notice\""; + else if (bad_safelog) + badness = "you disabled SafeLogging"; + else + badness = "you're logging more than \"notice\""; + if (new_badness) + log_warn(LD_GENERAL, "Your log may contain sensitive information - %s. " + "Don't log unless it serves an important reason. " + "Overwrite the log afterwards.", badness); + } + + if (set_conn_limit) { + /* + * If we adjusted the conn limit, recompute the OOS threshold too + * + * How many possible sockets to keep in reserve? If we have lots of + * possible sockets, keep this below a limit and set ConnLimit_high_thresh + * very close to ConnLimit_, but if ConnLimit_ is low, shrink it in + * proportion. + * + * Somewhat arbitrarily, set socks_in_reserve to 5% of ConnLimit_, but + * cap it at 64. + */ + int socks_in_reserve = options->ConnLimit_ / 20; + if (socks_in_reserve > 64) socks_in_reserve = 64; + + options->ConnLimit_high_thresh = options->ConnLimit_ - socks_in_reserve; + options->ConnLimit_low_thresh = (options->ConnLimit_ / 4) * 3; + log_info(LD_GENERAL, + "Recomputed OOS thresholds: ConnLimit %d, ConnLimit_ %d, " + "ConnLimit_high_thresh %d, ConnLimit_low_thresh %d", + options->ConnLimit, options->ConnLimit_, + options->ConnLimit_high_thresh, + options->ConnLimit_low_thresh); + + /* Give the OOS handler a chance with the new thresholds */ + connection_check_oos(get_n_open_sockets(), 0); + } + + goto done; + + rollback: + r = -1; + tor_assert(*msg); + + if (logs_marked) { + rollback_log_changes(); + control_adjust_event_log_severity(); + } + + if (set_conn_limit && old_options) + set_max_file_descriptors((unsigned)old_options->ConnLimit, + &options->ConnLimit_); + + SMARTLIST_FOREACH(new_listeners, connection_t *, conn, + { + log_notice(LD_NET, "Closing partially-constructed %s on %s:%d", + conn_type_to_string(conn->type), conn->address, conn->port); + connection_close_immediate(conn); + connection_mark_for_close(conn); + }); + + done: + smartlist_free(new_listeners); + return r; +} + +/** If we need to have a GEOIP ip-to-country map to run with our configured + * options, return 1 and set *<b>reason_out</b> to a description of why. */ +int +options_need_geoip_info(const or_options_t *options, const char **reason_out) +{ + int bridge_usage = should_record_bridge_info(options); + int routerset_usage = + routerset_needs_geoip(options->EntryNodes) || + routerset_needs_geoip(options->ExitNodes) || + routerset_needs_geoip(options->ExcludeExitNodes) || + routerset_needs_geoip(options->ExcludeNodes) || + routerset_needs_geoip(options->HSLayer2Nodes) || + routerset_needs_geoip(options->HSLayer3Nodes); + + if (routerset_usage && reason_out) { + *reason_out = "We've been configured to use (or avoid) nodes in certain " + "countries, and we need GEOIP information to figure out which ones they " + "are."; + } else if (bridge_usage && reason_out) { + *reason_out = "We've been configured to see which countries can access " + "us as a bridge, and we need GEOIP information to tell which countries " + "clients are in."; + } + return bridge_usage || routerset_usage; +} + +/** Return the bandwidthrate that we are going to report to the authorities + * based on the config options. */ +uint32_t +get_effective_bwrate(const or_options_t *options) +{ + uint64_t bw = options->BandwidthRate; + if (bw > options->MaxAdvertisedBandwidth) + bw = options->MaxAdvertisedBandwidth; + if (options->RelayBandwidthRate > 0 && bw > options->RelayBandwidthRate) + bw = options->RelayBandwidthRate; + /* ensure_bandwidth_cap() makes sure that this cast can't overflow. */ + return (uint32_t)bw; +} + +/** Return the bandwidthburst that we are going to report to the authorities + * based on the config options. */ +uint32_t +get_effective_bwburst(const or_options_t *options) +{ + uint64_t bw = options->BandwidthBurst; + if (options->RelayBandwidthBurst > 0 && bw > options->RelayBandwidthBurst) + bw = options->RelayBandwidthBurst; + /* ensure_bandwidth_cap() makes sure that this cast can't overflow. */ + return (uint32_t)bw; +} + +/* Used in the various options_transition_affects* functions. */ +#define YES_IF_CHANGED_BOOL(opt) \ + if (!CFG_EQ_BOOL(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_INT(opt) \ + if (!CFG_EQ_INT(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_STRING(opt) \ + if (!CFG_EQ_STRING(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_LINELIST(opt) \ + if (!CFG_EQ_LINELIST(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_SMARTLIST(opt) \ + if (!CFG_EQ_SMARTLIST(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_ROUTERSET(opt) \ + if (!CFG_EQ_ROUTERSET(old_options, new_options, opt)) return 1; + +/** + * Return true if changing the configuration from <b>old</b> to <b>new</b> + * affects the guard subsystem. + */ +static int +options_transition_affects_guards(const or_options_t *old_options, + const or_options_t *new_options) +{ + /* NOTE: Make sure this function stays in sync with + * node_passes_guard_filter */ + tor_assert(old_options); + tor_assert(new_options); + + YES_IF_CHANGED_BOOL(UseEntryGuards); + YES_IF_CHANGED_BOOL(UseBridges); + YES_IF_CHANGED_BOOL(ClientUseIPv4); + YES_IF_CHANGED_BOOL(ClientUseIPv6); + YES_IF_CHANGED_BOOL(FascistFirewall); + YES_IF_CHANGED_ROUTERSET(ExcludeNodes); + YES_IF_CHANGED_ROUTERSET(EntryNodes); + YES_IF_CHANGED_SMARTLIST(FirewallPorts); + YES_IF_CHANGED_LINELIST(Bridges); + YES_IF_CHANGED_LINELIST(ReachableORAddresses); + YES_IF_CHANGED_LINELIST(ReachableDirAddresses); + + return 0; +} + +/** + * Return true if changing the configuration from <b>old</b> to <b>new</b> + * affects the timing of the voting subsystem + */ +static int +options_transition_affects_dirauth_timing(const or_options_t *old_options, + const or_options_t *new_options) +{ + tor_assert(old_options); + tor_assert(new_options); + + if (authdir_mode_v3(old_options) != authdir_mode_v3(new_options)) + return 1; + if (! authdir_mode_v3(new_options)) + return 0; + YES_IF_CHANGED_INT(V3AuthVotingInterval); + YES_IF_CHANGED_INT(V3AuthVoteDelay); + YES_IF_CHANGED_INT(V3AuthDistDelay); + YES_IF_CHANGED_INT(TestingV3AuthInitialVotingInterval); + YES_IF_CHANGED_INT(TestingV3AuthInitialVoteDelay); + YES_IF_CHANGED_INT(TestingV3AuthInitialDistDelay); + YES_IF_CHANGED_INT(TestingV3AuthVotingStartOffset); + + return 0; +} + +/** Fetch the active option list, and take actions based on it. All of the + * things we do should survive being done repeatedly. If present, + * <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * here yet. Some is still in do_hup() and other places. + */ +STATIC int +options_act(const or_options_t *old_options) +{ + config_line_t *cl; + or_options_t *options = get_options_mutable(); + int running_tor = options->command == CMD_RUN_TOR; + char *msg=NULL; + const int transition_affects_workers = + old_options && options_transition_affects_workers(old_options, options); + const int transition_affects_guards = + old_options && options_transition_affects_guards(old_options, options); + + if (options->NoExec || options->Sandbox) { + tor_disable_spawning_background_processes(); + } + + /* disable ptrace and later, other basic debugging techniques */ + { + /* Remember if we already disabled debugger attachment */ + static int disabled_debugger_attach = 0; + /* Remember if we already warned about being configured not to disable + * debugger attachment */ + static int warned_debugger_attach = 0; + /* Don't disable debugger attachment when we're running the unit tests. */ + if (options->DisableDebuggerAttachment && !disabled_debugger_attach && + running_tor) { + int ok = tor_disable_debugger_attach(); + /* LCOV_EXCL_START the warned_debugger_attach is 0 can't reach inside. */ + if (warned_debugger_attach && ok == 1) { + log_notice(LD_CONFIG, "Disabled attaching debuggers for unprivileged " + "users."); + } + /* LCOV_EXCL_STOP */ + disabled_debugger_attach = (ok == 1); + } else if (!options->DisableDebuggerAttachment && + !warned_debugger_attach) { + log_notice(LD_CONFIG, "Not disabling debugger attaching for " + "unprivileged users."); + warned_debugger_attach = 1; + } + } + + /* Write control ports to disk as appropriate */ + control_ports_write_to_file(); + + if (running_tor && !have_lockfile()) { + if (try_locking(options, 1) < 0) + return -1; + } + + { + int warning_severity = options->ProtocolWarnings ? LOG_WARN : LOG_INFO; + set_protocol_warning_severity_level(warning_severity); + } + + if (consider_adding_dir_servers(options, old_options) < 0) { + // XXXX This should get validated earlier, and committed here, to + // XXXX lower opportunities for reaching an error case. + return -1; + } + + if (rend_non_anonymous_mode_enabled(options)) { + log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run " + "in a non-anonymous mode. It will provide NO ANONYMITY."); + } + + /* If we are a bridge with a pluggable transport proxy but no + Extended ORPort, inform the user that they are missing out. */ + if (server_mode(options) && options->ServerTransportPlugin && + !options->ExtORPort_lines) { + log_notice(LD_CONFIG, "We use pluggable transports but the Extended " + "ORPort is disabled. Tor and your pluggable transports proxy " + "communicate with each other via the Extended ORPort so it " + "is suggested you enable it: it will also allow your Bridge " + "to collect statistics about its clients that use pluggable " + "transports. Please enable it using the ExtORPort torrc option " + "(e.g. set 'ExtORPort auto')."); + } + + if (options->Bridges) { + mark_bridge_list(); + for (cl = options->Bridges; cl; cl = cl->next) { + bridge_line_t *bridge_line = parse_bridge_line(cl->value); + if (!bridge_line) { + // LCOV_EXCL_START + log_warn(LD_BUG, + "Previously validated Bridge line could not be added!"); + return -1; + // LCOV_EXCL_STOP + } + bridge_add_from_config(bridge_line); + } + sweep_bridge_list(); + } + + if (running_tor && hs_config_service_all(options, 0)<0) { + // LCOV_EXCL_START + log_warn(LD_BUG, + "Previously validated hidden services line could not be added!"); + return -1; + // LCOV_EXCL_STOP + } + + if (running_tor && hs_config_client_auth_all(options, 0) < 0) { + // LCOV_EXCL_START + log_warn(LD_BUG, "Previously validated client authorization for " + "hidden services could not be added!"); + return -1; + // LCOV_EXCL_STOP + } + + if (running_tor && !old_options && + options->OwningControllerFD != UINT64_MAX) { + const unsigned ctrl_flags = + CC_LOCAL_FD_IS_OWNER | + CC_LOCAL_FD_IS_AUTHENTICATED; + tor_socket_t ctrl_sock = (tor_socket_t)options->OwningControllerFD; + if (control_connection_add_local_fd(ctrl_sock, ctrl_flags) < 0) { + log_warn(LD_CONFIG, "Could not add local controller connection with " + "given FD."); + return -1; + } + } + + /* Load state */ + if (! or_state_loaded() && running_tor) { + if (or_state_load()) + return -1; + rep_hist_load_mtbf_data(time(NULL)); + } + + /* If we have an ExtORPort, initialize its auth cookie. */ + if (running_tor && + init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) { + log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file."); + return -1; + } + + mark_transport_list(); + pt_prepare_proxy_list_for_config_read(); + if (!options->DisableNetwork) { + if (options->ClientTransportPlugin) { + for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { + if (parse_transport_line(options, cl->value, 0, 0) < 0) { + // LCOV_EXCL_START + log_warn(LD_BUG, + "Previously validated ClientTransportPlugin line " + "could not be added!"); + return -1; + // LCOV_EXCL_STOP + } + } + } + + if (options->ServerTransportPlugin && server_mode(options)) { + for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { + if (parse_transport_line(options, cl->value, 0, 1) < 0) { + // LCOV_EXCL_START + log_warn(LD_BUG, + "Previously validated ServerTransportPlugin line " + "could not be added!"); + return -1; + // LCOV_EXCL_STOP + } + } + } + } + sweep_transport_list(); + sweep_proxy_list(); + + /* Start the PT proxy configuration. By doing this configuration + here, we also figure out which proxies need to be restarted and + which not. */ + if (pt_proxies_configuration_pending() && !net_is_disabled()) + pt_configure_remaining_proxies(); + + /* Bail out at this point if we're not going to be a client or server: + * we want to not fork, and to log stuff to stderr. */ + if (!running_tor) + return 0; + + /* Finish backgrounding the process */ + if (options->RunAsDaemon) { + /* We may be calling this for the n'th time (on SIGHUP), but it's safe. */ + finish_daemon(options->DataDirectory); + } + + /* See whether we need to enable/disable our once-a-second timer. */ + reschedule_per_second_timer(); + + /* We want to reinit keys as needed before we do much of anything else: + keys are important, and other things can depend on them. */ + if (transition_affects_workers || + (options->V3AuthoritativeDir && (!old_options || + !old_options->V3AuthoritativeDir))) { + if (init_keys() < 0) { + log_warn(LD_BUG,"Error initializing keys; exiting"); + return -1; + } + } + + /* Write our PID to the PID file. If we do not have write permissions we + * will log a warning and exit. */ + if (options->PidFile && !sandbox_is_active()) { + if (write_pidfile(options->PidFile) < 0) { + log_err(LD_CONFIG, "Unable to write PIDFile %s", + escaped(options->PidFile)); + return -1; + } + } + + /* Register addressmap directives */ + config_register_addressmaps(options); + parse_virtual_addr_network(options->VirtualAddrNetworkIPv4, AF_INET,0,NULL); + parse_virtual_addr_network(options->VirtualAddrNetworkIPv6, AF_INET6,0,NULL); + + /* Update address policies. */ + if (policies_parse_from_options(options) < 0) { + /* This should be impossible, but let's be sure. */ + log_warn(LD_BUG,"Error parsing already-validated policy options."); + return -1; + } + + if (server_mode(options)) { + static int cdm_initialized = 0; + if (cdm_initialized == 0) { + cdm_initialized = 1; + consdiffmgr_configure(NULL); + consdiffmgr_validate(); + } + } + + if (init_control_cookie_authentication(options->CookieAuthentication) < 0) { + log_warn(LD_CONFIG,"Error creating control cookie authentication file."); + return -1; + } + + monitor_owning_controller_process(options->OwningControllerProcess); + + /* reload keys as needed for rendezvous services. */ + if (hs_service_load_all_keys() < 0) { + log_warn(LD_GENERAL,"Error loading rendezvous service keys"); + return -1; + } + + /* Inform the scheduler subsystem that a configuration changed happened. It + * might be a change of scheduler or parameter. */ + scheduler_conf_changed(); + + /* Set up accounting */ + if (accounting_parse_options(options, 0)<0) { + // LCOV_EXCL_START + log_warn(LD_BUG,"Error in previously validated accounting options"); + return -1; + // LCOV_EXCL_STOP + } + if (accounting_is_enabled(options)) + configure_accounting(time(NULL)); + + /* Change the cell EWMA settings */ + cmux_ewma_set_options(options, networkstatus_get_latest_consensus()); + + /* Update the BridgePassword's hashed version as needed. We store this as a + * digest so that we can do side-channel-proof comparisons on it. + */ + if (options->BridgePassword) { + char *http_authenticator; + http_authenticator = alloc_http_authenticator(options->BridgePassword); + if (!http_authenticator) { + // XXXX This should get validated in options_validate(). + log_warn(LD_BUG, "Unable to allocate HTTP authenticator. Not setting " + "BridgePassword."); + return -1; + } + options->BridgePassword_AuthDigest_ = tor_malloc(DIGEST256_LEN); + crypto_digest256(options->BridgePassword_AuthDigest_, + http_authenticator, strlen(http_authenticator), + DIGEST_SHA256); + tor_free(http_authenticator); + } + + if (parse_outbound_addresses(options, 0, &msg) < 0) { + // LCOV_EXCL_START + log_warn(LD_BUG, "Failed parsing previously validated outbound " + "bind addresses: %s", msg); + tor_free(msg); + return -1; + // LCOV_EXCL_STOP + } + + config_maybe_load_geoip_files_(options, old_options); + + if (geoip_is_loaded(AF_INET) && options->GeoIPExcludeUnknown) { + /* ExcludeUnknown is true or "auto" */ + const int is_auto = options->GeoIPExcludeUnknown == -1; + int changed; + + changed = routerset_add_unknown_ccs(&options->ExcludeNodes, is_auto); + changed += routerset_add_unknown_ccs(&options->ExcludeExitNodes, is_auto); + + if (changed) + routerset_add_unknown_ccs(&options->ExcludeExitNodesUnion_, is_auto); + } + + /* Check for transitions that need action. */ + if (old_options) { + int revise_trackexithosts = 0; + int revise_automap_entries = 0; + int abandon_circuits = 0; + if ((options->UseEntryGuards && !old_options->UseEntryGuards) || + options->UseBridges != old_options->UseBridges || + (options->UseBridges && + !config_lines_eq(options->Bridges, old_options->Bridges)) || + !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes) || + !routerset_equal(old_options->ExcludeExitNodes, + options->ExcludeExitNodes) || + !routerset_equal(old_options->EntryNodes, options->EntryNodes) || + !routerset_equal(old_options->ExitNodes, options->ExitNodes) || + !routerset_equal(old_options->HSLayer2Nodes, + options->HSLayer2Nodes) || + !routerset_equal(old_options->HSLayer3Nodes, + options->HSLayer3Nodes) || + options->StrictNodes != old_options->StrictNodes) { + log_info(LD_CIRC, + "Changed to using entry guards or bridges, or changed " + "preferred or excluded node lists. " + "Abandoning previous circuits."); + abandon_circuits = 1; + } + + if (transition_affects_guards) { + if (guards_update_all()) { + abandon_circuits = 1; + } + } + + if (abandon_circuits) { + circuit_mark_all_unused_circs(); + circuit_mark_all_dirty_circs_as_unusable(); + revise_trackexithosts = 1; + } + + if (!smartlist_strings_eq(old_options->TrackHostExits, + options->TrackHostExits)) + revise_trackexithosts = 1; + + if (revise_trackexithosts) + addressmap_clear_excluded_trackexithosts(options); + + if (!options->AutomapHostsOnResolve && + old_options->AutomapHostsOnResolve) { + revise_automap_entries = 1; + } else { + if (!smartlist_strings_eq(old_options->AutomapHostsSuffixes, + options->AutomapHostsSuffixes)) + revise_automap_entries = 1; + else if (!opt_streq(old_options->VirtualAddrNetworkIPv4, + options->VirtualAddrNetworkIPv4) || + !opt_streq(old_options->VirtualAddrNetworkIPv6, + options->VirtualAddrNetworkIPv6)) + revise_automap_entries = 1; + } + + if (revise_automap_entries) + addressmap_clear_invalid_automaps(options); + +/* How long should we delay counting bridge stats after becoming a bridge? + * We use this so we don't count clients who used our bridge thinking it is + * a relay. If you change this, don't forget to change the log message + * below. It's 4 hours (the time it takes to stop being used by clients) + * plus some extra time for clock skew. */ +#define RELAY_BRIDGE_STATS_DELAY (6 * 60 * 60) + + if (! bool_eq(options->BridgeRelay, old_options->BridgeRelay)) { + int was_relay = 0; + if (options->BridgeRelay) { + time_t int_start = time(NULL); + if (config_lines_eq(old_options->ORPort_lines,options->ORPort_lines)) { + int_start += RELAY_BRIDGE_STATS_DELAY; + was_relay = 1; + } + geoip_bridge_stats_init(int_start); + log_info(LD_CONFIG, "We are acting as a bridge now. Starting new " + "GeoIP stats interval%s.", was_relay ? " in 6 " + "hours from now" : ""); + } else { + geoip_bridge_stats_term(); + log_info(LD_GENERAL, "We are no longer acting as a bridge. " + "Forgetting GeoIP stats."); + } + } + + if (transition_affects_workers) { + log_info(LD_GENERAL, + "Worker-related options changed. Rotating workers."); + const int server_mode_turned_on = + server_mode(options) && !server_mode(old_options); + const int dir_server_mode_turned_on = + dir_server_mode(options) && !dir_server_mode(old_options); + + if (server_mode_turned_on || dir_server_mode_turned_on) { + cpu_init(); + } + + if (server_mode_turned_on) { + ip_address_changed(0); + if (have_completed_a_circuit() || !any_predicted_circuits(time(NULL))) + inform_testing_reachability(); + } + cpuworkers_rotate_keyinfo(); + if (dns_reset()) + return -1; + } else { + if (dns_reset()) + return -1; + } + + if (options->PerConnBWRate != old_options->PerConnBWRate || + options->PerConnBWBurst != old_options->PerConnBWBurst) + connection_or_update_token_buckets(get_connection_array(), options); + + if (options->BandwidthRate != old_options->BandwidthRate || + options->BandwidthBurst != old_options->BandwidthBurst || + options->RelayBandwidthRate != old_options->RelayBandwidthRate || + options->RelayBandwidthBurst != old_options->RelayBandwidthBurst) + connection_bucket_adjust(options); + + if (options->MainloopStats != old_options->MainloopStats) { + reset_main_loop_counters(); + } + } + + /* Only collect directory-request statistics on relays and bridges. */ + options->DirReqStatistics = options->DirReqStatistics_option && + server_mode(options); + options->HiddenServiceStatistics = + options->HiddenServiceStatistics_option && server_mode(options); + + if (options->CellStatistics || options->DirReqStatistics || + options->EntryStatistics || options->ExitPortStatistics || + options->ConnDirectionStatistics || + options->HiddenServiceStatistics || + options->BridgeAuthoritativeDir) { + time_t now = time(NULL); + int print_notice = 0; + + /* Only collect other relay-only statistics on relays. */ + if (!public_server_mode(options)) { + options->CellStatistics = 0; + options->EntryStatistics = 0; + options->ConnDirectionStatistics = 0; + options->ExitPortStatistics = 0; + } + + if ((!old_options || !old_options->CellStatistics) && + options->CellStatistics) { + rep_hist_buffer_stats_init(now); + print_notice = 1; + } + if ((!old_options || !old_options->DirReqStatistics) && + options->DirReqStatistics) { + if (geoip_is_loaded(AF_INET)) { + geoip_dirreq_stats_init(now); + print_notice = 1; + } else { + /* disable statistics collection since we have no geoip file */ + options->DirReqStatistics = 0; + if (options->ORPort_set) + log_notice(LD_CONFIG, "Configured to measure directory request " + "statistics, but no GeoIP database found. " + "Please specify a GeoIP database using the " + "GeoIPFile option."); + } + } + if ((!old_options || !old_options->EntryStatistics) && + options->EntryStatistics && !should_record_bridge_info(options)) { + /* If we get here, we've started recording bridge info when we didn't + * do so before. Note that "should_record_bridge_info()" will + * always be false at this point, because of the earlier block + * that cleared EntryStatistics when public_server_mode() was false. + * We're leaving it in as defensive programming. */ + if (geoip_is_loaded(AF_INET) || geoip_is_loaded(AF_INET6)) { + geoip_entry_stats_init(now); + print_notice = 1; + } else { + options->EntryStatistics = 0; + log_notice(LD_CONFIG, "Configured to measure entry node " + "statistics, but no GeoIP database found. " + "Please specify a GeoIP database using the " + "GeoIPFile option."); + } + } + if ((!old_options || !old_options->ExitPortStatistics) && + options->ExitPortStatistics) { + rep_hist_exit_stats_init(now); + print_notice = 1; + } + if ((!old_options || !old_options->ConnDirectionStatistics) && + options->ConnDirectionStatistics) { + rep_hist_conn_stats_init(now); + } + if ((!old_options || !old_options->HiddenServiceStatistics) && + options->HiddenServiceStatistics) { + log_info(LD_CONFIG, "Configured to measure hidden service statistics."); + rep_hist_hs_stats_init(now); + } + if ((!old_options || !old_options->BridgeAuthoritativeDir) && + options->BridgeAuthoritativeDir) { + rep_hist_desc_stats_init(now); + print_notice = 1; + } + if (print_notice) + log_notice(LD_CONFIG, "Configured to measure statistics. Look for " + "the *-stats files that will first be written to the " + "data directory in 24 hours from now."); + } + + /* If we used to have statistics enabled but we just disabled them, + stop gathering them. */ + if (old_options && old_options->CellStatistics && + !options->CellStatistics) + rep_hist_buffer_stats_term(); + if (old_options && old_options->DirReqStatistics && + !options->DirReqStatistics) + geoip_dirreq_stats_term(); + if (old_options && old_options->EntryStatistics && + !options->EntryStatistics) + geoip_entry_stats_term(); + if (old_options && old_options->HiddenServiceStatistics && + !options->HiddenServiceStatistics) + rep_hist_hs_stats_term(); + if (old_options && old_options->ExitPortStatistics && + !options->ExitPortStatistics) + rep_hist_exit_stats_term(); + if (old_options && old_options->ConnDirectionStatistics && + !options->ConnDirectionStatistics) + rep_hist_conn_stats_term(); + if (old_options && old_options->BridgeAuthoritativeDir && + !options->BridgeAuthoritativeDir) + rep_hist_desc_stats_term(); + + /* Since our options changed, we might need to regenerate and upload our + * server descriptor. + */ + if (!old_options || + options_transition_affects_descriptor(old_options, options)) + mark_my_descriptor_dirty("config change"); + + /* We may need to reschedule some directory stuff if our status changed. */ + if (old_options) { + if (options_transition_affects_dirauth_timing(old_options, options)) { + voting_schedule_recalculate_timing(options, time(NULL)); + reschedule_dirvote(options); + } + if (!bool_eq(directory_fetches_dir_info_early(options), + directory_fetches_dir_info_early(old_options)) || + !bool_eq(directory_fetches_dir_info_later(options), + directory_fetches_dir_info_later(old_options)) || + !config_lines_eq(old_options->Bridges, options->Bridges)) { + /* Make sure update_router_have_minimum_dir_info() gets called. */ + router_dir_info_changed(); + /* We might need to download a new consensus status later or sooner than + * we had expected. */ + update_consensus_networkstatus_fetch_time(time(NULL)); + } + } + + /* DoS mitigation subsystem only applies to public relay. */ + if (public_server_mode(options)) { + /* If we are configured as a relay, initialize the subsystem. Even on HUP, + * this is safe to call as it will load data from the current options + * or/and the consensus. */ + dos_init(); + } else if (old_options && public_server_mode(old_options)) { + /* Going from relay to non relay, clean it up. */ + dos_free_all(); + } + + /* Load the webpage we're going to serve every time someone asks for '/' on + our DirPort. */ + tor_free(global_dirfrontpagecontents); + if (options->DirPortFrontPage) { + global_dirfrontpagecontents = + read_file_to_str(options->DirPortFrontPage, 0, NULL); + if (!global_dirfrontpagecontents) { + log_warn(LD_CONFIG, + "DirPortFrontPage file '%s' not found. Continuing anyway.", + options->DirPortFrontPage); + } + } + + return 0; +} + +typedef enum { + TAKES_NO_ARGUMENT = 0, + ARGUMENT_NECESSARY = 1, + ARGUMENT_OPTIONAL = 2 +} takes_argument_t; + +static const struct { + const char *name; + takes_argument_t takes_argument; +} CMDLINE_ONLY_OPTIONS[] = { + { "-f", ARGUMENT_NECESSARY }, + { "--allow-missing-torrc", TAKES_NO_ARGUMENT }, + { "--defaults-torrc", ARGUMENT_NECESSARY }, + { "--hash-password", ARGUMENT_NECESSARY }, + { "--dump-config", ARGUMENT_OPTIONAL }, + { "--list-fingerprint", TAKES_NO_ARGUMENT }, + { "--keygen", TAKES_NO_ARGUMENT }, + { "--key-expiration", ARGUMENT_OPTIONAL }, + { "--newpass", TAKES_NO_ARGUMENT }, + { "--no-passphrase", TAKES_NO_ARGUMENT }, + { "--passphrase-fd", ARGUMENT_NECESSARY }, + { "--verify-config", TAKES_NO_ARGUMENT }, + { "--ignore-missing-torrc", TAKES_NO_ARGUMENT }, + { "--quiet", TAKES_NO_ARGUMENT }, + { "--hush", TAKES_NO_ARGUMENT }, + { "--version", TAKES_NO_ARGUMENT }, + { "--list-modules", TAKES_NO_ARGUMENT }, + { "--library-versions", TAKES_NO_ARGUMENT }, + { "-h", TAKES_NO_ARGUMENT }, + { "--help", TAKES_NO_ARGUMENT }, + { "--list-torrc-options", TAKES_NO_ARGUMENT }, + { "--list-deprecated-options",TAKES_NO_ARGUMENT }, + { "--nt-service", TAKES_NO_ARGUMENT }, + { "-nt-service", TAKES_NO_ARGUMENT }, + { NULL, 0 }, +}; + +/** Helper: Read a list of configuration options from the command line. If + * successful, or if ignore_errors is set, put them in *<b>result</b>, put the + * commandline-only options in *<b>cmdline_result</b>, and return 0; + * otherwise, return -1 and leave *<b>result</b> and <b>cmdline_result</b> + * alone. */ +int +config_parse_commandline(int argc, char **argv, int ignore_errors, + config_line_t **result, + config_line_t **cmdline_result) +{ + config_line_t *param = NULL; + + config_line_t *front = NULL; + config_line_t **new = &front; + + config_line_t *front_cmdline = NULL; + config_line_t **new_cmdline = &front_cmdline; + + char *s, *arg; + int i = 1; + + while (i < argc) { + unsigned command = CONFIG_LINE_NORMAL; + takes_argument_t want_arg = ARGUMENT_NECESSARY; + int is_cmdline = 0; + int j; + + for (j = 0; CMDLINE_ONLY_OPTIONS[j].name != NULL; ++j) { + if (!strcmp(argv[i], CMDLINE_ONLY_OPTIONS[j].name)) { + is_cmdline = 1; + want_arg = CMDLINE_ONLY_OPTIONS[j].takes_argument; + break; + } + } + + s = argv[i]; + + /* Each keyword may be prefixed with one or two dashes. */ + if (*s == '-') + s++; + if (*s == '-') + s++; + /* Figure out the command, if any. */ + if (*s == '+') { + s++; + command = CONFIG_LINE_APPEND; + } else if (*s == '/') { + s++; + command = CONFIG_LINE_CLEAR; + /* A 'clear' command has no argument. */ + want_arg = 0; + } + + const int is_last = (i == argc-1); + + if (want_arg == ARGUMENT_NECESSARY && is_last) { + if (ignore_errors) { + arg = tor_strdup(""); + } else { + log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", + argv[i]); + config_free_lines(front); + config_free_lines(front_cmdline); + return -1; + } + } else if (want_arg == ARGUMENT_OPTIONAL && is_last) { + arg = tor_strdup(""); + } else { + arg = (want_arg != TAKES_NO_ARGUMENT) ? tor_strdup(argv[i+1]) : + tor_strdup(""); + } + + param = tor_malloc_zero(sizeof(config_line_t)); + param->key = is_cmdline ? tor_strdup(argv[i]) : + tor_strdup(config_expand_abbrev(&options_format, s, 1, 1)); + param->value = arg; + param->command = command; + param->next = NULL; + log_debug(LD_CONFIG, "command line: parsed keyword '%s', value '%s'", + param->key, param->value); + + if (is_cmdline) { + *new_cmdline = param; + new_cmdline = &((*new_cmdline)->next); + } else { + *new = param; + new = &((*new)->next); + } + + i += want_arg ? 2 : 1; + } + *cmdline_result = front_cmdline; + *result = front; + return 0; +} + +/** Return true iff key is a valid configuration option. */ +int +option_is_recognized(const char *key) +{ + const config_var_t *var = config_find_option(&options_format, key); + return (var != NULL); +} + +/** Return the canonical name of a configuration option, or NULL + * if no such option exists. */ +const char * +option_get_canonical_name(const char *key) +{ + const config_var_t *var = config_find_option(&options_format, key); + return var ? var->name : NULL; +} + +/** Return a canonical list of the options assigned for key. + */ +config_line_t * +option_get_assignment(const or_options_t *options, const char *key) +{ + return config_get_assigned_option(&options_format, options, key, 1); +} + +/** Try assigning <b>list</b> to the global options. You do this by duping + * options, assigning list to the new one, then validating it. If it's + * ok, then throw out the old one and stick with the new one. Else, + * revert to old and return failure. Return SETOPT_OK on success, or + * a setopt_err_t on failure. + * + * If not success, point *<b>msg</b> to a newly allocated string describing + * what went wrong. + */ +setopt_err_t +options_trial_assign(config_line_t *list, unsigned flags, char **msg) +{ + int r; + or_options_t *trial_options = config_dup(&options_format, get_options()); + + if ((r=config_assign(&options_format, trial_options, + list, flags, msg)) < 0) { + or_options_free(trial_options); + return r; + } + + setopt_err_t rv; + or_options_t *cur_options = get_options_mutable(); + + in_option_validation = 1; + + if (options_validate(cur_options, trial_options, + global_default_options, 1, msg) < 0) { + or_options_free(trial_options); + rv = SETOPT_ERR_PARSE; /*XXX make this a separate return value. */ + goto done; + } + + if (options_transition_allowed(cur_options, trial_options, msg) < 0) { + or_options_free(trial_options); + rv = SETOPT_ERR_TRANSITION; + goto done; + } + in_option_validation = 0; + + if (set_options(trial_options, msg)<0) { + or_options_free(trial_options); + rv = SETOPT_ERR_SETTING; + goto done; + } + + /* we liked it. put it in place. */ + rv = SETOPT_OK; + done: + in_option_validation = 0; + return rv; +} + +/** Print a usage message for tor. */ +static void +print_usage(void) +{ + printf( +"Copyright (c) 2001-2004, Roger Dingledine\n" +"Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson\n" +"Copyright (c) 2007-2019, The Tor Project, Inc.\n\n" +"tor -f <torrc> [args]\n" +"See man page for options, or https://www.torproject.org/ for " +"documentation.\n"); +} + +/** Print all non-obsolete torrc options. */ +static void +list_torrc_options(void) +{ + int i; + for (i = 0; option_vars_[i].name; ++i) { + const config_var_t *var = &option_vars_[i]; + if (var->type == CONFIG_TYPE_OBSOLETE || + var->type == CONFIG_TYPE_LINELIST_V) + continue; + printf("%s\n", var->name); + } +} + +/** Print all deprecated but non-obsolete torrc options. */ +static void +list_deprecated_options(void) +{ + const config_deprecation_t *d; + for (d = option_deprecation_notes_; d->name; ++d) { + printf("%s\n", d->name); + } +} + +/** Print all compile-time modules and their enabled/disabled status. */ +static void +list_enabled_modules(void) +{ + printf("%s: %s\n", "dirauth", have_module_dirauth() ? "yes" : "no"); +} + +/** Last value actually set by resolve_my_address. */ +static uint32_t last_resolved_addr = 0; + +/** Accessor for last_resolved_addr from outside this file. */ +uint32_t +get_last_resolved_addr(void) +{ + return last_resolved_addr; +} + +/** Reset last_resolved_addr from outside this file. */ +void +reset_last_resolved_addr(void) +{ + last_resolved_addr = 0; +} + +/* Return true if <b>options</b> is using the default authorities, and false + * if any authority-related option has been overridden. */ +int +using_default_dir_authorities(const or_options_t *options) +{ + return (!options->DirAuthorities && !options->AlternateDirAuthority); +} + +/** + * Attempt getting our non-local (as judged by tor_addr_is_internal() + * function) IP address using following techniques, listed in + * order from best (most desirable, try first) to worst (least + * desirable, try if everything else fails). + * + * First, attempt using <b>options-\>Address</b> to get our + * non-local IP address. + * + * If <b>options-\>Address</b> represents a non-local IP address, + * consider it ours. + * + * If <b>options-\>Address</b> is a DNS name that resolves to + * a non-local IP address, consider this IP address ours. + * + * If <b>options-\>Address</b> is NULL, fall back to getting local + * hostname and using it in above-described ways to try and + * get our IP address. + * + * In case local hostname cannot be resolved to a non-local IP + * address, try getting an IP address of network interface + * in hopes it will be non-local one. + * + * Fail if one or more of the following is true: + * - DNS name in <b>options-\>Address</b> cannot be resolved. + * - <b>options-\>Address</b> is a local host address. + * - Attempt at getting local hostname fails. + * - Attempt at getting network interface address fails. + * + * Return 0 if all is well, or -1 if we can't find a suitable + * public IP address. + * + * If we are returning 0: + * - Put our public IP address (in host order) into *<b>addr_out</b>. + * - If <b>method_out</b> is non-NULL, set *<b>method_out</b> to a static + * string describing how we arrived at our answer. + * - "CONFIGURED" - parsed from IP address string in + * <b>options-\>Address</b> + * - "RESOLVED" - resolved from DNS name in <b>options-\>Address</b> + * - "GETHOSTNAME" - resolved from a local hostname. + * - "INTERFACE" - retrieved from a network interface. + * - If <b>hostname_out</b> is non-NULL, and we resolved a hostname to + * get our address, set *<b>hostname_out</b> to a newly allocated string + * holding that hostname. (If we didn't get our address by resolving a + * hostname, set *<b>hostname_out</b> to NULL.) + * + * XXXX ipv6 + */ +int +resolve_my_address(int warn_severity, const or_options_t *options, + uint32_t *addr_out, + const char **method_out, char **hostname_out) +{ + struct in_addr in; + uint32_t addr; /* host order */ + char hostname[256]; + const char *method_used; + const char *hostname_used; + int explicit_ip=1; + int explicit_hostname=1; + int from_interface=0; + char *addr_string = NULL; + const char *address = options->Address; + int notice_severity = warn_severity <= LOG_NOTICE ? + LOG_NOTICE : warn_severity; + + tor_addr_t myaddr; + tor_assert(addr_out); + + /* + * Step one: Fill in 'hostname' to be our best guess. + */ + + if (address && *address) { + strlcpy(hostname, address, sizeof(hostname)); + } else { /* then we need to guess our address */ + explicit_ip = 0; /* it's implicit */ + explicit_hostname = 0; /* it's implicit */ + + if (tor_gethostname(hostname, sizeof(hostname)) < 0) { + log_fn(warn_severity, LD_NET,"Error obtaining local hostname"); + return -1; + } + log_debug(LD_CONFIG, "Guessed local host name as '%s'", hostname); + } + + /* + * Step two: Now that we know 'hostname', parse it or resolve it. If + * it doesn't parse or resolve, look at the interface address. Set 'addr' + * to be our (host-order) 32-bit answer. + */ + + if (tor_inet_aton(hostname, &in) == 0) { + /* then we have to resolve it */ + explicit_ip = 0; + if (tor_lookup_hostname(hostname, &addr)) { /* failed to resolve */ + uint32_t interface_ip; /* host order */ + + if (explicit_hostname) { + log_fn(warn_severity, LD_CONFIG, + "Could not resolve local Address '%s'. Failing.", hostname); + return -1; + } + log_fn(notice_severity, LD_CONFIG, + "Could not resolve guessed local hostname '%s'. " + "Trying something else.", hostname); + if (get_interface_address(warn_severity, &interface_ip)) { + log_fn(warn_severity, LD_CONFIG, + "Could not get local interface IP address. Failing."); + return -1; + } + from_interface = 1; + addr = interface_ip; + log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for " + "local interface. Using that.", fmt_addr32(addr)); + strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname)); + } else { /* resolved hostname into addr */ + tor_addr_from_ipv4h(&myaddr, addr); + + if (!explicit_hostname && + tor_addr_is_internal(&myaddr, 0)) { + tor_addr_t interface_ip; + + log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' " + "resolves to a private IP address (%s). Trying something " + "else.", hostname, fmt_addr32(addr)); + + if (get_interface_address6(warn_severity, AF_INET, &interface_ip)<0) { + log_fn(warn_severity, LD_CONFIG, + "Could not get local interface IP address. Too bad."); + } else if (tor_addr_is_internal(&interface_ip, 0)) { + log_fn(notice_severity, LD_CONFIG, + "Interface IP address '%s' is a private address too. " + "Ignoring.", fmt_addr(&interface_ip)); + } else { + from_interface = 1; + addr = tor_addr_to_ipv4h(&interface_ip); + log_fn(notice_severity, LD_CONFIG, + "Learned IP address '%s' for local interface." + " Using that.", fmt_addr32(addr)); + strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname)); + } + } + } + } else { + addr = ntohl(in.s_addr); /* set addr so that addr_string is not + * illformed */ + } + + /* + * Step three: Check whether 'addr' is an internal IP address, and error + * out if it is and we don't want that. + */ + + tor_addr_from_ipv4h(&myaddr,addr); + + addr_string = tor_dup_ip(addr); + if (tor_addr_is_internal(&myaddr, 0)) { + /* make sure we're ok with publishing an internal IP */ + if (using_default_dir_authorities(options)) { + /* if they are using the default authorities, disallow internal IPs + * always. */ + log_fn(warn_severity, LD_CONFIG, + "Address '%s' resolves to private IP address '%s'. " + "Tor servers that use the default DirAuthorities must have " + "public IP addresses.", hostname, addr_string); + tor_free(addr_string); + return -1; + } + if (!explicit_ip) { + /* even if they've set their own authorities, require an explicit IP if + * they're using an internal address. */ + log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private " + "IP address '%s'. Please set the Address config option to be " + "the IP address you want to use.", hostname, addr_string); + tor_free(addr_string); + return -1; + } + } + + /* + * Step four: We have a winner! 'addr' is our answer for sure, and + * 'addr_string' is its string form. Fill out the various fields to + * say how we decided it. + */ + + log_debug(LD_CONFIG, "Resolved Address to '%s'.", addr_string); + + if (explicit_ip) { + method_used = "CONFIGURED"; + hostname_used = NULL; + } else if (explicit_hostname) { + method_used = "RESOLVED"; + hostname_used = hostname; + } else if (from_interface) { + method_used = "INTERFACE"; + hostname_used = NULL; + } else { + method_used = "GETHOSTNAME"; + hostname_used = hostname; + } + + *addr_out = addr; + if (method_out) + *method_out = method_used; + if (hostname_out) + *hostname_out = hostname_used ? tor_strdup(hostname_used) : NULL; + + /* + * Step five: Check if the answer has changed since last time (or if + * there was no last time), and if so call various functions to keep + * us up-to-date. + */ + + if (last_resolved_addr && last_resolved_addr != *addr_out) { + /* Leave this as a notice, regardless of the requested severity, + * at least until dynamic IP address support becomes bulletproof. */ + log_notice(LD_NET, + "Your IP address seems to have changed to %s " + "(METHOD=%s%s%s). Updating.", + addr_string, method_used, + hostname_used ? " HOSTNAME=" : "", + hostname_used ? hostname_used : ""); + ip_address_changed(0); + } + + if (last_resolved_addr != *addr_out) { + control_event_server_status(LOG_NOTICE, + "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s%s%s", + addr_string, method_used, + hostname_used ? " HOSTNAME=" : "", + hostname_used ? hostname_used : ""); + } + last_resolved_addr = *addr_out; + + /* + * And finally, clean up and return success. + */ + + tor_free(addr_string); + return 0; +} + +/** Return true iff <b>addr</b> is judged to be on the same network as us, or + * on a private network. + */ +MOCK_IMPL(int, +is_local_addr, (const tor_addr_t *addr)) +{ + if (tor_addr_is_internal(addr, 0)) + return 1; + /* Check whether ip is on the same /24 as we are. */ + if (get_options()->EnforceDistinctSubnets == 0) + return 0; + if (tor_addr_family(addr) == AF_INET) { + uint32_t ip = tor_addr_to_ipv4h(addr); + + /* It's possible that this next check will hit before the first time + * resolve_my_address actually succeeds. (For clients, it is likely that + * resolve_my_address will never be called at all). In those cases, + * last_resolved_addr will be 0, and so checking to see whether ip is on + * the same /24 as last_resolved_addr will be the same as checking whether + * it was on net 0, which is already done by tor_addr_is_internal. + */ + if ((last_resolved_addr & (uint32_t)0xffffff00ul) + == (ip & (uint32_t)0xffffff00ul)) + return 1; + } + return 0; +} + +/** Return a new empty or_options_t. Used for testing. */ +or_options_t * +options_new(void) +{ + return config_new(&options_format); +} + +/** Set <b>options</b> to hold reasonable defaults for most options. + * Each option defaults to zero. */ +void +options_init(or_options_t *options) +{ + config_init(&options_format, options); +} + +/** Return a string containing a possible configuration file that would give + * the configuration in <b>options</b>. If <b>minimal</b> is true, do not + * include options that are the same as Tor's defaults. + */ +char * +options_dump(const or_options_t *options, int how_to_dump) +{ + const or_options_t *use_defaults; + int minimal; + switch (how_to_dump) { + case OPTIONS_DUMP_MINIMAL: + use_defaults = global_default_options; + minimal = 1; + break; + case OPTIONS_DUMP_DEFAULTS: + use_defaults = NULL; + minimal = 1; + break; + case OPTIONS_DUMP_ALL: + use_defaults = NULL; + minimal = 0; + break; + default: + log_warn(LD_BUG, "Bogus value for how_to_dump==%d", how_to_dump); + return NULL; + } + + return config_dump(&options_format, use_defaults, options, minimal, 0); +} + +/** Return 0 if every element of sl is a string holding a decimal + * representation of a port number, or if sl is NULL. + * Otherwise set *msg and return -1. */ +static int +validate_ports_csv(smartlist_t *sl, const char *name, char **msg) +{ + int i; + tor_assert(name); + + if (!sl) + return 0; + + SMARTLIST_FOREACH(sl, const char *, cp, + { + i = atoi(cp); + if (i < 1 || i > 65535) { + tor_asprintf(msg, "Port '%s' out of range in %s", cp, name); + return -1; + } + }); + return 0; +} + +/** If <b>value</b> exceeds ROUTER_MAX_DECLARED_BANDWIDTH, write + * a complaint into *<b>msg</b> using string <b>desc</b>, and return -1. + * Else return 0. + */ +static int +ensure_bandwidth_cap(uint64_t *value, const char *desc, char **msg) +{ + if (*value > ROUTER_MAX_DECLARED_BANDWIDTH) { + /* This handles an understandable special case where somebody says "2gb" + * whereas our actual maximum is 2gb-1 (INT_MAX) */ + --*value; + } + if (*value > ROUTER_MAX_DECLARED_BANDWIDTH) { + tor_asprintf(msg, "%s (%"PRIu64") must be at most %d", + desc, (*value), + ROUTER_MAX_DECLARED_BANDWIDTH); + return -1; + } + return 0; +} + +/** Parse an authority type from <b>options</b>-\>PublishServerDescriptor + * and write it to <b>options</b>-\>PublishServerDescriptor_. Treat "1" + * as "v3" unless BridgeRelay is 1, in which case treat it as "bridge". + * Treat "0" as "". + * Return 0 on success or -1 if not a recognized authority type (in which + * case the value of PublishServerDescriptor_ is undefined). */ +static int +compute_publishserverdescriptor(or_options_t *options) +{ + smartlist_t *list = options->PublishServerDescriptor; + dirinfo_type_t *auth = &options->PublishServerDescriptor_; + *auth = NO_DIRINFO; + if (!list) /* empty list, answer is none */ + return 0; + SMARTLIST_FOREACH_BEGIN(list, const char *, string) { + if (!strcasecmp(string, "v1")) + log_warn(LD_CONFIG, "PublishServerDescriptor v1 has no effect, because " + "there are no v1 directory authorities anymore."); + else if (!strcmp(string, "1")) + if (options->BridgeRelay) + *auth |= BRIDGE_DIRINFO; + else + *auth |= V3_DIRINFO; + else if (!strcasecmp(string, "v2")) + log_warn(LD_CONFIG, "PublishServerDescriptor v2 has no effect, because " + "there are no v2 directory authorities anymore."); + else if (!strcasecmp(string, "v3")) + *auth |= V3_DIRINFO; + else if (!strcasecmp(string, "bridge")) + *auth |= BRIDGE_DIRINFO; + else if (!strcasecmp(string, "hidserv")) + log_warn(LD_CONFIG, + "PublishServerDescriptor hidserv is invalid. See " + "PublishHidServDescriptors."); + else if (!strcasecmp(string, "") || !strcmp(string, "0")) + /* no authority */; + else + return -1; + } SMARTLIST_FOREACH_END(string); + return 0; +} + +/** Lowest allowable value for RendPostPeriod; if this is too low, hidden + * services can overload the directory system. */ +#define MIN_REND_POST_PERIOD (10*60) +#define MIN_REND_POST_PERIOD_TESTING (5) + +/** Highest allowable value for CircuitsAvailableTimeout. + * If this is too large, client connections will stay open for too long, + * incurring extra padding overhead. */ +#define MAX_CIRCS_AVAILABLE_TIME (24*60*60) + +/** Highest allowable value for RendPostPeriod. */ +#define MAX_DIR_PERIOD ((7*24*60*60)/2) + +/** Lowest allowable value for MaxCircuitDirtiness; if this is too low, Tor + * will generate too many circuits and potentially overload the network. */ +#define MIN_MAX_CIRCUIT_DIRTINESS 10 + +/** Highest allowable value for MaxCircuitDirtiness: prevents time_t + * overflows. */ +#define MAX_MAX_CIRCUIT_DIRTINESS (30*24*60*60) + +/** Lowest allowable value for CircuitStreamTimeout; if this is too low, Tor + * will generate too many circuits and potentially overload the network. */ +#define MIN_CIRCUIT_STREAM_TIMEOUT 10 + +/** Lowest recommended value for CircuitBuildTimeout; if it is set too low + * and LearnCircuitBuildTimeout is off, the failure rate for circuit + * construction may be very high. In that case, if it is set below this + * threshold emit a warning. + * */ +#define RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT (10) + +static int +options_validate_cb(void *old_options, void *options, void *default_options, + int from_setconf, char **msg) +{ + in_option_validation = 1; + int rv = options_validate(old_options, options, default_options, + from_setconf, msg); + in_option_validation = 0; + return rv; +} + +/** Callback to free an or_options_t */ +static void +options_free_cb(void *options) +{ + or_options_free_(options); +} + +#define REJECT(arg) \ + STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END +#if defined(__GNUC__) && __GNUC__ <= 3 +#define COMPLAIN(args...) \ + STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END +#else +#define COMPLAIN(args, ...) \ + STMT_BEGIN log_warn(LD_CONFIG, args, ##__VA_ARGS__); STMT_END +#endif /* defined(__GNUC__) && __GNUC__ <= 3 */ + +/** Log a warning message iff <b>filepath</b> is not absolute. + * Warning message must contain option name <b>option</b> and + * an absolute path that <b>filepath</b> will resolve to. + * + * In case <b>filepath</b> is absolute, do nothing. + * + * Return 1 if there were relative paths; 0 otherwise. + */ +static int +warn_if_option_path_is_relative(const char *option, + char *filepath) +{ + if (filepath && path_is_relative(filepath)) { + char *abs_path = make_path_absolute(filepath); + COMPLAIN("Path for %s (%s) is relative and will resolve to %s." + " Is this what you wanted?", option, filepath, abs_path); + tor_free(abs_path); + return 1; + } + return 0; +} + +/** Scan <b>options</b> for occurrences of relative file/directory + * path and log a warning whenever it is found. + * + * Return 1 if there were relative paths; 0 otherwise. + */ +static int +warn_about_relative_paths(or_options_t *options) +{ + tor_assert(options); + int n = 0; + + n += warn_if_option_path_is_relative("CookieAuthFile", + options->CookieAuthFile); + n += warn_if_option_path_is_relative("ExtORPortCookieAuthFile", + options->ExtORPortCookieAuthFile); + n += warn_if_option_path_is_relative("DirPortFrontPage", + options->DirPortFrontPage); + n += warn_if_option_path_is_relative("V3BandwidthsFile", + options->V3BandwidthsFile); + n += warn_if_option_path_is_relative("ControlPortWriteToFile", + options->ControlPortWriteToFile); + n += warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile); + n += warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File); + n += warn_if_option_path_is_relative("Log",options->DebugLogFile); + n += warn_if_option_path_is_relative("AccelDir",options->AccelDir); + n += warn_if_option_path_is_relative("DataDirectory",options->DataDirectory); + n += warn_if_option_path_is_relative("PidFile",options->PidFile); + n += warn_if_option_path_is_relative("ClientOnionAuthDir", + options->ClientOnionAuthDir); + + for (config_line_t *hs_line = options->RendConfigLines; hs_line; + hs_line = hs_line->next) { + if (!strcasecmp(hs_line->key, "HiddenServiceDir")) + n += warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value); + } + return n != 0; +} + +/* Validate options related to the scheduler. From the Schedulers list, the + * SchedulerTypes_ list is created with int values so once we select the + * scheduler, which can happen anytime at runtime, we don't have to parse + * strings and thus be quick. + * + * Return 0 on success else -1 and msg is set with an error message. */ +static int +options_validate_scheduler(or_options_t *options, char **msg) +{ + tor_assert(options); + tor_assert(msg); + + if (!options->Schedulers || smartlist_len(options->Schedulers) == 0) { + REJECT("Empty Schedulers list. Either remove the option so the defaults " + "can be used or set at least one value."); + } + /* Ok, we do have scheduler types, validate them. */ + options->SchedulerTypes_ = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(options->Schedulers, const char *, type) { + int *sched_type; + if (!strcasecmp("KISTLite", type)) { + sched_type = tor_malloc_zero(sizeof(int)); + *sched_type = SCHEDULER_KIST_LITE; + smartlist_add(options->SchedulerTypes_, sched_type); + } else if (!strcasecmp("KIST", type)) { + sched_type = tor_malloc_zero(sizeof(int)); + *sched_type = SCHEDULER_KIST; + smartlist_add(options->SchedulerTypes_, sched_type); + } else if (!strcasecmp("Vanilla", type)) { + sched_type = tor_malloc_zero(sizeof(int)); + *sched_type = SCHEDULER_VANILLA; + smartlist_add(options->SchedulerTypes_, sched_type); + } else { + tor_asprintf(msg, "Unknown type %s in option Schedulers. " + "Possible values are KIST, KISTLite and Vanilla.", + escaped(type)); + return -1; + } + } SMARTLIST_FOREACH_END(type); + + if (options->KISTSockBufSizeFactor < 0) { + REJECT("KISTSockBufSizeFactor must be at least 0"); + } + + /* Don't need to validate that the Interval is less than anything because + * zero is valid and all negative values are valid. */ + if (options->KISTSchedRunInterval > KIST_SCHED_RUN_INTERVAL_MAX) { + tor_asprintf(msg, "KISTSchedRunInterval must not be more than %d (ms)", + KIST_SCHED_RUN_INTERVAL_MAX); + return -1; + } + + return 0; +} + +/* Validate options related to single onion services. + * Modifies some options that are incompatible with single onion services. + * On failure returns -1, and sets *msg to an error string. + * Returns 0 on success. */ +STATIC int +options_validate_single_onion(or_options_t *options, char **msg) +{ + /* The two single onion service options must have matching values. */ + if (options->HiddenServiceSingleHopMode && + !options->HiddenServiceNonAnonymousMode) { + REJECT("HiddenServiceSingleHopMode does not provide any server anonymity. " + "It must be used with HiddenServiceNonAnonymousMode set to 1."); + } + if (options->HiddenServiceNonAnonymousMode && + !options->HiddenServiceSingleHopMode) { + REJECT("HiddenServiceNonAnonymousMode does not provide any server " + "anonymity. It must be used with HiddenServiceSingleHopMode set to " + "1."); + } + + /* Now that we've checked that the two options are consistent, we can safely + * call the rend_service_* functions that abstract these options. */ + + /* If you run an anonymous client with an active Single Onion service, the + * client loses anonymity. */ + const int client_port_set = (options->SocksPort_set || + options->TransPort_set || + options->NATDPort_set || + options->DNSPort_set || + options->HTTPTunnelPort_set); + if (rend_service_non_anonymous_mode_enabled(options) && client_port_set) { + REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as " + "an anonymous client. Please set Socks/Trans/NATD/DNSPort to 0, or " + "revert HiddenServiceNonAnonymousMode to 0."); + } + + if (rend_service_allow_non_anonymous_connection(options) + && options->UseEntryGuards) { + /* Single Onion services only use entry guards when uploading descriptors; + * all other connections are one-hop. Further, Single Onions causes the + * hidden service code to do things which break the path bias + * detector, and it's far easier to turn off entry guards (and + * thus the path bias detector with it) than to figure out how to + * make path bias compatible with single onions. + */ + log_notice(LD_CONFIG, + "HiddenServiceSingleHopMode is enabled; disabling " + "UseEntryGuards."); + options->UseEntryGuards = 0; + } + + return 0; +} + +/** Return 0 if every setting in <b>options</b> is reasonable, is a + * permissible transition from <b>old_options</b>, and none of the + * testing-only settings differ from <b>default_options</b> unless in + * testing mode. Else return -1. Should have no side effects, except for + * normalizing the contents of <b>options</b>. + * + * On error, tor_strdup an error explanation into *<b>msg</b>. + * + * XXX + * If <b>from_setconf</b>, we were called by the controller, and our + * Log line should stay empty. If it's 0, then give us a default log + * if there are no logs defined. + */ +STATIC int +options_validate(or_options_t *old_options, or_options_t *options, + or_options_t *default_options, int from_setconf, char **msg) +{ + config_line_t *cl; + const char *uname = get_uname(); + int n_ports=0; + int world_writable_control_socket=0; + + tor_assert(msg); + *msg = NULL; + + if (parse_ports(options, 1, msg, &n_ports, + &world_writable_control_socket) < 0) + return -1; + + /* Set UseEntryGuards from the configured value, before we check it below. + * We change UseEntryGuards when it's incompatible with other options, + * but leave UseEntryGuards_option with the original value. + * Always use the value of UseEntryGuards, not UseEntryGuards_option. */ + options->UseEntryGuards = options->UseEntryGuards_option; + + if (server_mode(options) && + (!strcmpstart(uname, "Windows 95") || + !strcmpstart(uname, "Windows 98") || + !strcmpstart(uname, "Windows Me"))) { + log_warn(LD_CONFIG, "Tor is running as a server, but you are " + "running %s; this probably won't work. See " + "https://www.torproject.org/docs/faq.html#BestOSForRelay " + "for details.", uname); + } + + if (parse_outbound_addresses(options, 1, msg) < 0) + return -1; + + if (validate_data_directories(options)<0) + REJECT("Invalid DataDirectory"); + + /* need to check for relative paths after we populate + * options->DataDirectory (just above). */ + if (warn_about_relative_paths(options) && options->RunAsDaemon) { + REJECT("You have specified at least one relative path (see above) " + "with the RunAsDaemon option. RunAsDaemon is not compatible " + "with relative paths."); + } + + if (options->Nickname == NULL) { + if (server_mode(options)) { + options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME); + } + } else { + if (!is_legal_nickname(options->Nickname)) { + tor_asprintf(msg, + "Nickname '%s', nicknames must be between 1 and 19 characters " + "inclusive, and must contain only the characters [a-zA-Z0-9].", + options->Nickname); + return -1; + } + } + + if (server_mode(options) && !options->ContactInfo) + log_notice(LD_CONFIG, "Your ContactInfo config option is not set. " + "Please consider setting it, so we can contact you if your server is " + "misconfigured or something else goes wrong."); + const char *ContactInfo = options->ContactInfo; + if (ContactInfo && !string_is_utf8(ContactInfo, strlen(ContactInfo))) + REJECT("ContactInfo config option must be UTF-8."); + + /* Special case on first boot if no Log options are given. */ + if (!options->Logs && !options->RunAsDaemon && !from_setconf) { + if (quiet_level == 0) + config_line_append(&options->Logs, "Log", "notice stdout"); + else if (quiet_level == 1) + config_line_append(&options->Logs, "Log", "warn stdout"); + } + + /* Validate the tor_log(s) */ + if (options_init_logs(old_options, options, 1)<0) + REJECT("Failed to validate Log options. See logs for details."); + + if (authdir_mode(options)) { + /* confirm that our address isn't broken, so we can complain now */ + uint32_t tmp; + if (resolve_my_address(LOG_WARN, options, &tmp, NULL, NULL) < 0) + REJECT("Failed to resolve/guess local address. See logs for details."); + } + + if (server_mode(options) && options->RendConfigLines) + log_warn(LD_CONFIG, + "Tor is currently configured as a relay and a hidden service. " + "That's not very secure: you should probably run your hidden service " + "in a separate Tor process, at least -- see " + "https://trac.torproject.org/8742"); + + /* XXXX require that the only port not be DirPort? */ + /* XXXX require that at least one port be listened-upon. */ + if (n_ports == 0 && !options->RendConfigLines) + log_warn(LD_CONFIG, + "SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all " + "undefined, and there aren't any hidden services configured. " + "Tor will still run, but probably won't do anything."); + + options->TransProxyType_parsed = TPT_DEFAULT; +#ifdef USE_TRANSPARENT + if (options->TransProxyType) { + if (!strcasecmp(options->TransProxyType, "default")) { + options->TransProxyType_parsed = TPT_DEFAULT; + } else if (!strcasecmp(options->TransProxyType, "pf-divert")) { +#if !defined(OpenBSD) && !defined( DARWIN ) + /* Later versions of OS X have pf */ + REJECT("pf-divert is a OpenBSD-specific " + "and OS X/Darwin-specific feature."); +#else + options->TransProxyType_parsed = TPT_PF_DIVERT; +#endif /* !defined(OpenBSD) && !defined( DARWIN ) */ + } else if (!strcasecmp(options->TransProxyType, "tproxy")) { +#if !defined(__linux__) + REJECT("TPROXY is a Linux-specific feature."); +#else + options->TransProxyType_parsed = TPT_TPROXY; +#endif + } else if (!strcasecmp(options->TransProxyType, "ipfw")) { +#ifndef KERNEL_MAY_SUPPORT_IPFW + /* Earlier versions of OS X have ipfw */ + REJECT("ipfw is a FreeBSD-specific " + "and OS X/Darwin-specific feature."); +#else + options->TransProxyType_parsed = TPT_IPFW; +#endif /* !defined(KERNEL_MAY_SUPPORT_IPFW) */ + } else { + REJECT("Unrecognized value for TransProxyType"); + } + + if (strcasecmp(options->TransProxyType, "default") && + !options->TransPort_set) { + REJECT("Cannot use TransProxyType without any valid TransPort."); + } + } +#else /* !(defined(USE_TRANSPARENT)) */ + if (options->TransPort_set) + REJECT("TransPort is disabled in this build."); +#endif /* defined(USE_TRANSPARENT) */ + + if (options->TokenBucketRefillInterval <= 0 + || options->TokenBucketRefillInterval > 1000) { + REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive."); + } + + if (options->ExcludeExitNodes || options->ExcludeNodes) { + options->ExcludeExitNodesUnion_ = routerset_new(); + routerset_union(options->ExcludeExitNodesUnion_,options->ExcludeExitNodes); + routerset_union(options->ExcludeExitNodesUnion_,options->ExcludeNodes); + } + + if (options->NodeFamilies) { + options->NodeFamilySets = smartlist_new(); + for (cl = options->NodeFamilies; cl; cl = cl->next) { + routerset_t *rs = routerset_new(); + if (routerset_parse(rs, cl->value, cl->key) == 0) { + smartlist_add(options->NodeFamilySets, rs); + } else { + routerset_free(rs); + } + } + } + + if (options->ExcludeNodes && options->StrictNodes) { + COMPLAIN("You have asked to exclude certain relays from all positions " + "in your circuits. Expect hidden services and other Tor " + "features to be broken in unpredictable ways."); + } + + for (cl = options->RecommendedPackages; cl; cl = cl->next) { + if (! validate_recommended_package_line(cl->value)) { + log_warn(LD_CONFIG, "Invalid RecommendedPackage line %s will be ignored", + escaped(cl->value)); + } + } + + if (options->AuthoritativeDir) { + if (!options->ContactInfo && !options->TestingTorNetwork) + REJECT("Authoritative directory servers must set ContactInfo"); + if (!options->RecommendedClientVersions) + options->RecommendedClientVersions = + config_lines_dup(options->RecommendedVersions); + if (!options->RecommendedServerVersions) + options->RecommendedServerVersions = + config_lines_dup(options->RecommendedVersions); + if (options->VersioningAuthoritativeDir && + (!options->RecommendedClientVersions || + !options->RecommendedServerVersions)) + REJECT("Versioning authoritative dir servers must set " + "Recommended*Versions."); + +#ifdef HAVE_MODULE_DIRAUTH + char *t; + /* Call these functions to produce warnings only. */ + t = format_recommended_version_list(options->RecommendedClientVersions, 1); + tor_free(t); + t = format_recommended_version_list(options->RecommendedServerVersions, 1); + tor_free(t); +#endif + + if (options->UseEntryGuards) { + log_info(LD_CONFIG, "Authoritative directory servers can't set " + "UseEntryGuards. Disabling."); + options->UseEntryGuards = 0; + } + if (!options->DownloadExtraInfo && authdir_mode_v3(options)) { + log_info(LD_CONFIG, "Authoritative directories always try to download " + "extra-info documents. Setting DownloadExtraInfo."); + options->DownloadExtraInfo = 1; + } + if (!(options->BridgeAuthoritativeDir || + options->V3AuthoritativeDir)) + REJECT("AuthoritativeDir is set, but none of " + "(Bridge/V3)AuthoritativeDir is set."); + /* 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); + } + /* same for guardfraction file */ + if (options->GuardfractionFile && !old_options) { + dirserv_read_guardfraction_file(options->GuardfractionFile, NULL); + } + } + + if (options->AuthoritativeDir && !options->DirPort_set) + REJECT("Running as authoritative directory, but no DirPort set."); + + if (options->AuthoritativeDir && !options->ORPort_set) + REJECT("Running as authoritative directory, but no ORPort set."); + + if (options->AuthoritativeDir && options->ClientOnly) + REJECT("Running as authoritative directory, but ClientOnly also set."); + + if (options->FetchDirInfoExtraEarly && !options->FetchDirInfoEarly) + REJECT("FetchDirInfoExtraEarly requires that you also set " + "FetchDirInfoEarly"); + + if (options->ConnLimit <= 0) { + tor_asprintf(msg, + "ConnLimit must be greater than 0, but was set to %d", + options->ConnLimit); + return -1; + } + + if (options->PathsNeededToBuildCircuits >= 0.0) { + if (options->PathsNeededToBuildCircuits < 0.25) { + log_warn(LD_CONFIG, "PathsNeededToBuildCircuits is too low. Increasing " + "to 0.25"); + options->PathsNeededToBuildCircuits = 0.25; + } else if (options->PathsNeededToBuildCircuits > 0.95) { + log_warn(LD_CONFIG, "PathsNeededToBuildCircuits is too high. Decreasing " + "to 0.95"); + options->PathsNeededToBuildCircuits = 0.95; + } + } + + if (options->MaxClientCircuitsPending <= 0 || + options->MaxClientCircuitsPending > MAX_MAX_CLIENT_CIRCUITS_PENDING) { + tor_asprintf(msg, + "MaxClientCircuitsPending must be between 1 and %d, but " + "was set to %d", MAX_MAX_CLIENT_CIRCUITS_PENDING, + options->MaxClientCircuitsPending); + return -1; + } + + if (validate_ports_csv(options->FirewallPorts, "FirewallPorts", msg) < 0) + return -1; + + if (validate_ports_csv(options->LongLivedPorts, "LongLivedPorts", msg) < 0) + return -1; + + if (validate_ports_csv(options->RejectPlaintextPorts, + "RejectPlaintextPorts", msg) < 0) + return -1; + + if (validate_ports_csv(options->WarnPlaintextPorts, + "WarnPlaintextPorts", msg) < 0) + return -1; + + if (options->FascistFirewall && !options->ReachableAddresses) { + if (options->FirewallPorts && smartlist_len(options->FirewallPorts)) { + /* We already have firewall ports set, so migrate them to + * ReachableAddresses, which will set ReachableORAddresses and + * ReachableDirAddresses if they aren't set explicitly. */ + smartlist_t *instead = smartlist_new(); + config_line_t *new_line = tor_malloc_zero(sizeof(config_line_t)); + new_line->key = tor_strdup("ReachableAddresses"); + /* If we're configured with the old format, we need to prepend some + * open ports. */ + SMARTLIST_FOREACH(options->FirewallPorts, const char *, portno, + { + int p = atoi(portno); + if (p<0) continue; + smartlist_add_asprintf(instead, "*:%d", p); + }); + new_line->value = smartlist_join_strings(instead,",",0,NULL); + /* These have been deprecated since 0.1.1.5-alpha-cvs */ + log_notice(LD_CONFIG, + "Converting FascistFirewall and FirewallPorts " + "config options to new format: \"ReachableAddresses %s\"", + new_line->value); + options->ReachableAddresses = new_line; + SMARTLIST_FOREACH(instead, char *, cp, tor_free(cp)); + smartlist_free(instead); + } else { + /* We do not have FirewallPorts set, so add 80 to + * ReachableDirAddresses, and 443 to ReachableORAddresses. */ + if (!options->ReachableDirAddresses) { + config_line_t *new_line = tor_malloc_zero(sizeof(config_line_t)); + new_line->key = tor_strdup("ReachableDirAddresses"); + new_line->value = tor_strdup("*:80"); + options->ReachableDirAddresses = new_line; + log_notice(LD_CONFIG, "Converting FascistFirewall config option " + "to new format: \"ReachableDirAddresses *:80\""); + } + if (!options->ReachableORAddresses) { + config_line_t *new_line = tor_malloc_zero(sizeof(config_line_t)); + new_line->key = tor_strdup("ReachableORAddresses"); + new_line->value = tor_strdup("*:443"); + options->ReachableORAddresses = new_line; + log_notice(LD_CONFIG, "Converting FascistFirewall config option " + "to new format: \"ReachableORAddresses *:443\""); + } + } + } + + if ((options->ReachableAddresses || + options->ReachableORAddresses || + options->ReachableDirAddresses || + options->ClientUseIPv4 == 0) && + server_mode(options)) + REJECT("Servers must be able to freely connect to the rest " + "of the Internet, so they must not set Reachable*Addresses " + "or FascistFirewall or FirewallPorts or ClientUseIPv4 0."); + + if (options->UseBridges && + server_mode(options)) + REJECT("Servers must be able to freely connect to the rest " + "of the Internet, so they must not set UseBridges."); + + /* If both of these are set, we'll end up with funny behavior where we + * demand enough entrynodes be up and running else we won't build + * circuits, yet we never actually use them. */ + if (options->UseBridges && options->EntryNodes) + REJECT("You cannot set both UseBridges and EntryNodes."); + + /* If we have UseBridges as 1 and UseEntryGuards as 0, we end up bypassing + * the use of bridges */ + if (options->UseBridges && !options->UseEntryGuards) + REJECT("Setting UseBridges requires also setting UseEntryGuards."); + + options->MaxMemInQueues = + compute_real_max_mem_in_queues(options->MaxMemInQueues_raw, + server_mode(options)); + options->MaxMemInQueues_low_threshold = (options->MaxMemInQueues / 4) * 3; + + if (!options->SafeLogging || + !strcasecmp(options->SafeLogging, "0")) { + options->SafeLogging_ = SAFELOG_SCRUB_NONE; + } else if (!strcasecmp(options->SafeLogging, "relay")) { + options->SafeLogging_ = SAFELOG_SCRUB_RELAY; + } else if (!strcasecmp(options->SafeLogging, "1")) { + options->SafeLogging_ = SAFELOG_SCRUB_ALL; + } else { + tor_asprintf(msg, + "Unrecognized value '%s' in SafeLogging", + escaped(options->SafeLogging)); + return -1; + } + + if (compute_publishserverdescriptor(options) < 0) { + tor_asprintf(msg, "Unrecognized value in PublishServerDescriptor"); + return -1; + } + + if ((options->BridgeRelay + || options->PublishServerDescriptor_ & BRIDGE_DIRINFO) + && (options->PublishServerDescriptor_ & V3_DIRINFO)) { + REJECT("Bridges are not supposed to publish router descriptors to the " + "directory authorities. Please correct your " + "PublishServerDescriptor line."); + } + + if (options->BridgeRelay && options->DirPort_set) { + log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling " + "DirPort"); + config_free_lines(options->DirPort_lines); + options->DirPort_lines = NULL; + options->DirPort_set = 0; + } + + if (server_mode(options) && options->ConnectionPadding != -1) { + REJECT("Relays must use 'auto' for the ConnectionPadding setting."); + } + + if (server_mode(options) && options->ReducedConnectionPadding != 0) { + REJECT("Relays cannot set ReducedConnectionPadding. "); + } + + if (options->BridgeDistribution) { + if (!options->BridgeRelay) { + REJECT("You set BridgeDistribution, but you didn't set BridgeRelay!"); + } + if (check_bridge_distribution_setting(options->BridgeDistribution) < 0) { + REJECT("Invalid BridgeDistribution value."); + } + } + + if (options->MinUptimeHidServDirectoryV2 < 0) { + log_warn(LD_CONFIG, "MinUptimeHidServDirectoryV2 option must be at " + "least 0 seconds. Changing to 0."); + options->MinUptimeHidServDirectoryV2 = 0; + } + + const int min_rendpostperiod = + options->TestingTorNetwork ? + MIN_REND_POST_PERIOD_TESTING : MIN_REND_POST_PERIOD; + if (options->RendPostPeriod < min_rendpostperiod) { + log_warn(LD_CONFIG, "RendPostPeriod option is too short; " + "raising to %d seconds.", min_rendpostperiod); + options->RendPostPeriod = min_rendpostperiod; + } + + if (options->RendPostPeriod > MAX_DIR_PERIOD) { + log_warn(LD_CONFIG, "RendPostPeriod is too large; clipping to %ds.", + MAX_DIR_PERIOD); + options->RendPostPeriod = MAX_DIR_PERIOD; + } + + /* Check the Single Onion Service options */ + if (options_validate_single_onion(options, msg) < 0) + return -1; + + if (options->CircuitsAvailableTimeout > MAX_CIRCS_AVAILABLE_TIME) { + // options_t is immutable for new code (the above code is older), + // so just make the user fix the value themselves rather than + // silently keep a shadow value lower than what they asked for. + REJECT("CircuitsAvailableTimeout is too large. Max is 24 hours."); + } + + if (options->EntryNodes && !options->UseEntryGuards) { + REJECT("If EntryNodes is set, UseEntryGuards must be enabled."); + } + + if (!(options->UseEntryGuards) && + (options->RendConfigLines != NULL) && + !rend_service_allow_non_anonymous_connection(options)) { + log_warn(LD_CONFIG, + "UseEntryGuards is disabled, but you have configured one or more " + "hidden services on this Tor instance. Your hidden services " + "will be very easy to locate using a well-known attack -- see " + "http://freehaven.net/anonbib/#hs-attack06 for details."); + } + + if (options->NumPrimaryGuards && options->NumEntryGuards && + options->NumEntryGuards > options->NumPrimaryGuards) { + REJECT("NumEntryGuards must not be greater than NumPrimaryGuards."); + } + + if (options->EntryNodes && + routerset_is_list(options->EntryNodes) && + (routerset_len(options->EntryNodes) == 1) && + (options->RendConfigLines != NULL)) { + tor_asprintf(msg, + "You have one single EntryNodes and at least one hidden service " + "configured. This is bad because it's very easy to locate your " + "entry guard which can then lead to the deanonymization of your " + "hidden service -- for more details, see " + "https://trac.torproject.org/projects/tor/ticket/14917. " + "For this reason, the use of one EntryNodes with an hidden " + "service is prohibited until a better solution is found."); + return -1; + } + + /* Inform the hidden service operator that pinning EntryNodes can possibly + * be harmful for the service anonymity. */ + if (options->EntryNodes && + routerset_is_list(options->EntryNodes) && + (options->RendConfigLines != NULL)) { + log_warn(LD_CONFIG, + "EntryNodes is set with multiple entries and at least one " + "hidden service is configured. Pinning entry nodes can possibly " + "be harmful to the service anonymity. Because of this, we " + "recommend you either don't do that or make sure you know what " + "you are doing. For more details, please look at " + "https://trac.torproject.org/projects/tor/ticket/21155."); + } + + /* Single Onion Services: non-anonymous hidden services */ + if (rend_service_non_anonymous_mode_enabled(options)) { + log_warn(LD_CONFIG, + "HiddenServiceNonAnonymousMode is set. Every hidden service on " + "this tor instance is NON-ANONYMOUS. If " + "the HiddenServiceNonAnonymousMode option is changed, Tor will " + "refuse to launch hidden services from the same directories, to " + "protect your anonymity against config errors. This setting is " + "for experimental use only."); + } + + if (!options->LearnCircuitBuildTimeout && options->CircuitBuildTimeout && + options->CircuitBuildTimeout < RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT) { + log_warn(LD_CONFIG, + "CircuitBuildTimeout is shorter (%d seconds) than the recommended " + "minimum (%d seconds), and LearnCircuitBuildTimeout is disabled. " + "If tor isn't working, raise this value or enable " + "LearnCircuitBuildTimeout.", + options->CircuitBuildTimeout, + RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT ); + } else if (!options->LearnCircuitBuildTimeout && + !options->CircuitBuildTimeout) { + int severity = LOG_NOTICE; + /* Be a little quieter if we've deliberately disabled + * LearnCircuitBuildTimeout. */ + if (circuit_build_times_disabled_(options, 1)) { + severity = LOG_INFO; + } + log_fn(severity, LD_CONFIG, "You disabled LearnCircuitBuildTimeout, but " + "didn't specify a CircuitBuildTimeout. I'll pick a plausible " + "default."); + } + + if (options->PathBiasNoticeRate > 1.0) { + tor_asprintf(msg, + "PathBiasNoticeRate is too high. " + "It must be between 0 and 1.0"); + return -1; + } + if (options->PathBiasWarnRate > 1.0) { + tor_asprintf(msg, + "PathBiasWarnRate is too high. " + "It must be between 0 and 1.0"); + return -1; + } + if (options->PathBiasExtremeRate > 1.0) { + tor_asprintf(msg, + "PathBiasExtremeRate is too high. " + "It must be between 0 and 1.0"); + return -1; + } + if (options->PathBiasNoticeUseRate > 1.0) { + tor_asprintf(msg, + "PathBiasNoticeUseRate is too high. " + "It must be between 0 and 1.0"); + return -1; + } + if (options->PathBiasExtremeUseRate > 1.0) { + tor_asprintf(msg, + "PathBiasExtremeUseRate is too high. " + "It must be between 0 and 1.0"); + return -1; + } + + if (options->MaxCircuitDirtiness < MIN_MAX_CIRCUIT_DIRTINESS) { + log_warn(LD_CONFIG, "MaxCircuitDirtiness option is too short; " + "raising to %d seconds.", MIN_MAX_CIRCUIT_DIRTINESS); + options->MaxCircuitDirtiness = MIN_MAX_CIRCUIT_DIRTINESS; + } + + if (options->MaxCircuitDirtiness > MAX_MAX_CIRCUIT_DIRTINESS) { + log_warn(LD_CONFIG, "MaxCircuitDirtiness option is too high; " + "setting to %d days.", MAX_MAX_CIRCUIT_DIRTINESS/86400); + options->MaxCircuitDirtiness = MAX_MAX_CIRCUIT_DIRTINESS; + } + + if (options->CircuitStreamTimeout && + options->CircuitStreamTimeout < MIN_CIRCUIT_STREAM_TIMEOUT) { + log_warn(LD_CONFIG, "CircuitStreamTimeout option is too short; " + "raising to %d seconds.", MIN_CIRCUIT_STREAM_TIMEOUT); + options->CircuitStreamTimeout = MIN_CIRCUIT_STREAM_TIMEOUT; + } + + if (options->HeartbeatPeriod && + options->HeartbeatPeriod < MIN_HEARTBEAT_PERIOD) { + log_warn(LD_CONFIG, "HeartbeatPeriod option is too short; " + "raising to %d seconds.", MIN_HEARTBEAT_PERIOD); + options->HeartbeatPeriod = MIN_HEARTBEAT_PERIOD; + } + + if (options->KeepalivePeriod < 1) + REJECT("KeepalivePeriod option must be positive."); + + if (ensure_bandwidth_cap(&options->BandwidthRate, + "BandwidthRate", msg) < 0) + return -1; + if (ensure_bandwidth_cap(&options->BandwidthBurst, + "BandwidthBurst", msg) < 0) + return -1; + if (ensure_bandwidth_cap(&options->MaxAdvertisedBandwidth, + "MaxAdvertisedBandwidth", msg) < 0) + return -1; + if (ensure_bandwidth_cap(&options->RelayBandwidthRate, + "RelayBandwidthRate", msg) < 0) + return -1; + if (ensure_bandwidth_cap(&options->RelayBandwidthBurst, + "RelayBandwidthBurst", msg) < 0) + return -1; + if (ensure_bandwidth_cap(&options->PerConnBWRate, + "PerConnBWRate", msg) < 0) + return -1; + if (ensure_bandwidth_cap(&options->PerConnBWBurst, + "PerConnBWBurst", msg) < 0) + return -1; + if (ensure_bandwidth_cap(&options->AuthDirFastGuarantee, + "AuthDirFastGuarantee", msg) < 0) + return -1; + if (ensure_bandwidth_cap(&options->AuthDirGuardBWGuarantee, + "AuthDirGuardBWGuarantee", msg) < 0) + return -1; + + if (options->RelayBandwidthRate && !options->RelayBandwidthBurst) + options->RelayBandwidthBurst = options->RelayBandwidthRate; + if (options->RelayBandwidthBurst && !options->RelayBandwidthRate) + options->RelayBandwidthRate = options->RelayBandwidthBurst; + + if (server_mode(options)) { + const unsigned required_min_bw = + public_server_mode(options) ? + RELAY_REQUIRED_MIN_BANDWIDTH : BRIDGE_REQUIRED_MIN_BANDWIDTH; + const char * const optbridge = + public_server_mode(options) ? "" : "bridge "; + if (options->BandwidthRate < required_min_bw) { + tor_asprintf(msg, + "BandwidthRate is set to %d bytes/second. " + "For %sservers, it must be at least %u.", + (int)options->BandwidthRate, optbridge, + required_min_bw); + return -1; + } else if (options->MaxAdvertisedBandwidth < + required_min_bw/2) { + tor_asprintf(msg, + "MaxAdvertisedBandwidth is set to %d bytes/second. " + "For %sservers, it must be at least %u.", + (int)options->MaxAdvertisedBandwidth, optbridge, + required_min_bw/2); + return -1; + } + if (options->RelayBandwidthRate && + options->RelayBandwidthRate < required_min_bw) { + tor_asprintf(msg, + "RelayBandwidthRate is set to %d bytes/second. " + "For %sservers, it must be at least %u.", + (int)options->RelayBandwidthRate, optbridge, + required_min_bw); + return -1; + } + } + + if (options->RelayBandwidthRate > options->RelayBandwidthBurst) + REJECT("RelayBandwidthBurst must be at least equal " + "to RelayBandwidthRate."); + + if (options->BandwidthRate > options->BandwidthBurst) + REJECT("BandwidthBurst must be at least equal to BandwidthRate."); + + /* if they set relaybandwidth* really high but left bandwidth* + * at the default, raise the defaults. */ + if (options->RelayBandwidthRate > options->BandwidthRate) + options->BandwidthRate = options->RelayBandwidthRate; + if (options->RelayBandwidthBurst > options->BandwidthBurst) + options->BandwidthBurst = options->RelayBandwidthBurst; + + if (accounting_parse_options(options, 1)<0) + REJECT("Failed to parse accounting options. See logs for details."); + + if (options->AccountingMax) { + if (options->RendConfigLines && server_mode(options)) { + log_warn(LD_CONFIG, "Using accounting with a hidden service and an " + "ORPort is risky: your hidden service(s) and your public " + "address will all turn off at the same time, which may alert " + "observers that they are being run by the same party."); + } else if (config_count_key(options->RendConfigLines, + "HiddenServiceDir") > 1) { + log_warn(LD_CONFIG, "Using accounting with multiple hidden services is " + "risky: they will all turn off at the same time, which may " + "alert observers that they are being run by the same party."); + } + } + + options->AccountingRule = ACCT_MAX; + if (options->AccountingRule_option) { + if (!strcmp(options->AccountingRule_option, "sum")) + options->AccountingRule = ACCT_SUM; + else if (!strcmp(options->AccountingRule_option, "max")) + options->AccountingRule = ACCT_MAX; + else if (!strcmp(options->AccountingRule_option, "in")) + options->AccountingRule = ACCT_IN; + else if (!strcmp(options->AccountingRule_option, "out")) + options->AccountingRule = ACCT_OUT; + else + REJECT("AccountingRule must be 'sum', 'max', 'in', or 'out'"); + } + + if (options->DirPort_set && !options->DirCache) { + REJECT("DirPort configured but DirCache disabled. DirPort requires " + "DirCache."); + } + + if (options->BridgeRelay && !options->DirCache) { + REJECT("We're a bridge but DirCache is disabled. BridgeRelay requires " + "DirCache."); + } + + if (server_mode(options)) { + char *dircache_msg = NULL; + if (have_enough_mem_for_dircache(options, 0, &dircache_msg)) { + log_warn(LD_CONFIG, "%s", dircache_msg); + tor_free(dircache_msg); + } + } + + if (options->HTTPProxy) { /* parse it now */ + if (tor_addr_port_lookup(options->HTTPProxy, + &options->HTTPProxyAddr, &options->HTTPProxyPort) < 0) + REJECT("HTTPProxy failed to parse or resolve. Please fix."); + if (options->HTTPProxyPort == 0) { /* give it a default */ + options->HTTPProxyPort = 80; + } + } + + if (options->HTTPProxyAuthenticator) { + if (strlen(options->HTTPProxyAuthenticator) >= 512) + REJECT("HTTPProxyAuthenticator is too long (>= 512 chars)."); + } + + if (options->HTTPSProxy) { /* parse it now */ + if (tor_addr_port_lookup(options->HTTPSProxy, + &options->HTTPSProxyAddr, &options->HTTPSProxyPort) <0) + REJECT("HTTPSProxy failed to parse or resolve. Please fix."); + if (options->HTTPSProxyPort == 0) { /* give it a default */ + options->HTTPSProxyPort = 443; + } + } + + if (options->HTTPSProxyAuthenticator) { + if (strlen(options->HTTPSProxyAuthenticator) >= 512) + REJECT("HTTPSProxyAuthenticator is too long (>= 512 chars)."); + } + + if (options->Socks4Proxy) { /* parse it now */ + if (tor_addr_port_lookup(options->Socks4Proxy, + &options->Socks4ProxyAddr, + &options->Socks4ProxyPort) <0) + REJECT("Socks4Proxy failed to parse or resolve. Please fix."); + if (options->Socks4ProxyPort == 0) { /* give it a default */ + options->Socks4ProxyPort = 1080; + } + } + + if (options->Socks5Proxy) { /* parse it now */ + if (tor_addr_port_lookup(options->Socks5Proxy, + &options->Socks5ProxyAddr, + &options->Socks5ProxyPort) <0) + REJECT("Socks5Proxy failed to parse or resolve. Please fix."); + if (options->Socks5ProxyPort == 0) { /* give it a default */ + options->Socks5ProxyPort = 1080; + } + } + + /* Check if more than one exclusive proxy type has been enabled. */ + if (!!options->Socks4Proxy + !!options->Socks5Proxy + + !!options->HTTPSProxy > 1) + REJECT("You have configured more than one proxy type. " + "(Socks4Proxy|Socks5Proxy|HTTPSProxy)"); + + /* Check if the proxies will give surprising behavior. */ + if (options->HTTPProxy && !(options->Socks4Proxy || + options->Socks5Proxy || + options->HTTPSProxy)) { + log_warn(LD_CONFIG, "HTTPProxy configured, but no SOCKS proxy or " + "HTTPS proxy configured. Watch out: this configuration will " + "proxy unencrypted directory connections only."); + } + + if (options->Socks5ProxyUsername) { + size_t len; + + len = strlen(options->Socks5ProxyUsername); + if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE) + REJECT("Socks5ProxyUsername must be between 1 and 255 characters."); + + if (!options->Socks5ProxyPassword) + REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername."); + + len = strlen(options->Socks5ProxyPassword); + if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE) + REJECT("Socks5ProxyPassword must be between 1 and 255 characters."); + } else if (options->Socks5ProxyPassword) + REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername."); + + if (options->HashedControlPassword) { + smartlist_t *sl = decode_hashed_passwords(options->HashedControlPassword); + if (!sl) { + REJECT("Bad HashedControlPassword: wrong length or bad encoding"); + } else { + SMARTLIST_FOREACH(sl, char*, cp, tor_free(cp)); + smartlist_free(sl); + } + } + + if (options->HashedControlSessionPassword) { + smartlist_t *sl = decode_hashed_passwords( + options->HashedControlSessionPassword); + if (!sl) { + REJECT("Bad HashedControlSessionPassword: wrong length or bad encoding"); + } else { + SMARTLIST_FOREACH(sl, char*, cp, tor_free(cp)); + smartlist_free(sl); + } + } + + if (options->OwningControllerProcess) { + const char *validate_pspec_msg = NULL; + if (tor_validate_process_specifier(options->OwningControllerProcess, + &validate_pspec_msg)) { + tor_asprintf(msg, "Bad OwningControllerProcess: %s", + validate_pspec_msg); + return -1; + } + } + + if ((options->ControlPort_set || world_writable_control_socket) && + !options->HashedControlPassword && + !options->HashedControlSessionPassword && + !options->CookieAuthentication) { + log_warn(LD_CONFIG, "Control%s is %s, but no authentication method " + "has been configured. This means that any program on your " + "computer can reconfigure your Tor. That's bad! You should " + "upgrade your Tor controller as soon as possible.", + options->ControlPort_set ? "Port" : "Socket", + options->ControlPort_set ? "open" : "world writable"); + } + + if (options->CookieAuthFileGroupReadable && !options->CookieAuthFile) { + log_warn(LD_CONFIG, "CookieAuthFileGroupReadable is set, but will have " + "no effect: you must specify an explicit CookieAuthFile to " + "have it group-readable."); + } + + if (options->MyFamily_lines && options->BridgeRelay) { + log_warn(LD_CONFIG, "Listing a family for a bridge relay is not " + "supported: it can reveal bridge fingerprints to censors. " + "You should also make sure you aren't listing this bridge's " + "fingerprint in any other MyFamily."); + } + if (normalize_nickname_list(&options->MyFamily, + options->MyFamily_lines, "MyFamily", msg)) + return -1; + for (cl = options->NodeFamilies; cl; cl = cl->next) { + routerset_t *rs = routerset_new(); + if (routerset_parse(rs, cl->value, cl->key)) { + routerset_free(rs); + return -1; + } + routerset_free(rs); + } + + if (validate_addr_policies(options, msg) < 0) + return -1; + + /* If FallbackDir is set, we don't UseDefaultFallbackDirs */ + if (options->UseDefaultFallbackDirs && options->FallbackDir) { + log_info(LD_CONFIG, "You have set UseDefaultFallbackDirs 1 and " + "FallbackDir(s). Ignoring UseDefaultFallbackDirs, and " + "using the FallbackDir(s) you have set."); + } + + if (validate_dir_servers(options, old_options) < 0) + REJECT("Directory authority/fallback line did not parse. See logs " + "for details."); + + if (options->UseBridges && !options->Bridges) + REJECT("If you set UseBridges, you must specify at least one bridge."); + + for (cl = options->Bridges; cl; cl = cl->next) { + bridge_line_t *bridge_line = parse_bridge_line(cl->value); + if (!bridge_line) + REJECT("Bridge line did not parse. See logs for details."); + bridge_line_free(bridge_line); + } + + for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { + if (parse_transport_line(options, cl->value, 1, 0) < 0) + REJECT("Invalid client transport line. See logs for details."); + } + + for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { + if (parse_transport_line(options, cl->value, 1, 1) < 0) + REJECT("Invalid server transport line. See logs for details."); + } + + if (options->ServerTransportPlugin && !server_mode(options)) { + log_notice(LD_GENERAL, "Tor is not configured as a relay but you specified" + " a ServerTransportPlugin line (%s). The ServerTransportPlugin " + "line will be ignored.", + escaped(options->ServerTransportPlugin->value)); + } + + for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) { + /** If get_bindaddr_from_transport_listen_line() fails with + 'transport' being NULL, it means that something went wrong + while parsing the ServerTransportListenAddr line. */ + char *bindaddr = get_bindaddr_from_transport_listen_line(cl->value, NULL); + if (!bindaddr) + REJECT("ServerTransportListenAddr did not parse. See logs for details."); + tor_free(bindaddr); + } + + if (options->ServerTransportListenAddr && !options->ServerTransportPlugin) { + log_notice(LD_GENERAL, "You need at least a single managed-proxy to " + "specify a transport listen address. The " + "ServerTransportListenAddr line will be ignored."); + } + + for (cl = options->ServerTransportOptions; cl; cl = cl->next) { + /** If get_options_from_transport_options_line() fails with + 'transport' being NULL, it means that something went wrong + while parsing the ServerTransportOptions line. */ + smartlist_t *options_sl = + get_options_from_transport_options_line(cl->value, NULL); + if (!options_sl) + REJECT("ServerTransportOptions did not parse. See logs for details."); + + SMARTLIST_FOREACH(options_sl, char *, cp, tor_free(cp)); + smartlist_free(options_sl); + } + + if (options->ConstrainedSockets) { + /* If the user wants to constrain socket buffer use, make sure the desired + * limit is between MIN|MAX_TCPSOCK_BUFFER in k increments. */ + if (options->ConstrainedSockSize < MIN_CONSTRAINED_TCP_BUFFER || + options->ConstrainedSockSize > MAX_CONSTRAINED_TCP_BUFFER || + options->ConstrainedSockSize % 1024) { + tor_asprintf(msg, + "ConstrainedSockSize is invalid. Must be a value between %d and %d " + "in 1024 byte increments.", + MIN_CONSTRAINED_TCP_BUFFER, MAX_CONSTRAINED_TCP_BUFFER); + return -1; + } + if (options->DirPort_set) { + /* Providing cached directory entries while system TCP buffers are scarce + * will exacerbate the socket errors. Suggest that this be disabled. */ + COMPLAIN("You have requested constrained socket buffers while also " + "serving directory entries via DirPort. It is strongly " + "suggested that you disable serving directory requests when " + "system TCP buffer resources are scarce."); + } + } + + if (options->V3AuthVoteDelay + options->V3AuthDistDelay >= + options->V3AuthVotingInterval/2) { + /* + This doesn't work, but it seems like it should: + what code is preventing the interval being less than twice the lead-up? + if (options->TestingTorNetwork) { + if (options->V3AuthVoteDelay + options->V3AuthDistDelay >= + options->V3AuthVotingInterval) { + REJECT("V3AuthVoteDelay plus V3AuthDistDelay must be less than " + "V3AuthVotingInterval"); + } else { + COMPLAIN("V3AuthVoteDelay plus V3AuthDistDelay is more than half " + "V3AuthVotingInterval. This may lead to " + "consensus instability, particularly if clocks drift."); + } + } else { + */ + REJECT("V3AuthVoteDelay plus V3AuthDistDelay must be less than half " + "V3AuthVotingInterval"); + /* + } + */ + } + + if (options->V3AuthVoteDelay < MIN_VOTE_SECONDS) { + if (options->TestingTorNetwork) { + if (options->V3AuthVoteDelay < MIN_VOTE_SECONDS_TESTING) { + REJECT("V3AuthVoteDelay is way too low."); + } else { + COMPLAIN("V3AuthVoteDelay is very low. " + "This may lead to failure to vote for a consensus."); + } + } else { + REJECT("V3AuthVoteDelay is way too low."); + } + } + + if (options->V3AuthDistDelay < MIN_DIST_SECONDS) { + if (options->TestingTorNetwork) { + if (options->V3AuthDistDelay < MIN_DIST_SECONDS_TESTING) { + REJECT("V3AuthDistDelay is way too low."); + } else { + COMPLAIN("V3AuthDistDelay is very low. " + "This may lead to missing votes in a consensus."); + } + } else { + REJECT("V3AuthDistDelay is way too low."); + } + } + + if (options->V3AuthNIntervalsValid < 2) + REJECT("V3AuthNIntervalsValid must be at least 2."); + + if (options->V3AuthVotingInterval < MIN_VOTE_INTERVAL) { + if (options->TestingTorNetwork) { + if (options->V3AuthVotingInterval < MIN_VOTE_INTERVAL_TESTING) { + REJECT("V3AuthVotingInterval is insanely low."); + } else { + COMPLAIN("V3AuthVotingInterval is very low. " + "This may lead to failure to synchronise for a consensus."); + } + } else { + REJECT("V3AuthVotingInterval is insanely low."); + } + } else if (options->V3AuthVotingInterval > 24*60*60) { + REJECT("V3AuthVotingInterval is insanely high."); + } else if (((24*60*60) % options->V3AuthVotingInterval) != 0) { + COMPLAIN("V3AuthVotingInterval does not divide evenly into 24 hours."); + } + + if (hs_config_service_all(options, 1) < 0) + REJECT("Failed to configure rendezvous options. See logs for details."); + + /* Parse client-side authorization for hidden services. */ + if (hs_config_client_auth_all(options, 1) < 0) + REJECT("Failed to configure client authorization for hidden services. " + "See logs for details."); + + if (parse_virtual_addr_network(options->VirtualAddrNetworkIPv4, + AF_INET, 1, msg)<0) + return -1; + if (parse_virtual_addr_network(options->VirtualAddrNetworkIPv6, + AF_INET6, 1, msg)<0) + return -1; + + if (options->TestingTorNetwork && + !(options->DirAuthorities || + (options->AlternateDirAuthority && + options->AlternateBridgeAuthority))) { + REJECT("TestingTorNetwork may only be configured in combination with " + "a non-default set of DirAuthority or both of " + "AlternateDirAuthority and AlternateBridgeAuthority configured."); + } + +#define CHECK_DEFAULT(arg) \ + STMT_BEGIN \ + if (!options->TestingTorNetwork && \ + !options->UsingTestNetworkDefaults_ && \ + !config_is_same(&options_format,options, \ + default_options,#arg)) { \ + REJECT(#arg " may only be changed in testing Tor " \ + "networks!"); \ + } STMT_END + CHECK_DEFAULT(TestingV3AuthInitialVotingInterval); + CHECK_DEFAULT(TestingV3AuthInitialVoteDelay); + CHECK_DEFAULT(TestingV3AuthInitialDistDelay); + CHECK_DEFAULT(TestingV3AuthVotingStartOffset); + CHECK_DEFAULT(TestingAuthDirTimeToLearnReachability); + CHECK_DEFAULT(TestingEstimatedDescriptorPropagationTime); + CHECK_DEFAULT(TestingServerDownloadInitialDelay); + CHECK_DEFAULT(TestingClientDownloadInitialDelay); + CHECK_DEFAULT(TestingServerConsensusDownloadInitialDelay); + CHECK_DEFAULT(TestingClientConsensusDownloadInitialDelay); + CHECK_DEFAULT(TestingBridgeDownloadInitialDelay); + CHECK_DEFAULT(TestingBridgeBootstrapDownloadInitialDelay); + CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest); + CHECK_DEFAULT(TestingDirConnectionMaxStall); + CHECK_DEFAULT(TestingAuthKeyLifetime); + CHECK_DEFAULT(TestingLinkCertLifetime); + CHECK_DEFAULT(TestingSigningKeySlop); + CHECK_DEFAULT(TestingAuthKeySlop); + CHECK_DEFAULT(TestingLinkKeySlop); +#undef CHECK_DEFAULT + + if (!options->ClientDNSRejectInternalAddresses && + !(options->DirAuthorities || + (options->AlternateDirAuthority && options->AlternateBridgeAuthority))) + REJECT("ClientDNSRejectInternalAddresses used for default network."); + if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2) + REJECT("SigningKeyLifetime is too short."); + if (options->TestingLinkCertLifetime < options->TestingAuthKeySlop*2) + REJECT("LinkCertLifetime is too short."); + if (options->TestingAuthKeyLifetime < options->TestingLinkKeySlop*2) + REJECT("TestingAuthKeyLifetime is too short."); + + if (options->TestingV3AuthInitialVotingInterval + < MIN_VOTE_INTERVAL_TESTING_INITIAL) { + REJECT("TestingV3AuthInitialVotingInterval is insanely low."); + } else if (((30*60) % options->TestingV3AuthInitialVotingInterval) != 0) { + REJECT("TestingV3AuthInitialVotingInterval does not divide evenly into " + "30 minutes."); + } + + if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS_TESTING) { + REJECT("TestingV3AuthInitialVoteDelay is way too low."); + } + + if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS_TESTING) { + REJECT("TestingV3AuthInitialDistDelay is way too low."); + } + + if (options->TestingV3AuthInitialVoteDelay + + options->TestingV3AuthInitialDistDelay >= + options->TestingV3AuthInitialVotingInterval) { + REJECT("TestingV3AuthInitialVoteDelay plus TestingV3AuthInitialDistDelay " + "must be less than TestingV3AuthInitialVotingInterval"); + } + + if (options->TestingV3AuthVotingStartOffset > + MIN(options->TestingV3AuthInitialVotingInterval, + options->V3AuthVotingInterval)) { + REJECT("TestingV3AuthVotingStartOffset is higher than the voting " + "interval."); + } else if (options->TestingV3AuthVotingStartOffset < 0) { + REJECT("TestingV3AuthVotingStartOffset must be non-negative."); + } + + if (options->TestingAuthDirTimeToLearnReachability < 0) { + REJECT("TestingAuthDirTimeToLearnReachability must be non-negative."); + } else if (options->TestingAuthDirTimeToLearnReachability > 2*60*60) { + COMPLAIN("TestingAuthDirTimeToLearnReachability is insanely high."); + } + + if (options->TestingEstimatedDescriptorPropagationTime < 0) { + REJECT("TestingEstimatedDescriptorPropagationTime must be non-negative."); + } else if (options->TestingEstimatedDescriptorPropagationTime > 60*60) { + COMPLAIN("TestingEstimatedDescriptorPropagationTime is insanely high."); + } + + if (options->TestingClientMaxIntervalWithoutRequest < 1) { + REJECT("TestingClientMaxIntervalWithoutRequest is way too low."); + } else if (options->TestingClientMaxIntervalWithoutRequest > 3600) { + COMPLAIN("TestingClientMaxIntervalWithoutRequest is insanely high."); + } + + if (options->TestingDirConnectionMaxStall < 5) { + REJECT("TestingDirConnectionMaxStall is way too low."); + } else if (options->TestingDirConnectionMaxStall > 3600) { + COMPLAIN("TestingDirConnectionMaxStall is insanely high."); + } + + if (options->ClientBootstrapConsensusMaxInProgressTries < 1) { + REJECT("ClientBootstrapConsensusMaxInProgressTries must be greater " + "than 0."); + } else if (options->ClientBootstrapConsensusMaxInProgressTries + > 100) { + COMPLAIN("ClientBootstrapConsensusMaxInProgressTries is insanely " + "high."); + } + + if (options->TestingEnableConnBwEvent && + !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { + REJECT("TestingEnableConnBwEvent may only be changed in testing " + "Tor networks!"); + } + + if (options->TestingEnableCellStatsEvent && + !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { + REJECT("TestingEnableCellStatsEvent may only be changed in testing " + "Tor networks!"); + } + + if (options->TestingTorNetwork) { + log_warn(LD_CONFIG, "TestingTorNetwork is set. This will make your node " + "almost unusable in the public Tor network, and is " + "therefore only advised if you are building a " + "testing Tor network!"); + } + + if (options->AccelName && !options->HardwareAccel) + options->HardwareAccel = 1; + if (options->AccelDir && !options->AccelName) + REJECT("Can't use hardware crypto accelerator dir without engine name."); + + if (options->PublishServerDescriptor) + SMARTLIST_FOREACH(options->PublishServerDescriptor, const char *, pubdes, { + if (!strcmp(pubdes, "1") || !strcmp(pubdes, "0")) + if (smartlist_len(options->PublishServerDescriptor) > 1) { + COMPLAIN("You have passed a list of multiple arguments to the " + "PublishServerDescriptor option that includes 0 or 1. " + "0 or 1 should only be used as the sole argument. " + "This configuration will be rejected in a future release."); + break; + } + }); + + if (options->BridgeRelay == 1 && ! options->ORPort_set) + REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid " + "combination."); + + if (options_validate_scheduler(options, msg) < 0) { + return -1; + } + + return 0; +} + +#undef REJECT +#undef COMPLAIN + +/* Given the value that the user has set for MaxMemInQueues, compute the + * actual maximum value. We clip this value if it's too low, and autodetect + * it if it's set to 0. */ +STATIC uint64_t +compute_real_max_mem_in_queues(const uint64_t val, int log_guess) +{ + uint64_t result; + + if (val == 0) { +#define ONE_GIGABYTE (UINT64_C(1) << 30) +#define ONE_MEGABYTE (UINT64_C(1) << 20) + /* The user didn't pick a memory limit. Choose a very large one + * that is still smaller than the system memory */ + static int notice_sent = 0; + size_t ram = 0; + if (get_total_system_memory(&ram) < 0) { + /* We couldn't determine our total system memory! */ +#if SIZEOF_VOID_P >= 8 + /* 64-bit system. Let's hope for 8 GB. */ + result = 8 * ONE_GIGABYTE; +#else + /* (presumably) 32-bit system. Let's hope for 1 GB. */ + result = ONE_GIGABYTE; +#endif /* SIZEOF_VOID_P >= 8 */ + } else { + /* We detected the amount of memory available. */ + uint64_t avail = 0; + +#if SIZEOF_SIZE_T > 4 +/* On a 64-bit platform, we consider 8GB "very large". */ +#define RAM_IS_VERY_LARGE(x) ((x) >= (8 * ONE_GIGABYTE)) +#else +/* On a 32-bit platform, we can't have 8GB of ram. */ +#define RAM_IS_VERY_LARGE(x) (0) +#endif + + if (RAM_IS_VERY_LARGE(ram)) { + /* If we have 8 GB, or more, RAM available, we set the MaxMemInQueues + * to 0.4 * RAM. The idea behind this value is that the amount of RAM + * is more than enough for a single relay and should allow the relay + * operator to run two relays if they have additional bandwidth + * available. + */ + avail = (ram / 5) * 2; + } else { + /* If we have less than 8 GB of RAM available, we use the "old" default + * for MaxMemInQueues of 0.75 * RAM. + */ + avail = (ram / 4) * 3; + } + + /* Make sure it's in range from 0.25 GB to 8 GB for 64-bit and 0.25 to 2 + * GB for 32-bit. */ + if (avail > MAX_DEFAULT_MEMORY_QUEUE_SIZE) { + /* If you want to use more than this much RAM, you need to configure + it yourself */ + result = MAX_DEFAULT_MEMORY_QUEUE_SIZE; + } else if (avail < ONE_GIGABYTE / 4) { + result = ONE_GIGABYTE / 4; + } else { + result = avail; + } + } + if (log_guess && ! notice_sent) { + log_notice(LD_CONFIG, "%sMaxMemInQueues is set to %"PRIu64" MB. " + "You can override this by setting MaxMemInQueues by hand.", + ram ? "Based on detected system memory, " : "", + (result / ONE_MEGABYTE)); + notice_sent = 1; + } + return result; + } else if (val < ONE_GIGABYTE / 4) { + log_warn(LD_CONFIG, "MaxMemInQueues must be at least 256 MB for now. " + "Ideally, have it as large as you can afford."); + return ONE_GIGABYTE / 4; + } else { + /* The value was fine all along */ + return val; + } +} + +/* If we have less than 300 MB suggest disabling dircache */ +#define DIRCACHE_MIN_MEM_MB 300 +#define DIRCACHE_MIN_MEM_BYTES (DIRCACHE_MIN_MEM_MB*ONE_MEGABYTE) +#define STRINGIFY(val) #val + +/** Create a warning message for emitting if we are a dircache but may not have + * enough system memory, or if we are not a dircache but probably should be. + * Return -1 when a message is returned in *msg*, else return 0. */ +STATIC int +have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem, + char **msg) +{ + *msg = NULL; + /* XXX We should possibly be looking at MaxMemInQueues here + * unconditionally. Or we should believe total_mem unconditionally. */ + if (total_mem == 0) { + if (get_total_system_memory(&total_mem) < 0) { + total_mem = options->MaxMemInQueues >= SIZE_MAX ? + SIZE_MAX : (size_t)options->MaxMemInQueues; + } + } + if (options->DirCache) { + if (total_mem < DIRCACHE_MIN_MEM_BYTES) { + if (options->BridgeRelay) { + tor_asprintf(msg, "Running a Bridge with less than %d MB of memory " + "is not recommended.", DIRCACHE_MIN_MEM_MB); + } else { + tor_asprintf(msg, "Being a directory cache (default) with less than " + "%d MB of memory is not recommended and may consume " + "most of the available resources. Consider disabling " + "this functionality by setting the DirCache option " + "to 0.", DIRCACHE_MIN_MEM_MB); + } + } + } else { + if (total_mem >= DIRCACHE_MIN_MEM_BYTES) { + *msg = tor_strdup("DirCache is disabled and we are configured as a " + "relay. We will not become a Guard."); + } + } + return *msg == NULL ? 0 : -1; +} +#undef STRINGIFY + +/** Helper: return true iff s1 and s2 are both NULL, or both non-NULL + * equal strings. */ +static int +opt_streq(const char *s1, const char *s2) +{ + return 0 == strcmp_opt(s1, s2); +} + +/** Check if any of the previous options have changed but aren't allowed to. */ +static int +options_transition_allowed(const or_options_t *old, + const or_options_t *new_val, + char **msg) +{ + if (!old) + return 0; + +#define BAD_CHANGE_TO(opt, how) do { \ + *msg = tor_strdup("While Tor is running"how", changing " #opt \ + " is not allowed"); \ + return -1; \ + } while (0) + +#define NO_CHANGE_BOOL(opt) \ + if (! CFG_EQ_BOOL(old, new_val, opt)) BAD_CHANGE_TO(opt,"") +#define NO_CHANGE_INT(opt) \ + if (! CFG_EQ_INT(old, new_val, opt)) BAD_CHANGE_TO(opt,"") +#define NO_CHANGE_STRING(opt) \ + if (! CFG_EQ_STRING(old, new_val, opt)) BAD_CHANGE_TO(opt,"") + + NO_CHANGE_STRING(PidFile); + NO_CHANGE_BOOL(RunAsDaemon); + NO_CHANGE_BOOL(Sandbox); + NO_CHANGE_STRING(DataDirectory); + NO_CHANGE_STRING(KeyDirectory); + NO_CHANGE_STRING(CacheDirectory); + NO_CHANGE_STRING(User); + NO_CHANGE_BOOL(KeepBindCapabilities); + NO_CHANGE_STRING(SyslogIdentityTag); + NO_CHANGE_STRING(AndroidIdentityTag); + NO_CHANGE_BOOL(HardwareAccel); + NO_CHANGE_STRING(AccelName); + NO_CHANGE_STRING(AccelDir); + NO_CHANGE_BOOL(TestingTorNetwork); + NO_CHANGE_BOOL(DisableAllSwap); + NO_CHANGE_INT(TokenBucketRefillInterval); + NO_CHANGE_BOOL(HiddenServiceSingleHopMode); + NO_CHANGE_BOOL(HiddenServiceNonAnonymousMode); + NO_CHANGE_BOOL(DisableDebuggerAttachment); + NO_CHANGE_BOOL(NoExec); + NO_CHANGE_INT(OwningControllerFD); + NO_CHANGE_BOOL(DisableSignalHandlers); + + if (sandbox_is_active()) { +#define SB_NOCHANGE_STR(opt) \ + if (! CFG_EQ_STRING(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") +#define SB_NOCHANGE_LINELIST(opt) \ + if (! CFG_EQ_LINELIST(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") +#define SB_NOCHANGE_INT(opt) \ + if (! CFG_EQ_INT(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") + + SB_NOCHANGE_STR(Address); + SB_NOCHANGE_STR(ServerDNSResolvConfFile); + SB_NOCHANGE_STR(DirPortFrontPage); + SB_NOCHANGE_STR(CookieAuthFile); + SB_NOCHANGE_STR(ExtORPortCookieAuthFile); + SB_NOCHANGE_LINELIST(Logs); + SB_NOCHANGE_INT(ConnLimit); + + if (server_mode(old) != server_mode(new_val)) { + *msg = tor_strdup("Can't start/stop being a server while " + "Sandbox is active"); + return -1; + } + } + +#undef SB_NOCHANGE_LINELIST +#undef SB_NOCHANGE_STR +#undef SB_NOCHANGE_INT +#undef BAD_CHANGE_TO +#undef NO_CHANGE_BOOL +#undef NO_CHANGE_INT +#undef NO_CHANGE_STRING + return 0; +} + +/** Return 1 if any change from <b>old_options</b> to <b>new_options</b> + * will require us to rotate the CPU and DNS workers; else return 0. */ +static int +options_transition_affects_workers(const or_options_t *old_options, + const or_options_t *new_options) +{ + YES_IF_CHANGED_STRING(DataDirectory); + YES_IF_CHANGED_INT(NumCPUs); + YES_IF_CHANGED_LINELIST(ORPort_lines); + YES_IF_CHANGED_BOOL(ServerDNSSearchDomains); + YES_IF_CHANGED_BOOL(SafeLogging_); + YES_IF_CHANGED_BOOL(ClientOnly); + YES_IF_CHANGED_BOOL(LogMessageDomains); + YES_IF_CHANGED_LINELIST(Logs); + + if (server_mode(old_options) != server_mode(new_options) || + public_server_mode(old_options) != public_server_mode(new_options) || + dir_server_mode(old_options) != dir_server_mode(new_options)) + return 1; + + /* Nothing that changed matters. */ + return 0; +} + +/** Return 1 if any change from <b>old_options</b> to <b>new_options</b> + * will require us to generate a new descriptor; else return 0. */ +static int +options_transition_affects_descriptor(const or_options_t *old_options, + const or_options_t *new_options) +{ + /* XXX We can be smarter here. If your DirPort isn't being + * published and you just turned it off, no need to republish. Etc. */ + + YES_IF_CHANGED_STRING(DataDirectory); + YES_IF_CHANGED_STRING(Nickname); + YES_IF_CHANGED_STRING(Address); + YES_IF_CHANGED_LINELIST(ExitPolicy); + YES_IF_CHANGED_BOOL(ExitRelay); + YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate); + YES_IF_CHANGED_BOOL(ExitPolicyRejectLocalInterfaces); + YES_IF_CHANGED_BOOL(IPv6Exit); + YES_IF_CHANGED_LINELIST(ORPort_lines); + YES_IF_CHANGED_LINELIST(DirPort_lines); + YES_IF_CHANGED_LINELIST(DirPort_lines); + YES_IF_CHANGED_BOOL(ClientOnly); + YES_IF_CHANGED_BOOL(DisableNetwork); + YES_IF_CHANGED_BOOL(PublishServerDescriptor_); + YES_IF_CHANGED_STRING(ContactInfo); + YES_IF_CHANGED_STRING(BridgeDistribution); + YES_IF_CHANGED_LINELIST(MyFamily); + YES_IF_CHANGED_STRING(AccountingStart); + YES_IF_CHANGED_INT(AccountingMax); + YES_IF_CHANGED_INT(AccountingRule); + YES_IF_CHANGED_BOOL(DirCache); + YES_IF_CHANGED_BOOL(AssumeReachable); + + if (get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || + get_effective_bwburst(old_options) != + get_effective_bwburst(new_options) || + public_server_mode(old_options) != public_server_mode(new_options)) + return 1; + + return 0; +} + +#ifdef _WIN32 +/** Return the directory on windows where we expect to find our application + * data. */ +static char * +get_windows_conf_root(void) +{ + static int is_set = 0; + static char path[MAX_PATH*2+1]; + TCHAR tpath[MAX_PATH] = {0}; + + LPITEMIDLIST idl; + IMalloc *m; + HRESULT result; + + if (is_set) + return path; + + /* Find X:\documents and settings\username\application data\ . + * We would use SHGetSpecialFolder path, but that wasn't added until IE4. + */ +#ifdef ENABLE_LOCAL_APPDATA +#define APPDATA_PATH CSIDL_LOCAL_APPDATA +#else +#define APPDATA_PATH CSIDL_APPDATA +#endif + if (!SUCCEEDED(SHGetSpecialFolderLocation(NULL, APPDATA_PATH, &idl))) { + getcwd(path,MAX_PATH); + is_set = 1; + log_warn(LD_CONFIG, + "I couldn't find your application data folder: are you " + "running an ancient version of Windows 95? Defaulting to \"%s\"", + path); + return path; + } + /* Convert the path from an "ID List" (whatever that is!) to a path. */ + result = SHGetPathFromIDList(idl, tpath); +#ifdef UNICODE + wcstombs(path,tpath,sizeof(path)); + path[sizeof(path)-1] = '\0'; +#else + strlcpy(path,tpath,sizeof(path)); +#endif /* defined(UNICODE) */ + + /* Now we need to free the memory that the path-idl was stored in. In + * typical Windows fashion, we can't just call 'free()' on it. */ + SHGetMalloc(&m); + if (m) { + m->lpVtbl->Free(m, idl); + m->lpVtbl->Release(m); + } + if (!SUCCEEDED(result)) { + return NULL; + } + strlcat(path,"\\tor",MAX_PATH); + is_set = 1; + return path; +} +#endif /* defined(_WIN32) */ + +/** Return the default location for our torrc file (if <b>defaults_file</b> is + * false), or for the torrc-defaults file (if <b>defaults_file</b> is true). */ +static const char * +get_default_conf_file(int defaults_file) +{ +#ifdef DISABLE_SYSTEM_TORRC + (void) defaults_file; + return NULL; +#elif defined(_WIN32) + if (defaults_file) { + static char defaults_path[MAX_PATH+1]; + tor_snprintf(defaults_path, MAX_PATH, "%s\\torrc-defaults", + get_windows_conf_root()); + return defaults_path; + } else { + static char path[MAX_PATH+1]; + tor_snprintf(path, MAX_PATH, "%s\\torrc", + get_windows_conf_root()); + return path; + } +#else + return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc"; +#endif /* defined(DISABLE_SYSTEM_TORRC) || ... */ +} + +/** Verify whether lst is a list of strings containing valid-looking + * comma-separated nicknames, or NULL. Will normalise <b>lst</b> to prefix '$' + * to any nickname or fingerprint that needs it. Also splits comma-separated + * list elements into multiple elements. Return 0 on success. + * Warn and return -1 on failure. + */ +static int +normalize_nickname_list(config_line_t **normalized_out, + const config_line_t *lst, const char *name, + char **msg) +{ + if (!lst) + return 0; + + config_line_t *new_nicknames = NULL; + config_line_t **new_nicknames_next = &new_nicknames; + + const config_line_t *cl; + for (cl = lst; cl; cl = cl->next) { + const char *line = cl->value; + if (!line) + continue; + + int valid_line = 1; + smartlist_t *sl = smartlist_new(); + smartlist_split_string(sl, line, ",", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0); + SMARTLIST_FOREACH_BEGIN(sl, char *, s) + { + char *normalized = NULL; + if (!is_legal_nickname_or_hexdigest(s)) { + // check if first char is dollar + if (s[0] != '$') { + // Try again but with a dollar symbol prepended + char *prepended; + tor_asprintf(&prepended, "$%s", s); + + if (is_legal_nickname_or_hexdigest(prepended)) { + // The nickname is valid when it's prepended, set it as the + // normalized version + normalized = prepended; + } else { + // Still not valid, free and fallback to error message + tor_free(prepended); + } + } + + if (!normalized) { + tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name); + valid_line = 0; + break; + } + } else { + normalized = tor_strdup(s); + } + + config_line_t *next = tor_malloc_zero(sizeof(*next)); + next->key = tor_strdup(cl->key); + next->value = normalized; + next->next = NULL; + + *new_nicknames_next = next; + new_nicknames_next = &next->next; + } SMARTLIST_FOREACH_END(s); + + SMARTLIST_FOREACH(sl, char *, s, tor_free(s)); + smartlist_free(sl); + + if (!valid_line) { + config_free_lines(new_nicknames); + return -1; + } + } + + *normalized_out = new_nicknames; + + return 0; +} + +/** Learn config file name from command line arguments, or use the default. + * + * If <b>defaults_file</b> is true, we're looking for torrc-defaults; + * otherwise, we're looking for the regular torrc_file. + * + * Set *<b>using_default_fname</b> to true if we're using the default + * configuration file name; or false if we've set it from the command line. + * + * Set *<b>ignore_missing_torrc</b> to true if we should ignore the resulting + * filename if it doesn't exist. + */ +static char * +find_torrc_filename(config_line_t *cmd_arg, + int defaults_file, + int *using_default_fname, int *ignore_missing_torrc) +{ + char *fname=NULL; + config_line_t *p_index; + const char *fname_opt = defaults_file ? "--defaults-torrc" : "-f"; + const char *ignore_opt = defaults_file ? NULL : "--ignore-missing-torrc"; + + if (defaults_file) + *ignore_missing_torrc = 1; + + for (p_index = cmd_arg; p_index; p_index = p_index->next) { + if (!strcmp(p_index->key, fname_opt)) { + if (fname) { + log_warn(LD_CONFIG, "Duplicate %s options on command line.", + fname_opt); + tor_free(fname); + } + fname = expand_filename(p_index->value); + + { + char *absfname; + absfname = make_path_absolute(fname); + tor_free(fname); + fname = absfname; + } + + *using_default_fname = 0; + } else if (ignore_opt && !strcmp(p_index->key,ignore_opt)) { + *ignore_missing_torrc = 1; + } + } + + if (*using_default_fname) { + /* didn't find one, try CONFDIR */ + const char *dflt = get_default_conf_file(defaults_file); + file_status_t st = file_status(dflt); + if (dflt && (st == FN_FILE || st == FN_EMPTY)) { + fname = tor_strdup(dflt); + } else { +#ifndef _WIN32 + char *fn = NULL; + if (!defaults_file) { + fn = expand_filename("~/.torrc"); + } + if (fn) { + file_status_t hmst = file_status(fn); + if (hmst == FN_FILE || hmst == FN_EMPTY || dflt == NULL) { + fname = fn; + } else { + tor_free(fn); + fname = tor_strdup(dflt); + } + } else { + fname = dflt ? tor_strdup(dflt) : NULL; + } +#else /* !(!defined(_WIN32)) */ + fname = dflt ? tor_strdup(dflt) : NULL; +#endif /* !defined(_WIN32) */ + } + } + return fname; +} + +/** Read the torrc from standard input and return it as a string. + * Upon failure, return NULL. + */ +static char * +load_torrc_from_stdin(void) +{ + size_t sz_out; + + return read_file_to_str_until_eof(STDIN_FILENO,SIZE_MAX,&sz_out); +} + +/** Load a configuration file from disk, setting torrc_fname or + * torrc_defaults_fname if successful. + * + * If <b>defaults_file</b> is true, load torrc-defaults; otherwise load torrc. + * + * Return the contents of the file on success, and NULL on failure. + */ +static char * +load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file) +{ + char *fname=NULL; + char *cf = NULL; + int using_default_torrc = 1; + int ignore_missing_torrc = 0; + char **fname_var = defaults_file ? &torrc_defaults_fname : &torrc_fname; + + if (*fname_var == NULL) { + fname = find_torrc_filename(cmd_arg, defaults_file, + &using_default_torrc, &ignore_missing_torrc); + tor_free(*fname_var); + *fname_var = fname; + } else { + fname = *fname_var; + } + log_debug(LD_CONFIG, "Opening config file \"%s\"", fname?fname:"<NULL>"); + + /* Open config file */ + file_status_t st = fname ? file_status(fname) : FN_EMPTY; + if (fname == NULL || + !(st == FN_FILE || st == FN_EMPTY) || + !(cf = read_file_to_str(fname,0,NULL))) { + if (using_default_torrc == 1 || ignore_missing_torrc) { + if (!defaults_file) + log_notice(LD_CONFIG, "Configuration file \"%s\" not present, " + "using reasonable defaults.", fname); + tor_free(fname); /* sets fname to NULL */ + *fname_var = NULL; + cf = tor_strdup(""); + } else { + log_warn(LD_CONFIG, + "Unable to open configuration file \"%s\".", fname); + goto err; + } + } else { + log_notice(LD_CONFIG, "Read configuration file \"%s\".", fname); + } + + return cf; + err: + tor_free(fname); + *fname_var = NULL; + return NULL; +} + +/** Read a configuration file into <b>options</b>, finding the configuration + * file location based on the command line. After loading the file + * call options_init_from_string() to load the config. + * Return 0 if success, -1 if failure, and 1 if we succeeded but should exit + * anyway. */ +int +options_init_from_torrc(int argc, char **argv) +{ + char *cf=NULL, *cf_defaults=NULL; + int command; + int retval = -1; + char *command_arg = NULL; + char *errmsg=NULL; + config_line_t *p_index = NULL; + config_line_t *cmdline_only_options = NULL; + + /* Go through command-line variables */ + if (! have_parsed_cmdline) { + /* Or we could redo the list every time we pass this place. + * It does not really matter */ + if (config_parse_commandline(argc, argv, 0, &global_cmdline_options, + &global_cmdline_only_options) < 0) { + goto err; + } + have_parsed_cmdline = 1; + } + cmdline_only_options = global_cmdline_only_options; + + if (config_line_find(cmdline_only_options, "-h") || + config_line_find(cmdline_only_options, "--help")) { + print_usage(); + return 1; + } + if (config_line_find(cmdline_only_options, "--list-torrc-options")) { + /* For validating whether we've documented everything. */ + list_torrc_options(); + return 1; + } + if (config_line_find(cmdline_only_options, "--list-deprecated-options")) { + /* For validating whether what we have deprecated really exists. */ + list_deprecated_options(); + return 1; + } + + if (config_line_find(cmdline_only_options, "--version")) { + printf("Tor version %s.\n",get_version()); + return 1; + } + + if (config_line_find(cmdline_only_options, "--list-modules")) { + list_enabled_modules(); + return 1; + } + + if (config_line_find(cmdline_only_options, "--library-versions")) { + printf("Tor version %s. \n", get_version()); + printf("Library versions\tCompiled\t\tRuntime\n"); + printf("Libevent\t\t%-15s\t\t%s\n", + tor_libevent_get_header_version_str(), + tor_libevent_get_version_str()); +#ifdef ENABLE_OPENSSL + printf("OpenSSL \t\t%-15s\t\t%s\n", + crypto_openssl_get_header_version_str(), + crypto_openssl_get_version_str()); +#endif +#ifdef ENABLE_NSS + printf("NSS \t\t%-15s\t\t%s\n", + crypto_nss_get_header_version_str(), + crypto_nss_get_version_str()); +#endif + if (tor_compress_supports_method(ZLIB_METHOD)) { + printf("Zlib \t\t%-15s\t\t%s\n", + tor_compress_version_str(ZLIB_METHOD), + tor_compress_header_version_str(ZLIB_METHOD)); + } + if (tor_compress_supports_method(LZMA_METHOD)) { + printf("Liblzma \t\t%-15s\t\t%s\n", + tor_compress_version_str(LZMA_METHOD), + tor_compress_header_version_str(LZMA_METHOD)); + } + if (tor_compress_supports_method(ZSTD_METHOD)) { + printf("Libzstd \t\t%-15s\t\t%s\n", + tor_compress_version_str(ZSTD_METHOD), + tor_compress_header_version_str(ZSTD_METHOD)); + } + //TODO: Hex versions? + return 1; + } + + command = CMD_RUN_TOR; + for (p_index = cmdline_only_options; p_index; p_index = p_index->next) { + if (!strcmp(p_index->key,"--keygen")) { + command = CMD_KEYGEN; + } else if (!strcmp(p_index->key, "--key-expiration")) { + command = CMD_KEY_EXPIRATION; + command_arg = p_index->value; + } else if (!strcmp(p_index->key,"--list-fingerprint")) { + command = CMD_LIST_FINGERPRINT; + } else if (!strcmp(p_index->key, "--hash-password")) { + command = CMD_HASH_PASSWORD; + command_arg = p_index->value; + } else if (!strcmp(p_index->key, "--dump-config")) { + command = CMD_DUMP_CONFIG; + command_arg = p_index->value; + } else if (!strcmp(p_index->key, "--verify-config")) { + command = CMD_VERIFY_CONFIG; + } + } + + if (command == CMD_HASH_PASSWORD) { + cf_defaults = tor_strdup(""); + cf = tor_strdup(""); + } else { + cf_defaults = load_torrc_from_disk(cmdline_only_options, 1); + + const config_line_t *f_line = config_line_find(cmdline_only_options, + "-f"); + + const int read_torrc_from_stdin = + (f_line != NULL && strcmp(f_line->value, "-") == 0); + + if (read_torrc_from_stdin) { + cf = load_torrc_from_stdin(); + } else { + cf = load_torrc_from_disk(cmdline_only_options, 0); + } + + if (!cf) { + if (config_line_find(cmdline_only_options, "--allow-missing-torrc")) { + cf = tor_strdup(""); + } else { + goto err; + } + } + } + + retval = options_init_from_string(cf_defaults, cf, command, command_arg, + &errmsg); + + if (retval < 0) + goto err; + + if (config_line_find(cmdline_only_options, "--no-passphrase")) { + if (command == CMD_KEYGEN) { + get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF; + } else { + log_err(LD_CONFIG, "--no-passphrase specified without --keygen!"); + retval = -1; + goto err; + } + } + + if (config_line_find(cmdline_only_options, "--newpass")) { + if (command == CMD_KEYGEN) { + get_options_mutable()->change_key_passphrase = 1; + } else { + log_err(LD_CONFIG, "--newpass specified without --keygen!"); + retval = -1; + goto err; + } + } + + { + const config_line_t *fd_line = config_line_find(cmdline_only_options, + "--passphrase-fd"); + if (fd_line) { + if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { + log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!"); + retval = -1; + goto err; + } else if (command != CMD_KEYGEN) { + log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!"); + retval = -1; + goto err; + } else { + const char *v = fd_line->value; + int ok = 1; + long fd = tor_parse_long(v, 10, 0, INT_MAX, &ok, NULL); + if (fd < 0 || ok == 0) { + log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(v)); + retval = -1; + goto err; + } + get_options_mutable()->keygen_passphrase_fd = (int)fd; + get_options_mutable()->use_keygen_passphrase_fd = 1; + get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_ON; + } + } + } + + { + const config_line_t *key_line = config_line_find(cmdline_only_options, + "--master-key"); + if (key_line) { + if (command != CMD_KEYGEN) { + log_err(LD_CONFIG, "--master-key without --keygen!"); + retval = -1; + goto err; + } else { + get_options_mutable()->master_key_fname = tor_strdup(key_line->value); + } + } + } + + err: + + tor_free(cf); + tor_free(cf_defaults); + if (errmsg) { + log_warn(LD_CONFIG,"%s", errmsg); + tor_free(errmsg); + } + return retval < 0 ? -1 : 0; +} + +/** Load the options from the configuration in <b>cf</b>, validate + * them for consistency and take actions based on them. + * + * Return 0 if success, negative on error: + * * -1 for general errors. + * * -2 for failure to parse/validate, + * * -3 for transition not allowed + * * -4 for error while setting the new options + */ +setopt_err_t +options_init_from_string(const char *cf_defaults, const char *cf, + int command, const char *command_arg, + char **msg) +{ + or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL; + config_line_t *cl; + int retval; + setopt_err_t err = SETOPT_ERR_MISC; + int cf_has_include = 0; + tor_assert(msg); + + oldoptions = global_options; /* get_options unfortunately asserts if + this is the first time we run*/ + + newoptions = tor_malloc_zero(sizeof(or_options_t)); + newoptions->magic_ = OR_OPTIONS_MAGIC; + options_init(newoptions); + newoptions->command = command; + newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; + + smartlist_t *opened_files = smartlist_new(); + for (int i = 0; i < 2; ++i) { + const char *body = i==0 ? cf_defaults : cf; + if (!body) + continue; + + /* get config lines, assign them */ + retval = config_get_lines_include(body, &cl, 1, + body == cf ? &cf_has_include : NULL, + opened_files); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + retval = config_assign(&options_format, newoptions, cl, + CAL_WARN_DEPRECATIONS, msg); + config_free_lines(cl); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + if (i==0) + newdefaultoptions = config_dup(&options_format, newoptions); + } + + if (newdefaultoptions == NULL) { + newdefaultoptions = config_dup(&options_format, global_default_options); + } + + /* Go through command-line variables too */ + retval = config_assign(&options_format, newoptions, + global_cmdline_options, CAL_WARN_DEPRECATIONS, msg); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + + newoptions->IncludeUsed = cf_has_include; + newoptions->FilesOpenedByIncludes = opened_files; + + /* If this is a testing network configuration, change defaults + * for a list of dependent config options, re-initialize newoptions + * with the new defaults, and assign all options to it second time. */ + if (newoptions->TestingTorNetwork) { + /* XXXX this is a bit of a kludge. perhaps there's a better way to do + * this? We could, for example, make the parsing algorithm do two passes + * over the configuration. If it finds any "suite" options like + * TestingTorNetwork, it could change the defaults before its second pass. + * Not urgent so long as this seems to work, but at any sign of trouble, + * let's clean it up. -NM */ + + /* Change defaults. */ + for (int i = 0; testing_tor_network_defaults[i].name; ++i) { + const config_var_t *new_var = &testing_tor_network_defaults[i]; + config_var_t *old_var = + config_find_option_mutable(&options_format, new_var->name); + tor_assert(new_var); + tor_assert(old_var); + old_var->initvalue = new_var->initvalue; + + if ((config_find_deprecation(&options_format, new_var->name))) { + log_warn(LD_GENERAL, "Testing options override the deprecated " + "option %s. Is that intentional?", + new_var->name); + } + } + + /* Clear newoptions and re-initialize them with new defaults. */ + or_options_free(newoptions); + or_options_free(newdefaultoptions); + newdefaultoptions = NULL; + newoptions = tor_malloc_zero(sizeof(or_options_t)); + newoptions->magic_ = OR_OPTIONS_MAGIC; + options_init(newoptions); + newoptions->command = command; + newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; + + /* Assign all options a second time. */ + opened_files = smartlist_new(); + for (int i = 0; i < 2; ++i) { + const char *body = i==0 ? cf_defaults : cf; + if (!body) + continue; + + /* get config lines, assign them */ + retval = config_get_lines_include(body, &cl, 1, + body == cf ? &cf_has_include : NULL, + opened_files); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + retval = config_assign(&options_format, newoptions, cl, 0, msg); + config_free_lines(cl); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + if (i==0) + newdefaultoptions = config_dup(&options_format, newoptions); + } + /* Assign command-line variables a second time too */ + retval = config_assign(&options_format, newoptions, + global_cmdline_options, 0, msg); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + } + + newoptions->IncludeUsed = cf_has_include; + in_option_validation = 1; + newoptions->FilesOpenedByIncludes = opened_files; + + /* Validate newoptions */ + if (options_validate(oldoptions, newoptions, newdefaultoptions, + 0, msg) < 0) { + err = SETOPT_ERR_PARSE; /*XXX make this a separate return value.*/ + goto err; + } + + if (options_transition_allowed(oldoptions, newoptions, msg) < 0) { + err = SETOPT_ERR_TRANSITION; + goto err; + } + in_option_validation = 0; + + if (set_options(newoptions, msg)) { + err = SETOPT_ERR_SETTING; + goto err; /* frees and replaces old options */ + } + + or_options_free(global_default_options); + global_default_options = newdefaultoptions; + + return SETOPT_OK; + + err: + in_option_validation = 0; + if (opened_files) { + SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f)); + smartlist_free(opened_files); + } + // may have been set to opened_files, avoid double free + newoptions->FilesOpenedByIncludes = NULL; + or_options_free(newoptions); + or_options_free(newdefaultoptions); + if (*msg) { + char *old_msg = *msg; + tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg); + tor_free(old_msg); + } + return err; +} + +/** Return the location for our configuration file. May return NULL. + */ +const char * +get_torrc_fname(int defaults_fname) +{ + const char *fname = defaults_fname ? torrc_defaults_fname : torrc_fname; + + if (fname) + return fname; + else + return get_default_conf_file(defaults_fname); +} + +/** Adjust the address map based on the MapAddress elements in the + * configuration <b>options</b> + */ +void +config_register_addressmaps(const or_options_t *options) +{ + smartlist_t *elts; + config_line_t *opt; + const char *from, *to, *msg; + + addressmap_clear_configured(); + elts = smartlist_new(); + for (opt = options->AddressMap; opt; opt = opt->next) { + smartlist_split_string(elts, opt->value, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); + if (smartlist_len(elts) < 2) { + log_warn(LD_CONFIG,"MapAddress '%s' has too few arguments. Ignoring.", + opt->value); + goto cleanup; + } + + from = smartlist_get(elts,0); + to = smartlist_get(elts,1); + + if (to[0] == '.' || from[0] == '.') { + log_warn(LD_CONFIG,"MapAddress '%s' is ambiguous - address starts with a" + "'.'. Ignoring.",opt->value); + goto cleanup; + } + + if (addressmap_register_auto(from, to, 0, ADDRMAPSRC_TORRC, &msg) < 0) { + log_warn(LD_CONFIG,"MapAddress '%s' failed: %s. Ignoring.", opt->value, + msg); + goto cleanup; + } + + if (smartlist_len(elts) > 2) + log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress."); + + cleanup: + SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp)); + smartlist_clear(elts); + } + smartlist_free(elts); +} + +/** As addressmap_register(), but detect the wildcarded status of "from" and + * "to", and do not steal a reference to <b>to</b>. */ +/* XXXX move to connection_edge.c */ +int +addressmap_register_auto(const char *from, const char *to, + time_t expires, + addressmap_entry_source_t addrmap_source, + const char **msg) +{ + int from_wildcard = 0, to_wildcard = 0; + + *msg = "whoops, forgot the error message"; + + if (!strcmp(to, "*") || !strcmp(from, "*")) { + *msg = "can't remap from or to *"; + return -1; + } + /* Detect asterisks in expressions of type: '*.example.com' */ + if (!strncmp(from,"*.",2)) { + from += 2; + from_wildcard = 1; + } + if (!strncmp(to,"*.",2)) { + to += 2; + to_wildcard = 1; + } + + if (to_wildcard && !from_wildcard) { + *msg = "can only use wildcard (i.e. '*.') if 'from' address " + "uses wildcard also"; + return -1; + } + + if (address_is_invalid_destination(to, 1)) { + *msg = "destination is invalid"; + return -1; + } + + addressmap_register(from, tor_strdup(to), expires, addrmap_source, + from_wildcard, to_wildcard); + + return 0; +} + +/** + * As add_file_log, but open the file as appropriate. + */ +STATIC int +open_and_add_file_log(const log_severity_list_t *severity, + const char *filename, int truncate_log) +{ + int open_flags = O_WRONLY|O_CREAT; + open_flags |= truncate_log ? O_TRUNC : O_APPEND; + + int fd = tor_open_cloexec(filename, open_flags, 0640); + if (fd < 0) + return -1; + + return add_file_log(severity, filename, fd); +} + +/** + * Initialize the logs based on the configuration file. + */ +static int +options_init_logs(const or_options_t *old_options, or_options_t *options, + int validate_only) +{ + config_line_t *opt; + int ok; + smartlist_t *elts; + int run_as_daemon = +#ifdef _WIN32 + 0; +#else + options->RunAsDaemon; +#endif + + if (options->LogTimeGranularity <= 0) { + log_warn(LD_CONFIG, "Log time granularity '%d' has to be positive.", + options->LogTimeGranularity); + return -1; + } else if (1000 % options->LogTimeGranularity != 0 && + options->LogTimeGranularity % 1000 != 0) { + int granularity = options->LogTimeGranularity; + if (granularity < 40) { + do granularity++; + while (1000 % granularity != 0); + } else if (granularity < 1000) { + granularity = 1000 / granularity; + while (1000 % granularity != 0) + granularity--; + granularity = 1000 / granularity; + } else { + granularity = 1000 * ((granularity / 1000) + 1); + } + log_warn(LD_CONFIG, "Log time granularity '%d' has to be either a " + "divisor or a multiple of 1 second. Changing to " + "'%d'.", + options->LogTimeGranularity, granularity); + if (!validate_only) + set_log_time_granularity(granularity); + } else { + if (!validate_only) + set_log_time_granularity(options->LogTimeGranularity); + } + + ok = 1; + elts = smartlist_new(); + + for (opt = options->Logs; opt; opt = opt->next) { + log_severity_list_t *severity; + const char *cfg = opt->value; + severity = tor_malloc_zero(sizeof(log_severity_list_t)); + if (parse_log_severity_config(&cfg, severity) < 0) { + log_warn(LD_CONFIG, "Couldn't parse log levels in Log option 'Log %s'", + opt->value); + ok = 0; goto cleanup; + } + + smartlist_split_string(elts, cfg, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); + + if (smartlist_len(elts) == 0) + smartlist_add_strdup(elts, "stdout"); + + if (smartlist_len(elts) == 1 && + (!strcasecmp(smartlist_get(elts,0), "stdout") || + !strcasecmp(smartlist_get(elts,0), "stderr"))) { + int err = smartlist_len(elts) && + !strcasecmp(smartlist_get(elts,0), "stderr"); + if (!validate_only) { + if (run_as_daemon) { + log_warn(LD_CONFIG, + "Can't log to %s with RunAsDaemon set; skipping stdout", + err?"stderr":"stdout"); + } else { + add_stream_log(severity, err?"<stderr>":"<stdout>", + fileno(err?stderr:stdout)); + } + } + goto cleanup; + } + if (smartlist_len(elts) == 1) { + if (!strcasecmp(smartlist_get(elts,0), "syslog")) { +#ifdef HAVE_SYSLOG_H + if (!validate_only) { + add_syslog_log(severity, options->SyslogIdentityTag); + } +#else + log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); +#endif /* defined(HAVE_SYSLOG_H) */ + goto cleanup; + } + + if (!strcasecmp(smartlist_get(elts, 0), "android")) { +#ifdef HAVE_ANDROID_LOG_H + if (!validate_only) { + add_android_log(severity, options->AndroidIdentityTag); + } +#else + log_warn(LD_CONFIG, "Android logging is not supported" + " on this system. Sorry."); +#endif // HAVE_ANDROID_LOG_H. + goto cleanup; + } + } + + if (smartlist_len(elts) == 2 && + !strcasecmp(smartlist_get(elts,0), "file")) { + if (!validate_only) { + char *fname = expand_filename(smartlist_get(elts, 1)); + /* Truncate if TruncateLogFile is set and we haven't seen this option + line before. */ + int truncate_log = 0; + if (options->TruncateLogFile) { + truncate_log = 1; + if (old_options) { + config_line_t *opt2; + for (opt2 = old_options->Logs; opt2; opt2 = opt2->next) + if (!strcmp(opt->value, opt2->value)) { + truncate_log = 0; + break; + } + } + } + if (open_and_add_file_log(severity, fname, truncate_log) < 0) { + log_warn(LD_CONFIG, "Couldn't open file for 'Log %s': %s", + opt->value, strerror(errno)); + ok = 0; + } + tor_free(fname); + } + goto cleanup; + } + + log_warn(LD_CONFIG, "Bad syntax on file Log option 'Log %s'", + opt->value); + ok = 0; goto cleanup; + + cleanup: + SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp)); + smartlist_clear(elts); + tor_free(severity); + } + smartlist_free(elts); + + if (ok && !validate_only) + logs_set_domain_logging(options->LogMessageDomains); + + return ok?0:-1; +} + +/** Given a smartlist of SOCKS arguments to be passed to a transport + * proxy in <b>args</b>, validate them and return -1 if they are + * corrupted. Return 0 if they seem OK. */ +static int +validate_transport_socks_arguments(const smartlist_t *args) +{ + char *socks_string = NULL; + size_t socks_string_len; + + tor_assert(args); + tor_assert(smartlist_len(args) > 0); + + SMARTLIST_FOREACH_BEGIN(args, const char *, s) { + if (!string_is_key_value(LOG_WARN, s)) { /* items should be k=v items */ + log_warn(LD_CONFIG, "'%s' is not a k=v item.", s); + return -1; + } + } SMARTLIST_FOREACH_END(s); + + socks_string = pt_stringify_socks_args(args); + if (!socks_string) + return -1; + + socks_string_len = strlen(socks_string); + tor_free(socks_string); + + if (socks_string_len > MAX_SOCKS5_AUTH_SIZE_TOTAL) { + log_warn(LD_CONFIG, "SOCKS arguments can't be more than %u bytes (%lu).", + MAX_SOCKS5_AUTH_SIZE_TOTAL, + (unsigned long) socks_string_len); + return -1; + } + + return 0; +} + +/** Deallocate a bridge_line_t structure. */ +/* private */ void +bridge_line_free_(bridge_line_t *bridge_line) +{ + if (!bridge_line) + return; + + if (bridge_line->socks_args) { + SMARTLIST_FOREACH(bridge_line->socks_args, char*, s, tor_free(s)); + smartlist_free(bridge_line->socks_args); + } + tor_free(bridge_line->transport_name); + tor_free(bridge_line); +} + +/** Parse the contents of a string, <b>line</b>, containing a Bridge line, + * into a bridge_line_t. + * + * Validates that the IP:PORT, fingerprint, and SOCKS arguments (given to the + * Pluggable Transport, if a one was specified) are well-formed. + * + * Returns NULL If the Bridge line could not be validated, and returns a + * bridge_line_t containing the parsed information otherwise. + * + * Bridge line format: + * Bridge [transport] IP:PORT [id-fingerprint] [k=v] [k=v] ... + */ +/* private */ bridge_line_t * +parse_bridge_line(const char *line) +{ + smartlist_t *items = NULL; + char *addrport=NULL, *fingerprint=NULL; + char *field=NULL; + bridge_line_t *bridge_line = tor_malloc_zero(sizeof(bridge_line_t)); + + items = smartlist_new(); + smartlist_split_string(items, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + if (smartlist_len(items) < 1) { + log_warn(LD_CONFIG, "Too few arguments to Bridge line."); + goto err; + } + + /* first field is either a transport name or addrport */ + field = smartlist_get(items, 0); + smartlist_del_keeporder(items, 0); + + if (string_is_C_identifier(field)) { + /* It's a transport name. */ + bridge_line->transport_name = field; + if (smartlist_len(items) < 1) { + log_warn(LD_CONFIG, "Too few items to Bridge line."); + goto err; + } + addrport = smartlist_get(items, 0); /* Next field is addrport then. */ + smartlist_del_keeporder(items, 0); + } else { + addrport = field; + } + + if (tor_addr_port_parse(LOG_INFO, addrport, + &bridge_line->addr, &bridge_line->port, 443)<0) { + log_warn(LD_CONFIG, "Error parsing Bridge address '%s'", addrport); + goto err; + } + + /* If transports are enabled, next field could be a fingerprint or a + socks argument. If transports are disabled, next field must be + a fingerprint. */ + if (smartlist_len(items)) { + if (bridge_line->transport_name) { /* transports enabled: */ + field = smartlist_get(items, 0); + smartlist_del_keeporder(items, 0); + + /* If it's a key=value pair, then it's a SOCKS argument for the + transport proxy... */ + if (string_is_key_value(LOG_DEBUG, field)) { + bridge_line->socks_args = smartlist_new(); + smartlist_add(bridge_line->socks_args, field); + } else { /* ...otherwise, it's the bridge fingerprint. */ + fingerprint = field; + } + + } else { /* transports disabled: */ + fingerprint = smartlist_join_strings(items, "", 0, NULL); + } + } + + /* Handle fingerprint, if it was provided. */ + if (fingerprint) { + if (strlen(fingerprint) != HEX_DIGEST_LEN) { + log_warn(LD_CONFIG, "Key digest for Bridge is wrong length."); + goto err; + } + if (base16_decode(bridge_line->digest, DIGEST_LEN, + fingerprint, HEX_DIGEST_LEN) != DIGEST_LEN) { + log_warn(LD_CONFIG, "Unable to decode Bridge key digest."); + goto err; + } + } + + /* If we are using transports, any remaining items in the smartlist + should be k=v values. */ + if (bridge_line->transport_name && smartlist_len(items)) { + if (!bridge_line->socks_args) + bridge_line->socks_args = smartlist_new(); + + /* append remaining items of 'items' to 'socks_args' */ + smartlist_add_all(bridge_line->socks_args, items); + smartlist_clear(items); + + tor_assert(smartlist_len(bridge_line->socks_args) > 0); + } + + if (bridge_line->socks_args) { + if (validate_transport_socks_arguments(bridge_line->socks_args) < 0) + goto err; + } + + goto done; + + err: + bridge_line_free(bridge_line); + bridge_line = NULL; + + done: + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + tor_free(addrport); + tor_free(fingerprint); + + return bridge_line; +} + +/** Read the contents of a ClientTransportPlugin or ServerTransportPlugin + * line from <b>line</b>, depending on the value of <b>server</b>. Return 0 + * if the line is well-formed, and -1 if it isn't. + * + * If <b>validate_only</b> is 0, the line is well-formed, and the transport is + * needed by some bridge: + * - If it's an external proxy line, add the transport described in the line to + * our internal transport list. + * - If it's a managed proxy line, launch the managed proxy. + */ + +STATIC int +parse_transport_line(const or_options_t *options, + const char *line, int validate_only, + int server) +{ + + smartlist_t *items = NULL; + int r; + const char *transports = NULL; + smartlist_t *transport_list = NULL; + char *type = NULL; + char *addrport = NULL; + tor_addr_t addr; + uint16_t port = 0; + int socks_ver = PROXY_NONE; + + /* managed proxy options */ + int is_managed = 0; + char **proxy_argv = NULL; + char **tmp = NULL; + int proxy_argc, i; + int is_useless_proxy = 1; + + int line_length; + + /* Split the line into space-separated tokens */ + items = smartlist_new(); + smartlist_split_string(items, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + line_length = smartlist_len(items); + + if (line_length < 3) { + log_warn(LD_CONFIG, + "Too few arguments on %sTransportPlugin line.", + server ? "Server" : "Client"); + goto err; + } + + /* Get the first line element, split it to commas into + transport_list (in case it's multiple transports) and validate + the transport names. */ + transports = smartlist_get(items, 0); + transport_list = smartlist_new(); + smartlist_split_string(transport_list, transports, ",", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport_name) { + /* validate transport names */ + if (!string_is_C_identifier(transport_name)) { + log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).", + transport_name); + goto err; + } + + /* see if we actually need the transports provided by this proxy */ + if (!validate_only && transport_is_needed(transport_name)) + is_useless_proxy = 0; + } SMARTLIST_FOREACH_END(transport_name); + + type = smartlist_get(items, 1); + if (!strcmp(type, "exec")) { + is_managed = 1; + } else if (server && !strcmp(type, "proxy")) { + /* 'proxy' syntax only with ServerTransportPlugin */ + is_managed = 0; + } else if (!server && !strcmp(type, "socks4")) { + /* 'socks4' syntax only with ClientTransportPlugin */ + is_managed = 0; + socks_ver = PROXY_SOCKS4; + } else if (!server && !strcmp(type, "socks5")) { + /* 'socks5' syntax only with ClientTransportPlugin */ + is_managed = 0; + socks_ver = PROXY_SOCKS5; + } else { + log_warn(LD_CONFIG, + "Strange %sTransportPlugin type '%s'", + server ? "Server" : "Client", type); + goto err; + } + + if (is_managed && options->Sandbox) { + log_warn(LD_CONFIG, + "Managed proxies are not compatible with Sandbox mode." + "(%sTransportPlugin line was %s)", + server ? "Server" : "Client", escaped(line)); + goto err; + } + + if (is_managed && options->NoExec) { + log_warn(LD_CONFIG, + "Managed proxies are not compatible with NoExec mode; ignoring." + "(%sTransportPlugin line was %s)", + server ? "Server" : "Client", escaped(line)); + r = 0; + goto done; + } + + if (is_managed) { + /* managed */ + + if (!server && !validate_only && is_useless_proxy) { + log_info(LD_GENERAL, + "Pluggable transport proxy (%s) does not provide " + "any needed transports and will not be launched.", + line); + } + + /* + * If we are not just validating, use the rest of the line as the + * argv of the proxy to be launched. Also, make sure that we are + * only launching proxies that contribute useful transports. + */ + + if (!validate_only && (server || !is_useless_proxy)) { + proxy_argc = line_length - 2; + tor_assert(proxy_argc > 0); + proxy_argv = tor_calloc((proxy_argc + 1), sizeof(char *)); + tmp = proxy_argv; + + for (i = 0; i < proxy_argc; i++) { + /* store arguments */ + *tmp++ = smartlist_get(items, 2); + smartlist_del_keeporder(items, 2); + } + *tmp = NULL; /* terminated with NULL, just like execve() likes it */ + + /* kickstart the thing */ + if (server) { + pt_kickstart_server_proxy(transport_list, proxy_argv); + } else { + pt_kickstart_client_proxy(transport_list, proxy_argv); + } + } + } else { + /* external */ + + /* ClientTransportPlugins connecting through a proxy is managed only. */ + if (!server && (options->Socks4Proxy || options->Socks5Proxy || + options->HTTPSProxy)) { + log_warn(LD_CONFIG, "You have configured an external proxy with another " + "proxy type. (Socks4Proxy|Socks5Proxy|HTTPSProxy)"); + goto err; + } + + if (smartlist_len(transport_list) != 1) { + log_warn(LD_CONFIG, + "You can't have an external proxy with more than " + "one transport."); + goto err; + } + + addrport = smartlist_get(items, 2); + + if (tor_addr_port_lookup(addrport, &addr, &port) < 0) { + log_warn(LD_CONFIG, + "Error parsing transport address '%s'", addrport); + goto err; + } + + if (!port) { + log_warn(LD_CONFIG, + "Transport address '%s' has no port.", addrport); + goto err; + } + + if (!validate_only) { + log_info(LD_DIR, "%s '%s' at %s.", + server ? "Server transport" : "Transport", + transports, fmt_addrport(&addr, port)); + + if (!server) { + transport_add_from_config(&addr, port, + smartlist_get(transport_list, 0), + socks_ver); + } + } + } + + r = 0; + goto done; + + err: + r = -1; + + done: + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + if (transport_list) { + SMARTLIST_FOREACH(transport_list, char*, s, tor_free(s)); + smartlist_free(transport_list); + } + + return r; +} + +/** Given a ServerTransportListenAddr <b>line</b>, return its + * <address:port> string. Return NULL if the line was not + * well-formed. + * + * If <b>transport</b> is set, return NULL if the line is not + * referring to <b>transport</b>. + * + * The returned string is allocated on the heap and it's the + * responsibility of the caller to free it. */ +static char * +get_bindaddr_from_transport_listen_line(const char *line,const char *transport) +{ + smartlist_t *items = NULL; + const char *parsed_transport = NULL; + char *addrport = NULL; + tor_addr_t addr; + uint16_t port = 0; + + items = smartlist_new(); + smartlist_split_string(items, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + + if (smartlist_len(items) < 2) { + log_warn(LD_CONFIG,"Too few arguments on ServerTransportListenAddr line."); + goto err; + } + + parsed_transport = smartlist_get(items, 0); + addrport = tor_strdup(smartlist_get(items, 1)); + + /* If 'transport' is given, check if it matches the one on the line */ + if (transport && strcmp(transport, parsed_transport)) + goto err; + + /* Validate addrport */ + if (tor_addr_port_parse(LOG_WARN, addrport, &addr, &port, -1)<0) { + log_warn(LD_CONFIG, "Error parsing ServerTransportListenAddr " + "address '%s'", addrport); + goto err; + } + + goto done; + + err: + tor_free(addrport); + addrport = NULL; + + done: + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + + return addrport; +} + +/** Given a ServerTransportOptions <b>line</b>, return a smartlist + * with the options. Return NULL if the line was not well-formed. + * + * If <b>transport</b> is set, return NULL if the line is not + * referring to <b>transport</b>. + * + * The returned smartlist and its strings are allocated on the heap + * and it's the responsibility of the caller to free it. */ +smartlist_t * +get_options_from_transport_options_line(const char *line,const char *transport) +{ + smartlist_t *items = smartlist_new(); + smartlist_t *options = smartlist_new(); + const char *parsed_transport = NULL; + + smartlist_split_string(items, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + + if (smartlist_len(items) < 2) { + log_warn(LD_CONFIG,"Too few arguments on ServerTransportOptions line."); + goto err; + } + + parsed_transport = smartlist_get(items, 0); + /* If 'transport' is given, check if it matches the one on the line */ + if (transport && strcmp(transport, parsed_transport)) + goto err; + + SMARTLIST_FOREACH_BEGIN(items, const char *, option) { + if (option_sl_idx == 0) /* skip the transport field (first field)*/ + continue; + + /* validate that it's a k=v value */ + if (!string_is_key_value(LOG_WARN, option)) { + log_warn(LD_CONFIG, "%s is not a k=v value.", escaped(option)); + goto err; + } + + /* add it to the options smartlist */ + smartlist_add_strdup(options, option); + log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option)); + } SMARTLIST_FOREACH_END(option); + + goto done; + + err: + SMARTLIST_FOREACH(options, char*, s, tor_free(s)); + smartlist_free(options); + options = NULL; + + done: + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + + return options; +} + +/** Given the name of a pluggable transport in <b>transport</b>, check + * the configuration file to see if the user has explicitly asked for + * it to listen on a specific port. Return a <address:port> string if + * so, otherwise NULL. */ +char * +get_transport_bindaddr_from_config(const char *transport) +{ + config_line_t *cl; + const or_options_t *options = get_options(); + + for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) { + char *bindaddr = + get_bindaddr_from_transport_listen_line(cl->value, transport); + if (bindaddr) + return bindaddr; + } + + return NULL; +} + +/** Given the name of a pluggable transport in <b>transport</b>, check + * the configuration file to see if the user has asked us to pass any + * parameters to the pluggable transport. Return a smartlist + * containing the parameters, otherwise NULL. */ +smartlist_t * +get_options_for_server_transport(const char *transport) +{ + config_line_t *cl; + const or_options_t *options = get_options(); + + for (cl = options->ServerTransportOptions; cl; cl = cl->next) { + smartlist_t *options_sl = + get_options_from_transport_options_line(cl->value, transport); + if (options_sl) + return options_sl; + } + + return NULL; +} + +/** Read the contents of a DirAuthority line from <b>line</b>. If + * <b>validate_only</b> is 0, and the line is well-formed, and it + * shares any bits with <b>required_type</b> or <b>required_type</b> + * is NO_DIRINFO (zero), then add the dirserver described in the line + * (minus whatever bits it's missing) as a valid authority. + * Return 0 on success or filtering out by type, + * or -1 if the line isn't well-formed or if we can't add it. */ +STATIC int +parse_dir_authority_line(const char *line, dirinfo_type_t required_type, + int validate_only) +{ + smartlist_t *items = NULL; + int r; + char *addrport=NULL, *address=NULL, *nickname=NULL, *fingerprint=NULL; + tor_addr_port_t ipv6_addrport, *ipv6_addrport_ptr = NULL; + uint16_t dir_port = 0, or_port = 0; + char digest[DIGEST_LEN]; + char v3_digest[DIGEST_LEN]; + dirinfo_type_t type = 0; + double weight = 1.0; + + memset(v3_digest, 0, sizeof(v3_digest)); + + items = smartlist_new(); + smartlist_split_string(items, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + if (smartlist_len(items) < 1) { + log_warn(LD_CONFIG, "No arguments on DirAuthority line."); + goto err; + } + + if (is_legal_nickname(smartlist_get(items, 0))) { + nickname = smartlist_get(items, 0); + smartlist_del_keeporder(items, 0); + } + + while (smartlist_len(items)) { + char *flag = smartlist_get(items, 0); + if (TOR_ISDIGIT(flag[0])) + break; + if (!strcasecmp(flag, "hs") || + !strcasecmp(flag, "no-hs")) { + log_warn(LD_CONFIG, "The DirAuthority options 'hs' and 'no-hs' are " + "obsolete; you don't need them any more."); + } else if (!strcasecmp(flag, "bridge")) { + type |= BRIDGE_DIRINFO; + } else if (!strcasecmp(flag, "no-v2")) { + /* obsolete, but may still be contained in DirAuthority lines generated + by various tools */; + } else if (!strcasecmpstart(flag, "orport=")) { + int ok; + char *portstring = flag + strlen("orport="); + or_port = (uint16_t) tor_parse_long(portstring, 10, 1, 65535, &ok, NULL); + if (!ok) + log_warn(LD_CONFIG, "Invalid orport '%s' on DirAuthority line.", + portstring); + } else if (!strcmpstart(flag, "weight=")) { + int ok; + const char *wstring = flag + strlen("weight="); + weight = tor_parse_double(wstring, 0, (double)UINT64_MAX, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, "Invalid weight '%s' on DirAuthority line.",flag); + weight=1.0; + } + } else if (!strcasecmpstart(flag, "v3ident=")) { + char *idstr = flag + strlen("v3ident="); + if (strlen(idstr) != HEX_DIGEST_LEN || + base16_decode(v3_digest, DIGEST_LEN, + idstr, HEX_DIGEST_LEN) != DIGEST_LEN) { + log_warn(LD_CONFIG, "Bad v3 identity digest '%s' on DirAuthority line", + flag); + } else { + type |= V3_DIRINFO|EXTRAINFO_DIRINFO|MICRODESC_DIRINFO; + } + } else if (!strcasecmpstart(flag, "ipv6=")) { + if (ipv6_addrport_ptr) { + log_warn(LD_CONFIG, "Redundant ipv6 addr/port on DirAuthority line"); + } else { + if (tor_addr_port_parse(LOG_WARN, flag+strlen("ipv6="), + &ipv6_addrport.addr, &ipv6_addrport.port, + -1) < 0 + || tor_addr_family(&ipv6_addrport.addr) != AF_INET6) { + log_warn(LD_CONFIG, "Bad ipv6 addr/port %s on DirAuthority line", + escaped(flag)); + goto err; + } + ipv6_addrport_ptr = &ipv6_addrport; + } + } else { + log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirAuthority line", + flag); + } + tor_free(flag); + smartlist_del_keeporder(items, 0); + } + + if (smartlist_len(items) < 2) { + log_warn(LD_CONFIG, "Too few arguments to DirAuthority line."); + goto err; + } + addrport = smartlist_get(items, 0); + smartlist_del_keeporder(items, 0); + + if (tor_addr_port_split(LOG_WARN, addrport, &address, &dir_port) < 0) { + log_warn(LD_CONFIG, "Error parsing DirAuthority address '%s'.", addrport); + goto err; + } + + if (!string_is_valid_ipv4_address(address)) { + log_warn(LD_CONFIG, "Error parsing DirAuthority address '%s' " + "(invalid IPv4 address)", address); + goto err; + } + + if (!dir_port) { + log_warn(LD_CONFIG, "Missing port in DirAuthority address '%s'",addrport); + goto err; + } + + fingerprint = smartlist_join_strings(items, "", 0, NULL); + if (strlen(fingerprint) != HEX_DIGEST_LEN) { + log_warn(LD_CONFIG, "Key digest '%s' for DirAuthority is wrong length %d.", + fingerprint, (int)strlen(fingerprint)); + goto err; + } + if (base16_decode(digest, DIGEST_LEN, + fingerprint, HEX_DIGEST_LEN) != DIGEST_LEN) { + log_warn(LD_CONFIG, "Unable to decode DirAuthority key digest."); + goto err; + } + + if (!validate_only && (!required_type || required_type & type)) { + dir_server_t *ds; + if (required_type) + type &= required_type; /* pare down what we think of them as an + * authority for. */ + log_debug(LD_DIR, "Trusted %d dirserver at %s:%d (%s)", (int)type, + address, (int)dir_port, (char*)smartlist_get(items,0)); + if (!(ds = trusted_dir_server_new(nickname, address, dir_port, or_port, + ipv6_addrport_ptr, + digest, v3_digest, type, weight))) + goto err; + dir_server_add(ds); + } + + r = 0; + goto done; + + err: + r = -1; + + done: + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + tor_free(addrport); + tor_free(address); + tor_free(nickname); + tor_free(fingerprint); + return r; +} + +/** Read the contents of a FallbackDir line from <b>line</b>. If + * <b>validate_only</b> is 0, and the line is well-formed, then add the + * dirserver described in the line as a fallback directory. Return 0 on + * success, or -1 if the line isn't well-formed or if we can't add it. */ +int +parse_dir_fallback_line(const char *line, + int validate_only) +{ + int r = -1; + smartlist_t *items = smartlist_new(), *positional = smartlist_new(); + int orport = -1; + uint16_t dirport; + tor_addr_t addr; + int ok; + char id[DIGEST_LEN]; + char *address=NULL; + tor_addr_port_t ipv6_addrport, *ipv6_addrport_ptr = NULL; + double weight=1.0; + + memset(id, 0, sizeof(id)); + smartlist_split_string(items, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + SMARTLIST_FOREACH_BEGIN(items, const char *, cp) { + const char *eq = strchr(cp, '='); + ok = 1; + if (! eq) { + smartlist_add(positional, (char*)cp); + continue; + } + if (!strcmpstart(cp, "orport=")) { + orport = (int)tor_parse_long(cp+strlen("orport="), 10, + 1, 65535, &ok, NULL); + } else if (!strcmpstart(cp, "id=")) { + ok = base16_decode(id, DIGEST_LEN, cp+strlen("id="), + strlen(cp)-strlen("id=")) == DIGEST_LEN; + } else if (!strcasecmpstart(cp, "ipv6=")) { + if (ipv6_addrport_ptr) { + log_warn(LD_CONFIG, "Redundant ipv6 addr/port on FallbackDir line"); + } else { + if (tor_addr_port_parse(LOG_WARN, cp+strlen("ipv6="), + &ipv6_addrport.addr, &ipv6_addrport.port, + -1) < 0 + || tor_addr_family(&ipv6_addrport.addr) != AF_INET6) { + log_warn(LD_CONFIG, "Bad ipv6 addr/port %s on FallbackDir line", + escaped(cp)); + goto end; + } + ipv6_addrport_ptr = &ipv6_addrport; + } + } else if (!strcmpstart(cp, "weight=")) { + int num_ok; + const char *wstring = cp + strlen("weight="); + weight = tor_parse_double(wstring, 0, (double)UINT64_MAX, &num_ok, NULL); + if (!num_ok) { + log_warn(LD_CONFIG, "Invalid weight '%s' on FallbackDir line.", cp); + weight=1.0; + } + } + + if (!ok) { + log_warn(LD_CONFIG, "Bad FallbackDir option %s", escaped(cp)); + goto end; + } + } SMARTLIST_FOREACH_END(cp); + + if (smartlist_len(positional) != 1) { + log_warn(LD_CONFIG, "Couldn't parse FallbackDir line %s", escaped(line)); + goto end; + } + + if (tor_digest_is_zero(id)) { + log_warn(LD_CONFIG, "Missing identity on FallbackDir line"); + goto end; + } + + if (orport <= 0) { + log_warn(LD_CONFIG, "Missing orport on FallbackDir line"); + goto end; + } + + if (tor_addr_port_split(LOG_INFO, smartlist_get(positional, 0), + &address, &dirport) < 0 || + tor_addr_parse(&addr, address)<0) { + log_warn(LD_CONFIG, "Couldn't parse address:port %s on FallbackDir line", + (const char*)smartlist_get(positional, 0)); + goto end; + } + + if (!validate_only) { + dir_server_t *ds; + ds = fallback_dir_server_new(&addr, dirport, orport, ipv6_addrport_ptr, + id, weight); + if (!ds) { + log_warn(LD_CONFIG, "Couldn't create FallbackDir %s", escaped(line)); + goto end; + } + dir_server_add(ds); + } + + r = 0; + + end: + SMARTLIST_FOREACH(items, char *, cp, tor_free(cp)); + smartlist_free(items); + smartlist_free(positional); + tor_free(address); + return r; +} + +/** Allocate and return a new port_cfg_t with reasonable defaults. */ +STATIC port_cfg_t * +port_cfg_new(size_t namelen) +{ + tor_assert(namelen <= SIZE_T_CEILING - sizeof(port_cfg_t) - 1); + port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t) + namelen + 1); + cfg->entry_cfg.ipv4_traffic = 1; + cfg->entry_cfg.ipv6_traffic = 1; + cfg->entry_cfg.dns_request = 1; + cfg->entry_cfg.onion_traffic = 1; + cfg->entry_cfg.prefer_ipv6_virtaddr = 1; + return cfg; +} + +/** Free all storage held in <b>port</b> */ +STATIC void +port_cfg_free_(port_cfg_t *port) +{ + tor_free(port); +} + +/** Warn for every port in <b>ports</b> of type <b>listener_type</b> that is + * on a publicly routable address. */ +static void +warn_nonlocal_client_ports(const smartlist_t *ports, + const char *portname, + const int listener_type) +{ + SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { + if (port->type != listener_type) + continue; + if (port->is_unix_addr) { + /* Unix sockets aren't accessible over a network. */ + } else if (!tor_addr_is_internal(&port->addr, 1)) { + log_warn(LD_CONFIG, "You specified a public address '%s' for %sPort. " + "Other people on the Internet might find your computer and " + "use it as an open proxy. Please don't allow this unless you " + "have a good reason.", + fmt_addrport(&port->addr, port->port), portname); + } else if (!tor_addr_is_loopback(&port->addr)) { + log_notice(LD_CONFIG, "You configured a non-loopback address '%s' " + "for %sPort. This allows everybody on your local network to " + "use your machine as a proxy. Make sure this is what you " + "wanted.", + fmt_addrport(&port->addr, port->port), portname); + } + } SMARTLIST_FOREACH_END(port); +} + +/** Warn for every Extended ORPort port in <b>ports</b> that is on a + * publicly routable address. */ +static void +warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname) +{ + SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { + if (port->type != CONN_TYPE_EXT_OR_LISTENER) + continue; + if (port->is_unix_addr) + continue; + /* XXX maybe warn even if address is RFC1918? */ + if (!tor_addr_is_internal(&port->addr, 1)) { + log_warn(LD_CONFIG, "You specified a public address '%s' for %sPort. " + "This is not advised; this address is supposed to only be " + "exposed on localhost so that your pluggable transport " + "proxies can connect to it.", + fmt_addrport(&port->addr, port->port), portname); + } + } SMARTLIST_FOREACH_END(port); +} + +/** Given a list of port_cfg_t in <b>ports</b>, warn if any controller port + * there is listening on any non-loopback address. If <b>forbid_nonlocal</b> + * is true, then emit a stronger warning and remove the port from the list. + */ +static void +warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal) +{ + int warned = 0; + SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) { + if (port->type != CONN_TYPE_CONTROL_LISTENER) + continue; + if (port->is_unix_addr) + continue; + if (!tor_addr_is_loopback(&port->addr)) { + if (forbid_nonlocal) { + if (!warned) + log_warn(LD_CONFIG, + "You have a ControlPort set to accept " + "unauthenticated connections from a non-local address. " + "This means that programs not running on your computer " + "can reconfigure your Tor, without even having to guess a " + "password. That's so bad that I'm closing your ControlPort " + "for you. If you need to control your Tor remotely, try " + "enabling authentication and using a tool like stunnel or " + "ssh to encrypt remote access."); + warned = 1; + port_cfg_free(port); + SMARTLIST_DEL_CURRENT(ports, port); + } else { + log_warn(LD_CONFIG, "You have a ControlPort set to accept " + "connections from a non-local address. This means that " + "programs not running on your computer can reconfigure your " + "Tor. That's pretty bad, since the controller " + "protocol isn't encrypted! Maybe you should just listen on " + "127.0.0.1 and use a tool like stunnel or ssh to encrypt " + "remote connections to your control port."); + return; /* No point in checking the rest */ + } + } + } SMARTLIST_FOREACH_END(port); +} + +/** + * Take a string (<b>line</b>) that begins with either an address:port, a + * port, or an AF_UNIX address, optionally quoted, prefixed with + * "unix:". Parse that line, and on success, set <b>addrport_out</b> to a new + * string containing the beginning portion (without prefix). Iff there was a + * unix: prefix, set <b>is_unix_out</b> to true. On success, also set + * <b>rest_out</b> to point to the part of the line after the address portion. + * + * Return 0 on success, -1 on failure. + */ +int +port_cfg_line_extract_addrport(const char *line, + char **addrport_out, + int *is_unix_out, + const char **rest_out) +{ + tor_assert(line); + tor_assert(addrport_out); + tor_assert(is_unix_out); + tor_assert(rest_out); + + line = eat_whitespace(line); + + if (!strcmpstart(line, unix_q_socket_prefix)) { + // It starts with unix:" + size_t sz; + *is_unix_out = 1; + *addrport_out = NULL; + line += strlen(unix_socket_prefix); /*No q: Keep the quote */ + *rest_out = unescape_string(line, addrport_out, &sz); + if (!*rest_out || (*addrport_out && sz != strlen(*addrport_out))) { + tor_free(*addrport_out); + return -1; + } + *rest_out = eat_whitespace(*rest_out); + return 0; + } else { + // Is there a unix: prefix? + if (!strcmpstart(line, unix_socket_prefix)) { + line += strlen(unix_socket_prefix); + *is_unix_out = 1; + } else { + *is_unix_out = 0; + } + + const char *end = find_whitespace(line); + if (BUG(!end)) { + end = strchr(line, '\0'); // LCOV_EXCL_LINE -- this can't be NULL + } + tor_assert(end && end >= line); + *addrport_out = tor_strndup(line, end - line); + *rest_out = eat_whitespace(end); + return 0; + } +} + +static void +warn_client_dns_cache(const char *option, int disabling) +{ + if (disabling) + return; + + warn_deprecated_option(option, + "Client-side DNS cacheing enables a wide variety of route-" + "capture attacks. If a single bad exit node lies to you about " + "an IP address, cacheing that address would make you visit " + "an address of the attacker's choice every time you connected " + "to your destination."); +} + +/** + * Validate the configured bridge distribution method from a BridgeDistribution + * config line. + * + * The input <b>bd</b>, is a string taken from the BridgeDistribution config + * line (if present). If the option wasn't set, return 0 immediately. The + * BridgeDistribution option is then validated. Currently valid, recognised + * options are: + * + * - "none" + * - "any" + * - "https" + * - "email" + * - "moat" + * - "hyphae" + * + * If the option string is unrecognised, a warning will be logged and 0 is + * returned. If the option string contains an invalid character, -1 is + * returned. + **/ +STATIC int +check_bridge_distribution_setting(const char *bd) +{ + if (bd == NULL) + return 0; + + const char *RECOGNIZED[] = { + "none", "any", "https", "email", "moat", "hyphae" + }; + unsigned i; + for (i = 0; i < ARRAY_LENGTH(RECOGNIZED); ++i) { + if (!strcmp(bd, RECOGNIZED[i])) + return 0; + } + + const char *cp = bd; + // Method = (KeywordChar | "_") + + while (TOR_ISALNUM(*cp) || *cp == '-' || *cp == '_') + ++cp; + + if (*cp == 0) { + log_warn(LD_CONFIG, "Unrecognized BridgeDistribution value %s. I'll " + "assume you know what you are doing...", escaped(bd)); + return 0; // we reached the end of the string; all is well + } else { + return -1; // we found a bad character in the string. + } +} + +/** + * Parse port configuration for a single port type. + * + * Read entries of the "FooPort" type from the list <b>ports</b>. Syntax is + * that FooPort can have any number of entries of the format + * "[Address:][Port] IsolationOptions". + * + * In log messages, describe the port type as <b>portname</b>. + * + * If no address is specified, default to <b>defaultaddr</b>. If no + * FooPort is given, default to defaultport (if 0, there is no default). + * + * If CL_PORT_NO_STREAM_OPTIONS is set in <b>flags</b>, do not allow stream + * isolation options in the FooPort entries. + * + * If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the + * ports are not on a local address. If CL_PORT_FORBID_NONLOCAL is set, + * this is a control port with no password set: don't even allow it. + * + * If CL_PORT_SERVER_OPTIONS is set in <b>flags</b>, do not allow stream + * isolation options in the FooPort entries; instead allow the + * server-port option set. + * + * If CL_PORT_TAKES_HOSTNAMES is set in <b>flags</b>, allow the options + * {No,}IPv{4,6}Traffic. + * + * On success, if <b>out</b> is given, add a new port_cfg_t entry to + * <b>out</b> for every port that the client should listen on. Return 0 + * on success, -1 on failure. + */ +STATIC int +parse_port_config(smartlist_t *out, + const config_line_t *ports, + const char *portname, + int listener_type, + const char *defaultaddr, + int defaultport, + const unsigned flags) +{ + smartlist_t *elts; + int retval = -1; + const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER); + const unsigned is_ext_orport = (listener_type == CONN_TYPE_EXT_OR_LISTENER); + const unsigned allow_no_stream_options = flags & CL_PORT_NO_STREAM_OPTIONS; + const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS; + const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL; + const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL; + const unsigned default_to_group_writable = + flags & CL_PORT_DFLT_GROUP_WRITABLE; + const unsigned takes_hostnames = flags & CL_PORT_TAKES_HOSTNAMES; + const unsigned is_unix_socket = flags & CL_PORT_IS_UNIXSOCKET; + int got_zero_port=0, got_nonzero_port=0; + char *unix_socket_path = NULL; + + /* If there's no FooPort, then maybe make a default one. */ + if (! ports) { + if (defaultport && defaultaddr && out) { + port_cfg_t *cfg = port_cfg_new(is_unix_socket ? strlen(defaultaddr) : 0); + cfg->type = listener_type; + if (is_unix_socket) { + tor_addr_make_unspec(&cfg->addr); + memcpy(cfg->unix_addr, defaultaddr, strlen(defaultaddr) + 1); + cfg->is_unix_addr = 1; + } else { + cfg->port = defaultport; + tor_addr_parse(&cfg->addr, defaultaddr); + } + cfg->entry_cfg.session_group = SESSION_GROUP_UNSET; + cfg->entry_cfg.isolation_flags = ISO_DEFAULT; + smartlist_add(out, cfg); + } + return 0; + } + + /* At last we can actually parse the FooPort lines. The syntax is: + * [Addr:](Port|auto) [Options].*/ + elts = smartlist_new(); + char *addrport = NULL; + + for (; ports; ports = ports->next) { + tor_addr_t addr; + tor_addr_make_unspec(&addr); + + int port; + int sessiongroup = SESSION_GROUP_UNSET; + unsigned isolation = ISO_DEFAULT; + int prefer_no_auth = 0; + int socks_iso_keep_alive = 0; + + uint16_t ptmp=0; + int ok; + /* This must be kept in sync with port_cfg_new's defaults */ + int no_listen = 0, no_advertise = 0, all_addrs = 0, + bind_ipv4_only = 0, bind_ipv6_only = 0, + ipv4_traffic = 1, ipv6_traffic = 1, prefer_ipv6 = 0, dns_request = 1, + onion_traffic = 1, + cache_ipv4 = 0, use_cached_ipv4 = 0, + cache_ipv6 = 0, use_cached_ipv6 = 0, + prefer_ipv6_automap = 1, world_writable = 0, group_writable = 0, + relax_dirmode_check = 0, + has_used_unix_socket_only_option = 0; + + int is_unix_tagged_addr = 0; + const char *rest_of_line = NULL; + if (port_cfg_line_extract_addrport(ports->value, + &addrport, &is_unix_tagged_addr, &rest_of_line)<0) { + log_warn(LD_CONFIG, "Invalid %sPort line with unparsable address", + portname); + goto err; + } + if (strlen(addrport) == 0) { + log_warn(LD_CONFIG, "Invalid %sPort line with no address", portname); + goto err; + } + + /* Split the remainder... */ + smartlist_split_string(elts, rest_of_line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + + /* Let's start to check if it's a Unix socket path. */ + if (is_unix_tagged_addr) { +#ifndef HAVE_SYS_UN_H + log_warn(LD_CONFIG, "Unix sockets not supported on this system."); + goto err; +#endif + unix_socket_path = addrport; + addrport = NULL; + } + + if (unix_socket_path && + ! conn_listener_type_supports_af_unix(listener_type)) { + log_warn(LD_CONFIG, "%sPort does not support unix sockets", portname); + goto err; + } + + if (unix_socket_path) { + port = 1; + } else if (is_unix_socket) { + if (BUG(!addrport)) + goto err; // LCOV_EXCL_LINE unreachable, but coverity can't tell that + unix_socket_path = tor_strdup(addrport); + if (!strcmp(addrport, "0")) + port = 0; + else + port = 1; + } else if (!strcasecmp(addrport, "auto")) { + port = CFG_AUTO_PORT; + int af = tor_addr_parse(&addr, defaultaddr); + tor_assert(af >= 0); + } else if (!strcasecmpend(addrport, ":auto")) { + char *addrtmp = tor_strndup(addrport, strlen(addrport)-5); + port = CFG_AUTO_PORT; + if (tor_addr_port_lookup(addrtmp, &addr, &ptmp)<0 || ptmp) { + log_warn(LD_CONFIG, "Invalid address '%s' for %sPort", + escaped(addrport), portname); + tor_free(addrtmp); + goto err; + } + tor_free(addrtmp); + } else { + /* Try parsing integer port before address, because, who knows? + "9050" might be a valid address. */ + port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL); + if (ok) { + int af = tor_addr_parse(&addr, defaultaddr); + tor_assert(af >= 0); + } else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) { + if (ptmp == 0) { + log_warn(LD_CONFIG, "%sPort line has address but no port", portname); + goto err; + } + port = ptmp; + } else { + log_warn(LD_CONFIG, "Couldn't parse address %s for %sPort", + escaped(addrport), portname); + goto err; + } + } + + if (unix_socket_path && default_to_group_writable) + group_writable = 1; + + /* Now parse the rest of the options, if any. */ + if (use_server_options) { + /* This is a server port; parse advertising options */ + SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { + if (!strcasecmp(elt, "NoAdvertise")) { + no_advertise = 1; + } else if (!strcasecmp(elt, "NoListen")) { + no_listen = 1; +#if 0 + /* not implemented yet. */ + } else if (!strcasecmp(elt, "AllAddrs")) { + + all_addrs = 1; +#endif /* 0 */ + } else if (!strcasecmp(elt, "IPv4Only")) { + bind_ipv4_only = 1; + } else if (!strcasecmp(elt, "IPv6Only")) { + bind_ipv6_only = 1; + } else { + log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", + portname, escaped(elt)); + } + } SMARTLIST_FOREACH_END(elt); + + if (no_advertise && no_listen) { + log_warn(LD_CONFIG, "Tried to set both NoListen and NoAdvertise " + "on %sPort line '%s'", + portname, escaped(ports->value)); + goto err; + } + if (bind_ipv4_only && bind_ipv6_only) { + log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only " + "on %sPort line '%s'", + portname, escaped(ports->value)); + goto err; + } + if (bind_ipv4_only && tor_addr_family(&addr) != AF_INET) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4", + portname); + goto err; + } + if (bind_ipv6_only && tor_addr_family(&addr) != AF_INET6) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", + portname); + goto err; + } + } else { + /* This is a client port; parse isolation options */ + SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { + int no = 0, isoflag = 0; + const char *elt_orig = elt; + + if (!strcasecmpstart(elt, "SessionGroup=")) { + int group = (int)tor_parse_long(elt+strlen("SessionGroup="), + 10, 0, INT_MAX, &ok, NULL); + if (!ok || allow_no_stream_options) { + log_warn(LD_CONFIG, "Invalid %sPort option '%s'", + portname, escaped(elt)); + goto err; + } + if (sessiongroup >= 0) { + log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort", + portname); + goto err; + } + sessiongroup = group; + continue; + } + + if (!strcasecmpstart(elt, "No")) { + no = 1; + elt += 2; + } + + if (!strcasecmp(elt, "GroupWritable")) { + group_writable = !no; + has_used_unix_socket_only_option = 1; + continue; + } else if (!strcasecmp(elt, "WorldWritable")) { + world_writable = !no; + has_used_unix_socket_only_option = 1; + continue; + } else if (!strcasecmp(elt, "RelaxDirModeCheck")) { + relax_dirmode_check = !no; + has_used_unix_socket_only_option = 1; + continue; + } + + if (allow_no_stream_options) { + log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", + portname, escaped(elt)); + continue; + } + + if (takes_hostnames) { + if (!strcasecmp(elt, "IPv4Traffic")) { + ipv4_traffic = ! no; + continue; + } else if (!strcasecmp(elt, "IPv6Traffic")) { + ipv6_traffic = ! no; + continue; + } else if (!strcasecmp(elt, "PreferIPv6")) { + prefer_ipv6 = ! no; + continue; + } else if (!strcasecmp(elt, "DNSRequest")) { + dns_request = ! no; + continue; + } else if (!strcasecmp(elt, "OnionTraffic")) { + onion_traffic = ! no; + continue; + } else if (!strcasecmp(elt, "OnionTrafficOnly")) { + /* Only connect to .onion addresses. Equivalent to + * NoDNSRequest, NoIPv4Traffic, NoIPv6Traffic. The option + * NoOnionTrafficOnly is not supported, it's too confusing. */ + if (no) { + log_warn(LD_CONFIG, "Unsupported %sPort option 'No%s'. Use " + "DNSRequest, IPv4Traffic, and/or IPv6Traffic instead.", + portname, escaped(elt)); + } else { + ipv4_traffic = ipv6_traffic = dns_request = 0; + } + continue; + } + } + if (!strcasecmp(elt, "CacheIPv4DNS")) { + warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha + cache_ipv4 = ! no; + continue; + } else if (!strcasecmp(elt, "CacheIPv6DNS")) { + warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha + cache_ipv6 = ! no; + continue; + } else if (!strcasecmp(elt, "CacheDNS")) { + warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha + cache_ipv4 = cache_ipv6 = ! no; + continue; + } else if (!strcasecmp(elt, "UseIPv4Cache")) { + warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha + use_cached_ipv4 = ! no; + continue; + } else if (!strcasecmp(elt, "UseIPv6Cache")) { + warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha + use_cached_ipv6 = ! no; + continue; + } else if (!strcasecmp(elt, "UseDNSCache")) { + warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha + use_cached_ipv4 = use_cached_ipv6 = ! no; + continue; + } else if (!strcasecmp(elt, "PreferIPv6Automap")) { + prefer_ipv6_automap = ! no; + continue; + } else if (!strcasecmp(elt, "PreferSOCKSNoAuth")) { + prefer_no_auth = ! no; + continue; + } else if (!strcasecmp(elt, "KeepAliveIsolateSOCKSAuth")) { + socks_iso_keep_alive = ! no; + continue; + } + + if (!strcasecmpend(elt, "s")) + elt[strlen(elt)-1] = '\0'; /* kill plurals. */ + + if (!strcasecmp(elt, "IsolateDestPort")) { + isoflag = ISO_DESTPORT; + } else if (!strcasecmp(elt, "IsolateDestAddr")) { + isoflag = ISO_DESTADDR; + } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) { + isoflag = ISO_SOCKSAUTH; + } else if (!strcasecmp(elt, "IsolateClientProtocol")) { + isoflag = ISO_CLIENTPROTO; + } else if (!strcasecmp(elt, "IsolateClientAddr")) { + isoflag = ISO_CLIENTADDR; + } else { + log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", + portname, escaped(elt_orig)); + } + + if (no) { + isolation &= ~isoflag; + } else { + isolation |= isoflag; + } + } SMARTLIST_FOREACH_END(elt); + } + + if (port) + got_nonzero_port = 1; + else + got_zero_port = 1; + + if (dns_request == 0 && listener_type == CONN_TYPE_AP_DNS_LISTENER) { + log_warn(LD_CONFIG, "You have a %sPort entry with DNS disabled; that " + "won't work.", portname); + goto err; + } + + if (ipv4_traffic == 0 && ipv6_traffic == 0 && onion_traffic == 0 + && listener_type != CONN_TYPE_AP_DNS_LISTENER) { + log_warn(LD_CONFIG, "You have a %sPort entry with all of IPv4 and " + "IPv6 and .onion disabled; that won't work.", portname); + goto err; + } + + if (dns_request == 1 && ipv4_traffic == 0 && ipv6_traffic == 0 + && listener_type != CONN_TYPE_AP_DNS_LISTENER) { + log_warn(LD_CONFIG, "You have a %sPort entry with DNSRequest enabled, " + "but IPv4 and IPv6 disabled; DNS-based sites won't work.", + portname); + goto err; + } + + if ( has_used_unix_socket_only_option && ! unix_socket_path) { + log_warn(LD_CONFIG, "You have a %sPort entry with GroupWritable, " + "WorldWritable, or RelaxDirModeCheck, but it is not a " + "unix socket.", portname); + goto err; + } + + if (!(isolation & ISO_SOCKSAUTH) && socks_iso_keep_alive) { + log_warn(LD_CONFIG, "You have a %sPort entry with both " + "NoIsolateSOCKSAuth and KeepAliveIsolateSOCKSAuth set.", + portname); + goto err; + } + + if (unix_socket_path && (isolation & ISO_CLIENTADDR)) { + /* `IsolateClientAddr` is nonsensical in the context of AF_LOCAL. + * just silently remove the isolation flag. + */ + isolation &= ~ISO_CLIENTADDR; + } + + if (out && port) { + size_t namelen = unix_socket_path ? strlen(unix_socket_path) : 0; + port_cfg_t *cfg = port_cfg_new(namelen); + if (unix_socket_path) { + tor_addr_make_unspec(&cfg->addr); + memcpy(cfg->unix_addr, unix_socket_path, namelen + 1); + cfg->is_unix_addr = 1; + tor_free(unix_socket_path); + } else { + tor_addr_copy(&cfg->addr, &addr); + cfg->port = port; + } + cfg->type = listener_type; + cfg->is_world_writable = world_writable; + cfg->is_group_writable = group_writable; + cfg->relax_dirmode_check = relax_dirmode_check; + cfg->entry_cfg.isolation_flags = isolation; + cfg->entry_cfg.session_group = sessiongroup; + cfg->server_cfg.no_advertise = no_advertise; + cfg->server_cfg.no_listen = no_listen; + cfg->server_cfg.all_addrs = all_addrs; + cfg->server_cfg.bind_ipv4_only = bind_ipv4_only; + cfg->server_cfg.bind_ipv6_only = bind_ipv6_only; + cfg->entry_cfg.ipv4_traffic = ipv4_traffic; + cfg->entry_cfg.ipv6_traffic = ipv6_traffic; + cfg->entry_cfg.prefer_ipv6 = prefer_ipv6; + cfg->entry_cfg.dns_request = dns_request; + cfg->entry_cfg.onion_traffic = onion_traffic; + cfg->entry_cfg.cache_ipv4_answers = cache_ipv4; + cfg->entry_cfg.cache_ipv6_answers = cache_ipv6; + cfg->entry_cfg.use_cached_ipv4_answers = use_cached_ipv4; + cfg->entry_cfg.use_cached_ipv6_answers = use_cached_ipv6; + cfg->entry_cfg.prefer_ipv6_virtaddr = prefer_ipv6_automap; + cfg->entry_cfg.socks_prefer_no_auth = prefer_no_auth; + if (! (isolation & ISO_SOCKSAUTH)) + cfg->entry_cfg.socks_prefer_no_auth = 1; + cfg->entry_cfg.socks_iso_keep_alive = socks_iso_keep_alive; + + smartlist_add(out, cfg); + } + SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); + smartlist_clear(elts); + tor_free(addrport); + tor_free(unix_socket_path); + } + + if (warn_nonlocal && out) { + if (is_control) + warn_nonlocal_controller_ports(out, forbid_nonlocal); + else if (is_ext_orport) + warn_nonlocal_ext_orports(out, portname); + else + warn_nonlocal_client_ports(out, portname, listener_type); + } + + if (got_zero_port && got_nonzero_port) { + log_warn(LD_CONFIG, "You specified a nonzero %sPort along with '%sPort 0' " + "in the same configuration. Did you mean to disable %sPort or " + "not?", portname, portname, portname); + goto err; + } + + retval = 0; + err: + SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); + smartlist_free(elts); + tor_free(unix_socket_path); + tor_free(addrport); + return retval; +} + +/** Return the number of ports which are actually going to listen with type + * <b>listenertype</b>. Do not count no_listen ports. Only count unix + * sockets if count_sockets is true. */ +static int +count_real_listeners(const smartlist_t *ports, int listenertype, + int count_sockets) +{ + int n = 0; + SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) { + if (port->server_cfg.no_listen) + continue; + if (!count_sockets && port->is_unix_addr) + continue; + if (port->type != listenertype) + continue; + ++n; + } SMARTLIST_FOREACH_END(port); + return n; +} + +/** Parse all ports from <b>options</b>. On success, set *<b>n_ports_out</b> + * to the number of ports that are listed, update the *Port_set values in + * <b>options</b>, and return 0. On failure, set *<b>msg</b> to a + * description of the problem and return -1. + * + * If <b>validate_only</b> is false, set configured_client_ports to the + * new list of ports parsed from <b>options</b>. + **/ +static int +parse_ports(or_options_t *options, int validate_only, + char **msg, int *n_ports_out, + int *world_writable_control_socket) +{ + smartlist_t *ports; + int retval = -1; + + ports = smartlist_new(); + + *n_ports_out = 0; + + const unsigned gw_flag = options->UnixSocksGroupWritable ? + CL_PORT_DFLT_GROUP_WRITABLE : 0; + if (parse_port_config(ports, + options->SocksPort_lines, + "Socks", CONN_TYPE_AP_LISTENER, + "127.0.0.1", 9050, + ((validate_only ? 0 : CL_PORT_WARN_NONLOCAL) + | CL_PORT_TAKES_HOSTNAMES | gw_flag)) < 0) { + *msg = tor_strdup("Invalid SocksPort configuration"); + goto err; + } + if (parse_port_config(ports, + options->DNSPort_lines, + "DNS", CONN_TYPE_AP_DNS_LISTENER, + "127.0.0.1", 0, + CL_PORT_WARN_NONLOCAL|CL_PORT_TAKES_HOSTNAMES) < 0) { + *msg = tor_strdup("Invalid DNSPort configuration"); + goto err; + } + if (parse_port_config(ports, + options->TransPort_lines, + "Trans", CONN_TYPE_AP_TRANS_LISTENER, + "127.0.0.1", 0, + CL_PORT_WARN_NONLOCAL) < 0) { + *msg = tor_strdup("Invalid TransPort configuration"); + goto err; + } + if (parse_port_config(ports, + options->NATDPort_lines, + "NATD", CONN_TYPE_AP_NATD_LISTENER, + "127.0.0.1", 0, + CL_PORT_WARN_NONLOCAL) < 0) { + *msg = tor_strdup("Invalid NatdPort configuration"); + goto err; + } + if (parse_port_config(ports, + options->HTTPTunnelPort_lines, + "HTTP Tunnel", CONN_TYPE_AP_HTTP_CONNECT_LISTENER, + "127.0.0.1", 0, + ((validate_only ? 0 : CL_PORT_WARN_NONLOCAL) + | CL_PORT_TAKES_HOSTNAMES | gw_flag)) < 0) { + *msg = tor_strdup("Invalid HTTPTunnelPort configuration"); + goto err; + } + { + unsigned control_port_flags = CL_PORT_NO_STREAM_OPTIONS | + CL_PORT_WARN_NONLOCAL; + const int any_passwords = (options->HashedControlPassword || + options->HashedControlSessionPassword || + options->CookieAuthentication); + if (! any_passwords) + control_port_flags |= CL_PORT_FORBID_NONLOCAL; + if (options->ControlSocketsGroupWritable) + control_port_flags |= CL_PORT_DFLT_GROUP_WRITABLE; + + if (parse_port_config(ports, + options->ControlPort_lines, + "Control", CONN_TYPE_CONTROL_LISTENER, + "127.0.0.1", 0, + control_port_flags) < 0) { + *msg = tor_strdup("Invalid ControlPort configuration"); + goto err; + } + + if (parse_port_config(ports, options->ControlSocket, + "ControlSocket", + CONN_TYPE_CONTROL_LISTENER, NULL, 0, + control_port_flags | CL_PORT_IS_UNIXSOCKET) < 0) { + *msg = tor_strdup("Invalid ControlSocket configuration"); + goto err; + } + } + if (! options->ClientOnly) { + if (parse_port_config(ports, + options->ORPort_lines, + "OR", CONN_TYPE_OR_LISTENER, + "0.0.0.0", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid ORPort configuration"); + goto err; + } + if (parse_port_config(ports, + options->ExtORPort_lines, + "ExtOR", CONN_TYPE_EXT_OR_LISTENER, + "127.0.0.1", 0, + CL_PORT_SERVER_OPTIONS|CL_PORT_WARN_NONLOCAL) < 0) { + *msg = tor_strdup("Invalid ExtORPort configuration"); + goto err; + } + if (parse_port_config(ports, + options->DirPort_lines, + "Dir", CONN_TYPE_DIR_LISTENER, + "0.0.0.0", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid DirPort configuration"); + goto err; + } + } + + int n_low_ports = 0; + if (check_server_ports(ports, options, &n_low_ports) < 0) { + *msg = tor_strdup("Misconfigured server ports"); + goto err; + } + if (have_low_ports < 0) + have_low_ports = (n_low_ports > 0); + + *n_ports_out = smartlist_len(ports); + + retval = 0; + + /* Update the *Port_set options. The !! here is to force a boolean out of + an integer. */ + options->ORPort_set = + !! count_real_listeners(ports, CONN_TYPE_OR_LISTENER, 0); + options->SocksPort_set = + !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER, 1); + options->TransPort_set = + !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER, 1); + options->NATDPort_set = + !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER, 1); + options->HTTPTunnelPort_set = + !! count_real_listeners(ports, CONN_TYPE_AP_HTTP_CONNECT_LISTENER, 1); + /* Use options->ControlSocket to test if a control socket is set */ + options->ControlPort_set = + !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER, 0); + options->DirPort_set = + !! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER, 0); + options->DNSPort_set = + !! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER, 1); + options->ExtORPort_set = + !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER, 0); + + if (world_writable_control_socket) { + SMARTLIST_FOREACH(ports, port_cfg_t *, p, + if (p->type == CONN_TYPE_CONTROL_LISTENER && + p->is_unix_addr && + p->is_world_writable) { + *world_writable_control_socket = 1; + break; + }); + } + + if (!validate_only) { + if (configured_ports) { + SMARTLIST_FOREACH(configured_ports, + port_cfg_t *, p, port_cfg_free(p)); + smartlist_free(configured_ports); + } + configured_ports = ports; + ports = NULL; /* prevent free below. */ + } + + err: + if (ports) { + SMARTLIST_FOREACH(ports, port_cfg_t *, p, port_cfg_free(p)); + smartlist_free(ports); + } + return retval; +} + +/* Does port bind to IPv4? */ +static int +port_binds_ipv4(const port_cfg_t *port) +{ + return tor_addr_family(&port->addr) == AF_INET || + (tor_addr_family(&port->addr) == AF_UNSPEC + && !port->server_cfg.bind_ipv6_only); +} + +/* Does port bind to IPv6? */ +static int +port_binds_ipv6(const port_cfg_t *port) +{ + return tor_addr_family(&port->addr) == AF_INET6 || + (tor_addr_family(&port->addr) == AF_UNSPEC + && !port->server_cfg.bind_ipv4_only); +} + +/** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal + * consistency and warn as appropriate. Set *<b>n_low_ports_out</b> to the + * number of sub-1024 ports we will be binding. */ +static int +check_server_ports(const smartlist_t *ports, + const or_options_t *options, + int *n_low_ports_out) +{ + int n_orport_advertised = 0; + int n_orport_advertised_ipv4 = 0; + int n_orport_listeners = 0; + int n_dirport_advertised = 0; + int n_dirport_listeners = 0; + int n_low_port = 0; + int r = 0; + + SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { + if (port->type == CONN_TYPE_DIR_LISTENER) { + if (! port->server_cfg.no_advertise) + ++n_dirport_advertised; + if (! port->server_cfg.no_listen) + ++n_dirport_listeners; + } else if (port->type == CONN_TYPE_OR_LISTENER) { + if (! port->server_cfg.no_advertise) { + ++n_orport_advertised; + if (port_binds_ipv4(port)) + ++n_orport_advertised_ipv4; + } + if (! port->server_cfg.no_listen) + ++n_orport_listeners; + } else { + continue; + } +#ifndef _WIN32 + if (!port->server_cfg.no_listen && port->port < 1024) + ++n_low_port; +#endif + } SMARTLIST_FOREACH_END(port); + + if (n_orport_advertised && !n_orport_listeners) { + log_warn(LD_CONFIG, "We are advertising an ORPort, but not actually " + "listening on one."); + r = -1; + } + if (n_orport_listeners && !n_orport_advertised) { + log_warn(LD_CONFIG, "We are listening on an ORPort, but not advertising " + "any ORPorts. This will keep us from building a %s " + "descriptor, and make us impossible to use.", + options->BridgeRelay ? "bridge" : "router"); + r = -1; + } + if (n_dirport_advertised && !n_dirport_listeners) { + log_warn(LD_CONFIG, "We are advertising a DirPort, but not actually " + "listening on one."); + r = -1; + } + if (n_dirport_advertised > 1) { + log_warn(LD_CONFIG, "Can't advertise more than one DirPort."); + r = -1; + } + if (n_orport_advertised && !n_orport_advertised_ipv4 && + !options->BridgeRelay) { + log_warn(LD_CONFIG, "Configured public relay to listen only on an IPv6 " + "address. Tor needs to listen on an IPv4 address too."); + r = -1; + } + + if (n_low_port && options->AccountingMax && + (!have_capability_support() || options->KeepBindCapabilities == 0)) { + const char *extra = ""; + if (options->KeepBindCapabilities == 0 && have_capability_support()) + extra = ", and you have disabled KeepBindCapabilities."; + log_warn(LD_CONFIG, + "You have set AccountingMax to use hibernation. You have also " + "chosen a low DirPort or OrPort%s." + "This combination can make Tor stop " + "working when it tries to re-attach the port after a period of " + "hibernation. Please choose a different port or turn off " + "hibernation unless you know this combination will work on your " + "platform.", extra); + } + + if (n_low_ports_out) + *n_low_ports_out = n_low_port; + + return r; +} + +/** Return a list of port_cfg_t for client ports parsed from the + * options. */ +MOCK_IMPL(const smartlist_t *, +get_configured_ports,(void)) +{ + if (!configured_ports) + configured_ports = smartlist_new(); + return configured_ports; +} + +/** Return an address:port string representation of the address + * where the first <b>listener_type</b> listener waits for + * connections. Return NULL if we couldn't find a listener. The + * string is allocated on the heap and it's the responsibility of the + * caller to free it after use. + * + * This function is meant to be used by the pluggable transport proxy + * spawning code, please make sure that it fits your purposes before + * using it. */ +char * +get_first_listener_addrport_string(int listener_type) +{ + static const char *ipv4_localhost = "127.0.0.1"; + static const char *ipv6_localhost = "[::1]"; + const char *address; + uint16_t port; + char *string = NULL; + + if (!configured_ports) + return NULL; + + SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) { + if (cfg->server_cfg.no_listen) + continue; + + if (cfg->type == listener_type && + tor_addr_family(&cfg->addr) != AF_UNSPEC) { + + /* We found the first listener of the type we are interested in! */ + + /* If a listener is listening on INADDR_ANY, assume that it's + also listening on 127.0.0.1, and point the transport proxy + there: */ + if (tor_addr_is_null(&cfg->addr)) + address = tor_addr_is_v4(&cfg->addr) ? ipv4_localhost : ipv6_localhost; + else + address = fmt_and_decorate_addr(&cfg->addr); + + /* If a listener is configured with port 'auto', we are forced + to iterate all listener connections and find out in which + port it ended up listening: */ + if (cfg->port == CFG_AUTO_PORT) { + port = router_get_active_listener_port_by_type_af(listener_type, + tor_addr_family(&cfg->addr)); + if (!port) + return NULL; + } else { + port = cfg->port; + } + + tor_asprintf(&string, "%s:%u", address, port); + + return string; + } + + } SMARTLIST_FOREACH_END(cfg); + + return NULL; +} + +/** Return the first advertised port of type <b>listener_type</b> in + * <b>address_family</b>. Returns 0 when no port is found, and when passed + * AF_UNSPEC. */ +int +get_first_advertised_port_by_type_af(int listener_type, int address_family) +{ + if (address_family == AF_UNSPEC) + return 0; + + const smartlist_t *conf_ports = get_configured_ports(); + SMARTLIST_FOREACH_BEGIN(conf_ports, const port_cfg_t *, cfg) { + if (cfg->type == listener_type && + !cfg->server_cfg.no_advertise) { + if ((address_family == AF_INET && port_binds_ipv4(cfg)) || + (address_family == AF_INET6 && port_binds_ipv6(cfg))) { + return cfg->port; + } + } + } SMARTLIST_FOREACH_END(cfg); + return 0; +} + +/** Return the first advertised address of type <b>listener_type</b> in + * <b>address_family</b>. Returns NULL if there is no advertised address, + * and when passed AF_UNSPEC. */ +const tor_addr_t * +get_first_advertised_addr_by_type_af(int listener_type, int address_family) +{ + if (address_family == AF_UNSPEC) + return NULL; + if (!configured_ports) + return NULL; + SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) { + if (cfg->type == listener_type && + !cfg->server_cfg.no_advertise) { + if ((address_family == AF_INET && port_binds_ipv4(cfg)) || + (address_family == AF_INET6 && port_binds_ipv6(cfg))) { + return &cfg->addr; + } + } + } SMARTLIST_FOREACH_END(cfg); + return NULL; +} + +/** Return 1 if a port exists of type <b>listener_type</b> on <b>addr</b> and + * <b>port</b>. If <b>check_wildcard</b> is true, INADDR[6]_ANY and AF_UNSPEC + * addresses match any address of the appropriate family; and port -1 matches + * any port. + * To match auto ports, pass CFG_PORT_AUTO. (Does not match on the actual + * automatically chosen listener ports.) */ +int +port_exists_by_type_addr_port(int listener_type, const tor_addr_t *addr, + int port, int check_wildcard) +{ + if (!configured_ports || !addr) + return 0; + SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) { + if (cfg->type == listener_type) { + if (cfg->port == port || (check_wildcard && port == -1)) { + /* Exact match */ + if (tor_addr_eq(&cfg->addr, addr)) { + return 1; + } + /* Skip wildcard matches if we're not doing them */ + if (!check_wildcard) { + continue; + } + /* Wildcard matches IPv4 */ + const int cfg_v4 = port_binds_ipv4(cfg); + const int cfg_any_v4 = tor_addr_is_null(&cfg->addr) && cfg_v4; + const int addr_v4 = tor_addr_family(addr) == AF_INET || + tor_addr_family(addr) == AF_UNSPEC; + const int addr_any_v4 = tor_addr_is_null(&cfg->addr) && addr_v4; + if ((cfg_any_v4 && addr_v4) || (cfg_v4 && addr_any_v4)) { + return 1; + } + /* Wildcard matches IPv6 */ + const int cfg_v6 = port_binds_ipv6(cfg); + const int cfg_any_v6 = tor_addr_is_null(&cfg->addr) && cfg_v6; + const int addr_v6 = tor_addr_family(addr) == AF_INET6 || + tor_addr_family(addr) == AF_UNSPEC; + const int addr_any_v6 = tor_addr_is_null(&cfg->addr) && addr_v6; + if ((cfg_any_v6 && addr_v6) || (cfg_v6 && addr_any_v6)) { + return 1; + } + } + } + } SMARTLIST_FOREACH_END(cfg); + return 0; +} + +/* Like port_exists_by_type_addr_port, but accepts a host-order IPv4 address + * instead. */ +int +port_exists_by_type_addr32h_port(int listener_type, uint32_t addr_ipv4h, + int port, int check_wildcard) +{ + tor_addr_t ipv4; + tor_addr_from_ipv4h(&ipv4, addr_ipv4h); + return port_exists_by_type_addr_port(listener_type, &ipv4, port, + check_wildcard); +} + +/** Allocate and return a good value for the DataDirectory based on + * <b>val</b>, which may be NULL. Return NULL on failure. */ +static char * +get_data_directory(const char *val) +{ +#ifdef _WIN32 + if (val) { + return tor_strdup(val); + } else { + return tor_strdup(get_windows_conf_root()); + } +#else /* !(defined(_WIN32)) */ + const char *d = val; + if (!d) + d = "~/.tor"; + + if (!strcmpstart(d, "~/")) { + char *fn = expand_filename(d); + if (!fn) { + log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d); + return NULL; + } + if (!val && !strcmp(fn,"/.tor")) { + /* If our homedir is /, we probably don't want to use it. */ + /* Default to LOCALSTATEDIR/tor which is probably closer to what we + * want. */ + log_warn(LD_CONFIG, + "Default DataDirectory is \"~/.tor\". This expands to " + "\"%s\", which is probably not what you want. Using " + "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR); + tor_free(fn); + fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor"); + } + return fn; + } + return tor_strdup(d); +#endif /* defined(_WIN32) */ +} + +/** Check and normalize the values of options->{Key,Data,Cache}Directory; + * return 0 if it is sane, -1 otherwise. */ +static int +validate_data_directories(or_options_t *options) +{ + tor_free(options->DataDirectory); + options->DataDirectory = get_data_directory(options->DataDirectory_option); + if (!options->DataDirectory) + return -1; + if (strlen(options->DataDirectory) > (512-128)) { + log_warn(LD_CONFIG, "DataDirectory is too long."); + return -1; + } + + tor_free(options->KeyDirectory); + if (options->KeyDirectory_option) { + options->KeyDirectory = get_data_directory(options->KeyDirectory_option); + if (!options->KeyDirectory) + return -1; + } else { + /* Default to the data directory's keys subdir */ + tor_asprintf(&options->KeyDirectory, "%s"PATH_SEPARATOR"keys", + options->DataDirectory); + } + + tor_free(options->CacheDirectory); + if (options->CacheDirectory_option) { + options->CacheDirectory = get_data_directory( + options->CacheDirectory_option); + if (!options->CacheDirectory) + return -1; + } else { + /* Default to the data directory. */ + options->CacheDirectory = tor_strdup(options->DataDirectory); + } + + return 0; +} + +/** This string must remain the same forevermore. It is how we + * recognize that the torrc file doesn't need to be backed up. */ +#define GENERATED_FILE_PREFIX "# This file was generated by Tor; " \ + "if you edit it, comments will not be preserved" +/** This string can change; it tries to give the reader an idea + * that editing this file by hand is not a good plan. */ +#define GENERATED_FILE_COMMENT "# The old torrc file was renamed " \ + "to torrc.orig.1 or similar, and Tor will ignore it" + +/** Save a configuration file for the configuration in <b>options</b> + * into the file <b>fname</b>. If the file already exists, and + * doesn't begin with GENERATED_FILE_PREFIX, rename it. Otherwise + * replace it. Return 0 on success, -1 on failure. */ +static int +write_configuration_file(const char *fname, const or_options_t *options) +{ + char *old_val=NULL, *new_val=NULL, *new_conf=NULL; + int rename_old = 0, r; + + if (!fname) + return -1; + + switch (file_status(fname)) { + /* create backups of old config files, even if they're empty */ + case FN_FILE: + case FN_EMPTY: + old_val = read_file_to_str(fname, 0, NULL); + if (!old_val || strcmpstart(old_val, GENERATED_FILE_PREFIX)) { + rename_old = 1; + } + tor_free(old_val); + break; + case FN_NOENT: + break; + case FN_ERROR: + case FN_DIR: + default: + log_warn(LD_CONFIG, + "Config file \"%s\" is not a file? Failing.", fname); + return -1; + } + + if (!(new_conf = options_dump(options, OPTIONS_DUMP_MINIMAL))) { + log_warn(LD_BUG, "Couldn't get configuration string"); + goto err; + } + + tor_asprintf(&new_val, "%s\n%s\n\n%s", + GENERATED_FILE_PREFIX, GENERATED_FILE_COMMENT, new_conf); + + if (rename_old) { + int i = 1; + char *fn_tmp = NULL; + while (1) { + tor_asprintf(&fn_tmp, "%s.orig.%d", fname, i); + if (file_status(fn_tmp) == FN_NOENT) + break; + tor_free(fn_tmp); + ++i; + } + log_notice(LD_CONFIG, "Renaming old configuration file to \"%s\"", fn_tmp); + if (tor_rename(fname, fn_tmp) < 0) {//XXXX sandbox doesn't allow + log_warn(LD_FS, + "Couldn't rename configuration file \"%s\" to \"%s\": %s", + fname, fn_tmp, strerror(errno)); + tor_free(fn_tmp); + goto err; + } + tor_free(fn_tmp); + } + + if (write_str_to_file(fname, new_val, 0) < 0) + goto err; + + r = 0; + goto done; + err: + r = -1; + done: + tor_free(new_val); + tor_free(new_conf); + return r; +} + +/** + * Save the current configuration file value to disk. Return 0 on + * success, -1 on failure. + **/ +int +options_save_current(void) +{ + /* This fails if we can't write to our configuration file. + * + * If we try falling back to datadirectory or something, we have a better + * chance of saving the configuration, but a better chance of doing + * something the user never expected. */ + return write_configuration_file(get_torrc_fname(0), get_options()); +} + +/** Return the number of cpus configured in <b>options</b>. If we are + * told to auto-detect the number of cpus, return the auto-detected number. */ +int +get_num_cpus(const or_options_t *options) +{ + if (options->NumCPUs == 0) { + int n = compute_num_cpus(); + return (n >= 1) ? n : 1; + } else { + return options->NumCPUs; + } +} + +/** + * Initialize the libevent library. + */ +static void +init_libevent(const or_options_t *options) +{ + tor_libevent_cfg cfg; + + tor_assert(options); + + configure_libevent_logging(); + /* If the kernel complains that some method (say, epoll) doesn't + * exist, we don't care about it, since libevent will cope. + */ + suppress_libevent_log_msg("Function not implemented"); + + memset(&cfg, 0, sizeof(cfg)); + cfg.num_cpus = get_num_cpus(options); + cfg.msec_per_tick = options->TokenBucketRefillInterval; + + tor_libevent_initialize(&cfg); + + suppress_libevent_log_msg(NULL); +} + +/** Return a newly allocated string holding a filename relative to the + * directory in <b>options</b> specified by <b>roottype</b>. + * If <b>sub1</b> is present, it is the first path component after + * the data directory. If <b>sub2</b> is also present, it is the second path + * component after the data directory. If <b>suffix</b> is present, it + * is appended to the filename. + * + * Note: Consider using macros in config.h that wrap this function; + * you should probably never need to call it as-is. + */ +MOCK_IMPL(char *, +options_get_dir_fname2_suffix,(const or_options_t *options, + directory_root_t roottype, + const char *sub1, const char *sub2, + const char *suffix)) +{ + tor_assert(options); + + const char *rootdir = NULL; + switch (roottype) { + case DIRROOT_DATADIR: + rootdir = options->DataDirectory; + break; + case DIRROOT_CACHEDIR: + rootdir = options->CacheDirectory; + break; + case DIRROOT_KEYDIR: + rootdir = options->KeyDirectory; + break; + default: + tor_assert_unreached(); + break; + } + tor_assert(rootdir); + + if (!suffix) + suffix = ""; + + char *fname = NULL; + + if (sub1 == NULL) { + tor_asprintf(&fname, "%s%s", rootdir, suffix); + tor_assert(!sub2); /* If sub2 is present, sub1 must be present. */ + } else if (sub2 == NULL) { + tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s%s", rootdir, sub1, suffix); + } else { + tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s%s", + rootdir, sub1, sub2, suffix); + } + + return fname; +} + +/** Check wether the data directory has a private subdirectory + * <b>subdir</b>. If not, try to create it. Return 0 on success, + * -1 otherwise. */ +int +check_or_create_data_subdir(const char *subdir) +{ + char *statsdir = get_datadir_fname(subdir); + int return_val = 0; + + if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { + log_warn(LD_HIST, "Unable to create %s/ directory!", subdir); + return_val = -1; + } + tor_free(statsdir); + return return_val; +} + +/** Create a file named <b>fname</b> with contents <b>str</b> in the + * subdirectory <b>subdir</b> of the data directory. <b>descr</b> + * should be a short description of the file's content and will be + * used for the warning message, if it's present and the write process + * fails. Return 0 on success, -1 otherwise.*/ +int +write_to_data_subdir(const char* subdir, const char* fname, + const char* str, const char* descr) +{ + char *filename = get_datadir_fname2(subdir, fname); + int return_val = 0; + + if (write_str_to_file(filename, str, 0) < 0) { + log_warn(LD_HIST, "Unable to write %s to disk!", descr ? descr : fname); + return_val = -1; + } + tor_free(filename); + return return_val; +} + +/** Return a smartlist of ports that must be forwarded by + * tor-fw-helper. The smartlist contains the ports in a string format + * that is understandable by tor-fw-helper. */ +smartlist_t * +get_list_of_ports_to_forward(void) +{ + smartlist_t *ports_to_forward = smartlist_new(); + int port = 0; + + /** XXX TODO tor-fw-helper does not support forwarding ports to + other hosts than the local one. If the user is binding to a + different IP address, tor-fw-helper won't work. */ + port = router_get_advertised_or_port(get_options()); /* Get ORPort */ + if (port) + smartlist_add_asprintf(ports_to_forward, "%d:%d", port, port); + + port = router_get_advertised_dir_port(get_options(), 0); /* Get DirPort */ + if (port) + smartlist_add_asprintf(ports_to_forward, "%d:%d", port, port); + + /* Get ports of transport proxies */ + { + smartlist_t *transport_ports = get_transport_proxy_ports(); + if (transport_ports) { + smartlist_add_all(ports_to_forward, transport_ports); + smartlist_free(transport_ports); + } + } + + if (!smartlist_len(ports_to_forward)) { + smartlist_free(ports_to_forward); + ports_to_forward = NULL; + } + + return ports_to_forward; +} + +/** Helper to implement GETINFO functions about configuration variables (not + * their values). Given a "config/names" question, set *<b>answer</b> to a + * new string describing the supported configuration variables and their + * types. */ +int +getinfo_helper_config(control_connection_t *conn, + const char *question, char **answer, + const char **errmsg) +{ + (void) conn; + (void) errmsg; + if (!strcmp(question, "config/names")) { + smartlist_t *sl = smartlist_new(); + int i; + for (i = 0; option_vars_[i].name; ++i) { + const config_var_t *var = &option_vars_[i]; + const char *type; + /* don't tell controller about triple-underscore options */ + if (!strncmp(option_vars_[i].name, "___", 3)) + continue; + switch (var->type) { + case CONFIG_TYPE_STRING: type = "String"; break; + case CONFIG_TYPE_FILENAME: type = "Filename"; break; + case CONFIG_TYPE_UINT: type = "Integer"; break; + case CONFIG_TYPE_UINT64: type = "Integer"; break; + case CONFIG_TYPE_INT: type = "SignedInteger"; break; + case CONFIG_TYPE_PORT: type = "Port"; break; + case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break; + case CONFIG_TYPE_MSEC_INTERVAL: type = "TimeMsecInterval"; break; + case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break; + case CONFIG_TYPE_DOUBLE: type = "Float"; break; + case CONFIG_TYPE_BOOL: type = "Boolean"; break; + case CONFIG_TYPE_AUTOBOOL: type = "Boolean+Auto"; break; + case CONFIG_TYPE_ISOTIME: type = "Time"; break; + case CONFIG_TYPE_ROUTERSET: type = "RouterList"; break; + case CONFIG_TYPE_CSV: type = "CommaList"; break; + /* This type accepts more inputs than TimeInterval, but it ignores + * everything after the first entry, so we may as well pretend + * it's a TimeInterval. */ + case CONFIG_TYPE_CSV_INTERVAL: type = "TimeInterval"; break; + case CONFIG_TYPE_LINELIST: type = "LineList"; break; + case CONFIG_TYPE_LINELIST_S: type = "Dependent"; break; + case CONFIG_TYPE_LINELIST_V: type = "Virtual"; break; + default: + case CONFIG_TYPE_OBSOLETE: + type = NULL; break; + } + if (!type) + continue; + smartlist_add_asprintf(sl, "%s %s\n",var->name,type); + } + *answer = smartlist_join_strings(sl, "", 0, NULL); + SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); + smartlist_free(sl); + } else if (!strcmp(question, "config/defaults")) { + smartlist_t *sl = smartlist_new(); + int dirauth_lines_seen = 0, fallback_lines_seen = 0; + for (int i = 0; option_vars_[i].name; ++i) { + const config_var_t *var = &option_vars_[i]; + if (var->initvalue != NULL) { + if (strcmp(option_vars_[i].name, "DirAuthority") == 0) { + /* + * Count dirauth lines we have a default for; we'll use the + * count later to decide whether to add the defaults manually + */ + ++dirauth_lines_seen; + } + if (strcmp(option_vars_[i].name, "FallbackDir") == 0) { + /* + * Similarly count fallback lines, so that we can decided later + * to add the defaults manually. + */ + ++fallback_lines_seen; + } + char *val = esc_for_log(var->initvalue); + smartlist_add_asprintf(sl, "%s %s\n",var->name,val); + tor_free(val); + } + } + + if (dirauth_lines_seen == 0) { + /* + * We didn't see any directory authorities with default values, + * so add the list of default authorities manually. + */ + + /* + * default_authorities is defined earlier in this file and + * is a const char ** NULL-terminated array of dirauth config + * lines. + */ + for (const char **i = default_authorities; *i != NULL; ++i) { + char *val = esc_for_log(*i); + smartlist_add_asprintf(sl, "DirAuthority %s\n", val); + tor_free(val); + } + } + + if (fallback_lines_seen == 0 && + get_options()->UseDefaultFallbackDirs == 1) { + /* + * We didn't see any explicitly configured fallback mirrors, + * so add the defaults to the list manually. + * + * default_fallbacks is included earlier in this file and + * is a const char ** NULL-terminated array of fallback config lines. + */ + const char **i; + + for (i = default_fallbacks; *i != NULL; ++i) { + char *val = esc_for_log(*i); + smartlist_add_asprintf(sl, "FallbackDir %s\n", val); + tor_free(val); + } + } + + *answer = smartlist_join_strings(sl, "", 0, NULL); + SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); + smartlist_free(sl); + } + return 0; +} + +/* Check whether an address has already been set against the options + * depending on address family and destination type. Any exsting + * value will lead to a fail, even if it is the same value. If not + * set and not only validating, copy it into this location too. + * Returns 0 on success or -1 if this address is already set. + */ +static int +verify_and_store_outbound_address(sa_family_t family, tor_addr_t *addr, + outbound_addr_t type, or_options_t *options, int validate_only) +{ + if (type>=OUTBOUND_ADDR_MAX || (family!=AF_INET && family!=AF_INET6)) { + return -1; + } + int fam_index=0; + if (family==AF_INET6) { + fam_index=1; + } + tor_addr_t *dest=&options->OutboundBindAddresses[type][fam_index]; + if (!tor_addr_is_null(dest)) { + return -1; + } + if (!validate_only) { + tor_addr_copy(dest, addr); + } + return 0; +} + +/* Parse a list of address lines for a specific destination type. + * Will store them into the options if not validate_only. If a + * problem occurs, a suitable error message is store in msg. + * Returns 0 on success or -1 if any address is already set. + */ +static int +parse_outbound_address_lines(const config_line_t *lines, outbound_addr_t type, + or_options_t *options, int validate_only, char **msg) +{ + tor_addr_t addr; + sa_family_t family; + while (lines) { + family = tor_addr_parse(&addr, lines->value); + if (verify_and_store_outbound_address(family, &addr, type, + options, validate_only)) { + if (msg) + tor_asprintf(msg, "Multiple%s%s outbound bind addresses " + "configured: %s", + family==AF_INET?" IPv4":(family==AF_INET6?" IPv6":""), + type==OUTBOUND_ADDR_OR?" OR": + (type==OUTBOUND_ADDR_EXIT?" exit":""), lines->value); + return -1; + } + lines = lines->next; + } + return 0; +} + +/** Parse outbound bind address option lines. If <b>validate_only</b> + * is not 0 update OutboundBindAddresses in <b>options</b>. + * Only one address can be set for any of these values. + * On failure, set <b>msg</b> (if provided) to a newly allocated string + * containing a description of the problem and return -1. + */ +static int +parse_outbound_addresses(or_options_t *options, int validate_only, char **msg) +{ + if (!validate_only) { + memset(&options->OutboundBindAddresses, 0, + sizeof(options->OutboundBindAddresses)); + } + + if (parse_outbound_address_lines(options->OutboundBindAddress, + OUTBOUND_ADDR_EXIT_AND_OR, options, + validate_only, msg) < 0) { + goto err; + } + + if (parse_outbound_address_lines(options->OutboundBindAddressOR, + OUTBOUND_ADDR_OR, options, validate_only, + msg) < 0) { + goto err; + } + + if (parse_outbound_address_lines(options->OutboundBindAddressExit, + OUTBOUND_ADDR_EXIT, options, validate_only, + msg) < 0) { + goto err; + } + + return 0; + err: + return -1; +} + +/** Load one of the geoip files, <a>family</a> determining which + * one. <a>default_fname</a> is used if on Windows and + * <a>fname</a> equals "<default>". */ +static void +config_load_geoip_file_(sa_family_t family, + const char *fname, + const char *default_fname) +{ + const or_options_t *options = get_options(); + const char *msg = ""; + int severity = options_need_geoip_info(options, &msg) ? LOG_WARN : LOG_INFO; + int r; + +#ifdef _WIN32 + char *free_fname = NULL; /* Used to hold any temporary-allocated value */ + /* XXXX Don't use this "<default>" junk; make our filename options + * understand prefixes somehow. -NM */ + if (!strcmp(fname, "<default>")) { + const char *conf_root = get_windows_conf_root(); + tor_asprintf(&free_fname, "%s\\%s", conf_root, default_fname); + fname = free_fname; + } + r = geoip_load_file(family, fname, severity); + tor_free(free_fname); +#else /* !(defined(_WIN32)) */ + (void)default_fname; + r = geoip_load_file(family, fname, severity); +#endif /* defined(_WIN32) */ + + if (r < 0 && severity == LOG_WARN) { + log_warn(LD_GENERAL, "%s", msg); + } +} + +/** Load geoip files for IPv4 and IPv6 if <a>options</a> and + * <a>old_options</a> indicate we should. */ +static void +config_maybe_load_geoip_files_(const or_options_t *options, + const or_options_t *old_options) +{ + /* XXXX Reload GeoIPFile on SIGHUP. -NM */ + + if (options->GeoIPFile && + ((!old_options || !opt_streq(old_options->GeoIPFile, + options->GeoIPFile)) + || !geoip_is_loaded(AF_INET))) { + config_load_geoip_file_(AF_INET, options->GeoIPFile, "geoip"); + /* Okay, now we need to maybe change our mind about what is in + * which country. We do this for IPv4 only since that's what we + * store in node->country. */ + refresh_all_country_info(); + } + if (options->GeoIPv6File && + ((!old_options || !opt_streq(old_options->GeoIPv6File, + options->GeoIPv6File)) + || !geoip_is_loaded(AF_INET6))) { + config_load_geoip_file_(AF_INET6, options->GeoIPv6File, "geoip6"); + } +} + +/** Initialize cookie authentication (used so far by the ControlPort + * and Extended ORPort). + * + * Allocate memory and create a cookie (of length <b>cookie_len</b>) + * in <b>cookie_out</b>. + * Then write it down to <b>fname</b> and prepend it with <b>header</b>. + * + * If <b>group_readable</b> is set, set <b>fname</b> to be readable + * by the default GID. + * + * If the whole procedure was successful, set + * <b>cookie_is_set_out</b> to True. */ +int +init_cookie_authentication(const char *fname, const char *header, + int cookie_len, int group_readable, + uint8_t **cookie_out, int *cookie_is_set_out) +{ + char cookie_file_str_len = strlen(header) + cookie_len; + char *cookie_file_str = tor_malloc(cookie_file_str_len); + int retval = -1; + + /* We don't want to generate a new cookie every time we call + * options_act(). One should be enough. */ + if (*cookie_is_set_out) { + retval = 0; /* we are all set */ + goto done; + } + + /* If we've already set the cookie, free it before re-setting + it. This can happen if we previously generated a cookie, but + couldn't write it to a disk. */ + if (*cookie_out) + tor_free(*cookie_out); + + /* Generate the cookie */ + *cookie_out = tor_malloc(cookie_len); + crypto_rand((char *)*cookie_out, cookie_len); + + /* Create the string that should be written on the file. */ + memcpy(cookie_file_str, header, strlen(header)); + memcpy(cookie_file_str+strlen(header), *cookie_out, cookie_len); + if (write_bytes_to_file(fname, cookie_file_str, cookie_file_str_len, 1)) { + log_warn(LD_FS,"Error writing auth cookie to %s.", escaped(fname)); + goto done; + } + +#ifndef _WIN32 + if (group_readable) { + if (chmod(fname, 0640)) { + log_warn(LD_FS,"Unable to make %s group-readable.", escaped(fname)); + } + } +#else /* !(!defined(_WIN32)) */ + (void) group_readable; +#endif /* !defined(_WIN32) */ + + /* Success! */ + log_info(LD_GENERAL, "Generated auth cookie file in '%s'.", escaped(fname)); + *cookie_is_set_out = 1; + retval = 0; + + done: + memwipe(cookie_file_str, 0, cookie_file_str_len); + tor_free(cookie_file_str); + return retval; +} + +/** + * Return true if any option is set in <b>options</b> to make us behave + * as a client. + */ +int +options_any_client_port_set(const or_options_t *options) +{ + return (options->SocksPort_set || + options->TransPort_set || + options->NATDPort_set || + options->DNSPort_set || + options->HTTPTunnelPort_set); +} diff --git a/src/app/config/config.h b/src/app/config/config.h new file mode 100644 index 0000000000..301faf7067 --- /dev/null +++ b/src/app/config/config.h @@ -0,0 +1,300 @@ +/* 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 config.h + * \brief Header file for config.c. + **/ + +#ifndef TOR_CONFIG_H +#define TOR_CONFIG_H + +#include "app/config/or_options_st.h" +#include "lib/testsupport/testsupport.h" + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(DARWIN) +#define KERNEL_MAY_SUPPORT_IPFW +#endif + +/** Lowest allowable value for HeartbeatPeriod; if this is too low, we might + * expose more information than we're comfortable with. */ +#define MIN_HEARTBEAT_PERIOD (30*60) + +/** Maximum default value for MaxMemInQueues, in bytes. */ +#if SIZEOF_VOID_P >= 8 +#define MAX_DEFAULT_MEMORY_QUEUE_SIZE (UINT64_C(8) << 30) +#else +#define MAX_DEFAULT_MEMORY_QUEUE_SIZE (UINT64_C(2) << 30) +#endif + +MOCK_DECL(const char*, get_dirportfrontpage, (void)); +MOCK_DECL(const or_options_t *, get_options, (void)); +MOCK_DECL(or_options_t *, get_options_mutable, (void)); +int set_options(or_options_t *new_val, char **msg); +void config_free_all(void); +const char *safe_str_client(const char *address); +const char *safe_str(const char *address); +const char *escaped_safe_str_client(const char *address); +const char *escaped_safe_str(const char *address); +void init_protocol_warning_severity_level(void); +int get_protocol_warning_severity_level(void); +const char *get_version(void); +const char *get_short_version(void); + +/** An error from options_trial_assign() or options_init_from_string(). */ +typedef enum setopt_err_t { + SETOPT_OK = 0, + SETOPT_ERR_MISC = -1, + SETOPT_ERR_PARSE = -2, + SETOPT_ERR_TRANSITION = -3, + SETOPT_ERR_SETTING = -4, +} setopt_err_t; +setopt_err_t options_trial_assign(struct config_line_t *list, unsigned flags, + char **msg); + +uint32_t get_last_resolved_addr(void); +void reset_last_resolved_addr(void); +int resolve_my_address(int warn_severity, const or_options_t *options, + uint32_t *addr_out, + const char **method_out, char **hostname_out); +MOCK_DECL(int, is_local_addr, (const tor_addr_t *addr)); +void options_init(or_options_t *options); + +#define OPTIONS_DUMP_MINIMAL 1 +#define OPTIONS_DUMP_DEFAULTS 2 +#define OPTIONS_DUMP_ALL 3 +char *options_dump(const or_options_t *options, int how_to_dump); +int options_init_from_torrc(int argc, char **argv); +setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf, + int command, const char *command_arg, char **msg); +int option_is_recognized(const char *key); +const char *option_get_canonical_name(const char *key); +struct config_line_t *option_get_assignment(const or_options_t *options, + const char *key); +int options_save_current(void); +const char *get_torrc_fname(int defaults_fname); +typedef enum { + DIRROOT_DATADIR, + DIRROOT_CACHEDIR, + DIRROOT_KEYDIR +} directory_root_t; + +MOCK_DECL(char *, + options_get_dir_fname2_suffix, + (const or_options_t *options, + directory_root_t roottype, + const char *sub1, const char *sub2, + const char *suffix)); + +/* These macros wrap options_get_dir_fname2_suffix to provide a more + * convenient API for finding filenames that Tor uses inside its storage + * They are named according to a pattern: + * (options_)?get_(cache|key|data)dir_fname(2)?(_suffix)? + * + * Macros that begin with options_ take an options argument; the others + * work with respect to the global options. + * + * Each macro works relative to the data directory, the key directory, + * or the cache directory, as determined by which one is mentioned. + * + * Macro variants with "2" in their name take two path components; others + * take one. + * + * Macro variants with "_suffix" at the end take an additional suffix + * that gets appended to the end of the file + */ +#define options_get_datadir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_DATADIR, \ + (sub1), (sub2), (suffix)) +#define options_get_cachedir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_CACHEDIR, \ + (sub1), (sub2), (suffix)) +#define options_get_keydir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_KEYDIR, \ + (sub1), (sub2), (suffix)) + +#define options_get_datadir_fname(opts,sub1) \ + options_get_datadir_fname2_suffix((opts),(sub1), NULL, NULL) +#define options_get_datadir_fname2(opts,sub1,sub2) \ + options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL) + +#define get_datadir_fname2_suffix(sub1, sub2, suffix) \ + options_get_datadir_fname2_suffix(get_options(), (sub1), (sub2), (suffix)) +#define get_datadir_fname(sub1) \ + get_datadir_fname2_suffix((sub1), NULL, NULL) +#define get_datadir_fname2(sub1,sub2) \ + get_datadir_fname2_suffix((sub1), (sub2), NULL) +#define get_datadir_fname_suffix(sub1, suffix) \ + get_datadir_fname2_suffix((sub1), NULL, (suffix)) + +/** DOCDOC */ +#define options_get_keydir_fname(options, sub1) \ + options_get_keydir_fname2_suffix((options), (sub1), NULL, NULL) +#define get_keydir_fname_suffix(sub1, suffix) \ + options_get_keydir_fname2_suffix(get_options(), (sub1), NULL, suffix) +#define get_keydir_fname(sub1) \ + options_get_keydir_fname2_suffix(get_options(), (sub1), NULL, NULL) + +#define get_cachedir_fname(sub1) \ + options_get_cachedir_fname2_suffix(get_options(), (sub1), NULL, NULL) +#define get_cachedir_fname_suffix(sub1, suffix) \ + options_get_cachedir_fname2_suffix(get_options(), (sub1), NULL, (suffix)) + +#define safe_str_client(address) \ + safe_str_client_opts(NULL, address) +#define safe_str(address) \ + safe_str_opts(NULL, address) + +const char * safe_str_client_opts(const or_options_t *options, + const char *address); +const char * safe_str_opts(const or_options_t *options, + const char *address); + +int using_default_dir_authorities(const or_options_t *options); + +int create_keys_directory(const or_options_t *options); + +int check_or_create_data_subdir(const char *subdir); +int write_to_data_subdir(const char* subdir, const char* fname, + const char* str, const char* descr); + +int get_num_cpus(const or_options_t *options); + +MOCK_DECL(const smartlist_t *,get_configured_ports,(void)); +int get_first_advertised_port_by_type_af(int listener_type, + int address_family); +#define get_primary_or_port() \ + (get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, AF_INET)) +#define get_primary_dir_port() \ + (get_first_advertised_port_by_type_af(CONN_TYPE_DIR_LISTENER, AF_INET)) +const tor_addr_t *get_first_advertised_addr_by_type_af(int listener_type, + int address_family); +int port_exists_by_type_addr_port(int listener_type, const tor_addr_t *addr, + int port, int check_wildcard); +int port_exists_by_type_addr32h_port(int listener_type, uint32_t addr_ipv4h, + int port, int check_wildcard); + +char *get_first_listener_addrport_string(int listener_type); + +int options_need_geoip_info(const or_options_t *options, + const char **reason_out); + +smartlist_t *get_list_of_ports_to_forward(void); + +int getinfo_helper_config(control_connection_t *conn, + const char *question, char **answer, + const char **errmsg); + +uint32_t get_effective_bwrate(const or_options_t *options); +uint32_t get_effective_bwburst(const or_options_t *options); + +char *get_transport_bindaddr_from_config(const char *transport); + +int init_cookie_authentication(const char *fname, const char *header, + int cookie_len, int group_readable, + uint8_t **cookie_out, int *cookie_is_set_out); + +or_options_t *options_new(void); + +int config_parse_commandline(int argc, char **argv, int ignore_errors, + struct config_line_t **result, + struct config_line_t **cmdline_result); + +void config_register_addressmaps(const or_options_t *options); +/* XXXX move to connection_edge.h */ +int addressmap_register_auto(const char *from, const char *to, + time_t expires, + addressmap_entry_source_t addrmap_source, + const char **msg); + +int port_cfg_line_extract_addrport(const char *line, + char **addrport_out, + int *is_unix_out, + const char **rest_out); + +/** Represents the information stored in a torrc Bridge line. */ +typedef struct bridge_line_t { + tor_addr_t addr; /* The IP address of the bridge. */ + uint16_t port; /* The TCP port of the bridge. */ + char *transport_name; /* The name of the pluggable transport that + should be used to connect to the bridge. */ + char digest[DIGEST_LEN]; /* The bridge's identity key digest. */ + smartlist_t *socks_args; /* SOCKS arguments for the pluggable + transport proxy. */ +} bridge_line_t; + +void bridge_line_free_(bridge_line_t *bridge_line); +#define bridge_line_free(line) \ + FREE_AND_NULL(bridge_line_t, bridge_line_free_, (line)) +bridge_line_t *parse_bridge_line(const char *line); +smartlist_t *get_options_from_transport_options_line(const char *line, + const char *transport); +smartlist_t *get_options_for_server_transport(const char *transport); + +/* Port helper functions. */ +int options_any_client_port_set(const or_options_t *options); + +#ifdef CONFIG_PRIVATE + +#define CL_PORT_NO_STREAM_OPTIONS (1u<<0) +#define CL_PORT_WARN_NONLOCAL (1u<<1) +/* Was CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2) */ +#define CL_PORT_SERVER_OPTIONS (1u<<3) +#define CL_PORT_FORBID_NONLOCAL (1u<<4) +#define CL_PORT_TAKES_HOSTNAMES (1u<<5) +#define CL_PORT_IS_UNIXSOCKET (1u<<6) +#define CL_PORT_DFLT_GROUP_WRITABLE (1u<<7) + +STATIC int options_act(const or_options_t *old_options); +#ifdef TOR_UNIT_TESTS +extern struct config_format_t options_format; +#endif + +STATIC port_cfg_t *port_cfg_new(size_t namelen); +#define port_cfg_free(port) \ + FREE_AND_NULL(port_cfg_t, port_cfg_free_, (port)) +STATIC void port_cfg_free_(port_cfg_t *port); +#define or_options_free(opt) \ + FREE_AND_NULL(or_options_t, or_options_free_, (opt)) +STATIC void or_options_free_(or_options_t *options); +STATIC int options_validate_single_onion(or_options_t *options, + char **msg); +STATIC int options_validate(or_options_t *old_options, + or_options_t *options, + or_options_t *default_options, + int from_setconf, char **msg); +STATIC int parse_transport_line(const or_options_t *options, + const char *line, int validate_only, + int server); +STATIC int consider_adding_dir_servers(const or_options_t *options, + const or_options_t *old_options); +STATIC void add_default_trusted_dir_authorities(dirinfo_type_t type); +MOCK_DECL(STATIC void, add_default_fallback_dir_servers, (void)); +STATIC int parse_dir_authority_line(const char *line, + dirinfo_type_t required_type, + int validate_only); +STATIC int parse_dir_fallback_line(const char *line, int validate_only); +STATIC int have_enough_mem_for_dircache(const or_options_t *options, + size_t total_mem, char **msg); +STATIC int parse_port_config(smartlist_t *out, + const struct config_line_t *ports, + const char *portname, + int listener_type, + const char *defaultaddr, + int defaultport, + const unsigned flags); + +STATIC int check_bridge_distribution_setting(const char *bd); + +STATIC uint64_t compute_real_max_mem_in_queues(const uint64_t val, + int log_guess); +STATIC int open_and_add_file_log(const log_severity_list_t *severity, + const char *fname, + int truncate_log); + +#endif /* defined(CONFIG_PRIVATE) */ + +#endif /* !defined(TOR_CONFIG_H) */ diff --git a/src/app/config/confparse.c b/src/app/config/confparse.c new file mode 100644 index 0000000000..729e7a4478 --- /dev/null +++ b/src/app/config/confparse.c @@ -0,0 +1,1207 @@ +/* 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 confparse.c + * + * \brief Back-end for parsing and generating key-value files, used to + * implement the torrc file format and the state file. + * + * This module is used by config.c to parse and encode torrc + * configuration files, and by statefile.c to parse and encode the + * $DATADIR/state file. + * + * To use this module, its callers provide an instance of + * config_format_t to describe the mappings from a set of configuration + * options to a number of fields in a C structure. With this mapping, + * the functions here can convert back and forth between the C structure + * specified, and a linked list of key-value pairs. + */ + +#include "core/or/or.h" +#include "app/config/confparse.h" +#include "feature/nodelist/routerset.h" + +#include "lib/container/bitarray.h" +#include "lib/encoding/confline.h" + +static uint64_t config_parse_memunit(const char *s, int *ok); +static int config_parse_msec_interval(const char *s, int *ok); +static int config_parse_interval(const char *s, int *ok); +static void config_reset(const config_format_t *fmt, void *options, + const config_var_t *var, int use_defaults); + +/** Allocate an empty configuration object of a given format type. */ +void * +config_new(const config_format_t *fmt) +{ + void *opts = tor_malloc_zero(fmt->size); + *(uint32_t*)STRUCT_VAR_P(opts, fmt->magic_offset) = fmt->magic; + CONFIG_CHECK(fmt, opts); + return opts; +} + +/* + * Functions to parse config options + */ + +/** If <b>option</b> is an official abbreviation for a longer option, + * return the longer option. Otherwise return <b>option</b>. + * If <b>command_line</b> is set, apply all abbreviations. Otherwise, only + * apply abbreviations that work for the config file and the command line. + * If <b>warn_obsolete</b> is set, warn about deprecated names. */ +const char * +config_expand_abbrev(const config_format_t *fmt, const char *option, + int command_line, int warn_obsolete) +{ + int i; + if (! fmt->abbrevs) + return option; + for (i=0; fmt->abbrevs[i].abbreviated; ++i) { + /* Abbreviations are case insensitive. */ + if (!strcasecmp(option,fmt->abbrevs[i].abbreviated) && + (command_line || !fmt->abbrevs[i].commandline_only)) { + if (warn_obsolete && fmt->abbrevs[i].warn) { + log_warn(LD_CONFIG, + "The configuration option '%s' is deprecated; " + "use '%s' instead.", + fmt->abbrevs[i].abbreviated, + fmt->abbrevs[i].full); + } + /* Keep going through the list in case we want to rewrite it more. + * (We could imagine recursing here, but I don't want to get the + * user into an infinite loop if we craft our list wrong.) */ + option = fmt->abbrevs[i].full; + } + } + return option; +} + +/** If <b>key</b> is a deprecated configuration option, return the message + * explaining why it is deprecated (which may be an empty string). Return NULL + * if it is not deprecated. The <b>key</b> field must be fully expanded. */ +const char * +config_find_deprecation(const config_format_t *fmt, const char *key) +{ + if (BUG(fmt == NULL) || BUG(key == NULL)) + return NULL; + if (fmt->deprecations == NULL) + return NULL; + + const config_deprecation_t *d; + for (d = fmt->deprecations; d->name; ++d) { + if (!strcasecmp(d->name, key)) { + return d->why_deprecated ? d->why_deprecated : ""; + } + } + return NULL; +} + +/** As config_find_option, but return a non-const pointer. */ +config_var_t * +config_find_option_mutable(config_format_t *fmt, const char *key) +{ + int i; + size_t keylen = strlen(key); + if (!keylen) + return NULL; /* if they say "--" on the command line, it's not an option */ + /* First, check for an exact (case-insensitive) match */ + for (i=0; fmt->vars[i].name; ++i) { + if (!strcasecmp(key, fmt->vars[i].name)) { + return &fmt->vars[i]; + } + } + /* If none, check for an abbreviated match */ + for (i=0; fmt->vars[i].name; ++i) { + if (!strncasecmp(key, fmt->vars[i].name, keylen)) { + log_warn(LD_CONFIG, "The abbreviation '%s' is deprecated. " + "Please use '%s' instead", + key, fmt->vars[i].name); + return &fmt->vars[i]; + } + } + /* Okay, unrecognized option */ + return NULL; +} + +/** If <b>key</b> is a configuration option, return the corresponding const + * config_var_t. Otherwise, if <b>key</b> is a non-standard abbreviation, + * warn, and return the corresponding const config_var_t. Otherwise return + * NULL. + */ +const config_var_t * +config_find_option(const config_format_t *fmt, const char *key) +{ + return config_find_option_mutable((config_format_t*)fmt, key); +} + +/** Return the number of option entries in <b>fmt</b>. */ +static int +config_count_options(const config_format_t *fmt) +{ + int i; + for (i=0; fmt->vars[i].name; ++i) + ; + return i; +} + +/* + * Functions to assign config options. + */ + +/** <b>c</b>-\>key is known to be a real key. Update <b>options</b> + * with <b>c</b>-\>value and return 0, or return -1 if bad value. + * + * Called from config_assign_line() and option_reset(). + */ +static int +config_assign_value(const config_format_t *fmt, void *options, + config_line_t *c, char **msg) +{ + int i, ok; + const config_var_t *var; + void *lvalue; + + CONFIG_CHECK(fmt, options); + + var = config_find_option(fmt, c->key); + tor_assert(var); + + lvalue = STRUCT_VAR_P(options, var->var_offset); + + switch (var->type) { + + case CONFIG_TYPE_PORT: + if (!strcasecmp(c->value, "auto")) { + *(int *)lvalue = CFG_AUTO_PORT; + break; + } + /* fall through */ + case CONFIG_TYPE_INT: + case CONFIG_TYPE_UINT: + i = (int)tor_parse_long(c->value, 10, + var->type==CONFIG_TYPE_INT ? INT_MIN : 0, + var->type==CONFIG_TYPE_PORT ? 65535 : INT_MAX, + &ok, NULL); + if (!ok) { + tor_asprintf(msg, + "Int keyword '%s %s' is malformed or out of bounds.", + c->key, c->value); + return -1; + } + *(int *)lvalue = i; + break; + + case CONFIG_TYPE_UINT64: { + uint64_t u64 = tor_parse_uint64(c->value, 10, + 0, UINT64_MAX, &ok, NULL); + if (!ok) { + tor_asprintf(msg, + "uint64 keyword '%s %s' is malformed or out of bounds.", + c->key, c->value); + return -1; + } + *(uint64_t *)lvalue = u64; + break; + } + + case CONFIG_TYPE_CSV_INTERVAL: { + /* We used to have entire smartlists here. But now that all of our + * download schedules use exponential backoff, only the first part + * matters. */ + const char *comma = strchr(c->value, ','); + const char *val = c->value; + char *tmp = NULL; + if (comma) { + tmp = tor_strndup(c->value, comma - c->value); + val = tmp; + } + + i = config_parse_interval(val, &ok); + if (!ok) { + tor_asprintf(msg, + "Interval '%s %s' is malformed or out of bounds.", + c->key, c->value); + tor_free(tmp); + return -1; + } + *(int *)lvalue = i; + tor_free(tmp); + break; + } + + case CONFIG_TYPE_INTERVAL: { + i = config_parse_interval(c->value, &ok); + if (!ok) { + tor_asprintf(msg, + "Interval '%s %s' is malformed or out of bounds.", + c->key, c->value); + return -1; + } + *(int *)lvalue = i; + break; + } + + case CONFIG_TYPE_MSEC_INTERVAL: { + i = config_parse_msec_interval(c->value, &ok); + if (!ok) { + tor_asprintf(msg, + "Msec interval '%s %s' is malformed or out of bounds.", + c->key, c->value); + return -1; + } + *(int *)lvalue = i; + break; + } + + case CONFIG_TYPE_MEMUNIT: { + uint64_t u64 = config_parse_memunit(c->value, &ok); + if (!ok) { + tor_asprintf(msg, + "Value '%s %s' is malformed or out of bounds.", + c->key, c->value); + return -1; + } + *(uint64_t *)lvalue = u64; + break; + } + + case CONFIG_TYPE_BOOL: + i = (int)tor_parse_long(c->value, 10, 0, 1, &ok, NULL); + if (!ok) { + tor_asprintf(msg, + "Boolean '%s %s' expects 0 or 1.", + c->key, c->value); + return -1; + } + *(int *)lvalue = i; + break; + + case CONFIG_TYPE_AUTOBOOL: + if (!strcasecmp(c->value, "auto")) + *(int *)lvalue = -1; + else if (!strcmp(c->value, "0")) + *(int *)lvalue = 0; + else if (!strcmp(c->value, "1")) + *(int *)lvalue = 1; + else { + tor_asprintf(msg, "Boolean '%s %s' expects 0, 1, or 'auto'.", + c->key, c->value); + return -1; + } + break; + + case CONFIG_TYPE_STRING: + case CONFIG_TYPE_FILENAME: + tor_free(*(char **)lvalue); + *(char **)lvalue = tor_strdup(c->value); + break; + + case CONFIG_TYPE_DOUBLE: + *(double *)lvalue = atof(c->value); + break; + + case CONFIG_TYPE_ISOTIME: + if (parse_iso_time(c->value, (time_t *)lvalue)) { + tor_asprintf(msg, + "Invalid time '%s' for keyword '%s'", c->value, c->key); + return -1; + } + break; + + case CONFIG_TYPE_ROUTERSET: + if (*(routerset_t**)lvalue) { + routerset_free(*(routerset_t**)lvalue); + } + *(routerset_t**)lvalue = routerset_new(); + if (routerset_parse(*(routerset_t**)lvalue, c->value, c->key)<0) { + tor_asprintf(msg, "Invalid exit list '%s' for option '%s'", + c->value, c->key); + return -1; + } + break; + + case CONFIG_TYPE_CSV: + if (*(smartlist_t**)lvalue) { + SMARTLIST_FOREACH(*(smartlist_t**)lvalue, char *, cp, tor_free(cp)); + smartlist_clear(*(smartlist_t**)lvalue); + } else { + *(smartlist_t**)lvalue = smartlist_new(); + } + + smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + break; + + case CONFIG_TYPE_LINELIST: + case CONFIG_TYPE_LINELIST_S: + { + config_line_t *lastval = *(config_line_t**)lvalue; + if (lastval && lastval->fragile) { + if (c->command != CONFIG_LINE_APPEND) { + config_free_lines(lastval); + *(config_line_t**)lvalue = NULL; + } else { + lastval->fragile = 0; + } + } + + config_line_append((config_line_t**)lvalue, c->key, c->value); + } + break; + case CONFIG_TYPE_OBSOLETE: + log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key); + break; + case CONFIG_TYPE_LINELIST_V: + tor_asprintf(msg, + "You may not provide a value for virtual option '%s'", c->key); + return -1; + default: + tor_assert(0); + break; + } + return 0; +} + +/** Mark every linelist in <b>options</b> "fragile", so that fresh assignments + * to it will replace old ones. */ +static void +config_mark_lists_fragile(const config_format_t *fmt, void *options) +{ + int i; + tor_assert(fmt); + tor_assert(options); + + for (i = 0; fmt->vars[i].name; ++i) { + const config_var_t *var = &fmt->vars[i]; + config_line_t *list; + if (var->type != CONFIG_TYPE_LINELIST && + var->type != CONFIG_TYPE_LINELIST_V) + continue; + + list = *(config_line_t **)STRUCT_VAR_P(options, var->var_offset); + if (list) + list->fragile = 1; + } +} + +void +warn_deprecated_option(const char *what, const char *why) +{ + const char *space = (why && strlen(why)) ? " " : ""; + log_warn(LD_CONFIG, "The %s option is deprecated, and will most likely " + "be removed in a future version of Tor.%s%s (If you think this is " + "a mistake, please let us know!)", + what, space, why); +} + +/** If <b>c</b> is a syntactically valid configuration line, update + * <b>options</b> with its value and return 0. Otherwise return -1 for bad + * key, -2 for bad value. + * + * If <b>clear_first</b> is set, clear the value first. Then if + * <b>use_defaults</b> is set, set the value to the default. + * + * Called from config_assign(). + */ +static int +config_assign_line(const config_format_t *fmt, void *options, + config_line_t *c, unsigned flags, + bitarray_t *options_seen, char **msg) +{ + const unsigned use_defaults = flags & CAL_USE_DEFAULTS; + const unsigned clear_first = flags & CAL_CLEAR_FIRST; + const unsigned warn_deprecations = flags & CAL_WARN_DEPRECATIONS; + const config_var_t *var; + + CONFIG_CHECK(fmt, options); + + var = config_find_option(fmt, c->key); + if (!var) { + if (fmt->extra) { + void *lvalue = STRUCT_VAR_P(options, fmt->extra->var_offset); + log_info(LD_CONFIG, + "Found unrecognized option '%s'; saving it.", c->key); + config_line_append((config_line_t**)lvalue, c->key, c->value); + return 0; + } else { + tor_asprintf(msg, + "Unknown option '%s'. Failing.", c->key); + return -1; + } + } + + /* Put keyword into canonical case. */ + if (strcmp(var->name, c->key)) { + tor_free(c->key); + c->key = tor_strdup(var->name); + } + + const char *deprecation_msg; + if (warn_deprecations && + (deprecation_msg = config_find_deprecation(fmt, var->name))) { + warn_deprecated_option(var->name, deprecation_msg); + } + + if (!strlen(c->value)) { + /* reset or clear it, then return */ + if (!clear_first) { + if ((var->type == CONFIG_TYPE_LINELIST || + var->type == CONFIG_TYPE_LINELIST_S) && + c->command != CONFIG_LINE_CLEAR) { + /* We got an empty linelist from the torrc or command line. + As a special case, call this an error. Warn and ignore. */ + log_warn(LD_CONFIG, + "Linelist option '%s' has no value. Skipping.", c->key); + } else { /* not already cleared */ + config_reset(fmt, options, var, use_defaults); + } + } + return 0; + } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) { + config_reset(fmt, options, var, use_defaults); + } + + if (options_seen && (var->type != CONFIG_TYPE_LINELIST && + var->type != CONFIG_TYPE_LINELIST_S)) { + /* We're tracking which options we've seen, and this option is not + * supposed to occur more than once. */ + int var_index = (int)(var - fmt->vars); + if (bitarray_is_set(options_seen, var_index)) { + log_warn(LD_CONFIG, "Option '%s' used more than once; all but the last " + "value will be ignored.", var->name); + } + bitarray_set(options_seen, var_index); + } + + if (config_assign_value(fmt, options, c, msg) < 0) + return -2; + return 0; +} + +/** Restore the option named <b>key</b> in options to its default value. + * Called from config_assign(). */ +static void +config_reset_line(const config_format_t *fmt, void *options, + const char *key, int use_defaults) +{ + const config_var_t *var; + + CONFIG_CHECK(fmt, options); + + var = config_find_option(fmt, key); + if (!var) + return; /* give error on next pass. */ + + config_reset(fmt, options, var, use_defaults); +} + +/** Return true iff value needs to be quoted and escaped to be used in + * a configuration file. */ +static int +config_value_needs_escape(const char *value) +{ + if (*value == '\"') + return 1; + while (*value) { + switch (*value) + { + case '\r': + case '\n': + case '#': + /* Note: quotes and backspaces need special handling when we are using + * quotes, not otherwise, so they don't trigger escaping on their + * own. */ + return 1; + default: + if (!TOR_ISPRINT(*value)) + return 1; + } + ++value; + } + return 0; +} + +/** Return newly allocated line or lines corresponding to <b>key</b> in the + * configuration <b>options</b>. If <b>escape_val</b> is true and a + * value needs to be quoted before it's put in a config file, quote and + * escape that value. Return NULL if no such key exists. */ +config_line_t * +config_get_assigned_option(const config_format_t *fmt, const void *options, + const char *key, int escape_val) +{ + const config_var_t *var; + const void *value; + config_line_t *result; + tor_assert(options && key); + + CONFIG_CHECK(fmt, options); + + var = config_find_option(fmt, key); + if (!var) { + log_warn(LD_CONFIG, "Unknown option '%s'. Failing.", key); + return NULL; + } + value = STRUCT_VAR_P(options, var->var_offset); + + result = tor_malloc_zero(sizeof(config_line_t)); + result->key = tor_strdup(var->name); + switch (var->type) + { + case CONFIG_TYPE_STRING: + case CONFIG_TYPE_FILENAME: + if (*(char**)value) { + result->value = tor_strdup(*(char**)value); + } else { + tor_free(result->key); + tor_free(result); + return NULL; + } + break; + case CONFIG_TYPE_ISOTIME: + if (*(time_t*)value) { + result->value = tor_malloc(ISO_TIME_LEN+1); + format_iso_time(result->value, *(time_t*)value); + } else { + tor_free(result->key); + tor_free(result); + } + escape_val = 0; /* Can't need escape. */ + break; + case CONFIG_TYPE_PORT: + if (*(int*)value == CFG_AUTO_PORT) { + result->value = tor_strdup("auto"); + escape_val = 0; + break; + } + /* fall through */ + case CONFIG_TYPE_CSV_INTERVAL: + case CONFIG_TYPE_INTERVAL: + case CONFIG_TYPE_MSEC_INTERVAL: + case CONFIG_TYPE_UINT: + case CONFIG_TYPE_INT: + /* This means every or_options_t uint or bool element + * needs to be an int. Not, say, a uint16_t or char. */ + tor_asprintf(&result->value, "%d", *(int*)value); + escape_val = 0; /* Can't need escape. */ + break; + case CONFIG_TYPE_UINT64: /* Fall through */ + case CONFIG_TYPE_MEMUNIT: + tor_asprintf(&result->value, "%"PRIu64, + (*(uint64_t*)value)); + escape_val = 0; /* Can't need escape. */ + break; + case CONFIG_TYPE_DOUBLE: + tor_asprintf(&result->value, "%f", *(double*)value); + escape_val = 0; /* Can't need escape. */ + break; + + case CONFIG_TYPE_AUTOBOOL: + if (*(int*)value == -1) { + result->value = tor_strdup("auto"); + escape_val = 0; + break; + } + /* fall through */ + case CONFIG_TYPE_BOOL: + result->value = tor_strdup(*(int*)value ? "1" : "0"); + escape_val = 0; /* Can't need escape. */ + break; + case CONFIG_TYPE_ROUTERSET: + result->value = routerset_to_string(*(routerset_t**)value); + break; + case CONFIG_TYPE_CSV: + if (*(smartlist_t**)value) + result->value = + smartlist_join_strings(*(smartlist_t**)value, ",", 0, NULL); + else + result->value = tor_strdup(""); + break; + case CONFIG_TYPE_OBSOLETE: + log_fn(LOG_INFO, LD_CONFIG, + "You asked me for the value of an obsolete config option '%s'.", + key); + tor_free(result->key); + tor_free(result); + return NULL; + case CONFIG_TYPE_LINELIST_S: + tor_free(result->key); + tor_free(result); + result = config_lines_dup_and_filter(*(const config_line_t **)value, + key); + break; + case CONFIG_TYPE_LINELIST: + case CONFIG_TYPE_LINELIST_V: + tor_free(result->key); + tor_free(result); + result = config_lines_dup(*(const config_line_t**)value); + break; + default: + tor_free(result->key); + tor_free(result); + log_warn(LD_BUG,"Unknown type %d for known key '%s'", + var->type, key); + return NULL; + } + + if (escape_val) { + config_line_t *line; + for (line = result; line; line = line->next) { + if (line->value && config_value_needs_escape(line->value)) { + char *newval = esc_for_log(line->value); + tor_free(line->value); + line->value = newval; + } + } + } + + return result; +} +/** Iterate through the linked list of requested options <b>list</b>. + * For each item, convert as appropriate and assign to <b>options</b>. + * If an item is unrecognized, set *msg and return -1 immediately, + * else return 0 for success. + * + * If <b>clear_first</b>, interpret config options as replacing (not + * extending) their previous values. If <b>clear_first</b> is set, + * then <b>use_defaults</b> to decide if you set to defaults after + * clearing, or make the value 0 or NULL. + * + * Here are the use cases: + * 1. A non-empty AllowInvalid line in your torrc. Appends to current + * if linelist, replaces current if csv. + * 2. An empty AllowInvalid line in your torrc. Should clear it. + * 3. "RESETCONF AllowInvalid" sets it to default. + * 4. "SETCONF AllowInvalid" makes it NULL. + * 5. "SETCONF AllowInvalid=foo" clears it and sets it to "foo". + * + * Use_defaults Clear_first + * 0 0 "append" + * 1 0 undefined, don't use + * 0 1 "set to null first" + * 1 1 "set to defaults first" + * Return 0 on success, -1 on bad key, -2 on bad value. + * + * As an additional special case, if a LINELIST config option has + * no value and clear_first is 0, then warn and ignore it. + */ + +/* +There are three call cases for config_assign() currently. + +Case one: Torrc entry +options_init_from_torrc() calls config_assign(0, 0) + calls config_assign_line(0, 0). + if value is empty, calls config_reset(0) and returns. + calls config_assign_value(), appends. + +Case two: setconf +options_trial_assign() calls config_assign(0, 1) + calls config_reset_line(0) + calls config_reset(0) + calls option_clear(). + calls config_assign_line(0, 1). + if value is empty, returns. + calls config_assign_value(), appends. + +Case three: resetconf +options_trial_assign() calls config_assign(1, 1) + calls config_reset_line(1) + calls config_reset(1) + calls option_clear(). + calls config_assign_value(default) + calls config_assign_line(1, 1). + returns. +*/ +int +config_assign(const config_format_t *fmt, void *options, config_line_t *list, + unsigned config_assign_flags, char **msg) +{ + config_line_t *p; + bitarray_t *options_seen; + const int n_options = config_count_options(fmt); + const unsigned clear_first = config_assign_flags & CAL_CLEAR_FIRST; + const unsigned use_defaults = config_assign_flags & CAL_USE_DEFAULTS; + + CONFIG_CHECK(fmt, options); + + /* pass 1: normalize keys */ + for (p = list; p; p = p->next) { + const char *full = config_expand_abbrev(fmt, p->key, 0, 1); + if (strcmp(full,p->key)) { + tor_free(p->key); + p->key = tor_strdup(full); + } + } + + /* pass 2: if we're reading from a resetting source, clear all + * mentioned config options, and maybe set to their defaults. */ + if (clear_first) { + for (p = list; p; p = p->next) + config_reset_line(fmt, options, p->key, use_defaults); + } + + options_seen = bitarray_init_zero(n_options); + /* pass 3: assign. */ + while (list) { + int r; + if ((r=config_assign_line(fmt, options, list, config_assign_flags, + options_seen, msg))) { + bitarray_free(options_seen); + return r; + } + list = list->next; + } + bitarray_free(options_seen); + + /** Now we're done assigning a group of options to the configuration. + * Subsequent group assignments should _replace_ linelists, not extend + * them. */ + config_mark_lists_fragile(fmt, options); + + return 0; +} + +/** Reset config option <b>var</b> to 0, 0.0, NULL, or the equivalent. + * Called from config_reset() and config_free(). */ +static void +config_clear(const config_format_t *fmt, void *options, + const config_var_t *var) +{ + void *lvalue = STRUCT_VAR_P(options, var->var_offset); + (void)fmt; /* unused */ + switch (var->type) { + case CONFIG_TYPE_STRING: + case CONFIG_TYPE_FILENAME: + tor_free(*(char**)lvalue); + break; + case CONFIG_TYPE_DOUBLE: + *(double*)lvalue = 0.0; + break; + case CONFIG_TYPE_ISOTIME: + *(time_t*)lvalue = 0; + break; + case CONFIG_TYPE_CSV_INTERVAL: + case CONFIG_TYPE_INTERVAL: + case CONFIG_TYPE_MSEC_INTERVAL: + case CONFIG_TYPE_UINT: + case CONFIG_TYPE_INT: + case CONFIG_TYPE_PORT: + case CONFIG_TYPE_BOOL: + *(int*)lvalue = 0; + break; + case CONFIG_TYPE_AUTOBOOL: + *(int*)lvalue = -1; + break; + case CONFIG_TYPE_UINT64: + case CONFIG_TYPE_MEMUNIT: + *(uint64_t*)lvalue = 0; + break; + case CONFIG_TYPE_ROUTERSET: + if (*(routerset_t**)lvalue) { + routerset_free(*(routerset_t**)lvalue); + *(routerset_t**)lvalue = NULL; + } + break; + case CONFIG_TYPE_CSV: + if (*(smartlist_t**)lvalue) { + SMARTLIST_FOREACH(*(smartlist_t **)lvalue, char *, cp, tor_free(cp)); + smartlist_free(*(smartlist_t **)lvalue); + *(smartlist_t **)lvalue = NULL; + } + break; + case CONFIG_TYPE_LINELIST: + case CONFIG_TYPE_LINELIST_S: + config_free_lines(*(config_line_t **)lvalue); + *(config_line_t **)lvalue = NULL; + break; + case CONFIG_TYPE_LINELIST_V: + /* handled by linelist_s. */ + break; + case CONFIG_TYPE_OBSOLETE: + break; + } +} + +/** Clear the option indexed by <b>var</b> in <b>options</b>. Then if + * <b>use_defaults</b>, set it to its default value. + * Called by config_init() and option_reset_line() and option_assign_line(). */ +static void +config_reset(const config_format_t *fmt, void *options, + const config_var_t *var, int use_defaults) +{ + config_line_t *c; + char *msg = NULL; + CONFIG_CHECK(fmt, options); + config_clear(fmt, options, var); /* clear it first */ + if (!use_defaults) + return; /* all done */ + if (var->initvalue) { + c = tor_malloc_zero(sizeof(config_line_t)); + c->key = tor_strdup(var->name); + c->value = tor_strdup(var->initvalue); + if (config_assign_value(fmt, options, c, &msg) < 0) { + log_warn(LD_BUG, "Failed to assign default: %s", msg); + tor_free(msg); /* if this happens it's a bug */ + } + config_free_lines(c); + } +} + +/** Release storage held by <b>options</b>. */ +void +config_free_(const config_format_t *fmt, void *options) +{ + int i; + + if (!options) + return; + + tor_assert(fmt); + + for (i=0; fmt->vars[i].name; ++i) + config_clear(fmt, options, &(fmt->vars[i])); + if (fmt->extra) { + config_line_t **linep = STRUCT_VAR_P(options, fmt->extra->var_offset); + config_free_lines(*linep); + *linep = NULL; + } + tor_free(options); +} + +/** Return true iff the option <b>name</b> has the same value in <b>o1</b> + * and <b>o2</b>. Must not be called for LINELIST_S or OBSOLETE options. + */ +int +config_is_same(const config_format_t *fmt, + const void *o1, const void *o2, + const char *name) +{ + config_line_t *c1, *c2; + int r = 1; + CONFIG_CHECK(fmt, o1); + CONFIG_CHECK(fmt, o2); + + c1 = config_get_assigned_option(fmt, o1, name, 0); + c2 = config_get_assigned_option(fmt, o2, name, 0); + r = config_lines_eq(c1, c2); + config_free_lines(c1); + config_free_lines(c2); + return r; +} + +/** Copy storage held by <b>old</b> into a new or_options_t and return it. */ +void * +config_dup(const config_format_t *fmt, const void *old) +{ + void *newopts; + int i; + config_line_t *line; + + newopts = config_new(fmt); + for (i=0; fmt->vars[i].name; ++i) { + if (fmt->vars[i].type == CONFIG_TYPE_LINELIST_S) + continue; + if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE) + continue; + line = config_get_assigned_option(fmt, old, fmt->vars[i].name, 0); + if (line) { + char *msg = NULL; + if (config_assign(fmt, newopts, line, 0, &msg) < 0) { + log_err(LD_BUG, "config_get_assigned_option() generated " + "something we couldn't config_assign(): %s", msg); + tor_free(msg); + tor_assert(0); + } + } + config_free_lines(line); + } + return newopts; +} +/** Set all vars in the configuration object <b>options</b> to their default + * values. */ +void +config_init(const config_format_t *fmt, void *options) +{ + int i; + const config_var_t *var; + CONFIG_CHECK(fmt, options); + + for (i=0; fmt->vars[i].name; ++i) { + var = &fmt->vars[i]; + if (!var->initvalue) + continue; /* defaults to NULL or 0 */ + config_reset(fmt, options, var, 1); + } +} + +/** Allocate and return a new string holding the written-out values of the vars + * in 'options'. If 'minimal', do not write out any default-valued vars. + * Else, if comment_defaults, write default values as comments. + */ +char * +config_dump(const config_format_t *fmt, const void *default_options, + const void *options, int minimal, + int comment_defaults) +{ + smartlist_t *elements; + const void *defaults = default_options; + void *defaults_tmp = NULL; + config_line_t *line, *assigned; + char *result; + int i; + char *msg = NULL; + + if (defaults == NULL) { + defaults = defaults_tmp = config_new(fmt); + config_init(fmt, defaults_tmp); + } + + /* XXX use a 1 here so we don't add a new log line while dumping */ + if (default_options == NULL) { + if (fmt->validate_fn(NULL, defaults_tmp, defaults_tmp, 1, &msg) < 0) { + log_err(LD_BUG, "Failed to validate default config: %s", msg); + tor_free(msg); + tor_assert(0); + } + } + + elements = smartlist_new(); + for (i=0; fmt->vars[i].name; ++i) { + int comment_option = 0; + if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE || + fmt->vars[i].type == CONFIG_TYPE_LINELIST_S) + continue; + /* Don't save 'hidden' control variables. */ + if (!strcmpstart(fmt->vars[i].name, "__")) + continue; + if (minimal && config_is_same(fmt, options, defaults, fmt->vars[i].name)) + continue; + else if (comment_defaults && + config_is_same(fmt, options, defaults, fmt->vars[i].name)) + comment_option = 1; + + line = assigned = + config_get_assigned_option(fmt, options, fmt->vars[i].name, 1); + + for (; line; line = line->next) { + if (!strcmpstart(line->key, "__")) { + /* This check detects "hidden" variables inside LINELIST_V structures. + */ + continue; + } + smartlist_add_asprintf(elements, "%s%s %s\n", + comment_option ? "# " : "", + line->key, line->value); + } + config_free_lines(assigned); + } + + if (fmt->extra) { + line = *(config_line_t**)STRUCT_VAR_P(options, fmt->extra->var_offset); + for (; line; line = line->next) { + smartlist_add_asprintf(elements, "%s %s\n", line->key, line->value); + } + } + + result = smartlist_join_strings(elements, "", 0, NULL); + SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); + smartlist_free(elements); + if (defaults_tmp) { + fmt->free_fn(defaults_tmp); + } + return result; +} + +/** Mapping from a unit name to a multiplier for converting that unit into a + * base unit. Used by config_parse_unit. */ +struct unit_table_t { + const char *unit; /**< The name of the unit */ + uint64_t multiplier; /**< How many of the base unit appear in this unit */ +}; + +/** Table to map the names of memory units to the number of bytes they + * contain. */ +static struct unit_table_t memory_units[] = { + { "", 1 }, + { "b", 1<< 0 }, + { "byte", 1<< 0 }, + { "bytes", 1<< 0 }, + { "kb", 1<<10 }, + { "kbyte", 1<<10 }, + { "kbytes", 1<<10 }, + { "kilobyte", 1<<10 }, + { "kilobytes", 1<<10 }, + { "kilobits", 1<<7 }, + { "kilobit", 1<<7 }, + { "kbits", 1<<7 }, + { "kbit", 1<<7 }, + { "m", 1<<20 }, + { "mb", 1<<20 }, + { "mbyte", 1<<20 }, + { "mbytes", 1<<20 }, + { "megabyte", 1<<20 }, + { "megabytes", 1<<20 }, + { "megabits", 1<<17 }, + { "megabit", 1<<17 }, + { "mbits", 1<<17 }, + { "mbit", 1<<17 }, + { "gb", 1<<30 }, + { "gbyte", 1<<30 }, + { "gbytes", 1<<30 }, + { "gigabyte", 1<<30 }, + { "gigabytes", 1<<30 }, + { "gigabits", 1<<27 }, + { "gigabit", 1<<27 }, + { "gbits", 1<<27 }, + { "gbit", 1<<27 }, + { "tb", UINT64_C(1)<<40 }, + { "tbyte", UINT64_C(1)<<40 }, + { "tbytes", UINT64_C(1)<<40 }, + { "terabyte", UINT64_C(1)<<40 }, + { "terabytes", UINT64_C(1)<<40 }, + { "terabits", UINT64_C(1)<<37 }, + { "terabit", UINT64_C(1)<<37 }, + { "tbits", UINT64_C(1)<<37 }, + { "tbit", UINT64_C(1)<<37 }, + { NULL, 0 }, +}; + +/** Table to map the names of time units to the number of seconds they + * contain. */ +static struct unit_table_t time_units[] = { + { "", 1 }, + { "second", 1 }, + { "seconds", 1 }, + { "minute", 60 }, + { "minutes", 60 }, + { "hour", 60*60 }, + { "hours", 60*60 }, + { "day", 24*60*60 }, + { "days", 24*60*60 }, + { "week", 7*24*60*60 }, + { "weeks", 7*24*60*60 }, + { "month", 2629728, }, /* about 30.437 days */ + { "months", 2629728, }, + { NULL, 0 }, +}; + +/** Table to map the names of time units to the number of milliseconds + * they contain. */ +static struct unit_table_t time_msec_units[] = { + { "", 1 }, + { "msec", 1 }, + { "millisecond", 1 }, + { "milliseconds", 1 }, + { "second", 1000 }, + { "seconds", 1000 }, + { "minute", 60*1000 }, + { "minutes", 60*1000 }, + { "hour", 60*60*1000 }, + { "hours", 60*60*1000 }, + { "day", 24*60*60*1000 }, + { "days", 24*60*60*1000 }, + { "week", 7*24*60*60*1000 }, + { "weeks", 7*24*60*60*1000 }, + { NULL, 0 }, +}; + +/** Parse a string <b>val</b> containing a number, zero or more + * spaces, and an optional unit string. If the unit appears in the + * table <b>u</b>, then multiply the number by the unit multiplier. + * On success, set *<b>ok</b> to 1 and return this product. + * Otherwise, set *<b>ok</b> to 0. + */ +static uint64_t +config_parse_units(const char *val, struct unit_table_t *u, int *ok) +{ + uint64_t v = 0; + double d = 0; + int use_float = 0; + char *cp; + + tor_assert(ok); + + v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp); + if (!*ok || (cp && *cp == '.')) { + d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp); + if (!*ok) + goto done; + use_float = 1; + } + + if (!cp) { + *ok = 1; + v = use_float ? ((uint64_t)d) : v; + goto done; + } + + cp = (char*) eat_whitespace(cp); + + for ( ;u->unit;++u) { + if (!strcasecmp(u->unit, cp)) { + if (use_float) + v = (uint64_t)(u->multiplier * d); + else + v *= u->multiplier; + *ok = 1; + goto done; + } + } + log_warn(LD_CONFIG, "Unknown unit '%s'.", cp); + *ok = 0; + done: + + if (*ok) + return v; + else + return 0; +} + +/** Parse a string in the format "number unit", where unit is a unit of + * information (byte, KB, M, etc). On success, set *<b>ok</b> to true + * and return the number of bytes specified. Otherwise, set + * *<b>ok</b> to false and return 0. */ +static uint64_t +config_parse_memunit(const char *s, int *ok) +{ + uint64_t u = config_parse_units(s, memory_units, ok); + return u; +} + +/** Parse a string in the format "number unit", where unit is a unit of + * time in milliseconds. On success, set *<b>ok</b> to true and return + * the number of milliseconds in the provided interval. Otherwise, set + * *<b>ok</b> to 0 and return -1. */ +static int +config_parse_msec_interval(const char *s, int *ok) +{ + uint64_t r; + r = config_parse_units(s, time_msec_units, ok); + if (r > INT_MAX) { + log_warn(LD_CONFIG, "Msec interval '%s' is too long", s); + *ok = 0; + return -1; + } + return (int)r; +} + +/** Parse a string in the format "number unit", where unit is a unit of time. + * On success, set *<b>ok</b> to true and return the number of seconds in + * the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1. + */ +static int +config_parse_interval(const char *s, int *ok) +{ + uint64_t r; + r = config_parse_units(s, time_units, ok); + if (r > INT_MAX) { + log_warn(LD_CONFIG, "Interval '%s' is too long", s); + *ok = 0; + return -1; + } + return (int)r; +} diff --git a/src/app/config/confparse.h b/src/app/config/confparse.h new file mode 100644 index 0000000000..57f1ec1762 --- /dev/null +++ b/src/app/config/confparse.h @@ -0,0 +1,233 @@ +/* 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 confparse.h + * + * \brief Header for confparse.c. + */ + +#ifndef TOR_CONFPARSE_H +#define TOR_CONFPARSE_H + +/** Enumeration of types which option values can take */ +typedef enum config_type_t { + CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ + CONFIG_TYPE_FILENAME, /**< A filename: some prefixes get expanded. */ + CONFIG_TYPE_UINT, /**< A non-negative integer less than MAX_INT */ + CONFIG_TYPE_INT, /**< Any integer. */ + CONFIG_TYPE_UINT64, /**< A value in range 0..UINT64_MAX */ + CONFIG_TYPE_PORT, /**< A port from 1...65535, 0 for "not set", or + * "auto". */ + CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/ + CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional + * units */ + CONFIG_TYPE_MEMUNIT, /**< A number of bytes, with optional units*/ + CONFIG_TYPE_DOUBLE, /**< A floating-point value */ + CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */ + CONFIG_TYPE_AUTOBOOL, /**< A boolean+auto value, expressed 0 for false, + * 1 for true, and -1 for auto */ + CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to UTC. */ + CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and + * optional whitespace. */ + CONFIG_TYPE_CSV_INTERVAL, /**< A list of strings, separated by commas and + * optional whitespace, representing intervals in + * seconds, with optional units. We allow + * multiple values here for legacy reasons, but + * ignore every value after the first. */ + CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */ + CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines, + * mixed with other keywords. */ + CONFIG_TYPE_LINELIST_V, /**< Catch-all "virtual" option to summarize + * context-sensitive config lines when fetching. + */ + CONFIG_TYPE_ROUTERSET, /**< A list of router names, addrs, and fps, + * parsed into a routerset_t. */ + CONFIG_TYPE_OBSOLETE, /**< Obsolete (ignored) option. */ +} config_type_t; + +#ifdef TOR_UNIT_TESTS +/** + * Union used when building in test mode typechecking the members of a type + * used with confparse.c. See CONF_CHECK_VAR_TYPE for a description of how + * it is used. */ +typedef union { + char **STRING; + char **FILENAME; + int *UINT; /* yes, really: Even though the confparse type is called + * "UINT", it still uses the C int type -- it just enforces that + * the values are in range [0,INT_MAX]. + */ + uint64_t *UINT64; + int *INT; + int *PORT; + int *INTERVAL; + int *MSEC_INTERVAL; + uint64_t *MEMUNIT; + double *DOUBLE; + int *BOOL; + int *AUTOBOOL; + time_t *ISOTIME; + smartlist_t **CSV; + int *CSV_INTERVAL; + struct config_line_t **LINELIST; + struct config_line_t **LINELIST_S; + struct config_line_t **LINELIST_V; + routerset_t **ROUTERSET; +} confparse_dummy_values_t; +#endif /* defined(TOR_UNIT_TESTS) */ + +/** An abbreviation for a configuration option allowed on the command line. */ +typedef struct config_abbrev_t { + const char *abbreviated; + const char *full; + int commandline_only; + int warn; +} config_abbrev_t; + +typedef struct config_deprecation_t { + const char *name; + const char *why_deprecated; +} config_deprecation_t; + +/* Handy macro for declaring "In the config file or on the command line, + * you can abbreviate <b>tok</b>s as <b>tok</b>". */ +#define PLURAL(tok) { #tok, #tok "s", 0, 0 } + +/** A variable allowed in the configuration file or on the command line. */ +typedef struct config_var_t { + const char *name; /**< The full keyword (case insensitive). */ + config_type_t type; /**< How to interpret the type and turn it into a + * value. */ + off_t var_offset; /**< Offset of the corresponding member of or_options_t. */ + const char *initvalue; /**< String (or null) describing initial value. */ + +#ifdef TOR_UNIT_TESTS + /** Used for compiler-magic to typecheck the corresponding field in the + * corresponding struct. Only used in unit test mode, at compile-time. */ + confparse_dummy_values_t var_ptr_dummy; +#endif +} config_var_t; + +/* Macros to define extra members inside config_var_t fields, and at the + * end of a list of them. + */ +#ifdef TOR_UNIT_TESTS +/* This is a somewhat magic type-checking macro for users of confparse.c. + * It initializes a union member "confparse_dummy_values_t.conftype" with + * the address of a static member "tp_dummy.member". This + * will give a compiler warning unless the member field is of the correct + * type. + * + * (This warning is mandatory, because a type mismatch here violates the type + * compatibility constraint for simple assignment, and requires a diagnostic, + * according to the C spec.) + * + * For example, suppose you say: + * "CONF_CHECK_VAR_TYPE(or_options_t, STRING, Address)". + * Then this macro will evaluate to: + * { .STRING = &or_options_t_dummy.Address } + * And since confparse_dummy_values_t.STRING has type "char **", that + * expression will create a warning unless or_options_t.Address also + * has type "char *". + */ +#define CONF_CHECK_VAR_TYPE(tp, conftype, member) \ + { . conftype = &tp ## _dummy . member } +#define CONF_TEST_MEMBERS(tp, conftype, member) \ + , CONF_CHECK_VAR_TYPE(tp, conftype, member) +#define END_OF_CONFIG_VARS \ + { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL, { .INT=NULL } } +#define DUMMY_TYPECHECK_INSTANCE(tp) \ + static tp tp ## _dummy +#else /* !(defined(TOR_UNIT_TESTS)) */ +#define CONF_TEST_MEMBERS(tp, conftype, member) +#define END_OF_CONFIG_VARS { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } +/* Repeatedly declarable incomplete struct to absorb redundant semicolons */ +#define DUMMY_TYPECHECK_INSTANCE(tp) \ + struct tor_semicolon_eater +#endif /* defined(TOR_UNIT_TESTS) */ + +/** Type of a callback to validate whether a given configuration is + * well-formed and consistent. See options_trial_assign() for documentation + * of arguments. */ +typedef int (*validate_fn_t)(void*,void*,void*,int,char**); + +/** Callback to free a configuration object. */ +typedef void (*free_cfg_fn_t)(void*); + +/** Information on the keys, value types, key-to-struct-member mappings, + * variable descriptions, validation functions, and abbreviations for a + * configuration or storage format. */ +typedef struct config_format_t { + size_t size; /**< Size of the struct that everything gets parsed into. */ + uint32_t magic; /**< Required 'magic value' to make sure we have a struct + * of the right type. */ + off_t magic_offset; /**< Offset of the magic value within the struct. */ + config_abbrev_t *abbrevs; /**< List of abbreviations that we expand when + * parsing this format. */ + const config_deprecation_t *deprecations; /** List of deprecated options */ + config_var_t *vars; /**< List of variables we recognize, their default + * values, and where we stick them in the structure. */ + validate_fn_t validate_fn; /**< Function to validate config. */ + free_cfg_fn_t free_fn; /**< Function to free the configuration. */ + /** If present, extra is a LINELIST variable for unrecognized + * lines. Otherwise, unrecognized lines are an error. */ + config_var_t *extra; +} config_format_t; + +/** Macro: assert that <b>cfg</b> has the right magic field for format + * <b>fmt</b>. */ +#define CONFIG_CHECK(fmt, cfg) STMT_BEGIN \ + tor_assert(fmt && cfg); \ + tor_assert((fmt)->magic == \ + *(uint32_t*)STRUCT_VAR_P(cfg,fmt->magic_offset)); \ + STMT_END + +#define CAL_USE_DEFAULTS (1u<<0) +#define CAL_CLEAR_FIRST (1u<<1) +#define CAL_WARN_DEPRECATIONS (1u<<2) + +void *config_new(const config_format_t *fmt); +void config_free_(const config_format_t *fmt, void *options); +#define config_free(fmt, options) do { \ + config_free_((fmt), (options)); \ + (options) = NULL; \ + } while (0) + +struct config_line_t *config_get_assigned_option(const config_format_t *fmt, + const void *options, const char *key, + int escape_val); +int config_is_same(const config_format_t *fmt, + const void *o1, const void *o2, + const char *name); +void config_init(const config_format_t *fmt, void *options); +void *config_dup(const config_format_t *fmt, const void *old); +char *config_dump(const config_format_t *fmt, const void *default_options, + const void *options, int minimal, + int comment_defaults); +int config_assign(const config_format_t *fmt, void *options, + struct config_line_t *list, + unsigned flags, char **msg); +config_var_t *config_find_option_mutable(config_format_t *fmt, + const char *key); +const char *config_find_deprecation(const config_format_t *fmt, + const char *key); +const config_var_t *config_find_option(const config_format_t *fmt, + const char *key); +const char *config_expand_abbrev(const config_format_t *fmt, + const char *option, + int command_line, int warn_obsolete); +void warn_deprecated_option(const char *what, const char *why); + +/* Helper macros to compare an option across two configuration objects */ +#define CFG_EQ_BOOL(a,b,opt) ((a)->opt == (b)->opt) +#define CFG_EQ_INT(a,b,opt) ((a)->opt == (b)->opt) +#define CFG_EQ_STRING(a,b,opt) (!strcmp_opt((a)->opt, (b)->opt)) +#define CFG_EQ_SMARTLIST(a,b,opt) smartlist_strings_eq((a)->opt, (b)->opt) +#define CFG_EQ_LINELIST(a,b,opt) config_lines_eq((a)->opt, (b)->opt) +#define CFG_EQ_ROUTERSET(a,b,opt) routerset_equal((a)->opt, (b)->opt) + +#endif /* !defined(TOR_CONFPARSE_H) */ diff --git a/src/app/config/fallback_dirs.inc b/src/app/config/fallback_dirs.inc new file mode 100644 index 0000000000..793f65ce88 --- /dev/null +++ b/src/app/config/fallback_dirs.inc @@ -0,0 +1,856 @@ +/* type=fallback */ +/* version=2.0.0 */ +/* timestamp=20190625114911 */ +/* timestamp0=20190625114911 */ +/* timestamp1=20190628085927 */ +/* source=whitelist */ +/* ===== */ +/* 0: Whitelist excluded 1550 of 1711 candidates. */ +/* 1: Whitelist excluded 1601 of 1765 candidates. */ +/* Checked IPv4 DirPorts served a consensus within 15.0s. */ +/* +0: +Final Count: 140 (Eligible 161, Target 414 (2072 * 0.20), Max 200) +Excluded: 21 (Same Operator 16, Failed/Skipped Download 3, Excess 2) +Bandwidth Range: 0.5 - 54.5 MByte/s + +MERGED WITH: + +1: +Final Count: 140 (Eligible 164, Target 414 (2073 * 0.20), Max 200) +Excluded: 24 (Same Operator 16, Failed/Skipped Download 4, Excess 4) +Bandwidth Range: 0.8 - 54.5 MByte/s +*/ +/* +): +Onionoo Source: details Date: 2019-06-25 10:00:00 Version: 7.0 +URL: https:onionoo.torproject.orgdetails?fieldsfingerprint%2Cnickname%2Ccontact%2Clast_changed_address_or_port%2Cconsensus_weight%2Cadvertised_bandwidth%2Cor_addresses%2Cdir_address%2Crecommended_version%2Cflags%2Ceffective_family%2Cplatform&flagV2Dir&typerelay&last_seen_days-0&first_seen_days90- + +MERGED WITH: + +1: +Onionoo Source: details Date: 2019-06-28 07:00:00 Version: 7.0 +URL: https:onionoo.torproject.orgdetails?fieldsfingerprint%2Cnickname%2Ccontact%2Clast_changed_address_or_port%2Cconsensus_weight%2Cadvertised_bandwidth%2Cor_addresses%2Cdir_address%2Crecommended_version%2Cflags%2Ceffective_family%2Cplatform&last_seen_days-0&flagV2Dir&first_seen_days90-&typerelay&order-consensus_weight%2Cfirst_seen +*/ +/* +0: +Onionoo Source: uptime Date: 2019-06-25 10:00:00 Version: 7.0 +URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&last_seen_days-0 + +MERGED WITH: + +1: +Onionoo Source: uptime Date: 2019-06-28 07:00:00 Version: 7.0 +URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&order-consensus_weight%2Cfirst_seen&last_seen_days-0 +*/ +/* ===== */ +"185.13.39.197:80 orport=443 id=001524DD403D729F08F7E5D77813EF12756CFA8D" +/* nickname=Neldoreth */ +/* extrainfo=0 */ +/* ===== */ +, +"185.100.85.61:80 orport=443 id=025B66CEBC070FCB0519D206CF0CF4965C20C96E" +/* nickname=nibbana */ +/* extrainfo=0 */ +/* ===== */ +, +"185.225.17.3:80 orport=443 id=0338F9F55111FE8E3570E7DE117EF3AF999CC1D7" +" ipv6=[2a0a:c800:1:5::3]:443" +/* nickname=Nebuchadnezzar */ +/* extrainfo=0 */ +/* ===== */ +, +"163.172.149.155:80 orport=443 id=0B85617241252517E8ECF2CFC7F4C1A32DCD153F" +/* nickname=niij02 */ +/* extrainfo=0 */ +/* ===== */ +, +"5.200.21.144:80 orport=443 id=0C039F35C2E40DCB71CD8A07E97C7FD7787D42D6" +/* nickname=libel */ +/* extrainfo=0 */ +/* ===== */ +, +"37.252.185.182:9030 orport=8080 id=113143469021882C3A4B82F084F8125B08EE471E" +" ipv6=[2a00:63c1:a:182::2]:8080" +/* nickname=parasol */ +/* extrainfo=0 */ +/* ===== */ +, +"37.120.174.249:80 orport=443 id=11DF0017A43AF1F08825CD5D973297F81AB00FF3" +" ipv6=[2a03:4000:6:724c:df98:15f9:b34d:443]:443" +/* nickname=gGDHjdcC6zAlM8k08lX */ +/* extrainfo=0 */ +/* ===== */ +, +"95.85.8.226:80 orport=443 id=1211AC1BBB8A1AF7CBA86BCE8689AA3146B86423" +/* nickname=ccrelaycc */ +/* extrainfo=0 */ +/* ===== */ +, +"193.11.114.43:9030 orport=9001 id=12AD30E5D25AA67F519780E2111E611A455FDC89" +" ipv6=[2001:6b0:30:1000::99]:9050" +/* nickname=mdfnet1 */ +/* extrainfo=0 */ +/* ===== */ +, +"37.157.195.87:8030 orport=443 id=12FD624EE73CEF37137C90D38B2406A66F68FAA2" +/* nickname=thanatosCZ */ +/* extrainfo=0 */ +/* ===== */ +, +"217.182.51.248:80 orport=443 id=183005F78229D94EE51CE7795A42280070A48D0D" +/* nickname=Cosworth02 */ +/* extrainfo=0 */ +/* ===== */ +, +"171.25.193.25:80 orport=443 id=185663B7C12777F052B2C2D23D7A239D8DA88A0F" +" ipv6=[2001:67c:289c::25]:443" +/* nickname=DFRI5 */ +/* extrainfo=0 */ +/* ===== */ +, +"149.56.141.138:9030 orport=9001 id=1938EBACBB1A7BFA888D9623C90061130E63BB3F" +/* nickname=Aerodynamik04 */ +/* extrainfo=0 */ +/* ===== */ +, +"81.7.14.253:9001 orport=443 id=1AE039EE0B11DB79E4B4B29CBA9F752864A0259E" +/* nickname=Ichotolot60 */ +/* extrainfo=1 */ +/* ===== */ +, +"50.7.74.171:9030 orport=9001 id=1CD17CB202063C51C7DAD3BACEF87ECE81C2350F" +" ipv6=[2001:49f0:d002:2::51]:443" +/* nickname=theia1 */ +/* extrainfo=0 */ +/* ===== */ +, +"199.184.246.250:80 orport=443 id=1F6ABD086F40B890A33C93CC4606EE68B31C9556" +" ipv6=[2620:124:1009:1::171]:443" +/* nickname=dao */ +/* extrainfo=0 */ +/* ===== */ +, +"212.47.229.2:9030 orport=9001 id=20462CBA5DA4C2D963567D17D0B7249718114A68" +" ipv6=[2001:bc8:4400:2100::f03]:9001" +/* nickname=scaletor */ +/* extrainfo=0 */ +/* ===== */ +, +"77.247.181.164:80 orport=443 id=204DFD2A2C6A0DC1FA0EACB495218E0B661704FD" +/* nickname=HaveHeart */ +/* extrainfo=0 */ +/* ===== */ +, +"163.172.176.167:80 orport=443 id=230A8B2A8BA861210D9B4BA97745AEC217A94207" +/* nickname=niij01 */ +/* extrainfo=0 */ +/* ===== */ +, +"97.74.237.196:9030 orport=9001 id=2F0F32AB1E5B943CA7D062C03F18960C86E70D94" +/* nickname=Minotaur */ +/* extrainfo=0 */ +/* ===== */ +, +"212.83.154.33:8080 orport=8443 id=322C6E3A973BC10FC36DE3037AD27BC89F14723B" +/* nickname=bauruine204 */ +/* extrainfo=0 */ +/* ===== */ +, +"109.105.109.162:52860 orport=60784 id=32EE911D968BE3E016ECA572BB1ED0A9EE43FC2F" +" ipv6=[2001:948:7:2::163]:5001" +/* nickname=ndnr1 */ +/* extrainfo=0 */ +/* ===== */ +, +"185.100.84.212:80 orport=443 id=330CD3DB6AD266DC70CDB512B036957D03D9BC59" +" ipv6=[2a06:1700:0:7::1]:443" +/* nickname=TeamTardis */ +/* extrainfo=0 */ +/* ===== */ +, +"37.157.255.35:9030 orport=9090 id=361D33C96D0F161275EE67E2C91EE10B276E778B" +/* nickname=cxx4freedom */ +/* extrainfo=0 */ +/* ===== */ +, +"64.79.152.132:80 orport=443 id=375DCBB2DBD94E5263BC0C015F0C9E756669617E" +/* nickname=ebola */ +/* extrainfo=0 */ +/* ===== */ +, +"213.183.60.21:9030 orport=443 id=39F91959416763AFD34DBEEC05474411B964B2DC" +/* nickname=angeltest11 */ +/* extrainfo=0 */ +/* ===== */ +, +"50.7.74.174:9030 orport=9001 id=3AFDAAD91A15B4C6A7686A53AA8627CA871FF491" +" ipv6=[2001:49f0:d002:2::57]:443" +/* nickname=theia7 */ +/* extrainfo=0 */ +/* ===== */ +, +"199.249.230.83:80 orport=443 id=3CA0D15567024D2E0B557DC0CF3E962B37999A79" +" ipv6=[2620:7:6001::ffff:c759:e653]:80" +/* nickname=QuintexAirVPN30 */ +/* extrainfo=0 */ +/* ===== */ +, +"51.38.65.160:9030 orport=9001 id=3CB4193EF4E239FCEDC4DC43468E0B0D6B67ACC3" +" ipv6=[2001:41d0:801:2000::f6e]:9001" +/* nickname=rofltor10 */ +/* extrainfo=0 */ +/* ===== */ +, +"217.79.179.177:9030 orport=9001 id=3E53D3979DB07EFD736661C934A1DED14127B684" +" ipv6=[2001:4ba0:fff9:131:6c4f::90d3]:9001" +/* nickname=Unnamed */ +/* extrainfo=0 */ +/* ===== */ +, +"66.111.2.16:9030 orport=9001 id=3F092986E9B87D3FDA09B71FA3A602378285C77A" +" ipv6=[2610:1c0:0:5::16]:9001" +/* nickname=NYCBUG1 */ +/* extrainfo=0 */ +/* ===== */ +, +"185.100.85.101:9030 orport=9001 id=4061C553CA88021B8302F0814365070AAE617270" +/* nickname=TorExitRomania */ +/* extrainfo=0 */ +/* ===== */ +, +"163.172.157.213:8080 orport=443 id=4623A9EC53BFD83155929E56D6F7B55B5E718C24" +/* nickname=Cotopaxi */ +/* extrainfo=0 */ +/* ===== */ +, +"195.123.245.141:9030 orport=443 id=465D17C6FC297E3857B5C6F152006A1E212944EA" +/* nickname=angeltest14 */ +/* extrainfo=0 */ +/* ===== */ +, +"31.31.78.49:80 orport=443 id=46791D156C9B6C255C2665D4D8393EC7DBAA7798" +/* nickname=KrigHaBandolo */ +/* extrainfo=0 */ +/* ===== */ +, +"193.70.43.76:9030 orport=9001 id=484A10BA2B8D48A5F0216674C8DD50EF27BC32F3" +/* nickname=Aerodynamik03 */ +/* extrainfo=0 */ +/* ===== */ +, +"37.187.102.186:9030 orport=9001 id=489D94333DF66D57FFE34D9D59CC2D97E2CB0053" +" ipv6=[2001:41d0:a:26ba::1]:9001" +/* nickname=txtfileTorNode65536 */ +/* extrainfo=0 */ +/* ===== */ +, +"81.7.13.84:80 orport=443 id=4EB55679FA91363B97372554F8DC7C63F4E5B101" +" ipv6=[2a02:180:1:1::5b8f:538c]:443" +/* nickname=torpidsDEisppro */ +/* extrainfo=0 */ +/* ===== */ +, +"108.53.208.157:80 orport=443 id=4F0DB7E687FC7C0AE55C8F243DA8B0EB27FBF1F2" +/* nickname=Binnacle */ +/* extrainfo=1 */ +/* ===== */ +, +"5.9.158.75:9030 orport=9001 id=509EAB4C5D10C9A9A24B4EA0CE402C047A2D64E6" +" ipv6=[2a01:4f8:190:514a::2]:9001" +/* nickname=zwiebeltoralf2 */ +/* extrainfo=1 */ +/* ===== */ +, +"81.7.16.182:80 orport=443 id=51E1CF613FD6F9F11FE24743C91D6F9981807D82" +" ipv6=[2a02:180:1:1::517:10b6]:993" +/* nickname=torpidsDEisppro3 */ +/* extrainfo=0 */ +/* ===== */ +, +"192.160.102.166:80 orport=9001 id=547DA56F6B88B6C596B3E3086803CDA4F0EF8F21" +" ipv6=[2620:132:300c:c01d::6]:9002" +/* nickname=chaucer */ +/* extrainfo=0 */ +/* ===== */ +, +"192.160.102.170:80 orport=9001 id=557ACEC850F54EEE65839F83CACE2B0825BE811E" +" ipv6=[2620:132:300c:c01d::a]:9002" +/* nickname=ogopogo */ +/* extrainfo=0 */ +/* ===== */ +, +"50.7.74.170:80 orport=443 id=5BF17163CBE73D8CD9FDBE030C944EA05707DA93" +" ipv6=[2001:49f0:d002:2::58]:443" +/* nickname=theia8 */ +/* extrainfo=0 */ +/* ===== */ +, +"172.98.193.43:80 orport=443 id=5E56738E7F97AA81DEEF59AF28494293DFBFCCDF" +/* nickname=Backplane */ +/* extrainfo=0 */ +/* ===== */ +, +"95.128.43.164:80 orport=443 id=616081EC829593AF4232550DE6FFAA1D75B37A90" +" ipv6=[2a02:ec0:209:10::4]:443" +/* nickname=AquaRayTerminus */ +/* extrainfo=0 */ +/* ===== */ +, +"163.172.139.104:8080 orport=443 id=68F175CCABE727AA2D2309BCD8789499CEE36ED7" +/* nickname=Pichincha */ +/* extrainfo=0 */ +/* ===== */ +, +"94.130.186.5:80 orport=443 id=6A7551EEE18F78A9813096E82BF84F740D32B911" +" ipv6=[2a01:4f8:1c0c:45f7::1]:443" +/* nickname=TorMachine */ +/* extrainfo=0 */ +/* ===== */ +, +"80.127.137.19:80 orport=443 id=6EF897645B79B6CB35E853B32506375014DE3621" +" ipv6=[2001:981:47c1:1::6]:443" +/* nickname=d6relay */ +/* extrainfo=0 */ +/* ===== */ +, +"37.139.8.104:9030 orport=9001 id=7088D485934E8A403B81531F8C90BDC75FA43C98" +" ipv6=[2a03:b0c0:0:1010::24c:1001]:9001" +/* nickname=Basil */ +/* extrainfo=0 */ +/* ===== */ +, +"188.138.88.42:80 orport=443 id=70C55A114C0EF3DC5784A4FAEE64388434A3398F" +/* nickname=torpidsFRplusserver */ +/* extrainfo=0 */ +/* ===== */ +, +"85.235.250.88:80 orport=443 id=72B2B12A3F60408BDBC98C6DF53988D3A0B3F0EE" +" ipv6=[2a01:3a0:1:1900:85:235:250:88]:443" +/* nickname=TykRelay01 */ +/* extrainfo=0 */ +/* ===== */ +, +"178.17.170.23:9030 orport=9001 id=742C45F2D9004AADE0077E528A4418A6A81BC2BA" +" ipv6=[2a00:1dc0:caff:7d::8254]:9001" +/* nickname=TorExitMoldova2 */ +/* extrainfo=0 */ +/* ===== */ +, +"50.7.74.173:9030 orport=9001 id=745369332749021C6FAF100D327BC3BF1DF4707B" +" ipv6=[2001:49f0:d002:2::55]:443" +/* nickname=theia5 */ +/* extrainfo=0 */ +/* ===== */ +, +"77.247.181.166:80 orport=443 id=77131D7E2EC1CA9B8D737502256DA9103599CE51" +/* nickname=CriticalMass */ +/* extrainfo=0 */ +/* ===== */ +, +"5.196.23.64:9030 orport=9001 id=775B0FAFDE71AADC23FFC8782B7BEB1D5A92733E" +/* nickname=Aerodynamik01 */ +/* extrainfo=0 */ +/* ===== */ +, +"185.244.193.141:9030 orport=9001 id=79509683AB4C8DDAF90A120C69A4179C6CD5A387" +" ipv6=[2a03:4000:27:192:24:12:1984:4]:9001" +/* nickname=DerDickeReloaded */ +/* extrainfo=0 */ +/* ===== */ +, +"51.254.136.195:80 orport=443 id=7BB70F8585DFC27E75D692970C0EEB0F22983A63" +/* nickname=torproxy02 */ +/* extrainfo=0 */ +/* ===== */ +, +"77.247.181.162:80 orport=443 id=7BFB908A3AA5B491DA4CA72CCBEE0E1F2A939B55" +/* nickname=sofia */ +/* extrainfo=0 */ +/* ===== */ +, +"185.220.101.48:10048 orport=20048 id=7E281CD2C315C4F7A84BC7C8721C3BC974DDBFA3" +/* nickname=niftyporcupine */ +/* extrainfo=0 */ +/* ===== */ +, +"193.11.114.45:9031 orport=9002 id=80AAF8D5956A43C197104CEF2550CD42D165C6FB" +/* nickname=mdfnet2 */ +/* extrainfo=0 */ +/* ===== */ +, +"51.254.96.208:9030 orport=9001 id=8101421BEFCCF4C271D5483C5AABCAAD245BBB9D" +" ipv6=[2001:41d0:401:3100::30dc]:9001" +/* nickname=rofltor01 */ +/* extrainfo=0 */ +/* ===== */ +, +"192.42.116.16:80 orport=443 id=81B75D534F91BFB7C57AB67DA10BCEF622582AE8" +/* nickname=hviv104 */ +/* extrainfo=0 */ +/* ===== */ +, +"192.160.102.164:80 orport=9001 id=823AA81E277F366505545522CEDC2F529CE4DC3F" +" ipv6=[2620:132:300c:c01d::4]:9002" +/* nickname=snowfall */ +/* extrainfo=0 */ +/* ===== */ +, +"192.87.28.82:9030 orport=9001 id=844AE9CAD04325E955E2BE1521563B79FE7094B7" +" ipv6=[2001:678:230:3028:192:87:28:82]:9001" +/* nickname=Smeerboel */ +/* extrainfo=0 */ +/* ===== */ +, +"62.210.254.132:80 orport=443 id=8456DFA94161CDD99E480C2A2992C366C6564410" +/* nickname=turingmachine */ +/* extrainfo=0 */ +/* ===== */ +, +"85.230.178.139:9030 orport=443 id=855BC2DABE24C861CD887DB9B2E950424B49FC34" +/* nickname=Logforme */ +/* extrainfo=0 */ +/* ===== */ +, +"178.254.7.88:8080 orport=8443 id=85A885433E50B1874F11CEC9BE98451E24660976" +/* nickname=wr3ck3d0ni0n01 */ +/* extrainfo=0 */ +/* ===== */ +, +"185.96.88.29:80 orport=443 id=86C281AD135058238D7A337D546C902BE8505DDE" +" ipv6=[2a00:4020::185:96:88:29]:443" +/* nickname=TykRelay05 */ +/* extrainfo=0 */ +/* ===== */ +, +"163.172.194.53:9030 orport=9001 id=8C00FA7369A7A308F6A137600F0FA07990D9D451" +" ipv6=[2001:bc8:225f:142:6c69:7461:7669:73]:9001" +/* nickname=GrmmlLitavis */ +/* extrainfo=0 */ +/* ===== */ +, +"5.189.169.190:8030 orport=8080 id=8D79F73DCD91FC4F5017422FAC70074D6DB8DD81" +/* nickname=thanatosDE */ +/* extrainfo=0 */ +/* ===== */ +, +"81.7.11.96:9030 orport=9001 id=8FA37B93397015B2BC5A525C908485260BE9F422" +/* nickname=Doedel22 */ +/* extrainfo=0 */ +/* ===== */ +, +"54.37.139.118:9030 orport=9001 id=90A5D1355C4B5840E950EB61E673863A6AE3ACA1" +" ipv6=[2001:41d0:601:1100::1b8]:9001" +/* nickname=rofltor09 */ +/* extrainfo=0 */ +/* ===== */ +, +"37.187.20.59:80 orport=443 id=91D23D8A539B83D2FB56AA67ECD4D75CC093AC55" +" ipv6=[2001:41d0:a:143b::1]:993" +/* nickname=torpidsFRovh */ +/* extrainfo=0 */ +/* ===== */ +, +"173.255.245.116:9030 orport=9001 id=91E4015E1F82DAF0121D62267E54A1F661AB6DC7" +/* nickname=IWorshipHisShadow */ +/* extrainfo=0 */ +/* ===== */ +, +"96.253.78.108:80 orport=443 id=924B24AFA7F075D059E8EEB284CC400B33D3D036" +/* nickname=NSDFreedom */ +/* extrainfo=0 */ +/* ===== */ +, +"92.38.163.21:9030 orport=443 id=9288B75B5FF8861EFF32A6BE8825CC38A4F9F8C2" +/* nickname=angeltest9 */ +/* extrainfo=0 */ +/* ===== */ +, +"163.172.53.84:80 orport=443 id=935F589545B8A271A722E330445BB99F67DBB058" +" ipv6=[2001:bc8:24f8::]:443" +/* nickname=Multivac0 */ +/* extrainfo=0 */ +/* ===== */ +, +"204.8.156.142:80 orport=443 id=94C4B7B8C50C86A92B6A20107539EE2678CF9A28" +/* nickname=BostonUCompSci */ +/* extrainfo=0 */ +/* ===== */ +, +"37.153.1.10:9030 orport=9001 id=9772EFB535397C942C3AB8804FB35CFFAD012438" +/* nickname=smallsweatnode */ +/* extrainfo=0 */ +/* ===== */ +, +"173.212.254.192:31336 orport=31337 id=99E246DB480B313A3012BC3363093CC26CD209C7" +/* nickname=ViDiSrv */ +/* extrainfo=0 */ +/* ===== */ +, +"185.100.86.128:9030 orport=9001 id=9B31F1F1C1554F9FFB3455911F82E818EF7C7883" +" ipv6=[2a06:1700:1::11]:9001" +/* nickname=TorExitFinland */ +/* extrainfo=0 */ +/* ===== */ +, +"185.220.101.49:10049 orport=20049 id=9B816A5B3EB20B8E4E9B9D1FBA299BD3F40F0320" +/* nickname=niftypygmyjerboa */ +/* extrainfo=0 */ +/* ===== */ +, +"86.105.212.130:9030 orport=443 id=9C900A7F6F5DD034CFFD192DAEC9CCAA813DB022" +/* nickname=firstor2 */ +/* extrainfo=0 */ +/* ===== */ +, +"31.185.104.19:80 orport=443 id=9EAD5B2D3DBD96DBC80DCE423B0C345E920A758D" +/* nickname=Digitalcourage3ip1 */ +/* extrainfo=0 */ +/* ===== */ +, +"46.28.110.244:80 orport=443 id=9F7D6E6420183C2B76D3CE99624EBC98A21A967E" +/* nickname=Nivrim */ +/* extrainfo=0 */ +/* ===== */ +, +"46.165.230.5:80 orport=443 id=A0F06C2FADF88D3A39AA3072B406F09D7095AC9E" +/* nickname=Dhalgren */ +/* extrainfo=1 */ +/* ===== */ +, +"81.7.3.67:993 orport=443 id=A2E6BB5C391CD46B38C55B4329C35304540771F1" +/* nickname=BeastieJoy62 */ +/* extrainfo=1 */ +/* ===== */ +, +"128.31.0.13:80 orport=443 id=A53C46F5B157DD83366D45A8E99A244934A14C46" +/* nickname=csailmitexit */ +/* extrainfo=0 */ +/* ===== */ +, +"185.246.152.22:9030 orport=443 id=A86EC24F5B8B964F67AC7C27CE92842025983274" +/* nickname=angeltest19 */ +/* extrainfo=0 */ +/* ===== */ +, +"163.172.149.122:80 orport=443 id=A9406A006D6E7B5DA30F2C6D4E42A338B5E340B2" +/* nickname=niij03 */ +/* extrainfo=0 */ +/* ===== */ +, +"176.10.107.180:9030 orport=9001 id=AC2BEDD0BAC72838EA7E6F113F856C4E8018ACDB" +/* nickname=schokomilch */ +/* extrainfo=0 */ +/* ===== */ +, +"185.129.62.62:9030 orport=9001 id=ACDD9E85A05B127BA010466C13C8C47212E8A38F" +" ipv6=[2a06:d380:0:3700::62]:9001" +/* nickname=kramse */ +/* extrainfo=0 */ +/* ===== */ +, +"31.185.104.20:80 orport=443 id=ADB2C26629643DBB9F8FE0096E7D16F9414B4F8D" +/* nickname=Digitalcourage3ip2 */ +/* extrainfo=0 */ +/* ===== */ +, +"45.79.108.130:9030 orport=9001 id=AEDAC7081AE14B8D241ECF0FF17A2858AB4383D0" +" ipv6=[2600:3c01:e000:131::8000:0]:9001" +/* nickname=linss */ +/* extrainfo=0 */ +/* ===== */ +, +"5.9.147.226:9030 orport=9001 id=B0553175AADB0501E5A61FC61CEA3970BE130FF2" +" ipv6=[2a01:4f8:190:30e1::2]:9001" +/* nickname=zwiubel */ +/* extrainfo=0 */ +/* ===== */ +, +"178.17.174.14:9030 orport=9001 id=B06F093A3D4DFAD3E923F4F28A74901BD4F74EB1" +" ipv6=[2a00:1dc0:caff:8b::5b9a]:9001" +/* nickname=TorExitMoldova */ +/* extrainfo=0 */ +/* ===== */ +, +"212.129.62.232:80 orport=443 id=B143D439B72D239A419F8DCE07B8A8EB1B486FA7" +/* nickname=wardsback */ +/* extrainfo=0 */ +/* ===== */ +, +"199.249.230.64:80 orport=443 id=B2197C23A4FF5D1C49EE45BA7688BA8BCCD89A0B" +" ipv6=[2620:7:6001::ffff:c759:e640]:80" +/* nickname=Quintex41 */ +/* extrainfo=0 */ +/* ===== */ +, +"136.243.214.137:80 orport=443 id=B291D30517D23299AD7CEE3E60DFE60D0E3A4664" +/* nickname=TorKIT */ +/* extrainfo=0 */ +/* ===== */ +, +"212.47.233.86:9030 orport=9001 id=B4CAFD9CBFB34EC5DAAC146920DC7DFAFE91EA20" +/* nickname=netimanmu */ +/* extrainfo=0 */ +/* ===== */ +, +"93.115.97.242:9030 orport=9001 id=B5212DB685A2A0FCFBAE425738E478D12361710D" +/* nickname=firstor */ +/* extrainfo=0 */ +/* ===== */ +, +"51.38.134.104:9030 orport=443 id=B57A87009FA838471FB2227DDE68165AB2A2FCC4" +/* nickname=angeltest5 */ +/* extrainfo=0 */ +/* ===== */ +, +"193.11.114.46:9032 orport=9003 id=B83DC1558F0D34353BB992EF93AFEAFDB226A73E" +/* nickname=mdfnet3 */ +/* extrainfo=0 */ +/* ===== */ +, +"85.248.227.164:444 orport=9002 id=B84F248233FEA90CAD439F292556A3139F6E1B82" +" ipv6=[2a00:1298:8011:212::164]:9004" +/* nickname=tollana */ +/* extrainfo=0 */ +/* ===== */ +, +"81.7.11.186:1080 orport=443 id=B86137AE9681701901C6720E55C16805B46BD8E3" +/* nickname=BeastieJoy60 */ +/* extrainfo=1 */ +/* ===== */ +, +"51.15.179.153:110 orport=995 id=BB60F5BA113A0B8B44B7B37DE3567FE561E92F78" +/* nickname=Casper04 */ +/* extrainfo=0 */ +/* ===== */ +, +"198.96.155.3:8080 orport=5001 id=BCEDF6C193AA687AE471B8A22EBF6BC57C2D285E" +/* nickname=gurgle */ +/* extrainfo=0 */ +/* ===== */ +, +"128.199.55.207:9030 orport=9001 id=BCEF908195805E03E92CCFE669C48738E556B9C5" +" ipv6=[2a03:b0c0:2:d0::158:3001]:9001" +/* nickname=EldritchReaper */ +/* extrainfo=0 */ +/* ===== */ +, +"213.141.138.174:9030 orport=9001 id=BD552C165E2ED2887D3F1CCE9CFF155DDA2D86E6" +/* nickname=Schakalium */ +/* extrainfo=0 */ +/* ===== */ +, +"148.251.190.229:9030 orport=9010 id=BF0FB582E37F738CD33C3651125F2772705BB8E8" +" ipv6=[2a01:4f8:211:c68::2]:9010" +/* nickname=quadhead */ +/* extrainfo=0 */ +/* ===== */ +, +"212.47.233.250:9030 orport=9001 id=BF735F669481EE1CCC348F0731551C933D1E2278" +" ipv6=[2001:bc8:4400:2b00::1c:629]:9001" +/* nickname=FreewaySca */ +/* extrainfo=0 */ +/* ===== */ +, +"192.160.102.169:80 orport=9001 id=C0192FF43E777250084175F4E59AC1BA2290CE38" +" ipv6=[2620:132:300c:c01d::9]:9002" +/* nickname=manipogo */ +/* extrainfo=0 */ +/* ===== */ +, +"132.248.241.5:9130 orport=9101 id=C0C4F339046EB824999F711D178472FDF53BE7F5" +/* nickname=toritounam2 */ +/* extrainfo=0 */ +/* ===== */ +, +"31.185.104.21:80 orport=443 id=C2AAB088555850FC434E68943F551072042B85F1" +/* nickname=Digitalcourage3ip3 */ +/* extrainfo=0 */ +/* ===== */ +, +"50.7.74.170:9030 orport=9001 id=C36A434DB54C66E1A97A5653858CE36024352C4D" +" ipv6=[2001:49f0:d002:2::59]:443" +/* nickname=theia9 */ +/* extrainfo=0 */ +/* ===== */ +, +"85.248.227.163:443 orport=9001 id=C793AB88565DDD3C9E4C6F15CCB9D8C7EF964CE9" +" ipv6=[2a00:1298:8011:212::163]:9003" +/* nickname=ori */ +/* extrainfo=0 */ +/* ===== */ +, +"192.160.102.165:80 orport=9001 id=C90CA3B7FE01A146B8268D56977DC4A2C024B9EA" +" ipv6=[2620:132:300c:c01d::5]:9002" +/* nickname=cowcat */ +/* extrainfo=0 */ +/* ===== */ +, +"176.31.103.150:9030 orport=9001 id=CBD0D1BD110EC52963082D839AC6A89D0AE243E7" +/* nickname=UV74S7mjxRcYVrGsAMw */ +/* extrainfo=0 */ +/* ===== */ +, +"51.254.147.57:80 orport=443 id=D15AFF44BE641368B958A32FB6B071AC2136B8B1" +/* nickname=Cosworth01 */ +/* extrainfo=0 */ +/* ===== */ +, +"50.7.74.172:80 orport=443 id=D1AFBF3117B308B6D1A7AA762B1315FD86A6B8AF" +" ipv6=[2001:49f0:d002:2::52]:443" +/* nickname=theia2 */ +/* extrainfo=0 */ +/* ===== */ +, +"62.141.38.69:9030 orport=443 id=D379A1CB8285748FFF64AE94296CA89878F25B22" +" ipv6=[2001:4ba0:cafe:ac5::1]:443" +/* nickname=angeltest3 */ +/* extrainfo=0 */ +/* ===== */ +, +"5.45.111.149:80 orport=443 id=D405FCCF06ADEDF898DF2F29C9348DCB623031BA" +" ipv6=[2a03:4000:6:2388:df98:15f9:b34d:443]:443" +/* nickname=gGDHjdcC6zAlM8k08lY */ +/* extrainfo=0 */ +/* ===== */ +, +"50.7.74.174:80 orport=443 id=D50101A2ABD09DC245F7E96C0818D003CDD62351" +" ipv6=[2001:49f0:d002:2::56]:443" +/* nickname=theia6 */ +/* extrainfo=0 */ +/* ===== */ +, +"37.187.115.157:9030 orport=9001 id=D5039E1EBFD96D9A3F9846BF99EC9F75EDDE902A" +/* nickname=Janky328891 */ +/* extrainfo=0 */ +/* ===== */ +, +"85.10.201.47:9030 orport=9001 id=D8B7A3A6542AA54D0946B9DC0257C53B6C376679" +" ipv6=[2a01:4f8:a0:43eb::beef]:9001" +/* nickname=sif */ +/* extrainfo=0 */ +/* ===== */ +, +"193.35.52.53:9030 orport=9001 id=DAA39FC00B196B353C2A271459C305C429AF09E4" +/* nickname=Arne */ +/* extrainfo=0 */ +/* ===== */ +, +"54.36.237.163:80 orport=443 id=DB2682153AC0CCAECD2BD1E9EBE99C6815807A1E" +/* nickname=GermanCraft2 */ +/* extrainfo=0 */ +/* ===== */ +, +"176.158.236.102:9030 orport=9001 id=DC163DDEF4B6F0C6BC226F9F6656A5A30C5C5686" +/* nickname=Underworld */ +/* extrainfo=0 */ +/* ===== */ +, +"178.33.183.251:80 orport=443 id=DD823AFB415380A802DCAEB9461AE637604107FB" +" ipv6=[2001:41d0:2:a683::251]:443" +/* nickname=grenouille */ +/* extrainfo=0 */ +/* ===== */ +, +"171.25.193.20:80 orport=443 id=DD8BD7307017407FCC36F8D04A688F74A0774C02" +" ipv6=[2001:67c:289c::20]:443" +/* nickname=DFRI0 */ +/* extrainfo=0 */ +/* ===== */ +, +"92.222.38.67:80 orport=443 id=DED6892FF89DBD737BA689698A171B2392EB3E82" +" ipv6=[2001:41d0:52:100::112a]:443" +/* nickname=ThorExit */ +/* extrainfo=0 */ +/* ===== */ +, +"166.70.207.2:9130 orport=9101 id=E41B16F7DDF52EBB1DB4268AB2FE340B37AD8904" +/* nickname=xmission1 */ +/* extrainfo=0 */ +/* ===== */ +, +"185.100.86.182:9030 orport=8080 id=E51620B90DCB310138ED89EDEDD0A5C361AAE24E" +/* nickname=NormalCitizen */ +/* extrainfo=0 */ +/* ===== */ +, +"212.47.244.38:8080 orport=443 id=E81EF60A73B3809F8964F73766B01BAA0A171E20" +/* nickname=Chimborazo */ +/* extrainfo=0 */ +/* ===== */ +, +"185.4.132.148:80 orport=443 id=E8D114B3C78D8E6E7FEB1004650DD632C2143C9E" +" ipv6=[2a02:c500:2:f0::5492]:443" +/* nickname=libreonion1 */ +/* extrainfo=0 */ +/* ===== */ +, +"131.188.40.188:1443 orport=80 id=EBE718E1A49EE229071702964F8DB1F318075FF8" +" ipv6=[2001:638:a000:4140::ffff:188]:80" +/* nickname=fluxe4 */ +/* extrainfo=1 */ +/* ===== */ +, +"192.87.28.28:9030 orport=9001 id=ED2338CAC2711B3E331392E1ED2831219B794024" +" ipv6=[2001:678:230:3028:192:87:28:28]:9001" +/* nickname=SEC6xFreeBSD64 */ +/* extrainfo=0 */ +/* ===== */ +, +"37.252.187.111:9030 orport=443 id=EE4AF632058F0734C1426B1AD689F47445CA2056" +" ipv6=[2a00:63c1:c:111::2]:443" +/* nickname=angeltest7 */ +/* extrainfo=0 */ +/* ===== */ +, +"217.182.75.181:9030 orport=9001 id=EFEACD781604EB80FBC025EDEDEA2D523AEAAA2F" +/* nickname=Aerodynamik02 */ +/* extrainfo=0 */ +/* ===== */ +, +"193.70.112.165:80 orport=443 id=F10BDE279AE71515DDCCCC61DC19AC8765F8A3CC" +/* nickname=ParkBenchInd001 */ +/* extrainfo=0 */ +/* ===== */ +, +"37.187.102.108:80 orport=443 id=F4263275CF54A6836EE7BD527B1328836A6F06E1" +" ipv6=[2001:41d0:a:266c::1]:443" +/* nickname=EvilMoe */ +/* extrainfo=0 */ +/* ===== */ +, +"5.199.142.236:9030 orport=9001 id=F4C0EDAA0BF0F7EC138746F8FEF1CE26C7860265" +/* nickname=tornodenumber9004 */ +/* extrainfo=0 */ +/* ===== */ +, +"192.160.102.168:80 orport=9001 id=F6A358DD367B3282D6EF5824C9D45E1A19C7E815" +" ipv6=[2620:132:300c:c01d::8]:9002" +/* nickname=prawksi */ +/* extrainfo=0 */ +/* ===== */ +, +"78.47.18.110:443 orport=80 id=F8D27B163B9247B232A2EEE68DD8B698695C28DE" +" ipv6=[2a01:4f8:120:4023::110]:80" +/* nickname=fluxe3 */ +/* extrainfo=1 */ +/* ===== */ +, +"185.96.180.29:80 orport=443 id=F93D8F37E35C390BCAD9F9069E13085B745EC216" +" ipv6=[2a00:4820::185:96:180:29]:443" +/* nickname=TykRelay06 */ +/* extrainfo=0 */ +/* ===== */ +, +"149.56.45.200:9030 orport=9001 id=FE296180018833AF03A8EACD5894A614623D3F76" +" ipv6=[2607:5300:201:3000::17d3]:9002" +/* nickname=PyotrTorpotkinOne */ +/* extrainfo=0 */ +/* ===== */ +, +"193.11.164.243:9030 orport=9001 id=FFA72BD683BC2FCF988356E6BEC1E490F313FB07" +" ipv6=[2001:6b0:7:125::243]:9001" +/* nickname=Lule */ +/* extrainfo=0 */ +/* ===== */ +, diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h new file mode 100644 index 0000000000..74d2fefa16 --- /dev/null +++ b/src/app/config/or_options_st.h @@ -0,0 +1,1077 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file or_options_st.h + * + * \brief The or_options_t structure, which represents Tor's configuration. + */ + +#ifndef TOR_OR_OPTIONS_ST_H +#define TOR_OR_OPTIONS_ST_H + +#include "lib/cc/torint.h" +#include "lib/net/address.h" + +struct smartlist_t; +struct config_line_t; + +/** Enumeration of outbound address configuration types: + * Exit-only, OR-only, or both */ +typedef enum {OUTBOUND_ADDR_EXIT, OUTBOUND_ADDR_OR, + OUTBOUND_ADDR_EXIT_AND_OR, + OUTBOUND_ADDR_MAX} outbound_addr_t; + +/** Configuration options for a Tor process. */ +struct or_options_t { + uint32_t magic_; + + /** What should the tor process actually do? */ + enum { + CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT, CMD_HASH_PASSWORD, + CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS, CMD_DUMP_CONFIG, + CMD_KEYGEN, + CMD_KEY_EXPIRATION, + } command; + char *command_arg; /**< Argument for command-line option. */ + + struct config_line_t *Logs; /**< New-style list of configuration lines + * for logs */ + int LogTimeGranularity; /**< Log resolution in milliseconds. */ + + int LogMessageDomains; /**< Boolean: Should we log the domain(s) in which + * each log message occurs? */ + int TruncateLogFile; /**< Boolean: Should we truncate the log file + before we start writing? */ + char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */ + char *AndroidIdentityTag; /**< Identity tag to add for Android logging. */ + + char *DebugLogFile; /**< Where to send verbose log messages. */ + char *DataDirectory_option; /**< Where to store long-term data, as + * configured by the user. */ + char *DataDirectory; /**< Where to store long-term data, as modified. */ + int DataDirectoryGroupReadable; /**< Boolean: Is the DataDirectory g+r? */ + + char *KeyDirectory_option; /**< Where to store keys, as + * configured by the user. */ + char *KeyDirectory; /**< Where to store keys data, as modified. */ + int KeyDirectoryGroupReadable; /**< Boolean: Is the KeyDirectory g+r? */ + + char *CacheDirectory_option; /**< Where to store cached data, as + * configured by the user. */ + char *CacheDirectory; /**< Where to store cached data, as modified. */ + int CacheDirectoryGroupReadable; /**< Boolean: Is the CacheDirectory g+r? */ + + char *Nickname; /**< OR only: nickname of this onion router. */ + char *Address; /**< OR only: configured address for this onion router. */ + char *PidFile; /**< Where to store PID of Tor process. */ + + routerset_t *ExitNodes; /**< Structure containing nicknames, digests, + * country codes and IP address patterns of ORs to + * consider as exits. */ + routerset_t *EntryNodes;/**< Structure containing nicknames, digests, + * country codes and IP address patterns of ORs to + * consider as entry points. */ + int StrictNodes; /**< Boolean: When none of our EntryNodes or ExitNodes + * are up, or we need to access a node in ExcludeNodes, + * do we just fail instead? */ + routerset_t *ExcludeNodes;/**< Structure containing nicknames, digests, + * country codes and IP address patterns of ORs + * not to use in circuits. But see StrictNodes + * above. */ + routerset_t *ExcludeExitNodes;/**< Structure containing nicknames, digests, + * country codes and IP address patterns of + * ORs not to consider as exits. */ + + /** Union of ExcludeNodes and ExcludeExitNodes */ + routerset_t *ExcludeExitNodesUnion_; + + int DisableAllSwap; /**< Boolean: Attempt to call mlockall() on our + * process for all current and future memory. */ + + struct config_line_t *ExitPolicy; /**< Lists of exit policy components. */ + int ExitPolicyRejectPrivate; /**< Should we not exit to reserved private + * addresses, and our own published addresses? + */ + int ExitPolicyRejectLocalInterfaces; /**< Should we not exit to local + * interface addresses? + * Includes OutboundBindAddresses and + * configured ports. */ + int ReducedExitPolicy; /**<Should we use the Reduced Exit Policy? */ + struct config_line_t *SocksPolicy; /**< Lists of socks policy components */ + struct config_line_t *DirPolicy; /**< Lists of dir policy components */ + /** Local address to bind outbound sockets */ + struct config_line_t *OutboundBindAddress; + /** Local address to bind outbound relay sockets */ + struct config_line_t *OutboundBindAddressOR; + /** Local address to bind outbound exit sockets */ + struct config_line_t *OutboundBindAddressExit; + /** Addresses derived from the various OutboundBindAddress lines. + * [][0] is IPv4, [][1] is IPv6 + */ + tor_addr_t OutboundBindAddresses[OUTBOUND_ADDR_MAX][2]; + /** Directory server only: which versions of + * Tor should we tell users to run? */ + struct config_line_t *RecommendedVersions; + struct config_line_t *RecommendedClientVersions; + struct config_line_t *RecommendedServerVersions; + struct config_line_t *RecommendedPackages; + /** Whether dirservers allow router descriptors with private IPs. */ + int DirAllowPrivateAddresses; + /** Whether routers accept EXTEND cells to routers with private IPs. */ + int ExtendAllowPrivateAddresses; + char *User; /**< Name of user to run Tor as. */ + /** Ports to listen on for OR connections. */ + struct config_line_t *ORPort_lines; + /** Ports to listen on for extended OR connections. */ + struct config_line_t *ExtORPort_lines; + /** Ports to listen on for SOCKS connections. */ + struct config_line_t *SocksPort_lines; + /** Ports to listen on for transparent pf/netfilter connections. */ + struct config_line_t *TransPort_lines; + char *TransProxyType; /**< What kind of transparent proxy + * implementation are we using? */ + /** Parsed value of TransProxyType. */ + enum { + TPT_DEFAULT, + TPT_PF_DIVERT, + TPT_IPFW, + TPT_TPROXY, + } TransProxyType_parsed; + /** Ports to listen on for transparent natd connections. */ + struct config_line_t *NATDPort_lines; + /** Ports to listen on for HTTP Tunnel connections. */ + struct config_line_t *HTTPTunnelPort_lines; + struct config_line_t *ControlPort_lines; /**< Ports to listen on for control + * connections. */ + /** List of Unix Domain Sockets to listen on for control connections. */ + struct config_line_t *ControlSocket; + + int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */ + int UnixSocksGroupWritable; /**< Boolean: Are SOCKS Unix sockets g+rw? */ + /** Ports to listen on for directory connections. */ + struct config_line_t *DirPort_lines; + /** Ports to listen on for DNS requests. */ + struct config_line_t *DNSPort_lines; + + /* MaxMemInQueues value as input by the user. We clean this up to be + * MaxMemInQueues. */ + uint64_t MaxMemInQueues_raw; + uint64_t MaxMemInQueues;/**< If we have more memory than this allocated + * for queues and buffers, run the OOM handler */ + /** Above this value, consider ourselves low on RAM. */ + uint64_t MaxMemInQueues_low_threshold; + + /** @name port booleans + * + * Derived booleans: For server ports and ControlPort, true iff there is a + * non-listener port on an AF_INET or AF_INET6 address of the given type + * configured in one of the _lines options above. + * For client ports, also true if there is a unix socket configured. + * If you are checking for client ports, you may want to use: + * SocksPort_set || TransPort_set || NATDPort_set || DNSPort_set || + * HTTPTunnelPort_set + * rather than SocksPort_set. + * + * @{ + */ + unsigned int ORPort_set : 1; + unsigned int SocksPort_set : 1; + unsigned int TransPort_set : 1; + unsigned int NATDPort_set : 1; + unsigned int ControlPort_set : 1; + unsigned int DirPort_set : 1; + unsigned int DNSPort_set : 1; + unsigned int ExtORPort_set : 1; + unsigned int HTTPTunnelPort_set : 1; + /**@}*/ + + int AssumeReachable; /**< Whether to publish our descriptor regardless. */ + int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */ + int V3AuthoritativeDir; /**< Boolean: is this an authoritative directory + * for version 3 directories? */ + int VersioningAuthoritativeDir; /**< Boolean: is this an authoritative + * directory that's willing to recommend + * versions? */ + int BridgeAuthoritativeDir; /**< Boolean: is this an authoritative directory + * that aggregates bridge descriptors? */ + + /** If set on a bridge relay, it will include this value on a new + * "bridge-distribution-request" line in its bridge descriptor. */ + char *BridgeDistribution; + + /** If set on a bridge authority, it will answer requests on its dirport + * for bridge statuses -- but only if the requests use this password. */ + char *BridgePassword; + /** If BridgePassword is set, this is a SHA256 digest of the basic http + * authenticator for it. Used so we can do a time-independent comparison. */ + char *BridgePassword_AuthDigest_; + + int UseBridges; /**< Boolean: should we start all circuits with a bridge? */ + struct config_line_t *Bridges; /**< List of bootstrap bridge addresses. */ + + struct config_line_t *ClientTransportPlugin; /**< List of client + transport plugins. */ + + struct config_line_t *ServerTransportPlugin; /**< List of client + transport plugins. */ + + /** List of TCP/IP addresses that transports should listen at. */ + struct config_line_t *ServerTransportListenAddr; + + /** List of options that must be passed to pluggable transports. */ + struct config_line_t *ServerTransportOptions; + + int BridgeRelay; /**< Boolean: are we acting as a bridge relay? We make + * this explicit so we can change how we behave in the + * future. */ + + /** Boolean: if we know the bridge's digest, should we get new + * descriptors from the bridge authorities or from the bridge itself? */ + int UpdateBridgesFromAuthority; + + int AvoidDiskWrites; /**< Boolean: should we never cache things to disk? + * Not used yet. */ + int ClientOnly; /**< Boolean: should we never evolve into a server role? */ + + int ReducedConnectionPadding; /**< Boolean: Should we try to keep connections + open shorter and pad them less against + connection-level traffic analysis? */ + /** Autobool: if auto, then connection padding will be negotiated by client + * and server. If 0, it will be fully disabled. If 1, the client will still + * pad to the server regardless of server support. */ + int ConnectionPadding; + + /** To what authority types do we publish our descriptor? Choices are + * "v1", "v2", "v3", "bridge", or "". */ + struct smartlist_t *PublishServerDescriptor; + /** A bitfield of authority types, derived from PublishServerDescriptor. */ + dirinfo_type_t PublishServerDescriptor_; + /** Boolean: do we publish hidden service descriptors to the HS auths? */ + int PublishHidServDescriptors; + int FetchServerDescriptors; /**< Do we fetch server descriptors as normal? */ + int FetchHidServDescriptors; /**< and hidden service descriptors? */ + + int MinUptimeHidServDirectoryV2; /**< As directory authority, accept hidden + * service directories after what time? */ + + int FetchUselessDescriptors; /**< Do we fetch non-running descriptors too? */ + int AllDirActionsPrivate; /**< Should every directory action be sent + * through a Tor circuit? */ + + /** A routerset that should be used when picking middle nodes for HS + * circuits. */ + routerset_t *HSLayer2Nodes; + + /** A routerset that should be used when picking third-hop nodes for HS + * circuits. */ + routerset_t *HSLayer3Nodes; + + /** Onion Services in HiddenServiceSingleHopMode make one-hop (direct) + * circuits between the onion service server, and the introduction and + * rendezvous points. (Onion service descriptors are still posted using + * 3-hop paths, to avoid onion service directories blocking the service.) + * This option makes every hidden service instance hosted by + * this tor instance a Single Onion Service. + * HiddenServiceSingleHopMode requires HiddenServiceNonAnonymousMode to be + * set to 1. + * Use rend_service_allow_non_anonymous_connection() or + * rend_service_reveal_startup_time() instead of using this option directly. + */ + int HiddenServiceSingleHopMode; + /* Makes hidden service clients and servers non-anonymous on this tor + * instance. Allows the non-anonymous HiddenServiceSingleHopMode. Enables + * non-anonymous behaviour in the hidden service protocol. + * Use rend_service_non_anonymous_mode_enabled() instead of using this option + * directly. + */ + int HiddenServiceNonAnonymousMode; + + int ConnLimit; /**< Demanded minimum number of simultaneous connections. */ + int ConnLimit_; /**< Maximum allowed number of simultaneous connections. */ + int ConnLimit_high_thresh; /**< start trying to lower socket usage if we + * have this many. */ + int ConnLimit_low_thresh; /**< try to get down to here after socket + * exhaustion. */ + int RunAsDaemon; /**< If true, run in the background. (Unix only) */ + int FascistFirewall; /**< Whether to prefer ORs reachable on open ports. */ + struct smartlist_t *FirewallPorts; /**< Which ports our firewall allows + * (strings). */ + /** IP:ports our firewall allows. */ + struct config_line_t *ReachableAddresses; + struct config_line_t *ReachableORAddresses; /**< IP:ports for OR conns. */ + struct config_line_t *ReachableDirAddresses; /**< IP:ports for Dir conns. */ + + int ConstrainedSockets; /**< Shrink xmit and recv socket buffers. */ + uint64_t ConstrainedSockSize; /**< Size of constrained buffers. */ + + /** Whether we should drop exit streams from Tors that we don't know are + * relays. One of "0" (never refuse), "1" (always refuse), or "-1" (do + * what the consensus says, defaulting to 'refuse' if the consensus says + * nothing). */ + int RefuseUnknownExits; + + /** Application ports that require all nodes in circ to have sufficient + * uptime. */ + struct smartlist_t *LongLivedPorts; + /** Application ports that are likely to be unencrypted and + * unauthenticated; we reject requests for them to prevent the + * user from screwing up and leaking plaintext secrets to an + * observer somewhere on the Internet. */ + struct smartlist_t *RejectPlaintextPorts; + /** Related to RejectPlaintextPorts above, except this config option + * controls whether we warn (in the log and via a controller status + * event) every time a risky connection is attempted. */ + struct smartlist_t *WarnPlaintextPorts; + /** Should we try to reuse the same exit node for a given host */ + struct smartlist_t *TrackHostExits; + int TrackHostExitsExpire; /**< Number of seconds until we expire an + * addressmap */ + struct config_line_t *AddressMap; /**< List of address map directives. */ + int AutomapHostsOnResolve; /**< If true, when we get a resolve request for a + * hostname ending with one of the suffixes in + * <b>AutomapHostsSuffixes</b>, map it to a + * virtual address. */ + /** List of suffixes for <b>AutomapHostsOnResolve</b>. The special value + * "." means "match everything." */ + struct smartlist_t *AutomapHostsSuffixes; + int RendPostPeriod; /**< How often do we post each rendezvous service + * descriptor? Remember to publish them independently. */ + int KeepalivePeriod; /**< How often do we send padding cells to keep + * connections alive? */ + int SocksTimeout; /**< How long do we let a socks connection wait + * unattached before we fail it? */ + int LearnCircuitBuildTimeout; /**< If non-zero, we attempt to learn a value + * for CircuitBuildTimeout based on timeout + * history. Use circuit_build_times_disabled() + * rather than checking this value directly. */ + int CircuitBuildTimeout; /**< Cull non-open circuits that were born at + * least this many seconds ago. Used until + * adaptive algorithm learns a new value. */ + int CircuitsAvailableTimeout; /**< Try to have an open circuit for at + least this long after last activity */ + int CircuitStreamTimeout; /**< If non-zero, detach streams from circuits + * and try a new circuit if the stream has been + * waiting for this many seconds. If zero, use + * our default internal timeout schedule. */ + int MaxOnionQueueDelay; /*< DOCDOC */ + int NewCircuitPeriod; /**< How long do we use a circuit before building + * a new one? */ + int MaxCircuitDirtiness; /**< Never use circs that were first used more than + this interval ago. */ + uint64_t BandwidthRate; /**< How much bandwidth, on average, are we willing + * to use in a second? */ + uint64_t BandwidthBurst; /**< How much bandwidth, at maximum, are we willing + * to use in a second? */ + uint64_t MaxAdvertisedBandwidth; /**< How much bandwidth are we willing to + * tell other nodes we have? */ + uint64_t RelayBandwidthRate; /**< How much bandwidth, on average, are we + * willing to use for all relayed conns? */ + uint64_t RelayBandwidthBurst; /**< How much bandwidth, at maximum, will we + * use in a second for all relayed conns? */ + uint64_t PerConnBWRate; /**< Long-term bw on a single TLS conn, if set. */ + uint64_t PerConnBWBurst; /**< Allowed burst on a single TLS conn, if set. */ + int NumCPUs; /**< How many CPUs should we try to use? */ + struct config_line_t *RendConfigLines; /**< List of configuration lines + * for rendezvous services. */ + struct config_line_t *HidServAuth; /**< List of configuration lines for + * client-side authorizations for hidden + * services */ + char *ClientOnionAuthDir; /**< Directory to keep client + * onion service authorization secret keys */ + char *ContactInfo; /**< Contact info to be published in the directory. */ + + int HeartbeatPeriod; /**< Log heartbeat messages after this many seconds + * have passed. */ + int MainloopStats; /**< Log main loop statistics as part of the + * heartbeat messages. */ + + char *HTTPProxy; /**< hostname[:port] to use as http proxy, if any. */ + tor_addr_t HTTPProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */ + uint16_t HTTPProxyPort; /**< Parsed port for http proxy, if any. */ + char *HTTPProxyAuthenticator; /**< username:password string, if any. */ + + char *HTTPSProxy; /**< hostname[:port] to use as https proxy, if any. */ + tor_addr_t HTTPSProxyAddr; /**< Parsed addr for https proxy, if any. */ + uint16_t HTTPSProxyPort; /**< Parsed port for https proxy, if any. */ + char *HTTPSProxyAuthenticator; /**< username:password string, if any. */ + + char *Socks4Proxy; /**< hostname:port to use as a SOCKS4 proxy, if any. */ + tor_addr_t Socks4ProxyAddr; /**< Derived from Socks4Proxy. */ + uint16_t Socks4ProxyPort; /**< Derived from Socks4Proxy. */ + + char *Socks5Proxy; /**< hostname:port to use as a SOCKS5 proxy, if any. */ + tor_addr_t Socks5ProxyAddr; /**< Derived from Sock5Proxy. */ + uint16_t Socks5ProxyPort; /**< Derived from Socks5Proxy. */ + char *Socks5ProxyUsername; /**< Username for SOCKS5 authentication, if any */ + char *Socks5ProxyPassword; /**< Password for SOCKS5 authentication, if any */ + + /** List of configuration lines for replacement directory authorities. + * If you just want to replace one class of authority at a time, + * use the "Alternate*Authority" options below instead. */ + struct config_line_t *DirAuthorities; + + /** List of fallback directory servers */ + struct config_line_t *FallbackDir; + /** Whether to use the default hard-coded FallbackDirs */ + int UseDefaultFallbackDirs; + + /** Weight to apply to all directory authority rates if considering them + * along with fallbackdirs */ + double DirAuthorityFallbackRate; + + /** If set, use these main (currently v3) directory authorities and + * not the default ones. */ + struct config_line_t *AlternateDirAuthority; + + /** If set, use these bridge authorities and not the default one. */ + struct config_line_t *AlternateBridgeAuthority; + + struct config_line_t *MyFamily_lines; /**< Declared family for this OR. */ + struct config_line_t *MyFamily; /**< Declared family for this OR, + normalized */ + struct config_line_t *NodeFamilies; /**< List of config lines for + * node families */ + /** List of parsed NodeFamilies values. */ + struct smartlist_t *NodeFamilySets; + struct config_line_t *AuthDirBadExit; /**< Address policy for descriptors to + * mark as bad exits. */ + struct config_line_t *AuthDirReject; /**< Address policy for descriptors to + * reject. */ + struct config_line_t *AuthDirInvalid; /**< Address policy for descriptors to + * never mark as valid. */ + /** @name AuthDir...CC + * + * Lists of country codes to mark as BadExit, or Invalid, or to + * reject entirely. + * + * @{ + */ + struct smartlist_t *AuthDirBadExitCCs; + struct smartlist_t *AuthDirInvalidCCs; + struct smartlist_t *AuthDirRejectCCs; + /**@}*/ + + int AuthDirListBadExits; /**< True iff we should list bad exits, + * and vote for all other exits as good. */ + int AuthDirMaxServersPerAddr; /**< Do not permit more than this + * number of servers per IP address. */ + int AuthDirHasIPv6Connectivity; /**< Boolean: are we on IPv6? */ + int AuthDirPinKeys; /**< Boolean: Do we enforce key-pinning? */ + + /** If non-zero, always vote the Fast flag for any relay advertising + * this amount of capacity or more. */ + uint64_t AuthDirFastGuarantee; + + /** If non-zero, this advertised capacity or more is always sufficient + * to satisfy the bandwidth requirement for the Guard flag. */ + uint64_t AuthDirGuardBWGuarantee; + + char *AccountingStart; /**< How long is the accounting interval, and when + * does it start? */ + uint64_t AccountingMax; /**< How many bytes do we allow per accounting + * interval before hibernation? 0 for "never + * hibernate." */ + /** How do we determine when our AccountingMax has been reached? + * "max" for when in or out reaches AccountingMax + * "sum" for when in plus out reaches AccountingMax + * "in" for when in reaches AccountingMax + * "out" for when out reaches AccountingMax */ + char *AccountingRule_option; + enum { ACCT_MAX, ACCT_SUM, ACCT_IN, ACCT_OUT } AccountingRule; + + /** Base64-encoded hash of accepted passwords for the control system. */ + struct config_line_t *HashedControlPassword; + /** As HashedControlPassword, but not saved. */ + struct config_line_t *HashedControlSessionPassword; + + int CookieAuthentication; /**< Boolean: do we enable cookie-based auth for + * the control system? */ + char *CookieAuthFile; /**< Filesystem location of a ControlPort + * authentication cookie. */ + char *ExtORPortCookieAuthFile; /**< Filesystem location of Extended + * ORPort authentication cookie. */ + int CookieAuthFileGroupReadable; /**< Boolean: Is the CookieAuthFile g+r? */ + int ExtORPortCookieAuthFileGroupReadable; /**< Boolean: Is the + * ExtORPortCookieAuthFile g+r? */ + int LeaveStreamsUnattached; /**< Boolean: Does Tor attach new streams to + * circuits itself (0), or does it expect a controller + * to cope? (1) */ + int DisablePredictedCircuits; /**< Boolean: does Tor preemptively + * make circuits in the background (0), + * or not (1)? */ + + /** Process specifier for a controller that ‘owns’ this Tor + * instance. Tor will terminate if its owning controller does. */ + char *OwningControllerProcess; + /** FD specifier for a controller that owns this Tor instance. */ + uint64_t OwningControllerFD; + + int ShutdownWaitLength; /**< When we get a SIGINT and we're a server, how + * long do we wait before exiting? */ + char *SafeLogging; /**< Contains "relay", "1", "0" (meaning no scrubbing). */ + + /* Derived from SafeLogging */ + enum { + SAFELOG_SCRUB_ALL, SAFELOG_SCRUB_RELAY, SAFELOG_SCRUB_NONE + } SafeLogging_; + + int Sandbox; /**< Boolean: should sandboxing be enabled? */ + int SafeSocks; /**< Boolean: should we outright refuse application + * connections that use socks4 or socks5-with-local-dns? */ + int ProtocolWarnings; /**< Boolean: when other parties screw up the Tor + * protocol, is it a warn or an info in our logs? */ + int TestSocks; /**< Boolean: when we get a socks connection, do we loudly + * log whether it was DNS-leaking or not? */ + int HardwareAccel; /**< Boolean: Should we enable OpenSSL hardware + * acceleration where available? */ + /** Token Bucket Refill resolution in milliseconds. */ + int TokenBucketRefillInterval; + char *AccelName; /**< Optional hardware acceleration engine name. */ + char *AccelDir; /**< Optional hardware acceleration engine search dir. */ + + /** Boolean: Do we try to enter from a smallish number + * of fixed nodes? */ + int UseEntryGuards_option; + /** Internal variable to remember whether we're actually acting on + * UseEntryGuards_option -- when we're a non-anonymous Single Onion Service, + * it is always false, otherwise we use the value of UseEntryGuards_option. + * */ + int UseEntryGuards; + + int NumEntryGuards; /**< How many entry guards do we try to establish? */ + + /** If 1, we use any guardfraction information we see in the + * consensus. If 0, we don't. If -1, let the consensus parameter + * decide. */ + int UseGuardFraction; + + int NumDirectoryGuards; /**< How many dir guards do we try to establish? + * If 0, use value from NumEntryGuards. */ + int NumPrimaryGuards; /**< How many primary guards do we want? */ + + int RephistTrackTime; /**< How many seconds do we keep rephist info? */ + /** Should we always fetch our dir info on the mirror schedule (which + * means directly from the authorities) no matter our other config? */ + int FetchDirInfoEarly; + + /** Should we fetch our dir info at the start of the consensus period? */ + int FetchDirInfoExtraEarly; + + int DirCache; /**< Cache all directory documents and accept requests via + * tunnelled dir conns from clients. If 1, enabled (default); + * If 0, disabled. */ + + char *VirtualAddrNetworkIPv4; /**< Address and mask to hand out for virtual + * MAPADDRESS requests for IPv4 addresses */ + char *VirtualAddrNetworkIPv6; /**< Address and mask to hand out for virtual + * MAPADDRESS requests for IPv6 addresses */ + int ServerDNSSearchDomains; /**< Boolean: If set, we don't force exit + * addresses to be FQDNs, but rather search for them in + * the local domains. */ + int ServerDNSDetectHijacking; /**< Boolean: If true, check for DNS failure + * hijacking. */ + int ServerDNSRandomizeCase; /**< Boolean: Use the 0x20-hack to prevent + * DNS poisoning attacks. */ + char *ServerDNSResolvConfFile; /**< If provided, we configure our internal + * resolver from the file here rather than from + * /etc/resolv.conf (Unix) or the registry (Windows). */ + char *DirPortFrontPage; /**< This is a full path to a file with an html + disclaimer. This allows a server administrator to show + that they're running Tor and anyone visiting their server + will know this without any specialized knowledge. */ + int DisableDebuggerAttachment; /**< Currently Linux only specific attempt to + disable ptrace; needs BSD testing. */ + /** Boolean: if set, we start even if our resolv.conf file is missing + * or broken. */ + int ServerDNSAllowBrokenConfig; + /** Boolean: if set, then even connections to private addresses will get + * rate-limited. */ + int CountPrivateBandwidth; + /** A list of addresses that definitely should be resolvable. Used for + * testing our DNS server. */ + struct smartlist_t *ServerDNSTestAddresses; + int EnforceDistinctSubnets; /**< If true, don't allow multiple routers in the + * same network zone in the same circuit. */ + int AllowNonRFC953Hostnames; /**< If true, we allow connections to hostnames + * with weird characters. */ + /** If true, we try resolving hostnames with weird characters. */ + int ServerDNSAllowNonRFC953Hostnames; + + /** If true, we try to download extra-info documents (and we serve them, + * if we are a cache). For authorities, this is always true. */ + int DownloadExtraInfo; + + /** If true, we're configured to collect statistics on clients + * requesting network statuses from us as directory. */ + int DirReqStatistics_option; + /** Internal variable to remember whether we're actually acting on + * DirReqStatistics_option -- yes if it's set and we're a server, else no. */ + int DirReqStatistics; + + /** If true, the user wants us to collect statistics on port usage. */ + int ExitPortStatistics; + + /** If true, the user wants us to collect connection statistics. */ + int ConnDirectionStatistics; + + /** If true, the user wants us to collect cell statistics. */ + int CellStatistics; + + /** If true, the user wants us to collect padding statistics. */ + int PaddingStatistics; + + /** If true, the user wants us to collect statistics as entry node. */ + int EntryStatistics; + + /** If true, the user wants us to collect statistics as hidden service + * directory, introduction point, or rendezvous point. */ + int HiddenServiceStatistics_option; + /** Internal variable to remember whether we're actually acting on + * HiddenServiceStatistics_option -- yes if it's set and we're a server, + * else no. */ + int HiddenServiceStatistics; + + /** If true, include statistics file contents in extra-info documents. */ + int ExtraInfoStatistics; + + /** If true, do not believe anybody who tells us that a domain resolves + * to an internal address, or that an internal address has a PTR mapping. + * Helps avoid some cross-site attacks. */ + int ClientDNSRejectInternalAddresses; + + /** If true, do not accept any requests to connect to internal addresses + * over randomly chosen exits. */ + int ClientRejectInternalAddresses; + + /** If true, clients may connect over IPv4. If false, they will avoid + * connecting over IPv4. We enforce this for OR and Dir connections. */ + int ClientUseIPv4; + /** If true, clients may connect over IPv6. If false, they will avoid + * connecting over IPv4. We enforce this for OR and Dir connections. + * Use fascist_firewall_use_ipv6() instead of accessing this value + * directly. */ + int ClientUseIPv6; + /** If true, prefer an IPv6 OR port over an IPv4 one for entry node + * connections. If auto, bridge clients prefer IPv6, and other clients + * prefer IPv4. Use node_ipv6_or_preferred() instead of accessing this value + * directly. */ + int ClientPreferIPv6ORPort; + /** If true, prefer an IPv6 directory port over an IPv4 one for direct + * directory connections. If auto, bridge clients prefer IPv6, and other + * clients prefer IPv4. Use fascist_firewall_prefer_ipv6_dirport() instead of + * accessing this value directly. */ + int ClientPreferIPv6DirPort; + + /** The length of time that we think a consensus should be fresh. */ + int V3AuthVotingInterval; + /** The length of time we think it will take to distribute votes. */ + int V3AuthVoteDelay; + /** The length of time we think it will take to distribute signatures. */ + int V3AuthDistDelay; + /** The number of intervals we think a consensus should be valid. */ + int V3AuthNIntervalsValid; + + /** Should advertise and sign consensuses with a legacy key, for key + * migration purposes? */ + int V3AuthUseLegacyKey; + + /** Location of bandwidth measurement file */ + char *V3BandwidthsFile; + + /** Location of guardfraction file */ + char *GuardfractionFile; + + /** Authority only: key=value pairs that we add to our networkstatus + * consensus vote on the 'params' line. */ + char *ConsensusParams; + + /** Authority only: minimum number of measured bandwidths we must see + * before we only believe measured bandwidths to assign flags. */ + int MinMeasuredBWsForAuthToIgnoreAdvertised; + + /** The length of time that we think an initial consensus should be fresh. + * Only altered on testing networks. */ + int TestingV3AuthInitialVotingInterval; + + /** The length of time we think it will take to distribute initial votes. + * Only altered on testing networks. */ + int TestingV3AuthInitialVoteDelay; + + /** The length of time we think it will take to distribute initial + * signatures. Only altered on testing networks.*/ + int TestingV3AuthInitialDistDelay; + + /** Offset in seconds added to the starting time for consensus + voting. Only altered on testing networks. */ + int TestingV3AuthVotingStartOffset; + + /** If an authority has been around for less than this amount of time, it + * does not believe its reachability information is accurate. Only + * altered on testing networks. */ + int TestingAuthDirTimeToLearnReachability; + + /** Clients don't download any descriptor this recent, since it will + * probably not have propagated to enough caches. Only altered on testing + * networks. */ + int TestingEstimatedDescriptorPropagationTime; + + /** Schedule for when servers should download things in general. Only + * altered on testing networks. */ + int TestingServerDownloadInitialDelay; + + /** Schedule for when clients should download things in general. Only + * altered on testing networks. */ + int TestingClientDownloadInitialDelay; + + /** Schedule for when servers should download consensuses. Only altered + * on testing networks. */ + int TestingServerConsensusDownloadInitialDelay; + + /** Schedule for when clients should download consensuses. Only altered + * on testing networks. */ + int TestingClientConsensusDownloadInitialDelay; + + /** Schedule for when clients should download consensuses from authorities + * if they are bootstrapping (that is, they don't have a usable, reasonably + * live consensus). Only used by clients fetching from a list of fallback + * directory mirrors. + * + * This schedule is incremented by (potentially concurrent) connection + * attempts, unlike other schedules, which are incremented by connection + * failures. Only altered on testing networks. */ + int ClientBootstrapConsensusAuthorityDownloadInitialDelay; + + /** Schedule for when clients should download consensuses from fallback + * directory mirrors if they are bootstrapping (that is, they don't have a + * usable, reasonably live consensus). Only used by clients fetching from a + * list of fallback directory mirrors. + * + * This schedule is incremented by (potentially concurrent) connection + * attempts, unlike other schedules, which are incremented by connection + * failures. Only altered on testing networks. */ + int ClientBootstrapConsensusFallbackDownloadInitialDelay; + + /** Schedule for when clients should download consensuses from authorities + * if they are bootstrapping (that is, they don't have a usable, reasonably + * live consensus). Only used by clients which don't have or won't fetch + * from a list of fallback directory mirrors. + * + * This schedule is incremented by (potentially concurrent) connection + * attempts, unlike other schedules, which are incremented by connection + * failures. Only altered on testing networks. */ + int ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay; + + /** Schedule for when clients should download bridge descriptors. Only + * altered on testing networks. */ + int TestingBridgeDownloadInitialDelay; + + /** Schedule for when clients should download bridge descriptors when they + * have no running bridges. Only altered on testing networks. */ + int TestingBridgeBootstrapDownloadInitialDelay; + + /** When directory clients have only a few descriptors to request, they + * batch them until they have more, or until this amount of time has + * passed. Only altered on testing networks. */ + int TestingClientMaxIntervalWithoutRequest; + + /** How long do we let a directory connection stall before expiring + * it? Only altered on testing networks. */ + int TestingDirConnectionMaxStall; + + /** How many simultaneous in-progress connections will we make when trying + * to fetch a consensus before we wait for one to complete, timeout, or + * error out? Only altered on testing networks. */ + int ClientBootstrapConsensusMaxInProgressTries; + + /** If true, we take part in a testing network. Change the defaults of a + * couple of other configuration options and allow to change the values + * of certain configuration options. */ + int TestingTorNetwork; + + /** Minimum value for the Exit flag threshold on testing networks. */ + uint64_t TestingMinExitFlagThreshold; + + /** Minimum value for the Fast flag threshold on testing networks. */ + uint64_t TestingMinFastFlagThreshold; + + /** Relays in a testing network which should be voted Exit + * regardless of exit policy. */ + routerset_t *TestingDirAuthVoteExit; + int TestingDirAuthVoteExitIsStrict; + + /** Relays in a testing network which should be voted Guard + * regardless of uptime and bandwidth. */ + routerset_t *TestingDirAuthVoteGuard; + int TestingDirAuthVoteGuardIsStrict; + + /** Relays in a testing network which should be voted HSDir + * regardless of uptime and DirPort. */ + routerset_t *TestingDirAuthVoteHSDir; + int TestingDirAuthVoteHSDirIsStrict; + + /** Enable CONN_BW events. Only altered on testing networks. */ + int TestingEnableConnBwEvent; + + /** Enable CELL_STATS events. Only altered on testing networks. */ + int TestingEnableCellStatsEvent; + + /** If true, and we have GeoIP data, and we're a bridge, keep a per-country + * count of how many client addresses have contacted us so that we can help + * the bridge authority guess which countries have blocked access to us. */ + int BridgeRecordUsageByCountry; + + /** Optionally, IPv4 and IPv6 GeoIP data. */ + char *GeoIPFile; + char *GeoIPv6File; + + /** Autobool: if auto, then any attempt to Exclude{Exit,}Nodes a particular + * country code will exclude all nodes in ?? and A1. If true, all nodes in + * ?? and A1 are excluded. Has no effect if we don't know any GeoIP data. */ + int GeoIPExcludeUnknown; + + /** If true, SIGHUP should reload the torrc. Sometimes controllers want + * to make this false. */ + int ReloadTorrcOnSIGHUP; + + /* The main parameter for picking circuits within a connection. + * + * If this value is positive, when picking a cell to relay on a connection, + * we always relay from the circuit whose weighted cell count is lowest. + * Cells are weighted exponentially such that if one cell is sent + * 'CircuitPriorityHalflife' seconds before another, it counts for half as + * much. + * + * If this value is zero, we're disabling the cell-EWMA algorithm. + * + * If this value is negative, we're using the default approach + * according to either Tor or a parameter set in the consensus. + */ + double CircuitPriorityHalflife; + + /** Set to true if the TestingTorNetwork configuration option is set. + * This is used so that options_validate() has a chance to realize that + * the defaults have changed. */ + int UsingTestNetworkDefaults_; + + /** If 1, we try to use microdescriptors to build circuits. If 0, we don't. + * If -1, Tor decides. */ + int UseMicrodescriptors; + + /** File where we should write the ControlPort. */ + char *ControlPortWriteToFile; + /** Should that file be group-readable? */ + int ControlPortFileGroupReadable; + +#define MAX_MAX_CLIENT_CIRCUITS_PENDING 1024 + /** Maximum number of non-open general-purpose origin circuits to allow at + * once. */ + int MaxClientCircuitsPending; + + /** If 1, we always send optimistic data when it's supported. If 0, we + * never use it. If -1, we do what the consensus says. */ + int OptimisticData; + + /** If 1, we accept and launch no external network connections, except on + * control ports. */ + int DisableNetwork; + + /** + * Parameters for path-bias detection. + * @{ + * These options override the default behavior of Tor's (**currently + * experimental**) path bias detection algorithm. To try to find broken or + * misbehaving guard nodes, Tor looks for nodes where more than a certain + * fraction of circuits through that guard fail to get built. + * + * The PathBiasCircThreshold option controls how many circuits we need to + * build through a guard before we make these checks. The + * PathBiasNoticeRate, PathBiasWarnRate and PathBiasExtremeRate options + * control what fraction of circuits must succeed through a guard so we + * won't write log messages. If less than PathBiasExtremeRate circuits + * succeed *and* PathBiasDropGuards is set to 1, we disable use of that + * guard. + * + * When we have seen more than PathBiasScaleThreshold circuits through a + * guard, we scale our observations by 0.5 (governed by the consensus) so + * that new observations don't get swamped by old ones. + * + * By default, or if a negative value is provided for one of these options, + * Tor uses reasonable defaults from the networkstatus consensus document. + * If no defaults are available there, these options default to 150, .70, + * .50, .30, 0, and 300 respectively. + */ + int PathBiasCircThreshold; + double PathBiasNoticeRate; + double PathBiasWarnRate; + double PathBiasExtremeRate; + int PathBiasDropGuards; + int PathBiasScaleThreshold; + /** @} */ + + /** + * Parameters for path-bias use detection + * @{ + * Similar to the above options, these options override the default behavior + * of Tor's (**currently experimental**) path use bias detection algorithm. + * + * Where as the path bias parameters govern thresholds for successfully + * building circuits, these four path use bias parameters govern thresholds + * only for circuit usage. Circuits which receive no stream usage are not + * counted by this detection algorithm. A used circuit is considered + * successful if it is capable of carrying streams or otherwise receiving + * well-formed responses to RELAY cells. + * + * By default, or if a negative value is provided for one of these options, + * Tor uses reasonable defaults from the networkstatus consensus document. + * If no defaults are available there, these options default to 20, .80, + * .60, and 100, respectively. + */ + int PathBiasUseThreshold; + double PathBiasNoticeUseRate; + double PathBiasExtremeUseRate; + int PathBiasScaleUseThreshold; + /** @} */ + + int IPv6Exit; /**< Do we support exiting to IPv6 addresses? */ + + /** Fraction: */ + double PathsNeededToBuildCircuits; + + /** What expiry time shall we place on our SSL certs? "0" means we + * should guess a suitable value. */ + int SSLKeyLifetime; + + /** How long (seconds) do we keep a guard before picking a new one? */ + int GuardLifetime; + + /** Is this an exit node? This is a tristate, where "1" means "yes, and use + * the default exit policy if none is given" and "0" means "no; exit policy + * is 'reject *'" and "auto" (-1) means "same as 1, but warn the user." + * + * XXXX Eventually, the default will be 0. */ + int ExitRelay; + + /** For how long (seconds) do we declare our signing keys to be valid? */ + int SigningKeyLifetime; + /** For how long (seconds) do we declare our link keys to be valid? */ + int TestingLinkCertLifetime; + /** For how long (seconds) do we declare our auth keys to be valid? */ + int TestingAuthKeyLifetime; + + /** How long before signing keys expire will we try to make a new one? */ + int TestingSigningKeySlop; + /** How long before link keys expire will we try to make a new one? */ + int TestingLinkKeySlop; + /** How long before auth keys expire will we try to make a new one? */ + int TestingAuthKeySlop; + + /** Force use of offline master key features: never generate a master + * ed25519 identity key except from tor --keygen */ + int OfflineMasterKey; + + enum { + FORCE_PASSPHRASE_AUTO=0, + FORCE_PASSPHRASE_ON, + FORCE_PASSPHRASE_OFF + } keygen_force_passphrase; + int use_keygen_passphrase_fd; + int keygen_passphrase_fd; + int change_key_passphrase; + char *master_key_fname; + + /** Autobool: Do we try to retain capabilities if we can? */ + int KeepBindCapabilities; + + /** Maximum total size of unparseable descriptors to log during the + * lifetime of this Tor process. + */ + uint64_t MaxUnparseableDescSizeToLog; + + /** Bool (default: 1): Switch for the shared random protocol. Only + * relevant to a directory authority. If off, the authority won't + * participate in the protocol. If on (default), a flag is added to the + * vote indicating participation. */ + int AuthDirSharedRandomness; + + /** If 1, we skip all OOS checks. */ + int DisableOOSCheck; + + /** Autobool: Should we include Ed25519 identities in extend2 cells? + * If -1, we should do whatever the consensus parameter says. */ + int ExtendByEd25519ID; + + /** Bool (default: 1): When testing routerinfos as a directory authority, + * do we enforce Ed25519 identity match? */ + /* NOTE: remove this option someday. */ + int AuthDirTestEd25519LinkKeys; + + /** Bool (default: 0): Tells if a %include was used on torrc */ + int IncludeUsed; + + /** The seconds after expiration which we as a relay should keep old + * consensuses around so that we can generate diffs from them. If 0, + * use the default. */ + int MaxConsensusAgeForDiffs; + + /** Bool (default: 0). Tells Tor to never try to exec another program. + */ + int NoExec; + + /** Have the KIST scheduler run every X milliseconds. If less than zero, do + * not use the KIST scheduler but use the old vanilla scheduler instead. If + * zero, do what the consensus says and fall back to using KIST as if this is + * set to "10 msec" if the consensus doesn't say anything. */ + int KISTSchedRunInterval; + + /** A multiplier for the KIST per-socket limit calculation. */ + double KISTSockBufSizeFactor; + + /** The list of scheduler type string ordered by priority that is first one + * has to be tried first. Default: KIST,KISTLite,Vanilla */ + struct smartlist_t *Schedulers; + /* An ordered list of scheduler_types mapped from Schedulers. */ + struct smartlist_t *SchedulerTypes_; + + /** List of files that were opened by %include in torrc and torrc-defaults */ + struct smartlist_t *FilesOpenedByIncludes; + + /** If true, Tor shouldn't install any posix signal handlers, since it is + * running embedded inside another process. + */ + int DisableSignalHandlers; + + /** Autobool: Is the circuit creation DoS mitigation subsystem enabled? */ + int DoSCircuitCreationEnabled; + /** Minimum concurrent connection needed from one single address before any + * defense is used. */ + int DoSCircuitCreationMinConnections; + /** Circuit rate used to refill the token bucket. */ + int DoSCircuitCreationRate; + /** Maximum allowed burst of circuits. Reaching that value, the address is + * detected as malicious and a defense might be used. */ + int DoSCircuitCreationBurst; + /** When an address is marked as malicous, what defense should be used + * against it. See the dos_cc_defense_type_t enum. */ + int DoSCircuitCreationDefenseType; + /** For how much time (in seconds) the defense is applicable for a malicious + * address. A random time delta is added to the defense time of an address + * which will be between 1 second and half of this value. */ + int DoSCircuitCreationDefenseTimePeriod; + + /** Autobool: Is the DoS connection mitigation subsystem enabled? */ + int DoSConnectionEnabled; + /** Maximum concurrent connection allowed per address. */ + int DoSConnectionMaxConcurrentCount; + /** When an address is reaches the maximum count, what defense should be + * used against it. See the dos_conn_defense_type_t enum. */ + int DoSConnectionDefenseType; + + /** Autobool: Do we refuse single hop client rendezvous? */ + int DoSRefuseSingleHopClientRendezvous; +}; + +#endif diff --git a/src/app/config/or_state_st.h b/src/app/config/or_state_st.h new file mode 100644 index 0000000000..5f8214d146 --- /dev/null +++ b/src/app/config/or_state_st.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-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file or_state_t + * + * \brief The or_state_t structure, which represents Tor's state file. + */ + +#ifndef TOR_OR_STATE_ST_H +#define TOR_OR_STATE_ST_H + +#include "lib/cc/torint.h" +struct smartlist_t; + +/** Persistent state for an onion router, as saved to disk. */ +struct or_state_t { + uint32_t magic_; + /** The time at which we next plan to write the state to the disk. Equal to + * TIME_MAX if there are no savable changes, 0 if there are changes that + * should be saved right away. */ + time_t next_write; + + /** When was the state last written to disk? */ + time_t LastWritten; + + /** Fields for accounting bandwidth use. */ + time_t AccountingIntervalStart; + uint64_t AccountingBytesReadInInterval; + uint64_t AccountingBytesWrittenInInterval; + int AccountingSecondsActive; + int AccountingSecondsToReachSoftLimit; + time_t AccountingSoftLimitHitAt; + uint64_t AccountingBytesAtSoftLimit; + uint64_t AccountingExpectedUsage; + + /** A list of Entry Guard-related configuration lines. (pre-prop271) */ + struct config_line_t *EntryGuards; + + /** A list of guard-related configuration lines. (post-prop271) */ + struct config_line_t *Guard; + + struct config_line_t *TransportProxies; + + /** Cached revision counters for active hidden services on this host */ + struct config_line_t *HidServRevCounter; + + /** These fields hold information on the history of bandwidth usage for + * servers. The "Ends" fields hold the time when we last updated the + * bandwidth usage. The "Interval" fields hold the granularity, in seconds, + * of the entries of Values. The "Values" lists hold decimal string + * representations of the number of bytes read or written in each + * interval. The "Maxima" list holds decimal strings describing the highest + * rate achieved during the interval. + */ + time_t BWHistoryReadEnds; + int BWHistoryReadInterval; + struct smartlist_t *BWHistoryReadValues; + struct smartlist_t *BWHistoryReadMaxima; + time_t BWHistoryWriteEnds; + int BWHistoryWriteInterval; + struct smartlist_t *BWHistoryWriteValues; + struct smartlist_t *BWHistoryWriteMaxima; + time_t BWHistoryDirReadEnds; + int BWHistoryDirReadInterval; + struct smartlist_t *BWHistoryDirReadValues; + struct smartlist_t *BWHistoryDirReadMaxima; + time_t BWHistoryDirWriteEnds; + int BWHistoryDirWriteInterval; + struct smartlist_t *BWHistoryDirWriteValues; + struct smartlist_t *BWHistoryDirWriteMaxima; + + /** Build time histogram */ + struct config_line_t * BuildtimeHistogram; + int TotalBuildTimes; + int CircuitBuildAbandonedCount; + + /** What version of Tor wrote this state file? */ + char *TorVersion; + + /** Holds any unrecognized values we found in the state file, in the order + * in which we found them. */ + struct config_line_t *ExtraLines; + + /** When did we last rotate our onion key? "0" for 'no idea'. */ + time_t LastRotatedOnionKey; +}; + +#endif diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c new file mode 100644 index 0000000000..89039a05b5 --- /dev/null +++ b/src/app/config/statefile.c @@ -0,0 +1,728 @@ +/* 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 statefile.c + * + * \brief Handles parsing and encoding the persistent 'state' file that carries + * miscellaneous persistent state between Tor invocations. + * + * This 'state' file is a typed key-value store that allows multiple + * entries for the same key. It follows the same metaformat as described + * in confparse.c, and uses the same code to read and write itself. + * + * The state file is most suitable for small values that don't change too + * frequently. For values that become very large, we typically use a separate + * file -- for example, see how we handle microdescriptors, by storing them in + * a separate file with a journal. + * + * The current state is accessed via get_or_state(), which returns a singleton + * or_state_t object. Functions that change it should call + * or_state_mark_dirty() to ensure that it will get written to disk. + * + * The or_state_save() function additionally calls various functioens + * throughout Tor that might want to flush more state to the the disk, + * including some in rephist.c, entrynodes.c, circuitstats.c, hibernate.c. + */ + +#define STATEFILE_PRIVATE +#include "core/or/or.h" +#include "core/or/circuitstats.h" +#include "app/config/config.h" +#include "app/config/confparse.h" +#include "core/mainloop/mainloop.h" +#include "core/mainloop/connection.h" +#include "feature/control/control.h" +#include "feature/client/entrynodes.h" +#include "feature/hibernate/hibernate.h" +#include "feature/stats/rephist.h" +#include "feature/relay/router.h" +#include "feature/relay/routermode.h" +#include "lib/sandbox/sandbox.h" +#include "app/config/statefile.h" +#include "lib/encoding/confline.h" +#include "lib/net/resolve.h" + +#include "app/config/or_state_st.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/** A list of state-file "abbreviations," for compatibility. */ +static config_abbrev_t state_abbrevs_[] = { + { "AccountingBytesReadInterval", "AccountingBytesReadInInterval", 0, 0 }, + { "HelperNode", "EntryGuard", 0, 0 }, + { "HelperNodeDownSince", "EntryGuardDownSince", 0, 0 }, + { "HelperNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 }, + { "EntryNode", "EntryGuard", 0, 0 }, + { "EntryNodeDownSince", "EntryGuardDownSince", 0, 0 }, + { "EntryNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 }, + { NULL, NULL, 0, 0}, +}; + +/** dummy instance of or_state_t, used for type-checking its + * members with CONF_CHECK_VAR_TYPE. */ +DUMMY_TYPECHECK_INSTANCE(or_state_t); + +/*XXXX these next two are duplicates or near-duplicates from config.c */ +#define VAR(name,conftype,member,initvalue) \ + { name, CONFIG_TYPE_ ## conftype, offsetof(or_state_t, member), \ + initvalue CONF_TEST_MEMBERS(or_state_t, conftype, member) } +/** As VAR, but the option name and member name are the same. */ +#define V(member,conftype,initvalue) \ + VAR(#member, conftype, member, initvalue) + +/** Array of "state" variables saved to the ~/.tor/state file. */ +static config_var_t state_vars_[] = { + /* Remember to document these in state-contents.txt ! */ + + V(AccountingBytesReadInInterval, MEMUNIT, NULL), + V(AccountingBytesWrittenInInterval, MEMUNIT, NULL), + V(AccountingExpectedUsage, MEMUNIT, NULL), + V(AccountingIntervalStart, ISOTIME, NULL), + V(AccountingSecondsActive, INTERVAL, NULL), + V(AccountingSecondsToReachSoftLimit,INTERVAL, NULL), + V(AccountingSoftLimitHitAt, ISOTIME, NULL), + V(AccountingBytesAtSoftLimit, MEMUNIT, NULL), + + VAR("EntryGuard", LINELIST_S, EntryGuards, NULL), + VAR("EntryGuardDownSince", LINELIST_S, EntryGuards, NULL), + VAR("EntryGuardUnlistedSince", LINELIST_S, EntryGuards, NULL), + VAR("EntryGuardAddedBy", LINELIST_S, EntryGuards, NULL), + VAR("EntryGuardPathBias", LINELIST_S, EntryGuards, NULL), + VAR("EntryGuardPathUseBias", LINELIST_S, EntryGuards, NULL), + V(EntryGuards, LINELIST_V, NULL), + + VAR("TransportProxy", LINELIST_S, TransportProxies, NULL), + V(TransportProxies, LINELIST_V, NULL), + + V(HidServRevCounter, LINELIST, NULL), + + V(BWHistoryReadEnds, ISOTIME, NULL), + V(BWHistoryReadInterval, UINT, "900"), + V(BWHistoryReadValues, CSV, ""), + V(BWHistoryReadMaxima, CSV, ""), + V(BWHistoryWriteEnds, ISOTIME, NULL), + V(BWHistoryWriteInterval, UINT, "900"), + V(BWHistoryWriteValues, CSV, ""), + V(BWHistoryWriteMaxima, CSV, ""), + V(BWHistoryDirReadEnds, ISOTIME, NULL), + V(BWHistoryDirReadInterval, UINT, "900"), + V(BWHistoryDirReadValues, CSV, ""), + V(BWHistoryDirReadMaxima, CSV, ""), + V(BWHistoryDirWriteEnds, ISOTIME, NULL), + V(BWHistoryDirWriteInterval, UINT, "900"), + V(BWHistoryDirWriteValues, CSV, ""), + V(BWHistoryDirWriteMaxima, CSV, ""), + + V(Guard, LINELIST, NULL), + + V(TorVersion, STRING, NULL), + + V(LastRotatedOnionKey, ISOTIME, NULL), + V(LastWritten, ISOTIME, NULL), + + V(TotalBuildTimes, UINT, NULL), + V(CircuitBuildAbandonedCount, UINT, "0"), + VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL), + VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL), + + END_OF_CONFIG_VARS +}; + +#undef VAR +#undef V + +static int or_state_validate(or_state_t *state, char **msg); + +static int or_state_validate_cb(void *old_options, void *options, + void *default_options, + int from_setconf, char **msg); + +static void or_state_free_cb(void *state); + +/** Magic value for or_state_t. */ +#define OR_STATE_MAGIC 0x57A73f57 + +/** "Extra" variable in the state that receives lines we can't parse. This + * lets us preserve options from versions of Tor newer than us. */ +static config_var_t state_extra_var = { + "__extra", CONFIG_TYPE_LINELIST, offsetof(or_state_t, ExtraLines), NULL + CONF_TEST_MEMBERS(or_state_t, LINELIST, ExtraLines) +}; + +/** Configuration format for or_state_t. */ +static const config_format_t state_format = { + sizeof(or_state_t), + OR_STATE_MAGIC, + offsetof(or_state_t, magic_), + state_abbrevs_, + NULL, + state_vars_, + or_state_validate_cb, + or_state_free_cb, + &state_extra_var, +}; + +/** Persistent serialized state. */ +static or_state_t *global_state = NULL; + +/** Return the persistent state struct for this Tor. */ +MOCK_IMPL(or_state_t *, +get_or_state, (void)) +{ + tor_assert(global_state); + return global_state; +} + +/** Return true iff we have loaded the global state for this Tor */ +int +or_state_loaded(void) +{ + return global_state != NULL; +} + +/** Return true if <b>line</b> is a valid state TransportProxy line. + * Return false otherwise. */ +static int +state_transport_line_is_valid(const char *line) +{ + smartlist_t *items = NULL; + char *addrport=NULL; + tor_addr_t addr; + uint16_t port = 0; + int r; + + items = smartlist_new(); + smartlist_split_string(items, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + + if (smartlist_len(items) != 2) { + log_warn(LD_CONFIG, "state: Not enough arguments in TransportProxy line."); + goto err; + } + + addrport = smartlist_get(items, 1); + if (tor_addr_port_lookup(addrport, &addr, &port) < 0) { + log_warn(LD_CONFIG, "state: Could not parse addrport."); + goto err; + } + + if (!port) { + log_warn(LD_CONFIG, "state: Transport line did not contain port."); + goto err; + } + + r = 1; + goto done; + + err: + r = 0; + + done: + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + return r; +} + +/** Return 0 if all TransportProxy lines in <b>state</b> are well + * formed. Otherwise, return -1. */ +static int +validate_transports_in_state(or_state_t *state) +{ + int broken = 0; + config_line_t *line; + + for (line = state->TransportProxies ; line ; line = line->next) { + tor_assert(!strcmp(line->key, "TransportProxy")); + if (!state_transport_line_is_valid(line->value)) + broken = 1; + } + + if (broken) + log_warn(LD_CONFIG, "state: State file seems to be broken."); + + return 0; +} + +static int +or_state_validate_cb(void *old_state, void *state, void *default_state, + int from_setconf, char **msg) +{ + /* We don't use these; only options do. Still, we need to match that + * signature. */ + (void) from_setconf; + (void) default_state; + (void) old_state; + + return or_state_validate(state, msg); +} + +static void +or_state_free_cb(void *state) +{ + or_state_free_(state); +} + +/** Return 0 if every setting in <b>state</b> is reasonable, and a + * permissible transition from <b>old_state</b>. Else warn and return -1. + * Should have no side effects, except for normalizing the contents of + * <b>state</b>. + */ +static int +or_state_validate(or_state_t *state, char **msg) +{ + if (entry_guards_parse_state(state, 0, msg)<0) + return -1; + + if (validate_transports_in_state(state)<0) + return -1; + + return 0; +} + +/** Replace the current persistent state with <b>new_state</b> */ +static int +or_state_set(or_state_t *new_state) +{ + char *err = NULL; + int ret = 0; + tor_assert(new_state); + config_free(&state_format, global_state); + global_state = new_state; + if (entry_guards_parse_state(global_state, 1, &err)<0) { + log_warn(LD_GENERAL,"%s",err); + tor_free(err); + ret = -1; + } + if (rep_hist_load_state(global_state, &err)<0) { + log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err); + tor_free(err); + ret = -1; + } + if (circuit_build_times_parse_state( + get_circuit_build_times_mutable(),global_state) < 0) { + ret = -1; + } + return ret; +} + +/** + * Save a broken state file to a backup location. + */ +static void +or_state_save_broken(char *fname) +{ + int i, res; + file_status_t status; + char *fname2 = NULL; + for (i = 0; i < 100; ++i) { + tor_asprintf(&fname2, "%s.%d", fname, i); + status = file_status(fname2); + if (status == FN_NOENT) + break; + tor_free(fname2); + } + if (i == 100) { + log_warn(LD_BUG, "Unable to parse state in \"%s\"; too many saved bad " + "state files to move aside. Discarding the old state file.", + fname); + res = unlink(fname); + if (res != 0) { + log_warn(LD_FS, + "Also couldn't discard old state file \"%s\" because " + "unlink() failed: %s", + fname, strerror(errno)); + } + } else { + log_warn(LD_BUG, "Unable to parse state in \"%s\". Moving it aside " + "to \"%s\". This could be a bug in Tor; please tell " + "the developers.", fname, fname2); + if (tor_rename(fname, fname2) < 0) {//XXXX sandbox prohibits + log_warn(LD_BUG, "Weirdly, I couldn't even move the state aside. The " + "OS gave an error of %s", strerror(errno)); + } + } + tor_free(fname2); +} + +STATIC or_state_t * +or_state_new(void) +{ + or_state_t *new_state = tor_malloc_zero(sizeof(or_state_t)); + new_state->magic_ = OR_STATE_MAGIC; + config_init(&state_format, new_state); + + return new_state; +} + +/** Reload the persistent state from disk, generating a new state as needed. + * Return 0 on success, less than 0 on failure. + */ +int +or_state_load(void) +{ + or_state_t *new_state = NULL; + char *contents = NULL, *fname; + char *errmsg = NULL; + int r = -1, badstate = 0; + + fname = get_datadir_fname("state"); + switch (file_status(fname)) { + case FN_FILE: + if (!(contents = read_file_to_str(fname, 0, NULL))) { + log_warn(LD_FS, "Unable to read state file \"%s\"", fname); + goto done; + } + break; + /* treat empty state files as if the file doesn't exist, and generate + * a new state file, overwriting the empty file in or_state_save() */ + case FN_NOENT: + case FN_EMPTY: + break; + case FN_ERROR: + case FN_DIR: + default: + log_warn(LD_GENERAL,"State file \"%s\" is not a file? Failing.", fname); + goto done; + } + new_state = or_state_new(); + if (contents) { + config_line_t *lines=NULL; + int assign_retval; + if (config_get_lines(contents, &lines, 0)<0) + goto done; + assign_retval = config_assign(&state_format, new_state, + lines, 0, &errmsg); + config_free_lines(lines); + if (assign_retval<0) + badstate = 1; + if (errmsg) { + log_warn(LD_GENERAL, "%s", errmsg); + tor_free(errmsg); + } + } + + if (!badstate && or_state_validate(new_state, &errmsg) < 0) + badstate = 1; + + if (errmsg) { + log_warn(LD_GENERAL, "%s", errmsg); + tor_free(errmsg); + } + + if (badstate && !contents) { + log_warn(LD_BUG, "Uh oh. We couldn't even validate our own default state." + " This is a bug in Tor."); + goto done; + } else if (badstate && contents) { + or_state_save_broken(fname); + + tor_free(contents); + config_free(&state_format, new_state); + + new_state = or_state_new(); + } else if (contents) { + log_info(LD_GENERAL, "Loaded state from \"%s\"", fname); + /* Warn the user if their clock has been set backwards, + * they could be tricked into using old consensuses */ + time_t apparent_skew = time(NULL) - new_state->LastWritten; + if (apparent_skew < 0) { + /* Initialize bootstrap event reporting because we might call + * clock_skew_warning() before the bootstrap state is + * initialized, causing an assertion failure. */ + control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0); + clock_skew_warning(NULL, (long)apparent_skew, 1, LD_GENERAL, + "local state file", fname); + } + } else { + log_info(LD_GENERAL, "Initialized state"); + } + if (or_state_set(new_state) == -1) { + or_state_save_broken(fname); + } + new_state = NULL; + if (!contents) { + global_state->next_write = 0; + or_state_save(time(NULL)); + } + r = 0; + + done: + tor_free(fname); + tor_free(contents); + if (new_state) + config_free(&state_format, new_state); + + return r; +} + +/** Did the last time we tried to write the state file fail? If so, we + * should consider disabling such features as preemptive circuit generation + * to compute circuit-build-time. */ +static int last_state_file_write_failed = 0; + +/** Return whether the state file failed to write last time we tried. */ +int +did_last_state_file_write_fail(void) +{ + return last_state_file_write_failed; +} + +/** If writing the state to disk fails, try again after this many seconds. */ +#define STATE_WRITE_RETRY_INTERVAL 3600 + +/** If we're a relay, how often should we checkpoint our state file even + * if nothing else dirties it? This will checkpoint ongoing stats like + * bandwidth used, per-country user stats, etc. */ +#define STATE_RELAY_CHECKPOINT_INTERVAL (12*60*60) + +/** Write the persistent state to disk. Return 0 for success, <0 on failure. */ +int +or_state_save(time_t now) +{ + char *state, *contents; + char tbuf[ISO_TIME_LEN+1]; + char *fname; + + tor_assert(global_state); + + if (global_state->next_write > now) + return 0; + + /* Call everything else that might dirty the state even more, in order + * to avoid redundant writes. */ + entry_guards_update_state(global_state); + rep_hist_update_state(global_state); + circuit_build_times_update_state(get_circuit_build_times(), global_state); + if (accounting_is_enabled(get_options())) + accounting_run_housekeeping(now); + + global_state->LastWritten = now; + + tor_free(global_state->TorVersion); + tor_asprintf(&global_state->TorVersion, "Tor %s", get_version()); + + state = config_dump(&state_format, NULL, global_state, 1, 0); + format_local_iso_time(tbuf, now); + tor_asprintf(&contents, + "# Tor state file last generated on %s local time\n" + "# Other times below are in UTC\n" + "# You *do not* need to edit this file.\n\n%s", + tbuf, state); + tor_free(state); + fname = get_datadir_fname("state"); + if (write_str_to_file(fname, contents, 0)<0) { + log_warn(LD_FS, "Unable to write state to file \"%s\"; " + "will try again later", fname); + last_state_file_write_failed = 1; + tor_free(fname); + tor_free(contents); + /* Try again after STATE_WRITE_RETRY_INTERVAL (or sooner, if the state + * changes sooner). */ + global_state->next_write = now + STATE_WRITE_RETRY_INTERVAL; + return -1; + } + + last_state_file_write_failed = 0; + log_info(LD_GENERAL, "Saved state to \"%s\"", fname); + tor_free(fname); + tor_free(contents); + + if (server_mode(get_options())) + global_state->next_write = now + STATE_RELAY_CHECKPOINT_INTERVAL; + else + global_state->next_write = TIME_MAX; + + return 0; +} + +/** Return the config line for transport <b>transport</b> in the current state. + * Return NULL if there is no config line for <b>transport</b>. */ +STATIC config_line_t * +get_transport_in_state_by_name(const char *transport) +{ + or_state_t *or_state = get_or_state(); + config_line_t *line; + config_line_t *ret = NULL; + smartlist_t *items = NULL; + + for (line = or_state->TransportProxies ; line ; line = line->next) { + tor_assert(!strcmp(line->key, "TransportProxy")); + + items = smartlist_new(); + smartlist_split_string(items, line->value, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + if (smartlist_len(items) != 2) /* broken state */ + goto done; + + if (!strcmp(smartlist_get(items, 0), transport)) { + ret = line; + goto done; + } + + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + items = NULL; + } + + done: + if (items) { + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + } + return ret; +} + +/** Return string containing the address:port part of the + * TransportProxy <b>line</b> for transport <b>transport</b>. + * If the line is corrupted, return NULL. */ +static const char * +get_transport_bindaddr(const char *line, const char *transport) +{ + char *line_tmp = NULL; + + if (strlen(line) < strlen(transport) + 2) { + goto broken_state; + } else { + /* line should start with the name of the transport and a space. + (for example, "obfs2 127.0.0.1:47245") */ + tor_asprintf(&line_tmp, "%s ", transport); + if (strcmpstart(line, line_tmp)) + goto broken_state; + + tor_free(line_tmp); + return (line+strlen(transport)+1); + } + + broken_state: + tor_free(line_tmp); + return NULL; +} + +/** Return a string containing the address:port that a proxy transport + * should bind on. The string is stored on the heap and must be freed + * by the caller of this function. */ +char * +get_stored_bindaddr_for_server_transport(const char *transport) +{ + char *default_addrport = NULL; + const char *stored_bindaddr = NULL; + config_line_t *line = NULL; + + { + /* See if the user explicitly asked for a specific listening + address for this transport. */ + char *conf_bindaddr = get_transport_bindaddr_from_config(transport); + if (conf_bindaddr) + return conf_bindaddr; + } + + line = get_transport_in_state_by_name(transport); + if (!line) /* Found no references in state for this transport. */ + goto no_bindaddr_found; + + stored_bindaddr = get_transport_bindaddr(line->value, transport); + if (stored_bindaddr) /* found stored bindaddr in state file. */ + return tor_strdup(stored_bindaddr); + + no_bindaddr_found: + /** If we didn't find references for this pluggable transport in the + state file, we should instruct the pluggable transport proxy to + listen on INADDR_ANY on a random ephemeral port. */ + tor_asprintf(&default_addrport, "%s:%s", fmt_addr32(INADDR_ANY), "0"); + return default_addrport; +} + +/** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to + state */ +void +save_transport_to_state(const char *transport, + const tor_addr_t *addr, uint16_t port) +{ + or_state_t *state = get_or_state(); + + char *transport_addrport=NULL; + + /** find where to write on the state */ + config_line_t **next, *line; + + /* see if this transport is already stored in state */ + config_line_t *transport_line = + get_transport_in_state_by_name(transport); + + if (transport_line) { /* if transport already exists in state... */ + const char *prev_bindaddr = /* get its addrport... */ + get_transport_bindaddr(transport_line->value, transport); + transport_addrport = tor_strdup(fmt_addrport(addr, port)); + + /* if transport in state has the same address as this one, life is good */ + if (!strcmp(prev_bindaddr, transport_addrport)) { + log_info(LD_CONFIG, "Transport seems to have spawned on its usual " + "address:port."); + goto done; + } else { /* if addrport in state is different than the one we got */ + log_info(LD_CONFIG, "Transport seems to have spawned on different " + "address:port. Let's update the state file with the new " + "address:port"); + tor_free(transport_line->value); /* free the old line */ + /* replace old addrport line with new line */ + tor_asprintf(&transport_line->value, "%s %s", transport, + fmt_addrport(addr, port)); + } + } else { /* never seen this one before; save it in state for next time */ + log_info(LD_CONFIG, "It's the first time we see this transport. " + "Let's save its address:port"); + next = &state->TransportProxies; + /* find the last TransportProxy line in the state and point 'next' + right after it */ + line = state->TransportProxies; + while (line) { + next = &(line->next); + line = line->next; + } + + /* allocate space for the new line and fill it in */ + *next = line = tor_malloc_zero(sizeof(config_line_t)); + line->key = tor_strdup("TransportProxy"); + tor_asprintf(&line->value, "%s %s", transport, fmt_addrport(addr, port)); + } + + if (!get_options()->AvoidDiskWrites) + or_state_mark_dirty(state, 0); + + done: + tor_free(transport_addrport); +} + +/** Change the next_write time of <b>state</b> to <b>when</b>, unless the + * state is already scheduled to be written to disk earlier than <b>when</b>. + */ +void +or_state_mark_dirty(or_state_t *state, time_t when) +{ + if (state->next_write > when) { + state->next_write = when; + reschedule_or_state_save(); + } +} + +STATIC void +or_state_free_(or_state_t *state) +{ + if (!state) + return; + + config_free(&state_format, state); +} + +void +or_state_free_all(void) +{ + or_state_free(global_state); + global_state = NULL; +} diff --git a/src/app/config/statefile.h b/src/app/config/statefile.h new file mode 100644 index 0000000000..1950078450 --- /dev/null +++ b/src/app/config/statefile.h @@ -0,0 +1,36 @@ +/* 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 statefile.h + * + * \brief Header for statefile.c + */ + +#ifndef TOR_STATEFILE_H +#define TOR_STATEFILE_H + +MOCK_DECL(or_state_t *,get_or_state,(void)); +int did_last_state_file_write_fail(void); +int or_state_save(time_t now); + +void save_transport_to_state(const char *transport_name, + const tor_addr_t *addr, uint16_t port); +char *get_stored_bindaddr_for_server_transport(const char *transport); +int or_state_load(void); +int or_state_loaded(void); +void or_state_free_all(void); +void or_state_mark_dirty(or_state_t *state, time_t when); + +#ifdef STATEFILE_PRIVATE +STATIC struct config_line_t *get_transport_in_state_by_name( + const char *transport); +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(TOR_STATEFILE_H) */ |