aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/compat_threads.c19
-rw-r--r--src/common/compat_threads.h68
-rw-r--r--src/common/crypto.c29
-rw-r--r--src/common/sandbox.c86
-rw-r--r--src/common/storagedir.c25
-rw-r--r--src/or/channel.c70
-rw-r--r--src/or/circuitbuild.c8
-rw-r--r--src/or/circuitstats.c11
-rw-r--r--src/or/circuituse.c18
-rw-r--r--src/or/config.c6
-rw-r--r--src/or/confparse.h6
-rw-r--r--src/or/connection.c4
-rw-r--r--src/or/connection_or.c43
-rw-r--r--src/or/connection_or.h3
-rw-r--r--src/or/conscache.c17
-rw-r--r--src/or/consdiffmgr.c13
-rw-r--r--src/or/control.c539
-rw-r--r--src/or/control.h65
-rw-r--r--src/or/directory.c139
-rw-r--r--src/or/directory.h44
-rw-r--r--src/or/dirserv.c19
-rw-r--r--src/or/dirserv.h1
-rw-r--r--src/or/dirvote.c27
-rw-r--r--src/or/dirvote.h14
-rw-r--r--src/or/dns.c5
-rw-r--r--src/or/entrynodes.c124
-rw-r--r--src/or/entrynodes.h35
-rw-r--r--src/or/git_revision.h2
-rw-r--r--src/or/hs_cache.c18
-rw-r--r--src/or/hs_cache.h2
-rw-r--r--src/or/hs_circuit.c11
-rw-r--r--src/or/hs_circuit.h2
-rw-r--r--src/or/hs_client.c5
-rw-r--r--src/or/hs_common.h11
-rw-r--r--src/or/hs_control.c256
-rw-r--r--src/or/hs_control.h52
-rw-r--r--src/or/hs_service.c245
-rw-r--r--src/or/hs_service.h15
-rw-r--r--src/or/include.am4
-rw-r--r--src/or/main.c2
-rw-r--r--src/or/microdesc.c101
-rw-r--r--src/or/microdesc.h4
-rw-r--r--src/or/networkstatus.c7
-rw-r--r--src/or/protover.c7
-rw-r--r--src/or/protover.h2
-rw-r--r--src/or/protover_rust.c2
-rw-r--r--src/or/rendclient.c10
-rw-r--r--src/or/rendservice.c16
-rw-r--r--src/or/rendservice.h11
-rw-r--r--src/or/rephist.c4
-rw-r--r--src/or/router.c2
-rw-r--r--src/or/routerlist.c20
-rw-r--r--src/or/scheduler.c25
-rw-r--r--src/or/scheduler_kist.c8
-rw-r--r--src/or/tor_api_internal.h2
-rw-r--r--src/rust/tor_rust/include.am14
-rw-r--r--src/test/include.am1
-rw-r--r--src/test/test.c1
-rw-r--r--src/test/test.h1
-rw-r--r--src/test/test_config.c11
-rw-r--r--src/test/test_controller.c168
-rw-r--r--src/test/test_entrynodes.c148
-rw-r--r--src/test/test_hs.c15
-rw-r--r--src/test/test_hs_control.c199
-rwxr-xr-xsrc/test/test_rust.sh8
65 files changed, 2354 insertions, 496 deletions
diff --git a/src/common/compat_threads.c b/src/common/compat_threads.c
index 208d3138d9..6adcaab956 100644
--- a/src/common/compat_threads.c
+++ b/src/common/compat_threads.c
@@ -352,12 +352,7 @@ alert_sockets_close(alert_sockets_t *socks)
socks->read_fd = socks->write_fd = -1;
}
-/*
- * XXXX We might be smart to move to compiler intrinsics or real atomic
- * XXXX operations at some point. But not yet.
- *
- */
-
+#ifndef HAVE_STDATOMIC_H
/** Initialize a new atomic counter with the value 0 */
void
atomic_counter_init(atomic_counter_t *counter)
@@ -397,4 +392,16 @@ atomic_counter_get(atomic_counter_t *counter)
tor_mutex_release(&counter->mutex);
return val;
}
+/** Replace the value of an atomic counter; return the old one. */
+size_t
+atomic_counter_exchange(atomic_counter_t *counter, size_t newval)
+{
+ size_t oldval;
+ tor_mutex_acquire(&counter->mutex);
+ oldval = counter->val;
+ counter->val = newval;
+ tor_mutex_release(&counter->mutex);
+ return oldval;
+}
+#endif /* !defined(HAVE_STDATOMIC_H) */
diff --git a/src/common/compat_threads.h b/src/common/compat_threads.h
index 42f14eab2a..ce86b15e9d 100644
--- a/src/common/compat_threads.h
+++ b/src/common/compat_threads.h
@@ -14,6 +14,10 @@
#include <pthread.h>
#endif
+#ifdef HAVE_STDATOMIC_H
+#include <stdatomic.h>
+#endif
+
#if defined(_WIN32)
#define USE_WIN32_THREADS
#elif defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_CREATE)
@@ -150,16 +154,68 @@ void tor_threadlocal_set(tor_threadlocal_t *threadlocal, void *value);
/**
* Atomic counter type; holds a size_t value.
*/
+#ifdef HAVE_STDATOMIC_H
+typedef struct atomic_counter_t {
+ atomic_size_t val;
+} atomic_counter_t;
+#define ATOMIC_LINKAGE static
+#else /* !(defined(HAVE_STDATOMIC_H)) */
typedef struct atomic_counter_t {
tor_mutex_t mutex;
size_t val;
} atomic_counter_t;
-
-void atomic_counter_init(atomic_counter_t *counter);
-void atomic_counter_destroy(atomic_counter_t *counter);
-void atomic_counter_add(atomic_counter_t *counter, size_t add);
-void atomic_counter_sub(atomic_counter_t *counter, size_t sub);
-size_t atomic_counter_get(atomic_counter_t *counter);
+#define ATOMIC_LINKAGE
+#endif /* defined(HAVE_STDATOMIC_H) */
+
+ATOMIC_LINKAGE void atomic_counter_init(atomic_counter_t *counter);
+ATOMIC_LINKAGE void atomic_counter_destroy(atomic_counter_t *counter);
+ATOMIC_LINKAGE void atomic_counter_add(atomic_counter_t *counter, size_t add);
+ATOMIC_LINKAGE void atomic_counter_sub(atomic_counter_t *counter, size_t sub);
+ATOMIC_LINKAGE size_t atomic_counter_get(atomic_counter_t *counter);
+ATOMIC_LINKAGE size_t atomic_counter_exchange(atomic_counter_t *counter,
+ size_t newval);
+#undef ATOMIC_LINKAGE
+
+#ifdef HAVE_STDATOMIC_H
+/** Initialize a new atomic counter with the value 0 */
+static inline void
+atomic_counter_init(atomic_counter_t *counter)
+{
+ atomic_init(&counter->val, 0);
+}
+/** Clean up all resources held by an atomic counter. */
+static inline void
+atomic_counter_destroy(atomic_counter_t *counter)
+{
+ (void)counter;
+}
+/** Add a value to an atomic counter. */
+static inline void
+atomic_counter_add(atomic_counter_t *counter, size_t add)
+{
+ (void) atomic_fetch_add(&counter->val, add);
+}
+/** Subtract a value from an atomic counter. */
+static inline void
+atomic_counter_sub(atomic_counter_t *counter, size_t sub)
+{
+ (void) atomic_fetch_sub(&counter->val, sub);
+}
+/** Return the current value of an atomic counter */
+static inline size_t
+atomic_counter_get(atomic_counter_t *counter)
+{
+ return atomic_load(&counter->val);
+}
+/** Replace the value of an atomic counter; return the old one. */
+static inline size_t
+atomic_counter_exchange(atomic_counter_t *counter, size_t newval)
+{
+ return atomic_exchange(&counter->val, newval);
+}
+
+#else /* !(defined(HAVE_STDATOMIC_H)) */
+#endif /* defined(HAVE_STDATOMIC_H) */
#endif /* !defined(TOR_COMPAT_THREADS_H) */
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 6fe3c661c8..380f038f42 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -645,11 +645,21 @@ crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits))
return 0;
}
+/** A PEM callback that always reports a failure to get a password */
+static int
+pem_no_password_cb(char *buf, int size, int rwflag, void *u)
+{
+ (void)buf;
+ (void)size;
+ (void)rwflag;
+ (void)u;
+ return 0;
+}
+
/** Read a PEM-encoded private key from the <b>len</b>-byte string <b>s</b>
* into <b>env</b>. Return 0 on success, -1 on failure. If len is -1,
* the string is nul-terminated.
*/
-/* Used here, and used for testing. */
int
crypto_pk_read_private_key_from_string(crypto_pk_t *env,
const char *s, ssize_t len)
@@ -668,7 +678,7 @@ crypto_pk_read_private_key_from_string(crypto_pk_t *env,
if (env->key)
RSA_free(env->key);
- env->key = PEM_read_bio_RSAPrivateKey(b,NULL,NULL,NULL);
+ env->key = PEM_read_bio_RSAPrivateKey(b,NULL,pem_no_password_cb,NULL);
BIO_free(b);
@@ -800,7 +810,7 @@ crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src,
if (env->key)
RSA_free(env->key);
- env->key = PEM_read_bio_RSAPublicKey(b, NULL, NULL, NULL);
+ env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL);
BIO_free(b);
if (!env->key) {
crypto_log_errors(LOG_WARN, "reading public key from string");
@@ -2865,8 +2875,17 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
tor_assert(errno != EAGAIN);
tor_assert(errno != EINTR);
- /* Probably ENOSYS. */
- log_warn(LD_CRYPTO, "Can't get entropy from getrandom().");
+ /* Useful log message for errno. */
+ if (errno == ENOSYS) {
+ log_warn(LD_CRYPTO, "Can't get entropy from getrandom(). "
+ " You are running a version of Tor built to support"
+ " getrandom(), but the kernel doesn't implement this"
+ " implement this function--probably because it is too old?");
+ } else {
+ log_warn(LD_CRYPTO, "Can't get entropy from getrandom(): %s.",
+ strerror(errno));
+ }
+
getrandom_works = 0; /* Don't bother trying again. */
return -1;
/* LCOV_EXCL_STOP */
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
index 8cb78bd28e..aec9857e94 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -62,6 +62,9 @@
#include <time.h>
#include <poll.h>
+#ifdef HAVE_GNU_LIBC_VERSION_H
+#include <gnu/libc-version.h>
+#endif
#ifdef HAVE_LINUX_NETFILTER_IPV4_H
#include <linux/netfilter_ipv4.h>
#endif
@@ -133,6 +136,9 @@ static int filter_nopar_gen[] = {
SCMP_SYS(clone),
SCMP_SYS(epoll_create),
SCMP_SYS(epoll_wait),
+#ifdef __NR_epoll_pwait
+ SCMP_SYS(epoll_pwait),
+#endif
#ifdef HAVE_EVENTFD
SCMP_SYS(eventfd2),
#endif
@@ -397,6 +403,52 @@ sb_mmap2(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
}
#endif /* defined(__NR_mmap2) */
+#ifdef HAVE_GNU_LIBC_VERSION_H
+#ifdef HAVE_GNU_GET_LIBC_VERSION
+#define CHECK_LIBC_VERSION
+#endif
+#endif
+
+/* Return true if we think we're running with a libc that always uses
+ * openat on linux. */
+static int
+libc_uses_openat_for_everything(void)
+{
+#ifdef CHECK_LIBC_VERSION
+ const char *version = gnu_get_libc_version();
+ if (version == NULL)
+ return 0;
+
+ int major = -1;
+ int minor = -1;
+
+ tor_sscanf(version, "%d.%d", &major, &minor);
+ if (major >= 3)
+ return 1;
+ else if (major == 2 && minor >= 26)
+ return 1;
+ else
+ return 0;
+#else /* !(defined(CHECK_LIBC_VERSION)) */
+ return 0;
+#endif /* defined(CHECK_LIBC_VERSION) */
+}
+
+/** Allow a single file to be opened. If <b>use_openat</b> is true,
+ * we're using a libc that remaps all the opens into openats. */
+static int
+allow_file_open(scmp_filter_ctx ctx, int use_openat, const char *file)
+{
+ if (use_openat) {
+ return seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat),
+ SCMP_CMP_STR(0, SCMP_CMP_EQ, AT_FDCWD),
+ SCMP_CMP_STR(1, SCMP_CMP_EQ, file));
+ } else {
+ return seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open),
+ SCMP_CMP_STR(0, SCMP_CMP_EQ, file));
+ }
+}
+
/**
* Function responsible for setting up the open syscall for
* the seccomp filter sandbox.
@@ -407,14 +459,15 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
int rc;
sandbox_cfg_t *elem = NULL;
+ int use_openat = libc_uses_openat_for_everything();
+
// for each dynamic parameter filters
for (elem = filter; elem != NULL; elem = elem->next) {
smp_param_t *param = elem->param;
if (param != NULL && param->prot == 1 && param->syscall
== SCMP_SYS(open)) {
- rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open),
- SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value));
+ rc = allow_file_open(ctx, use_openat, param->value);
if (rc != 0) {
log_err(LD_BUG,"(Sandbox) failed to add open syscall, received "
"libseccomp error %d", rc);
@@ -432,6 +485,15 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
return rc;
}
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(openat),
+ SCMP_CMP_MASKED(2, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|O_NOFOLLOW,
+ O_RDONLY));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add openat syscall, received "
+ "libseccomp error %d", rc);
+ return rc;
+ }
+
return 0;
}
@@ -621,7 +683,7 @@ sb_socket(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket),
SCMP_CMP(0, SCMP_CMP_EQ, PF_NETLINK),
- SCMP_CMP(1, SCMP_CMP_EQ, SOCK_RAW),
+ SCMP_CMP_MASKED(1, SOCK_CLOEXEC, SOCK_RAW),
SCMP_CMP(2, SCMP_CMP_EQ, 0));
if (rc)
return rc;
@@ -1054,6 +1116,19 @@ sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
}
#endif /* defined(__NR_stat64) */
+static int
+sb_kill(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ (void) filter;
+#ifdef __NR_kill
+ /* Allow killing anything with signal 0 -- it isn't really a kill. */
+ return seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(kill),
+ SCMP_CMP(1, SCMP_CMP_EQ, 0));
+#else
+ return 0;
+#endif /* defined(__NR_kill) */
+}
+
/**
* Array of function pointers responsible for filtering different syscalls at
* a parameter level.
@@ -1090,10 +1165,10 @@ static sandbox_filter_func_t filter_func[] = {
sb_setsockopt,
sb_getsockopt,
sb_socketpair,
-
#ifdef HAVE_KIST_SUPPORT
sb_ioctl,
#endif
+ sb_kill
};
const char *
@@ -1601,7 +1676,8 @@ add_param_filter(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
// function pointer
for (i = 0; i < ARRAY_LENGTH(filter_func); i++) {
- if ((filter_func[i])(ctx, cfg)) {
+ rc = filter_func[i](ctx, cfg);
+ if (rc) {
log_err(LD_BUG,"(Sandbox) failed to add syscall %d, received libseccomp "
"error %d", i, rc);
return rc;
diff --git a/src/common/storagedir.c b/src/common/storagedir.c
index 31933f64c2..c471ea911f 100644
--- a/src/common/storagedir.c
+++ b/src/common/storagedir.c
@@ -187,14 +187,19 @@ storage_dir_get_usage(storage_dir_t *d)
return d->usage;
}
-/** Mmap a specified file within <b>d</b>. */
+/** Mmap a specified file within <b>d</b>.
+ *
+ * On failure, return NULL and set errno as for tor_mmap_file(). */
tor_mmap_t *
storage_dir_map(storage_dir_t *d, const char *fname)
{
char *path = NULL;
tor_asprintf(&path, "%s/%s", d->directory, fname);
tor_mmap_t *result = tor_mmap_file(path);
+ int errval = errno;
tor_free(path);
+ if (result == NULL)
+ errno = errval;
return result;
}
@@ -364,6 +369,10 @@ storage_dir_save_labeled_to_file(storage_dir_t *d,
* the data's size into *<b>sz_out</b>. On success, also return a tor_mmap_t
* object whose contents should not be used -- it needs to be kept around,
* though, for as long as <b>data_out</b> is going to be valid.
+ *
+ * On failure, set errno as for tor_mmap_file() if the file was missing or
+ * empty, and set errno to EINVAL if the file was not in the labeled
+ * format expected.
*/
tor_mmap_t *
storage_dir_map_labeled(storage_dir_t *dir,
@@ -373,13 +382,20 @@ storage_dir_map_labeled(storage_dir_t *dir,
size_t *sz_out)
{
tor_mmap_t *m = storage_dir_map(dir, fname);
- if (! m)
+ int errval;
+ if (! m) {
+ errval = errno;
goto err;
+ }
const char *nulp = memchr(m->data, '\0', m->size);
- if (! nulp)
+ if (! nulp) {
+ errval = EINVAL;
goto err;
- if (labels_out && config_get_lines(m->data, labels_out, 0) < 0)
+ }
+ if (labels_out && config_get_lines(m->data, labels_out, 0) < 0) {
+ errval = EINVAL;
goto err;
+ }
size_t offset = nulp - m->data + 1;
tor_assert(offset <= m->size);
*data_out = (const uint8_t *)(m->data + offset);
@@ -388,6 +404,7 @@ storage_dir_map_labeled(storage_dir_t *dir,
return m;
err:
tor_munmap_file(m);
+ errno = errval;
return NULL;
}
diff --git a/src/or/channel.c b/src/or/channel.c
index 0b5a7fde90..7590ba8a5b 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -4726,6 +4726,16 @@ channel_set_circid_type,(channel_t *chan,
}
}
+static int
+channel_sort_by_ed25519_identity(const void **a_, const void **b_)
+{
+ const channel_t *a = *a_,
+ *b = *b_;
+ return fast_memcmp(&a->ed25519_identity.pubkey,
+ &b->ed25519_identity.pubkey,
+ sizeof(a->ed25519_identity.pubkey));
+}
+
/** Helper for channel_update_bad_for_new_circs(): Perform the
* channel_update_bad_for_new_circs operation on all channels in <b>lst</b>,
* all of which MUST have the same RSA ID. (They MAY have different
@@ -4734,44 +4744,52 @@ static void
channel_rsa_id_group_set_badness(struct channel_list_s *lst, int force)
{
/*XXXX This function should really be about channels. 15056 */
- channel_t *chan;
+ channel_t *chan = TOR_LIST_FIRST(lst);
+
+ if (!chan)
+ return;
+
+ /* if there is only one channel, don't bother looping */
+ if (PREDICT_LIKELY(!TOR_LIST_NEXT(chan, next_with_same_id))) {
+ connection_or_single_set_badness_(
+ time(NULL), BASE_CHAN_TO_TLS(chan)->conn, force);
+ return;
+ }
+
+ smartlist_t *channels = smartlist_new();
- /* First, get a minimal list of the ed25519 identites */
- smartlist_t *ed_identities = smartlist_new();
TOR_LIST_FOREACH(chan, lst, next_with_same_id) {
- uint8_t *id_copy =
- tor_memdup(&chan->ed25519_identity.pubkey, DIGEST256_LEN);
- smartlist_add(ed_identities, id_copy);
+ if (BASE_CHAN_TO_TLS(chan)->conn) {
+ smartlist_add(channels, chan);
+ }
}
- smartlist_sort_digests256(ed_identities);
- smartlist_uniq_digests256(ed_identities);
- /* Now, for each Ed identity, build a smartlist and find the best entry on
- * it. */
+ smartlist_sort(channels, channel_sort_by_ed25519_identity);
+
+ const ed25519_public_key_t *common_ed25519_identity = NULL;
+ /* it would be more efficient to do a slice, but this case is rare */
smartlist_t *or_conns = smartlist_new();
- SMARTLIST_FOREACH_BEGIN(ed_identities, const uint8_t *, ed_id) {
- TOR_LIST_FOREACH(chan, lst, next_with_same_id) {
- channel_tls_t *chantls = BASE_CHAN_TO_TLS(chan);
- if (tor_memneq(ed_id, &chan->ed25519_identity.pubkey, DIGEST256_LEN))
- continue;
- or_connection_t *orconn = chantls->conn;
- if (orconn) {
- tor_assert(orconn->chan == chantls);
- smartlist_add(or_conns, orconn);
- }
+ SMARTLIST_FOREACH_BEGIN(channels, channel_t *, channel) {
+ if (!common_ed25519_identity)
+ common_ed25519_identity = &channel->ed25519_identity;
+
+ if (! ed25519_pubkey_eq(&channel->ed25519_identity,
+ common_ed25519_identity)) {
+ connection_or_group_set_badness_(or_conns, force);
+ smartlist_clear(or_conns);
+ common_ed25519_identity = &channel->ed25519_identity;
}
- connection_or_group_set_badness_(or_conns, force);
- smartlist_clear(or_conns);
- } SMARTLIST_FOREACH_END(ed_id);
+ smartlist_add(or_conns, BASE_CHAN_TO_TLS(channel)->conn);
+ } SMARTLIST_FOREACH_END(channel);
+
+ connection_or_group_set_badness_(or_conns, force);
/* XXXX 15056 we may want to do something special with connections that have
* no set Ed25519 identity! */
smartlist_free(or_conns);
-
- SMARTLIST_FOREACH(ed_identities, uint8_t *, ed_id, tor_free(ed_id));
- smartlist_free(ed_identities);
+ smartlist_free(channels);
}
/** Go through all the channels (or if <b>digest</b> is non-NULL, just
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 7f0bcc4150..2e6b63b4d6 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -2707,12 +2707,16 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
node_describe(node));
}
+ /* Retrieve the curve25519 pubkey. */
+ const curve25519_public_key_t *curve_pubkey =
+ node_get_curve25519_onion_key(node);
+
if (valid_addr && node->ri)
return extend_info_new(node->ri->nickname,
node->identity,
ed_pubkey,
node->ri->onion_pkey,
- node->ri->onion_curve25519_pkey,
+ curve_pubkey,
&ap.addr,
ap.port);
else if (valid_addr && node->rs && node->md)
@@ -2720,7 +2724,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
node->identity,
ed_pubkey,
node->md->onion_pkey,
- node->md->onion_curve25519_pkey,
+ curve_pubkey,
&ap.addr,
ap.port);
else
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index 923a6d794f..b8421a3c7e 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -431,9 +431,14 @@ circuit_build_times_new_consensus_params(circuit_build_times_t *cbt,
if (num > 0) {
if (num != cbt->liveness.num_recent_circs) {
int8_t *recent_circs;
- log_notice(LD_CIRC, "The Tor Directory Consensus has changed how many "
- "circuits we must track to detect network failures from %d "
- "to %d.", cbt->liveness.num_recent_circs, num);
+ if (cbt->liveness.num_recent_circs > 0) {
+ log_notice(LD_CIRC, "The Tor Directory Consensus has changed how "
+ "many circuits we must track to detect network failures "
+ "from %d to %d.", cbt->liveness.num_recent_circs, num);
+ } else {
+ log_notice(LD_CIRC, "Upon receiving a consensus directory, "
+ "re-enabling circuit-based network failure detection.");
+ }
tor_assert(cbt->liveness.timeouts_after_firsthop ||
cbt->liveness.num_recent_circs == 0);
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 62cc865f48..aa0df95652 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -543,8 +543,7 @@ circuit_expire_building(void)
cutoff = begindir_cutoff;
else if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT)
cutoff = close_cutoff;
- else if (victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCING ||
- victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
+ else if (victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
cutoff = c_intro_cutoff;
else if (victim->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)
cutoff = s_intro_cutoff;
@@ -661,12 +660,13 @@ circuit_expire_building(void)
TO_ORIGIN_CIRCUIT(victim)->path_state = PATH_STATE_USE_FAILED;
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
- /* We keep old introducing circuits around for
- * a while in parallel, and they can end up "opened".
- * We decide below if we're going to mark them timed
- * out and eventually close them.
- */
- break;
+ /* That purpose means that the intro point circuit has been opened
+ * succesfully but the INTRODUCE1 cell hasn't been sent yet because
+ * the client is waiting for the rendezvous point circuit to open.
+ * Keep this circuit open while waiting for the rendezvous circuit.
+ * We let the circuit idle timeout take care of cleaning this
+ * circuit if it never used. */
+ continue;
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
@@ -757,8 +757,6 @@ circuit_expire_building(void)
NULL)
break;
/* fallthrough! */
- case CIRCUIT_PURPOSE_C_INTRODUCING:
- /* connection_ap_handshake_attach_circuit() will relaunch for us */
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
/* If we have reached this line, we want to spare the circ for now. */
diff --git a/src/or/config.c b/src/or/config.c
index 16e6445616..d589876785 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -1094,6 +1094,7 @@ static const char *default_authorities[] = {
"199.58.81.140:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145",
"bastet orport=443 "
"v3ident=27102BC123E7AF1D4741AE047E160C91ADC76B21 "
+ "ipv6=[2620:13:4000:6000::1000:118]:443 "
"204.13.164.118:80 24E2 F139 121D 4394 C54B 5BCC 368B 3B41 1857 C413",
NULL
};
@@ -1858,7 +1859,7 @@ options_act(const or_options_t *old_options)
"given FD.");
return -1;
}
-#endif
+#endif /* defined(_WIN32) */
}
/* Load state */
@@ -6584,7 +6585,6 @@ port_cfg_new(size_t namelen)
cfg->entry_cfg.ipv6_traffic = 1;
cfg->entry_cfg.dns_request = 1;
cfg->entry_cfg.onion_traffic = 1;
- cfg->entry_cfg.cache_ipv4_answers = 1;
cfg->entry_cfg.prefer_ipv6_virtaddr = 1;
return cfg;
}
@@ -6900,7 +6900,7 @@ parse_port_config(smartlist_t *out,
bind_ipv4_only = 0, bind_ipv6_only = 0,
ipv4_traffic = 1, ipv6_traffic = 1, prefer_ipv6 = 0, dns_request = 1,
onion_traffic = 1,
- cache_ipv4 = 1, use_cached_ipv4 = 0,
+ cache_ipv4 = 0, use_cached_ipv4 = 0,
cache_ipv6 = 0, use_cached_ipv6 = 0,
prefer_ipv6_automap = 1, world_writable = 0, group_writable = 0,
relax_dirmode_check = 0,
diff --git a/src/or/confparse.h b/src/or/confparse.h
index 6f0b3b325c..022886e811 100644
--- a/src/or/confparse.h
+++ b/src/or/confparse.h
@@ -68,7 +68,7 @@ typedef union {
config_line_t **LINELIST_V;
routerset_t **ROUTERSET;
} confparse_dummy_values_t;
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** An abbreviation for a configuration option allowed on the command line. */
typedef struct config_abbrev_t {
@@ -132,13 +132,13 @@ typedef struct config_var_t {
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL, { .INT=NULL } }
#define DUMMY_TYPECHECK_INSTANCE(tp) \
static tp tp ## _dummy
-#else
+#else /* !(defined(TOR_UNIT_TESTS)) */
#define CONF_TEST_MEMBERS(tp, conftype, member)
#define END_OF_CONFIG_VARS { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
/* Repeatedly declarable incomplete struct to absorb redundant semicolons */
#define DUMMY_TYPECHECK_INSTANCE(tp) \
struct tor_semicolon_eater
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** Type of a callback to validate whether a given configuration is
* well-formed and consistent. See options_trial_assign() for documentation
diff --git a/src/or/connection.c b/src/or/connection.c
index 24f5a64622..92ccd3d5c8 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -768,6 +768,10 @@ connection_close_immediate(connection_t *conn)
connection_unregister_events(conn);
+ /* Prevent the event from getting unblocked. */
+ conn->read_blocked_on_bw =
+ conn->write_blocked_on_bw = 0;
+
if (SOCKET_OK(conn->s))
tor_close_socket(conn->s);
conn->s = TOR_INVALID_SOCKET;
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 7af1f2b645..fdf1b2ebb1 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -965,6 +965,36 @@ connection_or_mark_bad_for_new_circs(or_connection_t *or_conn)
* too old for new circuits? */
#define TIME_BEFORE_OR_CONN_IS_TOO_OLD (60*60*24*7)
+/** Expire an or_connection if it is too old. Helper for
+ * connection_or_group_set_badness_ and fast path for
+ * channel_rsa_id_group_set_badness.
+ *
+ * Returns 1 if the connection was already expired, else 0.
+ */
+int
+connection_or_single_set_badness_(time_t now,
+ or_connection_t *or_conn,
+ int force)
+{
+ /* XXXX this function should also be about channels? */
+ if (or_conn->base_.marked_for_close ||
+ connection_or_is_bad_for_new_circs(or_conn))
+ return 1;
+
+ if (force ||
+ or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD
+ < now) {
+ log_info(LD_OR,
+ "Marking OR conn to %s:%d as too old for new circuits "
+ "(fd "TOR_SOCKET_T_FORMAT", %d secs old).",
+ or_conn->base_.address, or_conn->base_.port, or_conn->base_.s,
+ (int)(now - or_conn->base_.timestamp_created));
+ connection_or_mark_bad_for_new_circs(or_conn);
+ }
+
+ return 0;
+}
+
/** Given a list of all the or_connections with a given
* identity, set elements of that list as is_bad_for_new_circs as
* appropriate. Helper for connection_or_set_bad_connections().
@@ -995,19 +1025,8 @@ connection_or_group_set_badness_(smartlist_t *group, int force)
/* Pass 1: expire everything that's old, and see what the status of
* everything else is. */
SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) {
- if (or_conn->base_.marked_for_close ||
- connection_or_is_bad_for_new_circs(or_conn))
+ if (connection_or_single_set_badness_(now, or_conn, force))
continue;
- if (force ||
- or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD
- < now) {
- log_info(LD_OR,
- "Marking OR conn to %s:%d as too old for new circuits "
- "(fd "TOR_SOCKET_T_FORMAT", %d secs old).",
- or_conn->base_.address, or_conn->base_.port, or_conn->base_.s,
- (int)(now - or_conn->base_.timestamp_created));
- connection_or_mark_bad_for_new_circs(or_conn);
- }
if (connection_or_is_bad_for_new_circs(or_conn)) {
++n_old;
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index ee66b7c528..644df5c2c9 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -112,6 +112,9 @@ void var_cell_free(var_cell_t *cell);
#define MIN_LINK_PROTO_FOR_CHANNEL_PADDING 5
#define MAX_LINK_PROTO MIN_LINK_PROTO_FOR_CHANNEL_PADDING
+int connection_or_single_set_badness_(time_t now,
+ or_connection_t *or_conn,
+ int force);
void connection_or_group_set_badness_(smartlist_t *group, int force);
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/conscache.c b/src/or/conscache.c
index 1407d0f0b9..9a90ff4dd2 100644
--- a/src/or/conscache.c
+++ b/src/or/conscache.c
@@ -539,9 +539,20 @@ consensus_cache_rescan(consensus_cache_t *cache)
map = storage_dir_map_labeled(cache->dir, fname,
&labels, &body, &bodylen);
if (! map) {
- /* Can't load this; continue */
- log_warn(LD_FS, "Unable to map file %s from consensus cache: %s",
- escaped(fname), strerror(errno));
+ /* The ERANGE error might come from tor_mmap_file() -- it means the file
+ * was empty. EINVAL might come from ..map_labeled() -- it means the
+ * file was misformatted. In both cases, we should just delete it.
+ */
+ if (errno == ERANGE || errno == EINVAL) {
+ log_warn(LD_FS, "Found %s file %s in consensus cache; removing it.",
+ errno == ERANGE ? "empty" : "misformatted",
+ escaped(fname));
+ storage_dir_remove_file(cache->dir, fname);
+ } else {
+ /* Can't load this; continue */
+ log_warn(LD_FS, "Unable to map file %s from consensus cache: %s",
+ escaped(fname), strerror(errno));
+ }
continue;
}
consensus_cache_entry_t *ent =
diff --git a/src/or/consdiffmgr.c b/src/or/consdiffmgr.c
index 1d63d59577..e539b61484 100644
--- a/src/or/consdiffmgr.c
+++ b/src/or/consdiffmgr.c
@@ -283,6 +283,10 @@ cdm_diff_ht_set_status(consensus_flavor_t flav,
int status,
consensus_cache_entry_handle_t *handle)
{
+ if (handle == NULL) {
+ tor_assert_nonfatal(status != CDM_DIFF_PRESENT);
+ }
+
struct cdm_diff_t search, *ent;
memset(&search, 0, sizeof(cdm_diff_t));
search.flavor = flav;
@@ -1589,8 +1593,13 @@ consensus_diff_worker_replyfn(void *work_)
for (u = 0; u < ARRAY_LENGTH(handles); ++u) {
compress_method_t method = compress_diffs_with[u];
if (cache) {
- cdm_diff_ht_set_status(flav, from_sha3, to_sha3, method, status,
- handles[u]);
+ consensus_cache_entry_handle_t *h = handles[u];
+ int this_status = status;
+ if (h == NULL) {
+ this_status = CDM_DIFF_ERROR;
+ }
+ tor_assert_nonfatal(h != NULL || this_status == CDM_DIFF_ERROR);
+ cdm_diff_ht_set_status(flav, from_sha3, to_sha3, method, this_status, h);
} else {
consensus_cache_entry_handle_free(handles[u]);
}
diff --git a/src/or/control.c b/src/or/control.c
index e17e923650..de08b2ee7d 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -58,7 +58,9 @@
#include "entrynodes.h"
#include "geoip.h"
#include "hibernate.h"
+#include "hs_cache.h"
#include "hs_common.h"
+#include "hs_control.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
@@ -2014,36 +2016,89 @@ getinfo_helper_dir(control_connection_t *control_conn,
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
} else if (!strcmpstart(question, "hs/client/desc/id/")) {
- rend_cache_entry_t *e = NULL;
+ hostname_type_t addr_type;
question += strlen("hs/client/desc/id/");
- if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
+ if (rend_valid_v2_service_id(question)) {
+ addr_type = ONION_V2_HOSTNAME;
+ } else if (hs_address_is_valid(question)) {
+ addr_type = ONION_V3_HOSTNAME;
+ } else {
*errmsg = "Invalid address";
return -1;
}
- if (!rend_cache_lookup_entry(question, -1, &e)) {
- /* Descriptor found in cache */
- *answer = tor_strdup(e->desc);
+ if (addr_type == ONION_V2_HOSTNAME) {
+ rend_cache_entry_t *e = NULL;
+ if (!rend_cache_lookup_entry(question, -1, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
} else {
- *errmsg = "Not found in cache";
- return -1;
+ ed25519_public_key_t service_pk;
+ const char *desc;
+
+ /* The check before this if/else makes sure of this. */
+ tor_assert(addr_type == ONION_V3_HOSTNAME);
+
+ if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) {
+ *errmsg = "Invalid v3 address";
+ return -1;
+ }
+
+ desc = hs_cache_lookup_encoded_as_client(&service_pk);
+ if (desc) {
+ *answer = tor_strdup(desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
}
} else if (!strcmpstart(question, "hs/service/desc/id/")) {
- rend_cache_entry_t *e = NULL;
+ hostname_type_t addr_type;
question += strlen("hs/service/desc/id/");
- if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
+ if (rend_valid_v2_service_id(question)) {
+ addr_type = ONION_V2_HOSTNAME;
+ } else if (hs_address_is_valid(question)) {
+ addr_type = ONION_V3_HOSTNAME;
+ } else {
*errmsg = "Invalid address";
return -1;
}
+ rend_cache_entry_t *e = NULL;
- if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
- /* Descriptor found in cache */
- *answer = tor_strdup(e->desc);
+ if (addr_type == ONION_V2_HOSTNAME) {
+ if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
} else {
- *errmsg = "Not found in cache";
- return -1;
+ ed25519_public_key_t service_pk;
+ char *desc;
+
+ /* The check before this if/else makes sure of this. */
+ tor_assert(addr_type == ONION_V3_HOSTNAME);
+
+ if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) {
+ *errmsg = "Invalid v3 address";
+ return -1;
+ }
+
+ desc = hs_service_lookup_current_desc(&service_pk);
+ if (desc) {
+ /* Newly allocated string, we have ownership. */
+ *answer = desc;
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
}
} else if (!strcmpstart(question, "md/id/")) {
const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0);
@@ -2624,9 +2679,16 @@ circuit_describe_status_for_controller(origin_circuit_t *circ)
}
}
- if (circ->rend_data != NULL) {
- smartlist_add_asprintf(descparts, "REND_QUERY=%s",
- rend_data_get_address(circ->rend_data));
+ if (circ->rend_data != NULL || circ->hs_ident != NULL) {
+ char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ const char *onion_address;
+ if (circ->rend_data) {
+ onion_address = rend_data_get_address(circ->rend_data);
+ } else {
+ hs_build_address(&circ->hs_ident->identity_pk, HS_VERSION_THREE, addr);
+ onion_address = addr;
+ }
+ smartlist_add_asprintf(descparts, "REND_QUERY=%s", onion_address);
}
{
@@ -4277,9 +4339,11 @@ handle_control_hspost(control_connection_t *conn,
const char *body)
{
static const char *opt_server = "SERVER=";
+ static const char *opt_hsaddress = "HSADDRESS=";
smartlist_t *hs_dirs = NULL;
const char *encoded_desc = body;
size_t encoded_desc_len = len;
+ const char *onion_address = NULL;
char *cp = memchr(body, '\n', len);
if (cp == NULL) {
@@ -4309,15 +4373,16 @@ handle_control_hspost(control_connection_t *conn,
server);
goto done;
}
- if (!node->rs->is_hs_dir) {
- connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir"
- "\r\n", server);
- goto done;
- }
/* Valid server, add it to our local list. */
if (!hs_dirs)
hs_dirs = smartlist_new();
smartlist_add(hs_dirs, node->rs);
+ } else if (!strcasecmpstart(arg, opt_hsaddress)) {
+ if (!hs_address_is_valid(arg)) {
+ connection_printf_to_buf(conn, "512 Malformed onion address\r\n");
+ goto done;
+ }
+ onion_address = arg;
} else {
connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n",
arg);
@@ -4326,6 +4391,19 @@ handle_control_hspost(control_connection_t *conn,
} SMARTLIST_FOREACH_END(arg);
}
+ /* Handle the v3 case. */
+ if (onion_address) {
+ char *desc_str = NULL;
+ read_escaped_data(encoded_desc, encoded_desc_len, &desc_str);
+ if (hs_control_hspost_command(desc_str, onion_address, hs_dirs) < 0) {
+ connection_printf_to_buf(conn, "554 Invalid descriptor\r\n");
+ }
+ tor_free(desc_str);
+ goto done;
+ }
+
+ /* From this point on, it is only v2. */
+
/* Read the dot encoded descriptor, and parse it. */
rend_encoded_v2_service_descriptor_t *desc =
tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
@@ -4370,6 +4448,52 @@ handle_control_hspost(control_connection_t *conn,
return 0;
}
+/* Helper function for ADD_ONION that adds an ephemeral service depending on
+ * the given hs_version.
+ *
+ * The secret key in pk depends on the hs_version. The ownership of the key
+ * used in pk is given to the HS subsystem so the caller must stop accessing
+ * it after.
+ *
+ * The port_cfgs is a list of service port. Ownership transfered to service.
+ * The max_streams refers to the MaxStreams= key.
+ * The max_streams_close_circuit refers to the MaxStreamsCloseCircuit key.
+ * The auth_type is the authentication type of the clients in auth_clients.
+ * The ownership of that list is transfered to the service.
+ *
+ * On success (RSAE_OKAY), the address_out points to a newly allocated string
+ * containing the onion address without the .onion part. On error, address_out
+ * is untouched. */
+static hs_service_add_ephemeral_status_t
+add_onion_helper_add_service(int hs_version,
+ add_onion_secret_key_t *pk,
+ smartlist_t *port_cfgs, int max_streams,
+ int max_streams_close_circuit, int auth_type,
+ smartlist_t *auth_clients, char **address_out)
+{
+ hs_service_add_ephemeral_status_t ret;
+
+ tor_assert(pk);
+ tor_assert(port_cfgs);
+ tor_assert(address_out);
+
+ switch (hs_version) {
+ case HS_VERSION_TWO:
+ ret = rend_service_add_ephemeral(pk->v2, port_cfgs, max_streams,
+ max_streams_close_circuit, auth_type,
+ auth_clients, address_out);
+ break;
+ case HS_VERSION_THREE:
+ ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams,
+ max_streams_close_circuit, address_out);
+ break;
+ default:
+ tor_assert_unreached();
+ }
+
+ return ret;
+}
+
/** Called when we get a ADD_ONION command; parse the body, and set up
* the new ephemeral Onion Service. */
static int
@@ -4551,15 +4675,15 @@ handle_control_add_onion(control_connection_t *conn,
}
/* Parse the "keytype:keyblob" argument. */
- crypto_pk_t *pk = NULL;
+ int hs_version = 0;
+ add_onion_secret_key_t pk;
const char *key_new_alg = NULL;
char *key_new_blob = NULL;
char *err_msg = NULL;
- pk = add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk,
- &key_new_alg, &key_new_blob,
- &err_msg);
- if (!pk) {
+ if (add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk,
+ &key_new_alg, &key_new_blob, &pk, &hs_version,
+ &err_msg) < 0) {
if (err_msg) {
connection_write_str_to_buf(err_msg, conn);
tor_free(err_msg);
@@ -4568,16 +4692,23 @@ handle_control_add_onion(control_connection_t *conn,
}
tor_assert(!err_msg);
+ /* Hidden service version 3 don't have client authentication support so if
+ * ClientAuth was given, send back an error. */
+ if (hs_version == HS_VERSION_THREE && auth_clients) {
+ connection_printf_to_buf(conn, "513 ClientAuth not supported\r\n");
+ goto out;
+ }
+
/* Create the HS, using private key pk, client authentication auth_type,
* the list of auth_clients, and port config port_cfg.
* rend_service_add_ephemeral() will take ownership of pk and port_cfg,
* regardless of success/failure.
*/
char *service_id = NULL;
- int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams,
- max_streams_close_circuit,
- auth_type, auth_clients,
- &service_id);
+ int ret = add_onion_helper_add_service(hs_version, &pk, port_cfgs,
+ max_streams,
+ max_streams_close_circuit, auth_type,
+ auth_clients, &service_id);
port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
auth_clients = NULL; /* so is auth_clients */
switch (ret) {
@@ -4670,9 +4801,10 @@ handle_control_add_onion(control_connection_t *conn,
* Note: The error messages returned are deliberately vague to avoid echoing
* key material.
*/
-STATIC crypto_pk_t *
+STATIC int
add_onion_helper_keyarg(const char *arg, int discard_pk,
const char **key_new_alg_out, char **key_new_blob_out,
+ add_onion_secret_key_t *decoded_key, int *hs_version,
char **err_msg_out)
{
smartlist_t *key_args = smartlist_new();
@@ -4680,7 +4812,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
const char *key_new_alg = NULL;
char *key_new_blob = NULL;
char *err_msg = NULL;
- int ok = 0;
+ int ret = -1;
smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0);
if (smartlist_len(key_args) != 2) {
@@ -4692,6 +4824,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
static const char *key_type_new = "NEW";
static const char *key_type_best = "BEST";
static const char *key_type_rsa1024 = "RSA1024";
+ static const char *key_type_ed25519_v3 = "ED25519-V3";
const char *key_type = smartlist_get(key_args, 0);
const char *key_blob = smartlist_get(key_args, 1);
@@ -4704,9 +4837,23 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
goto err;
}
if (crypto_pk_num_bits(pk) != PK_BYTES*8) {
+ crypto_pk_free(pk);
err_msg = tor_strdup("512 Invalid RSA key size\r\n");
goto err;
}
+ decoded_key->v2 = pk;
+ *hs_version = HS_VERSION_TWO;
+ } else if (!strcasecmp(key_type_ed25519_v3, key_type)) {
+ /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */
+ ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
+ if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob,
+ strlen(key_blob)) != sizeof(sk->seckey)) {
+ tor_free(sk);
+ err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n");
+ goto err;
+ }
+ decoded_key->v3 = sk;
+ *hs_version = HS_VERSION_THREE;
} else if (!strcasecmp(key_type_new, key_type)) {
/* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */
if (!strcasecmp(key_type_rsa1024, key_blob) ||
@@ -4720,12 +4867,38 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
}
if (!discard_pk) {
if (crypto_pk_base64_encode(pk, &key_new_blob)) {
+ crypto_pk_free(pk);
tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
key_type_rsa1024);
goto err;
}
key_new_alg = key_type_rsa1024;
}
+ decoded_key->v2 = pk;
+ *hs_version = HS_VERSION_TWO;
+ } else if (!strcasecmp(key_type_ed25519_v3, key_blob)) {
+ ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
+ if (ed25519_secret_key_generate(sk, 1) < 0) {
+ tor_free(sk);
+ tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n",
+ key_type_ed25519_v3);
+ goto err;
+ }
+ if (!discard_pk) {
+ ssize_t len = base64_encode_size(sizeof(sk->seckey), 0) + 1;
+ key_new_blob = tor_malloc_zero(len);
+ if (base64_encode(key_new_blob, len, (const char *) sk->seckey,
+ sizeof(sk->seckey), 0) != (len - 1)) {
+ tor_free(sk);
+ tor_free(key_new_blob);
+ tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
+ key_type_ed25519_v3);
+ goto err;
+ }
+ key_new_alg = key_type_ed25519_v3;
+ }
+ decoded_key->v3 = sk;
+ *hs_version = HS_VERSION_THREE;
} else {
err_msg = tor_strdup("513 Invalid key type\r\n");
goto err;
@@ -4736,8 +4909,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
}
/* Succeded in loading or generating a private key. */
- tor_assert(pk);
- ok = 1;
+ ret = 0;
err:
SMARTLIST_FOREACH(key_args, char *, cp, {
@@ -4746,10 +4918,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
});
smartlist_free(key_args);
- if (!ok) {
- crypto_pk_free(pk);
- pk = NULL;
- }
if (err_msg_out) {
*err_msg_out = err_msg;
} else {
@@ -4758,7 +4926,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
*key_new_alg_out = key_new_alg;
*key_new_blob_out = key_new_blob;
- return pk;
+ return ret;
}
/** Helper function to handle parsing a ClientAuth argument to the
@@ -4827,6 +4995,7 @@ handle_control_del_onion(control_connection_t *conn,
uint32_t len,
const char *body)
{
+ int hs_version = 0;
smartlist_t *args;
(void) len; /* body is nul-terminated; it's safe to ignore the length */
args = getargs_helper("DEL_ONION", conn, body, 1, 1);
@@ -4834,7 +5003,11 @@ handle_control_del_onion(control_connection_t *conn,
return 0;
const char *service_id = smartlist_get(args, 0);
- if (!rend_valid_v2_service_id(service_id)) {
+ if (rend_valid_v2_service_id(service_id)) {
+ hs_version = HS_VERSION_TWO;
+ } else if (hs_address_is_valid(service_id)) {
+ hs_version = HS_VERSION_THREE;
+ } else {
connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n");
goto out;
}
@@ -4861,8 +5034,20 @@ handle_control_del_onion(control_connection_t *conn,
if (onion_services == NULL) {
connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n");
} else {
- int ret = rend_service_del_ephemeral(service_id);
- if (ret) {
+ int ret = -1;
+ switch (hs_version) {
+ case HS_VERSION_TWO:
+ ret = rend_service_del_ephemeral(service_id);
+ break;
+ case HS_VERSION_THREE:
+ ret = hs_service_del_ephemeral(service_id);
+ break;
+ default:
+ /* The ret value will be -1 thus hitting the warning below. This should
+ * never happen because of the check at the start of the function. */
+ break;
+ }
+ if (ret < 0) {
/* This should *NEVER* fail, since the service is on either the
* per-control connection list, or the global one.
*/
@@ -4932,9 +5117,16 @@ connection_control_closed(control_connection_t *conn)
* The list and it's contents are scrubbed/freed in connection_free_.
*/
if (conn->ephemeral_onion_services) {
- SMARTLIST_FOREACH(conn->ephemeral_onion_services, char *, cp, {
- rend_service_del_ephemeral(cp);
- });
+ SMARTLIST_FOREACH_BEGIN(conn->ephemeral_onion_services, char *, cp) {
+ if (rend_valid_v2_service_id(cp)) {
+ rend_service_del_ephemeral(cp);
+ } else if (hs_address_is_valid(cp)) {
+ hs_service_del_ephemeral(cp);
+ } else {
+ /* An invalid .onion in our list should NEVER happen */
+ tor_fragile_assert();
+ }
+ } SMARTLIST_FOREACH_END(cp);
}
if (conn->is_owning_control_connection) {
@@ -7012,27 +7204,33 @@ rend_hsaddress_str_or_unknown(const char *onion_address)
* <b>rend_query</b> is used to fetch requested onion address and auth type.
* <b>hs_dir</b> is the description of contacting hs directory.
* <b>desc_id_base32</b> is the ID of requested hs descriptor.
+ * <b>hsdir_index</b> is the HSDir fetch index value for v3, an hex string.
*/
void
-control_event_hs_descriptor_requested(const rend_data_t *rend_query,
+control_event_hs_descriptor_requested(const char *onion_address,
+ rend_auth_type_t auth_type,
const char *id_digest,
- const char *desc_id_base32)
+ const char *desc_id,
+ const char *hsdir_index)
{
- if (!id_digest || !rend_query || !desc_id_base32) {
- log_warn(LD_BUG, "Called with rend_query==%p, "
- "id_digest==%p, desc_id_base32==%p",
- rend_query, id_digest, desc_id_base32);
+ char *hsdir_index_field = NULL;
+
+ if (BUG(!id_digest || !desc_id)) {
return;
}
+ if (hsdir_index) {
+ tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index);
+ }
+
send_control_event(EVENT_HS_DESC,
- "650 HS_DESC REQUESTED %s %s %s %s\r\n",
- rend_hsaddress_str_or_unknown(
- rend_data_get_address(rend_query)),
- rend_auth_type_to_string(
- TO_REND_DATA_V2(rend_query)->auth_type),
+ "650 HS_DESC REQUESTED %s %s %s %s%s\r\n",
+ rend_hsaddress_str_or_unknown(onion_address),
+ rend_auth_type_to_string(auth_type),
node_describe_longname_by_id(id_digest),
- desc_id_base32);
+ desc_id,
+ hsdir_index_field ? hsdir_index_field : "");
+ tor_free(hsdir_index_field);
}
/** For an HS descriptor query <b>rend_data</b>, using the
@@ -7081,89 +7279,87 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
/** send HS_DESC CREATED event when a local service generates a descriptor.
*
- * <b>service_id</b> is the descriptor onion address.
- * <b>desc_id_base32</b> is the descriptor ID.
- * <b>replica</b> is the the descriptor replica number.
+ * <b>onion_address</b> is service address.
+ * <b>desc_id</b> is the descriptor ID.
+ * <b>replica</b> is the the descriptor replica number. If it is negative, it
+ * is ignored.
*/
void
-control_event_hs_descriptor_created(const char *service_id,
- const char *desc_id_base32,
+control_event_hs_descriptor_created(const char *onion_address,
+ const char *desc_id,
int replica)
{
- if (!service_id || !desc_id_base32) {
- log_warn(LD_BUG, "Called with service_digest==%p, "
- "desc_id_base32==%p", service_id, desc_id_base32);
+ char *replica_field = NULL;
+
+ if (BUG(!onion_address || !desc_id)) {
return;
}
+ if (replica >= 0) {
+ tor_asprintf(&replica_field, " REPLICA=%d", replica);
+ }
+
send_control_event(EVENT_HS_DESC,
- "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s "
- "REPLICA=%d\r\n",
- service_id,
- desc_id_base32,
- replica);
+ "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s%s\r\n",
+ onion_address, desc_id,
+ replica_field ? replica_field : "");
+ tor_free(replica_field);
}
/** send HS_DESC upload event.
*
- * <b>service_id</b> is the descriptor onion address.
+ * <b>onion_address</b> is service address.
* <b>hs_dir</b> is the description of contacting hs directory.
- * <b>desc_id_base32</b> is the ID of requested hs descriptor.
+ * <b>desc_id</b> is the ID of requested hs descriptor.
*/
void
-control_event_hs_descriptor_upload(const char *service_id,
+control_event_hs_descriptor_upload(const char *onion_address,
const char *id_digest,
- const char *desc_id_base32)
+ const char *desc_id,
+ const char *hsdir_index)
{
- if (!service_id || !id_digest || !desc_id_base32) {
- log_warn(LD_BUG, "Called with service_digest==%p, "
- "desc_id_base32==%p, id_digest==%p", service_id,
- desc_id_base32, id_digest);
+ char *hsdir_index_field = NULL;
+
+ if (BUG(!onion_address || !id_digest || !desc_id)) {
return;
}
+ if (hsdir_index) {
+ tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index);
+ }
+
send_control_event(EVENT_HS_DESC,
- "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n",
- service_id,
+ "650 HS_DESC UPLOAD %s UNKNOWN %s %s%s\r\n",
+ onion_address,
node_describe_longname_by_id(id_digest),
- desc_id_base32);
+ desc_id,
+ hsdir_index_field ? hsdir_index_field : "");
+ tor_free(hsdir_index_field);
}
/** send HS_DESC event after got response from hs directory.
*
* NOTE: this is an internal function used by following functions:
- * control_event_hs_descriptor_received
- * control_event_hs_descriptor_failed
+ * control_event_hsv2_descriptor_received
+ * control_event_hsv2_descriptor_failed
+ * control_event_hsv3_descriptor_failed
*
* So do not call this function directly.
*/
-void
-control_event_hs_descriptor_receive_end(const char *action,
- const char *onion_address,
- const rend_data_t *rend_data,
- const char *id_digest,
- const char *reason)
+static void
+event_hs_descriptor_receive_end(const char *action,
+ const char *onion_address,
+ const char *desc_id,
+ rend_auth_type_t auth_type,
+ const char *hsdir_id_digest,
+ const char *reason)
{
- char *desc_id_field = NULL;
char *reason_field = NULL;
- char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
- const char *desc_id = NULL;
- if (!action || !rend_data || !onion_address) {
- log_warn(LD_BUG, "Called with action==%p, rend_data==%p, "
- "onion_address==%p", action, rend_data, onion_address);
+ if (BUG(!action || !onion_address)) {
return;
}
- desc_id = get_desc_id_from_query(rend_data, id_digest);
- if (desc_id != NULL) {
- /* Set the descriptor ID digest to base32 so we can send it. */
- base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
- DIGEST_LEN);
- /* Extra whitespace is needed before the value. */
- tor_asprintf(&desc_id_field, " %s", desc_id_base32);
- }
-
if (reason) {
tor_asprintf(&reason_field, " REASON=%s", reason);
}
@@ -7172,14 +7368,13 @@ control_event_hs_descriptor_receive_end(const char *action,
"650 HS_DESC %s %s %s %s%s%s\r\n",
action,
rend_hsaddress_str_or_unknown(onion_address),
- rend_auth_type_to_string(
- TO_REND_DATA_V2(rend_data)->auth_type),
- id_digest ?
- node_describe_longname_by_id(id_digest) : "UNKNOWN",
- desc_id_field ? desc_id_field : "",
+ rend_auth_type_to_string(auth_type),
+ hsdir_id_digest ?
+ node_describe_longname_by_id(hsdir_id_digest) :
+ "UNKNOWN",
+ desc_id ? desc_id : "",
reason_field ? reason_field : "");
- tor_free(desc_id_field);
tor_free(reason_field);
}
@@ -7199,9 +7394,7 @@ control_event_hs_descriptor_upload_end(const char *action,
{
char *reason_field = NULL;
- if (!action || !id_digest) {
- log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action,
- id_digest);
+ if (BUG(!action || !id_digest)) {
return;
}
@@ -7224,17 +7417,54 @@ control_event_hs_descriptor_upload_end(const char *action,
* called when we successfully received a hidden service descriptor.
*/
void
-control_event_hs_descriptor_received(const char *onion_address,
- const rend_data_t *rend_data,
- const char *id_digest)
+control_event_hsv2_descriptor_received(const char *onion_address,
+ const rend_data_t *rend_data,
+ const char *hsdir_id_digest)
{
- if (!rend_data || !id_digest || !onion_address) {
- log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p, "
- "onion_address==%p", rend_data, id_digest, onion_address);
+ char *desc_id_field = NULL;
+ const char *desc_id;
+
+ if (BUG(!rend_data || !hsdir_id_digest || !onion_address)) {
return;
}
- control_event_hs_descriptor_receive_end("RECEIVED", onion_address,
- rend_data, id_digest, NULL);
+
+ desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
+ if (desc_id != NULL) {
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ /* Set the descriptor ID digest to base32 so we can send it. */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
+ /* Extra whitespace is needed before the value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id_base32);
+ }
+
+ event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
+ TO_REND_DATA_V2(rend_data)->auth_type,
+ hsdir_id_digest, NULL);
+ tor_free(desc_id_field);
+}
+
+/* Send HS_DESC RECEIVED event
+ *
+ * Called when we successfully received a hidden service descriptor. */
+void
+control_event_hsv3_descriptor_received(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest)
+{
+ char *desc_id_field = NULL;
+
+ if (BUG(!onion_address || !desc_id || !hsdir_id_digest)) {
+ return;
+ }
+
+ /* Because DescriptorID is an optional positional value, we need to add a
+ * whitespace before in order to not be next to the HsDir value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id);
+
+ event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
+ REND_NO_AUTH, hsdir_id_digest, NULL);
+ tor_free(desc_id_field);
}
/** send HS_DESC UPLOADED event
@@ -7245,9 +7475,7 @@ void
control_event_hs_descriptor_uploaded(const char *id_digest,
const char *onion_address)
{
- if (!id_digest) {
- log_warn(LD_BUG, "Called with id_digest==%p",
- id_digest);
+ if (BUG(!id_digest)) {
return;
}
@@ -7261,17 +7489,58 @@ control_event_hs_descriptor_uploaded(const char *id_digest,
* add it to REASON= field.
*/
void
-control_event_hs_descriptor_failed(const rend_data_t *rend_data,
- const char *id_digest,
- const char *reason)
+control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
+ const char *hsdir_id_digest,
+ const char *reason)
{
- if (!rend_data) {
- log_warn(LD_BUG, "Called with rend_data==%p", rend_data);
+ char *desc_id_field = NULL;
+ const char *desc_id;
+
+ if (BUG(!rend_data)) {
return;
}
- control_event_hs_descriptor_receive_end("FAILED",
- rend_data_get_address(rend_data),
- rend_data, id_digest, reason);
+
+ desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
+ if (desc_id != NULL) {
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ /* Set the descriptor ID digest to base32 so we can send it. */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
+ /* Extra whitespace is needed before the value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id_base32);
+ }
+
+ event_hs_descriptor_receive_end("FAILED", rend_data_get_address(rend_data),
+ desc_id_field,
+ TO_REND_DATA_V2(rend_data)->auth_type,
+ hsdir_id_digest, reason);
+ tor_free(desc_id_field);
+}
+
+/** Send HS_DESC event to inform controller that the query to
+ * <b>onion_address</b> failed to retrieve hidden service descriptor
+ * <b>desc_id</b> from directory identified by <b>hsdir_id_digest</b>. If
+ * NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL, add it to REASON=
+ * field. */
+void
+control_event_hsv3_descriptor_failed(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest,
+ const char *reason)
+{
+ char *desc_id_field = NULL;
+
+ if (BUG(!onion_address || !desc_id || !reason)) {
+ return;
+ }
+
+ /* Because DescriptorID is an optional positional value, we need to add a
+ * whitespace before in order to not be next to the HsDir value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id);
+
+ event_hs_descriptor_receive_end("FAILED", onion_address, desc_id_field,
+ REND_NO_AUTH, hsdir_id_digest, reason);
+ tor_free(desc_id_field);
}
/** Send HS_DESC_CONTENT event after completion of a successful fetch from hs
@@ -7321,9 +7590,7 @@ control_event_hs_descriptor_upload_failed(const char *id_digest,
const char *onion_address,
const char *reason)
{
- if (!id_digest) {
- log_warn(LD_BUG, "Called with id_digest==%p",
- id_digest);
+ if (BUG(!id_digest)) {
return;
}
control_event_hs_descriptor_upload_end("FAILED", onion_address,
diff --git a/src/or/control.h b/src/or/control.h
index 7ec182cb78..28ffeaed86 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -115,32 +115,39 @@ void control_event_transport_launched(const char *mode,
tor_addr_t *addr, uint16_t port);
const char *rend_auth_type_to_string(rend_auth_type_t auth_type);
MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
-void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
- const char *desc_id_base32,
- const char *hs_dir);
-void control_event_hs_descriptor_created(const char *service_id,
- const char *desc_id_base32,
+void control_event_hs_descriptor_requested(const char *onion_address,
+ rend_auth_type_t auth_type,
+ const char *id_digest,
+ const char *desc_id,
+ const char *hsdir_index);
+void control_event_hs_descriptor_created(const char *onion_address,
+ const char *desc_id,
int replica);
-void control_event_hs_descriptor_upload(const char *service_id,
- const char *desc_id_base32,
- const char *hs_dir);
-void control_event_hs_descriptor_receive_end(const char *action,
- const char *onion_address,
- const rend_data_t *rend_data,
- const char *id_digest,
- const char *reason);
+void control_event_hs_descriptor_upload(const char *onion_address,
+ const char *desc_id,
+ const char *hs_dir,
+ const char *hsdir_index);
void control_event_hs_descriptor_upload_end(const char *action,
const char *onion_address,
const char *hs_dir,
const char *reason);
-void control_event_hs_descriptor_received(const char *onion_address,
- const rend_data_t *rend_data,
- const char *id_digest);
void control_event_hs_descriptor_uploaded(const char *hs_dir,
const char *onion_address);
-void control_event_hs_descriptor_failed(const rend_data_t *rend_data,
- const char *id_digest,
- const char *reason);
+/* Hidden service v2 HS_DESC specific. */
+void control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
+ const char *id_digest,
+ const char *reason);
+void control_event_hsv2_descriptor_received(const char *onion_address,
+ const rend_data_t *rend_data,
+ const char *id_digest);
+/* Hidden service v3 HS_DESC specific. */
+void control_event_hsv3_descriptor_failed(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest,
+ const char *reason);
+void control_event_hsv3_descriptor_received(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest);
void control_event_hs_descriptor_upload_failed(const char *hs_dir,
const char *onion_address,
const char *reason);
@@ -256,10 +263,22 @@ void format_cell_stats(char **event_string, circuit_t *circ,
cell_stats_t *cell_stats);
STATIC char *get_bw_samples(void);
-STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk,
- const char **key_new_alg_out,
- char **key_new_blob_out,
- char **err_msg_out);
+/* ADD_ONION secret key to create an ephemeral service. The command supports
+ * multiple versions so this union stores the key and passes it to the HS
+ * subsystem depending on the requested version. */
+typedef union add_onion_secret_key_t {
+ /* Hidden service v2 secret key. */
+ crypto_pk_t *v2;
+ /* Hidden service v3 secret key. */
+ ed25519_secret_key_t *v3;
+} add_onion_secret_key_t;
+
+STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk,
+ const char **key_new_alg_out,
+ char **key_new_blob_out,
+ add_onion_secret_key_t *decoded_key,
+ int *hs_version, char **err_msg_out);
+
STATIC rend_authorized_client_t *
add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out);
diff --git a/src/or/directory.c b/src/or/directory.c
index e7a5da8139..1a7c016c3e 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -25,6 +25,7 @@
#include "geoip.h"
#include "hs_cache.h"
#include "hs_common.h"
+#include "hs_control.h"
#include "hs_client.h"
#include "main.h"
#include "microdesc.h"
@@ -116,7 +117,8 @@ static void dir_routerdesc_download_failed(smartlist_t *failed,
int was_extrainfo,
int was_descriptor_digests);
static void dir_microdesc_download_failed(smartlist_t *failed,
- int status_code);
+ int status_code,
+ const char *dir_id);
static int client_likes_consensus(const struct consensus_cache_entry_t *ent,
const char *want_url);
@@ -469,7 +471,7 @@ directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags,
log_warn(LD_BUG, "Called when we have UseBridges set.");
if (should_use_directory_guards(options)) {
- const node_t *node = guards_choose_dirguard(guard_state_out);
+ const node_t *node = guards_choose_dirguard(dir_purpose, guard_state_out);
if (node)
rs = node->rs;
} else {
@@ -603,7 +605,7 @@ directory_get_from_dirserver,(
* sort of dir fetch we'll be doing, so it won't return a bridge
* that can't answer our question.
*/
- const node_t *node = guards_choose_dirguard(&guard_state);
+ const node_t *node = guards_choose_dirguard(dir_purpose, &guard_state);
if (node && node->ri) {
/* every bridge has a routerinfo. */
routerinfo_t *ri = node->ri;
@@ -1011,48 +1013,6 @@ directory_must_use_begindir(const or_options_t *options)
return !public_server_mode(options);
}
-struct directory_request_t {
- /**
- * These fields specify which directory we're contacting. Routerstatus,
- * if present, overrides the other fields.
- *
- * @{ */
- tor_addr_port_t or_addr_port;
- tor_addr_port_t dir_addr_port;
- char digest[DIGEST_LEN];
-
- const routerstatus_t *routerstatus;
- /** @} */
- /** One of DIR_PURPOSE_* other than DIR_PURPOSE_SERVER. Describes what
- * kind of operation we'll be doing (upload/download), and of what kind
- * of document. */
- uint8_t dir_purpose;
- /** One of ROUTER_PURPOSE_*; used for uploads and downloads of routerinfo
- * and extrainfo docs. */
- uint8_t router_purpose;
- /** Enum: determines whether to anonymize, and whether to use dirport or
- * orport. */
- dir_indirection_t indirection;
- /** Alias to the variable part of the URL for this request */
- const char *resource;
- /** Alias to the payload to upload (if any) */
- const char *payload;
- /** Number of bytes to upload from payload</b> */
- size_t payload_len;
- /** Value to send in an if-modified-since header, or 0 for none. */
- time_t if_modified_since;
- /** Hidden-service-specific information v2. */
- const rend_data_t *rend_query;
- /** Extra headers to append to the request */
- config_line_t *additional_headers;
- /** Hidden-service-specific information for v3+. */
- const hs_ident_dir_conn_t *hs_ident;
- /** Used internally to directory.c: gets informed when the attempt to
- * connect to the directory succeeds or fails, if that attempt bears on the
- * directory's usability as a directory guard. */
- circuit_guard_state_t *guard_state;
-};
-
/** Evaluate the situation and decide if we should use an encrypted
* "begindir-style" connection for this directory request.
* 0) If there is no DirPort, yes.
@@ -2245,8 +2205,6 @@ static int handle_response_fetch_detached_signatures(dir_connection_t *,
const response_handler_args_t *);
static int handle_response_fetch_desc(dir_connection_t *,
const response_handler_args_t *);
-static int handle_response_fetch_microdesc(dir_connection_t *,
- const response_handler_args_t *);
static int handle_response_upload_dir(dir_connection_t *,
const response_handler_args_t *);
static int handle_response_upload_vote(dir_connection_t *,
@@ -2887,7 +2845,7 @@ handle_response_fetch_desc(dir_connection_t *conn,
conn->router_purpose,
conn->base_.address)) {
time_t now = approx_time();
- directory_info_has_arrived(now, 0, 0);
+ directory_info_has_arrived(now, 0, 1);
}
}
}
@@ -2914,7 +2872,7 @@ handle_response_fetch_desc(dir_connection_t *conn,
* Handler function: processes a response to a request for a group of
* microdescriptors
**/
-static int
+STATIC int
handle_response_fetch_microdesc(dir_connection_t *conn,
const response_handler_args_t *args)
{
@@ -2931,6 +2889,7 @@ handle_response_fetch_microdesc(dir_connection_t *conn,
conn->base_.port);
tor_assert(conn->requested_resource &&
!strcmpstart(conn->requested_resource, "d/"));
+ tor_assert_nonfatal(!tor_mem_is_zero(conn->identity_digest, DIGEST_LEN));
which = smartlist_new();
dir_split_resource_into_fingerprints(conn->requested_resource+2,
which, NULL,
@@ -2941,7 +2900,7 @@ handle_response_fetch_microdesc(dir_connection_t *conn,
"soon.",
status_code, escaped(reason), conn->base_.address,
(int)conn->base_.port, conn->requested_resource);
- dir_microdesc_download_failed(which, status_code);
+ dir_microdesc_download_failed(which, status_code, conn->identity_digest);
SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
smartlist_free(which);
return 0;
@@ -2953,7 +2912,7 @@ handle_response_fetch_microdesc(dir_connection_t *conn,
now, which);
if (smartlist_len(which)) {
/* Mark remaining ones as failed. */
- dir_microdesc_download_failed(which, status_code);
+ dir_microdesc_download_failed(which, status_code, conn->identity_digest);
}
if (mds && smartlist_len(mds)) {
control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
@@ -3132,10 +3091,19 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
/* We got something: Try storing it in the cache. */
if (hs_cache_store_as_client(body, &conn->hs_ident->identity_pk) < 0) {
log_warn(LD_REND, "Failed to store hidden service descriptor");
+ /* Fire control port FAILED event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "BAD_DESC");
+ hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
+ NULL);
} else {
log_info(LD_REND, "Stored hidden service descriptor successfully.");
TO_CONN(conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC;
hs_client_desc_has_arrived(conn->hs_ident);
+ /* Fire control port RECEIVED event. */
+ hs_control_desc_event_received(conn->hs_ident, conn->identity_digest);
+ hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
+ body);
}
break;
case 404:
@@ -3143,13 +3111,22 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
* tries to clean this conn up. */
log_info(LD_REND, "Fetching hidden service v3 descriptor not found: "
"Retrying at another directory.");
- /* TODO: Inform the control port */
+ /* Fire control port FAILED event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "NOT_FOUND");
+ hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
+ NULL);
break;
case 400:
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
"http status 400 (%s). Dirserver didn't like our "
"query? Retrying at another directory.",
escaped(reason));
+ /* Fire control port FAILED event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "QUERY_REJECTED");
+ hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
+ NULL);
break;
default:
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
@@ -3157,6 +3134,11 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
"'%s:%d'. Retrying at another directory.",
status_code, escaped(reason), TO_CONN(conn)->address,
TO_CONN(conn)->port);
+ /* Fire control port FAILED event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "UNEXPECTED");
+ hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
+ NULL);
break;
}
@@ -3178,9 +3160,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn,
const size_t body_len = args->body_len;
#define SEND_HS_DESC_FAILED_EVENT(reason) \
- (control_event_hs_descriptor_failed(conn->rend_data, \
- conn->identity_digest, \
- reason))
+ (control_event_hsv2_descriptor_failed(conn->rend_data, \
+ conn->identity_digest, \
+ reason))
#define SEND_HS_DESC_FAILED_CONTENT() \
(control_event_hs_descriptor_content( \
rend_data_get_address(conn->rend_data), \
@@ -3215,9 +3197,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn,
/* success. notify pending connections about this. */
log_info(LD_REND, "Successfully fetched v2 rendezvous "
"descriptor.");
- control_event_hs_descriptor_received(service_id,
- conn->rend_data,
- conn->identity_digest);
+ control_event_hsv2_descriptor_received(service_id,
+ conn->rend_data,
+ conn->identity_digest);
control_event_hs_descriptor_content(service_id,
conn->requested_resource,
conn->identity_digest,
@@ -3334,14 +3316,16 @@ handle_response_upload_hsdesc(dir_connection_t *conn,
case 200:
log_info(LD_REND, "Uploading hidden service descriptor: "
"finished with status 200 (%s)", escaped(reason));
- /* XXX: Trigger control event. */
+ hs_control_desc_event_uploaded(conn->hs_ident, conn->identity_digest);
break;
case 400:
- log_warn(LD_REND, "Uploading hidden service descriptor: http "
- "status 400 (%s) response from dirserver "
- "'%s:%d'. Malformed hidden service descriptor?",
- escaped(reason), conn->base_.address, conn->base_.port);
- /* XXX: Trigger control event. */
+ log_fn(LOG_PROTOCOL_WARN, LD_REND,
+ "Uploading hidden service descriptor: http "
+ "status 400 (%s) response from dirserver "
+ "'%s:%d'. Malformed hidden service descriptor?",
+ escaped(reason), conn->base_.address, conn->base_.port);
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "UPLOAD_REJECTED");
break;
default:
log_warn(LD_REND, "Uploading hidden service descriptor: http "
@@ -3349,7 +3333,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn,
"'%s:%d').",
status_code, escaped(reason), conn->base_.address,
conn->base_.port);
- /* XXX: Trigger control event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "UNEXPECTED");
break;
}
@@ -5793,13 +5778,14 @@ dir_routerdesc_download_failed(smartlist_t *failed, int status_code,
* every 10 or 60 seconds (FOO_DESCRIPTOR_RETRY_INTERVAL) in main.c. */
}
-/** Called when a connection to download microdescriptors has failed in whole
- * or in part. <b>failed</b> is a list of every microdesc digest we didn't
- * get. <b>status_code</b> is the http status code we received. Reschedule the
- * microdesc downloads as appropriate. */
+/** Called when a connection to download microdescriptors from relay with
+ * <b>dir_id</b> has failed in whole or in part. <b>failed</b> is a list
+ * of every microdesc digest we didn't get. <b>status_code</b> is the http
+ * status code we received. Reschedule the microdesc downloads as
+ * appropriate. */
static void
dir_microdesc_download_failed(smartlist_t *failed,
- int status_code)
+ int status_code, const char *dir_id)
{
networkstatus_t *consensus
= networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC);
@@ -5810,17 +5796,26 @@ dir_microdesc_download_failed(smartlist_t *failed,
if (! consensus)
return;
+
+ /* We failed to fetch a microdescriptor from 'dir_id', note it down
+ * so that we don't try the same relay next time... */
+ microdesc_note_outdated_dirserver(dir_id);
+
SMARTLIST_FOREACH_BEGIN(failed, const char *, d) {
rs = router_get_mutable_consensus_status_by_descriptor_digest(consensus,d);
if (!rs)
continue;
dls = &rs->dl_status;
if (dls->n_download_failures >=
- get_options()->TestingMicrodescMaxDownloadTries)
+ get_options()->TestingMicrodescMaxDownloadTries) {
continue;
- {
+ }
+
+ { /* Increment the failure count for this md fetch */
char buf[BASE64_DIGEST256_LEN+1];
digest256_to_base64(buf, d);
+ log_info(LD_DIR, "Failed to download md %s from %s",
+ buf, hex_str(dir_id, DIGEST_LEN));
download_status_increment_failure(dls, status_code, buf,
server, now);
}
diff --git a/src/or/directory.h b/src/or/directory.h
index 904bdfae46..3aef600716 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -183,6 +183,48 @@ typedef struct response_handler_args_t {
const char *headers;
} response_handler_args_t;
+struct directory_request_t {
+ /**
+ * These fields specify which directory we're contacting. Routerstatus,
+ * if present, overrides the other fields.
+ *
+ * @{ */
+ tor_addr_port_t or_addr_port;
+ tor_addr_port_t dir_addr_port;
+ char digest[DIGEST_LEN];
+
+ const routerstatus_t *routerstatus;
+ /** @} */
+ /** One of DIR_PURPOSE_* other than DIR_PURPOSE_SERVER. Describes what
+ * kind of operation we'll be doing (upload/download), and of what kind
+ * of document. */
+ uint8_t dir_purpose;
+ /** One of ROUTER_PURPOSE_*; used for uploads and downloads of routerinfo
+ * and extrainfo docs. */
+ uint8_t router_purpose;
+ /** Enum: determines whether to anonymize, and whether to use dirport or
+ * orport. */
+ dir_indirection_t indirection;
+ /** Alias to the variable part of the URL for this request */
+ const char *resource;
+ /** Alias to the payload to upload (if any) */
+ const char *payload;
+ /** Number of bytes to upload from payload</b> */
+ size_t payload_len;
+ /** Value to send in an if-modified-since header, or 0 for none. */
+ time_t if_modified_since;
+ /** Hidden-service-specific information v2. */
+ const rend_data_t *rend_query;
+ /** Extra headers to append to the request */
+ config_line_t *additional_headers;
+ /** Hidden-service-specific information for v3+. */
+ const hs_ident_dir_conn_t *hs_ident;
+ /** Used internally to directory.c: gets informed when the attempt to
+ * connect to the directory succeeds or fails, if that attempt bears on the
+ * directory's usability as a directory guard. */
+ struct circuit_guard_state_t *guard_state;
+};
+
struct get_handler_args_t;
STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn,
const struct get_handler_args_t *args);
@@ -193,6 +235,8 @@ STATIC void warn_disallowed_anonymous_compression_method(compress_method_t);
STATIC int handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
const response_handler_args_t *args);
+STATIC int handle_response_fetch_microdesc(dir_connection_t *conn,
+ const response_handler_args_t *args);
STATIC int handle_response_fetch_consensus(dir_connection_t *conn,
const response_handler_args_t *args);
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 432fe6ae2b..ce737d0b1d 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -1903,21 +1903,28 @@ version_from_platform(const char *platform)
/** Helper: write the router-status information in <b>rs</b> into a newly
* allocated character buffer. Use the same format as in network-status
* documents. If <b>version</b> is non-NULL, add a "v" line for the platform.
+ *
+ * consensus_method is the current consensus method when format is
+ * NS_V3_CONSENSUS or NS_V3_CONSENSUS_MICRODESC. It is ignored for other
+ * formats: pass ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD.
+ *
* Return 0 on success, -1 on failure.
*
* The format argument has one of the following values:
* NS_V2 - Output an entry suitable for a V2 NS opinion document
* NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry
+ * for consensus_method.
* NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc
- * consensus entry.
+ * consensus entry for consensus_method.
* NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present,
* it contains additional information for the vote.
- * NS_CONTROL_PORT - Output a NS document for the control port
+ * NS_CONTROL_PORT - Output a NS document for the control port.
*/
char *
routerstatus_format_entry(const routerstatus_t *rs, const char *version,
const char *protocols,
routerstatus_format_type_t format,
+ int consensus_method,
const vote_routerstatus_t *vrs)
{
char *summary;
@@ -1948,8 +1955,10 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
* networkstatus_type_t values, with an additional control port value
* added -MP */
- /* V3 microdesc consensuses don't have "a" lines. */
- if (format == NS_V3_CONSENSUS_MICRODESC)
+ /* V3 microdesc consensuses only have "a" lines in later consensus methods
+ */
+ if (format == NS_V3_CONSENSUS_MICRODESC &&
+ consensus_method < MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS)
goto done;
/* Possible "a" line. At most one for now. */
@@ -1958,7 +1967,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport));
}
- if (format == NS_V3_CONSENSUS)
+ if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC)
goto done;
smartlist_add_asprintf(chunks,
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index 46967a6cb2..9dc239f213 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -150,6 +150,7 @@ char *routerstatus_format_entry(
const char *version,
const char *protocols,
routerstatus_format_type_t format,
+ int consensus_method,
const vote_routerstatus_t *vrs);
void dirserv_free_all(void);
void cached_dir_decref(cached_dir_t *d);
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index ce82a5ef4a..24a6ed42be 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -278,7 +278,9 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
vote_microdesc_hash_t *h;
rsf = routerstatus_format_entry(&vrs->status,
vrs->version, vrs->protocols,
- NS_V3_VOTE, vrs);
+ NS_V3_VOTE,
+ ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD,
+ vrs);
if (rsf)
smartlist_add(chunks, rsf);
@@ -519,7 +521,7 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method,
/* compare_vote_rs_() sorts the items by identity digest (all the same),
* then by SD digest. That way, if we have a tie that the published_on
- * date cannot tie, we use the descriptor with the smaller digest.
+ * date cannot break, we use the descriptor with the smaller digest.
*/
smartlist_sort(votes, compare_vote_rs_);
SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) {
@@ -795,6 +797,9 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
output = smartlist_new();
SMARTLIST_FOREACH_BEGIN(param_list, const char *, param) {
+ /* resolve spurious clang shallow analysis null pointer errors */
+ tor_assert(param);
+
const char *next_param;
int ok=0;
eq = strchr(param, '=');
@@ -807,8 +812,7 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
next_param = NULL;
else
next_param = smartlist_get(param_list, param_sl_idx+1);
- /* resolve spurious clang shallow analysis null pointer errors */
- tor_assert(param);
+
if (!next_param || strncmp(next_param, param, cur_param_len)) {
/* We've reached the end of a series. */
/* Make sure enough authorities voted on this param, unless the
@@ -1315,8 +1319,9 @@ compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes)
/** Given a list of vote networkstatus_t in <b>votes</b>, our public
* authority <b>identity_key</b>, our private authority <b>signing_key</b>,
* and the number of <b>total_authorities</b> that we believe exist in our
- * voting quorum, generate the text of a new v3 consensus vote, and return the
- * value in a newly allocated string.
+ * voting quorum, generate the text of a new v3 consensus or microdescriptor
+ * consensus (depending on <b>flavor</b>), and return the value in a newly
+ * allocated string.
*
* Note: this function DOES NOT check whether the votes are from
* recognized authorities. (dirvote_add_vote does that.)
@@ -2099,7 +2104,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
char *buf;
/* Okay!! Now we can write the descriptor... */
/* First line goes into "buf". */
- buf = routerstatus_format_entry(&rs_out, NULL, NULL, rs_format, NULL);
+ buf = routerstatus_format_entry(&rs_out, NULL, NULL,
+ rs_format, consensus_method, NULL);
if (buf)
smartlist_add(chunks, buf);
}
@@ -3829,7 +3835,10 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
}
+ /* We originally put a lines in the micrdescriptors, but then we worked out
+ * that we needed them in the microdesc consensus. See #20916. */
if (consensus_method >= MIN_METHOD_FOR_A_LINES &&
+ consensus_method < MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC &&
!tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport)
smartlist_add_asprintf(chunks, "a %s\n",
fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport));
@@ -3938,7 +3947,9 @@ static const struct consensus_method_range_t {
{MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1},
{MIN_METHOD_FOR_NTOR_KEY, MIN_METHOD_FOR_ID_HASH_IN_MD - 1},
{MIN_METHOD_FOR_ID_HASH_IN_MD, MIN_METHOD_FOR_ED25519_ID_IN_MD - 1},
- {MIN_METHOD_FOR_ED25519_ID_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD},
+ {MIN_METHOD_FOR_ED25519_ID_IN_MD,
+ MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC - 1},
+ {MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC, MAX_SUPPORTED_CONSENSUS_METHOD},
{-1, -1}
};
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index 72a35fea6d..c9cb527788 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -51,11 +51,15 @@
#define MIN_VOTE_INTERVAL_TESTING_INITIAL \
((MIN_VOTE_SECONDS_TESTING)+(MIN_DIST_SECONDS_TESTING)+1)
+/* A placeholder for routerstatus_format_entry() when the consensus method
+ * argument is not applicable. */
+#define ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD 0
+
/** The lowest consensus method that we currently support. */
#define MIN_SUPPORTED_CONSENSUS_METHOD 13
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 26
+#define MAX_SUPPORTED_CONSENSUS_METHOD 28
/** Lowest consensus method where microdesc consensuses omit any entry
* with no microdesc. */
@@ -115,6 +119,14 @@
* instead of 0. See #14881 */
#define MIN_METHOD_FOR_INIT_BW_WEIGHTS_ONE 26
+/** Lowest consensus method where the microdesc consensus contains relay IPv6
+ * addresses. See #23826 and #20916. */
+#define MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS 27
+
+/** Lowest consensus method where microdescriptors do not contain relay IPv6
+ * addresses. See #23828 and #20916. */
+#define MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC 28
+
/** Default bandwidth to clip unmeasured bandwidths to using method >=
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
* get confused with the above macros.) */
diff --git a/src/or/dns.c b/src/or/dns.c
index c992c8c358..f140051e81 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -1578,10 +1578,11 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
escaped_safe_str(hostname));
tor_free(escaped_address);
} else if (count) {
- log_warn(LD_EXIT, "eventdns returned only non-IPv4 answers for %s.",
+ log_info(LD_EXIT, "eventdns returned only unrecognized answer types "
+ " for %s.",
escaped_safe_str(string_address));
} else {
- log_warn(LD_BUG, "eventdns returned no addresses or error for %s!",
+ log_info(LD_EXIT, "eventdns returned no addresses or error for %s.",
escaped_safe_str(string_address));
}
}
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 76a8f591b9..f9438361f0 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -740,7 +740,8 @@ node_is_possible_guard(const node_t *node)
node->is_stable &&
node->is_fast &&
node->is_valid &&
- node_is_dir(node));
+ node_is_dir(node) &&
+ !router_digest_is_me(node->identity));
}
/**
@@ -966,7 +967,7 @@ entry_guard_learned_bridge_identity(const tor_addr_port_t *addrport,
* violate it.
*/
STATIC int
-num_reachable_filtered_guards(guard_selection_t *gs,
+num_reachable_filtered_guards(const guard_selection_t *gs,
const entry_guard_restriction_t *rst)
{
int n_reachable_filtered_guards = 0;
@@ -1460,6 +1461,94 @@ guard_in_node_family(const entry_guard_t *guard, const node_t *node)
}
}
+/* Allocate and return a new exit guard restriction (where <b>exit_id</b> is of
+ * size DIGEST_LEN) */
+STATIC entry_guard_restriction_t *
+guard_create_exit_restriction(const uint8_t *exit_id)
+{
+ entry_guard_restriction_t *rst = NULL;
+ rst = tor_malloc_zero(sizeof(entry_guard_restriction_t));
+ rst->type = RST_EXIT_NODE;
+ memcpy(rst->exclude_id, exit_id, DIGEST_LEN);
+ return rst;
+}
+
+/** If we have fewer than this many possible usable guards, don't set
+ * MD-availability-based restrictions: we might blacklist all of them. */
+#define MIN_GUARDS_FOR_MD_RESTRICTION 10
+
+/** Return true if we should set md dirserver restrictions. We might not want
+ * to set those if our guard options are too restricted, since we don't want
+ * to blacklist all of them. */
+static int
+should_set_md_dirserver_restriction(void)
+{
+ const guard_selection_t *gs = get_guard_selection_info();
+ int num_usable_guards = num_reachable_filtered_guards(gs, NULL);
+
+ /* Don't set restriction if too few reachable filtered guards. */
+ if (num_usable_guards < MIN_GUARDS_FOR_MD_RESTRICTION) {
+ log_info(LD_GUARD, "Not setting md restriction: only %d"
+ " usable guards.", num_usable_guards);
+ return 0;
+ }
+
+ /* We have enough usable guards: set MD restriction */
+ return 1;
+}
+
+/** Allocate and return an outdated md guard restriction. Return NULL if no
+ * such restriction is needed. */
+STATIC entry_guard_restriction_t *
+guard_create_dirserver_md_restriction(void)
+{
+ entry_guard_restriction_t *rst = NULL;
+
+ if (!should_set_md_dirserver_restriction()) {
+ log_debug(LD_GUARD, "Not setting md restriction: too few "
+ "filtered guards.");
+ return NULL;
+ }
+
+ rst = tor_malloc_zero(sizeof(entry_guard_restriction_t));
+ rst->type = RST_OUTDATED_MD_DIRSERVER;
+
+ return rst;
+}
+
+/* Return True if <b>guard</b> obeys the exit restriction <b>rst</b>. */
+static int
+guard_obeys_exit_restriction(const entry_guard_t *guard,
+ const entry_guard_restriction_t *rst)
+{
+ tor_assert(rst->type == RST_EXIT_NODE);
+
+ // Exclude the exit ID and all of its family.
+ const node_t *node = node_get_by_id((const char*)rst->exclude_id);
+ if (node && guard_in_node_family(guard, node))
+ return 0;
+
+ return tor_memneq(guard->identity, rst->exclude_id, DIGEST_LEN);
+}
+
+/** Return True if <b>guard</b> should be used as a dirserver for fetching
+ * microdescriptors. */
+static int
+guard_obeys_md_dirserver_restriction(const entry_guard_t *guard)
+{
+ /* If this guard is an outdated dirserver, don't use it. */
+ if (microdesc_relay_is_outdated_dirserver(guard->identity)) {
+ log_info(LD_GENERAL, "Skipping %s dirserver: outdated",
+ hex_str(guard->identity, DIGEST_LEN));
+ return 0;
+ }
+
+ log_debug(LD_GENERAL, "%s dirserver obeys md restrictions",
+ hex_str(guard->identity, DIGEST_LEN));
+
+ return 1;
+}
+
/**
* Return true iff <b>guard</b> obeys the restrictions defined in <b>rst</b>.
* (If <b>rst</b> is NULL, there are no restrictions.)
@@ -1472,13 +1561,14 @@ entry_guard_obeys_restriction(const entry_guard_t *guard,
if (! rst)
return 1; // No restriction? No problem.
- // Only one kind of restriction exists right now: excluding an exit
- // ID and all of its family.
- const node_t *node = node_get_by_id((const char*)rst->exclude_id);
- if (node && guard_in_node_family(guard, node))
- return 0;
+ if (rst->type == RST_EXIT_NODE) {
+ return guard_obeys_exit_restriction(guard, rst);
+ } else if (rst->type == RST_OUTDATED_MD_DIRSERVER) {
+ return guard_obeys_md_dirserver_restriction(guard);
+ }
- return tor_memneq(guard->identity, rst->exclude_id, DIGEST_LEN);
+ tor_assert_nonfatal_unreached();
+ return 0;
}
/**
@@ -2105,7 +2195,7 @@ entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b)
}
/** Release all storage held in <b>restriction</b> */
-static void
+STATIC void
entry_guard_restriction_free(entry_guard_restriction_t *rst)
{
tor_free(rst);
@@ -3358,8 +3448,8 @@ guards_choose_guard(cpath_build_state_t *state,
/* We're building to a targeted exit node, so that node can't be
* chosen as our guard for this circuit. Remember that fact in a
* restriction. */
- rst = tor_malloc_zero(sizeof(entry_guard_restriction_t));
- memcpy(rst->exclude_id, exit_id, DIGEST_LEN);
+ rst = guard_create_exit_restriction(exit_id);
+ tor_assert(rst);
}
if (entry_guard_pick_for_circuit(get_guard_selection_info(),
GUARD_USAGE_TRAFFIC,
@@ -3411,12 +3501,20 @@ remove_all_entry_guards(void)
/** Helper: pick a directory guard, with whatever algorithm is used. */
const node_t *
-guards_choose_dirguard(circuit_guard_state_t **guard_state_out)
+guards_choose_dirguard(uint8_t dir_purpose,
+ circuit_guard_state_t **guard_state_out)
{
const node_t *r = NULL;
+ entry_guard_restriction_t *rst = NULL;
+
+ /* If we are fetching microdescs, don't query outdated dirservers. */
+ if (dir_purpose == DIR_PURPOSE_FETCH_MICRODESC) {
+ rst = guard_create_dirserver_md_restriction();
+ }
+
if (entry_guard_pick_for_circuit(get_guard_selection_info(),
GUARD_USAGE_DIRGUARD,
- NULL,
+ rst,
&r,
guard_state_out) < 0) {
tor_assert(r == NULL);
diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h
index 86f0517df2..9d1293d02a 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -272,22 +272,28 @@ struct guard_selection_s {
struct entry_guard_handle_t;
+/** Types of restrictions we impose when picking guard nodes */
+typedef enum guard_restriction_type_t {
+ /* Don't pick the same guard node as our exit node (or its family) */
+ RST_EXIT_NODE = 0,
+ /* Don't pick dirguards that have previously shown to be outdated */
+ RST_OUTDATED_MD_DIRSERVER = 1
+} guard_restriction_type_t;
+
/**
* A restriction to remember which entry guards are off-limits for a given
* circuit.
*
- * Right now, we only use restrictions to block a single guard and its family
- * from being selected; this mechanism is designed to be more extensible in
- * the future, however.
- *
* Note: This mechanism is NOT for recording which guards are never to be
* used: only which guards cannot be used on <em>one particular circuit</em>.
*/
struct entry_guard_restriction_t {
- /**
- * The guard's RSA identity digest must not equal this; and it must not
- * be in the same family as any node with this digest.
- */
+ /* What type of restriction are we imposing? */
+ guard_restriction_type_t type;
+
+ /* In case of restriction type RST_EXIT_NODE, the guard's RSA identity
+ * digest must not equal this; and it must not be in the same family as any
+ * node with this digest. */
uint8_t exclude_id[DIGEST_LEN];
};
@@ -316,7 +322,8 @@ struct circuit_guard_state_t {
int guards_update_all(void);
const node_t *guards_choose_guard(cpath_build_state_t *state,
circuit_guard_state_t **guard_state_out);
-const node_t *guards_choose_dirguard(circuit_guard_state_t **guard_state_out);
+const node_t *guards_choose_dirguard(uint8_t dir_purpose,
+ circuit_guard_state_t **guard_state_out);
#if 1
/* XXXX NM I would prefer that all of this stuff be private to
@@ -518,7 +525,7 @@ STATIC void entry_guard_consider_retry(entry_guard_t *guard);
STATIC void make_guard_confirmed(guard_selection_t *gs, entry_guard_t *guard);
STATIC void entry_guards_update_confirmed(guard_selection_t *gs);
STATIC void entry_guards_update_primary(guard_selection_t *gs);
-STATIC int num_reachable_filtered_guards(guard_selection_t *gs,
+STATIC int num_reachable_filtered_guards(const guard_selection_t *gs,
const entry_guard_restriction_t *rst);
STATIC void sampled_guards_update_from_consensus(guard_selection_t *gs);
/**
@@ -554,6 +561,14 @@ STATIC unsigned entry_guards_note_guard_success(guard_selection_t *gs,
unsigned old_state);
STATIC int entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b);
STATIC char *getinfo_helper_format_single_entry_guard(const entry_guard_t *e);
+
+STATIC entry_guard_restriction_t *guard_create_exit_restriction(
+ const uint8_t *exit_id);
+
+STATIC entry_guard_restriction_t *guard_create_dirserver_md_restriction(void);
+
+STATIC void entry_guard_restriction_free(entry_guard_restriction_t *rst);
+
#endif /* defined(ENTRYNODES_PRIVATE) */
void remove_all_entry_guards_for_guard_selection(guard_selection_t *gs);
diff --git a/src/or/git_revision.h b/src/or/git_revision.h
index 1ceaeedf16..5613cb4335 100644
--- a/src/or/git_revision.h
+++ b/src/or/git_revision.h
@@ -8,5 +8,5 @@
extern const char tor_git_revision[];
-#endif
+#endif /* !defined(TOR_GIT_REVISION_H) */
diff --git a/src/or/hs_cache.c b/src/or/hs_cache.c
index 3ebe13fb4d..b864a0f717 100644
--- a/src/or/hs_cache.c
+++ b/src/or/hs_cache.c
@@ -706,6 +706,24 @@ cache_clean_v3_as_client(time_t now)
}
/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
+ * its HS encoded descriptor if it's stored in our cache, or NULL if not. */
+const char *
+hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key)
+{
+ hs_cache_client_descriptor_t *cached_desc = NULL;
+
+ tor_assert(key);
+
+ cached_desc = lookup_v3_desc_as_client(key->pubkey);
+ if (cached_desc) {
+ tor_assert(cached_desc->encoded_desc);
+ return cached_desc->encoded_desc;
+ }
+
+ return NULL;
+}
+
+/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
* its HS descriptor if it's stored in our cache, or NULL if not. */
const hs_descriptor_t *
hs_cache_lookup_as_client(const ed25519_public_key_t *key)
diff --git a/src/or/hs_cache.h b/src/or/hs_cache.h
index 2dcc518a71..a141634cc4 100644
--- a/src/or/hs_cache.h
+++ b/src/or/hs_cache.h
@@ -81,6 +81,8 @@ int hs_cache_lookup_as_dir(uint32_t version, const char *query,
const hs_descriptor_t *
hs_cache_lookup_as_client(const ed25519_public_key_t *key);
+const char *
+hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key);
int hs_cache_store_as_client(const char *desc_str,
const ed25519_public_key_t *identity_pk);
void hs_cache_clean_as_client(time_t now);
diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c
index 26fe22eb7b..a232a40c80 100644
--- a/src/or/hs_circuit.c
+++ b/src/or/hs_circuit.c
@@ -617,6 +617,7 @@ get_lspecs_from_node(const node_t *node, smartlist_t *lspecs)
link_specifier_set_un_ipv6_port(ls, ap.port);
/* Sixteen bytes IPv6 and two bytes port. */
link_specifier_set_ls_len(ls, addr_len + sizeof(ap.port));
+ smartlist_add(lspecs, ls);
}
}
@@ -1100,9 +1101,19 @@ hs_circ_send_introduce1(origin_circuit_t *intro_circ,
tor_assert(ip);
tor_assert(subcredential);
+ /* It is undefined behavior in hs_cell_introduce1_data_clear() if intro1_data
+ * has been declared on the stack but not initialized. Here, we set it to 0.
+ */
+ memset(&intro1_data, 0, sizeof(hs_cell_introduce1_data_t));
+
/* This takes various objects in order to populate the introduce1 data
* object which is used to build the content of the cell. */
const node_t *exit_node = build_state_get_exit_node(rend_circ->build_state);
+ if (exit_node == NULL) {
+ log_info(LD_REND, "Unable to get rendezvous point for circuit %u. "
+ "Failing.", TO_CIRCUIT(intro_circ)->n_circ_id);
+ goto done;
+ }
setup_introduce1_data(ip, exit_node, subcredential, &intro1_data);
/* If we didn't get any link specifiers, it's because our node was
* bad. */
diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h
index 0a1186dbaa..b92fb4e494 100644
--- a/src/or/hs_circuit.h
+++ b/src/or/hs_circuit.h
@@ -67,7 +67,7 @@ create_rp_circuit_identifier(const hs_service_t *service,
const curve25519_public_key_t *server_pk,
const hs_ntor_rend_cell_keys_t *keys);
-#endif
+#endif /* defined(HS_CIRCUIT_PRIVATE) */
#endif /* !defined(TOR_HS_CIRCUIT_H) */
diff --git a/src/or/hs_client.c b/src/or/hs_client.c
index 9ac653c721..666860155c 100644
--- a/src/or/hs_client.c
+++ b/src/or/hs_client.c
@@ -21,6 +21,7 @@
#include "config.h"
#include "directory.h"
#include "hs_client.h"
+#include "hs_control.h"
#include "router.h"
#include "routerset.h"
#include "circuitlist.h"
@@ -349,6 +350,10 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
safe_str_client(base64_blinded_pubkey),
safe_str_client(routerstatus_describe(hsdir)));
+ /* Fire a REQUESTED event on the control port. */
+ hs_control_desc_event_requested(onion_identity_pk, base64_blinded_pubkey,
+ hsdir);
+
/* Cleanup memory. */
memwipe(&blinded_pubkey, 0, sizeof(blinded_pubkey));
memwipe(base64_blinded_pubkey, 0, sizeof(base64_blinded_pubkey));
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index c95e59a6f8..58fcd1b93c 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -130,6 +130,17 @@ typedef enum {
HS_AUTH_KEY_TYPE_ED25519 = 2,
} hs_auth_key_type_t;
+/* Return value when adding an ephemeral service through the ADD_ONION
+ * control port command. Both v2 and v3 share these. */
+typedef enum {
+ RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */
+ RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */
+ RSAE_ADDREXISTS = -3, /**< Onion address collision */
+ RSAE_BADPRIVKEY = -2, /**< Invalid public key */
+ RSAE_INTERNAL = -1, /**< Internal error */
+ RSAE_OKAY = 0 /**< Service added as expected */
+} hs_service_add_ephemeral_status_t;
+
/* Represents the mapping from a virtual port of a rendezvous service to a
* real port on some IP. */
typedef struct rend_service_port_config_t {
diff --git a/src/or/hs_control.c b/src/or/hs_control.c
new file mode 100644
index 0000000000..1b1fe78575
--- /dev/null
+++ b/src/or/hs_control.c
@@ -0,0 +1,256 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_control.c
+ * \brief Contains control port event related code.
+ **/
+
+#include "or.h"
+#include "control.h"
+#include "hs_common.h"
+#include "hs_control.h"
+#include "hs_descriptor.h"
+#include "hs_service.h"
+#include "nodelist.h"
+
+/* Send on the control port the "HS_DESC REQUESTED [...]" event.
+ *
+ * The onion_pk is the onion service public key, base64_blinded_pk is the
+ * base64 encoded blinded key for the service and hsdir_rs is the routerstatus
+ * object of the HSDir that this request is for. */
+void
+hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk,
+ const char *base64_blinded_pk,
+ const routerstatus_t *hsdir_rs)
+{
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ const uint8_t *hsdir_index;
+ const node_t *hsdir_node;
+
+ tor_assert(onion_pk);
+ tor_assert(base64_blinded_pk);
+ tor_assert(hsdir_rs);
+
+ hs_build_address(onion_pk, HS_VERSION_THREE, onion_address);
+
+ /* Get the node from the routerstatus object to get the HSDir index used for
+ * this request. We can't have a routerstatus entry without a node and we
+ * can't pick a node without an hsdir_index. */
+ hsdir_node = node_get_by_id(hsdir_rs->identity_digest);
+ tor_assert(hsdir_node);
+ tor_assert(hsdir_node->hsdir_index);
+ /* This is a fetch event. */
+ hsdir_index = hsdir_node->hsdir_index->fetch;
+
+ /* Trigger the event. */
+ control_event_hs_descriptor_requested(onion_address, REND_NO_AUTH,
+ hsdir_rs->identity_digest,
+ base64_blinded_pk,
+ hex_str((const char *) hsdir_index,
+ DIGEST256_LEN));
+ memwipe(onion_address, 0, sizeof(onion_address));
+}
+
+/* Send on the control port the "HS_DESC FAILED [...]" event.
+ *
+ * Using a directory connection identifier, the HSDir identity digest and a
+ * reason for the failure. None can be NULL. */
+void
+hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest,
+ const char *reason)
+{
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+
+ tor_assert(ident);
+ tor_assert(hsdir_id_digest);
+ tor_assert(reason);
+
+ /* Build onion address and encoded blinded key. */
+ IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
+ &ident->blinded_pk) < 0) {
+ return;
+ }
+ hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
+
+ control_event_hsv3_descriptor_failed(onion_address, base64_blinded_pk,
+ hsdir_id_digest, reason);
+}
+
+/* Send on the control port the "HS_DESC RECEIVED [...]" event.
+ *
+ * Using a directory connection identifier and the HSDir identity digest.
+ * None can be NULL. */
+void
+hs_control_desc_event_received(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest)
+{
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+
+ tor_assert(ident);
+ tor_assert(hsdir_id_digest);
+
+ /* Build onion address and encoded blinded key. */
+ IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
+ &ident->blinded_pk) < 0) {
+ return;
+ }
+ hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
+
+ control_event_hsv3_descriptor_received(onion_address, base64_blinded_pk,
+ hsdir_id_digest);
+}
+
+/* Send on the control port the "HS_DESC CREATED [...]" event.
+ *
+ * Using the onion address of the descriptor's service and the blinded public
+ * key of the descriptor as a descriptor ID. None can be NULL. */
+void
+hs_control_desc_event_created(const char *onion_address,
+ const ed25519_public_key_t *blinded_pk)
+{
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+
+ tor_assert(onion_address);
+ tor_assert(blinded_pk);
+
+ /* Build base64 encoded blinded key. */
+ IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) {
+ return;
+ }
+
+ /* Version 3 doesn't use the replica number in its descriptor ID computation
+ * so we pass negative value so the control port subsystem can ignore it. */
+ control_event_hs_descriptor_created(onion_address, base64_blinded_pk, -1);
+}
+
+/* Send on the control port the "HS_DESC UPLOAD [...]" event.
+ *
+ * Using the onion address of the descriptor's service, the HSDir identity
+ * digest, the blinded public key of the descriptor as a descriptor ID and the
+ * HSDir index for this particular request. None can be NULL. */
+void
+hs_control_desc_event_upload(const char *onion_address,
+ const char *hsdir_id_digest,
+ const ed25519_public_key_t *blinded_pk,
+ const uint8_t *hsdir_index)
+{
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+
+ tor_assert(onion_address);
+ tor_assert(hsdir_id_digest);
+ tor_assert(blinded_pk);
+ tor_assert(hsdir_index);
+
+ /* Build base64 encoded blinded key. */
+ IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) {
+ return;
+ }
+
+ control_event_hs_descriptor_upload(onion_address, hsdir_id_digest,
+ base64_blinded_pk,
+ hex_str((const char *) hsdir_index,
+ DIGEST256_LEN));
+}
+
+/* Send on the control port the "HS_DESC UPLOADED [...]" event.
+ *
+ * Using the directory connection identifier and the HSDir identity digest.
+ * None can be NULL. */
+void
+hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest)
+{
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+
+ tor_assert(ident);
+ tor_assert(hsdir_id_digest);
+
+ hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
+
+ control_event_hs_descriptor_uploaded(hsdir_id_digest, onion_address);
+}
+
+/* Send on the control port the "HS_DESC_CONTENT [...]" event.
+ *
+ * Using the directory connection identifier, the HSDir identity digest and
+ * the body of the descriptor (as it was received from the directory). None
+ * can be NULL. */
+void
+hs_control_desc_event_content(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest,
+ const char *body)
+{
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+
+ tor_assert(ident);
+ tor_assert(hsdir_id_digest);
+
+ /* Build onion address and encoded blinded key. */
+ IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
+ &ident->blinded_pk) < 0) {
+ return;
+ }
+ hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
+
+ control_event_hs_descriptor_content(onion_address, base64_blinded_pk,
+ hsdir_id_digest, body);
+}
+
+/* Handle the "HSPOST [...]" command. The body is an encoded descriptor for
+ * the given onion_address. The descriptor will be uploaded to each directory
+ * in hsdirs_rs. If NULL, the responsible directories for the current time
+ * period will be selected.
+ *
+ * Return -1 on if the descriptor plaintext section is not decodable. Else, 0
+ * on success. */
+int
+hs_control_hspost_command(const char *body, const char *onion_address,
+ const smartlist_t *hsdirs_rs)
+{
+ int ret = -1;
+ ed25519_public_key_t identity_pk;
+ hs_desc_plaintext_data_t plaintext;
+ smartlist_t *hsdirs = NULL;
+
+ tor_assert(body);
+ tor_assert(onion_address);
+
+ /* This can't fail because we require the caller to pass us a valid onion
+ * address that has passed hs_address_is_valid(). */
+ hs_parse_address(onion_address, &identity_pk, NULL, NULL);
+
+ /* Only decode the plaintext part which is what the directory will do to
+ * validate before caching. */
+ if (hs_desc_decode_plaintext(body, &plaintext) < 0) {
+ goto done;
+ }
+
+ /* No HSDir(s) given, we'll compute what the current ones should be. */
+ if (hsdirs_rs == NULL) {
+ hsdirs = smartlist_new();
+ hs_get_responsible_hsdirs(&plaintext.blinded_pubkey,
+ hs_get_time_period_num(0),
+ 0, /* Always the current descriptor which uses
+ * the first hsdir index. */
+ 0, /* It is for storing on a directory. */
+ hsdirs);
+ hsdirs_rs = hsdirs;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(hsdirs_rs, const routerstatus_t *, rs) {
+ hs_service_upload_desc_to_dir(body, plaintext.version, &identity_pk,
+ &plaintext.blinded_pubkey, rs);
+ } SMARTLIST_FOREACH_END(rs);
+ ret = 0;
+
+ done:
+ /* We don't have ownership of the objects in this list. */
+ smartlist_free(hsdirs);
+ return ret;
+}
+
diff --git a/src/or/hs_control.h b/src/or/hs_control.h
new file mode 100644
index 0000000000..95c46e655e
--- /dev/null
+++ b/src/or/hs_control.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_control.h
+ * \brief Header file containing control port event related code.
+ **/
+
+#ifndef TOR_HS_CONTROL_H
+#define TOR_HS_CONTROL_H
+
+#include "hs_ident.h"
+
+/* Event "HS_DESC REQUESTED [...]" */
+void hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk,
+ const char *base64_blinded_pk,
+ const routerstatus_t *hsdir_rs);
+
+/* Event "HS_DESC FAILED [...]" */
+void hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest,
+ const char *reason);
+
+/* Event "HS_DESC RECEIVED [...]" */
+void hs_control_desc_event_received(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest);
+
+/* Event "HS_DESC CREATED [...]" */
+void hs_control_desc_event_created(const char *onion_address,
+ const ed25519_public_key_t *blinded_pk);
+
+/* Event "HS_DESC UPLOAD [...]" */
+void hs_control_desc_event_upload(const char *onion_address,
+ const char *hsdir_id_digest,
+ const ed25519_public_key_t *blinded_pk,
+ const uint8_t *hsdir_index);
+
+/* Event "HS_DESC UPLOADED [...]" */
+void hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest);
+
+/* Event "HS_DESC_CONTENT [...]" */
+void hs_control_desc_event_content(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest,
+ const char *body);
+
+/* Command "HSPOST [...]" */
+int hs_control_hspost_command(const char *body, const char *onion_address,
+ const smartlist_t *hsdirs_rs);
+
+#endif /* !defined(TOR_HS_CONTROL_H) */
+
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 8e2f52dcf0..f65b59ae1c 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -30,6 +30,7 @@
#include "hs_circuit.h"
#include "hs_common.h"
#include "hs_config.h"
+#include "hs_control.h"
#include "hs_circuit.h"
#include "hs_descriptor.h"
#include "hs_ident.h"
@@ -1431,6 +1432,9 @@ build_service_descriptor(hs_service_t *service, time_t now,
/* Assign newly built descriptor to the next slot. */
*desc_out = desc;
+ /* Fire a CREATED control port event. */
+ hs_control_desc_event_created(service->onion_address,
+ &desc->blinded_kp.pubkey);
return;
err:
@@ -2199,16 +2203,12 @@ static void
upload_descriptor_to_hsdir(const hs_service_t *service,
hs_service_descriptor_t *desc, const node_t *hsdir)
{
- char version_str[4] = {0}, *encoded_desc = NULL;
- directory_request_t *dir_req;
- hs_ident_dir_conn_t ident;
+ char *encoded_desc = NULL;
tor_assert(service);
tor_assert(desc);
tor_assert(hsdir);
- memset(&ident, 0, sizeof(ident));
-
/* Let's avoid doing that if tor is configured to not publish. */
if (!get_options()->PublishHidServDescriptors) {
log_info(LD_REND, "Service %s not publishing descriptor. "
@@ -2224,29 +2224,10 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
goto end;
}
- /* Setup the connection identifier. */
- hs_ident_dir_conn_init(&service->keys.identity_pk, &desc->blinded_kp.pubkey,
- &ident);
-
- /* This is our resource when uploading which is used to construct the URL
- * with the version number: "/tor/hs/<version>/publish". */
- tor_snprintf(version_str, sizeof(version_str), "%u",
- service->config.version);
-
- /* Build the directory request for this HSDir. */
- dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC);
- directory_request_set_routerstatus(dir_req, hsdir->rs);
- directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS);
- directory_request_set_resource(dir_req, version_str);
- directory_request_set_payload(dir_req, encoded_desc,
- strlen(encoded_desc));
- /* The ident object is copied over the directory connection object once
- * the directory request is initiated. */
- directory_request_upload_set_hs_ident(dir_req, &ident);
-
- /* Initiate the directory request to the hsdir.*/
- directory_initiate_request(dir_req);
- directory_request_free(dir_req);
+ /* Time to upload the descriptor to the directory. */
+ hs_service_upload_desc_to_dir(encoded_desc, service->config.version,
+ &service->keys.identity_pk,
+ &desc->blinded_kp.pubkey, hsdir->rs);
/* Add this node to previous_hsdirs list */
service_desc_note_upload(desc, hsdir);
@@ -2263,9 +2244,12 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
desc->desc->plaintext_data.revision_counter,
safe_str_client(node_describe(hsdir)),
safe_str_client(hex_str((const char *) index, 32)));
+
+ /* Fire a UPLOAD control port event. */
+ hs_control_desc_event_upload(service->onion_address, hsdir->identity,
+ &desc->blinded_kp.pubkey, index);
}
- /* XXX: Inform control port of the upload event (#20699). */
end:
tor_free(encoded_desc);
return;
@@ -2900,6 +2884,205 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list)
/* Public API */
/* ========== */
+/* Upload an encoded descriptor in encoded_desc of the given version. This
+ * descriptor is for the service identity_pk and blinded_pk used to setup the
+ * directory connection identifier. It is uploaded to the directory hsdir_rs
+ * routerstatus_t object.
+ *
+ * NOTE: This function does NOT check for PublishHidServDescriptors because it
+ * is only used by the control port command HSPOST outside of this subsystem.
+ * Inside this code, upload_descriptor_to_hsdir() should be used. */
+void
+hs_service_upload_desc_to_dir(const char *encoded_desc,
+ const uint8_t version,
+ const ed25519_public_key_t *identity_pk,
+ const ed25519_public_key_t *blinded_pk,
+ const routerstatus_t *hsdir_rs)
+{
+ char version_str[4] = {0};
+ directory_request_t *dir_req;
+ hs_ident_dir_conn_t ident;
+
+ tor_assert(encoded_desc);
+ tor_assert(identity_pk);
+ tor_assert(blinded_pk);
+ tor_assert(hsdir_rs);
+
+ /* Setup the connection identifier. */
+ memset(&ident, 0, sizeof(ident));
+ hs_ident_dir_conn_init(identity_pk, blinded_pk, &ident);
+
+ /* This is our resource when uploading which is used to construct the URL
+ * with the version number: "/tor/hs/<version>/publish". */
+ tor_snprintf(version_str, sizeof(version_str), "%u", version);
+
+ /* Build the directory request for this HSDir. */
+ dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC);
+ directory_request_set_routerstatus(dir_req, hsdir_rs);
+ directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS);
+ directory_request_set_resource(dir_req, version_str);
+ directory_request_set_payload(dir_req, encoded_desc,
+ strlen(encoded_desc));
+ /* The ident object is copied over the directory connection object once
+ * the directory request is initiated. */
+ directory_request_upload_set_hs_ident(dir_req, &ident);
+
+ /* Initiate the directory request to the hsdir.*/
+ directory_initiate_request(dir_req);
+ directory_request_free(dir_req);
+}
+
+/* Add the ephemeral service using the secret key sk and ports. Both max
+ * streams parameter will be set in the newly created service.
+ *
+ * Ownership of sk and ports is passed to this routine. Regardless of
+ * success/failure, callers should not touch these values after calling this
+ * routine, and may assume that correct cleanup has been done on failure.
+ *
+ * Return an appropriate hs_service_add_ephemeral_status_t. */
+hs_service_add_ephemeral_status_t
+hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
+ int max_streams_per_rdv_circuit,
+ int max_streams_close_circuit, char **address_out)
+{
+ hs_service_add_ephemeral_status_t ret;
+ hs_service_t *service = NULL;
+
+ tor_assert(sk);
+ tor_assert(ports);
+ tor_assert(address_out);
+
+ service = hs_service_new(get_options());
+
+ /* Setup the service configuration with specifics. A default service is
+ * HS_VERSION_TWO so explicitely set it. */
+ service->config.version = HS_VERSION_THREE;
+ service->config.max_streams_per_rdv_circuit = max_streams_per_rdv_circuit;
+ service->config.max_streams_close_circuit = !!max_streams_close_circuit;
+ service->config.is_ephemeral = 1;
+ smartlist_free(service->config.ports);
+ service->config.ports = ports;
+
+ /* Handle the keys. */
+ memcpy(&service->keys.identity_sk, sk, sizeof(service->keys.identity_sk));
+ if (ed25519_public_key_generate(&service->keys.identity_pk,
+ &service->keys.identity_sk) < 0) {
+ log_warn(LD_CONFIG, "Unable to generate ed25519 public key"
+ "for v3 service.");
+ ret = RSAE_BADPRIVKEY;
+ goto err;
+ }
+
+ /* Make sure we have at least one port. */
+ if (smartlist_len(service->config.ports) == 0) {
+ log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified "
+ "for v3 service.");
+ ret = RSAE_BADVIRTPORT;
+ goto err;
+ }
+
+ /* The only way the registration can fail is if the service public key
+ * already exists. */
+ if (BUG(register_service(hs_service_map, service) < 0)) {
+ log_warn(LD_CONFIG, "Onion Service private key collides with an "
+ "existing v3 service.");
+ ret = RSAE_ADDREXISTS;
+ goto err;
+ }
+
+ /* Last step is to build the onion address. */
+ hs_build_address(&service->keys.identity_pk,
+ (uint8_t) service->config.version,
+ service->onion_address);
+ *address_out = tor_strdup(service->onion_address);
+
+ log_info(LD_CONFIG, "Added ephemeral v3 onion service: %s",
+ safe_str_client(service->onion_address));
+ ret = RSAE_OKAY;
+ goto end;
+
+ err:
+ hs_service_free(service);
+
+ end:
+ memwipe(sk, 0, sizeof(ed25519_secret_key_t));
+ tor_free(sk);
+ return ret;
+}
+
+/* For the given onion address, delete the ephemeral service. Return 0 on
+ * success else -1 on error. */
+int
+hs_service_del_ephemeral(const char *address)
+{
+ uint8_t version;
+ ed25519_public_key_t pk;
+ hs_service_t *service = NULL;
+
+ tor_assert(address);
+
+ if (hs_parse_address(address, &pk, NULL, &version) < 0) {
+ log_warn(LD_CONFIG, "Requested malformed v3 onion address for removal.");
+ goto err;
+ }
+
+ if (version != HS_VERSION_THREE) {
+ log_warn(LD_CONFIG, "Requested version of onion address for removal "
+ "is not supported.");
+ goto err;
+ }
+
+ service = find_service(hs_service_map, &pk);
+ if (service == NULL) {
+ log_warn(LD_CONFIG, "Requested non-existent v3 hidden service for "
+ "removal.");
+ goto err;
+ }
+
+ if (!service->config.is_ephemeral) {
+ log_warn(LD_CONFIG, "Requested non-ephemeral v3 hidden service for "
+ "removal.");
+ goto err;
+ }
+
+ /* Close circuits, remove from map and finally free. */
+ close_service_circuits(service);
+ remove_service(hs_service_map, service);
+ hs_service_free(service);
+
+ log_info(LD_CONFIG, "Removed ephemeral v3 hidden service: %s",
+ safe_str_client(address));
+ return 0;
+
+ err:
+ return -1;
+}
+
+/* Using the ed25519 public key pk, find a service for that key and return the
+ * current encoded descriptor as a newly allocated string or NULL if not
+ * found. This is used by the control port subsystem. */
+char *
+hs_service_lookup_current_desc(const ed25519_public_key_t *pk)
+{
+ const hs_service_t *service;
+
+ tor_assert(pk);
+
+ service = find_service(hs_service_map, pk);
+ if (service && service->desc_current) {
+ char *encoded_desc = NULL;
+ /* No matter what is the result (which should never be a failure), return
+ * the encoded variable, if success it will contain the right thing else
+ * it will be NULL. */
+ hs_desc_encode_descriptor(service->desc_current->desc,
+ &service->desc_current->signing_kp,
+ &encoded_desc);
+ return encoded_desc;
+ }
+
+ return NULL;
+}
+
/* Return the number of service we have configured and usable. */
unsigned int
hs_service_get_num_services(void)
@@ -2928,7 +3111,9 @@ hs_service_intro_circ_has_closed(origin_circuit_t *circ)
get_objects_from_ident(circ->hs_ident, &service, &ip, &desc);
if (service == NULL) {
- log_warn(LD_REND, "Unable to find any hidden service associated "
+ /* This is possible if the circuits are closed and the service is
+ * immediately deleted. */
+ log_info(LD_REND, "Unable to find any hidden service associated "
"identity key %s on intro circuit %u.",
ed25519_fmt(&circ->hs_ident->identity_pk),
TO_CIRCUIT(circ)->n_circ_id);
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index ed1053d850..678f24b0a2 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -271,6 +271,21 @@ int hs_service_receive_introduce2(origin_circuit_t *circ,
void hs_service_intro_circ_has_closed(origin_circuit_t *circ);
+char *hs_service_lookup_current_desc(const ed25519_public_key_t *pk);
+
+hs_service_add_ephemeral_status_t
+hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
+ int max_streams_per_rdv_circuit,
+ int max_streams_close_circuit, char **address_out);
+int hs_service_del_ephemeral(const char *address);
+
+/* Used outside of the HS subsystem by the control port command HSPOST. */
+void hs_service_upload_desc_to_dir(const char *encoded_desc,
+ const uint8_t version,
+ const ed25519_public_key_t *identity_pk,
+ const ed25519_public_key_t *blinded_pk,
+ const routerstatus_t *hsdir_rs);
+
#ifdef HS_SERVICE_PRIVATE
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/include.am b/src/or/include.am
index b783f4855a..1c66cd2de3 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -60,6 +60,7 @@ LIBTOR_A_SOURCES = \
src/or/hs_client.c \
src/or/hs_common.c \
src/or/hs_config.c \
+ src/or/hs_control.c \
src/or/hs_descriptor.c \
src/or/hs_ident.c \
src/or/hs_intropoint.c \
@@ -196,11 +197,12 @@ ORHEADERS = \
src/or/hibernate.h \
src/or/hs_cache.h \
src/or/hs_cell.h \
- src/or/hs_config.h \
src/or/hs_circuit.h \
src/or/hs_circuitmap.h \
src/or/hs_client.h \
src/or/hs_common.h \
+ src/or/hs_config.h \
+ src/or/hs_control.h \
src/or/hs_descriptor.h \
src/or/hs_ident.h \
src/or/hs_intropoint.h \
diff --git a/src/or/main.c b/src/or/main.c
index b70f47abeb..aae98dd8ab 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -3207,7 +3207,7 @@ tor_init(int argc, char *argv[])
log_notice(LD_GENERAL, "%s", rust_str);
}
tor_free(rust_str);
-#endif
+#endif /* defined(HAVE_RUST) */
if (network_init()<0) {
log_err(LD_BUG,"Error initializing network; exiting.");
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 3fe5e933d0..d8a4660af1 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -74,6 +74,102 @@ HT_GENERATE2(microdesc_map, microdesc_t, node,
microdesc_hash_, microdesc_eq_, 0.6,
tor_reallocarray_, tor_free_)
+/************************* md fetch fail cache *****************************/
+
+/* If we end up with too many outdated dirservers, something probably went
+ * wrong so clean up the list. */
+#define TOO_MANY_OUTDATED_DIRSERVERS 30
+
+/** List of dirservers with outdated microdesc information. The smartlist is
+ * filled with the hex digests of outdated dirservers. */
+static smartlist_t *outdated_dirserver_list = NULL;
+
+/** Note that we failed to fetch a microdescriptor from the relay with
+ * <b>relay_digest</b> (of size DIGEST_LEN). */
+void
+microdesc_note_outdated_dirserver(const char *relay_digest)
+{
+ char relay_hexdigest[HEX_DIGEST_LEN+1];
+
+ /* Don't register outdated dirservers if we don't have a live consensus,
+ * since we might be trying to fetch microdescriptors that are not even
+ * currently active. */
+ if (!networkstatus_get_live_consensus(approx_time())) {
+ return;
+ }
+
+ if (!outdated_dirserver_list) {
+ outdated_dirserver_list = smartlist_new();
+ }
+
+ tor_assert(outdated_dirserver_list);
+
+ /* If the list grows too big, clean it up */
+ if (BUG(smartlist_len(outdated_dirserver_list) >
+ TOO_MANY_OUTDATED_DIRSERVERS)) {
+ microdesc_reset_outdated_dirservers_list();
+ }
+
+ /* Turn the binary relay digest to a hex since smartlists have better support
+ * for strings than digests. */
+ base16_encode(relay_hexdigest,sizeof(relay_hexdigest),
+ relay_digest, DIGEST_LEN);
+
+ /* Make sure we don't add a dirauth as an outdated dirserver */
+ if (router_get_trusteddirserver_by_digest(relay_digest)) {
+ log_info(LD_GENERAL, "Auth %s gave us outdated dirinfo.", relay_hexdigest);
+ return;
+ }
+
+ /* Don't double-add outdated dirservers */
+ if (smartlist_contains_string(outdated_dirserver_list, relay_hexdigest)) {
+ return;
+ }
+
+ /* Add it to the list of outdated dirservers */
+ smartlist_add_strdup(outdated_dirserver_list, relay_hexdigest);
+
+ log_info(LD_GENERAL, "Noted %s as outdated md dirserver", relay_hexdigest);
+}
+
+/** Return True if the relay with <b>relay_digest</b> (size DIGEST_LEN) is an
+ * outdated dirserver */
+int
+microdesc_relay_is_outdated_dirserver(const char *relay_digest)
+{
+ char relay_hexdigest[HEX_DIGEST_LEN+1];
+
+ if (!outdated_dirserver_list) {
+ return 0;
+ }
+
+ /* Convert identity digest to hex digest */
+ base16_encode(relay_hexdigest, sizeof(relay_hexdigest),
+ relay_digest, DIGEST_LEN);
+
+ /* Last time we tried to fetch microdescs, was this directory mirror missing
+ * any mds we asked for? */
+ if (smartlist_contains_string(outdated_dirserver_list, relay_hexdigest)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Reset the list of outdated dirservers. */
+void
+microdesc_reset_outdated_dirservers_list(void)
+{
+ if (!outdated_dirserver_list) {
+ return;
+ }
+
+ SMARTLIST_FOREACH(outdated_dirserver_list, char *, cp, tor_free(cp));
+ smartlist_clear(outdated_dirserver_list);
+}
+
+/****************************************************************************/
+
/** Write the body of <b>md</b> into <b>f</b>, with appropriate annotations.
* On success, return the total number of bytes written, and set
* *<b>annotation_len_out</b> to the number of bytes written as
@@ -789,6 +885,11 @@ microdesc_free_all(void)
tor_free(the_microdesc_cache->journal_fname);
tor_free(the_microdesc_cache);
}
+
+ if (outdated_dirserver_list) {
+ SMARTLIST_FOREACH(outdated_dirserver_list, char *, cp, tor_free(cp));
+ smartlist_free(outdated_dirserver_list);
+ }
}
/** If there is a microdescriptor in <b>cache</b> whose sha256 digest is
diff --git a/src/or/microdesc.h b/src/or/microdesc.h
index b40d66b3e3..1be12156a4 100644
--- a/src/or/microdesc.h
+++ b/src/or/microdesc.h
@@ -50,5 +50,9 @@ int we_fetch_microdescriptors(const or_options_t *options);
int we_fetch_router_descriptors(const or_options_t *options);
int we_use_microdescriptors_for_circuits(const or_options_t *options);
+void microdesc_note_outdated_dirserver(const char *relay_digest);
+int microdesc_relay_is_outdated_dirserver(const char *relay_digest);
+void microdesc_reset_outdated_dirservers_list(void);
+
#endif /* !defined(TOR_MICRODESC_H) */
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index d382cc8033..d426d4858e 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -2023,6 +2023,9 @@ networkstatus_set_current_consensus(const char *consensus,
tor_free(flavormsg);
}
+ /* We got a new consesus. Reset our md fetch fail cache */
+ microdesc_reset_outdated_dirservers_list();
+
router_dir_info_changed();
result = 0;
@@ -2206,7 +2209,9 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
char *
networkstatus_getinfo_helper_single(const routerstatus_t *rs)
{
- return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, NULL);
+ return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT,
+ ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD,
+ NULL);
}
/** Alloc and return a string describing routerstatuses for the most
diff --git a/src/or/protover.c b/src/or/protover.c
index ae955296e6..748c0c2f50 100644
--- a/src/or/protover.c
+++ b/src/or/protover.c
@@ -737,6 +737,11 @@ protocol_list_contains(const smartlist_t *protos,
const char *
protover_compute_for_old_tor(const char *version)
{
+ if (version == NULL) {
+ /* No known version; guess the oldest series that is still supported. */
+ version = "0.2.5.15";
+ }
+
if (tor_version_as_new_as(version,
FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS)) {
return "";
@@ -778,5 +783,5 @@ protover_free_all(void)
}
}
-#endif
+#endif /* !defined(HAVE_RUST) */
diff --git a/src/or/protover.h b/src/or/protover.h
index a4dbc8bfc2..83a728e626 100644
--- a/src/or/protover.h
+++ b/src/or/protover.h
@@ -84,7 +84,7 @@ STATIC const char *protocol_type_to_str(protocol_type_t pr);
STATIC int str_to_protocol_type(const char *s, protocol_type_t *pr_out);
STATIC void proto_entry_free(proto_entry_t *entry);
-#endif
+#endif /* !defined(HAVE_RUST) && defined(TOR_UNIT_TESTS) */
#endif /* defined(PROTOVER_PRIVATE) */
diff --git a/src/or/protover_rust.c b/src/or/protover_rust.c
index 0c409b1681..26e21cc1c5 100644
--- a/src/or/protover_rust.c
+++ b/src/or/protover_rust.c
@@ -15,5 +15,5 @@
/* Define for compatibility, used in main.c */
void protover_free_all(void) {}
-#endif
+#endif /* defined(HAVE_RUST) */
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 3274819241..8291e5abfb 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -459,7 +459,8 @@ directory_get_from_hs_dir(const char *desc_id,
hs_dir = hs_pick_hsdir(responsible_dirs, desc_id_base32);
if (!hs_dir) {
/* No suitable hs dir can be found, stop right now. */
- control_event_hs_descriptor_failed(rend_query, NULL, "QUERY_NO_HSDIR");
+ control_event_hsv2_descriptor_failed(rend_query, NULL,
+ "QUERY_NO_HSDIR");
control_event_hs_descriptor_content(rend_data_get_address(rend_query),
desc_id_base32, NULL, NULL);
return 0;
@@ -482,7 +483,7 @@ directory_get_from_hs_dir(const char *desc_id,
REND_DESC_COOKIE_LEN,
0)<0) {
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
- control_event_hs_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC");
+ control_event_hsv2_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC");
control_event_hs_descriptor_content(rend_data_get_address(rend_query),
desc_id_base32, hsdir_fp, NULL);
return 0;
@@ -515,9 +516,10 @@ directory_get_from_hs_dir(const char *desc_id,
(rend_data->auth_type == REND_NO_AUTH ? "[none]" :
escaped_safe_str_client(descriptor_cookie_base64)),
routerstatus_describe(hs_dir));
- control_event_hs_descriptor_requested(rend_query,
+ control_event_hs_descriptor_requested(rend_data->onion_address,
+ rend_data->auth_type,
hs_dir->identity_digest,
- desc_id_base32);
+ desc_id_base32, NULL);
return 1;
}
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 60234a5c1f..15a0017d66 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -847,9 +847,9 @@ rend_config_service(const config_line_t *line_,
* after calling this routine, and may assume that correct cleanup has
* been done on failure.
*
- * Return an appropriate rend_service_add_ephemeral_status_t.
+ * Return an appropriate hs_service_add_ephemeral_status_t.
*/
-rend_service_add_ephemeral_status_t
+hs_service_add_ephemeral_status_t
rend_service_add_ephemeral(crypto_pk_t *pk,
smartlist_t *ports,
int max_streams_per_circuit,
@@ -1829,6 +1829,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
time_t now = time(NULL);
time_t elapsed;
int replay;
+ ssize_t keylen;
/* Do some initial validation and logging before we parse the cell */
if (circuit->base_.purpose != CIRCUIT_PURPOSE_S_INTRO) {
@@ -1903,9 +1904,10 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
}
/* check for replay of PK-encrypted portion. */
+ keylen = crypto_pk_keysize(intro_key);
replay = replaycache_add_test_and_elapsed(
intro_point->accepted_intro_rsa_parts,
- parsed_req->ciphertext, parsed_req->ciphertext_len,
+ parsed_req->ciphertext, MIN(parsed_req->ciphertext_len, keylen),
&elapsed);
if (replay) {
@@ -3574,7 +3576,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
"directories to post descriptors to.");
control_event_hs_descriptor_upload(service_id,
"UNKNOWN",
- "UNKNOWN");
+ "UNKNOWN", NULL);
goto done;
}
}
@@ -3629,7 +3631,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
hs_dir->or_port);
control_event_hs_descriptor_upload(service_id,
hs_dir->identity_digest,
- desc_id_base32);
+ desc_id_base32, NULL);
tor_free(hs_dir_ip);
/* Remember successful upload to this router for next time. */
if (!smartlist_contains_digest(successful_uploads,
@@ -3912,6 +3914,10 @@ remove_invalid_intro_points(rend_service_t *service,
log_info(LD_REND, "Expiring %s as intro point for %s.",
safe_str_client(extend_info_describe(intro->extend_info)),
safe_str_client(service->service_id));
+ /* We might have put it in the retry list if so, undo. */
+ if (retry_nodes) {
+ smartlist_remove(retry_nodes, intro);
+ }
smartlist_add(service->expiring_nodes, intro);
SMARTLIST_DEL_CURRENT(service->intro_nodes, intro);
/* Intro point is expired, we need a new one thus don't consider it
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 5946e31861..15badce6ab 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -187,16 +187,7 @@ void rend_service_port_config_free(rend_service_port_config_t *p);
void rend_authorized_client_free(rend_authorized_client_t *client);
-/** Return value from rend_service_add_ephemeral. */
-typedef enum {
- RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */
- RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */
- RSAE_ADDREXISTS = -3, /**< Onion address collision */
- RSAE_BADPRIVKEY = -2, /**< Invalid public key */
- RSAE_INTERNAL = -1, /**< Internal error */
- RSAE_OKAY = 0 /**< Service added as expected */
-} rend_service_add_ephemeral_status_t;
-rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
+hs_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
smartlist_t *ports,
int max_streams_per_circuit,
int max_streams_close_circuit,
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 51e800bc31..87b5a93713 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -1239,9 +1239,9 @@ 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? */
-#define NUM_SECS_BW_SUM_INTERVAL (4*60*60)
+#define NUM_SECS_BW_SUM_INTERVAL (24*60*60)
/** How far in the past do we remember and publish bandwidth use? */
-#define NUM_SECS_BW_SUM_IS_VALID (24*60*60)
+#define NUM_SECS_BW_SUM_IS_VALID (5*24*60*60)
/** How many bandwidth usage intervals do we remember? (derived) */
#define NUM_TOTALS (NUM_SECS_BW_SUM_IS_VALID/NUM_SECS_BW_SUM_INTERVAL)
diff --git a/src/or/router.c b/src/or/router.c
index 64bd9fdfcd..cd1848ee7b 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -2500,7 +2500,7 @@ mark_my_descriptor_dirty(const char *reason)
/** How frequently will we republish our descriptor because of large (factor
* of 2) shifts in estimated bandwidth? Note: We don't use this constant
* if our previous bandwidth estimate was exactly 0. */
-#define MAX_BANDWIDTH_CHANGE_FREQ (20*60)
+#define MAX_BANDWIDTH_CHANGE_FREQ (3*60*60)
/** Check whether bandwidth has changed a lot since the last time we announced
* bandwidth. If so, mark our descriptor dirty. */
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index bd200a783d..93c75ef583 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -2839,7 +2839,10 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
}
} SMARTLIST_FOREACH_END(node);
- if ((r = routerlist_find_my_routerinfo()))
+ /* If the node_t is not found we won't be to exclude ourself but we
+ * won't be able to pick ourself in router_choose_random_node() so
+ * this is fine to at least try with our routerinfo_t object. */
+ if ((r = router_get_my_routerinfo()))
routerlist_add_node_and_family(excludednodes, r);
router_add_running_nodes_to_smartlist(sl, need_uptime, need_capacity,
@@ -5197,10 +5200,23 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
log_warn(LD_BUG, "Failed to re-parse a router.");
continue;
}
+ /* need to compute this now, since add_to_routerlist may free. */
+ char time_cert_expires[ISO_TIME_LEN+1];
+ format_iso_time(time_cert_expires, ri->cert_expiration_time);
+
r = router_add_to_routerlist(ri, &msg, 1, 0);
if (WRA_WAS_OUTDATED(r)) {
- log_warn(LD_DIR, "Couldn't add re-parsed router: %s",
+ log_warn(LD_DIR, "Couldn't add re-parsed router: %s. This isn't "
+ "usually a big deal, but you should make sure that your "
+ "clock and timezone are set correctly.",
msg?msg:"???");
+ if (r == ROUTER_CERTS_EXPIRED) {
+ char time_cons[ISO_TIME_LEN+1];
+ format_iso_time(time_cons, consensus->valid_after);
+ log_warn(LD_DIR, " (I'm looking at a consensus from %s; This "
+ "router's certificates began expiring at %s.)",
+ time_cons, time_cert_expires);
+ }
}
} SMARTLIST_FOREACH_END(sd);
routerlist_assert_ok(rl);
diff --git a/src/or/scheduler.c b/src/or/scheduler.c
index 510c16b217..cd047d5a75 100644
--- a/src/or/scheduler.c
+++ b/src/or/scheduler.c
@@ -171,6 +171,8 @@ STATIC smartlist_t *channels_pending = NULL;
*/
STATIC struct event *run_sched_ev = NULL;
+static int have_logged_kist_suddenly_disabled = 0;
+
/*****************************************************************************
* Scheduling system static function definitions
*
@@ -252,13 +254,32 @@ select_scheduler(void)
case SCHEDULER_KIST:
if (!scheduler_can_use_kist()) {
#ifdef HAVE_KIST_SUPPORT
- log_notice(LD_SCHED, "Scheduler type KIST has been disabled by "
- "the consensus or no kernel support.");
+ if (!have_logged_kist_suddenly_disabled) {
+ /* We should only log this once in most cases. If it was the kernel
+ * losing support for kist that caused scheduler_can_use_kist() to
+ * return false, then this flag makes sure we only log this message
+ * once. If it was the consensus that switched from "yes use kist"
+ * to "no don't use kist", then we still set the flag so we log
+ * once, but we unset the flag elsewhere if we ever can_use_kist()
+ * again.
+ */
+ have_logged_kist_suddenly_disabled = 1;
+ log_notice(LD_SCHED, "Scheduler type KIST has been disabled by "
+ "the consensus or no kernel support.");
+ }
#else /* !(defined(HAVE_KIST_SUPPORT)) */
log_info(LD_SCHED, "Scheduler type KIST not built in");
#endif /* defined(HAVE_KIST_SUPPORT) */
continue;
}
+ /* This flag will only get set in one of two cases:
+ * 1 - the kernel lost support for kist. In that case, we don't expect to
+ * ever end up here
+ * 2 - the consensus went from "yes use kist" to "no don't use kist".
+ * We might end up here if the consensus changes back to "yes", in which
+ * case we might want to warn the user again if it goes back to "no"
+ * yet again. Thus we unset the flag */
+ have_logged_kist_suddenly_disabled = 0;
new_scheduler = get_kist_scheduler();
scheduler_kist_set_full_mode();
goto end;
diff --git a/src/or/scheduler_kist.c b/src/or/scheduler_kist.c
index d1726ba345..fea92705d4 100644
--- a/src/or/scheduler_kist.c
+++ b/src/or/scheduler_kist.c
@@ -525,9 +525,13 @@ kist_scheduler_schedule(void)
monotime_get(&now);
/* If time is really monotonic, we can never have now being smaller than the
- * last scheduler run. The scheduler_last_run at first is set to 0. */
+ * last scheduler run. The scheduler_last_run at first is set to 0.
+ * Unfortunately, not all platforms guarantee monotonic time so we log at
+ * info level but don't make it more noisy. */
diff = monotime_diff_msec(&scheduler_last_run, &now);
- IF_BUG_ONCE(diff < 0) {
+ if (diff < 0) {
+ log_info(LD_SCHED, "Monotonic time between now and last run of scheduler "
+ "is negative: %" PRId64 ". Setting diff to 0.", diff);
diff = 0;
}
if (diff < sched_run_interval) {
diff --git a/src/or/tor_api_internal.h b/src/or/tor_api_internal.h
index a69ba76420..10b6278b7b 100644
--- a/src/or/tor_api_internal.h
+++ b/src/or/tor_api_internal.h
@@ -16,5 +16,5 @@ struct tor_main_configuration_t {
char **argv;
};
-#endif
+#endif /* !defined(TOR_API_INTERNAL_H) */
diff --git a/src/rust/tor_rust/include.am b/src/rust/tor_rust/include.am
index 90f37a9f1b..c02324cb77 100644
--- a/src/rust/tor_rust/include.am
+++ b/src/rust/tor_rust/include.am
@@ -2,17 +2,23 @@ EXTRA_DIST +=\
src/rust/tor_rust/Cargo.toml \
src/rust/tor_rust/lib.rs
+EXTRA_CARGO_OPTIONS=
+
src/rust/target/release/@TOR_RUST_STATIC_NAME@: FORCE
- ( cd "$(abs_top_srcdir)/src/rust/tor_rust" ; \
+ ( cd "$(abs_top_builddir)/src/rust" ; \
CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \
CARGO_HOME="$(abs_top_builddir)/src/rust" \
- $(CARGO) build --release --quiet $(CARGO_ONLINE) )
+ $(CARGO) build --release $(EXTRA_CARGO_OPTIONS) \
+ $(CARGO_ONLINE) \
+ --manifest-path "$(abs_top_srcdir)/src/rust/tor_rust/Cargo.toml" )
distclean-rust:
- ( cd "$(abs_top_srcdir)/src/rust/tor_rust" ; \
+ ( cd "$(abs_top_builddir)/src/rust" ; \
CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \
CARGO_HOME="$(abs_top_builddir)/src/rust" \
- $(CARGO) clean --quiet $(CARGO_ONLINE) )
+ $(CARGO) clean $(EXTRA_CARGO_OPTIONS) \
+ $(CARGO_ONLINE) \
+ --manifest-path "$(abs_top_srcdir)/src/rust/tor_rust/Cargo.toml" )
rm -rf "$(abs_top_builddir)/src/rust/registry"
if USE_RUST
diff --git a/src/test/include.am b/src/test/include.am
index bbf8a370e2..b5c5dd24f7 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -125,6 +125,7 @@ src_test_test_SOURCES = \
src/test/test_hs_service.c \
src/test/test_hs_client.c \
src/test/test_hs_intropoint.c \
+ src/test/test_hs_control.c \
src/test/test_handles.c \
src/test/test_hs_cache.c \
src/test/test_hs_descriptor.c \
diff --git a/src/test/test.c b/src/test/test.c
index 00857c2386..432df35c34 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1201,6 +1201,7 @@ struct testgroup_t testgroups[] = {
{ "hs_cell/", hs_cell_tests },
{ "hs_common/", hs_common_tests },
{ "hs_config/", hs_config_tests },
+ { "hs_control/", hs_control_tests },
{ "hs_descriptor/", hs_descriptor },
{ "hs_ntor/", hs_ntor_tests },
{ "hs_service/", hs_service_tests },
diff --git a/src/test/test.h b/src/test/test.h
index b1a3366a80..b37fe25b33 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -210,6 +210,7 @@ extern struct testcase_t hs_cache[];
extern struct testcase_t hs_cell_tests[];
extern struct testcase_t hs_common_tests[];
extern struct testcase_t hs_config_tests[];
+extern struct testcase_t hs_control_tests[];
extern struct testcase_t hs_descriptor[];
extern struct testcase_t hs_ntor_tests[];
extern struct testcase_t hs_service_tests[];
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 79f72fca1f..4290d0dc6d 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -4003,7 +4003,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(port_cfg->entry_cfg.dns_request, OP_EQ, 1);
tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1);
tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1);
- tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.prefer_ipv6_virtaddr, OP_EQ, 1);
#endif /* defined(_WIN32) */
@@ -4346,7 +4346,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(ret, OP_EQ, 0);
tt_int_op(smartlist_len(slout), OP_EQ, 1);
port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
- tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.cache_ipv6_answers, OP_EQ, 1);
// Test success with no cache ipv4 DNS
@@ -5455,7 +5455,7 @@ test_config_check_bridge_distribution_setting_not_a_bridge(void *arg)
or_options_t* options = get_options_mutable();
or_options_t* old_options = options;
or_options_t* default_options = options;
- char* message = (char*)("");
+ char* message = NULL;
int ret;
(void)arg;
@@ -5466,8 +5466,11 @@ test_config_check_bridge_distribution_setting_not_a_bridge(void *arg)
ret = options_validate(old_options, options, default_options, 0, &message);
tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(message, OP_EQ, "You set BridgeDistribution, but you "
+ "didn't set BridgeRelay!");
done:
- return;
+ tor_free(message);
+ options->BridgeDistribution = NULL;
}
/* If the BridgeDistribution setting was valid, 0 should be returned. */
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 472fcb8c53..af19f63f6c 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -6,6 +6,7 @@
#include "bridges.h"
#include "control.h"
#include "entrynodes.h"
+#include "hs_common.h"
#include "networkstatus.h"
#include "rendservice.h"
#include "routerlist.h"
@@ -13,10 +14,87 @@
#include "test_helpers.h"
static void
-test_add_onion_helper_keyarg(void *arg)
+test_add_onion_helper_keyarg_v3(void *arg)
{
- crypto_pk_t *pk = NULL;
- crypto_pk_t *pk2 = NULL;
+ int ret, hs_version;
+ add_onion_secret_key_t pk;
+ char *key_new_blob = NULL;
+ char *err_msg = NULL;
+ const char *key_new_alg = NULL;
+
+ (void) arg;
+
+ memset(&pk, 0, sizeof(pk));
+
+ /* Test explicit ED25519-V3 key generation. */
+ ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg,
+ &key_new_blob, &pk, &hs_version,
+ &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
+ tt_assert(pk.v3);
+ tt_str_op(key_new_alg, OP_EQ, "ED25519-V3");
+ tt_assert(key_new_blob);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
+ tor_free(pk.v3); pk.v3 = NULL;
+ tor_free(key_new_blob);
+
+ /* Test discarding the private key. */
+ ret = add_onion_helper_keyarg("NEW:ED25519-V3", 1, &key_new_alg,
+ &key_new_blob, &pk, &hs_version,
+ &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
+ tt_assert(pk.v3);
+ tt_ptr_op(key_new_alg, OP_EQ, NULL);
+ tt_ptr_op(key_new_blob, OP_EQ, NULL);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
+ tor_free(pk.v3); pk.v3 = NULL;
+ tor_free(key_new_blob);
+
+ /* Test passing a key blob. */
+ {
+ /* The base64 key and hex key are the same. Hex key is 64 bytes long. The
+ * sk has been generated randomly using python3. */
+ const char *base64_sk =
+ "a9bT19PqGC9Y+BmOo1IQvCGjjwxMiaaxEXZ+FKMxpEQW"
+ "6AmSV5roThUGMRCaqQSCnR2jI1vL2QxHORzI4RxMmw==";
+ const char *hex_sk =
+ "\x6b\xd6\xd3\xd7\xd3\xea\x18\x2f\x58\xf8\x19\x8e\xa3\x52\x10\xbc"
+ "\x21\xa3\x8f\x0c\x4c\x89\xa6\xb1\x11\x76\x7e\x14\xa3\x31\xa4\x44"
+ "\x16\xe8\x09\x92\x57\x9a\xe8\x4e\x15\x06\x31\x10\x9a\xa9\x04\x82"
+ "\x9d\x1d\xa3\x23\x5b\xcb\xd9\x0c\x47\x39\x1c\xc8\xe1\x1c\x4c\x9b";
+ char *key_blob = NULL;
+
+ tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk);
+ tt_assert(key_blob);
+ ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg,
+ &key_new_blob, &pk, &hs_version,
+ &err_msg);
+ tor_free(key_blob);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
+ tt_assert(pk.v3);
+ tt_mem_op(pk.v3, OP_EQ, hex_sk, 64);
+ tt_ptr_op(key_new_alg, OP_EQ, NULL);
+ tt_ptr_op(key_new_blob, OP_EQ, NULL);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
+ tor_free(pk.v3); pk.v3 = NULL;
+ tor_free(key_new_blob);
+ }
+
+ done:
+ tor_free(pk.v3);
+ tor_free(key_new_blob);
+ tor_free(err_msg);
+}
+
+static void
+test_add_onion_helper_keyarg_v2(void *arg)
+{
+ int ret, hs_version;
+ add_onion_secret_key_t pk;
+ crypto_pk_t *pk1 = NULL;
const char *key_new_alg = NULL;
char *key_new_blob = NULL;
char *err_msg = NULL;
@@ -25,83 +103,100 @@ test_add_onion_helper_keyarg(void *arg)
(void) arg;
+ memset(&pk, 0, sizeof(pk));
+
/* Test explicit RSA1024 key generation. */
- pk = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_assert(pk);
+ ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(pk.v2);
tt_str_op(key_new_alg, OP_EQ, "RSA1024");
tt_assert(key_new_blob);
tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test "BEST" key generation (Assumes BEST = RSA1024). */
- crypto_pk_free(pk);
+ crypto_pk_free(pk.v2); pk.v2 = NULL;
tor_free(key_new_blob);
- pk = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_assert(pk);
+ ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(pk.v2);
tt_str_op(key_new_alg, OP_EQ, "RSA1024");
tt_assert(key_new_blob);
tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test discarding the private key. */
- crypto_pk_free(pk);
+ crypto_pk_free(pk.v2); pk.v2 = NULL;
tor_free(key_new_blob);
- pk = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_assert(pk);
+ ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test generating a invalid key type. */
- crypto_pk_free(pk);
- pk = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_ptr_op(pk, OP_EQ, NULL);
+ crypto_pk_free(pk.v2); pk.v2 = NULL;
+ ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(!pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_assert(err_msg);
/* Test loading a RSA1024 key. */
tor_free(err_msg);
- pk = pk_generate(0);
- tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk, &encoded));
+ pk1 = pk_generate(0);
+ tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded));
tor_asprintf(&arg_str, "RSA1024:%s", encoded);
- pk2 = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_assert(pk2);
+ ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_ptr_op(err_msg, OP_EQ, NULL);
- tt_int_op(crypto_pk_cmp_keys(pk, pk2), OP_EQ, 0);
+ tt_int_op(crypto_pk_cmp_keys(pk1, pk.v2), OP_EQ, 0);
/* Test loading a invalid key type. */
tor_free(arg_str);
- crypto_pk_free(pk); pk = NULL;
+ crypto_pk_free(pk1); pk1 = NULL;
+ crypto_pk_free(pk.v2); pk.v2 = NULL;
tor_asprintf(&arg_str, "RSA512:%s", encoded);
- pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_ptr_op(pk, OP_EQ, NULL);
+ ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(!pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_assert(err_msg);
/* Test loading a invalid key. */
tor_free(arg_str);
- crypto_pk_free(pk); pk = NULL;
+ crypto_pk_free(pk.v2); pk.v2 = NULL;
tor_free(err_msg);
encoded[strlen(encoded)/2] = '\0';
tor_asprintf(&arg_str, "RSA1024:%s", encoded);
- pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_ptr_op(pk, OP_EQ, NULL);
+ ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(!pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_assert(err_msg);
done:
- crypto_pk_free(pk);
- crypto_pk_free(pk2);
+ crypto_pk_free(pk1);
+ crypto_pk_free(pk.v2);
tor_free(key_new_blob);
tor_free(err_msg);
tor_free(encoded);
@@ -1370,7 +1465,10 @@ test_download_status_bridge(void *arg)
}
struct testcase_t controller_tests[] = {
- { "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL },
+ { "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0,
+ NULL, NULL },
+ { "add_onion_helper_keyarg_v3", test_add_onion_helper_keyarg_v3, 0,
+ NULL, NULL },
{ "getinfo_helper_onion", test_getinfo_helper_onion, 0, NULL, NULL },
{ "rend_service_parse_port_config", test_rend_service_parse_port_config, 0,
NULL, NULL },
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index f9d981953d..80ebebe3f8 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -7,6 +7,7 @@
#define STATEFILE_PRIVATE
#define ENTRYNODES_PRIVATE
#define ROUTERLIST_PRIVATE
+#define DIRECTORY_PRIVATE
#include "or.h"
#include "test.h"
@@ -15,6 +16,7 @@
#include "circuitlist.h"
#include "config.h"
#include "confparse.h"
+#include "directory.h"
#include "entrynodes.h"
#include "nodelist.h"
#include "networkstatus.h"
@@ -129,6 +131,14 @@ big_fake_network_setup(const struct testcase_t *testcase)
n->rs->has_bandwidth = 1;
n->rs->bandwidth_kb = 30;
+ /* Make a random nickname for each node */
+ {
+ char nickname_binary[8];
+ crypto_rand(nickname_binary, sizeof(nickname_binary));
+ base64_encode(n->rs->nickname, sizeof(n->rs->nickname),
+ nickname_binary, sizeof(nickname_binary), 0);
+ }
+
/* Call half of the nodes a possible guard. */
if (i % 2 == 0) {
n->is_possible_guard = 1;
@@ -1719,6 +1729,7 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg)
/* Simpler cases: no gaurds are confirmed yet. */
(void)arg;
guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL);
+ entry_guard_restriction_t *rst = NULL;
/* simple starting configuration */
entry_guards_update_primary(gs);
@@ -1800,14 +1811,13 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg)
tt_ptr_op(g2, OP_EQ, g);
/* But if we impose a restriction, we don't get the same guard */
- entry_guard_restriction_t rst;
- memset(&rst, 0, sizeof(rst));
- memcpy(rst.exclude_id, g->identity, DIGEST_LEN);
- g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state);
+ rst = guard_create_exit_restriction((uint8_t*)g->identity);
+ g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state);
tt_ptr_op(g2, OP_NE, g);
done:
guard_selection_free(gs);
+ entry_guard_restriction_free(rst);
}
static void
@@ -1817,6 +1827,7 @@ test_entry_guard_select_for_circuit_confirmed(void *arg)
guards, we use a confirmed guard. */
(void)arg;
int i;
+ entry_guard_restriction_t *rst = NULL;
guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL);
const int N_CONFIRMED = 10;
@@ -1877,10 +1888,8 @@ test_entry_guard_select_for_circuit_confirmed(void *arg)
get_options_mutable()->EnforceDistinctSubnets = 0;
g = smartlist_get(gs->confirmed_entry_guards,
smartlist_len(gs->primary_entry_guards)+2);
- entry_guard_restriction_t rst;
- memset(&rst, 0, sizeof(rst));
- memcpy(rst.exclude_id, g->identity, DIGEST_LEN);
- g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state);
+ rst = guard_create_exit_restriction((uint8_t*)g->identity);
+ g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state);
tt_ptr_op(g2, OP_NE, NULL);
tt_ptr_op(g2, OP_NE, g);
tt_int_op(g2->confirmed_idx, OP_EQ,
@@ -1906,13 +1915,13 @@ test_entry_guard_select_for_circuit_confirmed(void *arg)
// Regression test for bug 22753/TROVE-2017-006.
get_options_mutable()->EnforceDistinctSubnets = 1;
g = smartlist_get(gs->confirmed_entry_guards, 0);
- memset(&rst, 0, sizeof(rst));
- memcpy(rst.exclude_id, g->identity, DIGEST_LEN);
- g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state);
+ memcpy(rst->exclude_id, g->identity, DIGEST_LEN);
+ g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state);
tt_ptr_op(g2, OP_EQ, NULL);
done:
guard_selection_free(gs);
+ entry_guard_restriction_free(rst);
}
static void
@@ -2493,9 +2502,7 @@ test_entry_guard_upgrade_not_blocked_by_restricted_circ_complete(void *arg)
/* Once more, let circ1 become complete. But this time, we'll claim
* that circ2 was restricted to not use the same guard as circ1. */
data->guard2_state->restrictions =
- tor_malloc_zero(sizeof(entry_guard_restriction_t));
- memcpy(data->guard2_state->restrictions->exclude_id,
- data->guard1->identity, DIGEST_LEN);
+ guard_create_exit_restriction((uint8_t*)data->guard1->identity);
smartlist_t *result = smartlist_new();
int r;
@@ -2604,9 +2611,7 @@ test_entry_guard_upgrade_not_blocked_by_restricted_circ_pending(void *arg)
}
data->guard2_state->restrictions =
- tor_malloc_zero(sizeof(entry_guard_restriction_t));
- memcpy(data->guard2_state->restrictions->exclude_id,
- data->guard1->identity, DIGEST_LEN);
+ guard_create_exit_restriction((uint8_t*)data->guard1->identity);
smartlist_t *result = smartlist_new();
int r;
@@ -2674,6 +2679,114 @@ test_enty_guard_should_expire_waiting(void *arg)
tor_free(fake_state);
}
+static void
+mock_directory_initiate_request(directory_request_t *req)
+{
+ if (req->guard_state) {
+ circuit_guard_state_free(req->guard_state);
+ }
+}
+
+static networkstatus_t *mock_ns_val = NULL;
+static networkstatus_t *
+mock_ns_get_by_flavor(consensus_flavor_t f)
+{
+ (void)f;
+ return mock_ns_val;
+}
+
+/** Test that when we fetch microdescriptors we skip guards that have
+ * previously failed to serve us needed microdescriptors. */
+static void
+test_entry_guard_outdated_dirserver_exclusion(void *arg)
+{
+ int retval;
+ response_handler_args_t *args = NULL;
+ dir_connection_t *conn = NULL;
+ (void) arg;
+
+ /* Test prep: Make a new guard selection */
+ guard_selection_t *gs = get_guard_selection_by_name("default",
+ GS_TYPE_NORMAL, 1);
+
+ /* ... we want to use entry guards */
+ or_options_t *options = get_options_mutable();
+ options->UseEntryGuards = 1;
+ options->UseBridges = 0;
+
+ /* ... prepare some md digests we want to download in the future */
+ smartlist_t *digests = smartlist_new();
+ const char *prose = "unhurried and wise, we perceive.";
+ for (int i = 0; i < 20; i++) {
+ smartlist_add(digests, (char*)prose);
+ }
+
+ tt_int_op(smartlist_len(digests), OP_EQ, 20);
+
+ /* ... now mock some functions */
+ mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
+ MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor);
+ MOCK(directory_initiate_request, mock_directory_initiate_request);
+
+ /* Test logic:
+ * 0. Create a proper guard set and primary guard list.
+ * 1. Pretend to fail microdescriptor fetches from all the primary guards.
+ * 2. Order another microdescriptor fetch and make sure that primary guards
+ * get skipped since they failed previous fetches.
+ */
+
+ { /* Setup primary guard list */
+ int i;
+ entry_guards_update_primary(gs);
+ for (i = 0; i < DFLT_N_PRIMARY_GUARDS; ++i) {
+ entry_guard_t *guard = smartlist_get(gs->sampled_entry_guards, i);
+ make_guard_confirmed(gs, guard);
+ }
+ entry_guards_update_primary(gs);
+ }
+
+ {
+ /* Fail microdesc fetches with all the primary guards */
+ args = tor_malloc_zero(sizeof(response_handler_args_t));
+ args->status_code = 404;
+ args->reason = NULL;
+ args->body = NULL;
+ args->body_len = 0;
+
+ conn = tor_malloc_zero(sizeof(dir_connection_t));
+ conn->requested_resource = tor_strdup("d/jlinblackorigami");
+ conn->base_.purpose = DIR_PURPOSE_FETCH_MICRODESC;
+
+ /* Pretend to fail fetches with all primary guards */
+ SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards,const entry_guard_t *,g) {
+ memcpy(conn->identity_digest, g->identity, DIGEST_LEN);
+
+ retval = handle_response_fetch_microdesc(conn, args);
+ tt_int_op(retval, OP_EQ, 0);
+ } SMARTLIST_FOREACH_END(g);
+ }
+
+ {
+ /* Now order the final md download */
+ setup_full_capture_of_logs(LOG_INFO);
+ initiate_descriptor_downloads(NULL, DIR_PURPOSE_FETCH_MICRODESC,
+ digests, 3, 7, 0);
+
+ /* ... and check that because we failed to fetch microdescs from all our
+ * primaries, we didnt end up selecting a primary for fetching dir info */
+ expect_log_msg_containing("No primary or confirmed guards available.");
+ teardown_capture_of_logs();
+ }
+
+ done:
+ smartlist_free(digests);
+ tor_free(args);
+ if (conn) {
+ tor_free(conn->requested_resource);
+ tor_free(conn);
+ }
+}
+
static const struct testcase_setup_t big_fake_network = {
big_fake_network_setup, big_fake_network_cleanup
};
@@ -2734,6 +2847,7 @@ struct testcase_t entrynodes_tests[] = {
BFN_TEST(select_for_circuit_highlevel_primary_retry),
BFN_TEST(select_and_cancel),
BFN_TEST(drop_guards),
+ BFN_TEST(outdated_dirserver_exclusion),
UPGRADE_TEST(upgrade_a_circuit, "c1-done c2-done"),
UPGRADE_TEST(upgrade_blocked_by_live_primary_guards, "c1-done c2-done"),
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
index 7737499f50..55c6218dd1 100644
--- a/src/test/test_hs.c
+++ b/src/test/test_hs.c
@@ -258,8 +258,9 @@ test_hs_desc_event(void *arg)
sizeof(desc_id_base32));
/* test request event */
- control_event_hs_descriptor_requested(&rend_query.base_, HSDIR_EXIST_ID,
- STR_DESC_ID_BASE32);
+ control_event_hs_descriptor_requested(rend_query.onion_address,
+ rend_query.auth_type, HSDIR_EXIST_ID,
+ STR_DESC_ID_BASE32, NULL);
expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\
STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n";
tt_assert(received_msg);
@@ -268,8 +269,8 @@ test_hs_desc_event(void *arg)
/* test received event */
rend_query.auth_type = REND_BASIC_AUTH;
- control_event_hs_descriptor_received(rend_query.onion_address,
- &rend_query.base_, HSDIR_EXIST_ID);
+ control_event_hsv2_descriptor_received(rend_query.onion_address,
+ &rend_query.base_, HSDIR_EXIST_ID);
expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\
STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n";
tt_assert(received_msg);
@@ -278,7 +279,7 @@ test_hs_desc_event(void *arg)
/* test failed event */
rend_query.auth_type = REND_STEALTH_AUTH;
- control_event_hs_descriptor_failed(&rend_query.base_,
+ control_event_hsv2_descriptor_failed(&rend_query.base_,
HSDIR_NONE_EXIST_ID,
"QUERY_REJECTED");
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\
@@ -289,7 +290,7 @@ test_hs_desc_event(void *arg)
/* test invalid auth type */
rend_query.auth_type = 999;
- control_event_hs_descriptor_failed(&rend_query.base_,
+ control_event_hsv2_descriptor_failed(&rend_query.base_,
HSDIR_EXIST_ID,
"QUERY_REJECTED");
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\
@@ -301,7 +302,7 @@ test_hs_desc_event(void *arg)
/* test no HSDir fingerprint type */
rend_query.auth_type = REND_NO_AUTH;
- control_event_hs_descriptor_failed(&rend_query.base_, NULL,
+ control_event_hsv2_descriptor_failed(&rend_query.base_, NULL,
"QUERY_NO_HSDIR");
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" NO_AUTH " \
"UNKNOWN REASON=QUERY_NO_HSDIR\r\n";
diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c
new file mode 100644
index 0000000000..207a55de6d
--- /dev/null
+++ b/src/test/test_hs_control.c
@@ -0,0 +1,199 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_control.c
+ * \brief Unit tests for hidden service control port event and command.
+ **/
+
+#define CONTROL_PRIVATE
+#define CIRCUITBUILD_PRIVATE
+#define RENDCOMMON_PRIVATE
+#define RENDSERVICE_PRIVATE
+#define HS_SERVICE_PRIVATE
+
+#include "or.h"
+#include "test.h"
+#include "control.h"
+#include "config.h"
+#include "hs_common.h"
+#include "hs_control.h"
+#include "nodelist.h"
+//#include "rendcommon.h"
+//#include "rendservice.h"
+//#include "routerset.h"
+//#include "circuitbuild.h"
+#include "test_helpers.h"
+
+/* mock ID digest and longname for node that's in nodelist */
+#define HSDIR_EXIST_ID \
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" \
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+#define STR_HSDIR_EXIST_LONGNAME \
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=TestDir"
+#define STR_HSDIR_NONE_EXIST_LONGNAME \
+ "$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+
+/* Helper global variable for hidden service descriptor event test.
+ * It's used as a pointer to dynamically created message buffer in
+ * send_control_event_string_replacement function, which mocks
+ * send_control_event_string function.
+ *
+ * Always free it after use! */
+static char *received_msg = NULL;
+
+/** Mock function for send_control_event_string
+ */
+static void
+queue_control_event_string_replacement(uint16_t event, char *msg)
+{
+ (void) event;
+ tor_free(received_msg);
+ received_msg = msg;
+}
+
+/** Mock function for node_describe_longname_by_id, it returns either
+ * STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME
+ */
+static const char *
+node_describe_longname_by_id_replacement(const char *id_digest)
+{
+ if (!strcmp(id_digest, HSDIR_EXIST_ID)) {
+ return STR_HSDIR_EXIST_LONGNAME;
+ } else {
+ return STR_HSDIR_NONE_EXIST_LONGNAME;
+ }
+}
+
+/* HSDir fetch index is a series of 'D' */
+#define HSDIR_INDEX_FETCH_HEX \
+ "4343434343434343434343434343434343434343434343434343434343434343"
+#define HSDIR_INDEX_STORE_HEX \
+ "4444444444444444444444444444444444444444444444444444444444444444"
+
+static const node_t *
+mock_node_get_by_id(const char *digest)
+{
+ static node_t node;
+ memcpy(node.identity, digest, DIGEST_LEN);
+ node.hsdir_index = tor_malloc_zero(sizeof(hsdir_index_t));
+ memset(node.hsdir_index->fetch, 'C', DIGEST256_LEN);
+ memset(node.hsdir_index->store_first, 'D', DIGEST256_LEN);
+ return &node;
+}
+
+static void
+test_hs_desc_event(void *arg)
+{
+ int ret;
+ char *expected_msg = NULL;
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ ed25519_keypair_t identity_kp;
+ ed25519_public_key_t blinded_pk;
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+ routerstatus_t hsdir_rs;
+ hs_ident_dir_conn_t ident;
+
+ (void) arg;
+ MOCK(queue_control_event_string,
+ queue_control_event_string_replacement);
+ MOCK(node_describe_longname_by_id,
+ node_describe_longname_by_id_replacement);
+ MOCK(node_get_by_id, mock_node_get_by_id);
+
+ /* Setup what we need for this test. */
+ ed25519_keypair_generate(&identity_kp, 0);
+ hs_build_address(&identity_kp.pubkey, HS_VERSION_THREE, onion_address);
+ ret = hs_address_is_valid(onion_address);
+ tt_int_op(ret, OP_EQ, 1);
+ memset(&blinded_pk, 'B', sizeof(blinded_pk));
+ memset(&hsdir_rs, 0, sizeof(hsdir_rs));
+ memcpy(hsdir_rs.identity_digest, HSDIR_EXIST_ID, DIGEST_LEN);
+ ret = ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
+ tt_int_op(ret, OP_EQ, 0);
+ memcpy(&ident.identity_pk, &identity_kp.pubkey,
+ sizeof(ed25519_public_key_t));
+ memcpy(&ident.blinded_pk, &blinded_pk, sizeof(blinded_pk));
+
+ /* HS_DESC REQUESTED ... */
+ hs_control_desc_event_requested(&identity_kp.pubkey, base64_blinded_pk,
+ &hsdir_rs);
+ tor_asprintf(&expected_msg, "650 HS_DESC REQUESTED %s NO_AUTH "
+ STR_HSDIR_EXIST_LONGNAME " %s HSDIR_INDEX="
+ HSDIR_INDEX_FETCH_HEX "\r\n",
+ onion_address, base64_blinded_pk);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ /* HS_DESC CREATED... */
+ hs_control_desc_event_created(onion_address, &blinded_pk);
+ tor_asprintf(&expected_msg, "650 HS_DESC CREATED %s UNKNOWN "
+ "UNKNOWN %s\r\n",
+ onion_address, base64_blinded_pk);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ /* HS_DESC UPLOAD... */
+ uint8_t hsdir_index_store[DIGEST256_LEN];
+ memset(hsdir_index_store, 'D', sizeof(hsdir_index_store));
+ hs_control_desc_event_upload(onion_address, HSDIR_EXIST_ID,
+ &blinded_pk, hsdir_index_store);
+ tor_asprintf(&expected_msg, "650 HS_DESC UPLOAD %s UNKNOWN "
+ STR_HSDIR_EXIST_LONGNAME " %s "
+ "HSDIR_INDEX=" HSDIR_INDEX_STORE_HEX "\r\n",
+ onion_address, base64_blinded_pk);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ /* HS_DESC FAILED... */
+ hs_control_desc_event_failed(&ident, HSDIR_EXIST_ID, "BAD_DESC");
+ tor_asprintf(&expected_msg, "650 HS_DESC FAILED %s NO_AUTH "
+ STR_HSDIR_EXIST_LONGNAME " %s "
+ "REASON=BAD_DESC\r\n",
+ onion_address, base64_blinded_pk);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ /* HS_DESC RECEIVED... */
+ hs_control_desc_event_received(&ident, HSDIR_EXIST_ID);
+ tor_asprintf(&expected_msg, "650 HS_DESC RECEIVED %s NO_AUTH "
+ STR_HSDIR_EXIST_LONGNAME " %s\r\n",
+ onion_address, base64_blinded_pk);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ /* HS_DESC UPLOADED... */
+ hs_control_desc_event_uploaded(&ident, HSDIR_EXIST_ID);
+ tor_asprintf(&expected_msg, "650 HS_DESC UPLOADED %s UNKNOWN "
+ STR_HSDIR_EXIST_LONGNAME "\r\n",
+ onion_address);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ done:
+ UNMOCK(queue_control_event_string);
+ UNMOCK(node_describe_longname_by_id);
+ UNMOCK(node_get_by_id);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+}
+
+struct testcase_t hs_control_tests[] = {
+ { "hs_desc_event", test_hs_desc_event, TT_FORK,
+ NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh
index 8d7900e1df..133f2bb940 100755
--- a/src/test/test_rust.sh
+++ b/src/test/test_rust.sh
@@ -8,8 +8,12 @@ exitcode=0
set -e
for crate in $crates; do
- cd "${abs_top_srcdir:-.}/src/rust/${crate}"
- CARGO_TARGET_DIR="${abs_top_builddir:-../../..}/src/rust/target" CARGO_HOME="${abs_top_builddir:-../../..}/src/rust" "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} || exitcode=1
+ cd "${abs_top_builddir:-../../..}/src/rust"
+ CARGO_TARGET_DIR="${abs_top_builddir:-../../..}/src/rust/target" \
+ CARGO_HOME="${abs_top_builddir:-../../..}/src/rust" \
+ "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} \
+ --manifest-path "${abs_top_srcdir:-.}/src/rust/${crate}/Cargo.toml" \
+ || exitcode=1
cd -
done