diff --git a/ChangeLog b/ChangeLog index 1bd75ae9d3..125a064149 100644 --- a/ChangeLog +++ b/ChangeLog @@ -29,10 +29,20 @@ Changes in version 0.2.1.16-?? - 2009-??-?? o Minor bugfixes (on 0.2.0.x): - Log correct error messages for DNS-related network errors on Windows. + - Don't warn users about low port and hibernation mix when they + provide a *ListenAddress directive to fix that. Bugfix on + 0.2.1.15-rc. + - Fix a race condition that could cause crashes or memory + corruption when running as a server with a controller listening + for log messages. o Minor bugfixes (on 0.2.1.x): - When switching back and forth between bridge mode, do not start gathering GeoIP data until two hours have passed. + - Do not complain that the user has requested an excluded node as + an exit when the node is not really an exit. This could happen + because the circuit was for testing, or an introduction point. + Fix for bug 984. Changes in version 0.2.1.15-rc - 2009-05-25 diff --git a/src/common/compat.c b/src/common/compat.c index 35bb3a9ad3..d62b1ce1f4 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -2076,6 +2076,7 @@ tor_threads_init(void) pthread_mutexattr_init(&attr_reentrant); pthread_mutexattr_settype(&attr_reentrant, PTHREAD_MUTEX_RECURSIVE); threads_initialized = 1; + set_main_thread(); } } #elif defined(USE_WIN32_THREADS) @@ -2168,9 +2169,27 @@ tor_threads_init(void) #if 0 cond_event_tls_index = TlsAlloc(); #endif + set_main_thread(); } #endif +/** Identity of the "main" thread */ +static unsigned long main_thread_id = -1; + +/** Start considering the current thread to be the 'main thread'. This has + * no effect on anything besides in_main_thread(). */ +void +set_main_thread(void) +{ + main_thread_id = tor_get_thread_id(); +} +/** Return true iff called from the main thread. */ +int +in_main_thread(void) +{ + return main_thread_id == tor_get_thread_id(); +} + /** * On Windows, WSAEWOULDBLOCK is not always correct: when you see it, * you need to ask the socket for its actual errno. Also, you need to diff --git a/src/common/compat.h b/src/common/compat.h index edc38faf64..4d5a016cf2 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -522,6 +522,9 @@ void tor_threads_init(void); #define tor_threads_init() STMT_NIL #endif +void set_main_thread(void); +int in_main_thread(void); + #ifdef TOR_IS_MULTITHREADED #if 0 typedef struct tor_cond_t tor_cond_t; diff --git a/src/common/util.c b/src/common/util.c index a3338b1d18..7b9e5eb562 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2480,6 +2480,8 @@ start_daemon(void) if (fork() != 0) { exit(0); } + set_main_thread(); /* We are now the main thread. */ + return; } } diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index a70594ba38..479ecfbfec 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -1436,17 +1436,67 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir, /** Log a warning if the user specified an exit for the circuit that * has been excluded from use by ExcludeNodes or ExcludeExitNodes. */ static void -warn_if_router_excluded(const extend_info_t *exit) +warn_if_last_router_excluded(uint8_t purpose, const extend_info_t *exit) { or_options_t *options = get_options(); - routerinfo_t *ri = router_get_by_digest(exit->identity_digest); + routerset_t *rs = options->ExcludeNodes; + const char *description; + int severity; + int domain = LD_CIRC; - if (!ri || !options->_ExcludeExitNodesUnion) - return; + switch (purpose) + { + default: + case CIRCUIT_PURPOSE_OR: + case CIRCUIT_PURPOSE_INTRO_POINT: + case CIRCUIT_PURPOSE_REND_POINT_WAITING: + case CIRCUIT_PURPOSE_REND_ESTABLISHED: + log_warn(LD_BUG, "Called on non-origin circuit (purpose %d)", + (int)purpose); + return; + case CIRCUIT_PURPOSE_C_GENERAL: + description = "Requested exit node"; + rs = options->_ExcludeExitNodesUnion; + severity = LOG_WARN; + break; + case CIRCUIT_PURPOSE_C_INTRODUCING: + case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: + case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED: + description = "Introduction point for hidden service"; + severity = LOG_INFO; + break; + case CIRCUIT_PURPOSE_C_ESTABLISH_REND: + case CIRCUIT_PURPOSE_C_REND_READY: + case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: + case CIRCUIT_PURPOSE_C_REND_JOINED: + description = "Chosen rendezvous point"; + severity = LOG_WARN; + domain = LD_BUG; + break; + case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: + description = "Chosen introduction point"; + severity = LOG_INFO; + break; + case CIRCUIT_PURPOSE_S_CONNECT_REND: + case CIRCUIT_PURPOSE_S_REND_JOINED: + description = "Client-selected rendezvous point"; + severity = LOG_INFO; + break; + case CIRCUIT_PURPOSE_TESTING: + description = "Target for testing circuit"; + severity = LOG_INFO; + break; + case CIRCUIT_PURPOSE_CONTROLLER: + rs = options->_ExcludeExitNodesUnion; + description = "Controller-selected circuit target"; + severity = LOG_WARN; + break; + } - if (routerset_contains_router(options->_ExcludeExitNodesUnion, ri)) - log_warn(LD_CIRC,"Requested exit node '%s' is in ExcludeNodes, " - "or ExcludeExitNodes, using anyway.",exit->nickname); + if (routerset_contains_extendinfo(rs, exit)) + log_fn(severity, domain, "%s '%s' is in ExcludeNodes%s. Using anyway.", + description,exit->nickname, + rs==options->ExcludeNodes?"":" or ExcludeExitNodes."); return; } @@ -1471,7 +1521,7 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit) } if (exit) { /* the circuit-builder pre-requested one */ - warn_if_router_excluded(exit); + warn_if_last_router_excluded(circ->_base.purpose, exit); log_info(LD_CIRC,"Using requested exit node '%s'", exit->nickname); exit = extend_info_dup(exit); } else { /* we have to decide one */ diff --git a/src/or/config.c b/src/or/config.c index daf50a1e2b..9d5fb3b120 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -699,6 +699,9 @@ static int or_state_validate(or_state_t *old_options, or_state_t *options, static int or_state_load(void); static int options_init_logs(or_options_t *options, int validate_only); +static int is_listening_on_low_port(uint16_t port_option, + const config_line_t *listen_options); + static uint64_t config_parse_memunit(const char *s, int *ok); static int config_parse_interval(const char *s, int *ok); static void init_libevent(void); @@ -2631,6 +2634,35 @@ options_init(or_options_t *options) config_init(&options_format, options); } +/* Check if the port number given in port_option in combination with + * the specified port in listen_options will result in Tor actually + * opening a low port (meaning a port lower than 1024). Return 1 if + * it is, or 0 if it isn't or the concept of a low port isn't applicable for + * the platform we're on. */ +static int +is_listening_on_low_port(uint16_t port_option, + const config_line_t *listen_options) +{ +#ifdef MS_WINDOWS + return 0; /* No port is too low for windows. */ +#else + const config_line_t *l; + uint16_t p; + if (port_option == 0) + return 0; /* We're not listening */ + if (listen_options == NULL) + return (port_option < 1024); + + for (l = listen_options; l; l = l->next) { + parse_addr_port(LOG_WARN, l->value, NULL, NULL, &p); + if (p<1024) { + return 1; + } + } + return 0; +#endif +} + /** Set all vars in the configuration object options to their default * values. */ static void @@ -3032,17 +3064,18 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("TransPort and TransListenAddress are disabled in this build."); #endif -#ifndef MS_WINDOWS - if (options->AccountingMax && - (options->DirPort < 1024 || options->ORPort < 1024)) - log(LOG_WARN, LD_CONFIG, + if (options->AccountingMax && + (is_listening_on_low_port(options->ORPort, options->ORListenAddress) || + is_listening_on_low_port(options->DirPort, options->DirListenAddress))) + { + log(LOG_WARN, LD_CONFIG, "You have set AccountingMax to use hibernation. You have also " "chosen a low DirPort or OrPort. This combination can make Tor stop " "working when it tries to re-attach the port after a period of " "hibernation. Please choose a different port or turn off " "hibernation unless you know this combination will work on your " "platform."); -#endif + } if (options->ExcludeExitNodes || options->ExcludeNodes) { options->_ExcludeExitNodesUnion = routerset_new(); diff --git a/src/or/control.c b/src/or/control.c index 2052b967c7..e2ab9f31c5 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -3230,6 +3230,11 @@ control_event_logmsg(int severity, uint32_t domain, const char *msg) { int event; + /* Don't even think of trying to add stuff to a buffer from a cpuworker + * thread. */ + if (! in_main_thread()) + return; + if (disable_log_messages) return; diff --git a/src/or/or.h b/src/or/or.h index 5f91cec89f..2fa60264f5 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -4499,7 +4499,8 @@ int routerset_needs_geoip(const routerset_t *set); int routerset_contains_router(const routerset_t *set, routerinfo_t *ri); int routerset_contains_routerstatus(const routerset_t *set, routerstatus_t *rs); -int routerset_contains_extendinfo(const routerset_t *set, extend_info_t *ei); +int routerset_contains_extendinfo(const routerset_t *set, + const extend_info_t *ei); void routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset, int running_only); void routersets_get_disjunction(smartlist_t *target, const smartlist_t *source, diff --git a/src/or/routerlist.c b/src/or/routerlist.c index ed7f2f7a11..d8165e63bd 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -5119,7 +5119,7 @@ routerset_contains(const routerset_t *set, const tor_addr_t *addr, /** Return true iff we can tell that ei is a member of set. */ int -routerset_contains_extendinfo(const routerset_t *set, extend_info_t *ei) +routerset_contains_extendinfo(const routerset_t *set, const extend_info_t *ei) { return routerset_contains(set, &ei->addr,