diff options
Diffstat (limited to 'src')
41 files changed, 674 insertions, 231 deletions
diff --git a/src/common/compat.c b/src/common/compat.c index 76f9bcb97e..7d72b4b7fd 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -3424,3 +3424,4 @@ tor_get_avail_disk_space(const char *path) return -1; #endif } + diff --git a/src/common/container.h b/src/common/container.h index 5abd8b48d9..bf4f04762c 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -110,7 +110,8 @@ void smartlist_sort_digests256(smartlist_t *sl); void smartlist_sort_pointers(smartlist_t *sl); const char *smartlist_get_most_frequent_string(smartlist_t *sl); -const char *smartlist_get_most_frequent_string_(smartlist_t *sl, int *count_out); +const char *smartlist_get_most_frequent_string_(smartlist_t *sl, + int *count_out); const uint8_t *smartlist_get_most_frequent_digest256(smartlist_t *sl); void smartlist_uniq_strings(smartlist_t *sl); diff --git a/src/common/crypto.c b/src/common/crypto.c index 6d4b0d7e16..815c2ec0c5 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -2440,7 +2440,8 @@ crypto_rand_uint64_range(uint64_t min, uint64_t max) time_t crypto_rand_time_range(time_t min, time_t max) { - return (time_t) crypto_rand_uint64_range(min, max); + tor_assert(min < max); + return min + (time_t)crypto_rand_uint64(max - min); } /** Return a pseudorandom 64-bit integer, chosen uniformly from the values diff --git a/src/common/workqueue.c b/src/common/workqueue.c index b0b004dc25..c467bdf43b 100644 --- a/src/common/workqueue.c +++ b/src/common/workqueue.c @@ -25,7 +25,7 @@ struct threadpool_s { unsigned generation; /** Function that should be run for updates on each thread. */ - int (*update_fn)(void *, void *); + workqueue_reply_t (*update_fn)(void *, void *); /** Function to free update arguments if they can't be run. */ void (*free_update_arg_fn)(void *); /** Array of n_threads update arguments. */ @@ -56,7 +56,7 @@ struct workqueue_entry_s { /** True iff this entry is waiting for a worker to start processing it. */ uint8_t pending; /** Function to run in the worker thread. */ - int (*fn)(void *state, void *arg); + workqueue_reply_t (*fn)(void *state, void *arg); /** Function to run while processing the reply queue. */ void (*reply_fn)(void *arg); /** Argument for the above functions. */ @@ -96,7 +96,7 @@ static void queue_reply(replyqueue_t *queue, workqueue_entry_t *work); * <b>fn</b> in the worker thread, and <b>reply_fn</b> in the main * thread. See threadpool_queue_work() for full documentation. */ static workqueue_entry_t * -workqueue_entry_new(int (*fn)(void*, void*), +workqueue_entry_new(workqueue_reply_t (*fn)(void*, void*), void (*reply_fn)(void*), void *arg) { @@ -172,7 +172,7 @@ worker_thread_main(void *thread_) workerthread_t *thread = thread_; threadpool_t *pool = thread->in_pool; workqueue_entry_t *work; - int result; + workqueue_reply_t result; tor_mutex_acquire(&pool->lock); while (1) { @@ -182,13 +182,14 @@ worker_thread_main(void *thread_) if (thread->in_pool->generation != thread->generation) { void *arg = thread->in_pool->update_args[thread->index]; thread->in_pool->update_args[thread->index] = NULL; - int (*update_fn)(void*,void*) = thread->in_pool->update_fn; + workqueue_reply_t (*update_fn)(void*,void*) = + thread->in_pool->update_fn; thread->generation = thread->in_pool->generation; tor_mutex_release(&pool->lock); - int r = update_fn(thread->state, arg); + workqueue_reply_t r = update_fn(thread->state, arg); - if (r < 0) { + if (r != WQ_RPL_REPLY) { return; } @@ -208,7 +209,7 @@ worker_thread_main(void *thread_) queue_reply(thread->reply_queue, work); /* We may need to exit the thread. */ - if (result >= WQ_RPL_ERROR) { + if (result != WQ_RPL_REPLY) { return; } tor_mutex_acquire(&pool->lock); @@ -281,7 +282,7 @@ workerthread_new(void *state, threadpool_t *pool, replyqueue_t *replyqueue) */ workqueue_entry_t * threadpool_queue_work(threadpool_t *pool, - int (*fn)(void *, void *), + workqueue_reply_t (*fn)(void *, void *), void (*reply_fn)(void *), void *arg) { @@ -318,7 +319,7 @@ threadpool_queue_work(threadpool_t *pool, int threadpool_queue_update(threadpool_t *pool, void *(*dup_fn)(void *), - int (*fn)(void *, void *), + workqueue_reply_t (*fn)(void *, void *), void (*free_fn)(void *), void *arg) { diff --git a/src/common/workqueue.h b/src/common/workqueue.h index 92e82b8a48..9ce1eadafc 100644 --- a/src/common/workqueue.h +++ b/src/common/workqueue.h @@ -15,21 +15,22 @@ typedef struct threadpool_s threadpool_t; * pool. */ typedef struct workqueue_entry_s workqueue_entry_t; -/** Possible return value from a work function: indicates success. */ -#define WQ_RPL_REPLY 0 -/** Possible return value from a work function: indicates fatal error */ -#define WQ_RPL_ERROR 1 -/** Possible return value from a work function: indicates thread is shutting - * down. */ -#define WQ_RPL_SHUTDOWN 2 +/** Possible return value from a work function: */ +typedef enum { + WQ_RPL_REPLY = 0, /** indicates success */ + WQ_RPL_ERROR = 1, /** indicates fatal error */ + WQ_RPL_SHUTDOWN = 2, /** indicates thread is shutting down */ +} workqueue_reply_t; workqueue_entry_t *threadpool_queue_work(threadpool_t *pool, - int (*fn)(void *, void *), + workqueue_reply_t (*fn)(void *, + void *), void (*reply_fn)(void *), void *arg); + int threadpool_queue_update(threadpool_t *pool, void *(*dup_fn)(void *), - int (*fn)(void *, void *), + workqueue_reply_t (*fn)(void *, void *), void (*free_fn)(void *), void *arg); void *workqueue_entry_cancel(workqueue_entry_t *pending_work); diff --git a/src/ext/ed25519/donna/ed25519_tor.c b/src/ext/ed25519/donna/ed25519_tor.c index 7f5894da79..12493f7d14 100644 --- a/src/ext/ed25519/donna/ed25519_tor.c +++ b/src/ext/ed25519/donna/ed25519_tor.c @@ -323,7 +323,7 @@ int ed25519_donna_pubkey_from_curve25519_pubkey(unsigned char *out, const unsigned char *inp, int signbit) { - static const bignum25519 one = { 1 }; + static const bignum25519 ALIGN(16) one = { 1 }; bignum25519 ALIGN(16) u, uminus1, uplus1, inv_uplus1, y; /* Prop228: y = (u-1)/(u+1) */ diff --git a/src/ext/ed25519/donna/modm-donna-64bit.h b/src/ext/ed25519/donna/modm-donna-64bit.h index b22df2be75..012ea9ea08 100644 --- a/src/ext/ed25519/donna/modm-donna-64bit.h +++ b/src/ext/ed25519/donna/modm-donna-64bit.h @@ -261,6 +261,10 @@ contract256_slidingwindow_modm(signed char r[256], const bignum256modm s, int wi continue; for (b = 1; (b < (soplen - j)) && (b <= 6); b++) { + /* XXX Tor: coverity scan says that r[j+b] can + * overflow, but that's not possible: b < (soplen-j) + * guarantees that b + j < soplen, so b+j < 256, + * so the index doesn't overflow. */ if ((r[j] + (r[j + b] << b)) <= m) { r[j] += r[j + b] << b; r[j + b] = 0; diff --git a/src/or/buffers.c b/src/or/buffers.c index 2d7dd937d8..cc2f6f409b 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -178,13 +178,10 @@ preferred_chunk_size(size_t target) /** Collapse data from the first N chunks from <b>buf</b> into buf->head, * growing it as necessary, until buf->head has the first <b>bytes</b> bytes * of data from the buffer, or until buf->head has all the data in <b>buf</b>. - * - * If <b>nulterminate</b> is true, ensure that there is a 0 byte in - * buf->head->mem right after all the data. */ + */ STATIC void -buf_pullup(buf_t *buf, size_t bytes, int nulterminate) +buf_pullup(buf_t *buf, size_t bytes) { - /* XXXX nothing uses nulterminate; remove it. */ chunk_t *dest, *src; size_t capacity; if (!buf->head) @@ -194,17 +191,9 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate) if (buf->datalen < bytes) bytes = buf->datalen; - if (nulterminate) { - capacity = bytes + 1; - if (buf->head->datalen >= bytes && CHUNK_REMAINING_CAPACITY(buf->head)) { - *CHUNK_WRITE_PTR(buf->head) = '\0'; - return; - } - } else { - capacity = bytes; - if (buf->head->datalen >= bytes) - return; - } + capacity = bytes; + if (buf->head->datalen >= bytes) + return; if (buf->head->memlen >= capacity) { /* We don't need to grow the first chunk, but we might need to repack it.*/ @@ -248,11 +237,6 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate) } } - if (nulterminate) { - tor_assert(CHUNK_REMAINING_CAPACITY(buf->head)); - *CHUNK_WRITE_PTR(buf->head) = '\0'; - } - check(); } @@ -1203,7 +1187,7 @@ fetch_from_buf_http(buf_t *buf, /* Okay, we have a full header. Make sure it all appears in the first * chunk. */ if ((int)buf->head->datalen < crlf_offset + 4) - buf_pullup(buf, crlf_offset+4, 0); + buf_pullup(buf, crlf_offset+4); headerlen = crlf_offset + 4; headers = buf->head->data; @@ -1451,7 +1435,7 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req, do { n_drain = 0; - buf_pullup(buf, want_length, 0); + buf_pullup(buf, want_length); tor_assert(buf->head && buf->head->datalen >= 2); want_length = 0; @@ -1842,7 +1826,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req, log_warn(LD_PROTOCOL, "Your application (using socks5 to port %d) gave Tor " "a malformed hostname: %s. Rejecting the connection.", - req->port, escaped(req->address)); + req->port, escaped_safe_str_client(req->address)); return -1; } if (log_sockstype) @@ -1870,7 +1854,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req, *want_length_out = SOCKS4_NETWORK_LEN; return 0; /* not yet */ } - // buf_pullup(buf, 1280, 0); + // buf_pullup(buf, 1280); req->command = (unsigned char) *(data+1); if (req->command != SOCKS_COMMAND_CONNECT && req->command != SOCKS_COMMAND_RESOLVE) { @@ -2038,7 +2022,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason) if (buf->datalen < 2) return 0; - buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN, 0); + buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN); tor_assert(buf->head && buf->head->datalen >= 2); r = parse_socks_client((uint8_t*)buf->head->data, buf->head->datalen, diff --git a/src/or/buffers.h b/src/or/buffers.h index 6d0c68500b..7f79e3c0b2 100644 --- a/src/or/buffers.h +++ b/src/or/buffers.h @@ -101,7 +101,7 @@ void assert_buf_ok(buf_t *buf); #ifdef BUFFERS_PRIVATE STATIC int buf_find_string_offset(const buf_t *buf, const char *s, size_t n); -STATIC void buf_pullup(buf_t *buf, size_t bytes, int nulterminate); +STATIC void buf_pullup(buf_t *buf, size_t bytes); void buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz); #define DEBUG_CHUNK_ALLOC diff --git a/src/or/channel.c b/src/or/channel.c index af095026e4..21522a5303 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -3069,6 +3069,7 @@ channel_free_list(smartlist_t *channels, int mark_for_close) if (curr->cmux) { circuitmux_detach_all_circuits(curr->cmux, NULL); } + SMARTLIST_DEL_CURRENT(channels, curr); channel_unregister(curr); if (mark_for_close) { if (!CHANNEL_CONDEMNED(curr)) { diff --git a/src/or/circuituse.c b/src/or/circuituse.c index a3b71974ca..00340fd689 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -2284,8 +2284,15 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn, base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - if (!circ->base_.timestamp_dirty) - circ->base_.timestamp_dirty = time(NULL); + if (!circ->base_.timestamp_dirty || + ((conn->entry_cfg.isolation_flags & ISO_SOCKSAUTH) && + (conn->entry_cfg.socks_iso_keep_alive) && + (conn->socks_request->usernamelen || + conn->socks_request->passwordlen))) { + /* When stream isolation is in use and controlled by an application + * we are willing to keep using the stream. */ + circ->base_.timestamp_dirty = approx_time(); + } pathbias_count_use_attempt(circ); diff --git a/src/or/config.c b/src/or/config.c index 6e782de0e0..98d9d83846 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -289,7 +289,7 @@ static config_var_t option_vars_[] = { VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceNumIntroductionPoints", LINELIST_S, RendConfigLines, NULL), - V(HiddenServiceStatistics, BOOL, "0"), + V(HiddenServiceStatistics, BOOL, "1"), V(HidServAuth, LINELIST, NULL), V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"), V(CloseHSServiceRendCircuitsImmediatelyOnTimeout, BOOL, "0"), @@ -3173,6 +3173,21 @@ options_validate(or_options_t *old_options, or_options_t *options, "http://freehaven.net/anonbib/#hs-attack06 for details."); } + if (options->EntryNodes && + routerset_is_list(options->EntryNodes) && + (routerset_len(options->EntryNodes) == 1) && + (options->RendConfigLines != NULL)) { + tor_asprintf(msg, + "You have one single EntryNodes and at least one hidden service " + "configured. This is bad because it's very easy to locate your " + "entry guard which can then lead to the deanonymization of your " + "hidden service -- for more details, see " + "https://trac.torproject.org/projects/tor/ticket/14917. " + "For this reason, the use of one EntryNodes with an hidden " + "service is prohibited until a better solution is found."); + return -1; + } + if (!options->LearnCircuitBuildTimeout && options->CircuitBuildTimeout && options->CircuitBuildTimeout < RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT) { log_warn(LD_CONFIG, @@ -6022,6 +6037,7 @@ parse_port_config(smartlist_t *out, int sessiongroup = SESSION_GROUP_UNSET; unsigned isolation = ISO_DEFAULT; int prefer_no_auth = 0; + int socks_iso_keep_alive = 0; char *addrport; uint16_t ptmp=0; @@ -6231,6 +6247,9 @@ parse_port_config(smartlist_t *out, } else if (!strcasecmp(elt, "PreferSOCKSNoAuth")) { prefer_no_auth = ! no; continue; + } else if (!strcasecmp(elt, "KeepAliveIsolateSOCKSAuth")) { + socks_iso_keep_alive = ! no; + continue; } if (!strcasecmpend(elt, "s")) @@ -6276,6 +6295,13 @@ parse_port_config(smartlist_t *out, goto err; } + if (!(isolation & ISO_SOCKSAUTH) && socks_iso_keep_alive) { + log_warn(LD_CONFIG, "You have a %sPort entry with both " + "NoIsolateSOCKSAuth and KeepAliveIsolateSOCKSAuth set.", + portname); + goto err; + } + if (out && port) { size_t namelen = unix_socket_path ? strlen(unix_socket_path) : 0; port_cfg_t *cfg = port_cfg_new(namelen); @@ -6309,6 +6335,7 @@ parse_port_config(smartlist_t *out, cfg->entry_cfg.socks_prefer_no_auth = prefer_no_auth; if (! (isolation & ISO_SOCKSAUTH)) cfg->entry_cfg.socks_prefer_no_auth = 1; + cfg->entry_cfg.socks_iso_keep_alive = socks_iso_keep_alive; smartlist_add(out, cfg); } diff --git a/src/or/connection.c b/src/or/connection.c index ac36578069..78176d3768 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -654,8 +654,8 @@ connection_free_(connection_t *conn) /** Make sure <b>conn</b> isn't in any of the global conn lists; then free it. */ -void -connection_free(connection_t *conn) +MOCK_IMPL(void, +connection_free,(connection_t *conn)) { if (!conn) return; diff --git a/src/or/connection.h b/src/or/connection.h index d0a34ece5c..b6ff3d7bd6 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -28,7 +28,7 @@ listener_connection_t *listener_connection_new(int type, int socket_family); connection_t *connection_new(int type, int socket_family); void connection_link_connections(connection_t *conn_a, connection_t *conn_b); -void connection_free(connection_t *conn); +MOCK_DECL(void,connection_free,(connection_t *conn)); void connection_free_all(void); void connection_about_to_close_connection(connection_t *conn); void connection_close_immediate(connection_t *conn); diff --git a/src/or/control.c b/src/or/control.c index 3638bd2e05..220e7e514f 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1130,7 +1130,7 @@ static const struct control_event_t control_event_table[] = { { EVENT_CLIENTS_SEEN, "CLIENTS_SEEN" }, { EVENT_NEWCONSENSUS, "NEWCONSENSUS" }, { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" }, - { EVENT_SIGNAL, "SIGNAL" }, + { EVENT_GOT_SIGNAL, "SIGNAL" }, { EVENT_CONF_CHANGED, "CONF_CHANGED"}, { EVENT_CONN_BW, "CONN_BW" }, { EVENT_CELL_STATS, "CELL_STATS" }, @@ -5464,7 +5464,7 @@ control_event_signal(uintptr_t signal) { const char *signal_string = NULL; - if (!control_event_is_interesting(EVENT_SIGNAL)) + if (!control_event_is_interesting(EVENT_GOT_SIGNAL)) return 0; switch (signal) { @@ -5492,7 +5492,7 @@ control_event_signal(uintptr_t signal) return -1; } - send_control_event(EVENT_SIGNAL, "650 SIGNAL %s\r\n", + send_control_event(EVENT_GOT_SIGNAL, "650 SIGNAL %s\r\n", signal_string); return 0; } @@ -6478,3 +6478,4 @@ control_testing_set_global_event_mask(uint64_t mask) global_event_mask = mask; } #endif + diff --git a/src/or/control.h b/src/or/control.h index 574dd85002..fdf7903cb8 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -176,7 +176,7 @@ void control_free_all(void); #define EVENT_CLIENTS_SEEN 0x0015 #define EVENT_NEWCONSENSUS 0x0016 #define EVENT_BUILDTIMEOUT_SET 0x0017 -#define EVENT_SIGNAL 0x0018 +#define EVENT_GOT_SIGNAL 0x0018 #define EVENT_CONF_CHANGED 0x0019 #define EVENT_CONN_BW 0x001A #define EVENT_CELL_STATS 0x001B diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index d511ecf84c..76d97e05f2 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -160,7 +160,7 @@ typedef struct cpuworker_job_u { } u; } cpuworker_job_t; -static int +static workqueue_reply_t update_state_threadfn(void *state_, void *work_) { worker_state_t *state = state_; @@ -387,7 +387,7 @@ cpuworker_onion_handshake_replyfn(void *work_) } /** Implementation function for onion handshake requests. */ -static int +static workqueue_reply_t cpuworker_onion_handshake_threadfn(void *state_, void *work_) { worker_state_t *state = state_; diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 58ab009cbf..53b450cef5 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1344,7 +1344,7 @@ dirserv_thinks_router_is_unreliable(time_t now, /** Return true iff <b>router</b> should be assigned the "HSDir" flag. * Right now this means it advertises support for it, it has a high uptime, - * it has a DirPort open, it has the Stable flag and it's currently + * it has a DirPort open, it has the Stable and Fast flag and it's currently * considered Running. * * This function needs to be called after router-\>is_running has @@ -1372,7 +1372,7 @@ dirserv_thinks_router_is_hs_dir(const routerinfo_t *router, uptime = real_uptime(router, now); return (router->wants_to_be_hs_dir && router->dir_port && - node->is_stable && + node->is_stable && node->is_fast && uptime >= get_options()->MinUptimeHidServDirectoryV2 && router_is_active(router, node, now)); } @@ -3745,7 +3745,9 @@ validate_recommended_package_line(const char *line) cp = end_of_word + 1; } - return (n_entries == 0) ? 0 : 1; + /* If we reach this point, we have at least 1 entry. */ + tor_assert(n_entries > 0); + return 1; } /** Release all storage used by the directory server. */ diff --git a/src/or/dns.c b/src/or/dns.c index db77d20783..d71246d61e 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -11,6 +11,8 @@ * be nonblocking.) **/ +#define DNS_PRIVATE + #include "or.h" #include "circuitlist.h" #include "circuituse.h" @@ -81,9 +83,6 @@ struct evdns_request; #endif -/** Longest hostname we're willing to resolve. */ -#define MAX_ADDRESSLEN 256 - /** How long will we wait for an answer from the resolver before we decide * that the resolver is wedged? */ #define RESOLVE_MAX_TIMEOUT 300 @@ -102,104 +101,16 @@ static char *resolv_conf_fname = NULL; * the nameservers? Used to check whether we need to reconfigure. */ static time_t resolv_conf_mtime = 0; -/** Linked list of connections waiting for a DNS answer. */ -typedef struct pending_connection_t { - edge_connection_t *conn; - struct pending_connection_t *next; -} pending_connection_t; - -/** Value of 'magic' field for cached_resolve_t. Used to try to catch bad - * pointers and memory stomping. */ -#define CACHED_RESOLVE_MAGIC 0x1234F00D - -/* Possible states for a cached resolve_t */ -/** We are waiting for the resolver system to tell us an answer here. - * When we get one, or when we time out, the state of this cached_resolve_t - * will become "DONE" and we'll possibly add a CACHED - * entry. This cached_resolve_t will be in the hash table so that we will - * know not to launch more requests for this addr, but rather to add more - * connections to the pending list for the addr. */ -#define CACHE_STATE_PENDING 0 -/** This used to be a pending cached_resolve_t, and we got an answer for it. - * Now we're waiting for this cached_resolve_t to expire. This should - * have no pending connections, and should not appear in the hash table. */ -#define CACHE_STATE_DONE 1 -/** We are caching an answer for this address. This should have no pending - * connections, and should appear in the hash table. */ -#define CACHE_STATE_CACHED 2 - -/** @name status values for a single DNS request. - * - * @{ */ -/** The DNS request is in progress. */ -#define RES_STATUS_INFLIGHT 1 -/** The DNS request finished and gave an answer */ -#define RES_STATUS_DONE_OK 2 -/** The DNS request finished and gave an error */ -#define RES_STATUS_DONE_ERR 3 -/**@}*/ - -/** A DNS request: possibly completed, possibly pending; cached_resolve - * structs are stored at the OR side in a hash table, and as a linked - * list from oldest to newest. - */ -typedef struct cached_resolve_t { - HT_ENTRY(cached_resolve_t) node; - uint32_t magic; /**< Must be CACHED_RESOLVE_MAGIC */ - char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */ - - union { - uint32_t addr_ipv4; /**< IPv4 addr for <b>address</b>, if successful. - * (In host order.) */ - int err_ipv4; /**< One of DNS_ERR_*, if IPv4 lookup failed. */ - } result_ipv4; /**< Outcome of IPv4 lookup */ - union { - struct in6_addr addr_ipv6; /**< IPv6 addr for <b>address</b>, if - * successful */ - int err_ipv6; /**< One of DNS_ERR_*, if IPv6 lookup failed. */ - } result_ipv6; /**< Outcome of IPv6 lookup, if any */ - union { - char *hostname; /** A hostname, if PTR lookup happened successfully*/ - int err_hostname; /** One of DNS_ERR_*, if PTR lookup failed. */ - } result_ptr; - /** @name Status fields - * - * These take one of the RES_STATUS_* values, depending on the state - * of the corresponding lookup. - * - * @{ */ - unsigned int res_status_ipv4 : 2; - unsigned int res_status_ipv6 : 2; - unsigned int res_status_hostname : 2; - /**@}*/ - uint8_t state; /**< Is this cached entry pending/done/informative? */ - - time_t expire; /**< Remove items from cache after this time. */ - uint32_t ttl_ipv4; /**< What TTL did the nameserver tell us? */ - uint32_t ttl_ipv6; /**< What TTL did the nameserver tell us? */ - uint32_t ttl_hostname; /**< What TTL did the nameserver tell us? */ - /** Connections that want to know when we get an answer for this resolve. */ - pending_connection_t *pending_connections; - /** Position of this element in the heap*/ - int minheap_idx; -} cached_resolve_t; - static void purge_expired_resolves(time_t now); static void dns_found_answer(const char *address, uint8_t query_type, int dns_answer, const tor_addr_t *addr, const char *hostname, uint32_t ttl); -static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type, - const cached_resolve_t *resolve); static int launch_resolve(cached_resolve_t *resolve); static void add_wildcarded_test_address(const char *address); static int configure_nameservers(int force); static int answer_is_wildcarded(const char *ip); -static int dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, - or_circuit_t *oncirc, char **resolved_to_hostname, - int *made_connection_pending_out, - cached_resolve_t **resolve_out); static int set_exitconn_info_from_resolve(edge_connection_t *exitconn, const cached_resolve_t *resolve, char **hostname_out); @@ -367,7 +278,7 @@ dns_clip_ttl(uint32_t ttl) /** Helper: Given a TTL from a DNS response, determine how long to hold it in * our cache. */ -static uint32_t +STATIC uint32_t dns_get_expiry_ttl(uint32_t ttl) { if (ttl < MIN_DNS_TTL) @@ -605,9 +516,9 @@ purge_expired_resolves(time_t now) * answer back along circ; otherwise, send the answer back along * <b>conn</b>'s attached circuit. */ -static void -send_resolved_cell(edge_connection_t *conn, uint8_t answer_type, - const cached_resolve_t *resolved) +MOCK_IMPL(STATIC void, +send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type, + const cached_resolve_t *resolved)) { char buf[RELAY_PAYLOAD_SIZE], *cp = buf; size_t buflen = 0; @@ -671,8 +582,9 @@ send_resolved_cell(edge_connection_t *conn, uint8_t answer_type, * answer back along circ; otherwise, send the answer back along * <b>conn</b>'s attached circuit. */ -static void -send_resolved_hostname_cell(edge_connection_t *conn, const char *hostname) +MOCK_IMPL(STATIC void, +send_resolved_hostname_cell,(edge_connection_t *conn, + const char *hostname)) { char buf[RELAY_PAYLOAD_SIZE]; size_t buflen; @@ -800,11 +712,11 @@ dns_resolve(edge_connection_t *exitconn) * * Set *<b>resolve_out</b> to a cached resolve, if we found one. */ -static int -dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, +MOCK_IMPL(STATIC int, +dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve, or_circuit_t *oncirc, char **hostname_out, int *made_connection_pending_out, - cached_resolve_t **resolve_out) + cached_resolve_t **resolve_out)) { cached_resolve_t *resolve; cached_resolve_t search; @@ -1145,8 +1057,8 @@ connection_dns_remove(edge_connection_t *conn) * the resolve for <b>address</b> itself, and remove any cached results for * <b>address</b> from the cache. */ -void -dns_cancel_pending_resolve(const char *address) +MOCK_IMPL(void, +dns_cancel_pending_resolve,(const char *address)) { pending_connection_t *pend; cached_resolve_t search; diff --git a/src/or/dns.h b/src/or/dns.h index b13ab0f890..6af7796dbb 100644 --- a/src/or/dns.h +++ b/src/or/dns.h @@ -20,7 +20,7 @@ int dns_reset(void); void connection_dns_remove(edge_connection_t *conn); void assert_connection_edge_not_dns_pending(edge_connection_t *conn); void assert_all_pending_dns_resolves_ok(void); -void dns_cancel_pending_resolve(const char *question); +MOCK_DECL(void,dns_cancel_pending_resolve,(const char *question)); int dns_resolve(edge_connection_t *exitconn); void dns_launch_correctness_checks(void); int dns_seems_to_be_broken(void); @@ -28,5 +28,21 @@ int dns_seems_to_be_broken_for_ipv6(void); void dns_reset_correctness_checks(void); void dump_dns_mem_usage(int severity); +#ifdef DNS_PRIVATE +#include "dns_structs.h" + +STATIC uint32_t dns_get_expiry_ttl(uint32_t ttl); + +MOCK_DECL(STATIC int,dns_resolve_impl,(edge_connection_t *exitconn, +int is_resolve,or_circuit_t *oncirc, char **hostname_out, +int *made_connection_pending_out, cached_resolve_t **resolve_out)); + +MOCK_DECL(STATIC void,send_resolved_cell,(edge_connection_t *conn, +uint8_t answer_type,const cached_resolve_t *resolved)); + +MOCK_DECL(STATIC void,send_resolved_hostname_cell,(edge_connection_t *conn, +const char *hostname)); +#endif + #endif diff --git a/src/or/dns_structs.h b/src/or/dns_structs.h new file mode 100644 index 0000000000..bb67459d7b --- /dev/null +++ b/src/or/dns_structs.h @@ -0,0 +1,90 @@ +#ifndef TOR_DNS_STRUCTS_H +#define TOR_DNS_STRUCTS_H + +/** Longest hostname we're willing to resolve. */ +#define MAX_ADDRESSLEN 256 + +/** Linked list of connections waiting for a DNS answer. */ +typedef struct pending_connection_t { + edge_connection_t *conn; + struct pending_connection_t *next; +} pending_connection_t; + +/** Value of 'magic' field for cached_resolve_t. Used to try to catch bad + * pointers and memory stomping. */ +#define CACHED_RESOLVE_MAGIC 0x1234F00D + +/* Possible states for a cached resolve_t */ +/** We are waiting for the resolver system to tell us an answer here. + * When we get one, or when we time out, the state of this cached_resolve_t + * will become "DONE" and we'll possibly add a CACHED + * entry. This cached_resolve_t will be in the hash table so that we will + * know not to launch more requests for this addr, but rather to add more + * connections to the pending list for the addr. */ +#define CACHE_STATE_PENDING 0 +/** This used to be a pending cached_resolve_t, and we got an answer for it. + * Now we're waiting for this cached_resolve_t to expire. This should + * have no pending connections, and should not appear in the hash table. */ +#define CACHE_STATE_DONE 1 +/** We are caching an answer for this address. This should have no pending + * connections, and should appear in the hash table. */ +#define CACHE_STATE_CACHED 2 + +/** @name status values for a single DNS request. + * + * @{ */ +/** The DNS request is in progress. */ +#define RES_STATUS_INFLIGHT 1 +/** The DNS request finished and gave an answer */ +#define RES_STATUS_DONE_OK 2 +/** The DNS request finished and gave an error */ +#define RES_STATUS_DONE_ERR 3 +/**@}*/ + +/** A DNS request: possibly completed, possibly pending; cached_resolve + * structs are stored at the OR side in a hash table, and as a linked + * list from oldest to newest. + */ +typedef struct cached_resolve_t { + HT_ENTRY(cached_resolve_t) node; + uint32_t magic; /**< Must be CACHED_RESOLVE_MAGIC */ + char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */ + + union { + uint32_t addr_ipv4; /**< IPv4 addr for <b>address</b>, if successful. + * (In host order.) */ + int err_ipv4; /**< One of DNS_ERR_*, if IPv4 lookup failed. */ + } result_ipv4; /**< Outcome of IPv4 lookup */ + union { + struct in6_addr addr_ipv6; /**< IPv6 addr for <b>address</b>, if + * successful */ + int err_ipv6; /**< One of DNS_ERR_*, if IPv6 lookup failed. */ + } result_ipv6; /**< Outcome of IPv6 lookup, if any */ + union { + char *hostname; /** A hostname, if PTR lookup happened successfully*/ + int err_hostname; /** One of DNS_ERR_*, if PTR lookup failed. */ + } result_ptr; + /** @name Status fields + * + * These take one of the RES_STATUS_* values, depending on the state + * of the corresponding lookup. + * + * @{ */ + unsigned int res_status_ipv4 : 2; + unsigned int res_status_ipv6 : 2; + unsigned int res_status_hostname : 2; + /**@}*/ + uint8_t state; /**< Is this cached entry pending/done/informative? */ + + time_t expire; /**< Remove items from cache after this time. */ + uint32_t ttl_ipv4; /**< What TTL did the nameserver tell us? */ + uint32_t ttl_ipv6; /**< What TTL did the nameserver tell us? */ + uint32_t ttl_hostname; /**< What TTL did the nameserver tell us? */ + /** Connections that want to know when we get an answer for this resolve. */ + pending_connection_t *pending_connections; + /** Position of this element in the heap*/ + int minheap_idx; +} cached_resolve_t; + +#endif + diff --git a/src/or/include.am b/src/or/include.am index 5566d3cd46..7b12b56eb1 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -123,9 +123,9 @@ src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ src/common/libor-event-testing.a src/trunnel/libor-trunnel-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 = $(top_builddir)/src/or/tor-cov +export TESTING_TOR_BINARY = $(top_builddir)/src/or/tor-cov else -TESTING_TOR_BINARY = $(top_builddir)/src/or/tor +export TESTING_TOR_BINARY = $(top_builddir)/src/or/tor endif ORHEADERS = \ @@ -153,6 +153,7 @@ ORHEADERS = \ src/or/dirserv.h \ src/or/dirvote.h \ src/or/dns.h \ + src/or/dns_structs.h \ src/or/dnsserv.h \ src/or/eventdns_tor.h \ src/or/ext_orport.h \ diff --git a/src/or/main.c b/src/or/main.c index 69718107df..693d13cd13 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -3042,12 +3042,19 @@ sandbox_init_filter(void) OPEN_DATADIR_SUFFIX("state", ".tmp"); OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp"); OPEN_DATADIR_SUFFIX("v3-status-votes", ".tmp"); + OPEN_DATADIR("key-pinning-journal"); OPEN("/dev/srandom"); OPEN("/dev/urandom"); OPEN("/dev/random"); OPEN("/etc/hosts"); OPEN("/proc/meminfo"); + if (options->BridgeAuthoritativeDir) + OPEN_DATADIR_SUFFIX("networkstatus-bridges", ".tmp"); + + if (authdir_mode_handles_descs(options, -1)) + OPEN_DATADIR("approved-routers"); + if (options->ServerDNSResolvConfFile) sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->ServerDNSResolvConfFile)); @@ -3088,6 +3095,9 @@ sandbox_init_filter(void) RENAME_SUFFIX("unparseable-desc", ".tmp"); RENAME_SUFFIX("v3-status-votes", ".tmp"); + if (options->BridgeAuthoritativeDir) + RENAME_SUFFIX("networkstatus-bridges", ".tmp"); + #define STAT_DATADIR(name) \ sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname(name)) @@ -3156,6 +3166,13 @@ sandbox_init_filter(void) OPEN_DATADIR2("keys", "secret_onion_key.old"); OPEN_DATADIR2("keys", "secret_onion_key_ntor.old"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key", ".tmp"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key_encrypted", + ".tmp"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_public_key", ".tmp"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key", ".tmp"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_cert", ".tmp"); + OPEN_DATADIR2_SUFFIX("stats", "bridge-stats", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "dirreq-stats", ".tmp"); @@ -3186,6 +3203,12 @@ sandbox_init_filter(void) RENAME_SUFFIX("hashed-fingerprint", ".tmp"); RENAME_SUFFIX("router-stability", ".tmp"); + RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key", ".tmp"); + RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key_encrypted", ".tmp"); + RENAME_SUFFIX2("keys", "ed25519_master_id_public_key", ".tmp"); + RENAME_SUFFIX2("keys", "ed25519_signing_secret_key", ".tmp"); + RENAME_SUFFIX2("keys", "ed25519_signing_cert", ".tmp"); + sandbox_cfg_allow_rename(&cfg, get_datadir_fname2("keys", "secret_onion_key"), get_datadir_fname2("keys", "secret_onion_key.old")); diff --git a/src/or/or.h b/src/or/or.h index 8c40f1ab67..6660a0dcdc 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1155,6 +1155,8 @@ typedef struct entry_port_cfg_t { /** When both no-auth and user/pass are advertised by a SOCKS client, select * no-auth. */ unsigned int socks_prefer_no_auth : 1; + /** When ISO_SOCKSAUTH is in use, Keep-Alive circuits indefinitely. */ + unsigned int socks_iso_keep_alive : 1; /* Client port types only: */ unsigned int ipv4_traffic : 1; @@ -2877,6 +2879,11 @@ typedef struct circuit_t { * circuits entered certain states. This usage probably won't * interfere with this field's primary purpose, but we should * document it more thoroughly to make sure of that. + * + * XXX027 The SocksPort option KeepaliveIsolateSOCKSAuth will artificially + * adjust this value forward each time a suitable stream is attached to an + * already constructed circuit, potentially keeping the circuit alive + * indefinitely. */ time_t timestamp_dirty; diff --git a/src/or/rendcache.c b/src/or/rendcache.c index 9a33046fb6..fe9a1344f6 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -330,7 +330,7 @@ cache_failure_intro_lookup(const uint8_t *identity, const char *service_id, *intro_entry = intro_elem; } return 1; -not_found: + not_found: return 0; } diff --git a/src/or/rendclient.c b/src/or/rendclient.c index c6f29a7707..a39e518e99 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -1021,7 +1021,7 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, /* fall through */ case INTRO_POINT_FAILURE_GENERIC: rend_cache_intro_failure_note(failure_type, - (uint8_t *) failed_intro->identity_digest, + (uint8_t *)failed_intro->identity_digest, rend_query->onion_address); rend_intro_point_free(intro); smartlist_del(ent->parsed->intro_nodes, i); @@ -1038,9 +1038,10 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, intro->unreachable_count, zap_intro_point ? " Removing from descriptor.": ""); if (zap_intro_point) { - rend_cache_intro_failure_note(failure_type, - (uint8_t *) failed_intro->identity_digest, - rend_query->onion_address); + rend_cache_intro_failure_note( + failure_type, + (uint8_t *) failed_intro->identity_digest, + rend_query->onion_address); rend_intro_point_free(intro); smartlist_del(ent->parsed->intro_nodes, i); } diff --git a/src/or/rendservice.c b/src/or/rendservice.c index db6bc4b72e..8ba5327b1d 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -40,7 +40,7 @@ static rend_intro_point_t *find_expiring_intro_point( static extend_info_t *find_rp_for_intro( const rend_intro_cell_t *intro, - uint8_t *need_free_out, char **err_msg_out); + char **err_msg_out); static int intro_point_accepted_intro_count(rend_intro_point_t *intro); static int intro_point_should_expire_now(rend_intro_point_t *intro, @@ -1456,13 +1456,6 @@ rend_service_receive_introduction(origin_circuit_t *circuit, rend_intro_cell_t *parsed_req = NULL; /* Rendezvous point */ extend_info_t *rp = NULL; - /* - * We need to look up and construct the extend_info_t for v0 and v1, - * but all the info is in the cell and it's constructed by the parser - * for v2 and v3, so freeing it would be a double-free. Use this to - * keep track of whether we should free it. - */ - uint8_t need_rp_free = 0; /* XXX not handled yet */ char buf[RELAY_PAYLOAD_SIZE]; char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */ @@ -1602,7 +1595,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit, ++(intro_point->accepted_introduce2_count); /* Find the rendezvous point */ - rp = find_rp_for_intro(parsed_req, &need_rp_free, &err_msg); + rp = find_rp_for_intro(parsed_req, &err_msg); if (!rp) goto log_error; @@ -1761,27 +1754,25 @@ rend_service_receive_introduction(origin_circuit_t *circuit, /* Free the parsed cell */ rend_service_free_intro(parsed_req); - /* Free rp if we must */ - if (need_rp_free) extend_info_free(rp); + /* Free rp */ + extend_info_free(rp); return status; } /** Given a parsed and decrypted INTRODUCE2, find the rendezvous point or - * return NULL and an error string if we can't. - */ - + * return NULL and an error string if we can't. Return a newly allocated + * extend_info_t* for the rendezvous point. */ static extend_info_t * find_rp_for_intro(const rend_intro_cell_t *intro, - uint8_t *need_free_out, char **err_msg_out) + char **err_msg_out) { extend_info_t *rp = NULL; char *err_msg = NULL; const char *rp_nickname = NULL; const node_t *node = NULL; - uint8_t need_free = 0; - if (!intro || !need_free_out) { + if (!intro) { if (err_msg_out) err_msg = tor_strdup("Bad parameters to find_rp_for_intro()"); @@ -1812,13 +1803,11 @@ find_rp_for_intro(const rend_intro_cell_t *intro, } goto err; - } else { - need_free = 1; } } else if (intro->version == 2) { - rp = intro->u.v2.extend_info; + rp = extend_info_dup(intro->u.v2.extend_info); } else if (intro->version == 3) { - rp = intro->u.v3.extend_info; + rp = extend_info_dup(intro->u.v3.extend_info); } else { if (err_msg_out) { tor_asprintf(&err_msg, @@ -1836,8 +1825,6 @@ find_rp_for_intro(const rend_intro_cell_t *intro, else tor_free(err_msg); done: - if (rp && need_free_out) *need_free_out = need_free; - return rp; } diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index ad915479c2..1120578c28 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -295,6 +295,7 @@ ed_key_init_from_file(const char *fname, uint32_t flags, if (rv == 0) { have_secret = 1; loaded_secret_fname = secret_fname; + tor_assert(got_tag); } else { if (errno != ENOENT && norepair) { tor_log(severity, LD_OR, "Unable to read %s: %s", secret_fname, @@ -482,10 +483,12 @@ ed_key_init_from_file(const char *fname, uint32_t flags, tor_log(severity, LD_OR, "Cert was for wrong key"); bad_cert = 1; } else if (signing_key && - tor_cert_checksig(cert, &signing_key->pubkey, now) < 0 && - (signing_key || cert->cert_expired)) { + tor_cert_checksig(cert, &signing_key->pubkey, now) < 0) { tor_log(severity, LD_OR, "Can't check certificate"); bad_cert = 1; + } else if (cert->cert_expired) { + tor_log(severity, LD_OR, "Certificate is expired"); + bad_cert = 1; } else if (signing_key && cert->signing_key_included && ! ed25519_pubkey_eq(&signing_key->pubkey, &cert->signing_key)) { tor_log(severity, LD_OR, "Certificate signed by unexpectd key!"); diff --git a/src/or/routerset.c b/src/or/routerset.c index 99de11ed5e..9fe5dffdeb 100644 --- a/src/or/routerset.c +++ b/src/or/routerset.c @@ -162,6 +162,17 @@ routerset_is_empty(const routerset_t *set) return !set || smartlist_len(set->list) == 0; } +/** Return the number of entries in <b>set</b>. This does NOT return a + * negative value. */ +int +routerset_len(const routerset_t *set) +{ + if (!set) { + return 0; + } + return smartlist_len(set->list); +} + /** Helper. Return true iff <b>set</b> contains a router based on the other * provided fields. Return higher values for more specific subentries: a * single router is more specific than an address range of routers, which is diff --git a/src/or/routerset.h b/src/or/routerset.h index 8d41de8b6b..aca7c6e74e 100644 --- a/src/or/routerset.h +++ b/src/or/routerset.h @@ -38,6 +38,7 @@ void routerset_subtract_nodes(smartlist_t *out, char *routerset_to_string(const routerset_t *routerset); int routerset_equal(const routerset_t *old, const routerset_t *new); void routerset_free(routerset_t *routerset); +int routerset_len(const routerset_t *set); #ifdef ROUTERSET_PRIVATE STATIC char * routerset_get_countryname(const char *c); diff --git a/src/or/tor_main.c b/src/or/tor_main.c index af03b8c06a..65bb020c2c 100644 --- a/src/or/tor_main.c +++ b/src/or/tor_main.c @@ -27,6 +27,10 @@ int tor_main(int argc, char *argv[]); int main(int argc, char *argv[]) { - return tor_main(argc, argv); + int r = tor_main(argc, argv); + if (r < 0 || r > 255) + return 1; + else + return r; } diff --git a/src/test/include.am b/src/test/include.am index 3fe4f50edf..f7c0204832 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -1,12 +1,19 @@ -TESTSCRIPTS = src/test/test_zero_length_keys.sh src/test/test_keygen.sh +TESTSCRIPTS = src/test/test_zero_length_keys.sh if USEPYTHON TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh endif TESTS += src/test/test src/test/test-slow src/test/test-memwipe \ - src/test/test_workqueue $(TESTSCRIPTS) + src/test/test_workqueue src/test/test_keygen.sh $(TESTSCRIPTS) + +# These flavors are run using automake's test-driver and test-network.sh +TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min bridges+hs +# only run if we can ping6 ::1 (localhost) +TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min +# only run if we can find a stable (or simply another) version of tor +TEST_CHUTNEY_FLAVORS_MIXED = mixed ### This is a lovely feature, but it requires automake >= 1.12, and Tor ### doesn't require that yet. Below is a kludge to work around. @@ -90,6 +97,7 @@ src_test_test_SOURCES = \ src/test/test_threads.c \ src/test/test_util.c \ src/test/test_helpers.c \ + src/test/test_dns.c \ src/test/testing_common.c \ src/ext/tinytest.c @@ -185,4 +193,5 @@ EXTRA_DIST += \ src/test/bt_test.py \ src/test/ntor_ref.py \ src/test/slownacl_curve25519.py \ - src/test/zero_length_keys.sh + src/test/zero_length_keys.sh \ + src/test/test_keygen.sh diff --git a/src/test/test-network.sh b/src/test/test-network.sh index cc74c0f823..05080e0c52 100755 --- a/src/test/test-network.sh +++ b/src/test/test-network.sh @@ -3,9 +3,9 @@ ECHO_N="/bin/echo -n" use_coverage_binary=false -until [ -z $1 ] +until [ -z "$1" ] do - case $1 in + case "$1" in --chutney-path) export CHUTNEY_PATH="$2" shift @@ -55,12 +55,20 @@ do done TOR_DIR="${TOR_DIR:-$PWD}" -NETWORK_FLAVOUR=${NETWORK_FLAVOUR:-basic} +NETWORK_FLAVOUR=${NETWORK_FLAVOUR:-"bridges+hs"} CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR myname=$(basename $0) +[ -n "$CHUTNEY_PATH" ] || { + echo "$myname: \$CHUTNEY_PATH not set, trying $TOR_DIR/../chutney" + CHUTNEY_PATH="$TOR_DIR/../chutney" +} + [ -d "$CHUTNEY_PATH" ] && [ -x "$CHUTNEY_PATH/chutney" ] || { echo "$myname: missing 'chutney' in CHUTNEY_PATH ($CHUTNEY_PATH)" + echo "$myname: Get chutney: git clone https://git.torproject.org/\ +chutney.git" + echo "$myname: Set \$CHUTNEY_PATH to a non-standard location: export CHUTNEY_PATH=\`pwd\`/chutney" exit 1 } @@ -78,7 +86,7 @@ export CHUTNEY_TOR_GENCERT="${TOR_DIR}/src/tools/${tor_gencert_name}" # Sleep some, waiting for the network to bootstrap. # TODO: Add chutney command 'bootstrap-status' and use that instead. -BOOTSTRAP_TIME=${BOOTSTRAP_TIME:-25} +BOOTSTRAP_TIME=${BOOTSTRAP_TIME:-35} $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 7ad849f49e..e10e260266 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1158,6 +1158,7 @@ extern struct testcase_t socks_tests[]; extern struct testcase_t status_tests[]; extern struct testcase_t thread_tests[]; extern struct testcase_t util_tests[]; +extern struct testcase_t dns_tests[]; struct testgroup_t testgroups[] = { { "", test_array }, @@ -1204,6 +1205,7 @@ struct testgroup_t testgroups[] = { { "util/", util_tests }, { "util/logging/", logging_tests }, { "util/thread/", thread_tests }, + { "dns/", dns_tests }, END_OF_GROUPS }; diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index e8fce12314..29ee408616 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -206,9 +206,6 @@ test_buffer_pullup(void *arg) stuff = tor_malloc(16384); tmp = tor_malloc(16384); - /* Note: this test doesn't check the nulterminate argument to buf_pullup, - since nothing actually uses it. We should remove it some time. */ - buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */ tt_assert(buf); @@ -218,7 +215,7 @@ test_buffer_pullup(void *arg) /* There are a bunch of cases for pullup. One is the trivial case. Let's mess around with an empty buffer. */ - buf_pullup(buf, 16, 1); + buf_pullup(buf, 16); buf_get_first_chunk_data(buf, &cp, &sz); tt_ptr_op(cp, OP_EQ, NULL); tt_uint_op(sz, OP_EQ, 0); @@ -240,7 +237,7 @@ test_buffer_pullup(void *arg) * can get tested. */ tt_int_op(fetch_from_buf(tmp, 3000, buf), OP_EQ, 3000); tt_mem_op(tmp,OP_EQ, stuff, 3000); - buf_pullup(buf, 2048, 0); + buf_pullup(buf, 2048); assert_buf_ok(buf); buf_get_first_chunk_data(buf, &cp, &sz); tt_ptr_op(cp, OP_NE, NULL); @@ -263,7 +260,7 @@ test_buffer_pullup(void *arg) tt_ptr_op(cp, OP_NE, NULL); tt_int_op(sz, OP_LE, 4096); - buf_pullup(buf, 12500, 0); + buf_pullup(buf, 12500); assert_buf_ok(buf); buf_get_first_chunk_data(buf, &cp, &sz); tt_ptr_op(cp, OP_NE, NULL); @@ -286,7 +283,7 @@ test_buffer_pullup(void *arg) write_to_buf(stuff, 4000, buf); write_to_buf(stuff+4000, 4000, buf); fetch_from_buf(tmp, 100, buf); /* dump 100 bytes from first chunk */ - buf_pullup(buf, 16000, 0); /* Way too much. */ + buf_pullup(buf, 16000); /* Way too much. */ assert_buf_ok(buf); buf_get_first_chunk_data(buf, &cp, &sz); tt_ptr_op(cp, OP_NE, NULL); diff --git a/src/test/test_checkdir.c b/src/test/test_checkdir.c index a44584c0a3..d6ef353c87 100644 --- a/src/test/test_checkdir.c +++ b/src/test/test_checkdir.c @@ -4,7 +4,9 @@ #include "orconfig.h" #include "or.h" -#ifndef _WIN32 +#ifdef _WIN32 +#include <direct.h> +#else #include <dirent.h> #endif diff --git a/src/test/test_dns.c b/src/test/test_dns.c new file mode 100644 index 0000000000..ad81914ccb --- /dev/null +++ b/src/test/test_dns.c @@ -0,0 +1,311 @@ +#include "or.h" +#include "test.h" + +#define DNS_PRIVATE + +#include "dns.h" +#include "connection.h" + +static void +test_dns_clip_ttl(void *arg) +{ + (void)arg; + + uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_TTL / 2; + + tt_int_op(dns_clip_ttl(MIN_DNS_TTL - 1),==,MIN_DNS_TTL); + tt_int_op(dns_clip_ttl(ttl_mid),==,ttl_mid); + tt_int_op(dns_clip_ttl(MAX_DNS_TTL + 1),==,MAX_DNS_TTL); + + done: + return; +} + +static void +test_dns_expiry_ttl(void *arg) +{ + (void)arg; + + uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_ENTRY_AGE / 2; + + tt_int_op(dns_get_expiry_ttl(MIN_DNS_TTL - 1),==,MIN_DNS_TTL); + tt_int_op(dns_get_expiry_ttl(ttl_mid),==,ttl_mid); + tt_int_op(dns_get_expiry_ttl(MAX_DNS_ENTRY_AGE + 1),==,MAX_DNS_ENTRY_AGE); + + done: + return; +} + +static int resolve_retval = 0; +static int resolve_made_conn_pending = 0; +static char *resolved_name = NULL; +static cached_resolve_t *cache_entry = NULL; + +static int n_fake_impl = 0; + +/** This will be our configurable substitute for <b>dns_resolve_impl</b> in + * dns.c. It will return <b>resolve_retval</b>, + * and set <b>resolve_made_conn_pending</b> to + * <b>made_connection_pending_out</b>. It will set <b>hostname_out</b> + * to a duplicate of <b>resolved_name</b> and it will set <b>resolve_out</b> + * to <b>cache_entry</b>. Lastly, it will increment <b>n_fake_impl</b< by + * 1. + */ +static int +dns_resolve_fake_impl(edge_connection_t *exitconn, int is_resolve, + or_circuit_t *oncirc, char **hostname_out, + int *made_connection_pending_out, + cached_resolve_t **resolve_out) +{ + (void)oncirc; + (void)exitconn; + (void)is_resolve; + + if (made_connection_pending_out) + *made_connection_pending_out = resolve_made_conn_pending; + + if (hostname_out && resolved_name) + *hostname_out = tor_strdup(resolved_name); + + if (resolve_out && cache_entry) + *resolve_out = cache_entry; + + n_fake_impl++; + + return resolve_retval; +} + +static edge_connection_t *conn_for_resolved_cell = NULL; + +static int n_send_resolved_cell_replacement = 0; +static uint8_t last_answer_type = 0; +static cached_resolve_t *last_resolved; + +static void +send_resolved_cell_replacement(edge_connection_t *conn, uint8_t answer_type, + const cached_resolve_t *resolved) +{ + conn_for_resolved_cell = conn; + + last_answer_type = answer_type; + last_resolved = (cached_resolve_t *)resolved; + + n_send_resolved_cell_replacement++; +} + +static int n_send_resolved_hostname_cell_replacement = 0; + +static char *last_resolved_hostname = NULL; + +static void +send_resolved_hostname_cell_replacement(edge_connection_t *conn, + const char *hostname) +{ + conn_for_resolved_cell = conn; + + tor_free(last_resolved_hostname); + last_resolved_hostname = tor_strdup(hostname); + + n_send_resolved_hostname_cell_replacement++; +} + +static int n_dns_cancel_pending_resolve_replacement = 0; + +static void +dns_cancel_pending_resolve_replacement(const char *address) +{ + (void) address; + n_dns_cancel_pending_resolve_replacement++; +} + +static int n_connection_free = 0; +static connection_t *last_freed_conn = NULL; + +static void +connection_free_replacement(connection_t *conn) +{ + n_connection_free++; + + last_freed_conn = conn; +} + +static void +test_dns_resolve_outer(void *arg) +{ + (void) arg; + int retval; + int prev_n_send_resolved_hostname_cell_replacement; + int prev_n_send_resolved_cell_replacement; + int prev_n_connection_free; + cached_resolve_t *fake_resolved = tor_malloc(sizeof(cached_resolve_t)); + edge_connection_t *exitconn = tor_malloc(sizeof(edge_connection_t)); + edge_connection_t *nextconn = tor_malloc(sizeof(edge_connection_t)); + + or_circuit_t *on_circuit = tor_malloc(sizeof(or_circuit_t)); + memset(on_circuit,0,sizeof(or_circuit_t)); + on_circuit->base_.magic = OR_CIRCUIT_MAGIC; + + memset(fake_resolved,0,sizeof(cached_resolve_t)); + memset(exitconn,0,sizeof(edge_connection_t)); + memset(nextconn,0,sizeof(edge_connection_t)); + + MOCK(dns_resolve_impl,dns_resolve_fake_impl); + MOCK(send_resolved_cell,send_resolved_cell_replacement); + MOCK(send_resolved_hostname_cell,send_resolved_hostname_cell_replacement); + + /* + * CASE 1: dns_resolve_impl returns 1 and sets a hostname. purpose is + * EXIT_PURPOSE_RESOLVE. + * + * We want dns_resolve() to call send_resolved_hostname_cell() for a + * given exit connection (represented by edge_connection_t object) + * with a hostname it received from _impl. + */ + + prev_n_send_resolved_hostname_cell_replacement = + n_send_resolved_hostname_cell_replacement; + + exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE; + exitconn->on_circuit = &(on_circuit->base_); + + resolve_retval = 1; + resolved_name = tor_strdup("www.torproject.org"); + + retval = dns_resolve(exitconn); + + tt_int_op(retval,==,1); + tt_str_op(resolved_name,==,last_resolved_hostname); + tt_assert(conn_for_resolved_cell == exitconn); + tt_int_op(n_send_resolved_hostname_cell_replacement,==, + prev_n_send_resolved_hostname_cell_replacement + 1); + tt_assert(exitconn->on_circuit == NULL); + + tor_free(last_resolved_hostname); + // implies last_resolved_hostname = NULL; + + /* CASE 2: dns_resolve_impl returns 1, but does not set hostname. + * Instead, it yields cached_resolve_t object. + * + * We want dns_resolve to call send_resolved_cell on exitconn with + * RESOLVED_TYPE_AUTO and the cached_resolve_t object from _impl. + */ + + tor_free(resolved_name); + resolved_name = NULL; + + exitconn->on_circuit = &(on_circuit->base_); + + cache_entry = fake_resolved; + + prev_n_send_resolved_cell_replacement = + n_send_resolved_cell_replacement; + + retval = dns_resolve(exitconn); + + tt_int_op(retval,==,1); + tt_assert(conn_for_resolved_cell == exitconn); + tt_int_op(n_send_resolved_cell_replacement,==, + prev_n_send_resolved_cell_replacement + 1); + tt_assert(last_resolved == fake_resolved); + tt_int_op(last_answer_type,==,0xff); + tt_assert(exitconn->on_circuit == NULL); + + /* CASE 3: The purpose of exit connection is not EXIT_PURPOSE_RESOLVE + * and _impl returns 1. + * + * We want dns_resolve to prepend exitconn to n_streams linked list. + * We don't want it to send any cells about hostname being resolved. + */ + + exitconn->base_.purpose = EXIT_PURPOSE_CONNECT; + exitconn->on_circuit = &(on_circuit->base_); + + on_circuit->n_streams = nextconn; + + prev_n_send_resolved_cell_replacement = + n_send_resolved_cell_replacement; + + prev_n_send_resolved_hostname_cell_replacement = + n_send_resolved_hostname_cell_replacement; + + retval = dns_resolve(exitconn); + + tt_int_op(retval,==,1); + tt_assert(on_circuit->n_streams == exitconn); + tt_assert(exitconn->next_stream == nextconn); + tt_int_op(prev_n_send_resolved_cell_replacement,==, + n_send_resolved_cell_replacement); + tt_int_op(prev_n_send_resolved_hostname_cell_replacement,==, + n_send_resolved_hostname_cell_replacement); + + /* CASE 4: _impl returns 0. + * + * We want dns_resolve() to set exitconn state to + * EXIT_CONN_STATE_RESOLVING and prepend exitconn to resolving_streams + * linked list. + */ + + exitconn->on_circuit = &(on_circuit->base_); + + resolve_retval = 0; + + exitconn->next_stream = NULL; + on_circuit->resolving_streams = nextconn; + + retval = dns_resolve(exitconn); + + tt_int_op(retval,==,0); + tt_int_op(exitconn->base_.state,==,EXIT_CONN_STATE_RESOLVING); + tt_assert(on_circuit->resolving_streams == exitconn); + tt_assert(exitconn->next_stream == nextconn); + + /* CASE 5: _impl returns -1 when purpose of exitconn is + * EXIT_PURPOSE_RESOLVE. We want dns_resolve to call send_resolved_cell + * on exitconn with type being RESOLVED_TYPE_ERROR. + */ + + MOCK(dns_cancel_pending_resolve,dns_cancel_pending_resolve_replacement); + MOCK(connection_free,connection_free_replacement); + + exitconn->on_circuit = &(on_circuit->base_); + exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE; + + resolve_retval = -1; + + prev_n_send_resolved_cell_replacement = + n_send_resolved_cell_replacement; + + prev_n_connection_free = n_connection_free; + + retval = dns_resolve(exitconn); + + tt_int_op(retval,==,-1); + tt_int_op(n_send_resolved_cell_replacement,==, + prev_n_send_resolved_cell_replacement + 1); + tt_int_op(last_answer_type,==,RESOLVED_TYPE_ERROR); + tt_int_op(n_dns_cancel_pending_resolve_replacement,==,1); + tt_int_op(n_connection_free,==,prev_n_connection_free + 1); + tt_assert(last_freed_conn == TO_CONN(exitconn)); + + done: + UNMOCK(dns_resolve_impl); + UNMOCK(send_resolved_cell); + UNMOCK(send_resolved_hostname_cell); + UNMOCK(dns_cancel_pending_resolve); + UNMOCK(connection_free); + tor_free(on_circuit); + tor_free(exitconn); + tor_free(nextconn); + tor_free(resolved_name); + tor_free(fake_resolved); + tor_free(last_resolved_hostname); + return; +} + +struct testcase_t dns_tests[] = { + { "clip_ttl", test_dns_clip_ttl, 0, NULL, NULL }, + { "expiry_ttl", test_dns_expiry_ttl, 0, NULL, NULL }, + { "resolve_outer", test_dns_resolve_outer, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index e8e3c3d219..87012cd283 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -46,11 +46,12 @@ fi fi +dump() { xxd -p "$1" | tr -d '\n '; } die() { echo "$1" >&2 ; exit 5; } check_dir() { [ -d "$1" ] || die "$1 did not exist"; } check_file() { [ -e "$1" ] || die "$1 did not exist"; } check_no_file() { [ -e "$1" ] && die "$1 was not supposed to exist" || true; } -check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match"; } +check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match: `dump $1` vs `dump $2`"; } check_keys_eq() { check_files_eq "${SRC}/keys/${1}" "${ME}/keys/${1}"; } DATA_DIR=`mktemp -d -t tor_keygen_tests.XXXXXX` @@ -64,9 +65,13 @@ if [ ! -d "$DATA_DIR" ]; then fi trap "rm -rf '$DATA_DIR'" 0 +# Use an absolute path for this or Tor will complain +DATA_DIR=`cd "${DATA_DIR}" && pwd` + touch "${DATA_DIR}/empty_torrc" QUIETLY="--hush" +SILENTLY="--quiet" TOR="${TOR_BINARY} ${QUIETLY} --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f ${DATA_DIR}/empty_torrc" ##### SETUP @@ -76,9 +81,9 @@ TOR="${TOR_BINARY} ${QUIETLY} --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort # copying them into different keys directories in order to simulate # different kinds of configuration problems/issues. -# Step 1: Start Tor with --list-fingerprint. Make sure everything is there. +# Step 1: Start Tor with --list-fingerprint --quiet. Make sure everything is there. mkdir "${DATA_DIR}/orig" -${TOR} --DataDirectory "${DATA_DIR}/orig" --list-fingerprint > /dev/null +${TOR} --DataDirectory "${DATA_DIR}/orig" --list-fingerprint ${SILENTLY} > /dev/null check_dir "${DATA_DIR}/orig/keys" check_file "${DATA_DIR}/orig/keys/ed25519_master_id_public_key" @@ -109,7 +114,7 @@ check_file "${DATA_DIR}/encrypted/keys/ed25519_signing_cert" check_file "${DATA_DIR}/encrypted/keys/ed25519_signing_secret_key" -echo "=== Starting tests." +echo "=== Starting keygen tests." # # The "case X" numbers below come from s7r's email on @@ -124,7 +129,7 @@ ME="${DATA_DIR}/case2a" SRC="${DATA_DIR}/orig" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout" && die "Somehow succeeded when missing secret key, certs" || true +${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout" && die "Somehow succeeded when missing secret key, certs: `cat ${ME}/stdout`" || true check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" grep "We needed to load a secret key.*but couldn't find it" "${ME}/stdout" >/dev/null || die "Tor didn't declare that it was missing a secret key" @@ -162,7 +167,7 @@ SRC="${DATA_DIR}/orig" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_"* "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint >/dev/null || die "Tor failed when starting with only master key" +${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Tor failed when starting with only master key" check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" check_files_eq "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/ed25519_master_id_secret_key" check_file "${ME}/keys/ed25519_signing_cert" @@ -220,11 +225,11 @@ SRC="${DATA_DIR}/orig" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/fp1" || die "Tor wouldn't start with only unencrypted secret key" +${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} > "${ME}/fp1" || die "Tor wouldn't start with only unencrypted secret key" check_file "${ME}/keys/ed25519_master_id_public_key" check_file "${ME}/keys/ed25519_signing_cert" check_file "${ME}/keys/ed25519_signing_secret_key" -${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/fp2" || die "Tor wouldn't start again after starting once with only unencrypted secret key." +${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} > "${ME}/fp2" || die "Tor wouldn't start again after starting once with only unencrypted secret key." check_files_eq "${ME}/fp1" "${ME}/fp2" @@ -284,7 +289,7 @@ cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint >/dev/null || die "Failed when starting with missing public key" +${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Failed when starting with missing public key" check_keys_eq ed25519_master_id_secret_key check_keys_eq ed25519_master_id_public_key check_keys_eq ed25519_signing_secret_key @@ -306,7 +311,7 @@ cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint >/dev/null || die "Failed when starting with offline secret key" +${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Failed when starting with offline secret key" check_no_file "${ME}/keys/ed25519_master_id_secret_key" check_keys_eq ed25519_master_id_public_key check_keys_eq ed25519_signing_secret_key @@ -327,7 +332,7 @@ mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint >/dev/null || die "Failed when starting with only signing material" +${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Failed when starting with only signing material" check_no_file "${ME}/keys/ed25519_master_id_secret_key" check_file "${ME}/keys/ed25519_master_id_public_key" check_keys_eq ed25519_signing_secret_key diff --git a/src/test/test_util.c b/src/test/test_util.c index 8b4513d34c..0a5783e9f5 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -3654,7 +3654,6 @@ test_util_di_map(void *arg) dimap_free(dimap, tor_free_); } - /** * Test counting high bits */ diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c index 7f20642041..0d79733cf0 100644 --- a/src/test/test_workqueue.c +++ b/src/test/test_workqueue.c @@ -70,7 +70,7 @@ mark_handled(int serial) #endif } -static int +static workqueue_reply_t workqueue_do_rsa(void *state, void *work) { rsa_work_t *rw = work; @@ -98,7 +98,7 @@ workqueue_do_rsa(void *state, void *work) return WQ_RPL_REPLY; } -static int +static workqueue_reply_t workqueue_do_shutdown(void *state, void *work) { (void)state; @@ -108,7 +108,7 @@ workqueue_do_shutdown(void *state, void *work) return WQ_RPL_SHUTDOWN; } -static int +static workqueue_reply_t workqueue_do_ecdh(void *state, void *work) { ecdh_work_t *ew = work; @@ -124,6 +124,14 @@ workqueue_do_ecdh(void *state, void *work) return WQ_RPL_REPLY; } +static workqueue_reply_t +workqueue_shutdown_error(void *state, void *work) +{ + (void)state; + (void)work; + return WQ_RPL_REPLY; +} + static void * new_state(void *arg) { @@ -156,6 +164,7 @@ static int n_sent = 0; static int rsa_sent = 0; static int ecdh_sent = 0; static int n_received = 0; +static int no_shutdown = 0; #ifdef TRACK_RESPONSES bitarray_t *received; @@ -174,6 +183,14 @@ handle_reply(void *arg) ++n_received; } +/* This should never get called. */ +static void +handle_reply_shutdown(void *arg) +{ + (void)arg; + no_shutdown = 1; +} + static workqueue_entry_t * add_work(threadpool_t *tp) { @@ -288,6 +305,9 @@ replysock_readable_cb(tor_socket_t sock, short what, void *arg) shutting_down = 1; threadpool_queue_update(tp, NULL, workqueue_do_shutdown, NULL, NULL); + // Anything we add after starting the shutdown must not be executed. + threadpool_queue_work(tp, workqueue_shutdown_error, + handle_reply_shutdown, NULL); { struct timeval limit = { 2, 0 }; tor_event_base_loopexit(tor_libevent_get_base(), &limit); @@ -416,6 +436,9 @@ main(int argc, char **argv) printf("%d+%d vs %d\n", n_received, n_successful_cancel, n_sent); puts("FAIL"); return 1; + } else if (no_shutdown) { + puts("Accepted work after shutdown\n"); + puts("FAIL"); } else { puts("OK"); return 0; diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c index 08bce15e69..29f85c4d17 100644 --- a/src/tools/tor-resolve.c +++ b/src/tools/tor-resolve.c @@ -327,7 +327,7 @@ do_resolve(const char *hostname, uint32_t sockshost, uint16_t socksport, static void usage(void) { - puts("Syntax: tor-resolve [-4] [-5] [-v] [-x] [-F] [-p port] " + puts("Syntax: tor-resolve [-4] [-5] [-v] [-x] [-p port] " "hostname [sockshost[:socksport]]"); exit(1); } |