diff options
Diffstat (limited to 'src/or/config.c')
-rw-r--r-- | src/or/config.c | 2128 |
1 files changed, 1746 insertions, 382 deletions
diff --git a/src/or/config.c b/src/or/config.c index 8972506477..a0cf3c80ad 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" +#include "transports.h" #ifdef MS_WINDOWS #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. */ @@ -175,6 +184,8 @@ static config_var_t _option_vars[] = { V(AuthDirBadDir, LINELIST, NULL), V(AuthDirBadExit, LINELIST, NULL), V(AuthDirInvalid, LINELIST, NULL), + V(AuthDirFastGuarantee, MEMUNIT, "100 KB"), + V(AuthDirGuardBWGuarantee, MEMUNIT, "250 KB"), V(AuthDirReject, LINELIST, NULL), V(AuthDirRejectUnlisted, BOOL, "0"), V(AuthDirListBadDirs, BOOL, "0"), @@ -199,10 +210,12 @@ 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), @@ -215,8 +228,10 @@ 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(DisableNetwork, BOOL, "0"), V(DirAllowPrivateAddresses, BOOL, NULL), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), V(DirListenAddress, LINELIST, NULL), @@ -229,10 +244,13 @@ static config_var_t _option_vars[] = { OBSOLETE("DirRecordUsageGranularity"), OBSOLETE("DirRecordUsageRetainIPs"), OBSOLETE("DirRecordUsageSaveInterval"), - V(DirReqStatistics, BOOL, "0"), + 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"), @@ -246,7 +264,7 @@ static config_var_t _option_vars[] = { V(ExitPolicy, LINELIST, NULL), V(ExitPolicyRejectPrivate, BOOL, "1"), V(ExitPortStatistics, BOOL, "0"), - V(ExtraInfoStatistics, BOOL, "0"), + V(ExtraInfoStatistics, BOOL, "1"), #if defined (WINCE) V(FallbackNetworkstatusFile, FILENAME, "fallback-consensus"), @@ -269,8 +287,11 @@ static config_var_t _option_vars[] = { V(GeoIPFile, FILENAME, SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip"), #endif + V(GiveGuardFlagTo_CVE_2011_2768_VulnerableRelays, + 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,6 +310,7 @@ static config_var_t _option_vars[] = { 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), @@ -300,23 +322,25 @@ 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"), @@ -326,6 +350,9 @@ static config_var_t _option_vars[] = { 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"), @@ -337,7 +364,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"), @@ -362,23 +389,26 @@ 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(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"), @@ -394,6 +424,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"), @@ -410,7 +441,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"), @@ -419,6 +450,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"), @@ -430,6 +462,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 @@ -455,6 +488,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, ""), @@ -481,7 +517,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 } }; @@ -532,39 +567,48 @@ 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_client_ports(const or_options_t *options, int validate_only, + char **msg_out, int *n_ports_out); 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); @@ -574,8 +618,9 @@ 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. */ @@ -602,7 +647,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), @@ -618,14 +663,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 client-level (SOCKS, DNS, Trans, NATD) ports. */ +static smartlist_t *configured_client_ports = NULL; /** Return the contents of our frontpage string, or NULL if not configured. */ const char * @@ -636,7 +687,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; @@ -646,12 +697,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. @@ -659,6 +717,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 @@ -673,7 +734,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_create(); + 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; @@ -709,6 +797,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); + } config_free(&options_format, options); } @@ -719,6 +812,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; @@ -726,7 +821,15 @@ config_free_all(void) config_free_lines(global_cmdline_options); global_cmdline_options = NULL; + if (configured_client_ports) { + SMARTLIST_FOREACH(configured_client_ports, + port_cfg_t *, p, tor_free(p)); + smartlist_free(configured_client_ports); + configured_client_ports = NULL; + } + tor_free(torrc_fname); + tor_free(torrc_defaults_fname); tor_free(_version); tor_free(global_dirfrontpagecontents); } @@ -791,7 +894,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[] = { @@ -865,16 +968,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; } @@ -883,8 +986,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 = @@ -905,27 +1008,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; } @@ -937,12 +1041,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(); 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; @@ -971,6 +1075,7 @@ options_act_reversible(or_options_t *old_options, char **msg) #endif if (running_tor) { + int n_client_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) { @@ -982,18 +1087,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 client port configuration so we can launch listeners. */ + if (parse_client_ports(options, 0, msg, &n_client_ports)) { + if (!*msg) + *msg = tor_strdup("Unexpected problem parsing client 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) @@ -1038,7 +1162,7 @@ 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)) { + 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", @@ -1114,7 +1238,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; @@ -1139,7 +1263,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) @@ -1153,7 +1277,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) @@ -1162,6 +1286,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. @@ -1172,15 +1314,23 @@ 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 */ + if (options->DisableDebuggerAttachment) { + tor_disable_debugger_attach(); + } else { + log_notice(LD_CONFIG,"Debugger attachment enabled " + "for unprivileged users."); + } + if (running_tor && !have_lockfile()) { if (try_locking(options, 1) < 0) return -1; @@ -1220,6 +1370,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) @@ -1231,6 +1407,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 || @@ -1240,6 +1439,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 @@ -1279,17 +1485,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()); @@ -1408,44 +1613,69 @@ options_act(or_options_t *old_options) tor_free(actual_fname); } - if (options->DirReqStatistics && !geoip_is_loaded()) { - /* Check if GeoIP database could be loaded. */ - log_warn(LD_CONFIG, "Configured to measure directory request " - "statistics, but no GeoIP database found!"); - return -1; - } - - if (options->EntryStatistics) { - if (should_record_bridge_info(options)) { - /* Don't allow measuring statistics on entry guards when configured - * as bridge. */ - log_warn(LD_CONFIG, "Bridges cannot be configured to measure " - "additional GeoIP statistics as entry guards."); - return -1; - } else if (!geoip_is_loaded()) { - /* Check if GeoIP database could be loaded. */ - log_warn(LD_CONFIG, "Configured to measure entry node statistics, " - "but no GeoIP database found!"); - return -1; - } - } - 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; + + /* If we aren't acting as a server, we can't collect stats anyway. */ + if (!server_mode(options)) { + options->CellStatistics = 0; + options->DirReqStatistics = 0; + options->EntryStatistics = 0; + options->ExitPortStatistics = 0; + } + if ((!old_options || !old_options->CellStatistics) && - options->CellStatistics) + options->CellStatistics) { rep_hist_buffer_stats_init(now); + print_notice = 1; + } if ((!old_options || !old_options->DirReqStatistics) && - options->DirReqStatistics) - geoip_dirreq_stats_init(now); + options->DirReqStatistics) { + if (geoip_is_loaded()) { + geoip_dirreq_stats_init(now); + print_notice = 1; + } else { + options->DirReqStatistics = 0; + /* 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) && - options->EntryStatistics) - geoip_entry_stats_init(now); + options->EntryStatistics && !should_record_bridge_info(options)) { + if (geoip_is_loaded()) { + geoip_entry_stats_init(now); + print_notice = 1; + } else { + options->EntryStatistics = 0; + log_notice(LD_CONFIG, "Configured to measure entry node " + "statistics, but no GeoIP database found. " + "Please specify a GeoIP database using the " + "GeoIPFile option."); + } + } if ((!old_options || !old_options->ExitPortStatistics) && - options->ExitPortStatistics) + options->ExitPortStatistics) { rep_hist_exit_stats_init(now); - if (!old_options) + 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 " "data directory in 24 hours from now."); @@ -1463,6 +1693,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 && @@ -1520,7 +1756,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; @@ -1558,7 +1794,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; @@ -1575,13 +1815,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]; @@ -1590,15 +1823,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; @@ -1613,7 +1864,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; @@ -1626,9 +1877,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; @@ -1644,13 +1898,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); @@ -1680,12 +1951,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); @@ -1710,9 +1978,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) @@ -1730,11 +2009,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); @@ -1777,6 +2056,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) { @@ -1800,6 +2091,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); @@ -1844,7 +2149,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); @@ -1860,6 +2177,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. @@ -1870,11 +2209,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); @@ -1902,8 +2241,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, @@ -1913,6 +2253,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 && @@ -1935,10 +2277,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); @@ -1953,7 +2295,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); } @@ -1962,14 +2304,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); } @@ -2007,7 +2349,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; @@ -2022,10 +2364,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); @@ -2071,6 +2413,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. */ @@ -2086,6 +2429,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. */ @@ -2198,7 +2549,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; @@ -2235,6 +2586,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; } @@ -2260,7 +2617,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. */ } @@ -2282,7 +2639,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 */ @@ -2298,11 +2656,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; @@ -2336,8 +2698,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; @@ -2377,7 +2739,7 @@ list_torrc_options(void) int i; smartlist_t *lines = smartlist_create(); 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; @@ -2396,7 +2758,7 @@ static uint32_t last_resolved_addr = 0; * public IP address. */ 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; @@ -2405,7 +2767,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; @@ -2447,48 +2809,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 @@ -2496,7 +2853,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) { @@ -2504,19 +2862,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) { @@ -2535,11 +2894,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; } @@ -2574,7 +2934,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; @@ -2613,8 +2973,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; @@ -2631,7 +2992,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; @@ -2695,7 +3056,7 @@ is_listening_on_low_port(int port_option, return (port_option < 1024); for (l = listen_options; l; l = l->next) { - parse_addr_port(LOG_WARN, l->value, NULL, NULL, &p); + addr_port_lookup(LOG_WARN, l->value, NULL, NULL, &p); if (p<1024) { return 1; } @@ -2707,10 +3068,10 @@ is_listening_on_low_port(int port_option, /** 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) { @@ -2726,24 +3087,30 @@ 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(); @@ -2785,7 +3152,8 @@ config_dump(config_format_t *fmt, void *options, int minimal, 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; } @@ -2794,9 +3162,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 @@ -2853,24 +3222,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 " @@ -2898,6 +3267,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 @@ -2917,6 +3290,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_client_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 @@ -2940,57 +3314,8 @@ options_validate(or_options_t *old_options, or_options_t *options, 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_client_ports(options, 1, msg, &n_client_ports) < 0) + return -1; if (validate_data_directory(options)<0) REJECT("Invalid DataDirectory"); @@ -3014,8 +3339,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."); @@ -3027,20 +3356,12 @@ 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 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) + if (n_client_ports == 0 && options->ORPort == 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. " @@ -3051,6 +3372,11 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("TransPort and TransListenAddress are disabled in this build."); #endif + if (options->TokenBucketRefillInterval <= 0 + || options->TokenBucketRefillInterval > 1000) { + REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive."); + } + if (options->AccountingMax && (is_listening_on_low_port(options->ORPort, options->ORListenAddress) || is_listening_on_low_port(options->DirPort, options->DirListenAddress))) @@ -3070,17 +3396,24 @@ options_validate(or_options_t *old_options, or_options_t *options, routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeNodes); } + if (options->NodeFamilies) { + options->NodeFamilySets = smartlist_create(); + 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"); @@ -3142,6 +3475,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; @@ -3290,9 +3632,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."); @@ -3335,6 +3677,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."); @@ -3359,6 +3708,12 @@ options_validate(or_options_t *old_options, or_options_t *options, if (ensure_bandwidth_cap(&options->PerConnBWBurst, "PerConnBWBurst", msg) < 0) return -1; + if (ensure_bandwidth_cap(&options->AuthDirFastGuarantee, + "AuthDirFastGuarantee", msg) < 0) + return -1; + if (ensure_bandwidth_cap(&options->AuthDirGuardBWGuarantee, + "AuthDirGuardBWGuarantee", msg) < 0) + return -1; if (options->RelayBandwidthRate && !options->RelayBandwidthBurst) options->RelayBandwidthBurst = options->RelayBandwidthRate; @@ -3411,7 +3766,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 */ @@ -3425,7 +3780,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 */ @@ -3439,7 +3794,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."); @@ -3449,7 +3804,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."); @@ -3458,8 +3813,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; @@ -3562,8 +3920,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) @@ -3576,11 +3938,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) { @@ -3763,17 +4134,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) @@ -3823,14 +4190,26 @@ 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; + } + 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 || @@ -3853,8 +4232,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. */ @@ -3867,6 +4246,7 @@ options_transition_affects_descriptor(or_options_t *old_options, old_options->ORPort != new_options->ORPort || 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) || @@ -3875,7 +4255,8 @@ 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; @@ -3939,17 +4320,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; + 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 } @@ -3982,37 +4371,46 @@ 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]); *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"); + char *fn = NULL; + if (!defaults_file) + fn = expand_filename("~/.torrc"); if (fn && file_status(fn) == FN_FILE) { fname = fn; } else { @@ -4027,43 +4425,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; } @@ -4074,7 +4477,7 @@ load_torrc_from_disk(int argc, char **argv) int options_init_from_torrc(int argc, char **argv) { - char *cf=NULL; + char *cf=NULL, *cf_defaults=NULL; int i, retval, command; static char **backup_argv; static int backup_argc; @@ -4134,13 +4537,15 @@ 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); + retval = options_init_from_string(cf_defaults, cf, command, command_arg, &errmsg); tor_free(cf); + tor_free(cf_defaults); if (retval < 0) goto err; @@ -4164,13 +4569,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); @@ -4183,17 +4588,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 */ @@ -4218,9 +4630,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; @@ -4228,6 +4640,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); @@ -4235,22 +4649,24 @@ 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; - } - retval = config_assign(&options_format, newoptions, - global_cmdline_options, 0, 0, msg); - 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); } } @@ -4269,11 +4685,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); @@ -4285,19 +4704,21 @@ 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; @@ -4346,6 +4767,35 @@ options_init_logs(or_options_t *options, int validate_only) 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(); @@ -4435,6 +4885,8 @@ 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]; @@ -4446,9 +4898,25 @@ parse_bridge_line(const char *line, int validate_only) 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; } @@ -4472,26 +4940,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_create(); + 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_create(); + 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_create(); + 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_create(); + 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> @@ -4499,7 +5223,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; @@ -4508,7 +5232,7 @@ 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(); @@ -4529,13 +5253,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=")) { @@ -4552,7 +5276,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", @@ -4562,9 +5286,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."); @@ -4572,7 +5296,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; } @@ -4592,7 +5316,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) { @@ -4627,6 +5351,374 @@ 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); +} + +#define CL_PORT_NO_OPTIONS (1u<<0) +#define CL_PORT_WARN_NONLOCAL (1u<<1) +#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2) + +/** + * Parse port configuration for a single client 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. + * + * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn + * if FooListenAddress is set but FooPort is 0. + * + * 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_client_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 allow_client_options = !(flags & CL_PORT_NO_OPTIONS); + const unsigned warn_nonlocal = flags & CL_PORT_WARN_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; + } + + 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; + smartlist_add(out, cfg); + } + } + + if (warn_nonlocal && out) + 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_create(); + + 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; + + 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_client_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. */ + 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)); + cfg->type = listener_type; + cfg->port = port; + tor_addr_copy(&cfg->addr, &addr); + cfg->session_group = sessiongroup; + cfg->isolation_flags = isolation; + smartlist_add(out, cfg); + } + SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); + smartlist_clear(elts); + } + + if (warn_nonlocal && out) + warn_nonlocal_client_ports(out, portname); + + retval = 0; + err: + SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); + smartlist_free(elts); + return retval; +} + +/** 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_client_ports(const or_options_t *options, int validate_only, + char **msg, int *n_ports_out) +{ + smartlist_t *ports; + int retval = -1; + + ports = smartlist_create(); + + *n_ports_out = 0; + + if (parse_client_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_client_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_client_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_client_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; + } + + *n_ports_out = smartlist_len(ports); + + if (!validate_only) { + if (configured_client_ports) { + SMARTLIST_FOREACH(configured_client_ports, + port_cfg_t *, p, port_cfg_free(p)); + smartlist_free(configured_client_ports); + } + configured_client_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; +} + +/** Return a list of port_cfg_t for client ports parsed from the + * options. */ +const smartlist_t * +get_configured_client_ports(void) +{ + if (!configured_client_ports) + configured_client_ports = smartlist_create(); + return configured_client_ports; +} + /** Adjust the value of options->DataDirectory, or fill it in if it's * absent. Return 0 on success, -1 on failure. */ static int @@ -4698,7 +5790,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; @@ -4783,7 +5875,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 @@ -4839,6 +5931,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. @@ -4902,6 +6014,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. @@ -4921,13 +6052,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 @@ -4937,7 +6084,12 @@ 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); @@ -4976,7 +6128,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) { @@ -5011,6 +6163,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_create(); + 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 @@ -5029,6 +6244,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; } @@ -5123,7 +6341,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); @@ -5227,7 +6445,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" @@ -5261,6 +6479,150 @@ 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_create(); + 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 static string containing the address:port a proxy + * transport should bind on. */ +const char * +get_bindaddr_for_transport(const char *transport) +{ + static const char default_addrport[] = "127.0.0.1:0"; + const char *bindaddr = NULL; + + config_line_t *line = get_transport_in_state_by_name(transport); + if (!line) + return default_addrport; + + bindaddr = get_transport_bindaddr(line->value, transport); + + return bindaddr ? bindaddr : 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 @@ -5293,7 +6655,7 @@ getinfo_helper_config(control_connection_t *conn, smartlist_t *sl = smartlist_create(); 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) { @@ -5302,9 +6664,11 @@ getinfo_helper_config(control_connection_t *conn, 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; |