diff options
Diffstat (limited to 'src')
48 files changed, 1115 insertions, 269 deletions
diff --git a/src/common/address.c b/src/common/address.c index a80926049a..1c3777fa82 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -121,6 +121,15 @@ tor_addr_to_sockaddr(const tor_addr_t *a, } } +/** Set address <b>a</b> to zero. This address belongs to + * the AF_UNIX family. */ +static void +tor_addr_make_af_unix(tor_addr_t *a) +{ + memset(a, 0, sizeof(*a)); + a->family = AF_UNIX; +} + /** Set the tor_addr_t in <b>a</b> to contain the socket address contained in * <b>sa</b>. */ int @@ -142,6 +151,9 @@ tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa, tor_addr_from_in6(a, &sin6->sin6_addr); if (port_out) *port_out = ntohs(sin6->sin6_port); + } else if (sa->sa_family == AF_UNIX) { + tor_addr_make_af_unix(a); + return 0; } else { tor_addr_make_unspec(a); return -1; @@ -421,6 +433,10 @@ tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate) ptr = dest; } break; + case AF_UNIX: + tor_snprintf(dest, len, "AF_UNIX"); + ptr = dest; + break; default: return NULL; } @@ -816,6 +832,8 @@ tor_addr_is_null(const tor_addr_t *addr) } case AF_INET: return (tor_addr_to_ipv4n(addr) == 0); + case AF_UNIX: + return 1; case AF_UNSPEC: return 1; default: diff --git a/src/common/compat.c b/src/common/compat.c index 11e2545709..6d36321193 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -823,6 +823,7 @@ replace_file(const char *from, const char *to) case FN_NOENT: break; case FN_FILE: + case FN_EMPTY: if (unlink(to)) return -1; break; case FN_ERROR: diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 6a8281d35f..15308dd4cb 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -146,13 +146,25 @@ tor_evsignal_new(struct event_base * base, int sig, { return tor_event_new(base, sig, EV_SIGNAL|EV_PERSIST, cb, arg); } -/** Work-alike replacement for event_free() on pre-Libevent-2.0 systems. */ +/** Work-alike replacement for event_free() on pre-Libevent-2.0 systems, + * except tolerate tor_event_free(NULL). */ void tor_event_free(struct event *ev) { + if (ev == NULL) + return; event_del(ev); tor_free(ev); } +#else +/* Wrapper for event_free() that tolerates tor_event_free(NULL) */ +void +tor_event_free(struct event *ev) +{ + if (ev == NULL) + return; + event_free(ev); +} #endif /** Global event base for use by the main thread. */ diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 9296851132..6bbfae0056 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -5,6 +5,7 @@ #define TOR_COMPAT_LIBEVENT_H #include "orconfig.h" +#include "testsupport.h" struct event; struct event_base; @@ -28,11 +29,9 @@ void suppress_libevent_log_msg(const char *msg); #define tor_event_new event_new #define tor_evtimer_new evtimer_new #define tor_evsignal_new evsignal_new -#define tor_event_free event_free #define tor_evdns_add_server_port(sock, tcp, cb, data) \ evdns_add_server_port_with_base(tor_libevent_get_base(), \ (sock),(tcp),(cb),(data)); - #else struct event *tor_event_new(struct event_base * base, evutil_socket_t sock, short what, void (*cb)(evutil_socket_t, short, void *), void *arg); @@ -40,10 +39,11 @@ struct event *tor_evtimer_new(struct event_base * base, void (*cb)(evutil_socket_t, short, void *), void *arg); struct event *tor_evsignal_new(struct event_base * base, int sig, void (*cb)(evutil_socket_t, short, void *), void *arg); -void tor_event_free(struct event *ev); #define tor_evdns_add_server_port evdns_add_server_port #endif +void tor_event_free(struct event *ev); + typedef struct periodic_timer_t periodic_timer_t; periodic_timer_t *periodic_timer_new(struct event_base *base, diff --git a/src/common/torgzip.c b/src/common/torgzip.c index 7c3adeb8f0..4f23407e23 100644 --- a/src/common/torgzip.c +++ b/src/common/torgzip.c @@ -92,10 +92,27 @@ tor_zlib_get_header_version_str(void) /** Return the 'bits' value to tell zlib to use <b>method</b>.*/ static INLINE int -method_bits(compress_method_t method) +method_bits(compress_method_t method, zlib_compression_level_t level) { /* Bits+16 means "use gzip" in zlib >= 1.2 */ - return method == GZIP_METHOD ? 15+16 : 15; + const int flag = method == GZIP_METHOD ? 16 : 0; + switch (level) { + default: + case HIGH_COMPRESSION: return flag + 15; + case MEDIUM_COMPRESSION: return flag + 13; + case LOW_COMPRESSION: return flag + 11; + } +} + +static INLINE int +get_memlevel(zlib_compression_level_t level) +{ + switch (level) { + default: + case HIGH_COMPRESSION: return 8; + case MEDIUM_COMPRESSION: return 7; + case LOW_COMPRESSION: return 6; + } } /** @{ */ @@ -162,8 +179,9 @@ tor_gzip_compress(char **out, size_t *out_len, stream->avail_in = (unsigned int)in_len; if (deflateInit2(stream, Z_BEST_COMPRESSION, Z_DEFLATED, - method_bits(method), - 8, Z_DEFAULT_STRATEGY) != Z_OK) { + method_bits(method, HIGH_COMPRESSION), + get_memlevel(HIGH_COMPRESSION), + Z_DEFAULT_STRATEGY) != Z_OK) { log_warn(LD_GENERAL, "Error from deflateInit2: %s", stream->msg?stream->msg:"<no message>"); goto err; @@ -289,7 +307,7 @@ tor_gzip_uncompress(char **out, size_t *out_len, stream->avail_in = (unsigned int)in_len; if (inflateInit2(stream, - method_bits(method)) != Z_OK) { + method_bits(method, HIGH_COMPRESSION)) != Z_OK) { log_warn(LD_GENERAL, "Error from inflateInit2: %s", stream->msg?stream->msg:"<no message>"); goto err; @@ -315,7 +333,8 @@ tor_gzip_uncompress(char **out, size_t *out_len, log_warn(LD_BUG, "Error freeing gzip structures"); goto err; } - if (inflateInit2(stream, method_bits(method)) != Z_OK) { + if (inflateInit2(stream, + method_bits(method,HIGH_COMPRESSION)) != Z_OK) { log_warn(LD_GENERAL, "Error from second inflateInit2: %s", stream->msg?stream->msg:"<no message>"); goto err; @@ -426,10 +445,11 @@ struct tor_zlib_state_t { * <b>compress</b>, it's for compression; otherwise it's for * decompression. */ tor_zlib_state_t * -tor_zlib_new(int compress, compress_method_t method) +tor_zlib_new(int compress, compress_method_t method, + zlib_compression_level_t compression_level) { tor_zlib_state_t *out; - int bits; + int bits, memlevel; if (method == GZIP_METHOD && !is_gzip_supported()) { /* Old zlib version don't support gzip in inflateInit2 */ @@ -437,21 +457,29 @@ tor_zlib_new(int compress, compress_method_t method) return NULL; } + if (! compress) { + /* use this setting for decompression, since we might have the + * max number of window bits */ + compression_level = HIGH_COMPRESSION; + } + out = tor_malloc_zero(sizeof(tor_zlib_state_t)); out->stream.zalloc = Z_NULL; out->stream.zfree = Z_NULL; out->stream.opaque = NULL; out->compress = compress; - bits = method_bits(method); + bits = method_bits(method, compression_level); + memlevel = get_memlevel(compression_level); if (compress) { if (deflateInit2(&out->stream, Z_BEST_COMPRESSION, Z_DEFLATED, - bits, 8, Z_DEFAULT_STRATEGY) != Z_OK) + bits, memlevel, + Z_DEFAULT_STRATEGY) != Z_OK) goto err; } else { if (inflateInit2(&out->stream, bits) != Z_OK) goto err; } - out->allocation = tor_zlib_state_size_precalc(!compress, bits, 8); + out->allocation = tor_zlib_state_size_precalc(!compress, bits, memlevel); total_zlib_allocation += out->allocation; diff --git a/src/common/torgzip.h b/src/common/torgzip.h index 89ca6a6613..0fc2deb6c4 100644 --- a/src/common/torgzip.h +++ b/src/common/torgzip.h @@ -19,6 +19,15 @@ typedef enum { NO_METHOD=0, GZIP_METHOD=1, ZLIB_METHOD=2, UNKNOWN_METHOD=3 } compress_method_t; +/** + * Enumeration to define tradeoffs between memory usage and compression level. + * HIGH_COMPRESSION saves the most bandwidth; LOW_COMPRESSION saves the most + * memory. + **/ +typedef enum { + HIGH_COMPRESSION, MEDIUM_COMPRESSION, LOW_COMPRESSION +} zlib_compression_level_t; + int tor_gzip_compress(char **out, size_t *out_len, const char *in, size_t in_len, @@ -47,7 +56,8 @@ typedef enum { } tor_zlib_output_t; /** Internal state for an incremental zlib compression/decompression. */ typedef struct tor_zlib_state_t tor_zlib_state_t; -tor_zlib_state_t *tor_zlib_new(int compress, compress_method_t method); +tor_zlib_state_t *tor_zlib_new(int compress, compress_method_t method, + zlib_compression_level_t level); tor_zlib_output_t tor_zlib_process(tor_zlib_state_t *state, char **out, size_t *out_len, diff --git a/src/common/tortls.c b/src/common/tortls.c index dd33b330dc..ca629135a6 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -29,6 +29,20 @@ #include <ws2tcpip.h> #endif #endif + +#ifdef __GNUC__ +#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#endif + +#if __GNUC__ && GCC_VERSION >= 402 +#if GCC_VERSION >= 406 +#pragma GCC diagnostic push +#endif +/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in + * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ +#pragma GCC diagnostic ignored "-Wredundant-decls" +#endif + #include <openssl/ssl.h> #include <openssl/ssl3.h> #include <openssl/err.h> @@ -37,6 +51,14 @@ #include <openssl/bio.h> #include <openssl/opensslv.h> +#if __GNUC__ && GCC_VERSION >= 402 +#if GCC_VERSION >= 406 +#pragma GCC diagnostic pop +#else +#pragma GCC diagnostic warning "-Wredundant-decls" +#endif +#endif + #ifdef USE_BUFFEREVENTS #include <event2/bufferevent_ssl.h> #include <event2/buffer.h> diff --git a/src/common/util.c b/src/common/util.c index 1de5edc52c..be866a5fe6 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -527,15 +527,25 @@ round_int64_to_next_multiple_of(int64_t number, int64_t divisor) /** Transform a random value <b>p</b> from the uniform distribution in * [0.0, 1.0[ into a Laplace distributed value with location parameter - * <b>mu</b> and scale parameter <b>b</b> in [-Inf, Inf[. */ -double + * <b>mu</b> and scale parameter <b>b</b>. Truncate the final result + * to be an integer in [INT64_MIN, INT64_MAX]. */ +int64_t sample_laplace_distribution(double mu, double b, double p) { + double result; + tor_assert(p >= 0.0 && p < 1.0); /* This is the "inverse cumulative distribution function" from: * http://en.wikipedia.org/wiki/Laplace_distribution */ - return mu - b * (p > 0.5 ? 1.0 : -1.0) - * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5)); + result = mu - b * (p > 0.5 ? 1.0 : -1.0) + * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5)); + + if (result >= INT64_MAX) + return INT64_MAX; + else if (result <= INT64_MIN) + return INT64_MIN; + else + return (int64_t) result; } /** Add random noise between INT64_MIN and INT64_MAX coming from a @@ -546,10 +556,10 @@ int64_t add_laplace_noise(int64_t signal, double random, double delta_f, double epsilon) { - /* cast to int64_t intended */ int64_t noise = sample_laplace_distribution( 0.0, /* just add noise, no further signal */ delta_f / epsilon, random); + if (noise > 0 && INT64_MAX - noise < signal) return INT64_MAX; else if (noise < 0 && INT64_MIN - noise > signal) @@ -1704,15 +1714,18 @@ format_iso_time_nospace_usec(char *buf, const struct timeval *tv) /** Given an ISO-formatted UTC time value (after the epoch) in <b>cp</b>, * parse it and store its value in *<b>t</b>. Return 0 on success, -1 on - * failure. Ignore extraneous stuff in <b>cp</b> separated by whitespace from - * the end of the time string. */ + * failure. Ignore extraneous stuff in <b>cp</b> after the end of the time + * string, unless <b>strict</b> is set. */ int -parse_iso_time(const char *cp, time_t *t) +parse_iso_time_(const char *cp, time_t *t, int strict) { struct tm st_tm; unsigned int year=0, month=0, day=0, hour=0, minute=0, second=0; - if (tor_sscanf(cp, "%u-%2u-%2u %2u:%2u:%2u", &year, &month, - &day, &hour, &minute, &second) < 6) { + int n_fields; + char extra_char; + n_fields = tor_sscanf(cp, "%u-%2u-%2u %2u:%2u:%2u%c", &year, &month, + &day, &hour, &minute, &second, &extra_char); + if (strict ? (n_fields != 6) : (n_fields < 6)) { char *esc = esc_for_log(cp); log_warn(LD_GENERAL, "ISO time %s was unparseable", esc); tor_free(esc); @@ -1741,6 +1754,16 @@ parse_iso_time(const char *cp, time_t *t) return tor_timegm(&st_tm, t); } +/** Given an ISO-formatted UTC time value (after the epoch) in <b>cp</b>, + * parse it and store its value in *<b>t</b>. Return 0 on success, -1 on + * failure. Reject the string if any characters are present after the time. + */ +int +parse_iso_time(const char *cp, time_t *t) +{ + return parse_iso_time_(cp, t, 1); +} + /** Given a <b>date</b> in one of the three formats allowed by HTTP (ugh), * parse it into <b>tm</b>. Return 0 on success, negative on failure. */ int @@ -2005,15 +2028,24 @@ clean_name_for_stat(char *name) #endif } -/** Return FN_ERROR if filename can't be read, FN_NOENT if it doesn't - * exist, FN_FILE if it is a regular file, or FN_DIR if it's a - * directory. On FN_ERROR, sets errno. */ +/** Return: + * FN_ERROR if filename can't be read, is NULL, or is zero-length, + * FN_NOENT if it doesn't exist, + * FN_FILE if it is a non-empty regular file, or a FIFO on unix-like systems, + * FN_EMPTY for zero-byte regular files, + * FN_DIR if it's a directory, and + * FN_ERROR for any other file type. + * On FN_ERROR and FN_NOENT, sets errno. (errno is not set when FN_ERROR + * is returned due to an unhandled file type.) */ file_status_t file_status(const char *fname) { struct stat st; char *f; int r; + if (!fname || strlen(fname) == 0) { + return FN_ERROR; + } f = tor_strdup(fname); clean_name_for_stat(f); log_debug(LD_FS, "stat()ing %s", f); @@ -2025,16 +2057,23 @@ file_status(const char *fname) } return FN_ERROR; } - if (st.st_mode & S_IFDIR) + if (st.st_mode & S_IFDIR) { return FN_DIR; - else if (st.st_mode & S_IFREG) - return FN_FILE; + } else if (st.st_mode & S_IFREG) { + if (st.st_size > 0) { + return FN_FILE; + } else if (st.st_size == 0) { + return FN_EMPTY; + } else { + return FN_ERROR; + } #ifndef _WIN32 - else if (st.st_mode & S_IFIFO) + } else if (st.st_mode & S_IFIFO) { return FN_FILE; #endif - else + } else { return FN_ERROR; + } } /** Check whether <b>dirname</b> exists and is private. If yes return 0. If @@ -2953,7 +2992,7 @@ expand_filename(const char *filename) tor_free(username); rest = slash ? (slash+1) : ""; #else - log_warn(LD_CONFIG, "Couldn't expend homedir on system without pwd.h"); + log_warn(LD_CONFIG, "Couldn't expand homedir on system without pwd.h"); return tor_strdup(filename); #endif } diff --git a/src/common/util.h b/src/common/util.h index a1da53890e..89c140032a 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -173,7 +173,7 @@ unsigned round_to_next_multiple_of(unsigned number, unsigned divisor); uint32_t round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor); uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor); int64_t round_int64_to_next_multiple_of(int64_t number, int64_t divisor); -double sample_laplace_distribution(double mu, double b, double p); +int64_t sample_laplace_distribution(double mu, double b, double p); int64_t add_laplace_noise(int64_t signal, double random, double delta_f, double epsilon); int n_bits_set_u8(uint8_t v); @@ -274,6 +274,7 @@ void format_local_iso_time(char *buf, time_t t); void format_iso_time(char *buf, time_t t); void format_iso_time_nospace(char *buf, time_t t); void format_iso_time_nospace_usec(char *buf, const struct timeval *tv); +int parse_iso_time_(const char *cp, time_t *t, int strict); int parse_iso_time(const char *buf, time_t *t); int parse_http_time(const char *buf, struct tm *tm); int format_time_interval(char *out, size_t out_len, long interval); @@ -341,7 +342,7 @@ enum stream_status get_string_from_pipe(FILE *stream, char *buf, size_t count); /** Return values from file_status(); see that function's documentation * for details. */ -typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR } file_status_t; +typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR, FN_EMPTY } file_status_t; file_status_t file_status(const char *filename); /** Possible behaviors for check_private_dir() on encountering a nonexistent diff --git a/src/or/addressmap.c b/src/or/addressmap.c index 5e37a2621e..40e975fd3e 100644 --- a/src/or/addressmap.c +++ b/src/or/addressmap.c @@ -94,7 +94,7 @@ addressmap_ent_free(void *_ent) tor_free(ent); } -/** Free storage held by a virtaddress_entry_t* entry in <b>ent</b>. */ +/** Free storage held by a virtaddress_entry_t* entry in <b>_ent</b>. */ static void addressmap_virtaddress_ent_free(void *_ent) { @@ -104,11 +104,13 @@ addressmap_virtaddress_ent_free(void *_ent) ent = _ent; tor_free(ent->ipv4_address); + tor_free(ent->ipv6_address); tor_free(ent->hostname_address); tor_free(ent); } -/** Free storage held by a virtaddress_entry_t* entry in <b>ent</b>. */ +/** Remove <b>address</b> (which must map to <b>ent</b>) from the + * virtual address map. */ static void addressmap_virtaddress_remove(const char *address, addressmap_entry_t *ent) { @@ -120,9 +122,11 @@ addressmap_virtaddress_remove(const char *address, addressmap_entry_t *ent) if (ve) { if (!strcmp(address, ve->ipv4_address)) tor_free(ve->ipv4_address); + if (!strcmp(address, ve->ipv6_address)) + tor_free(ve->ipv6_address); if (!strcmp(address, ve->hostname_address)) tor_free(ve->hostname_address); - if (!ve->ipv4_address && !ve->hostname_address) { + if (!ve->ipv4_address && !ve->ipv6_address && !ve->hostname_address) { tor_free(ve); strmap_remove(virtaddress_reversemap, ent->new_address); } @@ -131,7 +135,7 @@ addressmap_virtaddress_remove(const char *address, addressmap_entry_t *ent) } /** Remove <b>ent</b> (which must be mapped to by <b>address</b>) from the - * client address maps. */ + * client address maps, and then free it. */ static void addressmap_ent_remove(const char *address, addressmap_entry_t *ent) { @@ -226,6 +230,8 @@ addressmap_address_should_automap(const char *address, return 0; SMARTLIST_FOREACH_BEGIN(suffix_list, const char *, suffix) { + if (!strcmp(suffix, ".")) + return 1; if (!strcasecmpend(address, suffix)) return 1; } SMARTLIST_FOREACH_END(suffix); @@ -496,7 +502,7 @@ addressmap_have_mapping(const char *address, int update_expiry) * equal to <b>address</b>, or any address ending with a period followed by * <b>address</b>. If <b>wildcard_addr</b> and <b>wildcard_new_addr</b> are * both true, the mapping will rewrite addresses that end with - * ".<b>address</b>" into ones that end with ".<b>new_address</b>." + * ".<b>address</b>" into ones that end with ".<b>new_address</b>". * * If <b>new_address</b> is NULL, or <b>new_address</b> is equal to * <b>address</b> and <b>wildcard_addr</b> is equal to @@ -535,9 +541,9 @@ addressmap_register(const char *address, char *new_address, time_t expires, if (expires > 1) { log_info(LD_APP,"Temporary addressmap ('%s' to '%s') not performed, " "since it's already mapped to '%s'", - safe_str_client(address), - safe_str_client(new_address), - safe_str_client(ent->new_address)); + safe_str_client(address), + safe_str_client(new_address), + safe_str_client(ent->new_address)); tor_free(new_address); return; } @@ -738,6 +744,12 @@ parse_virtual_addr_network(const char *val, sa_family_t family, const int max_bits = ipv6 ? 40 : 16; virtual_addr_conf_t *conf = ipv6 ? &virtaddr_conf_ipv6 : &virtaddr_conf_ipv4; + if (!val || val[0] == '\0') { + if (msg) + tor_asprintf(msg, "Value not present (%s) after VirtualAddressNetwork%s", + val?"Empty":"NULL", ipv6?"IPv6":""); + return -1; + } if (tor_addr_parse_mask_ports(val, 0, &addr, &bits, NULL, NULL) < 0) { if (msg) tor_asprintf(msg, "Error parsing VirtualAddressNetwork%s %s", @@ -839,8 +851,8 @@ get_random_virtual_addr(const virtual_addr_conf_t *conf, tor_addr_t *addr_out) } /** Return a newly allocated string holding an address of <b>type</b> - * (one of RESOLVED_TYPE_{IPV4|HOSTNAME}) that has not yet been mapped, - * and that is very unlikely to be the address of any real host. + * (one of RESOLVED_TYPE_{IPV4|IPV6|HOSTNAME}) that has not yet been + * mapped, and that is very unlikely to be the address of any real host. * * May return NULL if we have run out of virtual addresses. */ @@ -945,7 +957,7 @@ addressmap_register_virtual_address(int type, char *new_address) !strcasecmp(new_address, ent->new_address)) { tor_free(new_address); tor_assert(!vent_needs_to_be_added); - return tor_strdup(*addrp); + return *addrp; } else { log_warn(LD_BUG, "Internal confusion: I thought that '%s' was mapped to by " diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 0ad026b2d3..9620a23655 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -2065,7 +2065,7 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state) /*XXXX025 use the using_as_guard flag to accomplish this.*/ if (options->UseEntryGuards && (!options->TestingTorNetwork || - smartlist_len(nodelist_get_list()) > smartlist_len(get_entry_guards()) + smartlist_len(nodelist_get_list()) > smartlist_len(get_entry_guards()) )) { SMARTLIST_FOREACH(get_entry_guards(), const entry_guard_t *, entry, { diff --git a/src/or/config.c b/src/or/config.c index b6d9d4eb3d..23edd3a503 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -55,6 +55,16 @@ #include "procmon.h" +#ifdef HAVE_SYSTEMD +# if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) +/* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse + * Coverity. Here's a kludge to unconfuse it. + */ +# define __INCLUDE_LEVEL__ 2 +# endif +#include <systemd/sd-daemon.h> +#endif + /* From main.c */ extern int quiet_level; @@ -65,7 +75,6 @@ static config_abbrev_t option_abbrevs_[] = { PLURAL(AuthDirBadExitCC), PLURAL(AuthDirInvalidCC), PLURAL(AuthDirRejectCC), - PLURAL(ExitNode), PLURAL(EntryNode), PLURAL(ExcludeNode), PLURAL(FirewallPort), @@ -191,6 +200,8 @@ static config_var_t option_vars_[] = { V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), V(ControlSocketsGroupWritable, BOOL, "0"), + V(SocksSocket, LINELIST, NULL), + V(SocksSocketsGroupWritable, BOOL, "0"), V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), V(CookieAuthFile, STRING, NULL), @@ -229,6 +240,7 @@ static config_var_t option_vars_[] = { V(ExitPolicyRejectPrivate, BOOL, "1"), V(ExitPortStatistics, BOOL, "0"), V(ExtendAllowPrivateAddresses, BOOL, "0"), + V(ExitRelay, AUTOBOOL, "auto"), VPORT(ExtORPort, LINELIST, NULL), V(ExtORPortCookieAuthFile, STRING, NULL), V(ExtORPortCookieAuthFileGroupReadable, BOOL, "0"), @@ -427,7 +439,7 @@ static config_var_t option_vars_[] = { VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword, NULL), VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), - V(MinUptimeHidServDirectoryV2, INTERVAL, "25 hours"), + V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"), V(VoteOnHidServDirectoriesV2, BOOL, "1"), V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, " "300, 900, 2147483647"), @@ -448,6 +460,7 @@ static config_var_t option_vars_[] = { V(TestingCertMaxDownloadTries, UINT, "8"), V(TestingDirAuthVoteExit, ROUTERSET, NULL), V(TestingDirAuthVoteGuard, ROUTERSET, NULL), + V(TestingDirAuthVoteHSDir, ROUTERSET, NULL), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "0"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } @@ -496,6 +509,7 @@ static const config_var_t testing_tor_network_defaults[] = { V(TestingEnableCellStatsEvent, BOOL, "1"), V(TestingEnableTbEmptyEvent, BOOL, "1"), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"), + V(RendPostPeriod, INTERVAL, "2 minutes"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } }; @@ -1016,6 +1030,11 @@ options_act_reversible(const or_options_t *old_options, char **msg) start_daemon(); } +#ifdef HAVE_SYSTEMD + /* Our PID may have changed, inform supervisor */ + sd_notifyf(0, "MAINPID=%ld\n", (long int)getpid()); +#endif + #ifndef HAVE_SYS_UN_H if (options->ControlSocket || options->ControlSocketsGroupWritable) { *msg = tor_strdup("Unix domain sockets (ControlSocket) not supported " @@ -1030,6 +1049,20 @@ options_act_reversible(const or_options_t *old_options, char **msg) } #endif +#ifndef HAVE_SYS_UN_H + if (options->SocksSocket || options->SocksSocketsGroupWritable) { + *msg = tor_strdup("Unix domain sockets (SocksSocket) not supported " + "on this OS/with this build."); + goto rollback; + } +#else + if (options->SocksSocketsGroupWritable && !options->SocksSocket) { + *msg = tor_strdup("Setting SocksSocketGroupWritable without setting" + "a SocksSocket makes no sense."); + goto rollback; + } +#endif + if (running_tor) { int n_ports=0; /* We need to set the connection limit before we can open the listeners. */ @@ -2492,6 +2525,7 @@ compute_publishserverdescriptor(or_options_t *options) /** Lowest allowable value for RendPostPeriod; if this is too low, hidden * services can overload the directory system. */ #define MIN_REND_POST_PERIOD (10*60) +#define MIN_REND_POST_PERIOD_TESTING (5) /** Higest allowable value for PredictedPortsRelevanceTime; if this is * too high, our selection of exits will decrease for an extended @@ -2616,11 +2650,6 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("Failed to resolve/guess local address. See logs for details."); } -#ifndef _WIN32 - if (options->RunAsDaemon && torrc_fname && path_is_relative(torrc_fname)) - REJECT("Can't use a relative path to torrc when RunAsDaemon is set."); -#endif - if (server_mode(options) && options->RendConfigLines) log_warn(LD_CONFIG, "Tor is currently configured as a relay and a hidden service. " @@ -2911,6 +2940,7 @@ options_validate(or_options_t *old_options, or_options_t *options, options->MaxMemInQueues = compute_real_max_mem_in_queues(options->MaxMemInQueues_raw, server_mode(options)); + options->MaxMemInQueues_low_threshold = (options->MaxMemInQueues / 4) * 3; options->AllowInvalid_ = 0; @@ -2975,10 +3005,13 @@ options_validate(or_options_t *old_options, or_options_t *options, options->MinUptimeHidServDirectoryV2 = 0; } - if (options->RendPostPeriod < MIN_REND_POST_PERIOD) { + const int min_rendpostperiod = + options->TestingTorNetwork ? + MIN_REND_POST_PERIOD_TESTING : MIN_REND_POST_PERIOD; + if (options->RendPostPeriod < min_rendpostperiod) { log_warn(LD_CONFIG, "RendPostPeriod option is too short; " - "raising to %d seconds.", MIN_REND_POST_PERIOD); - options->RendPostPeriod = MIN_REND_POST_PERIOD; + "raising to %d seconds.", min_rendpostperiod); + options->RendPostPeriod = min_rendpostperiod;; } if (options->RendPostPeriod > MAX_DIR_PERIOD) { @@ -3529,15 +3562,6 @@ options_validate(or_options_t *old_options, or_options_t *options, AF_INET6, 1, msg)<0) return -1; - if (options->AutomapHostsSuffixes) { - SMARTLIST_FOREACH(options->AutomapHostsSuffixes, char *, suf, - { - size_t len = strlen(suf); - if (len && suf[len-1] == '.') - suf[len-1] = '\0'; - }); - } - if (options->TestingTorNetwork && !(options->DirAuthorities || (options->AlternateDirAuthority && @@ -3932,6 +3956,7 @@ options_transition_affects_descriptor(const or_options_t *old_options, !opt_streq(old_options->Nickname,new_options->Nickname) || !opt_streq(old_options->Address,new_options->Address) || !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) || + old_options->ExitRelay != new_options->ExitRelay || old_options->ExitPolicyRejectPrivate != new_options->ExitPolicyRejectPrivate || old_options->IPv6Exit != new_options->IPv6Exit || @@ -4147,17 +4172,24 @@ find_torrc_filename(config_line_t *cmd_arg, if (*using_default_fname) { /* didn't find one, try CONFDIR */ const char *dflt = get_default_conf_file(defaults_file); - if (dflt && file_status(dflt) == FN_FILE) { + file_status_t st = file_status(dflt); + if (dflt && (st == FN_FILE || st == FN_EMPTY)) { fname = tor_strdup(dflt); } else { #ifndef _WIN32 char *fn = NULL; - if (!defaults_file) + if (!defaults_file) { fn = expand_filename("~/.torrc"); - if (fn && file_status(fn) == FN_FILE) { - fname = fn; + } + if (fn) { + file_status_t hmst = file_status(fn); + if (hmst == FN_FILE || hmst == FN_EMPTY) { + fname = fn; + } else { + tor_free(fn); + fname = tor_strdup(dflt); + } } else { - tor_free(fn); fname = tor_strdup(dflt); } #else @@ -4184,16 +4216,20 @@ load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file) int ignore_missing_torrc = 0; char **fname_var = defaults_file ? &torrc_defaults_fname : &torrc_fname; - fname = find_torrc_filename(cmd_arg, defaults_file, - &using_default_torrc, &ignore_missing_torrc); - tor_assert(fname); + if (*fname_var == NULL) { + fname = find_torrc_filename(cmd_arg, defaults_file, + &using_default_torrc, &ignore_missing_torrc); + tor_assert(fname); + tor_free(*fname_var); + *fname_var = fname; + } else { + fname = *fname_var; + } log_debug(LD_CONFIG, "Opening config file \"%s\"", fname); - tor_free(*fname_var); - *fname_var = fname; - /* Open config file */ - if (file_status(fname) != FN_FILE || + file_status_t st = file_status(fname); + if (!(st == FN_FILE || st == FN_EMPTY) || !(cf = read_file_to_str(fname,0,NULL))) { if (using_default_torrc == 1 || ignore_missing_torrc) { if (!defaults_file) @@ -6012,22 +6048,87 @@ parse_port_config(smartlist_t *out, /** Parse a list of config_line_t for an AF_UNIX unix socket listener option * from <b>cfg</b> and add them to <b>out</b>. No fancy options are - * supported: the line contains nothing but the path to the AF_UNIX socket. */ + * supported: the line contains nothing but the path to the AF_UNIX socket. + * We support a *Socket 0 syntax to explicitly disable if we enable by + * default. To use this, pass a non-NULL list containing the default + * paths into this function as the 2nd parameter, and if no config lines at all + * are present they will be added to the output list. If the only config line + * present is '0' the input list will be unmodified. + */ static int -parse_unix_socket_config(smartlist_t *out, const config_line_t *cfg, - int listener_type) +parse_unix_socket_config(smartlist_t *out, smartlist_t *defaults, + const config_line_t *cfg, int listener_type) { + /* We can say things like SocksSocket 0 or ControlSocket 0 to explicitly + * disable this feature; use this to track if we've seen a disable line + */ + + int unix_socket_disable = 0; + size_t len; + smartlist_t *ports_to_add = NULL; if (!out) return 0; + ports_to_add = smartlist_new(); + for ( ; cfg; cfg = cfg->next) { - size_t len = strlen(cfg->value); - port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); - port->is_unix_addr = 1; - memcpy(port->unix_addr, cfg->value, len+1); - port->type = listener_type; - smartlist_add(out, port); + if (strcmp(cfg->value, "0") != 0) { + /* We have a non-disable; add it */ + len = strlen(cfg->value); + port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); + port->is_unix_addr = 1; + memcpy(port->unix_addr, cfg->value, len+1); + port->type = listener_type; + if (listener_type == CONN_TYPE_AP_LISTENER) { + /* Some more bits to twiddle for this case + * + * XXX this should support parsing the same options + * parse_port_config() does, and probably that code should be + * factored out into a function we can call from here. For + * now, some reasonable defaults. + */ + + port->entry_cfg.ipv4_traffic = 1; + port->entry_cfg.ipv6_traffic = 1; + port->entry_cfg.cache_ipv4_answers = 1; + port->entry_cfg.cache_ipv6_answers = 1; + } + smartlist_add(ports_to_add, port); + } else { + /* Keep track that we've seen a disable */ + unix_socket_disable = 1; + } + } + + if (unix_socket_disable) { + if (smartlist_len(ports_to_add) > 0) { + /* We saw a disable line and a path; bad news */ + SMARTLIST_FOREACH(ports_to_add, port_cfg_t *, port, tor_free(port)); + smartlist_free(ports_to_add); + return -1; + } + /* else we have a disable and nothing else, so add nothing to out */ + } else { + /* No disable; do we have any ports to add that we parsed? */ + if (smartlist_len(ports_to_add) > 0) { + SMARTLIST_FOREACH_BEGIN(ports_to_add, port_cfg_t *, port) { + smartlist_add(out, port); + } SMARTLIST_FOREACH_END(port); + } else if (defaults != NULL && smartlist_len(defaults) > 0) { + /* No, but we have some defaults to copy */ + SMARTLIST_FOREACH_BEGIN(defaults, const port_cfg_t *, defport) { + tor_assert(defport->is_unix_addr); + tor_assert(defport->unix_addr); + len = sizeof(port_cfg_t) + strlen(defport->unix_addr) + 1; + port_cfg_t *port = tor_malloc_zero(len); + memcpy(port, defport, len); + smartlist_add(out, port); + } SMARTLIST_FOREACH_END(defport); + } + + /* Free the temporary smartlist we used */ + smartlist_free(ports_to_add); } return 0; @@ -6121,12 +6222,19 @@ parse_ports(or_options_t *options, int validate_only, "configuration"); goto err; } - if (parse_unix_socket_config(ports, + + if (parse_unix_socket_config(ports, NULL, options->ControlSocket, CONN_TYPE_CONTROL_LISTENER) < 0) { *msg = tor_strdup("Invalid ControlSocket configuration"); goto err; } + if (parse_unix_socket_config(ports, NULL, + options->SocksSocket, + CONN_TYPE_AP_LISTENER) < 0) { + *msg = tor_strdup("Invalid SocksSocket configuration"); + goto err; + } } if (! options->ClientOnly) { if (parse_port_config(ports, @@ -6170,6 +6278,8 @@ parse_ports(or_options_t *options, int validate_only, !! count_real_listeners(ports, CONN_TYPE_OR_LISTENER); options->SocksPort_set = !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER); + options->SocksSocket_set = + !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER); options->TransPort_set = !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER); options->NATDPort_set = @@ -6452,7 +6562,9 @@ write_configuration_file(const char *fname, const or_options_t *options) tor_assert(fname); switch (file_status(fname)) { + /* create backups of old config files, even if they're empty */ case FN_FILE: + case FN_EMPTY: old_val = read_file_to_str(fname, 0, NULL); if (!old_val || strcmpstart(old_val, GENERATED_FILE_PREFIX)) { rename_old = 1; diff --git a/src/or/connection.c b/src/or/connection.c index 707bf73c9e..ccd823131d 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -308,6 +308,8 @@ entry_connection_new(int type, int socket_family) entry_conn->entry_cfg.ipv4_traffic = 1; else if (socket_family == AF_INET6) entry_conn->entry_cfg.ipv6_traffic = 1; + else if (socket_family == AF_UNIX) + entry_conn->is_socks_socket = 1; return entry_conn; } @@ -516,9 +518,10 @@ connection_free_(connection_t *conn) buf_free(conn->outbuf); } else { if (conn->socket_family == AF_UNIX) { - /* For now only control ports can be Unix domain sockets + /* For now only control and SOCKS ports can be Unix domain sockets * and listeners at the same time */ - tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER); + tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER || + conn->type == CONN_TYPE_AP_LISTENER); if (unlink(conn->address) < 0 && errno != ENOENT) { log_warn(LD_NET, "Could not unlink %s: %s", conn->address, @@ -574,8 +577,10 @@ connection_free_(connection_t *conn) tor_free(control_conn->incoming_cmd); } - tor_free(conn->read_event); /* Probably already freed by connection_free. */ - tor_free(conn->write_event); /* Probably already freed by connection_free. */ + /* Probably already freed by connection_free. */ + tor_event_free(conn->read_event); + tor_event_free(conn->write_event); + conn->read_event = conn->write_event = NULL; IF_HAS_BUFFEREVENT(conn, { /* This was a workaround to handle bugs in some old versions of libevent * where callbacks can occur after calling bufferevent_free(). Setting @@ -913,13 +918,57 @@ warn_too_many_conns(void) } #ifdef HAVE_SYS_UN_H + +#define UNIX_SOCKET_PURPOSE_CONTROL_SOCKET 0 +#define UNIX_SOCKET_PURPOSE_SOCKS_SOCKET 1 + +/** Check if the purpose isn't one of the ones we know what to do with */ + +static int +is_valid_unix_socket_purpose(int purpose) +{ + int valid = 0; + + switch (purpose) { + case UNIX_SOCKET_PURPOSE_CONTROL_SOCKET: + case UNIX_SOCKET_PURPOSE_SOCKS_SOCKET: + valid = 1; + break; + } + + return valid; +} + +/** Return a string description of a unix socket purpose */ +static const char * +unix_socket_purpose_to_string(int purpose) +{ + const char *s = "unknown-purpose socket"; + + switch (purpose) { + case UNIX_SOCKET_PURPOSE_CONTROL_SOCKET: + s = "control socket"; + break; + case UNIX_SOCKET_PURPOSE_SOCKS_SOCKET: + s = "SOCKS socket"; + break; + } + + return s; +} + /** Check whether we should be willing to open an AF_UNIX socket in * <b>path</b>. Return 0 if we should go ahead and -1 if we shouldn't. */ static int -check_location_for_unix_socket(const or_options_t *options, const char *path) +check_location_for_unix_socket(const or_options_t *options, const char *path, + int purpose) { int r = -1; - char *p = tor_strdup(path); + char *p = NULL; + + tor_assert(is_valid_unix_socket_purpose(purpose)); + + p = tor_strdup(path); cpd_check_t flags = CPD_CHECK_MODE_ONLY; if (get_parent_directory(p)<0 || p[0] != '/') { log_warn(LD_GENERAL, "Bad unix socket address '%s'. Tor does not support " @@ -927,18 +976,23 @@ check_location_for_unix_socket(const or_options_t *options, const char *path) goto done; } - if (options->ControlSocketsGroupWritable) + if ((purpose == UNIX_SOCKET_PURPOSE_CONTROL_SOCKET && + options->ControlSocketsGroupWritable) || + (purpose == UNIX_SOCKET_PURPOSE_SOCKS_SOCKET && + options->SocksSocketsGroupWritable)) { flags |= CPD_GROUP_OK; + } if (check_private_dir(p, flags, options->User) < 0) { char *escpath, *escdir; escpath = esc_for_log(path); escdir = esc_for_log(p); - log_warn(LD_GENERAL, "Before Tor can create a control socket in %s, the " - "directory %s needs to exist, and to be accessible only by the " - "user%s account that is running Tor. (On some Unix systems, " - "anybody who can list a socket can connect to it, so Tor is " - "being careful.)", escpath, escdir, + log_warn(LD_GENERAL, "Before Tor can create a %s in %s, the directory " + "%s needs to exist, and to be accessible only by the user%s " + "account that is running Tor. (On some Unix systems, anybody " + "who can list a socket can connect to it, so Tor is being " + "careful.)", + unix_socket_purpose_to_string(purpose), escpath, escdir, options->ControlSocketsGroupWritable ? " and group" : ""); tor_free(escpath); tor_free(escdir); @@ -1021,15 +1075,15 @@ connection_listener_new(const struct sockaddr *listensockaddr, static int global_next_session_group = SESSION_GROUP_FIRST_AUTO; tor_addr_t addr; - if (get_n_open_sockets() >= get_options()->ConnLimit_-1) { + if (get_n_open_sockets() >= options->ConnLimit_-1) { warn_too_many_conns(); return NULL; } if (listensockaddr->sa_family == AF_INET || listensockaddr->sa_family == AF_INET6) { - int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER); - if (is_tcp) + int is_stream = (type != CONN_TYPE_AP_DNS_LISTENER); + if (is_stream) start_reading = 1; tor_addr_from_sockaddr(&addr, listensockaddr, &usePort); @@ -1038,10 +1092,10 @@ connection_listener_new(const struct sockaddr *listensockaddr, conn_type_to_string(type), fmt_addrport(&addr, usePort)); s = tor_open_socket_nonblocking(tor_addr_family(&addr), - is_tcp ? SOCK_STREAM : SOCK_DGRAM, - is_tcp ? IPPROTO_TCP: IPPROTO_UDP); + is_stream ? SOCK_STREAM : SOCK_DGRAM, + is_stream ? IPPROTO_TCP: IPPROTO_UDP); if (!SOCKET_OK(s)) { - log_warn(LD_NET,"Socket creation failed: %s", + log_warn(LD_NET, "Socket creation failed: %s", tor_socket_strerror(tor_socket_errno(-1))); goto err; } @@ -1098,7 +1152,7 @@ connection_listener_new(const struct sockaddr *listensockaddr, goto err; } - if (is_tcp) { + if (is_stream) { if (tor_listen(s) < 0) { log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, tor_socket_strerror(tor_socket_errno(s))); @@ -1121,15 +1175,25 @@ connection_listener_new(const struct sockaddr *listensockaddr, tor_addr_from_sockaddr(&addr2, (struct sockaddr*)&ss, &gotPort); } #ifdef HAVE_SYS_UN_H + /* + * AF_UNIX generic setup stuff (this covers both CONN_TYPE_CONTROL_LISTENER + * and CONN_TYPE_AP_LISTENER cases) + */ } else if (listensockaddr->sa_family == AF_UNIX) { + /* We want to start reading for both AF_UNIX cases */ start_reading = 1; - /* For now only control ports can be Unix domain sockets + /* For now only control ports or SOCKS ports can be Unix domain sockets * and listeners at the same time */ - tor_assert(type == CONN_TYPE_CONTROL_LISTENER); + tor_assert(type == CONN_TYPE_CONTROL_LISTENER || + type == CONN_TYPE_AP_LISTENER); - if (check_location_for_unix_socket(options, address) < 0) - goto err; + if (check_location_for_unix_socket(options, address, + (type == CONN_TYPE_CONTROL_LISTENER) ? + UNIX_SOCKET_PURPOSE_CONTROL_SOCKET : + UNIX_SOCKET_PURPOSE_SOCKS_SOCKET) < 0) { + goto err; + } log_notice(LD_NET, "Opening %s on %s", conn_type_to_string(type), address); @@ -1141,17 +1205,20 @@ connection_listener_new(const struct sockaddr *listensockaddr, strerror(errno)); goto err; } + s = tor_open_socket_nonblocking(AF_UNIX, SOCK_STREAM, 0); if (! SOCKET_OK(s)) { log_warn(LD_NET,"Socket creation failed: %s.", strerror(errno)); goto err; } - if (bind(s, listensockaddr, (socklen_t)sizeof(struct sockaddr_un)) == -1) { + if (bind(s, listensockaddr, + (socklen_t)sizeof(struct sockaddr_un)) == -1) { log_warn(LD_NET,"Bind to %s failed: %s.", address, tor_socket_strerror(tor_socket_errno(s))); goto err; } + #ifdef HAVE_PWD_H if (options->User) { pw = tor_getpwnam(options->User); @@ -1166,13 +1233,27 @@ connection_listener_new(const struct sockaddr *listensockaddr, } } #endif - if (options->ControlSocketsGroupWritable) { + + if ((type == CONN_TYPE_CONTROL_LISTENER && + options->ControlSocketsGroupWritable) || + (type == CONN_TYPE_AP_LISTENER && + options->SocksSocketsGroupWritable)) { /* We need to use chmod; fchmod doesn't work on sockets on all * platforms. */ if (chmod(address, 0660) < 0) { log_warn(LD_FS,"Unable to make %s group-writable.", address); goto err; } + } else if ((type == CONN_TYPE_CONTROL_LISTENER && + !(options->ControlSocketsGroupWritable)) || + (type == CONN_TYPE_AP_LISTENER && + !(options->SocksSocketsGroupWritable))) { + /* We need to use chmod; fchmod doesn't work on sockets on all + * platforms. */ + if (chmod(address, 0600) < 0) { + log_warn(LD_FS,"Unable to make %s group-writable.", address); + goto err; + } } if (listen(s, SOMAXCONN) < 0) { @@ -1180,8 +1261,6 @@ connection_listener_new(const struct sockaddr *listensockaddr, tor_socket_strerror(tor_socket_errno(s))); goto err; } -#else - (void)options; #endif /* HAVE_SYS_UN_H */ } else { log_err(LD_BUG, "Got unexpected address family %d.", @@ -1286,6 +1365,8 @@ check_sockaddr(const struct sockaddr *sa, int len, int level) "Address for new connection has address/port equal to zero."); ok = 0; } + } else if (sa->sa_family == AF_UNIX) { + ok = 1; } else { ok = 0; } @@ -1370,7 +1451,8 @@ connection_handle_listener_read(connection_t *conn, int new_type) return 0; } - if (conn->socket_family == AF_INET || conn->socket_family == AF_INET6) { + if (conn->socket_family == AF_INET || conn->socket_family == AF_INET6 || + (conn->socket_family == AF_UNIX && new_type == CONN_TYPE_AP)) { tor_addr_t addr; uint16_t port; if (check_sockaddr(remote, remotelen, LOG_INFO)<0) { @@ -1411,14 +1493,21 @@ connection_handle_listener_read(connection_t *conn, int new_type) newconn->port = port; newconn->address = tor_dup_addr(&addr); + if (new_type == CONN_TYPE_AP && conn->socket_family != AF_UNIX) { + log_info(LD_NET, "New SOCKS connection opened from %s.", + fmt_and_decorate_addr(&addr)); + } + if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { + newconn->port = 0; + newconn->address = tor_strdup(conn->address); + log_info(LD_NET, "New SOCKS SocksSocket connection opened"); + } if (new_type == CONN_TYPE_CONTROL) { log_notice(LD_CONTROL, "New control connection opened from %s.", fmt_and_decorate_addr(&addr)); } - } else if (conn->socket_family == AF_UNIX) { - /* For now only control ports can be Unix domain sockets - * and listeners at the same time */ + } else if (conn->socket_family == AF_UNIX && conn->type != CONN_TYPE_AP) { tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER); tor_assert(new_type == CONN_TYPE_CONTROL); log_notice(LD_CONTROL, "New control connection opened."); @@ -2371,6 +2460,7 @@ connection_is_rate_limited(connection_t *conn) return 0; /* Internal connection */ else if (! options->CountPrivateBandwidth && (tor_addr_family(&conn->addr) == AF_UNSPEC || /* no address */ + tor_addr_family(&conn->addr) == AF_UNIX || /* no address */ tor_addr_is_internal(&conn->addr, 0))) return 0; /* Internal address */ else diff --git a/src/or/control.c b/src/or/control.c index d21682a19c..9ff71c9541 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1438,6 +1438,8 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, (void) conn; if (!strcmp(question, "version")) { *answer = tor_strdup(get_version()); + } else if (!strcmp(question, "bw-event-cache")) { + *answer = get_bw_samples(); } else if (!strcmp(question, "config-file")) { *answer = tor_strdup(get_torrc_fname(0)); } else if (!strcmp(question, "config-defaults-file")) { @@ -2113,6 +2115,7 @@ typedef struct getinfo_item_t { * to answer them. */ static const getinfo_item_t getinfo_items[] = { ITEM("version", misc, "The current version of Tor."), + ITEM("bw-event-cache", misc, "Cached BW events for a short interval."), ITEM("config-file", misc, "Current location of the \"torrc\" file."), ITEM("config-defaults-file", misc, "Current location of the defaults file."), ITEM("config-text", misc, @@ -2465,6 +2468,14 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len, goto done; } + if (smartlist_len(args) < 2) { + connection_printf_to_buf(conn, + "512 syntax error: not enough arguments.\r\n"); + SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); + smartlist_free(args); + goto done; + } + smartlist_split_string(router_nicknames, smartlist_get(args,1), ",", 0, 0); SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); @@ -4147,11 +4158,29 @@ control_event_tb_empty(const char *bucket, uint32_t read_empty_time, return 0; } +/* about 5 minutes worth. */ +#define N_BW_EVENTS_TO_CACHE 300 +/* Index into cached_bw_events to next write. */ +static int next_measurement_idx = 0; +/* number of entries set in n_measurements */ +static int n_measurements = 0; +static struct cached_bw_event_s { + uint32_t n_read; + uint32_t n_written; +} cached_bw_events[N_BW_EVENTS_TO_CACHE]; + /** A second or more has elapsed: tell any interested control * connections how much bandwidth we used. */ int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written) { + cached_bw_events[next_measurement_idx].n_read = n_read; + cached_bw_events[next_measurement_idx].n_written = n_written; + if (++next_measurement_idx == N_BW_EVENTS_TO_CACHE) + next_measurement_idx = 0; + if (n_measurements < N_BW_EVENTS_TO_CACHE) + ++n_measurements; + if (EVENT_IS_INTERESTING(EVENT_BANDWIDTH_USED)) { send_control_event(EVENT_BANDWIDTH_USED, ALL_FORMATS, "650 BW %lu %lu\r\n", @@ -4162,6 +4191,35 @@ control_event_bandwidth_used(uint32_t n_read, uint32_t n_written) return 0; } +STATIC char * +get_bw_samples(void) +{ + int i; + int idx = (next_measurement_idx + N_BW_EVENTS_TO_CACHE - n_measurements) + % N_BW_EVENTS_TO_CACHE; + tor_assert(0 <= idx && idx < N_BW_EVENTS_TO_CACHE); + + smartlist_t *elements = smartlist_new(); + + for (i = 0; i < n_measurements; ++i) { + tor_assert(0 <= idx && idx < N_BW_EVENTS_TO_CACHE); + const struct cached_bw_event_s *bwe = &cached_bw_events[idx]; + + smartlist_add_asprintf(elements, "%u,%u", + (unsigned)bwe->n_read, + (unsigned)bwe->n_written); + + idx = (idx + 1) % N_BW_EVENTS_TO_CACHE; + } + + char *result = smartlist_join_strings(elements, " ", 0, NULL); + + SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); + smartlist_free(elements); + + return result; +} + /** Called when we are sending a log message to the controllers: suspend * sending further log messages to the controllers until we're done. Used by * CONN_LOG_PROTECT. */ diff --git a/src/or/control.h b/src/or/control.h index 0af09ae653..8c9f7bbdc9 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -203,6 +203,7 @@ void append_cell_stats_by_command(smartlist_t *event_parts, const uint64_t *number_to_include); void format_cell_stats(char **event_string, circuit_t *circ, cell_stats_t *cell_stats); +STATIC char *get_bw_samples(void); #endif #endif diff --git a/src/or/directory.c b/src/or/directory.c index ceea410313..7b4020080c 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -20,6 +20,7 @@ #include "networkstatus.h" #include "nodelist.h" #include "policies.h" +#include "relay.h" #include "rendclient.h" #include "rendcommon.h" #include "rephist.h" @@ -2539,6 +2540,24 @@ client_likes_consensus(networkstatus_t *v, const char *want_url) return (have >= need_at_least); } +/** Return the compression level we should use for sending a compressed + * response of size <b>n_bytes</b>. */ +static zlib_compression_level_t +choose_compression_level(ssize_t n_bytes) +{ + if (! have_been_under_memory_pressure()) { + return HIGH_COMPRESSION; /* we have plenty of RAM. */ + } else if (n_bytes < 0) { + return HIGH_COMPRESSION; /* unknown; might be big. */ + } else if (n_bytes < 1024) { + return LOW_COMPRESSION; + } else if (n_bytes < 2048) { + return MEDIUM_COMPRESSION; + } else { + return HIGH_COMPRESSION; + } +} + /** Helper function: called when a dirserver gets a complete HTTP GET * request. Look for a request for a directory or for a rendezvous * service descriptor. On finding one, write a response into @@ -2724,7 +2743,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, smartlist_len(dir_fps) == 1 ? lifetime : 0); conn->fingerprint_stack = dir_fps; if (! compressed) - conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD); + conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD, HIGH_COMPRESSION); /* Prime the connection with some data. */ conn->dir_spool_src = DIR_SPOOL_NETWORKSTATUS; @@ -2812,7 +2831,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, if (smartlist_len(items)) { if (compressed) { - conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD); + conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD, + choose_compression_level(estimated_len)); SMARTLIST_FOREACH(items, const char *, c, connection_write_to_buf_zlib(c, strlen(c), conn, 0)); connection_write_to_buf_zlib("", 0, conn, 1); @@ -2861,7 +2881,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, conn->fingerprint_stack = fps; if (compressed) - conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD); + conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD, + choose_compression_level(dlen)); connection_dirserv_flushed_some(conn); goto done; @@ -2929,7 +2950,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, } write_http_response_header(conn, -1, compressed, cache_lifetime); if (compressed) - conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD); + conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD, + choose_compression_level(dlen)); /* Prime the connection with some data. */ connection_dirserv_flushed_some(conn); } @@ -3004,7 +3026,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, write_http_response_header(conn, compressed?-1:len, compressed, 60*60); if (compressed) { - conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD); + conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD, + choose_compression_level(len)); SMARTLIST_FOREACH(certs, authority_cert_t *, c, connection_write_to_buf_zlib(c->cache_info.signed_descriptor_body, c->cache_info.signed_descriptor_len, diff --git a/src/or/dirserv.c b/src/or/dirserv.c index fbb2156515..b694f8af77 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -2113,9 +2113,10 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, rs->ipv6_orport = ri->ipv6_orport; } - /* Iff we are in a testing network, use TestingDirAuthVoteExit to - give out Exit flags, and TestingDirAuthVoteGuard to - give out Guard flags. */ + /* Iff we are in a testing network, use TestingDirAuthVoteExit, + TestingDirAuthVoteGuard, and TestingDirAuthVoteHSDir to + give out the Exit, Guard, and HSDir flags, respectively. + But don't set the corresponding node flags. */ if (options->TestingTorNetwork) { if (routerset_contains_routerstatus(options->TestingDirAuthVoteExit, rs, 0)) { @@ -2123,9 +2124,15 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, } if (routerset_contains_routerstatus(options->TestingDirAuthVoteGuard, - rs, 0)) { + rs, 0)) { rs->is_possible_guard = 1; } + + if (routerset_contains_routerstatus(options->TestingDirAuthVoteHSDir, + rs, 0)) { + /* TestingDirAuthVoteHSDir respects VoteOnHidServDirectoriesV2 */ + rs->is_hs_dir = vote_on_hsdirs; + } } } @@ -2260,7 +2267,7 @@ int dirserv_read_measured_bandwidths(const char *from_file, smartlist_t *routerstatuses) { - char line[256]; + char line[512]; FILE *fp = tor_fopen_cloexec(from_file, "r"); int applied_lines = 0; time_t file_time, now; @@ -3196,7 +3203,7 @@ connection_dirserv_add_networkstatus_bytes_to_outbuf(dir_connection_t *conn) if (uncompressing && ! conn->zlib_state && conn->fingerprint_stack && smartlist_len(conn->fingerprint_stack)) { - conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD); + conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD, HIGH_COMPRESSION); } } if (r) return r; diff --git a/src/or/dns.c b/src/or/dns.c index 129ca395b6..cc4a169422 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -558,6 +558,8 @@ purge_expired_resolves(time_t now) /* Connections should only be pending if they have no socket. */ tor_assert(!SOCKET_OK(pend->conn->base_.s)); pendconn = pend->conn; + /* Prevent double-remove */ + pendconn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED; if (!pendconn->base_.marked_for_close) { connection_edge_end(pendconn, END_STREAM_REASON_TIMEOUT); circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); @@ -1133,7 +1135,9 @@ connection_dns_remove(edge_connection_t *conn) return; /* more are pending */ } } - tor_assert(0); /* not reachable unless onlyconn not in pending list */ + log_warn(LD_BUG, "Connection (fd "TOR_SOCKET_T_FORMAT") was not waiting " + "for a resolve of %s, but we tried to remove it.", + conn->base_.s, escaped_safe_str(conn->base_.address)); } } diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 9eb0efd670..968a993999 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -1319,7 +1319,7 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) "EntryGuardDownSince/UnlistedSince without EntryGuard"); break; } - if (parse_iso_time(line->value, &when)<0) { + if (parse_iso_time_(line->value, &when, 0)<0) { *msg = tor_strdup("Unable to parse entry nodes: " "Bad time in EntryGuardDownSince/UnlistedSince"); break; diff --git a/src/or/include.am b/src/or/include.am index fb1581c463..b44e1099dc 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -123,6 +123,9 @@ src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ src/common/libor-event-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ +TESTING_TOR_BINARY = ./src/or/tor-cov +else +TESTING_TOR_BINARY = ./src/or/tor endif ORHEADERS = \ diff --git a/src/or/main.c b/src/or/main.c index 44469ebe71..abf3230c4c 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -76,6 +76,12 @@ #endif #ifdef HAVE_SYSTEMD +# if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) +/* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse + * Coverity. Here's a kludge to unconfuse it. + */ +# define __INCLUDE_LEVEL__ 2 +# endif #include <systemd/sd-daemon.h> #endif @@ -385,6 +391,10 @@ connection_remove(connection_t *conn) (int)conn->s, conn_type_to_string(conn->type), smartlist_len(connection_array)); + if (conn->type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { + log_info(LD_NET, "Closing SOCKS SocksSocket connection"); + } + control_event_conn_bandwidth(conn); tor_assert(conn->conn_array_index >= 0); @@ -1437,7 +1447,7 @@ run_scheduled_events(time_t now) if (time_to_clean_caches < now) { rep_history_clean(now - options->RephistTrackTime); rend_cache_clean(now); - rend_cache_clean_v2_descs_as_dir(now); + rend_cache_clean_v2_descs_as_dir(now, 0); microdesc_cache_rebuild(NULL, 0); #define CLEAN_CACHES_INTERVAL (30*60) time_to_clean_caches = now + CLEAN_CACHES_INTERVAL; @@ -1770,7 +1780,9 @@ static periodic_timer_t *systemd_watchdog_timer = NULL; static void systemd_watchdog_callback(periodic_timer_t *timer, void *arg) { - sd_notify(1, "WATCHDOG=1"); + (void)timer; + (void)arg; + sd_notify(0, "WATCHDOG=1"); } #endif @@ -2081,7 +2093,7 @@ do_main_loop(void) #endif #ifdef HAVE_SYSTEMD - log_notice(LD_GENERAL, "Signaling readyness to systemd"); + log_notice(LD_GENERAL, "Signaling readiness to systemd"); sd_notify(0, "READY=1"); #endif @@ -2163,6 +2175,9 @@ process_signal(uintptr_t sig) tor_cleanup(); exit(0); } +#ifdef HAVE_SYSTEMD + sd_notify(0, "STOPPING=1"); +#endif hibernate_begin_shutdown(); break; #ifdef SIGPIPE @@ -2182,11 +2197,17 @@ process_signal(uintptr_t sig) control_event_signal(sig); break; case SIGHUP: +#ifdef HAVE_SYSTEMD + sd_notify(0, "RELOADING=1"); +#endif if (do_hup() < 0) { log_warn(LD_CONFIG,"Restart failed (config error?). Exiting."); tor_cleanup(); exit(1); } +#ifdef HAVE_SYSTEMD + sd_notify(0, "READY=1"); +#endif control_event_signal(sig); break; #ifdef SIGCHLD diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index fdab03d05a..59ba1e6cb7 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -876,7 +876,8 @@ update_consensus_networkstatus_fetch_time_impl(time_t now, int flav) log_debug(LD_DIR, "fresh_until: %ld start: %ld " "dl_interval: %ld valid_until: %ld ", - c->fresh_until, start, dl_interval, c->valid_until); + (long)c->fresh_until, (long)start, dl_interval, + (long)c->valid_until); /* We must not try to replace c while it's still fresh: */ tor_assert(c->fresh_until < start); /* We must download the next one before c is invalid: */ diff --git a/src/or/or.h b/src/or/or.h index 56a40ebf5b..7568fc16a8 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1673,6 +1673,8 @@ typedef struct entry_connection_t { */ unsigned int may_use_optimistic_data : 1; + /** Are we a socks SocksSocket listener? */ + unsigned int is_socks_socket:1; } entry_connection_t; typedef enum { @@ -3464,6 +3466,10 @@ typedef struct { * for control connections. */ int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */ + config_line_t *SocksSocket; /**< List of Unix Domain Sockets to listen on + * for SOCKS connections. */ + + int SocksSocketsGroupWritable; /**< Boolean: Are SOCKS sockets g+rw? */ /** Ports to listen on for directory connections. */ config_line_t *DirPort_lines; config_line_t *DNSPort_lines; /**< Ports to listen on for DNS requests. */ @@ -3473,6 +3479,8 @@ typedef struct { uint64_t MaxMemInQueues_raw; uint64_t MaxMemInQueues;/**< If we have more memory than this allocated * for queues and buffers, run the OOM handler */ + /** Above this value, consider ourselves low on RAM. */ + uint64_t MaxMemInQueues_low_threshold; /** @name port booleans * @@ -3484,6 +3492,7 @@ typedef struct { */ unsigned int ORPort_set : 1; unsigned int SocksPort_set : 1; + unsigned int SocksSocket_set : 1; unsigned int TransPort_set : 1; unsigned int NATDPort_set : 1; unsigned int ControlPort_set : 1; @@ -3609,8 +3618,9 @@ typedef struct { * hostname ending with one of the suffixes in * <b>AutomapHostsSuffixes</b>, map it to a * virtual address. */ - smartlist_t *AutomapHostsSuffixes; /**< List of suffixes for - * <b>AutomapHostsOnResolve</b>. */ + /** List of suffixes for <b>AutomapHostsOnResolve</b>. The special value + * "." means "match everything." */ + smartlist_t *AutomapHostsSuffixes; int RendPostPeriod; /**< How often do we post each rendezvous service * descriptor? Remember to publish them independently. */ int KeepalivePeriod; /**< How often do we send padding cells to keep @@ -4038,6 +4048,11 @@ typedef struct { * regardless of uptime and bandwidth. */ routerset_t *TestingDirAuthVoteGuard; + /** Relays in a testing network which should be voted HSDir + * regardless of uptime and ORPort connectivity. + * Respects VoteOnHidServDirectoriesV2. */ + routerset_t *TestingDirAuthVoteHSDir; + /** Enable CONN_BW events. Only altered on testing networks. */ int TestingEnableConnBwEvent; @@ -4204,6 +4219,14 @@ typedef struct { * when sending. */ int SchedulerMaxFlushCells__; + + /** Is this an exit node? This is a tristate, where "1" means "yes, and use + * the default exit policy if none is given" and "0" means "no; exit policy + * is 'reject *'" and "auto" (-1) means "same as 1, but warn the user." + * + * XXXX Eventually, the default will be 0. */ + int ExitRelay; + } or_options_t; /** Persistent state for an onion router, as saved to disk. */ @@ -4875,6 +4898,8 @@ typedef struct rend_service_descriptor_t { typedef struct rend_cache_entry_t { size_t len; /**< Length of <b>desc</b> */ time_t received; /**< When was the descriptor received? */ + time_t last_served; /**< When did we last write this one to somebody? + * (HSDir only) */ char *desc; /**< Service descriptor */ rend_service_descriptor_t *parsed; /**< Parsed value of 'desc' */ } rend_cache_entry_t; diff --git a/src/or/policies.c b/src/or/policies.c index 2095907025..560b8cb4c3 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -434,6 +434,33 @@ validate_addr_policies(const or_options_t *options, char **msg) REJECT("Error in ExitPolicy entry."); } + static int warned_about_exitrelay = 0; + + const int exitrelay_setting_is_auto = options->ExitRelay == -1; + const int policy_accepts_something = + ! (policy_is_reject_star(addr_policy, AF_INET) && + policy_is_reject_star(addr_policy, AF_INET6)); + + if (server_mode(options) && + ! warned_about_exitrelay && + exitrelay_setting_is_auto && + policy_accepts_something) { + /* Policy accepts something */ + warned_about_exitrelay = 1; + log_warn(LD_CONFIG, + "Tor is running as an exit relay%s. If you did not want this " + "behavior, please set the ExitRelay option to 0. If you do " + "want to run an exit Relay, please set the ExitRelay option " + "to 1 to disable this warning, and for forward compatibility.", + options->ExitPolicy == NULL ? + " with the default exit policy" : ""); + if (options->ExitPolicy == NULL) { + log_warn(LD_CONFIG, + "In a future version of Tor, ExitRelay 0 may become the " + "default when no ExitPolicy is given."); + } + } + /* The rest of these calls *append* to addr_policy. So don't actually * use the results for anything other than checking if they parse! */ if (parse_addr_policy(options->DirPolicy, &addr_policy, -1)) @@ -1022,6 +1049,9 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, * * If <b>or_options->BridgeRelay</b> is false, add entries of default * Tor exit policy into <b>result</b> smartlist. + * + * If or_options->ExitRelay is false, then make our exit policy into + * "reject *:*" regardless. */ int policies_parse_exit_policy_from_options(const or_options_t *or_options, @@ -1030,6 +1060,12 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options, { exit_policy_parser_cfg_t parser_cfg = 0; + if (or_options->ExitRelay == 0) { + append_exit_policy_string(result, "reject *4:*"); + append_exit_policy_string(result, "reject *6:*"); + return 0; + } + if (or_options->IPv6Exit) { parser_cfg |= EXIT_POLICY_IPV6_ENABLED; } diff --git a/src/or/reasons.c b/src/or/reasons.c index c65acb54ae..23ab6041a6 100644 --- a/src/or/reasons.c +++ b/src/or/reasons.c @@ -350,6 +350,8 @@ circuit_end_reason_to_control_string(int reason) return "NOSUCHSERVICE"; case END_CIRC_REASON_MEASUREMENT_EXPIRED: return "MEASUREMENT_EXPIRED"; + case END_CIRC_REASON_IP_NOW_REDUNDANT: + return "IP_NOW_REDUNDANT"; default: if (is_remote) { /* diff --git a/src/or/relay.c b/src/or/relay.c index 201f5c3d34..350353e452 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -2433,6 +2433,12 @@ cell_queues_get_total_allocation(void) return total_cells_allocated * packed_cell_mem_cost(); } +/** How long after we've been low on memory should we try to conserve it? */ +#define MEMORY_PRESSURE_INTERVAL (30*60) + +/** The time at which we were last low on memory. */ +static time_t last_time_under_memory_pressure = 0; + /** Check whether we've got too much space used for cells. If so, * call the OOM handler and return 1. Otherwise, return 0. */ STATIC int @@ -2441,13 +2447,37 @@ cell_queues_check_size(void) size_t alloc = cell_queues_get_total_allocation(); alloc += buf_get_total_allocation(); alloc += tor_zlib_get_total_allocation(); - if (alloc >= get_options()->MaxMemInQueues) { - circuits_handle_oom(alloc); - return 1; + const size_t rend_cache_total = rend_cache_get_total_allocation(); + alloc += rend_cache_total; + if (alloc >= get_options()->MaxMemInQueues_low_threshold) { + last_time_under_memory_pressure = approx_time(); + if (alloc >= get_options()->MaxMemInQueues) { + /* If we're spending over 20% of the memory limit on hidden service + * descriptors, free them until we're down to 10%. + */ + if (rend_cache_total > get_options()->MaxMemInQueues / 5) { + const size_t bytes_to_remove = + rend_cache_total - (size_t)(get_options()->MaxMemInQueues / 10); + rend_cache_clean_v2_descs_as_dir(time(NULL), bytes_to_remove); + alloc -= rend_cache_total; + alloc += rend_cache_get_total_allocation(); + } + circuits_handle_oom(alloc); + return 1; + } } return 0; } +/** Return true if we've been under memory pressure in the last + * MEMORY_PRESSURE_INTERVAL seconds. */ +int +have_been_under_memory_pressure(void) +{ + return last_time_under_memory_pressure + MEMORY_PRESSURE_INTERVAL + < approx_time(); +} + /** * Update the number of cells available on the circuit's n_chan or p_chan's * circuit mux. diff --git a/src/or/relay.h b/src/or/relay.h index df3edfbdb1..cdc2a9ae19 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -50,6 +50,8 @@ void clean_cell_pool(void); void dump_cell_pool_usage(int severity); size_t packed_cell_mem_cost(void); +int have_been_under_memory_pressure(void); + /* For channeltls.c */ void packed_cell_free(packed_cell_t *cell); diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 8cace92b2c..4b7d85a5e0 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -547,7 +547,12 @@ directory_clean_last_hid_serv_requests(time_t now) /** Remove all requests related to the hidden service named * <b>onion_address</b> from the history of times of requests to - * hidden service directories. */ + * hidden service directories. + * + * This is called from rend_client_note_connection_attempt_ended(), which + * must be idempotent, so any future changes to this function must leave + * it idempotent too. + */ static void purge_hid_serv_from_last_hid_serv_requests(const char *onion_address) { @@ -1076,8 +1081,11 @@ rend_client_desc_trynow(const char *query) /** Clear temporary state used only during an attempt to connect to * the hidden service named <b>onion_address</b>. Called when a - * connection attempt has ended; may be called occasionally at other - * times, and should be reasonably harmless. */ + * connection attempt has ended; it is possible for this to be called + * multiple times while handling an ended connection attempt, and + * any future changes to this function must ensure it remains + * idempotent. + */ void rend_client_note_connection_attempt_ended(const char *onion_address) { diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index e54ca40a25..88d9aaba48 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -411,7 +411,7 @@ rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc) &test_intro_content, &test_intro_size, &test_encoded_size, - &test_next, desc->desc_str); + &test_next, desc->desc_str, 1); rend_service_descriptor_free(test_parsed); tor_free(test_intro_content); return (res >= 0); @@ -704,6 +704,9 @@ static strmap_t *rend_cache = NULL; * directories. */ static digestmap_t *rend_cache_v2_dir = NULL; +/** DOCDOC */ +static size_t rend_cache_total_allocation = 0; + /** Initializes the service descriptor cache. */ void @@ -713,12 +716,64 @@ rend_cache_init(void) rend_cache_v2_dir = digestmap_new(); } +/** Return the approximate number of bytes needed to hold <b>e</b>. */ +static size_t +rend_cache_entry_allocation(const rend_cache_entry_t *e) +{ + if (!e) + return 0; + + /* This doesn't count intro_nodes or key size */ + return sizeof(*e) + e->len + sizeof(*e->parsed); +} + +/** DOCDOC */ +size_t +rend_cache_get_total_allocation(void) +{ + return rend_cache_total_allocation; +} + +/** Decrement the total bytes attributed to the rendezvous cache by n. */ +static void +rend_cache_decrement_allocation(size_t n) +{ + static int have_underflowed = 0; + + if (rend_cache_total_allocation >= n) { + rend_cache_total_allocation -= n; + } else { + rend_cache_total_allocation = 0; + if (! have_underflowed) { + have_underflowed = 1; + log_warn(LD_BUG, "Underflow in rend_cache_decrement_allocation"); + } + } +} + +/** Increase the total bytes attributed to the rendezvous cache by n. */ +static void +rend_cache_increment_allocation(size_t n) +{ + static int have_overflowed = 0; + if (rend_cache_total_allocation <= SIZE_MAX - n) { + rend_cache_total_allocation += n; + } else { + rend_cache_total_allocation = SIZE_MAX; + if (! have_overflowed) { + have_overflowed = 1; + log_warn(LD_BUG, "Overflow in rend_cache_increment_allocation"); + } + } +} + /** Helper: free storage held by a single service descriptor cache entry. */ static void rend_cache_entry_free(rend_cache_entry_t *e) { if (!e) return; + rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); rend_service_descriptor_free(e->parsed); tor_free(e->desc); tor_free(e); @@ -740,6 +795,7 @@ rend_cache_free_all(void) digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_); rend_cache = NULL; rend_cache_v2_dir = NULL; + rend_cache_total_allocation = 0; } /** Removes all old entries from the service descriptor cache. @@ -777,31 +833,46 @@ rend_cache_purge(void) } /** Remove all old v2 descriptors and those for which this hidden service - * directory is not responsible for any more. */ + * directory is not responsible for any more. + * + * If at all possible, remove at least <b>force_remove</b> bytes of data. + */ void -rend_cache_clean_v2_descs_as_dir(time_t now) +rend_cache_clean_v2_descs_as_dir(time_t now, size_t force_remove) { digestmap_iter_t *iter; time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW; - for (iter = digestmap_iter_init(rend_cache_v2_dir); - !digestmap_iter_done(iter); ) { - const char *key; - void *val; - rend_cache_entry_t *ent; - digestmap_iter_get(iter, &key, &val); - ent = val; - if (ent->parsed->timestamp < cutoff || - !hid_serv_responsible_for_desc_id(key)) { - char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; - base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN); - log_info(LD_REND, "Removing descriptor with ID '%s' from cache", - safe_str_client(key_base32)); - iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter); - rend_cache_entry_free(ent); - } else { - iter = digestmap_iter_next(rend_cache_v2_dir, iter); + const int LAST_SERVED_CUTOFF_STEP = 1800; + time_t last_served_cutoff = cutoff; + size_t bytes_removed = 0; + do { + for (iter = digestmap_iter_init(rend_cache_v2_dir); + !digestmap_iter_done(iter); ) { + const char *key; + void *val; + rend_cache_entry_t *ent; + digestmap_iter_get(iter, &key, &val); + ent = val; + if (ent->parsed->timestamp < cutoff || + ent->last_served < last_served_cutoff || + !hid_serv_responsible_for_desc_id(key)) { + char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN); + log_info(LD_REND, "Removing descriptor with ID '%s' from cache", + safe_str_client(key_base32)); + bytes_removed += rend_cache_entry_allocation(ent); + iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter); + rend_cache_entry_free(ent); + } else { + iter = digestmap_iter_next(rend_cache_v2_dir, iter); + } } - } + + /* In case we didn't remove enough bytes, advance the cutoff a little. */ + last_served_cutoff += LAST_SERVED_CUTOFF_STEP; + if (last_served_cutoff > now) + break; + } while (bytes_removed < force_remove); } /** Determines whether <b>a</b> is in the interval of <b>b</b> (excluded) and @@ -903,6 +974,7 @@ rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc) e = digestmap_get(rend_cache_v2_dir, desc_id_digest); if (e) { *desc = e->desc; + e->last_served = approx_time(); return 1; } return 0; @@ -946,7 +1018,7 @@ rend_cache_store_v2_desc_as_dir(const char *desc) } while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, &intro_size, &encoded_size, - &next_desc, current_desc) >= 0) { + &next_desc, current_desc, 1) >= 0) { number_parsed++; /* We don't care about the introduction points. */ tor_free(intro_content); @@ -993,7 +1065,13 @@ rend_cache_store_v2_desc_as_dir(const char *desc) if (!e) { e = tor_malloc_zero(sizeof(rend_cache_entry_t)); digestmap_set(rend_cache_v2_dir, desc_id, e); + /* Treat something just uploaded as having been served a little + * while ago, so that flooding with new descriptors doesn't help + * too much. + */ + e->last_served = approx_time() - 3600; } else { + rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); rend_service_descriptor_free(e->parsed); tor_free(e->desc); } @@ -1001,6 +1079,7 @@ rend_cache_store_v2_desc_as_dir(const char *desc) e->parsed = parsed; e->desc = tor_strndup(current_desc, encoded_size); e->len = encoded_size; + rend_cache_increment_allocation(rend_cache_entry_allocation(e)); log_info(LD_REND, "Successfully stored service descriptor with desc ID " "'%s' and len %d.", safe_str(desc_id_base32), (int)encoded_size); @@ -1091,7 +1170,7 @@ rend_cache_store_v2_desc_as_client(const char *desc, /* Parse the descriptor. */ if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, &intro_size, &encoded_size, - &next_desc, desc) < 0) { + &next_desc, desc, 0) < 0) { log_warn(LD_REND, "Could not parse descriptor."); goto err; } @@ -1189,6 +1268,7 @@ rend_cache_store_v2_desc_as_client(const char *desc, e = tor_malloc_zero(sizeof(rend_cache_entry_t)); strmap_set_lc(rend_cache, key, e); } else { + rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); rend_service_descriptor_free(e->parsed); tor_free(e->desc); } @@ -1197,6 +1277,7 @@ rend_cache_store_v2_desc_as_client(const char *desc, e->desc = tor_malloc_zero(encoded_size + 1); strlcpy(e->desc, desc, encoded_size + 1); e->len = encoded_size; + rend_cache_increment_allocation(rend_cache_entry_allocation(e)); log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.", safe_str_client(service_id), (int)encoded_size); return RCS_OKAY; diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h index 4b910d2729..8396cc3551 100644 --- a/src/or/rendcommon.h +++ b/src/or/rendcommon.h @@ -33,7 +33,7 @@ void rend_intro_point_free(rend_intro_point_t *intro); void rend_cache_init(void); void rend_cache_clean(time_t now); -void rend_cache_clean_v2_descs_as_dir(time_t now); +void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove); void rend_cache_purge(void); void rend_cache_free_all(void); int rend_valid_service_id(const char *query); @@ -51,7 +51,6 @@ rend_cache_store_status_t rend_cache_store_v2_desc_as_dir(const char *desc); rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc, const char *desc_id_base32, const rend_data_t *rend_query); - int rend_encode_v2_descriptors(smartlist_t *descs_out, rend_service_descriptor_t *desc, time_t now, uint8_t period, rend_auth_type_t auth_type, @@ -64,6 +63,7 @@ int rend_id_is_in_interval(const char *a, const char *b, const char *c); void rend_get_descriptor_id_bytes(char *descriptor_id_out, const char *service_id, const char *secret_id_part); +size_t rend_cache_get_total_allocation(void); #endif diff --git a/src/or/rendservice.c b/src/or/rendservice.c index b9d98755ea..ca9b380d7d 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -531,7 +531,7 @@ rend_config_services(const or_options_t *options, int validate_only) } } if (service) { - cpd_check_t check_opts = CPD_CHECK_MODE_ONLY; + cpd_check_t check_opts = CPD_CHECK_MODE_ONLY|CPD_CHECK; if (service->dir_group_readable) { check_opts |= CPD_GROUP_READ; } @@ -3270,6 +3270,9 @@ rend_services_introduce(void) smartlist_free(exclude_nodes); } +#define MIN_REND_INITIAL_POST_DELAY (30) +#define MIN_REND_INITIAL_POST_DELAY_TESTING (5) + /** Regenerate and upload rendezvous service descriptors for all * services, if necessary. If the descriptor has been dirty enough * for long enough, definitely upload; else only upload when the @@ -3284,6 +3287,9 @@ rend_consider_services_upload(time_t now) int i; rend_service_t *service; int rendpostperiod = get_options()->RendPostPeriod; + int rendinitialpostdelay = (get_options()->TestingTorNetwork ? + MIN_REND_INITIAL_POST_DELAY_TESTING : + MIN_REND_INITIAL_POST_DELAY); if (!get_options()->PublishHidServDescriptors) return; @@ -3291,17 +3297,17 @@ rend_consider_services_upload(time_t now) for (i=0; i < smartlist_len(rend_service_list); ++i) { service = smartlist_get(rend_service_list, i); if (!service->next_upload_time) { /* never been uploaded yet */ - /* The fixed lower bound of 30 seconds ensures that the descriptor - * is stable before being published. See comment below. */ + /* The fixed lower bound of rendinitialpostdelay seconds ensures that + * the descriptor is stable before being published. See comment below. */ service->next_upload_time = - now + 30 + crypto_rand_int(2*rendpostperiod); + now + rendinitialpostdelay + crypto_rand_int(2*rendpostperiod); } if (service->next_upload_time < now || (service->desc_is_dirty && - service->desc_is_dirty < now-30)) { + service->desc_is_dirty < now-rendinitialpostdelay)) { /* if it's time, or if the directory servers have a wrong service - * descriptor and ours has been stable for 30 seconds, upload a - * new one of each format. */ + * descriptor and ours has been stable for rendinitialpostdelay seconds, + * upload a new one of each format. */ rend_service_update_descriptor(service); upload_service_descriptor(service); } diff --git a/src/or/rephist.c b/src/or/rephist.c index bc29378bea..34908828a5 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -1131,9 +1131,7 @@ rep_hist_load_mtbf_data(time_t now) * totals? */ #define NUM_SECS_ROLLING_MEASURE 10 /** How large are the intervals for which we track and report bandwidth use? */ -/* XXXX Watch out! Before Tor 0.2.2.21-alpha, using any other value here would - * generate an unparseable state file. */ -#define NUM_SECS_BW_SUM_INTERVAL (15*60) +#define NUM_SECS_BW_SUM_INTERVAL (4*60*60) /** How far in the past do we remember and publish bandwidth use? */ #define NUM_SECS_BW_SUM_IS_VALID (24*60*60) /** How many bandwidth usage intervals do we remember? (derived) */ diff --git a/src/or/router.c b/src/or/router.c index 3eb0ea65b8..2ddaa895fc 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -313,6 +313,7 @@ rotate_onion_key(void) time_t now; fname = get_datadir_fname2("keys", "secret_onion_key"); fname_prev = get_datadir_fname2("keys", "secret_onion_key.old"); + /* There isn't much point replacing an old key with an empty file */ if (file_status(fname) == FN_FILE) { if (replace_file(fname, fname_prev)) goto error; @@ -335,6 +336,7 @@ rotate_onion_key(void) fname_prev = get_datadir_fname2("keys", "secret_onion_key_ntor.old"); if (curve25519_keypair_generate(&new_curve25519_keypair, 1) < 0) goto error; + /* There isn't much point replacing an old key with an empty file */ if (file_status(fname) == FN_FILE) { if (replace_file(fname, fname_prev)) goto error; @@ -411,7 +413,11 @@ init_key_from_file(const char *fname, int generate, int severity, case FN_ERROR: tor_log(severity, LD_FS,"Can't read key from \"%s\"", fname); goto error; + /* treat empty key files as if the file doesn't exist, and, + * if generate is set, replace the empty file in + * crypto_pk_write_private_key_to_filename() */ case FN_NOENT: + case FN_EMPTY: if (generate) { if (!have_lockfile()) { if (try_locking(get_options(), 0)<0) { @@ -464,10 +470,10 @@ init_key_from_file(const char *fname, int generate, int severity, } /** Load a curve25519 keypair from the file <b>fname</b>, writing it into - * <b>keys_out</b>. If the file isn't found and <b>generate</b> is true, - * create a new keypair and write it into the file. If there are errors, log - * them at level <b>severity</b>. Generate files using <b>tag</b> in their - * ASCII wrapper. */ + * <b>keys_out</b>. If the file isn't found, or is empty, and <b>generate</b> + * is true, create a new keypair and write it into the file. If there are + * errors, log them at level <b>severity</b>. Generate files using <b>tag</b> + * in their ASCII wrapper. */ static int init_curve25519_keypair_from_file(curve25519_keypair_t *keys_out, const char *fname, @@ -480,7 +486,10 @@ init_curve25519_keypair_from_file(curve25519_keypair_t *keys_out, case FN_ERROR: tor_log(severity, LD_FS,"Can't read key from \"%s\"", fname); goto error; + /* treat empty key files as if the file doesn't exist, and, if generate + * is set, replace the empty file in curve25519_keypair_write_to_file() */ case FN_NOENT: + case FN_EMPTY: if (generate) { if (!have_lockfile()) { if (try_locking(get_options(), 0)<0) { @@ -880,7 +889,9 @@ init_keys(void) keydir = get_datadir_fname2("keys", "secret_onion_key.old"); if (!lastonionkey && file_status(keydir) == FN_FILE) { - prkey = init_key_from_file(keydir, 1, LOG_ERR, 0); /* XXXX Why 1? */ + /* Load keys from non-empty files only. + * Missing old keys won't be replaced with freshly generated keys. */ + prkey = init_key_from_file(keydir, 0, LOG_ERR, 0); if (prkey) lastonionkey = prkey; } @@ -901,6 +912,8 @@ init_keys(void) last_curve25519_onion_key.pubkey.public_key, CURVE25519_PUBKEY_LEN) && file_status(keydir) == FN_FILE) { + /* Load keys from non-empty files only. + * Missing old keys won't be replaced with freshly generated keys. */ init_curve25519_keypair_from_file(&last_curve25519_onion_key, keydir, 0, LOG_ERR, "onion"); } @@ -2577,8 +2590,9 @@ router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport) * <b>end_line</b>, ensure that its timestamp is not more than 25 hours in * the past or more than 1 hour in the future with respect to <b>now</b>, * and write the file contents starting with that line to *<b>out</b>. - * Return 1 for success, 0 if the file does not exist, or -1 if the file - * does not contain a line matching these criteria or other failure. */ + * Return 1 for success, 0 if the file does not exist or is empty, or -1 + * if the file does not contain a line matching these criteria or other + * failure. */ static int load_stats_file(const char *filename, const char *end_line, time_t now, char **out) @@ -2612,7 +2626,9 @@ load_stats_file(const char *filename, const char *end_line, time_t now, notfound: tor_free(contents); break; + /* treat empty stats files as if the file doesn't exist */ case FN_NOENT: + case FN_EMPTY: r = 0; break; case FN_ERROR: diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 6cb052c03e..d3734238eb 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -1206,6 +1206,7 @@ router_reload_router_list_impl(desc_store_t *store) tor_free(fname); fname = get_datadir_fname_suffix(store->fname_base, ".new"); + /* don't load empty files - we wouldn't get any data, even if we tried */ if (file_status(fname) == FN_FILE) contents = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); if (contents) { diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 840350dab2..a2bc8fbb93 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -4250,40 +4250,50 @@ tor_version_parse(const char *s, tor_version_t *out) char *eos=NULL; const char *cp=NULL; /* Format is: - * "Tor " ? NUM dot NUM dot NUM [ ( pre | rc | dot ) NUM [ - tag ] ] + * "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ] */ tor_assert(s); tor_assert(out); memset(out, 0, sizeof(tor_version_t)); - + out->status = VER_RELEASE; if (!strcasecmpstart(s, "Tor ")) s += 4; - /* Get major. */ - out->major = (int)strtol(s,&eos,10); - if (!eos || eos==s || *eos != '.') return -1; - cp = eos+1; - - /* Get minor */ - out->minor = (int) strtol(cp,&eos,10); - if (!eos || eos==cp || *eos != '.') return -1; - cp = eos+1; - - /* Get micro */ - out->micro = (int) strtol(cp,&eos,10); - if (!eos || eos==cp) return -1; - if (!*eos) { - out->status = VER_RELEASE; - out->patchlevel = 0; + cp = s; + +#define NUMBER(m) \ + do { \ + out->m = (int)strtol(cp, &eos, 10); \ + if (!eos || eos == cp) \ + return -1; \ + cp = eos; \ + } while (0) + +#define DOT() \ + do { \ + if (*cp != '.') \ + return -1; \ + ++cp; \ + } while (0) + + NUMBER(major); + DOT(); + NUMBER(minor); + if (*cp == 0) return 0; - } - cp = eos; + else if (*cp == '-') + goto status_tag; + DOT(); + NUMBER(micro); /* Get status */ - if (*cp == '.') { - out->status = VER_RELEASE; + if (*cp == 0) { + return 0; + } else if (*cp == '.') { ++cp; + } else if (*cp == '-') { + goto status_tag; } else if (0==strncmp(cp, "pre", 3)) { out->status = VER_PRE; cp += 3; @@ -4294,11 +4304,9 @@ tor_version_parse(const char *s, tor_version_t *out) return -1; } - /* Get patchlevel */ - out->patchlevel = (int) strtol(cp,&eos,10); - if (!eos || eos==cp) return -1; - cp = eos; + NUMBER(patchlevel); + status_tag: /* Get status tag. */ if (*cp == '-' || *cp == '.') ++cp; @@ -4334,6 +4342,8 @@ tor_version_parse(const char *s, tor_version_t *out) } return 0; +#undef NUMBER +#undef DOT } /** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a > @@ -4421,6 +4431,9 @@ sort_version_list(smartlist_t *versions, int remove_duplicates) * to *<b>encoded_size_out</b>, and a pointer to the possibly next * descriptor to *<b>next_out</b>; return 0 for success (including validation) * and -1 for failure. + * + * If <b>as_hsdir</b> is 1, we're parsing this as an HSDir, and we should + * be strict about time formats. */ int rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, @@ -4428,7 +4441,8 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, char **intro_points_encrypted_out, size_t *intro_points_encrypted_size_out, size_t *encoded_size_out, - const char **next_out, const char *desc) + const char **next_out, const char *desc, + int as_hsdir) { rend_service_descriptor_t *result = tor_malloc_zero(sizeof(rend_service_descriptor_t)); @@ -4442,6 +4456,8 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, char public_key_hash[DIGEST_LEN]; char test_desc_id[DIGEST_LEN]; memarea_t *area = NULL; + const int strict_time_fmt = as_hsdir; + tor_assert(desc); /* Check if desc starts correctly. */ if (strncmp(desc, "rendezvous-service-descriptor ", @@ -4536,7 +4552,7 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, * descriptor. */ tok = find_by_keyword(tokens, R_PUBLICATION_TIME); tor_assert(tok->n_args == 1); - if (parse_iso_time(tok->args[0], &result->timestamp) < 0) { + if (parse_iso_time_(tok->args[0], &result->timestamp, strict_time_fmt) < 0) { log_warn(LD_REND, "Invalid publication time: '%s'", tok->args[0]); goto err; } diff --git a/src/or/routerparse.h b/src/or/routerparse.h index 15fd03b810..18a7d2563c 100644 --- a/src/or/routerparse.h +++ b/src/or/routerparse.h @@ -73,7 +73,8 @@ int rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, char **intro_points_encrypted_out, size_t *intro_points_encrypted_size_out, size_t *encoded_size_out, - const char **next_out, const char *desc); + const char **next_out, const char *desc, + int as_hsdir); int rend_decrypt_introduction_points(char **ipos_decrypted, size_t *ipos_decrypted_size, const char *descriptor_cookie, diff --git a/src/or/statefile.c b/src/or/statefile.c index c279858de6..dd1894beb7 100644 --- a/src/or/statefile.c +++ b/src/or/statefile.c @@ -323,7 +323,10 @@ or_state_load(void) goto done; } break; + /* treat empty state files as if the file doesn't exist, and generate + * a new state file, overwriting the empty file in or_state_save() */ case FN_NOENT: + case FN_EMPTY: break; case FN_ERROR: case FN_DIR: diff --git a/src/test/include.am b/src/test/include.am index 9db1587da7..b9b381fdae 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -122,9 +122,11 @@ if USEPYTHON ./src/test/test-bt-cl assert | $(PYTHON) $(top_srcdir)/src/test/bt_test.py ./src/test/test-bt-cl crash | $(PYTHON) $(top_srcdir)/src/test/bt_test.py endif + $(top_srcdir)/src/test/zero_length_keys.sh EXTRA_DIST += \ src/test/bt_test.py \ src/test/ntor_ref.py \ src/test/slownacl_curve25519.py \ - src/test/test_cmdline_args.py + src/test/test_cmdline_args.py \ + src/test/zero_length_keys.sh diff --git a/src/test/test-network.sh b/src/test/test-network.sh index d28fbde80f..be57cafb7f 100755 --- a/src/test/test-network.sh +++ b/src/test/test-network.sh @@ -45,7 +45,7 @@ PATH="$TOR_DIR/src/or:$TOR_DIR/src/tools:$PATH" # Sleep some, waiting for the network to bootstrap. # TODO: Add chutney command 'bootstrap-status' and use that instead. -BOOTSTRAP_TIME=${BOOTSTRAP_TIME:-18} +BOOTSTRAP_TIME=${BOOTSTRAP_TIME:-25} $ECHO_N "$myname: sleeping for $BOOTSTRAP_TIME seconds" n=$BOOTSTRAP_TIME; while [ $n -gt 0 ]; do sleep 1; n=$(expr $n - 1); $ECHO_N . diff --git a/src/test/test.c b/src/test/test.c index 513ec3c597..de6efaf873 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -696,12 +696,12 @@ test_rend_fns(void *arg) smartlist_get(descs, 0))->desc_id, OP_EQ, computed_desc_id, DIGEST_LEN); tt_assert(rend_parse_v2_service_descriptor(&parsed, parsed_desc_id, - &intro_points_encrypted, - &intro_points_size, - &encoded_size, - &next_desc, - ((rend_encoded_v2_service_descriptor_t *) - smartlist_get(descs, 0))->desc_str) == 0); + &intro_points_encrypted, + &intro_points_size, + &encoded_size, + &next_desc, + ((rend_encoded_v2_service_descriptor_t *) + smartlist_get(descs, 0))->desc_str, 1) == 0); tt_assert(parsed); tt_mem_op(((rend_encoded_v2_service_descriptor_t *) smartlist_get(descs, 0))->desc_id,OP_EQ, parsed_desc_id, DIGEST_LEN); diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index 875da7028f..101f448472 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -611,7 +611,7 @@ test_buffers_zlib_impl(int finalize_with_nil) int done; buf = buf_new_with_capacity(128); /* will round up */ - zlib_state = tor_zlib_new(1, ZLIB_METHOD); + zlib_state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); msg = tor_malloc(512); crypto_rand(msg, 512); @@ -688,7 +688,7 @@ test_buffers_zlib_fin_at_chunk_end(void *arg) tt_uint_op(buf->head->datalen, OP_EQ, headerjunk); tt_uint_op(buf_datalen(buf), OP_EQ, headerjunk); /* Write an empty string, with finalization on. */ - zlib_state = tor_zlib_new(1, ZLIB_METHOD); + zlib_state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); tt_int_op(write_to_buf_zlib(buf, zlib_state, "", 0, 1), OP_EQ, 0); in_len = buf_datalen(buf); diff --git a/src/test/test_checkdir.c b/src/test/test_checkdir.c index 882e3b3a61..ae859449cb 100644 --- a/src/test/test_checkdir.c +++ b/src/test/test_checkdir.c @@ -11,6 +11,7 @@ #ifdef _WIN32 #define mkdir(a,b) mkdir(a) #define tt_int_op_nowin(a,op,b) do { (void)(a); (void)(b); } while (0) +#define umask(mask) ((void)0) #else #define tt_int_op_nowin(a,op,b) tt_int_op((a),op,(b)) #endif @@ -28,6 +29,8 @@ test_checkdir_perms(void *testdata) cpd_check_t unix_verify_optsmask; struct stat st; + umask(022); + /* setup data directory before tests. */ tor_free(options->DataDirectory); options->DataDirectory = tor_strdup(get_fname(subdir)); @@ -134,7 +137,7 @@ test_checkdir_perms(void *testdata) { #name, test_checkdir_##name, (flags), NULL, NULL } struct testcase_t checkdir_tests[] = { - CHECKDIR(perms, 0), + CHECKDIR(perms, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_cmdline_args.py b/src/test/test_cmdline_args.py index 55d1cdb805..c8e68e8240 100755 --- a/src/test/test_cmdline_args.py +++ b/src/test/test_cmdline_args.py @@ -57,14 +57,14 @@ def run_tor(args, failure=False): raise UnexpectedFailure() elif not result and failure: raise UnexpectedSuccess() - return b2s(output) + return b2s(output.replace('\r\n','\n')) def spaceify_fp(fp): for i in range(0, len(fp), 4): yield fp[i:i+4] def lines(s): - out = s.split("\n") + out = s.splitlines() if out and out[-1] == '': del out[-1] return out @@ -151,7 +151,7 @@ class CmdlineTests(unittest.TestCase): if os.stat(TOR).st_mtime < os.stat(main_c).st_mtime: self.skipTest(TOR+" not up to date") out = run_tor(["--digests"]) - main_line = [ l for l in lines(out) if l.endswith("/main.c") ] + main_line = [ l for l in lines(out) if l.endswith("/main.c") or l.endswith(" main.c") ] digest, name = main_line[0].split() f = open(main_c, 'rb') actual = hashlib.sha1(f.read()).hexdigest() diff --git a/src/test/test_config.c b/src/test/test_config.c index 0b411e3354..fb8e4020dc 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -1218,7 +1218,6 @@ test_config_resolve_my_address(void *arg) UNMOCK(tor_gethostname); tor_free(hostname_out); - /* * CASE 7: * We want resolve_my_address() to try and get network interface address via diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 69933896dd..c6594f8359 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -845,6 +845,42 @@ test_dir_versions(void *arg) tt_int_op(VER_RELEASE,OP_EQ, ver1.status); tt_str_op("",OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("10.1", &ver1)); + tt_int_op(10, OP_EQ, ver1.major); + tt_int_op(1, OP_EQ, ver1.minor); + tt_int_op(0, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("5.99.999", &ver1)); + tt_int_op(5, OP_EQ, ver1.major); + tt_int_op(99, OP_EQ, ver1.minor); + tt_int_op(999, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("10.1-alpha", &ver1)); + tt_int_op(10, OP_EQ, ver1.major); + tt_int_op(1, OP_EQ, ver1.minor); + tt_int_op(0, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("alpha", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("2.1.700-alpha", &ver1)); + tt_int_op(2, OP_EQ, ver1.major); + tt_int_op(1, OP_EQ, ver1.minor); + tt_int_op(700, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("alpha", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("1.6.8-alpha-dev", &ver1)); + tt_int_op(1, OP_EQ, ver1.major); + tt_int_op(6, OP_EQ, ver1.minor); + tt_int_op(8, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("alpha-dev", OP_EQ, ver1.status_tag); + #define tt_versionstatus_op(vs1, op, vs2) \ tt_assert_test_type(vs1,vs2,#vs1" "#op" "#vs2,version_status_t, \ (val1_ op val2_),"%d",TT_EXIT_TEST_FUNCTION) diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c index 10652e20c1..0a6fef729c 100644 --- a/src/test/test_relaycell.c +++ b/src/test/test_relaycell.c @@ -104,7 +104,7 @@ test_relaycell_resolved(void *arg) tt_int_op(srm_answer_is_set, OP_EQ, 0); \ } \ tt_int_op(srm_ttl, OP_EQ, ttl); \ - tt_int_op(srm_expires, OP_EQ, expires); \ + tt_i64_op(srm_expires, OP_EQ, expires); \ } while (0) (void)arg; diff --git a/src/test/test_util.c b/src/test/test_util.c index f821c6e612..2ee1d6dfc1 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -589,15 +589,17 @@ test_util_time(void *arg) i = parse_iso_time("2004-8-4 0:48:22", &t_res); tt_int_op(0,OP_EQ, i); tt_int_op(t_res,OP_EQ, (time_t)1091580502UL); - tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-zz 99-99x99 GMT", &t_res)); - tt_int_op(-1,OP_EQ, parse_iso_time("2011-03-32 00:00:00 GMT", &t_res)); - tt_int_op(-1,OP_EQ, parse_iso_time("2011-03-30 24:00:00 GMT", &t_res)); - tt_int_op(-1,OP_EQ, parse_iso_time("2011-03-30 23:60:00 GMT", &t_res)); - tt_int_op(-1,OP_EQ, parse_iso_time("2011-03-30 23:59:62 GMT", &t_res)); - tt_int_op(-1,OP_EQ, parse_iso_time("1969-03-30 23:59:59 GMT", &t_res)); - tt_int_op(-1,OP_EQ, parse_iso_time("2011-00-30 23:59:59 GMT", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-zz 99-99x99", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("2011-03-32 00:00:00", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("2011-03-30 24:00:00", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("2011-03-30 23:60:00", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("2011-03-30 23:59:62", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("1969-03-30 23:59:59", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("2011-00-30 23:59:59", &t_res)); tt_int_op(-1,OP_EQ, parse_iso_time("2147483647-08-29 14:00:00", &t_res)); tt_int_op(-1,OP_EQ, parse_iso_time("2011-03-30 23:59", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04 00:48:22.100", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04 00:48:22XYZ", &t_res)); /* Test tor_gettimeofday */ @@ -1820,7 +1822,7 @@ test_util_gzip(void *arg) tor_free(buf1); tor_free(buf2); tor_free(buf3); - state = tor_zlib_new(1, ZLIB_METHOD); + state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); tt_assert(state); cp1 = buf1 = tor_malloc(1024); len1 = 1024; @@ -4612,26 +4614,26 @@ test_util_round_to_next_multiple_of(void *arg) { (void)arg; - tt_assert(round_uint64_to_next_multiple_of(0,1) == 0); - tt_assert(round_uint64_to_next_multiple_of(0,7) == 0); + tt_u64_op(round_uint64_to_next_multiple_of(0,1), ==, 0); + tt_u64_op(round_uint64_to_next_multiple_of(0,7), ==, 0); - tt_assert(round_uint64_to_next_multiple_of(99,1) == 99); - tt_assert(round_uint64_to_next_multiple_of(99,7) == 105); - tt_assert(round_uint64_to_next_multiple_of(99,9) == 99); + tt_u64_op(round_uint64_to_next_multiple_of(99,1), ==, 99); + tt_u64_op(round_uint64_to_next_multiple_of(99,7), ==, 105); + tt_u64_op(round_uint64_to_next_multiple_of(99,9), ==, 99); - tt_assert(round_int64_to_next_multiple_of(0,1) == 0); - tt_assert(round_int64_to_next_multiple_of(0,7) == 0); + tt_i64_op(round_int64_to_next_multiple_of(0,1), ==, 0); + tt_i64_op(round_int64_to_next_multiple_of(0,7), ==, 0); - tt_assert(round_int64_to_next_multiple_of(99,1) == 99); - tt_assert(round_int64_to_next_multiple_of(99,7) == 105); - tt_assert(round_int64_to_next_multiple_of(99,9) == 99); + tt_i64_op(round_int64_to_next_multiple_of(99,1), ==, 99); + tt_i64_op(round_int64_to_next_multiple_of(99,7), ==, 105); + tt_i64_op(round_int64_to_next_multiple_of(99,9), ==, 99); - tt_assert(round_int64_to_next_multiple_of(-99,1) == -99); - tt_assert(round_int64_to_next_multiple_of(-99,7) == -98); - tt_assert(round_int64_to_next_multiple_of(-99,9) == -99); + tt_i64_op(round_int64_to_next_multiple_of(-99,1), ==, -99); + tt_i64_op(round_int64_to_next_multiple_of(-99,7), ==, -98); + tt_i64_op(round_int64_to_next_multiple_of(-99,9), ==, -99); - tt_assert(round_int64_to_next_multiple_of(INT64_MIN,2) == INT64_MIN); - tt_assert(round_int64_to_next_multiple_of(INT64_MAX,2) == + tt_i64_op(round_int64_to_next_multiple_of(INT64_MIN,2), ==, INT64_MIN); + tt_i64_op(round_int64_to_next_multiple_of(INT64_MAX,2), ==, INT64_MAX-INT64_MAX%2); done: ; @@ -4652,25 +4654,26 @@ test_util_laplace(void *arg) const double delta_f = 15.0, epsilon = 0.3; /* b = 15.0 / 0.3 = 50.0 */ (void)arg; - tt_assert(isinf(sample_laplace_distribution(mu, b, 0.0))); - test_feq(-69.88855213, sample_laplace_distribution(mu, b, 0.01)); - test_feq(24.0, sample_laplace_distribution(mu, b, 0.5)); - test_feq(24.48486498, sample_laplace_distribution(mu, b, 0.51)); - test_feq(117.88855213, sample_laplace_distribution(mu, b, 0.99)); + tt_i64_op(INT64_MIN, ==, sample_laplace_distribution(mu, b, 0.0)); + tt_i64_op(-69, ==, sample_laplace_distribution(mu, b, 0.01)); + tt_i64_op(24, ==, sample_laplace_distribution(mu, b, 0.5)); + tt_i64_op(24, ==, sample_laplace_distribution(mu, b, 0.51)); + tt_i64_op(117, ==, sample_laplace_distribution(mu, b, 0.99)); /* >>> laplace.ppf([0.0, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99], * ... loc = 0, scale = 50) * array([ -inf, -80.47189562, -34.65735903, 0. , * 34.65735903, 80.47189562, 195.60115027]) */ - tt_assert(INT64_MIN + 20 == + tt_i64_op(INT64_MIN + 20, ==, add_laplace_noise(20, 0.0, delta_f, epsilon)); - tt_assert(-60 == add_laplace_noise(20, 0.1, delta_f, epsilon)); - tt_assert(-14 == add_laplace_noise(20, 0.25, delta_f, epsilon)); - tt_assert(20 == add_laplace_noise(20, 0.5, delta_f, epsilon)); - tt_assert(54 == add_laplace_noise(20, 0.75, delta_f, epsilon)); - tt_assert(100 == add_laplace_noise(20, 0.9, delta_f, epsilon)); - tt_assert(215 == add_laplace_noise(20, 0.99, delta_f, epsilon)); + tt_i64_op(-60, ==, add_laplace_noise(20, 0.1, delta_f, epsilon)); + tt_i64_op(-14, ==, add_laplace_noise(20, 0.25, delta_f, epsilon)); + tt_i64_op(20, ==, add_laplace_noise(20, 0.5, delta_f, epsilon)); + tt_i64_op(54, ==, add_laplace_noise(20, 0.75, delta_f, epsilon)); + tt_i64_op(100, ==, add_laplace_noise(20, 0.9, delta_f, epsilon)); + tt_i64_op(215, ==, add_laplace_noise(20, 0.99, delta_f, epsilon)); + done: ; } @@ -4849,7 +4852,7 @@ test_util_max_mem(void *arg) } else { /* You do not have a petabyte. */ #if SIZEOF_SIZE_T == SIZEOF_UINT64_T - tt_uint_op(memory1, OP_LT, (U64_LITERAL(1)<<50)); + tt_u64_op(memory1, OP_LT, (U64_LITERAL(1)<<50)); #endif } diff --git a/src/test/zero_length_keys.sh b/src/test/zero_length_keys.sh new file mode 100755 index 0000000000..3a99ca1f1d --- /dev/null +++ b/src/test/zero_length_keys.sh @@ -0,0 +1,115 @@ +#!/bin/sh +# Check that tor regenerates keys when key files are zero-length +# Test for bug #13111 - Tor fails to start if onion keys are zero length +# +# Usage: +# ./zero_length_keys.sh +# Run all the tests below +# ./zero_length_keys.sh -z +# Check tor will launch and regenerate zero-length keys +# ./zero_length_keys.sh -d +# Check tor regenerates deleted keys (existing behaviour) +# ./zero_length_keys.sh -e +# Check tor does not overwrite existing keys (existing behaviour) +# +# Exit Statuses: +# -2: test failed - tor did not generate the key files on first run +# -1: a command failed - the test could not be completed +# 0: test succeeded - tor regenerated/kept the files +# 1: test failed - tor did not regenerate/keep the files +# + +if [ $# -lt 1 ]; then + echo "Testing that tor correctly handles zero-length keys" + "$0" -z && "$0" -d && "$0" -e + exit $? +fi + +export DATA_DIR=`mktemp -d -t tor_zero_length_keys.XXXXXX` +# DisableNetwork means that the ORPort won't actually be opened. +# 'ExitRelay 0' suppresses a warning. +TOR="./src/or/tor --hush --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0" + +if [ -s "$DATA_DIR"/keys/secret_id_key -a -s "$DATA_DIR"/keys/secret_onion_key -a -s "$DATA_DIR"/keys/secret_onion_key_ntor ]; then + echo "Failure: Previous tor keys present in tor data directory" + exit -1 +else + echo "Generating initial tor keys" + $TOR --DataDirectory "$DATA_DIR" --PidFile "$DATA_DIR"/pid & + TOR_PID=$! + # generate SIGTERM, hopefully after the keys have been regenerated + sleep 5 + kill $TOR_PID + wait $TOR_PID + + # tor must successfully generate non-zero-length key files + if [ -s "$DATA_DIR"/keys/secret_id_key -a -s "$DATA_DIR"/keys/secret_onion_key -a -s "$DATA_DIR"/keys/secret_onion_key_ntor ]; then + true #echo "tor generated the initial key files" + else + echo "Failure: tor failed to generate the initial key files" + exit -2 + fi +fi + +#ls -lh "$DATA_DIR"/keys/ || exit -1 + +# backup and keep/delete/create zero-length files for the keys + +FILE_DESC="keeps existing" +# make a backup +cp -r "$DATA_DIR"/keys "$DATA_DIR"/keys.old + +# delete keys for -d or -z +if [ "$1" != "-e" ]; then + FILE_DESC="regenerates deleted" + rm "$DATA_DIR"/keys/secret_id_key || exit -1 + rm "$DATA_DIR"/keys/secret_onion_key || exit -1 + rm "$DATA_DIR"/keys/secret_onion_key_ntor || exit -1 +fi + +# create empty files for -z +if [ "$1" = "-z" ]; then + FILE_DESC="regenerates zero-length" + touch "$DATA_DIR"/keys/secret_id_key || exit -1 + touch "$DATA_DIR"/keys/secret_onion_key || exit -1 + touch "$DATA_DIR"/keys/secret_onion_key_ntor || exit -1 +fi + +echo "Running tor again to check if it $FILE_DESC keys" +$TOR --DataDirectory "$DATA_DIR" --PidFile "$DATA_DIR"/pid & +TOR_PID=$! +# generate SIGTERM, hopefully after the keys have been regenerated +sleep 5 +kill $TOR_PID +wait $TOR_PID + +#ls -lh "$DATA_DIR"/keys/ || exit -1 + +# tor must always have non-zero-length key files +if [ -s "$DATA_DIR"/keys/secret_id_key -a -s "$DATA_DIR"/keys/secret_onion_key -a -s "$DATA_DIR"/keys/secret_onion_key_ntor ]; then + # check if the keys are different to the old ones + diff -q -r "$DATA_DIR"/keys "$DATA_DIR"/keys.old > /dev/null + SAME_KEYS=$? + # if we're not testing existing keys, + # the current keys should be different to the old ones + if [ "$1" != "-e" ]; then + if [ $SAME_KEYS -ne 0 ]; then + echo "Success: test that tor $FILE_DESC key files: different keys" + exit 0 + else + echo "Failure: test that tor $FILE_DESC key files: same keys" + exit 1 + fi + else #[ "$1" == "-e" ]; then + if [ $SAME_KEYS -eq 0 ]; then + echo "Success: test that tor $FILE_DESC key files: same keys" + exit 0 + else + echo "Failure: test that tor $FILE_DESC key files: different keys" + exit 1 + fi + fi +else + echo "Failure: test that tor $FILE_DESC key files: no key files" + exit 1 +fi |