aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Dingledine <arma@torproject.org>2013-09-04 23:46:46 -0400
committerRoger Dingledine <arma@torproject.org>2013-09-04 23:46:46 -0400
commit6cf02b9ad6c937c84c9a43ef6181863275b90df4 (patch)
treee6b776c7b6b48a2b3e3162b6164e89b2c83ee273
parent20d4356c3d25e8b2ac23640e249d787f884a25ac (diff)
parentc6f1668db3010de6aed22bd87850aa846911d43b (diff)
downloadtor-6cf02b9ad6c937c84c9a43ef6181863275b90df4.tar.gz
tor-6cf02b9ad6c937c84c9a43ef6181863275b90df4.zip
Merge branch 'maint-0.2.4' into release-0.2.4
-rw-r--r--changes/bug94007
-rw-r--r--changes/bug9671_0235
-rw-r--r--changes/feature95747
-rw-r--r--src/common/compat.c14
-rw-r--r--src/or/circuituse.c4
-rw-r--r--src/or/onion.c231
-rw-r--r--src/or/onion.h4
-rw-r--r--src/test/test.c46
8 files changed, 264 insertions, 54 deletions
diff --git a/changes/bug9400 b/changes/bug9400
new file mode 100644
index 0000000000..974224068a
--- /dev/null
+++ b/changes/bug9400
@@ -0,0 +1,7 @@
+ o Minor bugfixes:
+
+ - Avoid double-closing the listener socket in our socketpair replacement
+ (used on Windows) in the case where the addresses on our opened
+ sockets don't match what we expected. Fixes bug 9400; bugfix on
+ every released Tor version. Found by Coverity.
+
diff --git a/changes/bug9671_023 b/changes/bug9671_023
new file mode 100644
index 0000000000..035ca5cdea
--- /dev/null
+++ b/changes/bug9671_023
@@ -0,0 +1,5 @@
+ o Major bugfixes:
+ - If the circuit build timeout logic is disabled (via the consensus,
+ or because we are an authority), then don't build testing circuits.
+ Fixes bug 9657; bugfix on 0.2.2.14-alpha.
+
diff --git a/changes/feature9574 b/changes/feature9574
new file mode 100644
index 0000000000..723606e396
--- /dev/null
+++ b/changes/feature9574
@@ -0,0 +1,7 @@
+ o Major features:
+ - Relays now process the new "NTor" circuit-level handshake requests
+ with higher priority than the old "TAP" circuit-level handshake
+ requests. We still process some TAP requests to not totally starve
+ 0.2.3 clients when NTor becomes popular. A new consensus parameter
+ "NumNTorsPerTAP" lets us tune the balance later if we need to.
+ Implements ticket 9574.
diff --git a/src/common/compat.c b/src/common/compat.c
index 69eb0643d0..d88c5f92de 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1225,9 +1225,9 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
* for now, and really, when localhost is down sometimes, we
* have other problems too.
*/
- tor_socket_t listener = -1;
- tor_socket_t connector = -1;
- tor_socket_t acceptor = -1;
+ tor_socket_t listener = TOR_INVALID_SOCKET;
+ tor_socket_t connector = TOR_INVALID_SOCKET;
+ tor_socket_t acceptor = TOR_INVALID_SOCKET;
struct sockaddr_in listen_addr;
struct sockaddr_in connect_addr;
int size;
@@ -1281,7 +1281,6 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
goto tidy_up_and_fail;
if (size != sizeof(listen_addr))
goto abort_tidy_up_and_fail;
- tor_close_socket(listener);
/* Now check we are talking to ourself by matching port and host on the
two sockets. */
if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
@@ -1292,6 +1291,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
|| listen_addr.sin_port != connect_addr.sin_port) {
goto abort_tidy_up_and_fail;
}
+ tor_close_socket(listener);
fd[0] = connector;
fd[1] = acceptor;
@@ -1306,11 +1306,11 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
tidy_up_and_fail:
if (saved_errno < 0)
saved_errno = errno;
- if (listener != -1)
+ if (SOCKET_OK(listener))
tor_close_socket(listener);
- if (connector != -1)
+ if (SOCKET_OK(connector))
tor_close_socket(connector);
- if (acceptor != -1)
+ if (SOCKET_OK(acceptor))
tor_close_socket(acceptor);
return -saved_errno;
#endif
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 8fb70f5853..25997ebdbe 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -948,7 +948,7 @@ circuit_predict_and_launch_new(void)
* want, don't do another -- we want to leave a few slots open so
* we can still build circuits preemptively as needed. */
if (num < MAX_UNUSED_OPEN_CIRCUITS-2 &&
- get_options()->LearnCircuitBuildTimeout &&
+ ! circuit_build_times_disabled() &&
circuit_build_times_needs_circuits_now(&circ_times)) {
flags = CIRCLAUNCH_NEED_CAPACITY;
log_info(LD_CIRC,
@@ -1084,7 +1084,7 @@ circuit_expire_old_circuits_clientside(void)
tor_gettimeofday(&now);
cutoff = now;
- if (get_options()->LearnCircuitBuildTimeout &&
+ if (! circuit_build_times_disabled() &&
circuit_build_times_needs_circuits(&circ_times)) {
/* Circuits should be shorter lived if we need more of them
* for learning a good build timeout */
diff --git a/src/or/onion.c b/src/or/onion.c
index d4a65022fc..41fe7b6eea 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -14,6 +14,7 @@
#include "circuitlist.h"
#include "config.h"
#include "cpuworker.h"
+#include "networkstatus.h"
#include "onion.h"
#include "onion_fast.h"
#include "onion_ntor.h"
@@ -28,6 +29,7 @@
typedef struct onion_queue_t {
TOR_TAILQ_ENTRY(onion_queue_t) next;
or_circuit_t *circ;
+ uint16_t handshake_type;
create_cell_t *onionskin;
time_t when_added;
} onion_queue_t;
@@ -35,13 +37,19 @@ typedef struct onion_queue_t {
/** 5 seconds on the onion queue til we just send back a destroy */
#define ONIONQUEUE_WAIT_CUTOFF 5
-/** Queue of circuits waiting for CPU workers, or NULL if the list is empty.*/
-TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t) ol_list =
- TOR_TAILQ_HEAD_INITIALIZER(ol_list);
+/** Array of queues of circuits waiting for CPU workers. An element is NULL
+ * if that queue is empty.*/
+TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t)
+ ol_list[MAX_ONION_HANDSHAKE_TYPE+1] = {
+ TOR_TAILQ_HEAD_INITIALIZER(ol_list[0]), /* tap */
+ TOR_TAILQ_HEAD_INITIALIZER(ol_list[1]), /* fast */
+ TOR_TAILQ_HEAD_INITIALIZER(ol_list[2]), /* ntor */
+};
-/** Number of entries of each type currently in ol_list. */
+/** Number of entries of each type currently in each element of ol_list[]. */
static int ol_entries[MAX_ONION_HANDSHAKE_TYPE+1];
+static int num_ntors_per_tap(void);
static void onion_queue_entry_remove(onion_queue_t *victim);
/* XXXX024 Check lengths vs MAX_ONIONSKIN_{CHALLENGE,REPLY}_LEN.
@@ -59,23 +67,51 @@ have_room_for_onionskin(uint16_t type)
const or_options_t *options = get_options();
int num_cpus;
uint64_t tap_usec, ntor_usec;
+ uint64_t ntor_during_tap_usec, tap_during_ntor_usec;
+
/* If we've got fewer than 50 entries, we always have room for one more. */
- if (ol_entries[ONION_HANDSHAKE_TYPE_TAP] +
- ol_entries[ONION_HANDSHAKE_TYPE_NTOR] < 50)
+ if (ol_entries[type] < 50)
return 1;
num_cpus = get_num_cpus(options);
/* Compute how many microseconds we'd expect to need to clear all
- * onionskins in the current queue. */
+ * onionskins in various combinations of the queues. */
+
+ /* How long would it take to process all the TAP cells in the queue? */
tap_usec = estimated_usec_for_onionskins(
ol_entries[ONION_HANDSHAKE_TYPE_TAP],
ONION_HANDSHAKE_TYPE_TAP) / num_cpus;
+
+ /* How long would it take to process all the NTor cells in the queue? */
ntor_usec = estimated_usec_for_onionskins(
ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
ONION_HANDSHAKE_TYPE_NTOR) / num_cpus;
+
+ /* How long would it take to process the tap cells that we expect to
+ * process while draining the ntor queue? */
+ tap_during_ntor_usec = estimated_usec_for_onionskins(
+ MIN(ol_entries[ONION_HANDSHAKE_TYPE_TAP],
+ ol_entries[ONION_HANDSHAKE_TYPE_NTOR] / num_ntors_per_tap()),
+ ONION_HANDSHAKE_TYPE_TAP) / num_cpus;
+
+ /* How long would it take to process the ntor cells that we expect to
+ * process while draining the tap queue? */
+ ntor_during_tap_usec = estimated_usec_for_onionskins(
+ MIN(ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
+ ol_entries[ONION_HANDSHAKE_TYPE_TAP] * num_ntors_per_tap()),
+ ONION_HANDSHAKE_TYPE_NTOR) / num_cpus;
+
/* See whether that exceeds MaxOnionQueueDelay. If so, we can't queue
* this. */
- if ((tap_usec + ntor_usec) / 1000 > (uint64_t)options->MaxOnionQueueDelay)
+ if (type == ONION_HANDSHAKE_TYPE_NTOR &&
+ (ntor_usec + tap_during_ntor_usec) / 1000 >
+ (uint64_t)options->MaxOnionQueueDelay)
+ return 0;
+
+ if (type == ONION_HANDSHAKE_TYPE_TAP &&
+ (tap_usec + ntor_during_tap_usec) / 1000 >
+ (uint64_t)options->MaxOnionQueueDelay)
return 0;
+
#ifdef CURVE25519_ENABLED
/* If we support the ntor handshake, then don't let TAP handshakes use
* more than 2/3 of the space on the queue. */
@@ -98,8 +134,15 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
onion_queue_t *tmp;
time_t now = time(NULL);
+ if (onionskin->handshake_type > MAX_ONION_HANDSHAKE_TYPE) {
+ log_warn(LD_BUG, "Handshake %d out of range! Dropping.",
+ onionskin->handshake_type);
+ return -1;
+ }
+
tmp = tor_malloc_zero(sizeof(onion_queue_t));
tmp->circ = circ;
+ tmp->handshake_type = onionskin->handshake_type;
tmp->onionskin = onionskin;
tmp->when_added = now;
@@ -108,7 +151,8 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
static ratelim_t last_warned =
RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL);
char *m;
- if ((m = rate_limit_log(&last_warned, approx_time()))) {
+ if (onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR &&
+ (m = rate_limit_log(&last_warned, approx_time()))) {
log_warn(LD_GENERAL,
"Your computer is too slow to handle this many circuit "
"creation requests! Please consider using the "
@@ -121,12 +165,17 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
}
++ol_entries[onionskin->handshake_type];
+ log_info(LD_OR, "New create (%s). Queues now ntor=%d and tap=%d.",
+ onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap",
+ ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
+ ol_entries[ONION_HANDSHAKE_TYPE_TAP]);
+
circ->onionqueue_entry = tmp;
- TOR_TAILQ_INSERT_TAIL(&ol_list, tmp, next);
+ TOR_TAILQ_INSERT_TAIL(&ol_list[onionskin->handshake_type], tmp, next);
/* cull elderly requests. */
while (1) {
- onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list);
+ onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[onionskin->handshake_type]);
if (now - head->when_added < (time_t)ONIONQUEUE_WAIT_CUTOFF)
break;
@@ -140,24 +189,87 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
return 0;
}
-/** Remove the first item from ol_list and return it, or return
- * NULL if the list is empty.
+/** Return a fairness parameter, to prefer processing NTOR style
+ * handshakes but still slowly drain the TAP queue so we don't starve
+ * it entirely. */
+static int
+num_ntors_per_tap(void)
+{
+#define DEFAULT_NUM_NTORS_PER_TAP 10
+#define MIN_NUM_NTORS_PER_TAP 1
+#define MAX_NUM_NTORS_PER_TAP 100000
+
+ return networkstatus_get_param(NULL, "NumNTorsPerTAP",
+ DEFAULT_NUM_NTORS_PER_TAP,
+ MIN_NUM_NTORS_PER_TAP,
+ MAX_NUM_NTORS_PER_TAP);
+}
+
+/** Choose which onion queue we'll pull from next. If one is empty choose
+ * the other; if they both have elements, load balance across them but
+ * favoring NTOR. */
+static uint16_t
+decide_next_handshake_type(void)
+{
+ /* The number of times we've chosen ntor lately when both were available. */
+ static int recently_chosen_ntors = 0;
+
+ if (!ol_entries[ONION_HANDSHAKE_TYPE_NTOR])
+ return ONION_HANDSHAKE_TYPE_TAP; /* no ntors? try tap */
+
+ if (!ol_entries[ONION_HANDSHAKE_TYPE_TAP]) {
+
+ /* Nick wants us to prioritize new tap requests when there aren't
+ * any in the queue and we've processed k ntor cells since the last
+ * tap cell. This strategy is maybe a good idea, since it starves tap
+ * less in the case where tap is rare, or maybe a poor idea, since it
+ * makes the new tap cell unfairly jump in front of ntor cells that
+ * got here first. In any case this edge case will only become relevant
+ * once tap is rare. We should reevaluate whether we like this decision
+ * once tap gets more rare. */
+ if (ol_entries[ONION_HANDSHAKE_TYPE_NTOR])
+ ++recently_chosen_ntors;
+
+ return ONION_HANDSHAKE_TYPE_NTOR; /* no taps? try ntor */
+ }
+
+ /* They both have something queued. Pick ntor if we haven't done that
+ * too much lately. */
+ if (++recently_chosen_ntors <= num_ntors_per_tap()) {
+ return ONION_HANDSHAKE_TYPE_NTOR;
+ }
+
+ /* Else, it's time to let tap have its turn. */
+ recently_chosen_ntors = 0;
+ return ONION_HANDSHAKE_TYPE_TAP;
+}
+
+/** Remove the highest priority item from ol_list[] and return it, or
+ * return NULL if the lists are empty.
*/
or_circuit_t *
onion_next_task(create_cell_t **onionskin_out)
{
or_circuit_t *circ;
- onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list);
+ uint16_t handshake_to_choose = decide_next_handshake_type();
+ onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[handshake_to_choose]);
if (!head)
return NULL; /* no onions pending, we're done */
tor_assert(head->circ);
- tor_assert(head->circ->p_chan); /* make sure it's still valid */
+ tor_assert(head->handshake_type <= MAX_ONION_HANDSHAKE_TYPE);
+// tor_assert(head->circ->p_chan); /* make sure it's still valid */
+/* XXX I only commented out the above line to make the unit tests
+ * more manageable. That's probably not good long-term. -RD */
circ = head->circ;
- if (head->onionskin &&
- head->onionskin->handshake_type <= MAX_ONION_HANDSHAKE_TYPE)
- --ol_entries[head->onionskin->handshake_type];
+ if (head->onionskin)
+ --ol_entries[head->handshake_type];
+ log_info(LD_OR, "Processing create (%s). Queues now ntor=%d and tap=%d.",
+ head->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap",
+ ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
+ ol_entries[ONION_HANDSHAKE_TYPE_TAP]);
+
*onionskin_out = head->onionskin;
head->onionskin = NULL; /* prevent free. */
circ->onionqueue_entry = NULL;
@@ -165,6 +277,14 @@ onion_next_task(create_cell_t **onionskin_out)
return circ;
}
+/** Return the number of <b>handshake_type</b>-style create requests pending.
+ */
+int
+onion_num_pending(uint16_t handshake_type)
+{
+ return ol_entries[handshake_type];
+}
+
/** Go through ol_list, find the onion_queue_t element which points to
* circ, remove and free that element. Leave circ itself alone.
*/
@@ -186,14 +306,20 @@ onion_pending_remove(or_circuit_t *circ)
static void
onion_queue_entry_remove(onion_queue_t *victim)
{
- TOR_TAILQ_REMOVE(&ol_list, victim, next);
+ if (victim->handshake_type > MAX_ONION_HANDSHAKE_TYPE) {
+ log_warn(LD_BUG, "Handshake %d out of range! Dropping.",
+ victim->handshake_type);
+ /* XXX leaks */
+ return;
+ }
+
+ TOR_TAILQ_REMOVE(&ol_list[victim->handshake_type], victim, next);
if (victim->circ)
victim->circ->onionqueue_entry = NULL;
- if (victim->onionskin &&
- victim->onionskin->handshake_type <= MAX_ONION_HANDSHAKE_TYPE)
- --ol_entries[victim->onionskin->handshake_type];
+ if (victim->onionskin)
+ --ol_entries[victim->handshake_type];
tor_free(victim->onionskin);
tor_free(victim);
@@ -204,8 +330,11 @@ void
clear_pending_onions(void)
{
onion_queue_t *victim;
- while ((victim = TOR_TAILQ_FIRST(&ol_list))) {
- onion_queue_entry_remove(victim);
+ int i;
+ for (i=0; i<=MAX_ONION_HANDSHAKE_TYPE; i++) {
+ while ((victim = TOR_TAILQ_FIRST(&ol_list[i]))) {
+ onion_queue_entry_remove(victim);
+ }
}
memset(ol_entries, 0, sizeof(ol_entries));
}
@@ -514,6 +643,22 @@ check_create_cell(const create_cell_t *cell, int unknown_ok)
return 0;
}
+/** Write the various parameters into the create cell. Separate from
+ * create_cell_parse() to make unit testing easier.
+ */
+void
+create_cell_init(create_cell_t *cell_out, uint8_t cell_type,
+ uint16_t handshake_type, uint16_t handshake_len,
+ const uint8_t *onionskin)
+{
+ memset(cell_out, 0, sizeof(*cell_out));
+
+ cell_out->cell_type = cell_type;
+ cell_out->handshake_type = handshake_type;
+ cell_out->handshake_len = handshake_len;
+ memcpy(cell_out->onionskin, onionskin, handshake_len);
+}
+
/** Helper: parse the CREATE2 payload at <b>p</b>, which could be up to
* <b>p_len</b> bytes long, and use it to fill the fields of
* <b>cell_out</b>. Return 0 on success and -1 on failure.
@@ -524,17 +669,21 @@ check_create_cell(const create_cell_t *cell, int unknown_ok)
static int
parse_create2_payload(create_cell_t *cell_out, const uint8_t *p, size_t p_len)
{
+ uint16_t handshake_type, handshake_len;
+
if (p_len < 4)
return -1;
- cell_out->cell_type = CELL_CREATE2;
- cell_out->handshake_type = ntohs(get_uint16(p));
- cell_out->handshake_len = ntohs(get_uint16(p+2));
- if (cell_out->handshake_len > CELL_PAYLOAD_SIZE - 4 ||
- cell_out->handshake_len > p_len - 4)
+
+ handshake_type = ntohs(get_uint16(p));
+ handshake_len = ntohs(get_uint16(p+2));
+
+ if (handshake_len > CELL_PAYLOAD_SIZE - 4 || handshake_len > p_len - 4)
return -1;
- if (cell_out->handshake_type == ONION_HANDSHAKE_TYPE_FAST)
+ if (handshake_type == ONION_HANDSHAKE_TYPE_FAST)
return -1;
- memcpy(cell_out->onionskin, p+4, cell_out->handshake_len);
+
+ create_cell_init(cell_out, CELL_CREATE2, handshake_type, handshake_len,
+ p+4);
return 0;
}
@@ -552,27 +701,19 @@ parse_create2_payload(create_cell_t *cell_out, const uint8_t *p, size_t p_len)
int
create_cell_parse(create_cell_t *cell_out, const cell_t *cell_in)
{
- memset(cell_out, 0, sizeof(*cell_out));
-
switch (cell_in->command) {
case CELL_CREATE:
- cell_out->cell_type = CELL_CREATE;
if (tor_memeq(cell_in->payload, NTOR_CREATE_MAGIC, 16)) {
- cell_out->handshake_type = ONION_HANDSHAKE_TYPE_NTOR;
- cell_out->handshake_len = NTOR_ONIONSKIN_LEN;
- memcpy(cell_out->onionskin, cell_in->payload+16, NTOR_ONIONSKIN_LEN);
+ create_cell_init(cell_out, CELL_CREATE, ONION_HANDSHAKE_TYPE_NTOR,
+ NTOR_ONIONSKIN_LEN, cell_in->payload+16);
} else {
- cell_out->handshake_type = ONION_HANDSHAKE_TYPE_TAP;
- cell_out->handshake_len = TAP_ONIONSKIN_CHALLENGE_LEN;
- memcpy(cell_out->onionskin, cell_in->payload,
- TAP_ONIONSKIN_CHALLENGE_LEN);
+ create_cell_init(cell_out, CELL_CREATE, ONION_HANDSHAKE_TYPE_TAP,
+ TAP_ONIONSKIN_CHALLENGE_LEN, cell_in->payload);
}
break;
case CELL_CREATE_FAST:
- cell_out->cell_type = CELL_CREATE_FAST;
- cell_out->handshake_type = ONION_HANDSHAKE_TYPE_FAST;
- cell_out->handshake_len = CREATE_FAST_LEN;
- memcpy(cell_out->onionskin, cell_in->payload, CREATE_FAST_LEN);
+ create_cell_init(cell_out, CELL_CREATE_FAST, ONION_HANDSHAKE_TYPE_FAST,
+ CREATE_FAST_LEN, cell_in->payload);
break;
case CELL_CREATE2:
if (parse_create2_payload(cell_out, cell_in->payload,
diff --git a/src/or/onion.h b/src/or/onion.h
index db4c999c9e..d62f032b87 100644
--- a/src/or/onion.h
+++ b/src/or/onion.h
@@ -15,6 +15,7 @@
struct create_cell_t;
int onion_pending_add(or_circuit_t *circ, struct create_cell_t *onionskin);
or_circuit_t *onion_next_task(struct create_cell_t **onionskin_out);
+int onion_num_pending(uint16_t handshake_type);
void onion_pending_remove(or_circuit_t *circ);
void clear_pending_onions(void);
@@ -99,6 +100,9 @@ typedef struct extended_cell_t {
created_cell_t created_cell;
} extended_cell_t;
+void create_cell_init(create_cell_t *cell_out, uint8_t cell_type,
+ uint16_t handshake_type, uint16_t handshake_len,
+ const uint8_t *onionskin);
int create_cell_parse(create_cell_t *cell_out, const cell_t *cell_in);
int created_cell_parse(created_cell_t *cell_out, const cell_t *cell_in);
int extend_cell_parse(extend_cell_t *cell_out, const uint8_t command,
diff --git a/src/test/test.c b/src/test/test.c
index 3ff39e6293..4ec8792344 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -44,6 +44,7 @@ double fabs(double x);
#include "or.h"
#include "buffers.h"
+#include "circuitlist.h"
#include "circuitstats.h"
#include "config.h"
#include "connection_edge.h"
@@ -53,6 +54,7 @@ double fabs(double x);
#include "torgzip.h"
#include "mempool.h"
#include "memarea.h"
+#include "onion.h"
#include "onion_tap.h"
#include "policies.h"
#include "rephist.h"
@@ -933,6 +935,49 @@ test_ntor_handshake(void *arg)
}
#endif
+/** Run unit tests for the onion queues. */
+static void
+test_onion_queues(void)
+{
+ uint8_t buf1[TAP_ONIONSKIN_CHALLENGE_LEN] = {0};
+ uint8_t buf2[NTOR_ONIONSKIN_LEN] = {0};
+
+ or_circuit_t *circ1 = or_circuit_new(0, NULL);
+ or_circuit_t *circ2 = or_circuit_new(0, NULL);
+
+ create_cell_t *onionskin = NULL;
+ create_cell_t *create1 = tor_malloc_zero(sizeof(create_cell_t));
+ create_cell_t *create2 = tor_malloc_zero(sizeof(create_cell_t));
+
+ create_cell_init(create1, CELL_CREATE, ONION_HANDSHAKE_TYPE_TAP,
+ TAP_ONIONSKIN_CHALLENGE_LEN, buf1);
+ create_cell_init(create2, CELL_CREATE, ONION_HANDSHAKE_TYPE_NTOR,
+ NTOR_ONIONSKIN_LEN, buf2);
+
+ test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP));
+ test_eq(0, onion_pending_add(circ1, create1));
+ test_eq(1, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP));
+
+ test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR));
+ test_eq(0, onion_pending_add(circ2, create2));
+ test_eq(1, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR));
+
+ test_eq_ptr(circ2, onion_next_task(&onionskin));
+ test_eq(1, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP));
+ test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR));
+
+ clear_pending_onions();
+ test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP));
+ test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR));
+
+ done:
+ ;
+// circuit_free(circ1);
+// circuit_free(circ2);
+ /* and free create1 and create2 */
+ /* XXX leaks everything here */
+}
+
static void
test_circuit_timeout(void)
{
@@ -2005,6 +2050,7 @@ static struct testcase_t test_array[] = {
ENT(buffers),
{ "buffer_copy", test_buffer_copy, 0, NULL, NULL },
ENT(onion_handshake),
+ ENT(onion_queues),
#ifdef CURVE25519_ENABLED
{ "ntor_handshake", test_ntor_handshake, 0, NULL, NULL },
#endif