diff options
Diffstat (limited to 'src/or/config.c')
-rw-r--r-- | src/or/config.c | 2727 |
1 files changed, 2199 insertions, 528 deletions
diff --git a/src/or/config.c b/src/or/config.c index fbbd9029f7..24edc4d793 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -33,13 +33,18 @@ #include "rendservice.h" #include "rephist.h" #include "router.h" +#include "util.h" #include "routerlist.h" -#ifdef MS_WINDOWS +#include "transports.h" +#ifdef _WIN32 #include <shlobj.h> #endif #include "procmon.h" +/* From main.c */ +extern int quiet_level; + /** Enumeration of types which option values can take */ typedef enum config_type_t { CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ @@ -48,9 +53,13 @@ typedef enum config_type_t { 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 GMT. */ CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and * optional whitespace. */ @@ -80,6 +89,10 @@ typedef struct config_abbrev_t { /** 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(ExitNode), PLURAL(EntryNode), PLURAL(ExcludeNode), @@ -173,11 +186,15 @@ static config_var_t _option_vars[] = { V(AlternateHSAuthority, LINELIST, NULL), V(AssumeReachable, BOOL, "0"), V(AuthDirBadDir, LINELIST, NULL), + V(AuthDirBadDirCCs, CSV, ""), V(AuthDirBadExit, LINELIST, NULL), + V(AuthDirBadExitCCs, CSV, ""), V(AuthDirInvalid, LINELIST, NULL), - V(AuthDirFastGuarantee, MEMUNIT, "20 KB"), + V(AuthDirInvalidCCs, CSV, ""), + V(AuthDirFastGuarantee, MEMUNIT, "100 KB"), V(AuthDirGuardBWGuarantee, MEMUNIT, "250 KB"), V(AuthDirReject, LINELIST, NULL), + V(AuthDirRejectCCs, CSV, ""), V(AuthDirRejectUnlisted, BOOL, "0"), V(AuthDirListBadDirs, BOOL, "0"), V(AuthDirListBadExits, BOOL, "0"), @@ -201,15 +218,17 @@ static config_var_t _option_vars[] = { V(CircuitStreamTimeout, INTERVAL, "0"), V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/ V(ClientDNSRejectInternalAddresses, BOOL,"1"), - V(ClientRejectInternalAddresses, BOOL, "1"), V(ClientOnly, BOOL, "0"), + V(ClientRejectInternalAddresses, BOOL, "1"), + V(ClientTransportPlugin, LINELIST, NULL), V(ConsensusParams, STRING, NULL), V(ConnLimit, UINT, "1000"), + V(ConnDirectionStatistics, BOOL, "0"), V(ConstrainedSockets, BOOL, "0"), V(ConstrainedSockSize, MEMUNIT, "8192"), V(ContactInfo, STRING, NULL), V(ControlListenAddress, LINELIST, NULL), - V(ControlPort, PORT, "0"), + V(ControlPort, LINELIST, NULL), V(ControlPortFileGroupReadable,BOOL, "0"), V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), @@ -217,14 +236,16 @@ static config_var_t _option_vars[] = { V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), V(CookieAuthFile, STRING, NULL), + V(CountPrivateBandwidth, BOOL, "0"), V(DataDirectory, FILENAME, NULL), OBSOLETE("DebugLogFile"), - V(DirAllowPrivateAddresses, BOOL, NULL), + V(DisableNetwork, BOOL, "0"), + V(DirAllowPrivateAddresses, BOOL, "0"), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), V(DirListenAddress, LINELIST, NULL), OBSOLETE("DirFetchPeriod"), V(DirPolicy, LINELIST, NULL), - V(DirPort, PORT, "0"), + V(DirPort, LINELIST, NULL), V(DirPortFrontPage, FILENAME, NULL), OBSOLETE("DirPostPeriod"), OBSOLETE("DirRecordUsageByCountry"), @@ -234,7 +255,10 @@ static config_var_t _option_vars[] = { V(DirReqStatistics, BOOL, "1"), VAR("DirServer", LINELIST, DirServers, NULL), V(DisableAllSwap, BOOL, "0"), - V(DNSPort, PORT, "0"), + V(DisableDebuggerAttachment, BOOL, "1"), + V(DisableIOCP, BOOL, "1"), + V(DynamicDHGroups, BOOL, "1"), + V(DNSPort, LINELIST, NULL), V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), V(EnforceDistinctSubnets, BOOL, "1"), @@ -265,7 +289,7 @@ static config_var_t _option_vars[] = { V(FetchHidServDescriptors, BOOL, "1"), V(FetchUselessDescriptors, BOOL, "0"), V(FetchV2Networkstatus, BOOL, "0"), -#ifdef WIN32 +#ifdef _WIN32 V(GeoIPFile, FILENAME, "<default>"), #else V(GeoIPFile, FILENAME, @@ -275,6 +299,7 @@ static config_var_t _option_vars[] = { BOOL, "0"), OBSOLETE("Group"), V(HardwareAccel, BOOL, "0"), + V(HeartbeatPeriod, INTERVAL, "6 hours"), V(AccelName, STRING, NULL), V(AccelDir, FILENAME, NULL), V(HashedControlPassword, LINELIST, NULL), @@ -289,10 +314,13 @@ static config_var_t _option_vars[] = { V(HidServAuth, LINELIST, NULL), V(HSAuthoritativeDir, BOOL, "0"), OBSOLETE("HSAuthorityRecordStats"), + V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"), + V(CloseHSServiceRendCircuitsImmediatelyOnTimeout, BOOL, "0"), V(HTTPProxy, STRING, NULL), V(HTTPProxyAuthenticator, STRING, NULL), V(HTTPSProxy, STRING, NULL), V(HTTPSProxyAuthenticator, STRING, NULL), + VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL), V(Socks4Proxy, STRING, NULL), V(Socks5Proxy, STRING, NULL), V(Socks5ProxyUsername, STRING, NULL), @@ -304,32 +332,37 @@ static config_var_t _option_vars[] = { OBSOLETE("LinkPadding"), OBSOLETE("LogLevel"), OBSOLETE("LogFile"), + V(LogTimeGranularity, MSEC_INTERVAL, "1 second"), V(LongLivedPorts, CSV, - "21,22,706,1863,5050,5190,5222,5223,6667,6697,8300"), + "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(MaxOnionsPending, UINT, "100"), OBSOLETE("MonthlyAccountingStart"), V(MyFamily, STRING, NULL), V(NewCircuitPeriod, INTERVAL, "30 seconds"), VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"), V(NATDListenAddress, LINELIST, NULL), - V(NATDPort, PORT, "0"), + V(NATDPort, LINELIST, NULL), V(Nickname, STRING, NULL), V(WarnUnsafeSocks, BOOL, "1"), OBSOLETE("NoPublish"), VAR("NodeFamily", LINELIST, NodeFamilies, NULL), - V(NumCPUs, UINT, "1"), + V(NumCPUs, UINT, "0"), V(NumEntryGuards, UINT, "3"), V(ORListenAddress, LINELIST, NULL), - V(ORPort, PORT, "0"), + V(ORPort, LINELIST, NULL), V(OutboundBindAddress, STRING, NULL), OBSOLETE("PathlenCoinWeight"), V(PerConnBWBurst, MEMUNIT, "0"), V(PerConnBWRate, MEMUNIT, "0"), V(PidFile, STRING, NULL), V(TestingTorNetwork, BOOL, "0"), + V(OptimisticData, AUTOBOOL, "auto"), + V(PortForwarding, BOOL, "0"), + V(PortForwardingHelper, FILENAME, "tor-fw-helper"), V(PreferTunneledDirConns, BOOL, "1"), V(ProtocolWarnings, BOOL, "0"), V(PublishServerDescriptor, CSV, "1"), @@ -341,7 +374,7 @@ static config_var_t _option_vars[] = { V(RecommendedClientVersions, LINELIST, NULL), V(RecommendedServerVersions, LINELIST, NULL), OBSOLETE("RedirectExit"), - V(RefuseUnknownExits, STRING, "auto"), + V(RefuseUnknownExits, AUTOBOOL, "auto"), V(RejectPlaintextPorts, CSV, ""), V(RelayBandwidthBurst, MEMUNIT, "0"), V(RelayBandwidthRate, MEMUNIT, "0"), @@ -366,23 +399,27 @@ static config_var_t _option_vars[] = { V(ShutdownWaitLength, INTERVAL, "30 seconds"), V(SocksListenAddress, LINELIST, NULL), V(SocksPolicy, LINELIST, NULL), - V(SocksPort, PORT, "9050"), + V(SocksPort, LINELIST, NULL), V(SocksTimeout, INTERVAL, "2 minutes"), OBSOLETE("StatusFetchPeriod"), V(StrictNodes, BOOL, "0"), OBSOLETE("SysLog"), V(TestSocks, BOOL, "0"), OBSOLETE("TestVia"), + V(TokenBucketRefillInterval, MSEC_INTERVAL, "100 msec"), + V(Tor2webMode, BOOL, "0"), V(TrackHostExits, CSV, NULL), V(TrackHostExitsExpire, INTERVAL, "30 minutes"), OBSOLETE("TrafficShaping"), V(TransListenAddress, LINELIST, NULL), - V(TransPort, PORT, "0"), + V(TransPort, LINELIST, NULL), V(TunnelDirConns, BOOL, "1"), V(UpdateBridgesFromAuthority, BOOL, "0"), V(UseBridges, BOOL, "0"), V(UseEntryGuards, BOOL, "1"), + V(UseMicrodescriptors, AUTOBOOL, "auto"), V(User, STRING, NULL), + V(UserspaceIOCPBuffers, BOOL, "0"), VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"), VAR("V2AuthoritativeDirectory",BOOL, V2AuthoritativeDir, "0"), VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"), @@ -398,6 +435,7 @@ static config_var_t _option_vars[] = { VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"), V(VirtualAddrNetwork, STRING, "127.192.0.0/10"), V(WarnPlaintextPorts, CSV, "23,109,110,143"), + V(_UseFilteringSSLBufferevents, BOOL, "0"), VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"), VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"), VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"), @@ -414,7 +452,7 @@ static config_var_t _option_vars[] = { /** Override default values with these if the user sets the TestingTorNetwork * option. */ -static config_var_t testing_tor_network_defaults[] = { +static const config_var_t testing_tor_network_defaults[] = { V(ServerDNSAllowBrokenConfig, BOOL, "1"), V(DirAllowPrivateAddresses, BOOL, "1"), V(EnforceDistinctSubnets, BOOL, "0"), @@ -423,6 +461,7 @@ static config_var_t testing_tor_network_defaults[] = { V(AuthDirMaxServersPerAuthAddr,UINT, "0"), V(ClientDNSRejectInternalAddresses, BOOL,"0"), V(ClientRejectInternalAddresses, BOOL, "0"), + V(CountPrivateBandwidth, BOOL, "1"), V(ExitPolicyRejectPrivate, BOOL, "0"), V(V3AuthVotingInterval, INTERVAL, "5 minutes"), V(V3AuthVoteDelay, INTERVAL, "20 seconds"), @@ -434,6 +473,7 @@ static config_var_t testing_tor_network_defaults[] = { V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"), V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"), V(_UsingTestNetworkDefaults, BOOL, "1"), + { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } }; #undef VAR @@ -444,6 +484,8 @@ static config_var_t testing_tor_network_defaults[] = { /** 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), @@ -459,6 +501,9 @@ static config_var_t _state_vars[] = { VAR("EntryGuardAddedBy", LINELIST_S, EntryGuards, NULL), V(EntryGuards, LINELIST_V, NULL), + VAR("TransportProxy", LINELIST_S, TransportProxies, NULL), + V(TransportProxies, LINELIST_V, NULL), + V(BWHistoryReadEnds, ISOTIME, NULL), V(BWHistoryReadInterval, UINT, "900"), V(BWHistoryReadValues, CSV, ""), @@ -485,7 +530,6 @@ static config_var_t _state_vars[] = { V(CircuitBuildAbandonedCount, UINT, "0"), VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL), VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL), - { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } }; @@ -531,55 +575,65 @@ typedef struct { *(uint32_t*)STRUCT_VAR_P(cfg,fmt->magic_offset)); \ STMT_END -#ifdef MS_WINDOWS +#ifdef _WIN32 static char *get_windows_conf_root(void); #endif static void config_line_append(config_line_t **lst, const char *key, const char *val); -static void option_clear(config_format_t *fmt, or_options_t *options, - config_var_t *var); -static void option_reset(config_format_t *fmt, or_options_t *options, - config_var_t *var, int use_defaults); -static void config_free(config_format_t *fmt, void *options); +static void option_clear(const config_format_t *fmt, or_options_t *options, + const config_var_t *var); +static void option_reset(const config_format_t *fmt, or_options_t *options, + const config_var_t *var, int use_defaults); +static void config_free(const config_format_t *fmt, void *options); static int config_lines_eq(config_line_t *a, config_line_t *b); -static int option_is_same(config_format_t *fmt, - or_options_t *o1, or_options_t *o2, +static int option_is_same(const config_format_t *fmt, + const or_options_t *o1, const or_options_t *o2, const char *name); -static or_options_t *options_dup(config_format_t *fmt, or_options_t *old); -static int options_validate(or_options_t *old_options, or_options_t *options, +static or_options_t *options_dup(const config_format_t *fmt, + const or_options_t *old); +static int options_validate(or_options_t *old_options, + or_options_t *options, int from_setconf, char **msg); -static int options_act_reversible(or_options_t *old_options, char **msg); -static int options_act(or_options_t *old_options); -static int options_transition_allowed(or_options_t *old, or_options_t *new, +static int options_act_reversible(const or_options_t *old_options, char **msg); +static int options_act(const or_options_t *old_options); +static int options_transition_allowed(const or_options_t *old, + const or_options_t *new, char **msg); -static int options_transition_affects_workers(or_options_t *old_options, - or_options_t *new_options); -static int options_transition_affects_descriptor(or_options_t *old_options, - or_options_t *new_options); +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 check_nickname_list(const char *lst, const char *name, char **msg); -static void config_register_addressmaps(or_options_t *options); static int parse_bridge_line(const char *line, int validate_only); +static int parse_client_transport_line(const char *line, int validate_only); + +static int parse_server_transport_line(const char *line, int validate_only); static int parse_dir_server_line(const char *line, - authority_type_t required_type, + dirinfo_type_t required_type, int validate_only); +static void port_cfg_free(port_cfg_t *port); +static int parse_ports(const or_options_t *options, int validate_only, + char **msg_out, int *n_ports_out); +static int check_server_ports(const smartlist_t *ports, + const or_options_t *options); + static int validate_data_directory(or_options_t *options); -static int write_configuration_file(const char *fname, or_options_t *options); -static config_line_t *get_assigned_option(config_format_t *fmt, - void *options, const char *key, - int escape_val); -static void config_init(config_format_t *fmt, void *options); +static int write_configuration_file(const char *fname, + const or_options_t *options); +static config_line_t *get_assigned_option(const config_format_t *fmt, + const void *options, const char *key, + int escape_val); +static void config_init(const config_format_t *fmt, void *options); static int or_state_validate(or_state_t *old_options, or_state_t *options, int from_setconf, char **msg); static int or_state_load(void); static int options_init_logs(or_options_t *options, int validate_only); -static int is_listening_on_low_port(int port_option, - const config_line_t *listen_options); - 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 init_libevent(void); +static void init_libevent(const or_options_t *options); static int opt_streq(const char *s1, const char *s2); /** Magic value for or_options_t. */ @@ -606,7 +660,7 @@ static config_var_t state_extra_var = { }; /** Configuration format for or_state_t. */ -static config_format_t state_format = { +static const config_format_t state_format = { sizeof(or_state_t), OR_STATE_MAGIC, STRUCT_OFFSET(or_state_t, _magic), @@ -622,14 +676,20 @@ static config_format_t state_format = { /** Command-line and config-file options. */ static or_options_t *global_options = NULL; +/** DOCDOC */ +static or_options_t *global_default_options = NULL; /** Name of most recently read torrc file. */ static char *torrc_fname = NULL; +/** DOCDOC */ +static char *torrc_defaults_fname; /** Persistent serialized state. */ static or_state_t *global_state = NULL; /** Configuration Options set by command line. */ static config_line_t *global_cmdline_options = NULL; /** 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; /** Return the contents of our frontpage string, or NULL if not configured. */ const char * @@ -640,7 +700,7 @@ get_dirportfrontpage(void) /** Allocate an empty configuration object of a given format type. */ static void * -config_alloc(config_format_t *fmt) +config_alloc(const config_format_t *fmt) { void *opts = tor_malloc_zero(fmt->size); *(uint32_t*)STRUCT_VAR_P(opts, fmt->magic_offset) = fmt->magic; @@ -650,12 +710,19 @@ config_alloc(config_format_t *fmt) /** Return the currently configured options. */ or_options_t * -get_options(void) +get_options_mutable(void) { tor_assert(global_options); return global_options; } +/** Returns the currently configured options */ +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. @@ -663,6 +730,9 @@ get_options(void) 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 @@ -677,7 +747,34 @@ set_options(or_options_t *new_val, char **msg) "Acting on config options left us in a broken state. Dying."); exit(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) { + 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 (!option_is_same(&options_format, new_val, old_options, var_name)) { + line = get_assigned_option(&options_format, new_val, var_name, 1); + + if (line) { + for (; line; line = line->next) { + smartlist_add(elements, line->key); + smartlist_add(elements, line->value); + } + } else { + smartlist_add(elements, (char*)options_format.vars[i].name); + smartlist_add(elements, NULL); + } + } + } + control_event_conf_changed(elements); + smartlist_free(elements); + } config_free(&options_format, old_options); return 0; @@ -686,22 +783,20 @@ set_options(or_options_t *new_val, char **msg) extern const char tor_git_revision[]; /* from tor_main.c */ /** The version of this Tor process, as parsed. */ -static char *_version = NULL; +static char *the_tor_version = NULL; /** Return the current Tor version. */ const char * get_version(void) { - if (_version == NULL) { + if (the_tor_version == NULL) { if (strlen(tor_git_revision)) { - size_t len = strlen(VERSION)+strlen(tor_git_revision)+16; - _version = tor_malloc(len); - tor_snprintf(_version, len, "%s (git-%s)", VERSION, tor_git_revision); + tor_asprintf(&the_tor_version, "%s (git-%s)", VERSION, tor_git_revision); } else { - _version = tor_strdup(VERSION); + the_tor_version = tor_strdup(VERSION); } } - return _version; + return the_tor_version; } /** Release additional memory allocated in options @@ -713,6 +808,11 @@ or_options_free(or_options_t *options) return; routerset_free(options->_ExcludeExitNodesUnion); + if (options->NodeFamilySets) { + SMARTLIST_FOREACH(options->NodeFamilySets, routerset_t *, + rs, routerset_free(rs)); + smartlist_free(options->NodeFamilySets); + } tor_free(options->_BridgePassword_AuthDigest); config_free(&options_format, options); } @@ -724,6 +824,8 @@ config_free_all(void) { or_options_free(global_options); global_options = NULL; + or_options_free(global_default_options); + global_default_options = NULL; config_free(&state_format, global_state); global_state = NULL; @@ -731,8 +833,16 @@ config_free_all(void) config_free_lines(global_cmdline_options); global_cmdline_options = NULL; + if (configured_ports) { + SMARTLIST_FOREACH(configured_ports, + port_cfg_t *, p, tor_free(p)); + smartlist_free(configured_ports); + configured_ports = NULL; + } + tor_free(torrc_fname); - tor_free(_version); + tor_free(torrc_defaults_fname); + tor_free(the_tor_version); tor_free(global_dirfrontpagecontents); } @@ -796,7 +906,7 @@ escaped_safe_str(const char *address) /** Add the default directory authorities directly into the trusted dir list, * but only add them insofar as they share bits with <b>type</b>. */ static void -add_default_trusted_dir_authorities(authority_type_t type) +add_default_trusted_dir_authorities(dirinfo_type_t type) { int i; const char *dirservers[] = { @@ -870,16 +980,16 @@ validate_dir_authorities(or_options_t *options, or_options_t *old_options) /* Now go through the four ways you can configure an alternate * set of directory authorities, and make sure none are broken. */ for (cl = options->DirServers; cl; cl = cl->next) - if (parse_dir_server_line(cl->value, NO_AUTHORITY, 1)<0) + if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0) return -1; for (cl = options->AlternateBridgeAuthority; cl; cl = cl->next) - if (parse_dir_server_line(cl->value, NO_AUTHORITY, 1)<0) + if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0) return -1; for (cl = options->AlternateDirAuthority; cl; cl = cl->next) - if (parse_dir_server_line(cl->value, NO_AUTHORITY, 1)<0) + if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0) return -1; for (cl = options->AlternateHSAuthority; cl; cl = cl->next) - if (parse_dir_server_line(cl->value, NO_AUTHORITY, 1)<0) + if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0) return -1; return 0; } @@ -888,8 +998,8 @@ validate_dir_authorities(or_options_t *options, or_options_t *old_options) * as appropriate. */ static int -consider_adding_dir_authorities(or_options_t *options, - or_options_t *old_options) +consider_adding_dir_authorities(const or_options_t *options, + const or_options_t *old_options) { config_line_t *cl; int need_to_update = @@ -910,27 +1020,28 @@ consider_adding_dir_authorities(or_options_t *options, if (!options->DirServers) { /* then we may want some of the defaults */ - authority_type_t type = NO_AUTHORITY; + dirinfo_type_t type = NO_DIRINFO; if (!options->AlternateBridgeAuthority) - type |= BRIDGE_AUTHORITY; + type |= BRIDGE_DIRINFO; if (!options->AlternateDirAuthority) - type |= V1_AUTHORITY | V2_AUTHORITY | V3_AUTHORITY; + type |= V1_DIRINFO | V2_DIRINFO | V3_DIRINFO | EXTRAINFO_DIRINFO | + MICRODESC_DIRINFO; if (!options->AlternateHSAuthority) - type |= HIDSERV_AUTHORITY; + type |= HIDSERV_DIRINFO; add_default_trusted_dir_authorities(type); } for (cl = options->DirServers; cl; cl = cl->next) - if (parse_dir_server_line(cl->value, NO_AUTHORITY, 0)<0) + if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0) return -1; for (cl = options->AlternateBridgeAuthority; cl; cl = cl->next) - if (parse_dir_server_line(cl->value, NO_AUTHORITY, 0)<0) + if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0) return -1; for (cl = options->AlternateDirAuthority; cl; cl = cl->next) - if (parse_dir_server_line(cl->value, NO_AUTHORITY, 0)<0) + if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0) return -1; for (cl = options->AlternateHSAuthority; cl; cl = cl->next) - if (parse_dir_server_line(cl->value, NO_AUTHORITY, 0)<0) + if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0) return -1; return 0; } @@ -942,12 +1053,12 @@ consider_adding_dir_authorities(or_options_t *options, * Return 0 if all goes well, return -1 if things went badly. */ static int -options_act_reversible(or_options_t *old_options, char **msg) +options_act_reversible(const or_options_t *old_options, char **msg) { - smartlist_t *new_listeners = smartlist_create(); - smartlist_t *replaced_listeners = smartlist_create(); + smartlist_t *new_listeners = smartlist_new(); + smartlist_t *replaced_listeners = smartlist_new(); static int libevent_initialized = 0; - or_options_t *options = get_options(); + or_options_t *options = get_options_mutable(); int running_tor = options->command == CMD_RUN_TOR; int set_conn_limit = 0; int r = -1; @@ -976,6 +1087,7 @@ options_act_reversible(or_options_t *old_options, char **msg) #endif if (running_tor) { + int n_ports=0; /* We need to set the connection limit before we can open the listeners. */ if (set_max_file_descriptors((unsigned)options->ConnLimit, &options->_ConnLimit) < 0) { @@ -987,18 +1099,37 @@ options_act_reversible(or_options_t *old_options, char **msg) /* Set up libevent. (We need to do this before we can register the * listeners as listeners.) */ if (running_tor && !libevent_initialized) { - init_libevent(); + init_libevent(options); libevent_initialized = 1; } + /* Adjust the port configuration so we can launch listeners. */ + if (parse_ports(options, 0, msg, &n_ports)) { + 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. */ + * ports under 1024.) We don't want to rebind if we're hibernating. If + * networking is disabled, this will close all but the control listeners, + * but disable those. */ if (!we_are_hibernating()) { if (retry_all_listeners(replaced_listeners, new_listeners) < 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(); + } } #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) @@ -1043,10 +1174,9 @@ options_act_reversible(or_options_t *old_options, char **msg) /* Write control ports to disk as appropriate */ control_ports_write_to_file(); - if (directory_caches_v2_dir_info(options)) { - size_t len = strlen(options->DataDirectory)+32; - char *fn = tor_malloc(len); - tor_snprintf(fn, len, "%s"PATH_SEPARATOR"cached-status", + if (directory_caches_v2_dir_info(options)) { + char *fn = NULL; + tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-status", options->DataDirectory); if (check_private_dir(fn, running_tor ? CPD_CREATE : CPD_CHECK, options->User) < 0) { @@ -1119,7 +1249,7 @@ options_act_reversible(or_options_t *old_options, char **msg) /** 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(or_options_t *options, const char **reason_out) +options_need_geoip_info(const or_options_t *options, const char **reason_out) { int bridge_usage = options->BridgeRelay && options->BridgeRecordUsageByCountry; @@ -1144,7 +1274,7 @@ options_need_geoip_info(or_options_t *options, const char **reason_out) /** Return the bandwidthrate that we are going to report to the authorities * based on the config options. */ uint32_t -get_effective_bwrate(or_options_t *options) +get_effective_bwrate(const or_options_t *options) { uint64_t bw = options->BandwidthRate; if (bw > options->MaxAdvertisedBandwidth) @@ -1158,7 +1288,7 @@ get_effective_bwrate(or_options_t *options) /** Return the bandwidthburst that we are going to report to the authorities * based on the config options. */ uint32_t -get_effective_bwburst(or_options_t *options) +get_effective_bwburst(const or_options_t *options) { uint64_t bw = options->BandwidthBurst; if (options->RelayBandwidthBurst > 0 && bw > options->RelayBandwidthBurst) @@ -1167,6 +1297,24 @@ get_effective_bwburst(or_options_t *options) return (uint32_t)bw; } +/** Return True if any changes from <b>old_options</b> to + * <b>new_options</b> needs us to refresh our TLS context. */ +static int +options_transition_requires_fresh_tls_context(const or_options_t *old_options, + const or_options_t *new_options) +{ + tor_assert(new_options); + + if (!old_options) + return 0; + + if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) { + return 1; + } + + 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. @@ -1177,15 +1325,39 @@ get_effective_bwburst(or_options_t *options) * here yet. Some is still in do_hup() and other places. */ static int -options_act(or_options_t *old_options) +options_act(const or_options_t *old_options) { config_line_t *cl; - or_options_t *options = get_options(); + or_options_t *options = get_options_mutable(); int running_tor = options->command == CMD_RUN_TOR; char *msg; const int transition_affects_workers = old_options && options_transition_affects_workers(old_options, options); + /* 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(); + if (warned_debugger_attach && ok == 1) { + log_notice(LD_CONFIG, "Disabled attaching debuggers for unprivileged " + "users."); + } + 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; + } + } + if (running_tor && !have_lockfile()) { if (try_locking(options, 1) < 0) return -1; @@ -1194,6 +1366,28 @@ options_act(or_options_t *old_options) if (consider_adding_dir_authorities(options, old_options) < 0) return -1; +#ifdef NON_ANONYMOUS_MODE_ENABLED + log(LOG_WARN, LD_GENERAL, "This copy of Tor was compiled to run in a " + "non-anonymous mode. It will provide NO ANONYMITY."); +#endif + +#ifdef ENABLE_TOR2WEB_MODE + if (!options->Tor2webMode) { + log_err(LD_CONFIG, "This copy of Tor was compiled to run in " + "'tor2web mode'. It can only be run with the Tor2webMode torrc " + "option enabled."); + return -1; + } +#else + if (options->Tor2webMode) { + log_err(LD_CONFIG, "This copy of Tor was not compiled to run in " + "'tor2web mode'. It cannot be run with the Tor2webMode torrc " + "option enabled. To enable Tor2webMode recompile with the " + "--enable-tor2webmode option."); + return -1; + } +#endif + if (options->Bridges) { mark_bridge_list(); for (cl = options->Bridges; cl; cl = cl->next) { @@ -1225,6 +1419,32 @@ options_act(or_options_t *old_options) rep_hist_load_mtbf_data(time(NULL)); } + mark_transport_list(); + pt_prepare_proxy_list_for_config_read(); + if (options->ClientTransportPlugin) { + for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { + if (parse_client_transport_line(cl->value, 0)<0) { + log_warn(LD_BUG, + "Previously validated ClientTransportPlugin line " + "could not be added!"); + return -1; + } + } + } + + if (options->ServerTransportPlugin) { + for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { + if (parse_server_transport_line(cl->value, 0)<0) { + log_warn(LD_BUG, + "Previously validated ServerTransportPlugin line " + "could not be added!"); + return -1; + } + } + } + sweep_transport_list(); + sweep_proxy_list(); + /* 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) @@ -1236,6 +1456,29 @@ options_act(or_options_t *old_options) finish_daemon(options->DataDirectory); } + /* If needed, generate a new TLS DH prime according to the current torrc. */ + if (server_mode(options)) { + if (!old_options) { + if (options->DynamicDHGroups) { + char *fname = get_datadir_fname2("keys", "dynamic_dh_params"); + crypto_set_tls_dh_prime(fname); + tor_free(fname); + } else { + crypto_set_tls_dh_prime(NULL); + } + } else { + if (options->DynamicDHGroups && !old_options->DynamicDHGroups) { + char *fname = get_datadir_fname2("keys", "dynamic_dh_params"); + crypto_set_tls_dh_prime(fname); + tor_free(fname); + } else if (!options->DynamicDHGroups && old_options->DynamicDHGroups) { + crypto_set_tls_dh_prime(NULL); + } + } + } else { /* clients don't need a dynamic DH prime. */ + crypto_set_tls_dh_prime(NULL); + } + /* 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 || @@ -1245,6 +1488,13 @@ options_act(or_options_t *old_options) log_warn(LD_BUG,"Error initializing keys; exiting"); return -1; } + } else if (old_options && + options_transition_requires_fresh_tls_context(old_options, + options)) { + if (router_initialize_tls_context() < 0) { + log_warn(LD_BUG,"Error initializing TLS context."); + return -1; + } } /* Write our PID to the PID file. If we do not have write permissions we @@ -1284,17 +1534,16 @@ options_act(or_options_t *old_options) if (accounting_is_enabled(options)) configure_accounting(time(NULL)); - /* parse RefuseUnknownExits tristate */ - if (!strcmp(options->RefuseUnknownExits, "0")) - options->RefuseUnknownExits_ = 0; - else if (!strcmp(options->RefuseUnknownExits, "1")) - options->RefuseUnknownExits_ = 1; - else if (!strcmp(options->RefuseUnknownExits, "auto")) - options->RefuseUnknownExits_ = -1; - else { - /* Should have caught this in options_validate */ - return -1; - } +#ifdef USE_BUFFEREVENTS + /* If we're using the bufferevents implementation and our rate limits + * changed, we need to tell the rate-limiting system about it. */ + if (!old_options || + old_options->BandwidthRate != options->BandwidthRate || + old_options->BandwidthBurst != options->BandwidthBurst || + old_options->RelayBandwidthRate != options->RelayBandwidthRate || + old_options->RelayBandwidthBurst != options->RelayBandwidthBurst) + connection_bucket_init(); +#endif /* Change the cell EWMA settings */ cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus()); @@ -1373,7 +1622,7 @@ options_act(or_options_t *old_options) int was_relay = 0; if (options->BridgeRelay) { time_t int_start = time(NULL); - if (old_options->ORPort == options->ORPort) { + if (config_lines_eq(old_options->ORPort, options->ORPort)) { int_start += RELAY_BRIDGE_STATS_DELAY; was_relay = 1; } @@ -1418,13 +1667,11 @@ options_act(or_options_t *old_options) * understand prefixes somehow. -NM */ /* XXXX023 Reload GeoIPFile on SIGHUP. -NM */ char *actual_fname = tor_strdup(options->GeoIPFile); -#ifdef WIN32 +#ifdef _WIN32 if (!strcmp(actual_fname, "<default>")) { const char *conf_root = get_windows_conf_root(); - size_t len = strlen(conf_root)+16; tor_free(actual_fname); - actual_fname = tor_malloc(len+1); - tor_snprintf(actual_fname, len, "%s\\geoip", conf_root); + tor_asprintf(&actual_fname, "%s\\geoip", conf_root); } #endif geoip_load_file(actual_fname, options); @@ -1432,7 +1679,9 @@ options_act(or_options_t *old_options) } if (options->CellStatistics || options->DirReqStatistics || - options->EntryStatistics || options->ExitPortStatistics) { + options->EntryStatistics || options->ExitPortStatistics || + options->ConnDirectionStatistics || + options->BridgeAuthoritativeDir) { time_t now = time(NULL); int print_notice = 0; @@ -1456,10 +1705,12 @@ options_act(or_options_t *old_options) print_notice = 1; } else { options->DirReqStatistics = 0; - log_notice(LD_CONFIG, "Configured to measure directory request " - "statistics, but no GeoIP database found! " - "Please specify a GeoIP database using the " - "GeoIPFile option!"); + /* Don't warn Tor clients, they don't use statistics */ + if (options->ORPort) + 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) && @@ -1470,9 +1721,9 @@ options_act(or_options_t *old_options) } else { options->EntryStatistics = 0; log_notice(LD_CONFIG, "Configured to measure entry node " - "statistics, but no GeoIP database found! " + "statistics, but no GeoIP database found. " "Please specify a GeoIP database using the " - "GeoIPFile option!"); + "GeoIPFile option."); } } if ((!old_options || !old_options->ExitPortStatistics) && @@ -1480,6 +1731,15 @@ options_act(or_options_t *old_options) 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->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 " @@ -1498,6 +1758,12 @@ options_act(or_options_t *old_options) 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(); /* Check if we need to parse and add the EntryNodes config option. */ if (options->EntryNodes && @@ -1555,7 +1821,7 @@ options_act(or_options_t *old_options) * apply abbreviations that work for the config file and the command line. * If <b>warn_obsolete</b> is set, warn about deprecated names. */ static const char * -expand_abbrev(config_format_t *fmt, const char *option, int command_line, +expand_abbrev(const config_format_t *fmt, const char *option, int command_line, int warn_obsolete) { int i; @@ -1593,7 +1859,11 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) int i = 1; while (i < argc) { + unsigned command = CONFIG_LINE_NORMAL; + int want_arg = 1; + if (!strcmp(argv[i],"-f") || + !strcmp(argv[i],"--defaults-torrc") || !strcmp(argv[i],"--hash-password")) { i += 2; /* command-line option with argument. ignore them. */ continue; @@ -1610,13 +1880,6 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) continue; } - if (i == argc-1) { - log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", - argv[i]); - config_free_lines(front); - return -1; - } - *new = tor_malloc_zero(sizeof(config_line_t)); s = argv[i]; @@ -1625,15 +1888,33 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) 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; + } + + if (want_arg && i == argc-1) { + log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", + argv[i]); + config_free_lines(front); + return -1; + } (*new)->key = tor_strdup(expand_abbrev(&options_format, s, 1, 1)); - (*new)->value = tor_strdup(argv[i+1]); + (*new)->value = want_arg ? tor_strdup(argv[i+1]) : tor_strdup(""); + (*new)->command = command; (*new)->next = NULL; log(LOG_DEBUG, LD_CONFIG, "command line: parsed keyword '%s', value '%s'", (*new)->key, (*new)->value); new = &((*new)->next); - i += 2; + i += want_arg ? 2 : 1; } *result = front; return 0; @@ -1648,7 +1929,7 @@ config_line_append(config_line_t **lst, { config_line_t *newline; - newline = tor_malloc(sizeof(config_line_t)); + newline = tor_malloc_zero(sizeof(config_line_t)); newline->key = tor_strdup(key); newline->value = tor_strdup(val); newline->next = NULL; @@ -1661,9 +1942,12 @@ config_line_append(config_line_t **lst, /** Helper: parse the config string and strdup into key/value * strings. Set *result to the list, or NULL if parsing the string * failed. Return 0 on success, -1 on failure. Warn and ignore any - * misformatted lines. */ + * misformatted lines. + * + * If <b>extended</b> is set, then treat keys beginning with / and with + as + * indicating "clear" and "append" respectively. */ int -config_get_lines(const char *string, config_line_t **result) +config_get_lines(const char *string, config_line_t **result, int extended) { config_line_t *list = NULL, **next; char *k, *v; @@ -1679,13 +1963,30 @@ config_get_lines(const char *string, config_line_t **result) return -1; } if (k && v) { + unsigned command = CONFIG_LINE_NORMAL; + if (extended) { + if (k[0] == '+') { + char *k_new = tor_strdup(k+1); + tor_free(k); + k = k_new; + command = CONFIG_LINE_APPEND; + } else if (k[0] == '/') { + char *k_new = tor_strdup(k+1); + tor_free(k); + k = k_new; + tor_free(v); + v = tor_strdup(""); + command = CONFIG_LINE_CLEAR; + } + } /* This list can get long, so we keep a pointer to the end of it * rather than using config_line_append over and over and getting * n^2 performance. */ - *next = tor_malloc(sizeof(config_line_t)); + *next = tor_malloc_zero(sizeof(config_line_t)); (*next)->key = k; (*next)->value = v; (*next)->next = NULL; + (*next)->command = command; next = &((*next)->next); } else { tor_free(k); @@ -1715,12 +2016,9 @@ config_free_lines(config_line_t *front) } } -/** If <b>key</b> is a configuration option, return the corresponding - * config_var_t. Otherwise, if <b>key</b> is a non-standard abbreviation, - * warn, and return the corresponding config_var_t. Otherwise return NULL. - */ +/** As config_find_option, but return a non-const pointer. */ static config_var_t * -config_find_option(config_format_t *fmt, const char *key) +config_find_option_mutable(config_format_t *fmt, const char *key) { int i; size_t keylen = strlen(key); @@ -1745,9 +2043,20 @@ config_find_option(config_format_t *fmt, const char *key) 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. + */ +static 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(config_format_t *fmt) +config_count_options(const config_format_t *fmt) { int i; for (i=0; fmt->vars[i].name; ++i) @@ -1765,11 +2074,11 @@ config_count_options(config_format_t *fmt) * Called from config_assign_line() and option_reset(). */ static int -config_assign_value(config_format_t *fmt, or_options_t *options, +config_assign_value(const config_format_t *fmt, or_options_t *options, config_line_t *c, char **msg) { int i, ok; - config_var_t *var; + const config_var_t *var; void *lvalue; CHECK(fmt, options); @@ -1812,6 +2121,18 @@ config_assign_value(config_format_t *fmt, or_options_t *options, 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) { @@ -1835,6 +2156,20 @@ config_assign_value(config_format_t *fmt, or_options_t *options, *(int *)lvalue = i; break; + case CONFIG_TYPE_AUTOBOOL: + if (!strcmp(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); @@ -1870,7 +2205,7 @@ config_assign_value(config_format_t *fmt, or_options_t *options, SMARTLIST_FOREACH(*(smartlist_t**)lvalue, char *, cp, tor_free(cp)); smartlist_clear(*(smartlist_t**)lvalue); } else { - *(smartlist_t**)lvalue = smartlist_create(); + *(smartlist_t**)lvalue = smartlist_new(); } smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",", @@ -1879,7 +2214,19 @@ config_assign_value(config_format_t *fmt, or_options_t *options, case CONFIG_TYPE_LINELIST: case CONFIG_TYPE_LINELIST_S: - config_line_append((config_line_t**)lvalue, c->key, c->value); + { + 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); @@ -1895,6 +2242,28 @@ config_assign_value(config_format_t *fmt, or_options_t *options, 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, or_options_t *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; + } +} + /** 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. @@ -1905,11 +2274,11 @@ config_assign_value(config_format_t *fmt, or_options_t *options, * Called from config_assign(). */ static int -config_assign_line(config_format_t *fmt, or_options_t *options, +config_assign_line(const config_format_t *fmt, or_options_t *options, config_line_t *c, int use_defaults, int clear_first, bitarray_t *options_seen, char **msg) { - config_var_t *var; + const config_var_t *var; CHECK(fmt, options); @@ -1937,8 +2306,9 @@ config_assign_line(config_format_t *fmt, or_options_t *options, 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) { + 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, @@ -1948,6 +2318,8 @@ config_assign_line(config_format_t *fmt, or_options_t *options, } } return 0; + } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) { + option_reset(fmt, options, var, use_defaults); } if (options_seen && (var->type != CONFIG_TYPE_LINELIST && @@ -1970,10 +2342,10 @@ config_assign_line(config_format_t *fmt, or_options_t *options, /** Restore the option named <b>key</b> in options to its default value. * Called from config_assign(). */ static void -config_reset_line(config_format_t *fmt, or_options_t *options, +config_reset_line(const config_format_t *fmt, or_options_t *options, const char *key, int use_defaults) { - config_var_t *var; + const config_var_t *var; CHECK(fmt, options); @@ -1988,7 +2360,7 @@ config_reset_line(config_format_t *fmt, or_options_t *options, int option_is_recognized(const char *key) { - config_var_t *var = config_find_option(&options_format, key); + const config_var_t *var = config_find_option(&options_format, key); return (var != NULL); } @@ -1997,14 +2369,14 @@ option_is_recognized(const char *key) const char * option_get_canonical_name(const char *key) { - config_var_t *var = config_find_option(&options_format, 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(or_options_t *options, const char *key) +option_get_assignment(const or_options_t *options, const char *key) { return get_assigned_option(&options_format, options, key, 1); } @@ -2042,7 +2414,7 @@ config_lines_dup(const config_line_t *inp) config_line_t *result = NULL; config_line_t **next_out = &result; while (inp) { - *next_out = tor_malloc(sizeof(config_line_t)); + *next_out = tor_malloc_zero(sizeof(config_line_t)); (*next_out)->key = tor_strdup(inp->key); (*next_out)->value = tor_strdup(inp->value); inp = inp->next; @@ -2057,10 +2429,10 @@ config_lines_dup(const config_line_t *inp) * 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. */ static config_line_t * -get_assigned_option(config_format_t *fmt, void *options, +get_assigned_option(const config_format_t *fmt, const void *options, const char *key, int escape_val) { - config_var_t *var; + const config_var_t *var; const void *value; config_line_t *result; tor_assert(options && key); @@ -2106,6 +2478,7 @@ get_assigned_option(config_format_t *fmt, void *options, } /* fall through */ case CONFIG_TYPE_INTERVAL: + case CONFIG_TYPE_MSEC_INTERVAL: case CONFIG_TYPE_UINT: /* This means every or_options_t uint or bool element * needs to be an int. Not, say, a uint16_t or char. */ @@ -2121,6 +2494,14 @@ get_assigned_option(config_format_t *fmt, void *options, 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. */ @@ -2233,7 +2614,7 @@ options_trial_assign() calls config_assign(1, 1) returns. */ static int -config_assign(config_format_t *fmt, void *options, config_line_t *list, +config_assign(const config_format_t *fmt, void *options, config_line_t *list, int use_defaults, int clear_first, char **msg) { config_line_t *p; @@ -2270,6 +2651,12 @@ config_assign(config_format_t *fmt, void *options, config_line_t *list, 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; } @@ -2295,7 +2682,7 @@ options_trial_assign(config_line_t *list, int use_defaults, return r; } - if (options_validate(get_options(), trial_options, 1, msg) < 0) { + if (options_validate(get_options_mutable(), trial_options, 1, msg) < 0) { config_free(&options_format, trial_options); return SETOPT_ERR_PARSE; /*XXX make this a separate return value. */ } @@ -2317,7 +2704,8 @@ options_trial_assign(config_line_t *list, int use_defaults, /** Reset config option <b>var</b> to 0, 0.0, NULL, or the equivalent. * Called from option_reset() and config_free(). */ static void -option_clear(config_format_t *fmt, or_options_t *options, config_var_t *var) +option_clear(const config_format_t *fmt, or_options_t *options, + const config_var_t *var) { void *lvalue = STRUCT_VAR_P(options, var->var_offset); (void)fmt; /* unused */ @@ -2333,11 +2721,15 @@ option_clear(config_format_t *fmt, or_options_t *options, config_var_t *var) *(time_t*)lvalue = 0; break; case CONFIG_TYPE_INTERVAL: + case CONFIG_TYPE_MSEC_INTERVAL: case CONFIG_TYPE_UINT: case CONFIG_TYPE_PORT: case CONFIG_TYPE_BOOL: *(int*)lvalue = 0; break; + case CONFIG_TYPE_AUTOBOOL: + *(int*)lvalue = -1; + break; case CONFIG_TYPE_MEMUNIT: *(uint64_t*)lvalue = 0; break; @@ -2371,8 +2763,8 @@ option_clear(config_format_t *fmt, or_options_t *options, config_var_t *var) * <b>use_defaults</b>, set it to its default value. * Called by config_init() and option_reset_line() and option_assign_line(). */ static void -option_reset(config_format_t *fmt, or_options_t *options, - config_var_t *var, int use_defaults) +option_reset(const config_format_t *fmt, or_options_t *options, + const config_var_t *var, int use_defaults) { config_line_t *c; char *msg = NULL; @@ -2410,9 +2802,9 @@ static void list_torrc_options(void) { int i; - smartlist_t *lines = smartlist_create(); + smartlist_t *lines = smartlist_new(); for (i = 0; _option_vars[i].name; ++i) { - config_var_t *var = &_option_vars[i]; + const config_var_t *var = &_option_vars[i]; if (var->type == CONFIG_TYPE_OBSOLETE || var->type == CONFIG_TYPE_LINELIST_V) continue; @@ -2429,9 +2821,10 @@ static uint32_t last_resolved_addr = 0; * set *<b>hostname_out</b> to a new string holding the hostname we used to * get the address. Return 0 if all is well, or -1 if we can't find a suitable * public IP address. + * XXXX ipv6 */ int -resolve_my_address(int warn_severity, or_options_t *options, +resolve_my_address(int warn_severity, const or_options_t *options, uint32_t *addr_out, char **hostname_out) { struct in_addr in; @@ -2440,7 +2833,7 @@ resolve_my_address(int warn_severity, or_options_t *options, int explicit_ip=1; int explicit_hostname=1; int from_interface=0; - char tmpbuf[INET_NTOA_BUF_LEN]; + char *addr_string = NULL; const char *address = options->Address; int notice_severity = warn_severity <= LOG_NOTICE ? LOG_NOTICE : warn_severity; @@ -2482,48 +2875,43 @@ resolve_my_address(int warn_severity, or_options_t *options, return -1; } from_interface = 1; - in.s_addr = htonl(interface_ip); - tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf)); + addr = interface_ip; log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for " - "local interface. Using that.", tmpbuf); + "local interface. Using that.", fmt_addr32(addr)); strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname)); } else { /* resolved hostname into addr */ - in.s_addr = htonl(addr); - if (!explicit_hostname && - is_internal_IP(ntohl(in.s_addr), 0)) { + is_internal_IP(addr, 0)) { uint32_t interface_ip; - tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf)); log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' " - "resolves to a private IP address (%s). Trying something " - "else.", hostname, tmpbuf); + "resolves to a private IP address (%s). Trying something " + "else.", hostname, fmt_addr32(addr)); if (get_interface_address(warn_severity, &interface_ip)) { log_fn(warn_severity, LD_CONFIG, "Could not get local interface IP address. Too bad."); } else if (is_internal_IP(interface_ip, 0)) { - struct in_addr in2; - in2.s_addr = htonl(interface_ip); - tor_inet_ntoa(&in2,tmpbuf,sizeof(tmpbuf)); log_fn(notice_severity, LD_CONFIG, "Interface IP address '%s' is a private address too. " - "Ignoring.", tmpbuf); + "Ignoring.", fmt_addr32(interface_ip)); } else { from_interface = 1; - in.s_addr = htonl(interface_ip); - tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf)); + addr = interface_ip; log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for local interface." - " Using that.", tmpbuf); + " 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 */ } - tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf)); - if (is_internal_IP(ntohl(in.s_addr), 0)) { + addr_string = tor_dup_ip(addr); + if (is_internal_IP(addr, 0)) { /* make sure we're ok with publishing an internal IP */ if (!options->DirServers && !options->AlternateDirAuthority) { /* if they are using the default dirservers, disallow internal IPs @@ -2531,7 +2919,8 @@ resolve_my_address(int warn_severity, or_options_t *options, log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private IP address '%s'. " "Tor servers that use the default DirServers must have public " - "IP addresses.", hostname, tmpbuf); + "IP addresses.", hostname, addr_string); + tor_free(addr_string); return -1; } if (!explicit_ip) { @@ -2539,19 +2928,20 @@ resolve_my_address(int warn_severity, or_options_t *options, * 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, tmpbuf); + "the IP address you want to use.", hostname, addr_string); + tor_free(addr_string); return -1; } } - log_debug(LD_CONFIG, "Resolved Address to '%s'.", tmpbuf); - *addr_out = ntohl(in.s_addr); + log_debug(LD_CONFIG, "Resolved Address to '%s'.", fmt_addr32(addr)); + *addr_out = addr; 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. Updating.", - tmpbuf); + addr_string); ip_address_changed(0); } if (last_resolved_addr != *addr_out) { @@ -2570,11 +2960,12 @@ resolve_my_address(int warn_severity, or_options_t *options, } control_event_server_status(LOG_NOTICE, "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s %s%s", - tmpbuf, method, h?"HOSTNAME=":"", h); + addr_string, method, h?"HOSTNAME=":"", h); } last_resolved_addr = *addr_out; if (hostname_out) *hostname_out = tor_strdup(hostname); + tor_free(addr_string); return 0; } @@ -2609,7 +3000,7 @@ is_local_addr(const tor_addr_t *addr) /** Release storage held by <b>options</b>. */ static void -config_free(config_format_t *fmt, void *options) +config_free(const config_format_t *fmt, void *options) { int i; @@ -2648,8 +3039,9 @@ config_lines_eq(config_line_t *a, config_line_t *b) * and <b>o2</b>. Must not be called for LINELIST_S or OBSOLETE options. */ static int -option_is_same(config_format_t *fmt, - or_options_t *o1, or_options_t *o2, const char *name) +option_is_same(const config_format_t *fmt, + const or_options_t *o1, const or_options_t *o2, + const char *name) { config_line_t *c1, *c2; int r = 1; @@ -2666,7 +3058,7 @@ option_is_same(config_format_t *fmt, /** Copy storage held by <b>old</b> into a new or_options_t and return it. */ static or_options_t * -options_dup(config_format_t *fmt, or_options_t *old) +options_dup(const config_format_t *fmt, const or_options_t *old) { or_options_t *newopts; int i; @@ -2708,44 +3100,13 @@ options_init(or_options_t *options) config_init(&options_format, options); } -/* Check if the port number given in <b>port_option</b> in combination with - * the specified port in <b>listen_options</b> will result in Tor actually - * opening a low port (meaning a port lower than 1024). Return 1 if - * it is, or 0 if it isn't or the concept of a low port isn't applicable for - * the platform we're on. */ -static int -is_listening_on_low_port(int port_option, - const config_line_t *listen_options) -{ -#ifdef MS_WINDOWS - (void) port_option; - (void) listen_options; - return 0; /* No port is too low for windows. */ -#else - const config_line_t *l; - uint16_t p; - if (port_option == 0) - return 0; /* We're not listening */ - if (listen_options == NULL) - return (port_option < 1024); - - for (l = listen_options; l; l = l->next) { - parse_addr_port(LOG_WARN, l->value, NULL, NULL, &p); - if (p<1024) { - return 1; - } - } - return 0; -#endif -} - /** Set all vars in the configuration object <b>options</b> to their default * values. */ static void -config_init(config_format_t *fmt, void *options) +config_init(const config_format_t *fmt, void *options) { int i; - config_var_t *var; + const config_var_t *var; CHECK(fmt, options); for (i=0; fmt->vars[i].name; ++i) { @@ -2761,27 +3122,33 @@ config_init(config_format_t *fmt, void *options) * Else, if comment_defaults, write default values as comments. */ static char * -config_dump(config_format_t *fmt, void *options, int minimal, +config_dump(const config_format_t *fmt, const void *default_options, + const void *options, int minimal, int comment_defaults) { smartlist_t *elements; - or_options_t *defaults; + const or_options_t *defaults = default_options; + void *defaults_tmp = NULL; config_line_t *line, *assigned; char *result; int i; char *msg = NULL; - defaults = config_alloc(fmt); - config_init(fmt, defaults); + if (defaults == NULL) { + defaults = defaults_tmp = config_alloc(fmt); + config_init(fmt, defaults_tmp); + } /* XXX use a 1 here so we don't add a new log line while dumping */ - if (fmt->validate_fn(NULL,defaults, 1, &msg) < 0) { - log_err(LD_BUG, "Failed to validate default config."); - tor_free(msg); - tor_assert(0); + if (default_options == NULL) { + if (fmt->validate_fn(NULL, defaults_tmp, 1, &msg) < 0) { + log_err(LD_BUG, "Failed to validate default config."); + tor_free(msg); + tor_assert(0); + } } - elements = smartlist_create(); + elements = smartlist_new(); for (i=0; fmt->vars[i].name; ++i) { int comment_option = 0; if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE || @@ -2799,11 +3166,9 @@ config_dump(config_format_t *fmt, void *options, int minimal, line = assigned = get_assigned_option(fmt, options, fmt->vars[i].name, 1); for (; line; line = line->next) { - char *tmp; - tor_asprintf(&tmp, "%s%s %s\n", + smartlist_add_asprintf(elements, "%s%s %s\n", comment_option ? "# " : "", line->key, line->value); - smartlist_add(elements, tmp); } config_free_lines(assigned); } @@ -2811,16 +3176,15 @@ config_dump(config_format_t *fmt, void *options, int minimal, if (fmt->extra) { line = *(config_line_t**)STRUCT_VAR_P(options, fmt->extra->var_offset); for (; line; line = line->next) { - char *tmp; - tor_asprintf(&tmp, "%s %s\n", line->key, line->value); - smartlist_add(elements, tmp); + 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); - config_free(fmt, defaults); + if (defaults_tmp) + config_free(fmt, defaults_tmp); return result; } @@ -2829,9 +3193,10 @@ config_dump(config_format_t *fmt, void *options, int minimal, * include options that are the same as Tor's defaults. */ char * -options_dump(or_options_t *options, int minimal) +options_dump(const or_options_t *options, int minimal) { - return config_dump(&options_format, options, minimal, 0); + return config_dump(&options_format, global_default_options, + options, minimal, 0); } /** Return 0 if every element of sl is a string holding a decimal @@ -2888,24 +3253,24 @@ static int compute_publishserverdescriptor(or_options_t *options) { smartlist_t *list = options->PublishServerDescriptor; - authority_type_t *auth = &options->_PublishServerDescriptor; - *auth = NO_AUTHORITY; + dirinfo_type_t *auth = &options->_PublishServerDescriptor; + *auth = NO_DIRINFO; if (!list) /* empty list, answer is none */ return 0; SMARTLIST_FOREACH(list, const char *, string, { if (!strcasecmp(string, "v1")) - *auth |= V1_AUTHORITY; + *auth |= V1_DIRINFO; else if (!strcmp(string, "1")) if (options->BridgeRelay) - *auth |= BRIDGE_AUTHORITY; + *auth |= BRIDGE_DIRINFO; else - *auth |= V2_AUTHORITY | V3_AUTHORITY; + *auth |= V2_DIRINFO | V3_DIRINFO; else if (!strcasecmp(string, "v2")) - *auth |= V2_AUTHORITY; + *auth |= V2_DIRINFO; else if (!strcasecmp(string, "v3")) - *auth |= V3_AUTHORITY; + *auth |= V3_DIRINFO; else if (!strcasecmp(string, "bridge")) - *auth |= BRIDGE_AUTHORITY; + *auth |= BRIDGE_DIRINFO; else if (!strcasecmp(string, "hidserv")) log_warn(LD_CONFIG, "PublishServerDescriptor hidserv is invalid. See " @@ -2933,6 +3298,10 @@ compute_publishserverdescriptor(or_options_t *options) * will generate too many circuits and potentially overload the network. */ #define MIN_CIRCUIT_STREAM_TIMEOUT 10 +/** Lowest allowable value for HeartbeatPeriod; if this is too low, we might + * expose more information than we're comfortable with. */ +#define MIN_HEARTBEAT_PERIOD (30*60) + /** Return 0 if every setting in <b>options</b> is reasonable, and a * permissible transition from <b>old_options</b>. Else return -1. * Should have no side effects, except for normalizing the contents of @@ -2952,6 +3321,7 @@ options_validate(or_options_t *old_options, or_options_t *options, int i; config_line_t *cl; const char *uname = get_uname(); + int n_ports=0; #define REJECT(arg) \ STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END #define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END @@ -2969,63 +3339,8 @@ options_validate(or_options_t *old_options, or_options_t *options, "for details.", uname); } - if (options->ORPort == 0 && options->ORListenAddress != NULL) - REJECT("ORPort must be defined if ORListenAddress is defined."); - - if (options->DirPort == 0 && options->DirListenAddress != NULL) - REJECT("DirPort must be defined if DirListenAddress is defined."); - - if (options->DNSPort == 0 && options->DNSListenAddress != NULL) - REJECT("DNSPort must be defined if DNSListenAddress is defined."); - - if (options->ControlPort == 0 && options->ControlListenAddress != NULL) - REJECT("ControlPort must be defined if ControlListenAddress is defined."); - - if (options->TransPort == 0 && options->TransListenAddress != NULL) - REJECT("TransPort must be defined if TransListenAddress is defined."); - - if (options->NATDPort == 0 && options->NATDListenAddress != NULL) - REJECT("NATDPort must be defined if NATDListenAddress is defined."); - - /* Don't gripe about SocksPort 0 with SocksListenAddress set; a standard - * configuration does this. */ - - for (i = 0; i < 3; ++i) { - int is_socks = i==0; - int is_trans = i==1; - config_line_t *line, *opt, *old; - const char *tp; - if (is_socks) { - opt = options->SocksListenAddress; - old = old_options ? old_options->SocksListenAddress : NULL; - tp = "SOCKS proxy"; - } else if (is_trans) { - opt = options->TransListenAddress; - old = old_options ? old_options->TransListenAddress : NULL; - tp = "transparent proxy"; - } else { - opt = options->NATDListenAddress; - old = old_options ? old_options->NATDListenAddress : NULL; - tp = "natd proxy"; - } - - for (line = opt; line; line = line->next) { - char *address = NULL; - uint16_t port; - uint32_t addr; - if (parse_addr_port(LOG_WARN, line->value, &address, &addr, &port)<0) - continue; /* We'll warn about this later. */ - if (!is_internal_IP(addr, 1) && - (!old_options || !config_lines_eq(old, opt))) { - log_warn(LD_CONFIG, - "You specified a public address '%s' for a %s. Other " - "people on the Internet might find your computer and use it as " - "an open %s. Please don't allow this unless you have " - "a good reason.", address, tp, tp); - } - tor_free(address); - } - } + if (parse_ports(options, 1, msg, &n_ports) < 0) + return -1; if (validate_data_directory(options)<0) REJECT("Invalid DataDirectory"); @@ -3049,8 +3364,12 @@ options_validate(or_options_t *old_options, or_options_t *options, "misconfigured or something else goes wrong."); /* Special case on first boot if no Log options are given. */ - if (!options->Logs && !options->RunAsDaemon && !from_setconf) - config_line_append(&options->Logs, "Log", "notice stdout"); + 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"); + } if (options_init_logs(options, 1)<0) /* Validate the log(s) */ REJECT("Failed to validate Log options. See logs for details."); @@ -3062,20 +3381,14 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("Failed to resolve/guess local address. See logs for details."); } - if (strcmp(options->RefuseUnknownExits, "0") && - strcmp(options->RefuseUnknownExits, "1") && - strcmp(options->RefuseUnknownExits, "auto")) { - REJECT("RefuseUnknownExits must be 0, 1, or auto"); - } - -#ifndef MS_WINDOWS +#ifndef _WIN32 if (options->RunAsDaemon && torrc_fname && path_is_relative(torrc_fname)) REJECT("Can't use a relative path to torrc when RunAsDaemon is set."); #endif - if (options->SocksPort == 0 && options->TransPort == 0 && - options->NATDPort == 0 && options->ORPort == 0 && - options->DNSPort == 0 && !options->RendConfigLines) + /* 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(LOG_WARN, LD_CONFIG, "SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all " "undefined, and there aren't any hidden services configured. " @@ -3086,17 +3399,9 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("TransPort and TransListenAddress are disabled in this build."); #endif - if (options->AccountingMax && - (is_listening_on_low_port(options->ORPort, options->ORListenAddress) || - is_listening_on_low_port(options->DirPort, options->DirListenAddress))) - { - log(LOG_WARN, LD_CONFIG, - "You have set AccountingMax to use hibernation. You have also " - "chosen a low DirPort or OrPort. 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."); + if (options->TokenBucketRefillInterval <= 0 + || options->TokenBucketRefillInterval > 1000) { + REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive."); } if (options->ExcludeExitNodes || options->ExcludeNodes) { @@ -3105,17 +3410,24 @@ options_validate(or_options_t *old_options, or_options_t *options, 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."); } - if (options->EntryNodes && !routerset_is_list(options->EntryNodes)) { - /* XXXX fix this; see entry_guards_prepend_from_config(). */ - REJECT("IPs or countries are not yet supported in EntryNodes."); - } - if (options->AuthoritativeDir) { if (!options->ContactInfo && !options->TestingTorNetwork) REJECT("Authoritative directory servers must set ContactInfo"); @@ -3177,6 +3489,15 @@ options_validate(or_options_t *old_options, or_options_t *options, return -1; } + 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; @@ -3196,7 +3517,7 @@ options_validate(or_options_t *old_options, or_options_t *options, /* 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_create(); + 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 @@ -3204,11 +3525,8 @@ options_validate(or_options_t *old_options, or_options_t *options, SMARTLIST_FOREACH(options->FirewallPorts, const char *, portno, { int p = atoi(portno); - char *s; if (p<0) continue; - s = tor_malloc(16); - tor_snprintf(s, 16, "*:%d", p); - smartlist_add(instead, s); + 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 */ @@ -3284,6 +3602,10 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->UseBridges && options->EntryNodes) REJECT("You cannot set both UseBridges and EntryNodes."); + if (options->EntryNodes && !options->UseEntryGuards) + log_warn(LD_CONFIG, "EntryNodes is set, but UseEntryGuards is disabled. " + "EntryNodes will be ignored."); + options->_AllowInvalid = 0; if (options->AllowInvalidNodes) { SMARTLIST_FOREACH(options->AllowInvalidNodes, const char *, cp, { @@ -3325,9 +3647,9 @@ options_validate(or_options_t *old_options, or_options_t *options, } if ((options->BridgeRelay - || options->_PublishServerDescriptor & BRIDGE_AUTHORITY) + || options->_PublishServerDescriptor & BRIDGE_DIRINFO) && (options->_PublishServerDescriptor - & (V1_AUTHORITY|V2_AUTHORITY|V3_AUTHORITY))) { + & (V1_DIRINFO|V2_DIRINFO|V3_DIRINFO))) { REJECT("Bridges are not supposed to publish router descriptors to the " "directory authorities. Please correct your " "PublishServerDescriptor line."); @@ -3336,7 +3658,8 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->BridgeRelay && options->DirPort) { log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling " "DirPort"); - options->DirPort = 0; + config_free_lines(options->DirPort); + options->DirPort = NULL; } if (options->MinUptimeHidServDirectoryV2 < 0) { @@ -3357,6 +3680,24 @@ options_validate(or_options_t *old_options, or_options_t *options, options->RendPostPeriod = MAX_DIR_PERIOD; } + if (options->Tor2webMode && options->LearnCircuitBuildTimeout) { + /* LearnCircuitBuildTimeout and Tor2webMode are incompatible in + * two ways: + * + * - LearnCircuitBuildTimeout results in a low CBT, which + * Tor2webMode's use of one-hop rendezvous circuits lowers + * much further, producing *far* too many timeouts. + * + * - The adaptive CBT code does not update its timeout estimate + * using build times for single-hop circuits. + * + * If we fix both of these issues someday, we should test + * Tor2webMode with LearnCircuitBuildTimeout on again. */ + log_notice(LD_CONFIG,"Tor2webMode is enabled; turning " + "LearnCircuitBuildTimeout off."); + options->LearnCircuitBuildTimeout = 0; + } + if (options->MaxCircuitDirtiness < MIN_MAX_CIRCUIT_DIRTINESS) { log_warn(LD_CONFIG, "MaxCircuitDirtiness option is too short; " "raising to %d seconds.", MIN_MAX_CIRCUIT_DIRTINESS); @@ -3370,6 +3711,13 @@ options_validate(or_options_t *old_options, or_options_t *options, 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."); @@ -3452,7 +3800,7 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("Failed to parse accounting options. See logs for details."); if (options->HTTPProxy) { /* parse it now */ - if (tor_addr_port_parse(options->HTTPProxy, + 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 */ @@ -3466,7 +3814,7 @@ options_validate(or_options_t *old_options, or_options_t *options, } if (options->HTTPSProxy) { /* parse it now */ - if (tor_addr_port_parse(options->HTTPSProxy, + 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 */ @@ -3480,7 +3828,7 @@ options_validate(or_options_t *old_options, or_options_t *options, } if (options->Socks4Proxy) { /* parse it now */ - if (tor_addr_port_parse(options->Socks4Proxy, + if (tor_addr_port_lookup(options->Socks4Proxy, &options->Socks4ProxyAddr, &options->Socks4ProxyPort) <0) REJECT("Socks4Proxy failed to parse or resolve. Please fix."); @@ -3490,7 +3838,7 @@ options_validate(or_options_t *old_options, or_options_t *options, } if (options->Socks5Proxy) { /* parse it now */ - if (tor_addr_port_parse(options->Socks5Proxy, + if (tor_addr_port_lookup(options->Socks5Proxy, &options->Socks5ProxyAddr, &options->Socks5ProxyPort) <0) REJECT("Socks5Proxy failed to parse or resolve. Please fix."); @@ -3499,8 +3847,11 @@ options_validate(or_options_t *old_options, or_options_t *options, } } - if (options->Socks4Proxy && options->Socks5Proxy) - REJECT("You cannot specify both Socks4Proxy and SOCKS5Proxy"); + /* Check if more than one proxy type has been enabled. */ + if (!!options->Socks4Proxy + !!options->Socks5Proxy + + !!options->HTTPSProxy + !!options->ClientTransportPlugin > 1) + REJECT("You have configured more than one proxy type. " + "(Socks4Proxy|Socks5Proxy|HTTPSProxy|ClientTransportPlugin)"); if (options->Socks5ProxyUsername) { size_t len; @@ -3549,39 +3900,6 @@ options_validate(or_options_t *old_options, or_options_t *options, } } - if (options->ControlListenAddress) { - int all_are_local = 1; - config_line_t *ln; - for (ln = options->ControlListenAddress; ln; ln = ln->next) { - if (strcmpstart(ln->value, "127.")) - all_are_local = 0; - } - if (!all_are_local) { - if (!options->HashedControlPassword && - !options->HashedControlSessionPassword && - !options->CookieAuthentication) { - log_warn(LD_CONFIG, - "You have a ControlListenAddress 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."); - options->ControlPort = 0; - } else { - log_warn(LD_CONFIG, "You have a ControlListenAddress 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."); - } - } - } - if (options->ControlPort && !options->HashedControlPassword && !options->HashedControlSessionPassword && !options->CookieAuthentication) { @@ -3603,8 +3921,12 @@ options_validate(or_options_t *old_options, or_options_t *options, if (check_nickname_list(options->MyFamily, "MyFamily", msg)) return -1; for (cl = options->NodeFamilies; cl; cl = cl->next) { - if (check_nickname_list(cl->value, "NodeFamily", msg)) + 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) @@ -3617,11 +3939,20 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("If you set UseBridges, you must specify at least one bridge."); if (options->UseBridges && !options->TunnelDirConns) REJECT("If you set UseBridges, you must set TunnelDirConns."); - if (options->Bridges) { - for (cl = options->Bridges; cl; cl = cl->next) { - if (parse_bridge_line(cl->value, 1)<0) - REJECT("Bridge line did not parse. See logs for details."); - } + + for (cl = options->Bridges; cl; cl = cl->next) { + if (parse_bridge_line(cl->value, 1)<0) + REJECT("Bridge line did not parse. See logs for details."); + } + + for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { + if (parse_client_transport_line(cl->value, 1)<0) + REJECT("Transport line did not parse. See logs for details."); + } + + for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { + if (parse_server_transport_line(cl->value, 1)<0) + REJECT("Server transport line did not parse. See logs for details."); } if (options->ConstrainedSockets) { @@ -3791,8 +4122,9 @@ options_validate(or_options_t *old_options, or_options_t *options, } }); - if (options->BridgeRelay == 1 && options->ORPort == 0) - REJECT("BridgeRelay is 1, ORPort is 0. This is an invalid combination."); + if (options->BridgeRelay == 1 && ! options->ORPort) + REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid " + "combination."); return 0; #undef REJECT @@ -3804,17 +4136,13 @@ options_validate(or_options_t *old_options, or_options_t *options, static int opt_streq(const char *s1, const char *s2) { - if (!s1 && !s2) - return 1; - else if (s1 && s2 && !strcmp(s1,s2)) - return 1; - else - return 0; + 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(or_options_t *old, or_options_t *new_val, +options_transition_allowed(const or_options_t *old, + const or_options_t *new_val, char **msg) { if (!old) @@ -3864,18 +4192,37 @@ options_transition_allowed(or_options_t *old, or_options_t *new_val, return -1; } + if (old->TokenBucketRefillInterval != new_val->TokenBucketRefillInterval) { + *msg = tor_strdup("While Tor is running, changing TokenBucketRefill" + "Interval is not allowed"); + return -1; + } + + if (old->DisableIOCP != new_val->DisableIOCP) { + *msg = tor_strdup("While Tor is running, changing DisableIOCP " + "is not allowed."); + return -1; + } + + if (old->DisableDebuggerAttachment && + !new_val->DisableDebuggerAttachment) { + *msg = tor_strdup("While Tor is running, disabling " + "DisableDebuggerAttachment is not allowed."); + return -1; + } + 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(or_options_t *old_options, - or_options_t *new_options) +options_transition_affects_workers(const or_options_t *old_options, + const or_options_t *new_options) { if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || old_options->NumCPUs != new_options->NumCPUs || - old_options->ORPort != new_options->ORPort || + !config_lines_eq(old_options->ORPort, new_options->ORPort) || old_options->ServerDNSSearchDomains != new_options->ServerDNSSearchDomains || old_options->_SafeLogging != new_options->_SafeLogging || @@ -3894,8 +4241,8 @@ options_transition_affects_workers(or_options_t *old_options, /** 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(or_options_t *old_options, - or_options_t *new_options) +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. */ @@ -3905,9 +4252,10 @@ options_transition_affects_descriptor(or_options_t *old_options, !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) || old_options->ExitPolicyRejectPrivate != new_options->ExitPolicyRejectPrivate || - old_options->ORPort != new_options->ORPort || - old_options->DirPort != new_options->DirPort || + !config_lines_eq(old_options->ORPort, new_options->ORPort) || + !config_lines_eq(old_options->DirPort, new_options->DirPort) || old_options->ClientOnly != new_options->ClientOnly || + old_options->DisableNetwork != new_options->DisableNetwork || old_options->_PublishServerDescriptor != new_options->_PublishServerDescriptor || get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || @@ -3916,13 +4264,14 @@ options_transition_affects_descriptor(or_options_t *old_options, !opt_streq(old_options->ContactInfo, new_options->ContactInfo) || !opt_streq(old_options->MyFamily, new_options->MyFamily) || !opt_streq(old_options->AccountingStart, new_options->AccountingStart) || - old_options->AccountingMax != new_options->AccountingMax) + old_options->AccountingMax != new_options->AccountingMax || + public_server_mode(old_options) != public_server_mode(new_options)) return 1; return 0; } -#ifdef MS_WINDOWS +#ifdef _WIN32 /** Return the directory on windows where we expect to find our application * data. */ static char * @@ -3980,17 +4329,25 @@ get_windows_conf_root(void) } #endif -/** Return the default location for our torrc file. */ +/** Return the default location for our torrc file. + * DOCDOC defaults_file */ static const char * -get_default_conf_file(void) +get_default_conf_file(int defaults_file) { -#ifdef MS_WINDOWS - static char path[MAX_PATH+1]; - strlcpy(path, get_windows_conf_root(), MAX_PATH); - strlcat(path,"\\torrc",MAX_PATH); - return path; +#ifdef _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 (CONFDIR "/torrc"); + return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc"; #endif } @@ -4005,7 +4362,7 @@ check_nickname_list(const char *lst, const char *name, char **msg) if (!lst) return 0; - sl = smartlist_create(); + sl = smartlist_new(); smartlist_split_string(sl, lst, ",", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0); @@ -4023,37 +4380,54 @@ check_nickname_list(const char *lst, const char *name, char **msg) return r; } -/** Learn config file name from command line arguments, or use the default */ +/** Learn config file name from command line arguments, or use the default, + * DOCDOC defaults_file */ static char * find_torrc_filename(int argc, char **argv, + int defaults_file, int *using_default_torrc, int *ignore_missing_torrc) { char *fname=NULL; int i; + 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 (i = 1; i < argc; ++i) { - if (i < argc-1 && !strcmp(argv[i],"-f")) { + if (i < argc-1 && !strcmp(argv[i],fname_opt)) { if (fname) { - log(LOG_WARN, LD_CONFIG, "Duplicate -f options on command line."); + log(LOG_WARN, LD_CONFIG, "Duplicate %s options on command line.", + fname_opt); tor_free(fname); } fname = expand_filename(argv[i+1]); + + { + char *absfname; + absfname = make_path_absolute(fname); + tor_free(fname); + fname = absfname; + } + *using_default_torrc = 0; ++i; - } else if (!strcmp(argv[i],"--ignore-missing-torrc")) { + } else if (ignore_opt && !strcmp(argv[i],ignore_opt)) { *ignore_missing_torrc = 1; } } if (*using_default_torrc) { /* didn't find one, try CONFDIR */ - const char *dflt = get_default_conf_file(); + const char *dflt = get_default_conf_file(defaults_file); if (dflt && file_status(dflt) == FN_FILE) { fname = tor_strdup(dflt); } else { -#ifndef MS_WINDOWS - char *fn; - fn = expand_filename("~/.torrc"); +#ifndef _WIN32 + char *fn = NULL; + if (!defaults_file) + fn = expand_filename("~/.torrc"); if (fn && file_status(fn) == FN_FILE) { fname = fn; } else { @@ -4068,43 +4442,48 @@ find_torrc_filename(int argc, char **argv, return fname; } -/** Load torrc from disk, setting torrc_fname if successful */ +/** Load torrc from disk, setting torrc_fname if successful. + * DOCDOC defaults_file */ static char * -load_torrc_from_disk(int argc, char **argv) +load_torrc_from_disk(int argc, char **argv, 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; - fname = find_torrc_filename(argc, argv, + fname = find_torrc_filename(argc, argv, defaults_file, &using_default_torrc, &ignore_missing_torrc); tor_assert(fname); log(LOG_DEBUG, LD_CONFIG, "Opening config file \"%s\"", fname); - tor_free(torrc_fname); - torrc_fname = fname; + tor_free(*fname_var); + *fname_var = fname; /* Open config file */ if (file_status(fname) != FN_FILE || !(cf = read_file_to_str(fname,0,NULL))) { - if (using_default_torrc == 1 || ignore_missing_torrc ) { - log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, " - "using reasonable defaults.", fname); + if (using_default_torrc == 1 || ignore_missing_torrc) { + if (!defaults_file) + log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, " + "using reasonable defaults.", fname); tor_free(fname); /* sets fname to NULL */ - torrc_fname = NULL; + *fname_var = NULL; cf = tor_strdup(""); } else { log(LOG_WARN, LD_CONFIG, "Unable to open configuration file \"%s\".", fname); goto err; } + } else { + log(LOG_NOTICE, LD_CONFIG, "Read configuration file \"%s\".", fname); } return cf; err: tor_free(fname); - torrc_fname = NULL; + *fname_var = NULL; return NULL; } @@ -4115,8 +4494,9 @@ load_torrc_from_disk(int argc, char **argv) int options_init_from_torrc(int argc, char **argv) { - char *cf=NULL; - int i, retval, command; + char *cf=NULL, *cf_defaults=NULL; + int i, command; + int retval = -1; static char **backup_argv; static int backup_argc; char *command_arg = NULL; @@ -4175,24 +4555,24 @@ options_init_from_torrc(int argc, char **argv) if (command == CMD_HASH_PASSWORD) { cf = tor_strdup(""); } else { - cf = load_torrc_from_disk(argc, argv); + cf_defaults = load_torrc_from_disk(argc, argv, 1); + cf = load_torrc_from_disk(argc, argv, 0); if (!cf) goto err; } - retval = options_init_from_string(cf, command, command_arg, &errmsg); - tor_free(cf); - if (retval < 0) - goto err; - - return 0; + retval = options_init_from_string(cf_defaults, cf, command, command_arg, + &errmsg); err: + + tor_free(cf); + tor_free(cf_defaults); if (errmsg) { log(LOG_WARN,LD_CONFIG,"%s", errmsg); tor_free(errmsg); } - return -1; + return retval < 0 ? -1 : 0; } /** Load the options from the configuration in <b>cf</b>, validate @@ -4205,13 +4585,13 @@ options_init_from_torrc(int argc, char **argv) * * -4 for error while setting the new options */ setopt_err_t -options_init_from_string(const char *cf, +options_init_from_string(const char *cf_defaults, const char *cf, int command, const char *command_arg, char **msg) { - or_options_t *oldoptions, *newoptions; + or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL; config_line_t *cl; - int retval; + int retval, i; setopt_err_t err = SETOPT_ERR_MISC; tor_assert(msg); @@ -4224,17 +4604,24 @@ options_init_from_string(const char *cf, newoptions->command = command; newoptions->command_arg = command_arg; - /* get config lines, assign them */ - retval = config_get_lines(cf, &cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); - config_free_lines(cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; + for (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(body, &cl, 1); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); + config_free_lines(cl); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + if (i==0) + newdefaultoptions = options_dup(&options_format, newoptions); } /* Go through command-line variables too */ @@ -4259,9 +4646,9 @@ options_init_from_string(const char *cf, /* Change defaults. */ int i; for (i = 0; testing_tor_network_defaults[i].name; ++i) { - config_var_t *new_var = &testing_tor_network_defaults[i]; + const config_var_t *new_var = &testing_tor_network_defaults[i]; config_var_t *old_var = - config_find_option(&options_format, new_var->name); + config_find_option_mutable(&options_format, new_var->name); tor_assert(new_var); tor_assert(old_var); old_var->initvalue = new_var->initvalue; @@ -4269,6 +4656,8 @@ options_init_from_string(const char *cf, /* Clear newoptions and re-initialize them with new defaults. */ config_free(&options_format, newoptions); + config_free(&options_format, newdefaultoptions); + newdefaultoptions = NULL; newoptions = tor_malloc_zero(sizeof(or_options_t)); newoptions->_magic = OR_OPTIONS_MAGIC; options_init(newoptions); @@ -4276,17 +4665,26 @@ options_init_from_string(const char *cf, newoptions->command_arg = command_arg; /* Assign all options a second time. */ - retval = config_get_lines(cf, &cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); - config_free_lines(cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; + for (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(body, &cl, 1); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); + config_free_lines(cl); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + if (i==0) + newdefaultoptions = options_dup(&options_format, newoptions); } + /* Assign command-line variables a second time too */ retval = config_assign(&options_format, newoptions, global_cmdline_options, 0, 0, msg); if (retval < 0) { @@ -4310,11 +4708,14 @@ options_init_from_string(const char *cf, err = SETOPT_ERR_SETTING; goto err; /* frees and replaces old options */ } + config_free(&options_format, global_default_options); + global_default_options = newdefaultoptions; return SETOPT_OK; err: config_free(&options_format, newoptions); + config_free(&options_format, newdefaultoptions); if (*msg) { char *old_msg = *msg; tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg); @@ -4326,45 +4727,83 @@ options_init_from_string(const char *cf, /** Return the location for our configuration file. */ const char * -get_torrc_fname(void) +get_torrc_fname(int defaults_fname) { - if (torrc_fname) - return torrc_fname; + const char *fname = defaults_fname ? torrc_defaults_fname : torrc_fname; + + if (fname) + return fname; else - return get_default_conf_file(); + return get_default_conf_file(defaults_fname); } /** Adjust the address map based on the MapAddress elements in the * configuration <b>options</b> */ -static void -config_register_addressmaps(or_options_t *options) +void +config_register_addressmaps(const or_options_t *options) { smartlist_t *elts; config_line_t *opt; char *from, *to; addressmap_clear_configured(); - elts = smartlist_create(); + elts = smartlist_new(); for (opt = options->AddressMap; opt; opt = opt->next) { + int from_wildcard = 0, to_wildcard = 0; smartlist_split_string(elts, opt->value, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); - if (smartlist_len(elts) >= 2) { - from = smartlist_get(elts,0); - to = smartlist_get(elts,1); - if (address_is_invalid_destination(to, 1)) { - log_warn(LD_CONFIG, - "Skipping invalid argument '%s' to MapAddress", to); - } else { - addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC); - if (smartlist_len(elts)>2) { - log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress."); - } - } - } else { + 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 (!strcmp(to, "*") || !strcmp(from, "*")) { + log_warn(LD_CONFIG,"MapAddress '%s' is unsupported - can't remap from " + "or to *. Ignoring.",opt->value); + goto cleanup; + } + /* 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) { + log_warn(LD_CONFIG, + "Skipping invalid argument '%s' to MapAddress: " + "can only use wildcard (i.e. '*.') if 'from' address " + "uses wildcard also", opt->value); + goto cleanup; + } + + if (address_is_invalid_destination(to, 1)) { + log_warn(LD_CONFIG, + "Skipping invalid argument '%s' to MapAddress", opt->value); + goto cleanup; + } + + addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC, + from_wildcard, to_wildcard); + + 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); } @@ -4381,14 +4820,43 @@ options_init_logs(or_options_t *options, int validate_only) int ok; smartlist_t *elts; int daemon = -#ifdef MS_WINDOWS +#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_create(); + elts = smartlist_new(); for (opt = options->Logs; opt; opt = opt->next) { log_severity_list_t *severity; @@ -4476,20 +4944,38 @@ parse_bridge_line(const char *line, int validate_only) smartlist_t *items = NULL; int r; char *addrport=NULL, *fingerprint=NULL; + char *transport_name=NULL; + char *field1=NULL; tor_addr_t addr; uint16_t port = 0; char digest[DIGEST_LEN]; - items = smartlist_create(); + 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; } - addrport = smartlist_get(items, 0); + + /* field1 is either a transport name or addrport */ + field1 = smartlist_get(items, 0); smartlist_del_keeporder(items, 0); - if (tor_addr_port_parse(addrport, &addr, &port)<0) { + + if (!(strstr(field1, ".") || strstr(field1, ":"))) { + /* new-style bridge line */ + transport_name = field1; + if (smartlist_len(items) < 1) { + log_warn(LD_CONFIG, "Too few items to Bridge line."); + goto err; + } + addrport = smartlist_get(items, 0); + smartlist_del_keeporder(items, 0); + } else { + addrport = field1; + } + + if (tor_addr_port_lookup(addrport, &addr, &port)<0) { log_warn(LD_CONFIG, "Error parsing Bridge address '%s'", addrport); goto err; } @@ -4513,26 +4999,282 @@ parse_bridge_line(const char *line, int validate_only) } if (!validate_only) { - log_debug(LD_DIR, "Bridge at %s:%d (%s)", fmt_addr(&addr), - (int)port, + log_debug(LD_DIR, "Bridge at %s:%d (transport: %s) (%s)", + fmt_addr(&addr), (int)port, + transport_name ? transport_name : "no transport", fingerprint ? fingerprint : "no key listed"); - bridge_add_from_config(&addr, port, fingerprint ? digest : NULL); + bridge_add_from_config(&addr, port, + fingerprint ? digest : NULL, transport_name); } r = 0; goto done; - err: + err: r = -1; - done: + done: SMARTLIST_FOREACH(items, char*, s, tor_free(s)); smartlist_free(items); tor_free(addrport); + tor_free(transport_name); tor_free(fingerprint); return r; } +/** Read the contents of a ClientTransportPlugin line from + * <b>line</b>. Return 0 if the line is well-formed, and -1 if it + * isn't. + * + * If <b>validate_only</b> is 0, and the line is well-formed: + * - 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_client_transport_line(const char *line, int validate_only) +{ + smartlist_t *items = NULL; + int r; + char *field2=NULL; + + const char *transports=NULL; + smartlist_t *transport_list=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 line_length; + + 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 ClientTransportPlugin line."); + 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) { + if (!string_is_C_identifier(transport_name)) { + log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).", + transport_name); + goto err; + } + } SMARTLIST_FOREACH_END(transport_name); + + /* field2 is either a SOCKS version or "exec" */ + field2 = smartlist_get(items, 1); + + if (!strcmp(field2,"socks4")) { + socks_ver = PROXY_SOCKS4; + } else if (!strcmp(field2,"socks5")) { + socks_ver = PROXY_SOCKS5; + } else if (!strcmp(field2,"exec")) { + is_managed=1; + } else { + log_warn(LD_CONFIG, "Strange ClientTransportPlugin field '%s'.", + field2); + goto err; + } + + if (is_managed) { /* managed */ + if (!validate_only) { /* if we are not just validating, use the + rest of the line as the argv of the proxy + to be launched */ + proxy_argc = line_length-2; + tor_assert(proxy_argc > 0); + proxy_argv = tor_malloc_zero(sizeof(char*)*(proxy_argc+1)); + 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 NUL pointer, just like execve() likes it*/ + + /* kickstart the thing */ + pt_kickstart_client_proxy(transport_list, proxy_argv); + } + } else { /* external */ + if (smartlist_len(transport_list) != 1) { + log_warn(LD_CONFIG, "You can't have an external proxy with " + "more than one transports."); + 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) { + transport_add_from_config(&addr, port, smartlist_get(transport_list, 0), + socks_ver); + + log_info(LD_DIR, "Transport '%s' found at %s:%d", + transports, fmt_addr(&addr), (int)port); + } + } + + 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; +} + +/** Read the contents of a ServerTransportPlugin line from + * <b>line</b>. Return 0 if the line is well-formed, and -1 if it + * isn't. + * If <b>validate_only</b> is 0, the line is well-formed, and it's a + * managed proxy line, launch the managed proxy. */ +static int +parse_server_transport_line(const char *line, int validate_only) +{ + 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; + + /* managed proxy options */ + int is_managed=0; + char **proxy_argv=NULL; + char **tmp=NULL; + int proxy_argc,i; + + int line_length; + + 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 ServerTransportPlugin line."); + 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) { + if (!string_is_C_identifier(transport_name)) { + log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).", + transport_name); + goto err; + } + } SMARTLIST_FOREACH_END(transport_name); + + type = smartlist_get(items, 1); + + if (!strcmp(type, "exec")) { + is_managed=1; + } else if (!strcmp(type, "proxy")) { + is_managed=0; + } else { + log_warn(LD_CONFIG, "Strange ServerTransportPlugin type '%s'", type); + goto err; + } + + if (is_managed) { /* managed */ + if (!validate_only) { + proxy_argc = line_length-2; + tor_assert(proxy_argc > 0); + proxy_argv = tor_malloc_zero(sizeof(char*)*(proxy_argc+1)); + 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 NUL pointer, just like execve() likes it*/ + + /* kickstart the thing */ + pt_kickstart_server_proxy(transport_list, proxy_argv); + } + } else { /* external */ + if (smartlist_len(transport_list) != 1) { + log_warn(LD_CONFIG, "You can't have an external proxy with " + "more than one transports."); + 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, "Server transport '%s' at %s:%d.", + transports, fmt_addr(&addr), (int)port); + } + } + + 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; +} + /** Read the contents of a DirServer 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> @@ -4540,7 +5282,7 @@ parse_bridge_line(const char *line, int validate_only) * bits it's missing) as a valid authority. Return 0 on success, * or -1 if the line isn't well-formed or if we can't add it. */ static int -parse_dir_server_line(const char *line, authority_type_t required_type, +parse_dir_server_line(const char *line, dirinfo_type_t required_type, int validate_only) { smartlist_t *items = NULL; @@ -4549,10 +5291,10 @@ parse_dir_server_line(const char *line, authority_type_t required_type, uint16_t dir_port = 0, or_port = 0; char digest[DIGEST_LEN]; char v3_digest[DIGEST_LEN]; - authority_type_t type = V2_AUTHORITY; + dirinfo_type_t type = V2_DIRINFO; int is_not_hidserv_authority = 0, is_not_v2_authority = 0; - items = smartlist_create(); + items = smartlist_new(); smartlist_split_string(items, line, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); if (smartlist_len(items) < 1) { @@ -4570,13 +5312,13 @@ parse_dir_server_line(const char *line, authority_type_t required_type, if (TOR_ISDIGIT(flag[0])) break; if (!strcasecmp(flag, "v1")) { - type |= (V1_AUTHORITY | HIDSERV_AUTHORITY); + type |= (V1_DIRINFO | HIDSERV_DIRINFO); } else if (!strcasecmp(flag, "hs")) { - type |= HIDSERV_AUTHORITY; + type |= HIDSERV_DIRINFO; } else if (!strcasecmp(flag, "no-hs")) { is_not_hidserv_authority = 1; } else if (!strcasecmp(flag, "bridge")) { - type |= BRIDGE_AUTHORITY; + type |= BRIDGE_DIRINFO; } else if (!strcasecmp(flag, "no-v2")) { is_not_v2_authority = 1; } else if (!strcasecmpstart(flag, "orport=")) { @@ -4593,7 +5335,7 @@ parse_dir_server_line(const char *line, authority_type_t required_type, log_warn(LD_CONFIG, "Bad v3 identity digest '%s' on DirServer line", flag); } else { - type |= V3_AUTHORITY; + type |= V3_DIRINFO|EXTRAINFO_DIRINFO|MICRODESC_DIRINFO; } } else { log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirServer line", @@ -4603,9 +5345,9 @@ parse_dir_server_line(const char *line, authority_type_t required_type, smartlist_del_keeporder(items, 0); } if (is_not_hidserv_authority) - type &= ~HIDSERV_AUTHORITY; + type &= ~HIDSERV_DIRINFO; if (is_not_v2_authority) - type &= ~V2_AUTHORITY; + type &= ~V2_DIRINFO; if (smartlist_len(items) < 2) { log_warn(LD_CONFIG, "Too few arguments to DirServer line."); @@ -4613,7 +5355,7 @@ parse_dir_server_line(const char *line, authority_type_t required_type, } addrport = smartlist_get(items, 0); smartlist_del_keeporder(items, 0); - if (parse_addr_port(LOG_WARN, addrport, &address, NULL, &dir_port)<0) { + if (addr_port_lookup(LOG_WARN, addrport, &address, NULL, &dir_port)<0) { log_warn(LD_CONFIG, "Error parsing DirServer address '%s'", addrport); goto err; } @@ -4633,7 +5375,7 @@ parse_dir_server_line(const char *line, authority_type_t required_type, * clause once Tor 0.1.2.17 is obsolete. */ log_warn(LD_CONFIG, "Dangerous dirserver line. To correct, erase your " "torrc file (%s), or reinstall Tor and use the default torrc.", - get_torrc_fname()); + get_torrc_fname(0)); goto err; } if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) { @@ -4668,12 +5410,669 @@ parse_dir_server_line(const char *line, authority_type_t required_type, return r; } +/** 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> that is on a publicly routable + * address. */ +static void +warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname) +{ + SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { + 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 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.", portname); + } else if (!tor_addr_is_loopback(&port->addr)) { + log_notice(LD_CONFIG, "You configured a non-loopback address for " + "%sPort. This allows everybody on your local network to use " + "your machine as a proxy. Make sure this is what you wanted.", + portname); + } + } SMARTLIST_FOREACH_END(port); +} + +/** DOCDOC */ +static void +warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid) +{ + 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) { + 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); +} + +#define CL_PORT_NO_OPTIONS (1u<<0) +#define CL_PORT_WARN_NONLOCAL (1u<<1) +#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2) +#define CL_PORT_SERVER_OPTIONS (1u<<3) +#define CL_PORT_FORBID_NONLOCAL (1u<<4) + +/** + * Parse port configuration for a single port type. + * + * Read entries of the "FooPort" type from the list <b>ports</b>, and + * entries of the "FooListenAddress" type from the list + * <b>listenaddrs</b>. Two syntaxes are supported: a legacy syntax + * where FooPort is at most a single entry containing a port number and + * where FooListenAddress has any number of address:port combinations; + * and a new syntax where there are no FooListenAddress entries and + * where 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_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 contrl port with no password set: don't even allow it. + * + * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn + * if FooListenAddress is set but FooPort is 0. + * + * 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. + * + * 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 config_line_t *listenaddrs, + const char *portname, + int listener_type, + const char *defaultaddr, + int defaultport, + unsigned flags) +{ + smartlist_t *elts; + int retval = -1; + const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER); + const unsigned allow_no_options = flags & CL_PORT_NO_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 allow_spurious_listenaddr = + flags & CL_PORT_ALLOW_EXTRA_LISTENADDR; + + /* FooListenAddress is deprecated; let's make it work like it used to work, + * though. */ + if (listenaddrs) { + int mainport = defaultport; + + if (ports && ports->next) { + log_warn(LD_CONFIG, "%sListenAddress can't be used when there are " + "multiple %sPort lines", portname, portname); + return -1; + } else if (ports) { + if (!strcmp(ports->value, "auto")) { + mainport = CFG_AUTO_PORT; + } else { + int ok; + mainport = (int)tor_parse_long(ports->value, 10, 0, 65535, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, "%sListenAddress can only be used with a single " + "%sPort with value \"auto\" or 1-65535.", + portname, portname); + return -1; + } + } + } + + if (mainport == 0) { + if (allow_spurious_listenaddr) + return 1; + log_warn(LD_CONFIG, "%sPort must be defined if %sListenAddress is used", + portname, portname); + return -1; + } + + if (use_server_options && out) { + /* Add a no_listen port. */ + port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); + cfg->type = listener_type; + cfg->port = mainport; + tor_addr_make_unspec(&cfg->addr); /* Server ports default to 0.0.0.0 */ + cfg->no_listen = 1; + cfg->ipv4_only = 1; + smartlist_add(out, cfg); + } + + for (; listenaddrs; listenaddrs = listenaddrs->next) { + tor_addr_t addr; + uint16_t port = 0; + if (tor_addr_port_lookup(listenaddrs->value, &addr, &port) < 0) { + log_warn(LD_CONFIG, "Unable to parse %sListenAddress '%s'", + portname, listenaddrs->value); + return -1; + } + if (out) { + port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); + cfg->type = listener_type; + cfg->port = port ? port : mainport; + tor_addr_copy(&cfg->addr, &addr); + cfg->session_group = SESSION_GROUP_UNSET; + cfg->isolation_flags = ISO_DEFAULT; + cfg->no_advertise = 1; + smartlist_add(out, cfg); + } + } + + if (warn_nonlocal && out) { + if (is_control) + warn_nonlocal_controller_ports(out, forbid_nonlocal); + else + warn_nonlocal_client_ports(out, portname); + } + return 0; + } /* end if (listenaddrs) */ + + /* No ListenAddress lines. If there's no FooPort, then maybe make a default + * one. */ + if (! ports) { + if (defaultport && out) { + port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); + cfg->type = listener_type; + cfg->port = defaultport; + tor_addr_parse(&cfg->addr, defaultaddr); + cfg->session_group = SESSION_GROUP_UNSET; + 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(); + + for (; ports; ports = ports->next) { + tor_addr_t addr; + int port; + int sessiongroup = SESSION_GROUP_UNSET; + unsigned isolation = ISO_DEFAULT; + + char *addrport; + uint16_t ptmp=0; + int ok; + int no_listen = 0, no_advertise = 0, all_addrs = 0, + ipv4_only = 0, ipv6_only = 0; + + smartlist_split_string(elts, ports->value, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(elts) == 0) { + log_warn(LD_CONFIG, "Invalid %sPort line with no value", portname); + goto err; + } + + if (allow_no_options && smartlist_len(elts) > 1) { + log_warn(LD_CONFIG, "Too many options on %sPort line", portname); + goto err; + } + + /* Now parse the addr/port value */ + addrport = smartlist_get(elts, 0); + if (!strcmp(addrport, "auto")) { + port = CFG_AUTO_PORT; + tor_addr_parse(&addr, defaultaddr); + } 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; + } + } 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) { + tor_addr_parse(&addr, defaultaddr); + } 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; + } + } + + /* 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 (elt_sl_idx == 0) + continue; /* Skip addr:port */ + + 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 + } else if (!strcasecmp(elt, "IPv4Only")) { + ipv4_only = 1; + } else if (!strcasecmp(elt, "IPv6Only")) { + 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 (ipv4_only && ipv6_only) { + log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only " + "on %sPort line '%s'", + portname, escaped(ports->value)); + goto err; + } + if (ipv4_only && tor_addr_family(&addr) == AF_INET6) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", + portname); + goto err; + } + if (ipv6_only && tor_addr_family(&addr) == AF_INET) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4", + 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 (elt_sl_idx == 0) + continue; /* Skip addr:port */ + if (!strcasecmpstart(elt, "SessionGroup=")) { + int group = (int)tor_parse_long(elt+strlen("SessionGroup="), + 10, 0, INT_MAX, &ok, NULL); + if (!ok) { + 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 (!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 (out && port) { + port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); + tor_addr_copy(&cfg->addr, &addr); + cfg->port = port; + cfg->type = listener_type; + cfg->isolation_flags = isolation; + cfg->session_group = sessiongroup; + cfg->no_advertise = no_advertise; + cfg->no_listen = no_listen; + cfg->all_addrs = all_addrs; + cfg->ipv4_only = ipv4_only; + cfg->ipv6_only = ipv6_only; + + smartlist_add(out, cfg); + } + SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); + smartlist_clear(elts); + } + + if (warn_nonlocal && out) { + if (is_control) + warn_nonlocal_controller_ports(out, forbid_nonlocal); + else + warn_nonlocal_client_ports(out, portname); + } + + retval = 0; + err: + SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); + smartlist_free(elts); + return retval; +} + +/** DOCDOC */ +static int +parse_socket_config(smartlist_t *out, const config_line_t *cfg, + int listener_type) +{ + + if (!out) + return 0; + + for ( ; cfg; cfg = cfg->next) { + size_t len = strlen(cfg->value); + port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); + port->is_unix_addr = 1; + memcpy(port->unix_addr, cfg->value, len+1); + port->type = listener_type; + smartlist_add(out, port); + } + + return 0; +} + +/** Parse all client port types (Socks, DNS, Trans, NATD) from + * <b>options</b>. On success, set *<b>n_ports_out</b> to the number of + * ports that are listed 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(const or_options_t *options, int validate_only, + char **msg, int *n_ports_out) +{ + smartlist_t *ports; + int retval = -1; + + ports = smartlist_new(); + + *n_ports_out = 0; + + if (parse_port_config(ports, + options->SocksPort, options->SocksListenAddress, + "Socks", CONN_TYPE_AP_LISTENER, + "127.0.0.1", 9050, + CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR) < 0) { + *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration"); + goto err; + } + if (parse_port_config(ports, + options->DNSPort, options->DNSListenAddress, + "DNS", CONN_TYPE_AP_DNS_LISTENER, + "127.0.0.1", 0, + CL_PORT_WARN_NONLOCAL) < 0) { + *msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration"); + goto err; + } + if (parse_port_config(ports, + options->TransPort, options->TransListenAddress, + "Trans", CONN_TYPE_AP_TRANS_LISTENER, + "127.0.0.1", 0, + CL_PORT_WARN_NONLOCAL) < 0) { + *msg = tor_strdup("Invalid TransPort/TransListenAddress configuration"); + goto err; + } + if (parse_port_config(ports, + options->NATDPort, options->NATDListenAddress, + "NATD", CONN_TYPE_AP_NATD_LISTENER, + "127.0.0.1", 0, + CL_PORT_WARN_NONLOCAL) < 0) { + *msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration"); + goto err; + } + { + unsigned control_port_flags = CL_PORT_NO_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 (parse_port_config(ports, + options->ControlPort, options->ControlListenAddress, + "Control", CONN_TYPE_CONTROL_LISTENER, + "127.0.0.1", 0, + control_port_flags) < 0) { + *msg = tor_strdup("Invalid ControlPort/ControlListenAddress " + "configuration"); + goto err; + } + if (parse_socket_config(ports, + options->ControlSocket, + CONN_TYPE_CONTROL_LISTENER) < 0) { + *msg = tor_strdup("Invalid ControlSocket configuration"); + goto err; + } + } + if (! options->ClientOnly) { + if (parse_port_config(ports, + options->ORPort, options->ORListenAddress, + "OR", CONN_TYPE_OR_LISTENER, + "0.0.0.0", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid ORPort/ORListenAddress configuration"); + goto err; + } + if (parse_port_config(ports, + options->DirPort, options->DirListenAddress, + "Dir", CONN_TYPE_DIR_LISTENER, + "0.0.0.0", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid DirPort/DirListenAddress configuration"); + goto err; + } + } + + if (check_server_ports(ports, options) < 0) { + *msg = tor_strdup("Misconfigured server ports"); + goto err; + } + + *n_ports_out = smartlist_len(ports); + + 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. */ + } + + retval = 0; + err: + if (ports) { + SMARTLIST_FOREACH(ports, port_cfg_t *, p, port_cfg_free(p)); + smartlist_free(ports); + } + return retval; +} + +/** DOCDOC */ +static int +check_server_ports(const smartlist_t *ports, + const or_options_t *options) +{ + 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->no_advertise) + ++n_dirport_advertised; + if (! port->no_listen) + ++n_dirport_listeners; + } else if (port->type == CONN_TYPE_OR_LISTENER) { + if (! port->no_advertise) { + ++n_orport_advertised; + if (tor_addr_family(&port->addr) == AF_INET || + (tor_addr_family(&port->addr) == AF_UNSPEC && !port->ipv6_only)) + ++n_orport_advertised_ipv4; + } + if (! port->no_listen) + ++n_orport_listeners; + } else { + continue; + } +#ifndef _WIN32 + if (!port->no_advertise && 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_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 non-bridge only to listen on an IPv6 " + "address."); + r = -1; + } + + if (n_low_port && options->AccountingMax) { + log(LOG_WARN, LD_CONFIG, + "You have set AccountingMax to use hibernation. You have also " + "chosen a low DirPort or OrPort. 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."); + } + + return r; +} + +/** Return a list of port_cfg_t for client ports parsed from the + * options. */ +const smartlist_t * +get_configured_ports(void) +{ + if (!configured_ports) + configured_ports = smartlist_new(); + return configured_ports; +} + +/** Return the first advertised port of type <b>listener_type</b> in + <b>address_family</b>. */ +int +get_first_advertised_port_by_type_af(int listener_type, int address_family) +{ + if (!configured_ports) + return 0; + SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) { + if (cfg->type == listener_type && + !cfg->no_advertise && + (tor_addr_family(&cfg->addr) == address_family || + tor_addr_family(&cfg->addr) == AF_UNSPEC)) { + if (tor_addr_family(&cfg->addr) != AF_UNSPEC || + (address_family == AF_INET && !cfg->ipv6_only) || + (address_family == AF_INET6 && !cfg->ipv4_only)) { + return cfg->port; + } + } + } SMARTLIST_FOREACH_END(cfg); + return 0; +} + /** Adjust the value of options->DataDirectory, or fill it in if it's * absent. Return 0 on success, -1 on failure. */ static int normalize_data_directory(or_options_t *options) { -#ifdef MS_WINDOWS +#ifdef _WIN32 char *p; if (options->DataDirectory) return 0; /* all set */ @@ -4739,7 +6138,7 @@ validate_data_directory(or_options_t *options) * 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, or_options_t *options) +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; @@ -4774,18 +6173,12 @@ write_configuration_file(const char *fname, or_options_t *options) if (rename_old) { int i = 1; - size_t fn_tmp_len = strlen(fname)+32; - char *fn_tmp; - tor_assert(fn_tmp_len > strlen(fname)); /*check for overflow*/ - fn_tmp = tor_malloc(fn_tmp_len); + char *fn_tmp = NULL; while (1) { - if (tor_snprintf(fn_tmp, fn_tmp_len, "%s.orig.%d", fname, i)<0) { - log_warn(LD_BUG, "tor_snprintf failed inexplicably"); - tor_free(fn_tmp); - goto err; - } + 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); @@ -4824,7 +6217,7 @@ options_save_current(void) * 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(), get_options()); + return write_configuration_file(get_torrc_fname(0), get_options()); } /** Mapping from a unit name to a multiplier for converting that unit into a @@ -4880,6 +6273,26 @@ static struct unit_table_t time_units[] = { { 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. @@ -4943,6 +6356,25 @@ config_parse_memunit(const char *s, int *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 (!ok) + return -1; + 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. @@ -4962,13 +6394,29 @@ config_parse_interval(const char *s, int *ok) return (int)r; } +/** 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(void) +init_libevent(const or_options_t *options) { const char *badness=NULL; + tor_libevent_cfg cfg; + + tor_assert(options); configure_libevent_logging(); /* If the kernel complains that some method (say, epoll) doesn't @@ -4978,12 +6426,17 @@ init_libevent(void) tor_check_libevent_header_compatibility(); - tor_libevent_initialize(); + memset(&cfg, 0, sizeof(cfg)); + cfg.disable_iocp = options->DisableIOCP; + cfg.num_cpus = get_num_cpus(options); + cfg.msec_per_tick = options->TokenBucketRefillInterval; + + tor_libevent_initialize(&cfg); suppress_libevent_log_msg(NULL); tor_check_libevent_version(tor_libevent_get_method(), - get_options()->ORPort != 0, + get_options()->ORPort != NULL, &badness); if (badness) { const char *v = tor_libevent_get_version_str(); @@ -5017,7 +6470,7 @@ get_or_state(void) * Note: Consider using the get_datadir_fname* macros in or.h. */ char * -options_get_datadir_fname2_suffix(or_options_t *options, +options_get_datadir_fname2_suffix(const or_options_t *options, const char *sub1, const char *sub2, const char *suffix) { @@ -5052,6 +6505,69 @@ options_get_datadir_fname2_suffix(or_options_t *options, return fname; } +/** 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; +} + /** 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 @@ -5070,6 +6586,9 @@ or_state_validate(or_state_t *old_state, or_state_t *state, if (entry_guards_parse_state(state, 0, msg)<0) return -1; + if (validate_transports_in_state(state)<0) + return -1; + return 0; } @@ -5106,13 +6625,13 @@ or_state_save_broken(char *fname) { int i; file_status_t status; - size_t len = strlen(fname)+16; - char *fname2 = tor_malloc(len); + char *fname2 = NULL; for (i = 0; i < 100; ++i) { - tor_snprintf(fname2, len, "%s.%d", fname, 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 " @@ -5164,7 +6683,7 @@ or_state_load(void) if (contents) { config_line_t *lines=NULL; int assign_retval; - if (config_get_lines(contents, &lines)<0) + if (config_get_lines(contents, &lines, 0)<0) goto done; assign_retval = config_assign(&state_format, new_state, lines, 0, 0, &errmsg); @@ -5268,7 +6787,7 @@ or_state_save(time_t now) tor_free(global_state->TorVersion); tor_asprintf(&global_state->TorVersion, "Tor %s", get_version()); - state = config_dump(&state_format, global_state, 1, 0); + 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" @@ -5302,6 +6821,158 @@ or_state_save(time_t now) 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 = 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); + tor_asprintf(&transport_addrport, "%s:%d", fmt_addr(addr), (int)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 */ + tor_asprintf(&transport_line->value, "%s %s:%d", transport, + fmt_addr(addr), + (int) port); /* replace old addrport line with new line */ + } + } 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:%d", transport, + fmt_addr(addr), (int) port); + + next = &(line->next); + } + + if (!get_options()->AvoidDiskWrites) + or_state_mark_dirty(state, 0); + + done: + tor_free(transport_addrport); +} + /** Given a file name check to see whether the file exists but has not been * modified for a very long time. If so, remove it. */ void @@ -5331,21 +7002,22 @@ getinfo_helper_config(control_connection_t *conn, (void) conn; (void) errmsg; if (!strcmp(question, "config/names")) { - smartlist_t *sl = smartlist_create(); + smartlist_t *sl = smartlist_new(); int i; for (i = 0; _option_vars[i].name; ++i) { - config_var_t *var = &_option_vars[i]; + const config_var_t *var = &_option_vars[i]; const char *type; - char *line; 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_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; @@ -5358,8 +7030,7 @@ getinfo_helper_config(control_connection_t *conn, } if (!type) continue; - tor_asprintf(&line, "%s %s\n",var->name,type); - smartlist_add(sl, line); + 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)); |