aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/buffers.c17
-rw-r--r--src/or/channeltls.c3
-rw-r--r--src/or/circuitbuild.c13
-rw-r--r--src/or/circuitstats.c6
-rw-r--r--src/or/circuituse.c29
-rw-r--r--src/or/config.c324
-rw-r--r--src/or/config.h7
-rw-r--r--src/or/config_codedigest.c13
-rw-r--r--src/or/connection.c30
-rw-r--r--src/or/connection_edge.c97
-rw-r--r--src/or/connection_or.c1
-rw-r--r--src/or/control.c1046
-rw-r--r--src/or/control.h62
-rw-r--r--src/or/cpuworker.c8
-rw-r--r--src/or/directory.c41
-rw-r--r--src/or/dirserv.c2
-rw-r--r--src/or/dirvote.c5
-rw-r--r--src/or/dns.c2
-rw-r--r--src/or/entrynodes.c76
-rw-r--r--src/or/include.am65
-rw-r--r--src/or/main.c337
-rw-r--r--src/or/main.h1
-rw-r--r--src/or/or.h49
-rw-r--r--src/or/rendclient.c453
-rw-r--r--src/or/rendclient.h8
-rw-r--r--src/or/rendcommon.c223
-rw-r--r--src/or/rendcommon.h19
-rw-r--r--src/or/rendmid.c14
-rw-r--r--src/or/rendservice.c731
-rw-r--r--src/or/rendservice.h25
-rw-r--r--src/or/rephist.c44
-rw-r--r--src/or/rephist.h4
-rw-r--r--src/or/router.c77
-rw-r--r--src/or/router.h1
-rw-r--r--src/or/routerlist.c6
-rw-r--r--src/or/routerparse.c35
-rw-r--r--src/or/routerparse.h1
-rw-r--r--src/or/status.c4
-rw-r--r--src/or/transports.c5
39 files changed, 2927 insertions, 957 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c
index 9f5dc70ed5..2d7dd937d8 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -20,8 +20,8 @@
#include "control.h"
#include "reasons.h"
#include "ext_orport.h"
-#include "../common/util.h"
-#include "../common/torlog.h"
+#include "util.h"
+#include "torlog.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -232,7 +232,7 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate)
size_t n = bytes - dest->datalen;
src = dest->next;
tor_assert(src);
- if (n > src->datalen) {
+ if (n >= src->datalen) {
memcpy(CHUNK_WRITE_PTR(dest), src->data, src->datalen);
dest->datalen += src->datalen;
dest->next = src->next;
@@ -615,7 +615,7 @@ read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf)
if (r < 0)
return r; /* Error */
tor_assert(total_read+r < INT_MAX);
- total_read += r;
+ total_read += r;
if ((size_t)r < readlen) /* eof, block, or no more to read. */
break;
}
@@ -2436,7 +2436,14 @@ assert_buf_ok(buf_t *buf)
total += ch->datalen;
tor_assert(ch->datalen <= ch->memlen);
tor_assert(ch->data >= &ch->mem[0]);
- tor_assert(ch->data < &ch->mem[0]+ch->memlen);
+ tor_assert(ch->data <= &ch->mem[0]+ch->memlen);
+ if (ch->data == &ch->mem[0]+ch->memlen) {
+ static int warned = 0;
+ if (! warned) {
+ log_warn(LD_BUG, "Invariant violation in buf.c related to #15083");
+ warned = 1;
+ }
+ }
tor_assert(ch->data+ch->datalen <= &ch->mem[0] + ch->memlen);
if (!ch->next)
tor_assert(ch == buf->tail);
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index 0376e74bcf..ecf02182fc 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -26,6 +26,7 @@
#include "control.h"
#include "link_handshake.h"
#include "relay.h"
+#include "rephist.h"
#include "router.h"
#include "routerlist.h"
#include "scheduler.h"
@@ -1457,6 +1458,8 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan)
return;
}
+ rep_hist_note_negotiated_link_proto(highest_supported_version, started_here);
+
chan->conn->link_proto = highest_supported_version;
chan->conn->handshake_state->received_versions = 1;
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 946c002735..0688398f6d 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -1053,6 +1053,10 @@ circuit_note_clock_jumped(int seconds_elapsed)
"CLOCK_JUMPED");
circuit_mark_all_unused_circs();
circuit_mark_all_dirty_circs_as_unusable();
+ if (seconds_elapsed < 0) {
+ /* Restart all the timers in case we jumped a long way into the past. */
+ reset_all_main_loop_timers();
+ }
}
/** Take the 'extend' <b>cell</b>, pull out addr/port plus the onion
@@ -1396,9 +1400,12 @@ onionskin_answer(or_circuit_t *circ,
log_debug(LD_CIRC,"Finished sending '%s' cell.",
circ->is_first_hop ? "created_fast" : "created");
- /* Ignore the local bit when testing - many test networks run on local
- * addresses */
- if ((!channel_is_local(circ->p_chan) || get_options()->TestingTorNetwork)
+ /* Ignore the local bit when ExtendAllowPrivateAddresses is set:
+ * it violates the assumption that private addresses are local.
+ * Also, many test networks run on local addresses, and
+ * TestingTorNetwork sets ExtendAllowPrivateAddresses. */
+ if ((!channel_is_local(circ->p_chan)
+ || get_options()->ExtendAllowPrivateAddresses)
&& !channel_is_outgoing(circ->p_chan)) {
/* record that we could process create cells from a non-local conn
* that we didn't initiate; presumably this means that create cells
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index 7b3ad56537..3ced5afad5 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -1232,6 +1232,9 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt)
}
cbt->liveness.network_last_live = now;
cbt->liveness.nonlive_timeouts = 0;
+
+ /* Tell control.c */
+ control_event_network_liveness_update(1);
}
/**
@@ -1316,6 +1319,9 @@ circuit_build_times_network_close(circuit_build_times_t *cbt,
"Tor has not observed any network activity for the past %d "
"seconds. Disabling circuit build timeout recording.",
(int)(now - cbt->liveness.network_last_live));
+
+ /* Tell control.c */
+ control_event_network_liveness_update(0);
} else {
log_info(LD_CIRC,
"Got non-live timeout. Current count is: %d",
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index d0d31ad9cf..a429a7d053 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1189,17 +1189,28 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
if (CIRCUIT_IS_ORIGIN(circ)) {
origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ int removed = 0;
if (conn == origin_circ->p_streams) {
origin_circ->p_streams = conn->next_stream;
- return;
+ removed = 1;
+ } else {
+ for (prevconn = origin_circ->p_streams;
+ prevconn && prevconn->next_stream && prevconn->next_stream != conn;
+ prevconn = prevconn->next_stream)
+ ;
+ if (prevconn && prevconn->next_stream) {
+ prevconn->next_stream = conn->next_stream;
+ removed = 1;
+ }
}
-
- for (prevconn = origin_circ->p_streams;
- prevconn && prevconn->next_stream && prevconn->next_stream != conn;
- prevconn = prevconn->next_stream)
- ;
- if (prevconn && prevconn->next_stream) {
- prevconn->next_stream = conn->next_stream;
+ if (removed) {
+ /* If the stream was removed, and it was a rend stream, decrement the
+ * number of streams on the circuit associated with the rend service.
+ */
+ if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
+ tor_assert(origin_circ->rend_data);
+ origin_circ->rend_data->nr_streams--;
+ }
return;
}
} else {
@@ -2149,7 +2160,7 @@ link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ,
* that an attempt to connect to a hidden service just
* succeeded. Tell rendclient.c. */
rend_client_note_connection_attempt_ended(
- ENTRY_TO_EDGE_CONN(apconn)->rend_data->onion_address);
+ ENTRY_TO_EDGE_CONN(apconn)->rend_data);
}
if (cpath) { /* we were given one; use it */
diff --git a/src/or/config.c b/src/or/config.c
index adda5287f9..ef249a653b 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -225,7 +225,7 @@ static config_var_t option_vars_[] = {
V(DisableDebuggerAttachment, BOOL, "1"),
V(DisableIOCP, BOOL, "1"),
OBSOLETE("DisableV2DirectoryInfo_"),
- V(DynamicDHGroups, BOOL, "0"),
+ OBSOLETE("DynamicDHGroups"),
VPORT(DNSPort, LINELIST, NULL),
V(DNSListenAddress, LINELIST, NULL),
V(DownloadExtraInfo, BOOL, "0"),
@@ -286,6 +286,8 @@ static config_var_t option_vars_[] = {
VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
VAR("HiddenServiceAllowUnknownPorts",LINELIST_S, RendConfigLines, NULL),
+ VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL),
+ VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL),
V(HiddenServiceStatistics, BOOL, "0"),
V(HidServAuth, LINELIST, NULL),
V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"),
@@ -553,8 +555,6 @@ static char *get_bindaddr_from_transport_listen_line(const char *line,
static int parse_dir_authority_line(const char *line,
dirinfo_type_t required_type,
int validate_only);
-static int parse_dir_fallback_line(const char *line,
- int validate_only);
static void port_cfg_free(port_cfg_t *port);
static int parse_ports(or_options_t *options, int validate_only,
char **msg_out, int *n_ports_out);
@@ -849,6 +849,41 @@ escaped_safe_str(const char *address)
return escaped(address);
}
+/** List of default directory authorities */
+
+static const char *default_authorities[] = {
+ "moria1 orport=9101 "
+ "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
+ "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
+ "tor26 orport=443 "
+ "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
+ "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D",
+ "dizum orport=443 "
+ "v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 "
+ "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755",
+ "Tonga orport=443 bridge "
+ "82.94.251.203:80 4A0C CD2D DC79 9508 3D73 F5D6 6710 0C8A 5831 F16D",
+ "gabelmoo orport=443 "
+ "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 "
+ "131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281",
+ "dannenberg orport=443 "
+ "v3ident=585769C78764D58426B8B52B6651A5A71137189A "
+ "193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123",
+ "urras orport=80 "
+ "v3ident=80550987E1D626E3EBA5E5E75A458DE0626D088C "
+ "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417",
+ "maatuska orport=80 "
+ "v3ident=49015F787433103580E3B66A1707A00E60F2D15B "
+ "171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810",
+ "Faravahar orport=443 "
+ "v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 "
+ "154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC",
+ "longclaw orport=443 "
+ "v3ident=23D15D965BC35114467363C165C4F724B64B4F66 "
+ "199.254.238.52:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145",
+ NULL
+};
+
/** Add the default directory authorities directly into the trusted dir list,
* but only add them insofar as they share bits with <b>type</b>.
* Each authority's bits are restricted to the bits shared with <b>type</b>.
@@ -857,50 +892,18 @@ static void
add_default_trusted_dir_authorities(dirinfo_type_t type)
{
int i;
- const char *authorities[] = {
- "moria1 orport=9101 "
- "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
- "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
- "tor26 orport=443 "
- "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
- "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D",
- "dizum orport=443 "
- "v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 "
- "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755",
- "Tonga orport=443 bridge "
- "82.94.251.203:80 4A0C CD2D DC79 9508 3D73 F5D6 6710 0C8A 5831 F16D",
- "gabelmoo orport=443 "
- "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 "
- "131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281",
- "dannenberg orport=443 "
- "v3ident=585769C78764D58426B8B52B6651A5A71137189A "
- "193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123",
- "urras orport=80 "
- "v3ident=80550987E1D626E3EBA5E5E75A458DE0626D088C "
- "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417",
- "maatuska orport=80 "
- "v3ident=49015F787433103580E3B66A1707A00E60F2D15B "
- "171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810",
- "Faravahar orport=443 "
- "v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 "
- "154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC",
- "longclaw orport=443 "
- "v3ident=23D15D965BC35114467363C165C4F724B64B4F66 "
- "199.254.238.52:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145",
- NULL
- };
- for (i=0; authorities[i]; i++) {
- if (parse_dir_authority_line(authorities[i], type, 0)<0) {
+ for (i=0; default_authorities[i]; i++) {
+ if (parse_dir_authority_line(default_authorities[i], type, 0)<0) {
log_err(LD_BUG, "Couldn't parse internal DirAuthority line %s",
- authorities[i]);
+ default_authorities[i]);
}
}
}
/** Add the default fallback directory servers into the fallback directory
* server list. */
-static void
-add_default_fallback_dir_servers(void)
+MOCK_IMPL(void,
+add_default_fallback_dir_servers,(void))
{
int i;
const char *fallback[] = {
@@ -969,7 +972,7 @@ validate_dir_servers(or_options_t *options, or_options_t *old_options)
/** Look at all the config options and assign new dir authorities
* as appropriate.
*/
-static int
+int
consider_adding_dir_servers(const or_options_t *options,
const or_options_t *old_options)
{
@@ -987,23 +990,36 @@ consider_adding_dir_servers(const or_options_t *options,
if (!need_to_update)
return 0; /* all done */
+ /* "You cannot set both DirAuthority and Alternate*Authority."
+ * Checking that this restriction holds allows us to simplify
+ * the unit tests. */
+ tor_assert(!(options->DirAuthorities &&
+ (options->AlternateDirAuthority
+ || options->AlternateBridgeAuthority)));
+
/* Start from a clean slate. */
clear_dir_servers();
if (!options->DirAuthorities) {
/* then we may want some of the defaults */
dirinfo_type_t type = NO_DIRINFO;
- if (!options->AlternateBridgeAuthority)
+ if (!options->AlternateBridgeAuthority) {
type |= BRIDGE_DIRINFO;
- if (!options->AlternateDirAuthority)
+ }
+ if (!options->AlternateDirAuthority) {
type |= V3_DIRINFO | EXTRAINFO_DIRINFO | MICRODESC_DIRINFO;
+ /* Only add the default fallback directories when the DirAuthorities,
+ * AlternateDirAuthority, and FallbackDir directory config options
+ * are set to their defaults. */
+ if (!options->FallbackDir) {
+ add_default_fallback_dir_servers();
+ }
+ }
/* if type == NO_DIRINFO, we don't want to add any of the
* default authorities, because we've replaced them all */
if (type != NO_DIRINFO)
add_default_trusted_dir_authorities(type);
}
- if (!options->FallbackDir)
- add_default_fallback_dir_servers();
for (cl = options->DirAuthorities; cl; cl = cl->next)
if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0)
@@ -1326,10 +1342,6 @@ options_transition_requires_fresh_tls_context(const or_options_t *old_options,
if (!old_options)
return 0;
- if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) {
- return 1;
- }
-
if (!opt_streq(old_options->TLSECGroup, new_options->TLSECGroup))
return 1;
@@ -1459,6 +1471,13 @@ options_act(const or_options_t *old_options)
rep_hist_load_mtbf_data(time(NULL));
}
+ /* If we have an ExtORPort, initialize its auth cookie. */
+ if (running_tor &&
+ init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) {
+ log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file.");
+ return -1;
+ }
+
mark_transport_list();
pt_prepare_proxy_list_for_config_read();
if (!options->DisableNetwork) {
@@ -1504,24 +1523,6 @@ options_act(const or_options_t *old_options)
finish_daemon(options->DataDirectory);
}
- /* If needed, generate a new TLS DH prime according to the current torrc. */
- if (server_mode(options) && options->DynamicDHGroups) {
- char *keydir = get_datadir_fname("keys");
- if (check_private_dir(keydir, CPD_CREATE, options->User)) {
- tor_free(keydir);
- return -1;
- }
- tor_free(keydir);
-
- if (!old_options || !old_options->DynamicDHGroups) {
- char *fname = get_datadir_fname2("keys", "dynamic_dh_params");
- crypto_set_tls_dh_prime(fname);
- tor_free(fname);
- }
- } else { /* clients don't need a dynamic DH prime. */
- crypto_set_tls_dh_prime(NULL);
- }
-
/* We want to reinit keys as needed before we do much of anything else:
keys are important, and other things can depend on them. */
if (transition_affects_workers ||
@@ -1563,12 +1564,6 @@ options_act(const or_options_t *old_options)
return -1;
}
- /* If we have an ExtORPort, initialize its auth cookie. */
- if (init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) {
- log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file.");
- return -1;
- }
-
monitor_owning_controller_process(options->OwningControllerProcess);
/* reload keys as needed for rendezvous services. */
@@ -1765,6 +1760,7 @@ options_act(const or_options_t *old_options)
if (!public_server_mode(options)) {
options->CellStatistics = 0;
options->EntryStatistics = 0;
+ options->ConnDirectionStatistics = 0;
options->HiddenServiceStatistics = 0;
options->ExitPortStatistics = 0;
}
@@ -1897,28 +1893,33 @@ options_act(const or_options_t *old_options)
return 0;
}
+typedef enum {
+ TAKES_NO_ARGUMENT = 0,
+ ARGUMENT_NECESSARY = 1,
+ ARGUMENT_OPTIONAL = 2
+} takes_argument_t;
+
static const struct {
const char *name;
- int takes_argument;
+ takes_argument_t takes_argument;
} CMDLINE_ONLY_OPTIONS[] = {
- { "-f", 1 },
- { "--allow-missing-torrc", 0 },
- { "--defaults-torrc", 1 },
- { "--hash-password", 1 },
- { "--dump-config", 1 },
- { "--list-fingerprint", 0 },
- { "--verify-config", 0 },
- { "--ignore-missing-torrc", 0 },
- { "--quiet", 0 },
- { "--hush", 0 },
- { "--version", 0 },
- { "--library-versions", 0 },
- { "-h", 0 },
- { "--help", 0 },
- { "--list-torrc-options", 0 },
- { "--digests", 0 },
- { "--nt-service", 0 },
- { "-nt-service", 0 },
+ { "-f", ARGUMENT_NECESSARY },
+ { "--allow-missing-torrc", TAKES_NO_ARGUMENT },
+ { "--defaults-torrc", ARGUMENT_NECESSARY },
+ { "--hash-password", ARGUMENT_NECESSARY },
+ { "--dump-config", ARGUMENT_OPTIONAL },
+ { "--list-fingerprint", TAKES_NO_ARGUMENT },
+ { "--verify-config", TAKES_NO_ARGUMENT },
+ { "--ignore-missing-torrc", TAKES_NO_ARGUMENT },
+ { "--quiet", TAKES_NO_ARGUMENT },
+ { "--hush", TAKES_NO_ARGUMENT },
+ { "--version", TAKES_NO_ARGUMENT },
+ { "--library-versions", TAKES_NO_ARGUMENT },
+ { "-h", TAKES_NO_ARGUMENT },
+ { "--help", TAKES_NO_ARGUMENT },
+ { "--list-torrc-options", TAKES_NO_ARGUMENT },
+ { "--nt-service", TAKES_NO_ARGUMENT },
+ { "-nt-service", TAKES_NO_ARGUMENT },
{ NULL, 0 },
};
@@ -1945,7 +1946,7 @@ config_parse_commandline(int argc, char **argv, int ignore_errors,
while (i < argc) {
unsigned command = CONFIG_LINE_NORMAL;
- int want_arg = 1;
+ takes_argument_t want_arg = ARGUMENT_NECESSARY;
int is_cmdline = 0;
int j;
@@ -1975,7 +1976,9 @@ config_parse_commandline(int argc, char **argv, int ignore_errors,
want_arg = 0;
}
- if (want_arg && i == argc-1) {
+ const int is_last = (i == argc-1);
+
+ if (want_arg == ARGUMENT_NECESSARY && is_last) {
if (ignore_errors) {
arg = strdup("");
} else {
@@ -1985,8 +1988,11 @@ config_parse_commandline(int argc, char **argv, int ignore_errors,
config_free_lines(front_cmdline);
return -1;
}
+ } else if (want_arg == ARGUMENT_OPTIONAL && is_last) {
+ arg = tor_strdup("");
} else {
- arg = want_arg ? tor_strdup(argv[i+1]) : strdup("");
+ arg = (want_arg != TAKES_NO_ARGUMENT) ? tor_strdup(argv[i+1]) :
+ tor_strdup("");
}
param = tor_malloc_zero(sizeof(config_line_t));
@@ -2573,6 +2579,61 @@ options_validate_cb(void *old_options, void *options, void *default_options,
from_setconf, msg);
}
+#define REJECT(arg) \
+ STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
+#define COMPLAIN(args...) \
+ STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END
+
+/** Log a warning message iff <b>filepath</b> is not absolute.
+ * Warning message must contain option name <b>option</b> and
+ * an absolute path that <b>filepath<b> will resolve to.
+ *
+ * In case <b>filepath</b> is absolute, do nothing.
+ */
+static void
+warn_if_option_path_is_relative(const char *option,
+ char *filepath)
+{
+ if (filepath && path_is_relative(filepath)) {
+ char *abs_path = make_path_absolute(filepath);
+ COMPLAIN("Path for %s (%s) is relative and will resolve to %s."
+ " Is this what you wanted?", option, filepath, abs_path);
+ tor_free(abs_path);
+ }
+}
+
+/** Scan <b>options</b> for occurances of relative file/directory
+ * path and log a warning whenever it is found.
+ */
+static void
+warn_about_relative_paths(or_options_t *options)
+{
+ tor_assert(options);
+
+ warn_if_option_path_is_relative("CookieAuthFile",
+ options->CookieAuthFile);
+ warn_if_option_path_is_relative("ExtORPortCookieAuthFile",
+ options->ExtORPortCookieAuthFile);
+ warn_if_option_path_is_relative("DirPortFrontPage",
+ options->DirPortFrontPage);
+ warn_if_option_path_is_relative("V3BandwidthsFile",
+ options->V3BandwidthsFile);
+ warn_if_option_path_is_relative("ControlPortWriteToFile",
+ options->ControlPortWriteToFile);
+ warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile);
+ warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File);
+ warn_if_option_path_is_relative("Log",options->DebugLogFile);
+ warn_if_option_path_is_relative("AccelDir",options->AccelDir);
+ warn_if_option_path_is_relative("DataDirectory",options->DataDirectory);
+ warn_if_option_path_is_relative("PidFile",options->PidFile);
+
+ for (config_line_t *hs_line = options->RendConfigLines; hs_line;
+ hs_line = hs_line->next) {
+ if (!strcasecmp(hs_line->key, "HiddenServiceDir"))
+ warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value);
+ }
+}
+
/** Return 0 if every setting in <b>options</b> is reasonable, is a
* permissible transition from <b>old_options</b>, and none of the
* testing-only settings differ from <b>default_options</b> unless in
@@ -2594,13 +2655,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
config_line_t *cl;
const char *uname = get_uname();
int n_ports=0;
-#define REJECT(arg) \
- STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
-#define COMPLAIN(arg) STMT_BEGIN log_warn(LD_CONFIG, arg); STMT_END
tor_assert(msg);
*msg = NULL;
+ warn_about_relative_paths(options);
+
if (server_mode(options) &&
(!strcmpstart(uname, "Windows 95") ||
!strcmpstart(uname, "Windows 98") ||
@@ -2754,6 +2814,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
COMPLAIN("Unrecognized TLSECGroup: Falling back to the default.");
tor_free(options->TLSECGroup);
}
+ if (!evaluate_ecgroup_for_tls(options->TLSECGroup)) {
+ REJECT("Unsupported TLSECGroup.");
+ }
if (options->ExcludeNodes && options->StrictNodes) {
COMPLAIN("You have asked to exclude certain relays from all positions "
@@ -3774,9 +3837,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
"combination.");
return 0;
+}
+
#undef REJECT
#undef COMPLAIN
-}
/* Given the value that the user has set for MaxMemInQueues, compute the
* actual maximum value. We clip this value if it's too low, and autodetect
@@ -4352,13 +4416,6 @@ options_init_from_torrc(int argc, char **argv)
exit(0);
}
- if (config_line_find(cmdline_only_options, "--digests")) {
- printf("Tor version %s.\n",get_version());
- printf("%s", libor_get_digests());
- printf("%s", tor_get_digests());
- exit(0);
- }
-
if (config_line_find(cmdline_only_options, "--library-versions")) {
printf("Tor version %s. \n", get_version());
printf("Library versions\tCompiled\t\tRuntime\n");
@@ -5477,7 +5534,7 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
* <b>validate_only</b> is 0, and the line is well-formed, then add the
* dirserver described in the line as a fallback directory. Return 0 on
* success, or -1 if the line isn't well-formed or if we can't add it. */
-static int
+int
parse_dir_fallback_line(const char *line,
int validate_only)
{
@@ -5953,7 +6010,8 @@ parse_port_config(smartlist_t *out,
port = 1;
} else if (!strcmp(addrport, "auto")) {
port = CFG_AUTO_PORT;
- tor_addr_parse(&addr, defaultaddr);
+ int af = tor_addr_parse(&addr, defaultaddr);
+ tor_assert(af >= 0);
} else if (!strcasecmpend(addrport, ":auto")) {
char *addrtmp = tor_strndup(addrport, strlen(addrport)-5);
port = CFG_AUTO_PORT;
@@ -5968,7 +6026,8 @@ parse_port_config(smartlist_t *out,
"9050" might be a valid address. */
port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL);
if (ok) {
- tor_addr_parse(&addr, defaultaddr);
+ int af = tor_addr_parse(&addr, defaultaddr);
+ tor_assert(af >= 0);
} else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) {
if (ptmp == 0) {
log_warn(LD_CONFIG, "%sPort line has address but no port", portname);
@@ -6714,7 +6773,6 @@ get_num_cpus(const or_options_t *options)
static void
init_libevent(const or_options_t *options)
{
- const char *badness=NULL;
tor_libevent_cfg cfg;
tor_assert(options);
@@ -6735,17 +6793,6 @@ init_libevent(const or_options_t *options)
tor_libevent_initialize(&cfg);
suppress_libevent_log_msg(NULL);
-
- tor_check_libevent_version(tor_libevent_get_method(),
- server_mode(get_options()),
- &badness);
- if (badness) {
- const char *v = tor_libevent_get_version_str();
- const char *m = tor_libevent_get_method();
- control_event_general_status(LOG_WARN,
- "BAD_LIBEVENT VERSION=%s METHOD=%s BADNESS=%s RECOVERED=NO",
- v, m, badness);
- }
}
/** Return a newly allocated string holding a filename relative to the data
@@ -6946,15 +6993,42 @@ getinfo_helper_config(control_connection_t *conn,
smartlist_free(sl);
} else if (!strcmp(question, "config/defaults")) {
smartlist_t *sl = smartlist_new();
- int i;
+ int i, dirauth_lines_seen = 0;
for (i = 0; option_vars_[i].name; ++i) {
const config_var_t *var = &option_vars_[i];
if (var->initvalue != NULL) {
- char *val = esc_for_log(var->initvalue);
- smartlist_add_asprintf(sl, "%s %s\n",var->name,val);
- tor_free(val);
+ if (strcmp(option_vars_[i].name, "DirAuthority") == 0) {
+ /*
+ * Count dirauth lines we have a default for; we'll use the
+ * count later to decide whether to add the defaults manually
+ */
+ ++dirauth_lines_seen;
+ }
+ char *val = esc_for_log(var->initvalue);
+ smartlist_add_asprintf(sl, "%s %s\n",var->name,val);
+ tor_free(val);
}
}
+
+ if (dirauth_lines_seen == 0) {
+ /*
+ * We didn't see any directory authorities with default values,
+ * so add the list of default authorities manually.
+ */
+ const char **i;
+
+ /*
+ * default_authorities is defined earlier in this file and
+ * is a const char ** NULL-terminated array of dirauth config
+ * lines.
+ */
+ for (i = default_authorities; *i != NULL; ++i) {
+ char *val = esc_for_log(*i);
+ smartlist_add_asprintf(sl, "DirAuthority %s\n", val);
+ tor_free(val);
+ }
+ }
+
*answer = smartlist_join_strings(sl, "", 0, NULL);
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
diff --git a/src/or/config.h b/src/or/config.h
index e97248540c..0ee1e1a3c4 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -95,7 +95,6 @@ int getinfo_helper_config(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
-const char *tor_get_digests(void);
uint32_t get_effective_bwrate(const or_options_t *options);
uint32_t get_effective_bwburst(const or_options_t *options);
@@ -149,6 +148,12 @@ STATIC int options_validate(or_options_t *old_options,
STATIC int parse_transport_line(const or_options_t *options,
const char *line, int validate_only,
int server);
+STATIC int consider_adding_dir_servers(const or_options_t *options,
+ const or_options_t *old_options);
+MOCK_DECL(STATIC void, add_default_fallback_dir_servers, (void));
+STATIC int
+parse_dir_fallback_line(const char *line,
+ int validate_only);
#endif
#endif
diff --git a/src/or/config_codedigest.c b/src/or/config_codedigest.c
deleted file mode 100644
index 86d14bacef..0000000000
--- a/src/or/config_codedigest.c
+++ /dev/null
@@ -1,13 +0,0 @@
-
-const char *tor_get_digests(void);
-
-/** Return a string describing the digest of the source files in src/or/
- */
-const char *
-tor_get_digests(void)
-{
- return ""
-#include "or_sha1.i"
- ;
-}
-
diff --git a/src/or/connection.c b/src/or/connection.c
index 7db0238b3d..24d47ccd18 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -586,6 +586,13 @@ connection_free_(connection_t *conn)
control_connection_t *control_conn = TO_CONTROL_CONN(conn);
tor_free(control_conn->safecookie_client_hash);
tor_free(control_conn->incoming_cmd);
+ if (control_conn->ephemeral_onion_services) {
+ SMARTLIST_FOREACH(control_conn->ephemeral_onion_services, char *, cp, {
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+ });
+ smartlist_free(control_conn->ephemeral_onion_services);
+ }
}
/* Probably already freed by connection_free. */
@@ -1407,7 +1414,7 @@ static int
connection_handle_listener_read(connection_t *conn, int new_type)
{
tor_socket_t news; /* the new socket */
- connection_t *newconn;
+ connection_t *newconn = 0;
/* information about the remote peer when connecting to other routers */
struct sockaddr_storage addrbuf;
struct sockaddr *remote = (struct sockaddr*)&addrbuf;
@@ -3774,7 +3781,7 @@ connection_fetch_from_buf_line(connection_t *conn, char *data,
}
}
-/** As fetch_from_buf_http, but fetches from a conncetion's input buffer_t or
+/** As fetch_from_buf_http, but fetches from a connection's input buffer_t or
* its bufferevent as appropriate. */
int
connection_fetch_from_buf_http(connection_t *conn,
@@ -4440,25 +4447,12 @@ alloc_http_authenticator(const char *authenticator)
/* an authenticator in Basic authentication
* is just the string "username:password" */
const size_t authenticator_length = strlen(authenticator);
- /* The base64_encode function needs a minimum buffer length
- * of 66 bytes. */
- const size_t base64_authenticator_length = (authenticator_length/48+1)*66;
+ const size_t base64_authenticator_length =
+ base64_encode_size(authenticator_length, 0) + 1;
char *base64_authenticator = tor_malloc(base64_authenticator_length);
if (base64_encode(base64_authenticator, base64_authenticator_length,
- authenticator, authenticator_length) < 0) {
+ authenticator, authenticator_length, 0) < 0) {
tor_free(base64_authenticator); /* free and set to null */
- } else {
- int i = 0, j = 0;
- ssize_t len = strlen(base64_authenticator);
-
- /* remove all newline occurrences within the string */
- for (i=0; i < len; ++i) {
- if ('\n' != base64_authenticator[i]) {
- base64_authenticator[j] = base64_authenticator[i];
- ++j;
- }
- }
- base64_authenticator[j]='\0';
}
return base64_authenticator;
}
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 3c817decfb..c63c350fd8 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -102,8 +102,7 @@ connection_mark_unattached_ap_,(entry_connection_t *conn, int endreason,
* but we should fix it someday anyway. */
if ((edge_conn->on_circuit != NULL || edge_conn->edge_has_sent_end) &&
connection_edge_is_rendezvous_stream(edge_conn)) {
- rend_client_note_connection_attempt_ended(
- edge_conn->rend_data->onion_address);
+ rend_client_note_connection_attempt_ended(edge_conn->rend_data);
}
if (base_conn->marked_for_close) {
@@ -1499,61 +1498,76 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return -1;
}
+ /* Look up if we have client authorization configured for this hidden
+ * service. If we do, associate it with the rend_data. */
+ rend_service_authorization_t *client_auth =
+ rend_client_lookup_service_authorization(socks->address);
+
+ const char *cookie = NULL;
+ rend_auth_type_t auth_type = REND_NO_AUTH;
+ if (client_auth) {
+ log_info(LD_REND, "Using previously configured client authorization "
+ "for hidden service request.");
+ auth_type = client_auth->auth_type;
+ cookie = client_auth->descriptor_cookie;
+ }
+
/* Fill in the rend_data field so we can start doing a connection to
* a hidden service. */
rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data =
- tor_malloc_zero(sizeof(rend_data_t));
- strlcpy(rend_data->onion_address, socks->address,
- sizeof(rend_data->onion_address));
+ rend_data_client_create(socks->address, NULL, cookie, auth_type);
+ if (rend_data == NULL) {
+ return -1;
+ }
log_info(LD_REND,"Got a hidden service request for ID '%s'",
safe_str_client(rend_data->onion_address));
- /* see if we already have a hidden service descriptor cached for this
- * address. */
+ /* Lookup the given onion address. If invalid, stop right now else we
+ * might have it in the cache or not, it will be tested later on. */
+ unsigned int refetch_desc = 0;
rend_cache_entry_t *entry = NULL;
const int rend_cache_lookup_result =
rend_cache_lookup_entry(rend_data->onion_address, -1, &entry);
if (rend_cache_lookup_result < 0) {
- /* We should already have rejected this address! */
- log_warn(LD_BUG,"Invalid service name '%s'",
- safe_str_client(rend_data->onion_address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
- return -1;
+ switch (-rend_cache_lookup_result) {
+ case EINVAL:
+ /* We should already have rejected this address! */
+ log_warn(LD_BUG,"Invalid service name '%s'",
+ safe_str_client(rend_data->onion_address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ return -1;
+ case ENOENT:
+ refetch_desc = 1;
+ break;
+ default:
+ log_warn(LD_BUG, "Unknown cache lookup error %d",
+ rend_cache_lookup_result);
+ return -1;
+ }
}
/* Help predict this next time. We're not sure if it will need
* a stable circuit yet, but we know we'll need *something*. */
rep_hist_note_used_internal(now, 0, 1);
- /* Look up if we have client authorization configured for this hidden
- * service. If we do, associate it with the rend_data. */
- rend_service_authorization_t *client_auth =
- rend_client_lookup_service_authorization(
- rend_data->onion_address);
- if (client_auth) {
- log_info(LD_REND, "Using previously configured client authorization "
- "for hidden service request.");
- memcpy(rend_data->descriptor_cookie,
- client_auth->descriptor_cookie, REND_DESC_COOKIE_LEN);
- rend_data->auth_type = client_auth->auth_type;
- }
-
- /* Now, we either launch an attempt to connect to the hidden service,
- * or we launch an attempt to look up its descriptor, depending on
- * whether we had the descriptor. */
- if (rend_cache_lookup_result == 0) {
+ /* Now we have a descriptor but is it usable or not? If not, refetch.
+ * Also, a fetch could have been requested if the onion address was not
+ * found in the cache previously. */
+ if (refetch_desc || !rend_client_any_intro_points_usable(entry)) {
base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
- safe_str_client(rend_data->onion_address));
+ safe_str_client(rend_data->onion_address));
rend_client_refetch_v2_renddesc(rend_data);
- } else { /* rend_cache_lookup_result > 0 */
- base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
- log_info(LD_REND, "Descriptor is here. Great.");
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- return -1;
- }
+ return 0;
+ }
+
+ /* We have the descriptor so launch a connection to the HS. */
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ log_info(LD_REND, "Descriptor is here. Great.");
+ if (connection_ap_handshake_attach_circuit(conn) < 0) {
+ if (!base_conn->marked_for_close)
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
+ return -1;
}
return 0;
}
@@ -1598,7 +1612,6 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
struct sockaddr_storage orig_dst;
socklen_t orig_dst_len = sizeof(orig_dst);
tor_addr_t addr;
- int rv;
#ifdef TRANS_TRPOXY
if (options->TransProxyType_parsed == TPT_TPROXY) {
@@ -1613,6 +1626,7 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
#endif
#ifdef TRANS_NETFILTER
+ int rv = -1;
switch (ENTRY_TO_CONN(conn)->socket_family) {
#ifdef TRANS_NETFILTER_IPV4
case AF_INET:
@@ -1763,7 +1777,8 @@ connection_ap_get_original_destination(entry_connection_t *conn,
if (options->TransProxyType_parsed == TPT_PF_DIVERT)
return destination_from_socket(conn, req);
- if (options->TransProxyType_parsed == TPT_DEFAULT)
+ if (options->TransProxyType_parsed == TPT_DEFAULT ||
+ options->TransProxyType_parsed == TPT_IPFW)
return destination_from_pf(conn, req);
(void)conn;
@@ -2845,6 +2860,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
origin_circ->p_streams = n_stream;
assert_circuit_ok(circ);
+ origin_circ->rend_data->nr_streams++;
+
connection_exit_connect(n_stream);
/* For path bias: This circuit was used successfully */
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index ba30987eab..48128d6335 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -1820,6 +1820,7 @@ connection_tls_finish_handshake(or_connection_t *conn)
conn->base_.port, digest_rcvd, 0);
}
tor_tls_block_renegotiation(conn->tls);
+ rep_hist_note_negotiated_link_proto(1, started_here);
return connection_or_set_state_open(conn);
} else {
connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V2);
diff --git a/src/or/control.c b/src/or/control.c
index a2b986768a..746dfff921 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -37,6 +37,9 @@
#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
+#include "rendclient.h"
+#include "rendcommon.h"
+#include "rendservice.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
@@ -72,7 +75,7 @@ static int disable_log_messages = 0;
/** Macro: true if any control connection is interested in events of type
* <b>e</b>. */
#define EVENT_IS_INTERESTING(e) \
- (!! (global_event_mask & (((uint64_t)1)<<(e))))
+ (!! (global_event_mask & EVENT_MASK_(e)))
/** If we're using cookie-type authentication, how long should our cookies be?
*/
@@ -92,6 +95,11 @@ static uint8_t *authentication_cookie = NULL;
"Tor safe cookie authentication controller-to-server hash"
#define SAFECOOKIE_SERVER_NONCE_LEN DIGEST256_LEN
+/** The list of onion services that have been added via ADD_ONION that do not
+ * belong to any particular control connection.
+ */
+static smartlist_t *detached_onion_services = NULL;
+
/** A sufficiently large size to record the last bootstrap phase string. */
#define BOOTSTRAP_MSG_LEN 1024
@@ -157,11 +165,22 @@ static int handle_control_resolve(control_connection_t *conn, uint32_t len,
static int handle_control_usefeature(control_connection_t *conn,
uint32_t len,
const char *body);
+static int handle_control_hsfetch(control_connection_t *conn, uint32_t len,
+ const char *body);
+static int handle_control_hspost(control_connection_t *conn, uint32_t len,
+ const char *body);
+static int handle_control_add_onion(control_connection_t *conn, uint32_t len,
+ const char *body);
+static int handle_control_del_onion(control_connection_t *conn, uint32_t len,
+ const char *body);
static int write_stream_target_to_buf(entry_connection_t *conn, char *buf,
size_t len);
static void orconn_target_get_name(char *buf, size_t len,
or_connection_t *conn);
+static int get_cached_network_liveness(void);
+static void set_cached_network_liveness(int liveness);
+
/** Given a control event code for a message event, return the corresponding
* log severity. */
static INLINE int
@@ -941,6 +960,8 @@ static const struct control_event_t control_event_table[] = {
{ EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" },
{ EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" },
{ EVENT_HS_DESC, "HS_DESC" },
+ { EVENT_HS_DESC_CONTENT, "HS_DESC_CONTENT" },
+ { EVENT_NETWORK_LIVENESS, "NETWORK_LIVENESS" },
{ 0, NULL },
};
@@ -1713,6 +1734,22 @@ getinfo_helper_dir(control_connection_t *control_conn,
*answer = smartlist_join_strings(sl, "", 0, NULL);
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;
+
+ question += strlen("hs/client/desc/id/");
+ if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
+ *errmsg = "Invalid address";
+ return -1;
+ }
+
+ 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 if (!strcmpstart(question, "md/id/")) {
const node_t *node = node_get_by_hex_id(question+strlen("md/id/"));
const microdesc_t *md = NULL;
@@ -2100,6 +2137,46 @@ getinfo_helper_events(control_connection_t *control_conn,
return -1;
}
*answer = bridge_stats;
+ } else if (!strcmp(question, "status/fresh-relay-descs")) {
+ if (!server_mode(get_options())) {
+ *errmsg = "Only relays have descriptors";
+ return -1;
+ }
+ routerinfo_t *r;
+ extrainfo_t *e;
+ if (router_build_fresh_descriptor(&r, &e) < 0) {
+ *errmsg = "Error generating descriptor";
+ return -1;
+ }
+ size_t size = r->cache_info.signed_descriptor_len + 1;
+ if (e) {
+ size += e->cache_info.signed_descriptor_len + 1;
+ }
+ tor_assert(r->cache_info.signed_descriptor_len);
+ char *descs = tor_malloc(size);
+ char *cp = descs;
+ memcpy(cp, signed_descriptor_get_body(&r->cache_info),
+ r->cache_info.signed_descriptor_len);
+ cp += r->cache_info.signed_descriptor_len - 1;
+ if (e) {
+ if (cp[0] == '\0') {
+ cp[0] = '\n';
+ } else if (cp[0] != '\n') {
+ cp[1] = '\n';
+ cp++;
+ }
+ memcpy(cp, signed_descriptor_get_body(&e->cache_info),
+ e->cache_info.signed_descriptor_len);
+ cp += e->cache_info.signed_descriptor_len - 1;
+ }
+ if (cp[0] == '\n') {
+ cp[0] = '\0';
+ } else if (cp[0] != '\0') {
+ cp[1] = '\0';
+ }
+ *answer = descs;
+ routerinfo_free(r);
+ extrainfo_free(e);
} else {
return 0;
}
@@ -2107,6 +2184,51 @@ getinfo_helper_events(control_connection_t *control_conn,
return 0;
}
+/** Implementation helper for GETINFO: knows how to enumerate hidden services
+ * created via the control port. */
+static int
+getinfo_helper_onions(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg)
+{
+ smartlist_t *onion_list = NULL;
+
+ if (!strcmp(question, "onions/current")) {
+ onion_list = control_conn->ephemeral_onion_services;
+ } else if (!strcmp(question, "onions/detached")) {
+ onion_list = detached_onion_services;
+ } else {
+ return 0;
+ }
+ if (!onion_list || smartlist_len(onion_list) == 0) {
+ *errmsg = "No onion services of the specified type.";
+ return -1;
+ }
+ *answer = smartlist_join_strings(onion_list, "\r\n", 0, NULL);
+
+ return 0;
+}
+
+/** Implementation helper for GETINFO: answers queries about network
+ * liveness. */
+static int
+getinfo_helper_liveness(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg)
+{
+ (void)control_conn;
+ (void)errmsg;
+ if (strcmp(question, "network-liveness") == 0) {
+ if (get_cached_network_liveness()) {
+ *answer = tor_strdup("up");
+ } else {
+ *answer = tor_strdup("down");
+ }
+ }
+
+ return 0;
+}
+
/** Callback function for GETINFO: on a given control connection, try to
* answer the question <b>q</b> and store the newly-allocated answer in
* *<b>a</b>. If an internal error occurs, return -1 and optionally set
@@ -2176,6 +2298,8 @@ static const getinfo_item_t getinfo_items[] = {
PREFIX("md/id/", dir, "Microdescriptors by ID"),
PREFIX("md/name/", dir, "Microdescriptors by name"),
PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."),
+ PREFIX("hs/client/desc/id", dir,
+ "Hidden Service descriptor in client's cache by onion."),
PREFIX("net/listeners/", listeners, "Bound addresses by type"),
ITEM("ns/all", networkstatus,
"Brief summary of router status (v2 directory format)"),
@@ -2189,6 +2313,8 @@ static const getinfo_item_t getinfo_items[] = {
"Information about and from the ns consensus."),
ITEM("network-status", dir,
"Brief summary of router status (v1 directory format)"),
+ ITEM("network-liveness", liveness,
+ "Current opinion on whether the network is live"),
ITEM("circuit-status", events, "List of current circuits originating here."),
ITEM("stream-status", events,"List of current streams."),
ITEM("orconn-status", events, "A list of current OR connections."),
@@ -2210,6 +2336,8 @@ static const getinfo_item_t getinfo_items[] = {
"The last bootstrap phase status event that Tor sent."),
DOC("status/clients-seen",
"Breakdown of client countries seen by a bridge."),
+ DOC("status/fresh-relay-descs",
+ "A fresh relay/ei descriptor pair for Tor's current state. Not stored."),
DOC("status/version/recommended", "List of currently recommended versions."),
DOC("status/version/current", "Status of the current version."),
DOC("status/version/num-versioning", "Number of versioning authorities."),
@@ -2239,6 +2367,10 @@ static const getinfo_item_t getinfo_items[] = {
ITEM("exit-policy/ipv4", policies, "IPv4 parts of exit policy"),
ITEM("exit-policy/ipv6", policies, "IPv6 parts of exit policy"),
PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"),
+ ITEM("onions/current", onions,
+ "Onion services owned by the current control connection."),
+ ITEM("onions/detached", onions,
+ "Onion services detached from the control connection."),
{ NULL, NULL, NULL, 0 }
};
@@ -2733,12 +2865,14 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
uint8_t purpose = ROUTER_PURPOSE_GENERAL;
int cache = 0; /* eventually, we may switch this to 1 */
- char *cp = memchr(body, '\n', len);
+ const char *cp = memchr(body, '\n', len);
smartlist_t *args = smartlist_new();
tor_assert(cp);
- *cp++ = '\0';
+ ++cp;
- smartlist_split_string(args, body, " ",
+ char *cmdline = tor_memdup_nulterm(body, cp-body);
+
+ smartlist_split_string(args, cmdline, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(args, char *, option) {
if (!strcasecmpstart(option, "purpose=")) {
@@ -2787,6 +2921,7 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
done:
SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
smartlist_free(args);
+ tor_free(cmdline);
return 0;
}
@@ -3102,8 +3237,8 @@ handle_control_authchallenge(control_connection_t *conn, uint32_t len,
tor_free(client_nonce);
return -1;
}
-
- tor_assert(!crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN));
+ const int fail = crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
+ tor_assert(!fail);
/* Now compute and send the server-to-controller response, and the
* server's nonce. */
@@ -3211,6 +3346,570 @@ handle_control_dropguards(control_connection_t *conn,
return 0;
}
+/** Implementation for the HSFETCH command. */
+static int
+handle_control_hsfetch(control_connection_t *conn, uint32_t len,
+ const char *body)
+{
+ int i;
+ char digest[DIGEST_LEN], *hsaddress = NULL, *arg1 = NULL, *desc_id = NULL;
+ smartlist_t *args = NULL, *hsdirs = NULL;
+ (void) len; /* body is nul-terminated; it's safe to ignore the length */
+ static const char *hsfetch_command = "HSFETCH";
+ static const char *v2_str = "v2-";
+ const size_t v2_str_len = strlen(v2_str);
+ rend_data_t *rend_query = NULL;
+
+ /* Make sure we have at least one argument, the HSAddress. */
+ args = getargs_helper(hsfetch_command, conn, body, 1, -1);
+ if (!args) {
+ goto exit;
+ }
+
+ /* Extract the first argument (either HSAddress or DescID). */
+ arg1 = smartlist_get(args, 0);
+ /* Test if it's an HS address without the .onion part. */
+ if (rend_valid_service_id(arg1)) {
+ hsaddress = arg1;
+ } else if (strcmpstart(arg1, v2_str) == 0 &&
+ rend_valid_descriptor_id(arg1 + v2_str_len) &&
+ base32_decode(digest, sizeof(digest), arg1 + v2_str_len,
+ REND_DESC_ID_V2_LEN_BASE32) == 0) {
+ /* We have a well formed version 2 descriptor ID. Keep the decoded value
+ * of the id. */
+ desc_id = digest;
+ } else {
+ connection_printf_to_buf(conn, "513 Unrecognized \"%s\"\r\n",
+ arg1);
+ goto done;
+ }
+
+ static const char *opt_server = "SERVER=";
+
+ /* Skip first argument because it's the HSAddress or DescID. */
+ for (i = 1; i < smartlist_len(args); ++i) {
+ const char *arg = smartlist_get(args, i);
+ const node_t *node;
+
+ if (!strcasecmpstart(arg, opt_server)) {
+ const char *server;
+
+ server = arg + strlen(opt_server);
+ node = node_get_by_hex_id(server);
+ if (!node) {
+ connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
+ server);
+ goto done;
+ }
+ if (!hsdirs) {
+ /* Stores routerstatus_t object for each specified server. */
+ hsdirs = smartlist_new();
+ }
+ /* Valid server, add it to our local list. */
+ smartlist_add(hsdirs, node->rs);
+ } else {
+ connection_printf_to_buf(conn, "513 Unexpected argument \"%s\"\r\n",
+ arg);
+ goto done;
+ }
+ }
+
+ rend_query = rend_data_client_create(hsaddress, desc_id, NULL,
+ REND_NO_AUTH);
+ if (rend_query == NULL) {
+ connection_printf_to_buf(conn, "551 Error creating the HS query\r\n");
+ goto done;
+ }
+
+ /* Using a descriptor ID, we force the user to provide at least one
+ * hsdir server using the SERVER= option. */
+ if (desc_id && (!hsdirs || !smartlist_len(hsdirs))) {
+ connection_printf_to_buf(conn, "512 %s option is required\r\n",
+ opt_server);
+ goto done;
+ }
+
+ /* We are about to trigger HSDir fetch so send the OK now because after
+ * that 650 event(s) are possible so better to have the 250 OK before them
+ * to avoid out of order replies. */
+ send_control_done(conn);
+
+ /* Trigger the fetch using the built rend query and possibly a list of HS
+ * directory to use. This function ignores the client cache thus this will
+ * always send a fetch command. */
+ rend_client_fetch_v2_desc(rend_query, hsdirs);
+
+ done:
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
+ /* Contains data pointer that we don't own thus no cleanup. */
+ smartlist_free(hsdirs);
+ rend_data_free(rend_query);
+ exit:
+ return 0;
+}
+
+/** Implementation for the HSPOST command. */
+static int
+handle_control_hspost(control_connection_t *conn,
+ uint32_t len,
+ const char *body)
+{
+ static const char *opt_server = "SERVER=";
+ smartlist_t *args = smartlist_new();
+ smartlist_t *hs_dirs = NULL;
+ const char *encoded_desc = body;
+ size_t encoded_desc_len = len;
+
+ char *cp = memchr(body, '\n', len);
+ char *argline = tor_strndup(body, cp-body);
+
+ /* If any SERVER= options were specified, try parse the options line */
+ if (!strcasecmpstart(argline, opt_server)) {
+ /* encoded_desc begins after a newline character */
+ cp = cp + 1;
+ encoded_desc = cp;
+ encoded_desc_len = len-(cp-body);
+
+ smartlist_split_string(args, argline, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
+ if (!strcasecmpstart(arg, opt_server)) {
+ const char *server = arg + strlen(opt_server);
+ const node_t *node = node_get_by_hex_id(server);
+
+ if (!node || !node->rs) {
+ connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
+ 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 {
+ connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n",
+ arg);
+ goto done;
+ }
+ } SMARTLIST_FOREACH_END(arg);
+ }
+
+ /* 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));
+ read_escaped_data(encoded_desc, encoded_desc_len, &desc->desc_str);
+
+ rend_service_descriptor_t *parsed = NULL;
+ char *intro_content = NULL;
+ size_t intro_size;
+ size_t encoded_size;
+ const char *next_desc;
+ if (!rend_parse_v2_service_descriptor(&parsed, desc->desc_id, &intro_content,
+ &intro_size, &encoded_size,
+ &next_desc, desc->desc_str, 1)) {
+ /* Post the descriptor. */
+ char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
+ if (!rend_get_service_id(parsed->pk, serviceid)) {
+ smartlist_t *descs = smartlist_new();
+ smartlist_add(descs, desc);
+
+ /* We are about to trigger HS descriptor upload so send the OK now
+ * because after that 650 event(s) are possible so better to have the
+ * 250 OK before them to avoid out of order replies. */
+ send_control_done(conn);
+
+ /* Trigger the descriptor upload */
+ directory_post_to_hs_dir(parsed, descs, hs_dirs, serviceid, 0);
+ smartlist_free(descs);
+ }
+
+ rend_service_descriptor_free(parsed);
+ } else {
+ connection_printf_to_buf(conn, "554 Invalid descriptor\r\n");
+ }
+
+ tor_free(intro_content);
+ rend_encoded_v2_service_descriptor_free(desc);
+ done:
+ tor_free(argline);
+ smartlist_free(hs_dirs); /* Contents belong to the rend service code. */
+ SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
+ smartlist_free(args);
+ return 0;
+}
+
+/** Called when we get a ADD_ONION command; parse the body, and set up
+ * the new ephemeral Onion Service. */
+static int
+handle_control_add_onion(control_connection_t *conn,
+ uint32_t len,
+ const char *body)
+{
+ smartlist_t *args;
+ size_t arg_len;
+ (void) len; /* body is nul-terminated; it's safe to ignore the length */
+ args = getargs_helper("ADD_ONION", conn, body, 2, -1);
+ if (!args)
+ return 0;
+ arg_len = smartlist_len(args);
+
+ /* Parse all of the arguments that do not involve handling cryptographic
+ * material first, since there's no reason to touch that at all if any of
+ * the other arguments are malformed.
+ */
+ smartlist_t *port_cfgs = smartlist_new();
+ int discard_pk = 0;
+ int detach = 0;
+ int max_streams = 0;
+ int max_streams_close_circuit = 0;
+ for (size_t i = 1; i < arg_len; i++) {
+ static const char *port_prefix = "Port=";
+ static const char *flags_prefix = "Flags=";
+ static const char *max_s_prefix = "MaxStreams=";
+
+ const char *arg = smartlist_get(args, i);
+ if (!strcasecmpstart(arg, port_prefix)) {
+ /* "Port=VIRTPORT[,TARGET]". */
+ const char *port_str = arg + strlen(port_prefix);
+
+ rend_service_port_config_t *cfg =
+ rend_service_parse_port_config(port_str, ",", NULL);
+ if (!cfg) {
+ connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n");
+ goto out;
+ }
+ smartlist_add(port_cfgs, cfg);
+ } else if (!strcasecmpstart(arg, max_s_prefix)) {
+ /* "MaxStreams=[0..65535]". */
+ const char *max_s_str = arg + strlen(max_s_prefix);
+ int ok = 0;
+ max_streams = (int)tor_parse_long(max_s_str, 10, 0, 65535, &ok, NULL);
+ if (!ok) {
+ connection_printf_to_buf(conn, "512 Invalid MaxStreams\r\n");
+ goto out;
+ }
+ } else if (!strcasecmpstart(arg, flags_prefix)) {
+ /* "Flags=Flag[,Flag]", where Flag can be:
+ * * 'DiscardPK' - If tor generates the keypair, do not include it in
+ * the response.
+ * * 'Detach' - Do not tie this onion service to any particular control
+ * connection.
+ * * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is
+ * exceeded.
+ */
+ static const char *discard_flag = "DiscardPK";
+ static const char *detach_flag = "Detach";
+ static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
+
+ smartlist_t *flags = smartlist_new();
+ int bad = 0;
+
+ smartlist_split_string(flags, arg + strlen(flags_prefix), ",",
+ SPLIT_IGNORE_BLANK, 0);
+ if (smartlist_len(flags) < 1) {
+ connection_printf_to_buf(conn, "512 Invalid 'Flags' argument\r\n");
+ bad = 1;
+ }
+ SMARTLIST_FOREACH_BEGIN(flags, const char *, flag)
+ {
+ if (!strcasecmp(flag, discard_flag)) {
+ discard_pk = 1;
+ } else if (!strcasecmp(flag, detach_flag)) {
+ detach = 1;
+ } else if (!strcasecmp(flag, max_s_close_flag)) {
+ max_streams_close_circuit = 1;
+ } else {
+ connection_printf_to_buf(conn,
+ "512 Invalid 'Flags' argument: %s\r\n",
+ escaped(flag));
+ bad = 1;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(flag);
+ SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp));
+ smartlist_free(flags);
+ if (bad)
+ goto out;
+ } else {
+ connection_printf_to_buf(conn, "513 Invalid argument\r\n");
+ goto out;
+ }
+ }
+ if (smartlist_len(port_cfgs) == 0) {
+ connection_printf_to_buf(conn, "512 Missing 'Port' argument\r\n");
+ goto out;
+ }
+
+ /* Parse the "keytype:keyblob" argument. */
+ crypto_pk_t *pk = NULL;
+ 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 (err_msg) {
+ connection_write_str_to_buf(err_msg, conn);
+ tor_free(err_msg);
+ }
+ goto out;
+ }
+ tor_assert(!err_msg);
+
+ /* Create the HS, using private key pk, 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,
+ &service_id);
+ port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
+ switch (ret) {
+ case RSAE_OKAY:
+ {
+ char *buf = NULL;
+ tor_assert(service_id);
+ if (key_new_alg) {
+ tor_assert(key_new_blob);
+ tor_asprintf(&buf,
+ "250-ServiceID=%s\r\n"
+ "250-PrivateKey=%s:%s\r\n"
+ "250 OK\r\n",
+ service_id,
+ key_new_alg,
+ key_new_blob);
+ } else {
+ tor_asprintf(&buf,
+ "250-ServiceID=%s\r\n"
+ "250 OK\r\n",
+ service_id);
+ }
+ if (detach) {
+ if (!detached_onion_services)
+ detached_onion_services = smartlist_new();
+ smartlist_add(detached_onion_services, service_id);
+ } else {
+ if (!conn->ephemeral_onion_services)
+ conn->ephemeral_onion_services = smartlist_new();
+ smartlist_add(conn->ephemeral_onion_services, service_id);
+ }
+
+ connection_write_str_to_buf(buf, conn);
+ memwipe(buf, 0, strlen(buf));
+ tor_free(buf);
+ break;
+ }
+ case RSAE_BADPRIVKEY:
+ connection_printf_to_buf(conn, "551 Failed to generate onion address\r\n");
+ break;
+ case RSAE_ADDREXISTS:
+ connection_printf_to_buf(conn, "550 Onion address collision\r\n");
+ break;
+ case RSAE_BADVIRTPORT:
+ connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n");
+ break;
+ case RSAE_INTERNAL: /* FALLSTHROUGH */
+ default:
+ connection_printf_to_buf(conn, "551 Failed to add Onion Service\r\n");
+ }
+ if (key_new_blob) {
+ memwipe(key_new_blob, 0, strlen(key_new_blob));
+ tor_free(key_new_blob);
+ }
+
+ out:
+ if (port_cfgs) {
+ SMARTLIST_FOREACH(port_cfgs, rend_service_port_config_t*, p,
+ rend_service_port_config_free(p));
+ smartlist_free(port_cfgs);
+ }
+
+ SMARTLIST_FOREACH(args, char *, cp, {
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+ });
+ smartlist_free(args);
+ return 0;
+}
+
+/** Helper function to handle parsing the KeyType:KeyBlob argument to the
+ * ADD_ONION command. Return a new crypto_pk_t and if a new key was generated
+ * and the private key not discarded, the algorithm and serialized private key,
+ * or NULL and an optional control protocol error message on failure. The
+ * caller is responsible for freeing the returned key_new_blob and err_msg.
+ *
+ * Note: The error messages returned are deliberately vague to avoid echoing
+ * key material.
+ */
+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)
+{
+ smartlist_t *key_args = smartlist_new();
+ crypto_pk_t *pk = NULL;
+ const char *key_new_alg = NULL;
+ char *key_new_blob = NULL;
+ char *err_msg = NULL;
+ int ok = 0;
+
+ smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0);
+ if (smartlist_len(key_args) != 2) {
+ err_msg = tor_strdup("512 Invalid key type/blob\r\n");
+ goto err;
+ }
+
+ /* The format is "KeyType:KeyBlob". */
+ static const char *key_type_new = "NEW";
+ static const char *key_type_best = "BEST";
+ static const char *key_type_rsa1024 = "RSA1024";
+
+ const char *key_type = smartlist_get(key_args, 0);
+ const char *key_blob = smartlist_get(key_args, 1);
+
+ if (!strcasecmp(key_type_rsa1024, key_type)) {
+ /* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */
+ pk = crypto_pk_base64_decode(key_blob, strlen(key_blob));
+ if (!pk) {
+ err_msg = tor_strdup("512 Failed to decode RSA key\r\n");
+ goto err;
+ }
+ if (crypto_pk_num_bits(pk) != PK_BYTES*8) {
+ err_msg = tor_strdup("512 Invalid RSA key size\r\n");
+ goto err;
+ }
+ } else if (!strcasecmp(key_type_new, key_type)) {
+ /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */
+ if (!strcasecmp(key_type_rsa1024, key_blob) ||
+ !strcasecmp(key_type_best, key_blob)) {
+ /* "RSA1024", RSA 1024 bit, also currently "BEST" by default. */
+ pk = crypto_pk_new();
+ if (crypto_pk_generate_key(pk)) {
+ tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n",
+ key_type_rsa1024);
+ goto err;
+ }
+ if (!discard_pk) {
+ if (crypto_pk_base64_encode(pk, &key_new_blob)) {
+ tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
+ key_type_rsa1024);
+ goto err;
+ }
+ key_new_alg = key_type_rsa1024;
+ }
+ } else {
+ err_msg = tor_strdup("513 Invalid key type\r\n");
+ goto err;
+ }
+ } else {
+ err_msg = tor_strdup("513 Invalid key type\r\n");
+ goto err;
+ }
+
+ /* Succeded in loading or generating a private key. */
+ tor_assert(pk);
+ ok = 1;
+
+ err:
+ SMARTLIST_FOREACH(key_args, char *, cp, {
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+ });
+ smartlist_free(key_args);
+
+ if (!ok) {
+ crypto_pk_free(pk);
+ pk = NULL;
+ }
+ if (err_msg_out) {
+ *err_msg_out = err_msg;
+ } else {
+ tor_free(err_msg);
+ }
+ *key_new_alg_out = key_new_alg;
+ *key_new_blob_out = key_new_blob;
+
+ return pk;
+}
+
+/** Called when we get a DEL_ONION command; parse the body, and remove
+ * the existing ephemeral Onion Service. */
+static int
+handle_control_del_onion(control_connection_t *conn,
+ uint32_t len,
+ const char *body)
+{
+ 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);
+ if (!args)
+ return 0;
+
+ const char *service_id = smartlist_get(args, 0);
+ if (!rend_valid_service_id(service_id)) {
+ connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n");
+ goto out;
+ }
+
+ /* Determine if the onion service belongs to this particular control
+ * connection, or if it is in the global list of detached services. If it
+ * is in neither, either the service ID is invalid in some way, or it
+ * explicitly belongs to a different control connection, and an error
+ * should be returned.
+ */
+ smartlist_t *services[2] = {
+ conn->ephemeral_onion_services,
+ detached_onion_services
+ };
+ smartlist_t *onion_services = NULL;
+ int idx = -1;
+ for (size_t i = 0; i < ARRAY_LENGTH(services); i++) {
+ idx = smartlist_string_pos(services[i], service_id);
+ if (idx != -1) {
+ onion_services = services[i];
+ break;
+ }
+ }
+ 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) {
+ /* This should *NEVER* fail, since the service is on either the
+ * per-control connection list, or the global one.
+ */
+ log_warn(LD_BUG, "Failed to remove Onion Service %s.",
+ escaped(service_id));
+ tor_fragile_assert();
+ }
+
+ /* Remove/scrub the service_id from the appropriate list. */
+ char *cp = smartlist_get(onion_services, idx);
+ smartlist_del(onion_services, idx);
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+
+ send_control_done(conn);
+ }
+
+ out:
+ SMARTLIST_FOREACH(args, char *, cp, {
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+ });
+ smartlist_free(args);
+ return 0;
+}
+
/** Called when <b>conn</b> has no more bytes left on its outbuf. */
int
connection_control_finished_flushing(control_connection_t *conn)
@@ -3257,6 +3956,15 @@ connection_control_closed(control_connection_t *conn)
conn->event_mask = 0;
control_update_global_event_mask();
+ /* Close all ephemeral Onion Services if any.
+ * 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);
+ });
+ }
+
if (conn->is_owning_control_connection) {
lost_owning_controller("connection", "closed");
}
@@ -3508,6 +4216,22 @@ connection_control_process_inbuf(control_connection_t *conn)
} else if (!strcasecmp(conn->incoming_cmd, "DROPGUARDS")) {
if (handle_control_dropguards(conn, cmd_data_len, args))
return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) {
+ if (handle_control_hsfetch(conn, cmd_data_len, args))
+ return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "+HSPOST")) {
+ if (handle_control_hspost(conn, cmd_data_len, args))
+ return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "ADD_ONION")) {
+ int ret = handle_control_add_onion(conn, cmd_data_len, args);
+ memwipe(args, 0, cmd_data_len); /* Scrub the private key. */
+ if (ret)
+ return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "DEL_ONION")) {
+ int ret = handle_control_del_onion(conn, cmd_data_len, args);
+ memwipe(args, 0, cmd_data_len); /* Scrub the service id/pk. */
+ if (ret)
+ return -1;
} else {
connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n",
conn->incoming_cmd);
@@ -4415,6 +5139,52 @@ control_event_or_authdir_new_descriptor(const char *action,
return 0;
}
+/** Cached liveness for network liveness events and GETINFO
+ */
+
+static int network_is_live = 0;
+
+static int
+get_cached_network_liveness(void)
+{
+ return network_is_live;
+}
+
+static void
+set_cached_network_liveness(int liveness)
+{
+ network_is_live = liveness;
+}
+
+/** The network liveness has changed; this is called from circuitstats.c
+ * whenever we receive a cell, or when timeout expires and we assume the
+ * network is down. */
+int
+control_event_network_liveness_update(int liveness)
+{
+ if (liveness > 0) {
+ if (get_cached_network_liveness() <= 0) {
+ /* Update cached liveness */
+ set_cached_network_liveness(1);
+ log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS UP");
+ send_control_event_string(EVENT_NETWORK_LIVENESS, ALL_FORMATS,
+ "650 NETWORK_LIVENESS UP\r\n");
+ }
+ /* else was already live, no-op */
+ } else {
+ if (get_cached_network_liveness() > 0) {
+ /* Update cached liveness */
+ set_cached_network_liveness(0);
+ log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS DOWN");
+ send_control_event_string(EVENT_NETWORK_LIVENESS, ALL_FORMATS,
+ "650 NETWORK_LIVENESS DOWN\r\n");
+ }
+ /* else was already dead, no-op */
+ }
+
+ return 0;
+}
+
/** Helper function for NS-style events. Constructs and sends an event
* of type <b>event</b> with string <b>event_string</b> out of the set of
* networkstatuses <b>statuses</b>. Currently it is used for NS events
@@ -5086,19 +5856,26 @@ MOCK_IMPL(void,
log_fn(severity,
LD_CONTROL, "Problem bootstrapping. Stuck at %d%%: %s. (%s; %s; "
- "count %d; recommendation %s)",
+ "count %d; recommendation %s; host %s at %s:%d)",
status, summary, warn,
orconn_end_reason_to_control_string(reason),
- bootstrap_problems, recommendation);
+ bootstrap_problems, recommendation,
+ hex_str(or_conn->identity_digest, DIGEST_LEN),
+ or_conn->base_.address,
+ or_conn->base_.port);
connection_or_report_broken_states(severity, LD_HANDSHAKE);
tor_snprintf(buf, sizeof(buf),
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\" WARNING=\"%s\" REASON=%s "
- "COUNT=%d RECOMMENDATION=%s",
+ "COUNT=%d RECOMMENDATION=%s HOSTID=\"%s\" HOSTADDR=\"%s:%d\"",
bootstrap_percent, tag, summary, warn,
orconn_end_reason_to_control_string(reason), bootstrap_problems,
- recommendation);
+ recommendation,
+ hex_str(or_conn->identity_digest, DIGEST_LEN),
+ or_conn->base_.address,
+ (int)or_conn->base_.port);
+
tor_snprintf(last_sent_bootstrap_message,
sizeof(last_sent_bootstrap_message),
"WARN %s", buf);
@@ -5169,6 +5946,29 @@ node_describe_longname_by_id,(const char *id_digest))
return longname;
}
+/** Return either the onion address if the given pointer is a non empty
+ * string else the unknown string. */
+static const char *
+rend_hsaddress_str_or_unknown(const char *onion_address)
+{
+ static const char *str_unknown = "UNKNOWN";
+ const char *str_ret = str_unknown;
+
+ /* No valid pointer, unknown it is. */
+ if (!onion_address) {
+ goto end;
+ }
+ /* Empty onion address thus we don't know, unknown it is. */
+ if (onion_address[0] == '\0') {
+ goto end;
+ }
+ /* All checks are good so return the given onion address. */
+ str_ret = onion_address;
+
+ end:
+ return str_ret;
+}
+
/** send HS_DESC requested event.
*
* <b>rend_query</b> is used to fetch requested onion address and auth type.
@@ -5189,12 +5989,75 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query,
send_control_event(EVENT_HS_DESC, ALL_FORMATS,
"650 HS_DESC REQUESTED %s %s %s %s\r\n",
- rend_query->onion_address,
+ rend_hsaddress_str_or_unknown(rend_query->onion_address),
rend_auth_type_to_string(rend_query->auth_type),
node_describe_longname_by_id(id_digest),
desc_id_base32);
}
+/** For an HS descriptor query <b>rend_data</b>, using the
+ * <b>onion_address</b> and HSDir fingerprint <b>hsdir_fp</b>, find out
+ * which descriptor ID in the query is the right one.
+ *
+ * Return a pointer of the binary descriptor ID found in the query's object
+ * or NULL if not found. */
+static const char *
+get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
+{
+ int replica;
+ const char *desc_id = NULL;
+
+ /* Possible if the fetch was done using a descriptor ID. This means that
+ * the HSFETCH command was used. */
+ if (!tor_digest_is_zero(rend_data->desc_id_fetch)) {
+ desc_id = rend_data->desc_id_fetch;
+ goto end;
+ }
+
+ /* OK, we have an onion address so now let's find which descriptor ID
+ * is the one associated with the HSDir fingerprint. */
+ for (replica = 0; replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
+ replica++) {
+ const char *digest = rend_data->descriptor_id[replica];
+
+ SMARTLIST_FOREACH_BEGIN(rend_data->hsdirs_fp, char *, fingerprint) {
+ if (tor_memcmp(fingerprint, hsdir_fp, DIGEST_LEN) == 0) {
+ /* Found it! This descriptor ID is the right one. */
+ desc_id = digest;
+ goto end;
+ }
+ } SMARTLIST_FOREACH_END(fingerprint);
+ }
+
+end:
+ return desc_id;
+}
+
+/** send HS_DESC upload event.
+ *
+ * <b>service_id</b> is the descriptor onion address.
+ * <b>hs_dir</b> is the description of contacting hs directory.
+ * <b>desc_id_base32</b> is the ID of requested hs descriptor.
+ */
+void
+control_event_hs_descriptor_upload(const char *service_id,
+ const char *id_digest,
+ const char *desc_id_base32)
+{
+ 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);
+ return;
+ }
+
+ send_control_event(EVENT_HS_DESC, ALL_FORMATS,
+ "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n",
+ service_id,
+ node_describe_longname_by_id(id_digest),
+ desc_id_base32);
+}
+
/** send HS_DESC event after got response from hs directory.
*
* NOTE: this is an internal function used by following functions:
@@ -5205,27 +6068,77 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query,
*/
void
control_event_hs_descriptor_receive_end(const char *action,
- const rend_data_t *rend_query,
+ const char *onion_address,
+ const rend_data_t *rend_data,
const char *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_query || !id_digest) {
- log_warn(LD_BUG, "Called with action==%p, rend_query==%p, "
- "id_digest==%p", action, rend_query, id_digest);
+ if (!action || !id_digest || !rend_data || !onion_address) {
+ log_warn(LD_BUG, "Called with action==%p, id_digest==%p, "
+ "rend_data==%p, onion_address==%p", action, id_digest,
+ rend_data, 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);
}
send_control_event(EVENT_HS_DESC, ALL_FORMATS,
- "650 HS_DESC %s %s %s %s%s\r\n",
+ "650 HS_DESC %s %s %s %s%s%s\r\n",
+ action,
+ rend_hsaddress_str_or_unknown(onion_address),
+ rend_auth_type_to_string(rend_data->auth_type),
+ node_describe_longname_by_id(id_digest),
+ desc_id_field ? desc_id_field : "",
+ reason_field ? reason_field : "");
+
+ tor_free(desc_id_field);
+ tor_free(reason_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_uploaded
+ * control_event_hs_descriptor_upload_failed
+ *
+ * So do not call this function directly.
+ */
+void
+control_event_hs_descriptor_upload_end(const char *action,
+ const char *id_digest,
+ const char *reason)
+{
+ char *reason_field = NULL;
+
+ if (!action || !id_digest) {
+ log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action,
+ id_digest);
+ return;
+ }
+
+ if (reason) {
+ tor_asprintf(&reason_field, " REASON=%s", reason);
+ }
+
+ send_control_event(EVENT_HS_DESC, ALL_FORMATS,
+ "650 HS_DESC %s UNKNOWN UNKNOWN %s%s\r\n",
action,
- rend_query->onion_address,
- rend_auth_type_to_string(rend_query->auth_type),
node_describe_longname_by_id(id_digest),
reason_field ? reason_field : "");
@@ -5234,19 +6147,35 @@ control_event_hs_descriptor_receive_end(const char *action,
/** send HS_DESC RECEIVED event
*
- * called when a we successfully received a hidden service descriptor.
+ * called when we successfully received a hidden service descriptor.
*/
void
-control_event_hs_descriptor_received(const rend_data_t *rend_query,
+control_event_hs_descriptor_received(const char *onion_address,
+ const rend_data_t *rend_data,
const char *id_digest)
{
- if (!rend_query || !id_digest) {
- log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p",
- rend_query, 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);
return;
}
- control_event_hs_descriptor_receive_end("RECEIVED", rend_query,
- id_digest, NULL);
+ control_event_hs_descriptor_receive_end("RECEIVED", onion_address,
+ rend_data, id_digest, NULL);
+}
+
+/** send HS_DESC UPLOADED event
+ *
+ * called when we successfully uploaded a hidden service descriptor.
+ */
+void
+control_event_hs_descriptor_uploaded(const char *id_digest)
+{
+ if (!id_digest) {
+ log_warn(LD_BUG, "Called with id_digest==%p",
+ id_digest);
+ return;
+ }
+ control_event_hs_descriptor_upload_end("UPLOADED", id_digest, NULL);
}
/** Send HS_DESC event to inform controller that query <b>rend_query</b>
@@ -5255,17 +6184,68 @@ control_event_hs_descriptor_received(const rend_data_t *rend_query,
* field.
*/
void
-control_event_hs_descriptor_failed(const rend_data_t *rend_query,
+control_event_hs_descriptor_failed(const rend_data_t *rend_data,
const char *id_digest,
const char *reason)
{
- if (!rend_query || !id_digest) {
- log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p",
- rend_query, id_digest);
+ if (!rend_data || !id_digest) {
+ log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p",
+ rend_data, id_digest);
return;
}
- control_event_hs_descriptor_receive_end("FAILED", rend_query,
- id_digest, reason);
+ control_event_hs_descriptor_receive_end("FAILED",
+ rend_data->onion_address,
+ rend_data, id_digest, reason);
+}
+
+/** send HS_DESC_CONTENT event after completion of a successful fetch from
+ * hs directory. */
+void
+control_event_hs_descriptor_content(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest,
+ const char *content)
+{
+ static const char *event_name = "HS_DESC_CONTENT";
+ char *esc_content = NULL;
+
+ if (!onion_address || !desc_id || !hsdir_id_digest) {
+ log_warn(LD_BUG, "Called with onion_address==%p, desc_id==%p, "
+ "hsdir_id_digest==%p", onion_address, desc_id, hsdir_id_digest);
+ return;
+ }
+
+ if (content == NULL) {
+ /* Point it to empty content so it can still be escaped. */
+ content = "";
+ }
+ write_escaped_data(content, strlen(content), &esc_content);
+
+ send_control_event(EVENT_HS_DESC_CONTENT, ALL_FORMATS,
+ "650+%s %s %s %s\r\n%s650 OK\r\n",
+ event_name,
+ rend_hsaddress_str_or_unknown(onion_address),
+ desc_id,
+ node_describe_longname_by_id(hsdir_id_digest),
+ esc_content);
+ tor_free(esc_content);
+}
+
+/** Send HS_DESC event to inform controller upload of hidden service
+ * descriptor identified by <b>id_digest</b> failed. If <b>reason</b>
+ * is not NULL, add it to REASON= field.
+ */
+void
+control_event_hs_descriptor_upload_failed(const char *id_digest,
+ const char *reason)
+{
+ if (!id_digest) {
+ log_warn(LD_BUG, "Called with id_digest==%p",
+ id_digest);
+ return;
+ }
+ control_event_hs_descriptor_upload_end("UPLOAD_FAILED",
+ id_digest, reason);
}
/** Free any leftover allocated memory of the control.c subsystem. */
@@ -5274,6 +6254,10 @@ control_free_all(void)
{
if (authentication_cookie) /* Free the auth cookie */
tor_free(authentication_cookie);
+ if (detached_onion_services) { /* Free the detached onion services */
+ SMARTLIST_FOREACH(detached_onion_services, char *, cp, tor_free(cp));
+ smartlist_free(detached_onion_services);
+ }
}
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/control.h b/src/or/control.h
index 47a601817a..2d02443834 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -67,6 +67,7 @@ int control_event_or_authdir_new_descriptor(const char *action,
size_t desclen,
const char *msg);
int control_event_my_descriptor_changed(void);
+int control_event_network_liveness_update(int liveness);
int control_event_networkstatus_changed(smartlist_t *statuses);
int control_event_newconsensus(const networkstatus_t *consensus);
@@ -106,15 +107,30 @@ 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_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 rend_data_t *rend_query,
- const char *hs_dir,
- const char *reason);
-void control_event_hs_descriptor_received(const rend_data_t *rend_query,
- const char *hs_dir);
-void control_event_hs_descriptor_failed(const rend_data_t *rend_query,
- const char *hs_dir,
+ const char *onion_address,
+ const rend_data_t *rend_data,
+ const char *id_digest,
+ const char *reason);
+void control_event_hs_descriptor_upload_end(const char *action,
+ 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);
+void control_event_hs_descriptor_failed(const rend_data_t *rend_data,
+ const char *id_digest,
const char *reason);
+void control_event_hs_descriptor_upload_failed(const char *hs_dir,
+ const char *reason);
+void control_event_hs_descriptor_content(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_fp,
+ const char *content);
void control_free_all(void);
@@ -123,6 +139,7 @@ void control_free_all(void);
* because it is used both as a list of v0 event types, and as indices
* into the bitfield to determine which controllers want which events.
*/
+/* This bitfield has no event zero 0x0000 */
#define EVENT_MIN_ 0x0001
#define EVENT_CIRCUIT_STATUS 0x0001
#define EVENT_STREAM_STATUS 0x0002
@@ -157,10 +174,32 @@ void control_free_all(void);
#define EVENT_CIRC_BANDWIDTH_USED 0x001D
#define EVENT_TRANSPORT_LAUNCHED 0x0020
#define EVENT_HS_DESC 0x0021
-#define EVENT_MAX_ 0x0021
-/* If EVENT_MAX_ ever hits 0x003F, we need to make the mask into a
+#define EVENT_HS_DESC_CONTENT 0x0022
+#define EVENT_NETWORK_LIVENESS 0x0023
+#define EVENT_MAX_ 0x0023
+
+/* sizeof(control_connection_t.event_mask) in bits, currently a uint64_t */
+#define EVENT_CAPACITY_ 0x0040
+
+/* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a
* different structure, as it can only handle a maximum left shift of 1<<63. */
+#if EVENT_MAX_ >= EVENT_CAPACITY_
+#error control_connection_t.event_mask has an event greater than its capacity
+#endif
+
+#define EVENT_MASK_(e) (((uint64_t)1)<<(e))
+
+#define EVENT_MASK_NONE_ ((uint64_t)0x0)
+
+#define EVENT_MASK_ABOVE_MIN_ ((~((uint64_t)0x0)) << EVENT_MIN_)
+#define EVENT_MASK_BELOW_MAX_ ((~((uint64_t)0x0)) \
+ >> (EVENT_CAPACITY_ - EVENT_MAX_ \
+ - EVENT_MIN_))
+
+#define EVENT_MASK_ALL_ (EVENT_MASK_ABOVE_MIN_ \
+ & EVENT_MASK_BELOW_MAX_)
+
/* Used only by control.c and test.c */
STATIC size_t write_escaped_data(const char *data, size_t len, char **out);
STATIC size_t read_escaped_data(const char *data, size_t len, char **out);
@@ -204,6 +243,11 @@ void append_cell_stats_by_command(smartlist_t *event_parts,
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);
#endif
#endif
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index 09ffdb81d1..d511ecf84c 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -178,6 +178,12 @@ update_state_threadfn(void *state_, void *work_)
void
cpuworkers_rotate_keyinfo(void)
{
+ if (!threadpool) {
+ /* If we're a client, then we won't have cpuworkers, and we won't need
+ * to tell them to rotate their state.
+ */
+ return;
+ }
if (threadpool_queue_update(threadpool,
worker_state_new,
update_state_threadfn,
@@ -486,6 +492,8 @@ assign_onionskin_to_cpuworker(or_circuit_t *circ,
cpuworker_request_t req;
int should_time;
+ tor_assert(threadpool);
+
if (!circ->p_chan) {
log_info(LD_OR,"circ->p_chan gone. Failing circ.");
tor_free(onionskin);
diff --git a/src/or/directory.c b/src/or/directory.c
index d2b6b86f6d..9fe3052774 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2102,14 +2102,23 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
control_event_hs_descriptor_failed(conn->rend_data, \
conn->identity_digest, \
reason) )
+ #define SEND_HS_DESC_FAILED_CONTENT() ( \
+ control_event_hs_descriptor_content(conn->rend_data->onion_address, \
+ conn->requested_resource, \
+ conn->identity_digest, \
+ NULL) )
tor_assert(conn->rend_data);
log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d "
"(%s))",
(int)body_len, status_code, escaped(reason));
switch (status_code) {
case 200:
+ {
+ rend_cache_entry_t *entry = NULL;
+
switch (rend_cache_store_v2_desc_as_client(body,
- conn->requested_resource, conn->rend_data)) {
+ conn->requested_resource, conn->rend_data,
+ &entry)) {
case RCS_BADDESC:
case RCS_NOTDIR: /* Impossible */
log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. "
@@ -2117,25 +2126,41 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
/* We'll retry when connection_about_to_close_connection()
* cleans this dir conn up. */
SEND_HS_DESC_FAILED_EVENT("BAD_DESC");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
case RCS_OKAY:
default:
+ {
+ char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
+ /* Should never be NULL here for an OKAY returned code. */
+ tor_assert(entry);
+ rend_get_service_id(entry->parsed->pk, service_id);
+
/* success. notify pending connections about this. */
log_info(LD_REND, "Successfully fetched v2 rendezvous "
"descriptor.");
- control_event_hs_descriptor_received(conn->rend_data,
+ control_event_hs_descriptor_received(service_id,
+ conn->rend_data,
conn->identity_digest);
+ control_event_hs_descriptor_content(service_id,
+ conn->requested_resource,
+ conn->identity_digest,
+ body);
conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2;
- rend_client_desc_trynow(conn->rend_data->onion_address);
+ rend_client_desc_trynow(service_id);
+ memwipe(service_id, 0, sizeof(service_id));
break;
+ }
}
break;
+ }
case 404:
/* Not there. We'll retry when
* connection_about_to_close_connection() cleans this conn up. */
log_info(LD_REND,"Fetching v2 rendezvous descriptor failed: "
"Retrying at another directory.");
SEND_HS_DESC_FAILED_EVENT("NOT_FOUND");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
case 400:
log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
@@ -2143,6 +2168,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
"v2 rendezvous query? Retrying at another directory.",
escaped(reason));
SEND_HS_DESC_FAILED_EVENT("QUERY_REJECTED");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
default:
log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
@@ -2152,11 +2178,15 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
status_code, escaped(reason), conn->base_.address,
conn->base_.port);
SEND_HS_DESC_FAILED_EVENT("UNEXPECTED");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
}
}
if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) {
+ #define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason) ( \
+ control_event_hs_descriptor_upload_failed(conn->identity_digest, \
+ reason) )
log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
"(%s))",
status_code, escaped(reason));
@@ -2165,17 +2195,20 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
log_info(LD_REND,
"Uploading rendezvous descriptor: finished with status "
"200 (%s)", escaped(reason));
+ control_event_hs_descriptor_uploaded(conn->identity_digest);
break;
case 400:
log_warn(LD_REND,"http status 400 (%s) response from dirserver "
"'%s:%d'. Malformed rendezvous descriptor?",
escaped(reason), conn->base_.address, conn->base_.port);
+ SEND_HS_DESC_UPLOAD_FAILED_EVENT("UPLOAD_REJECTED");
break;
default:
log_warn(LD_REND,"http status %d (%s) response unexpected (server "
"'%s:%d').",
status_code, escaped(reason), conn->base_.address,
conn->base_.port);
+ SEND_HS_DESC_UPLOAD_FAILED_EVENT("UNEXPECTED");
break;
}
}
@@ -3066,7 +3099,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
/* Handle v2 rendezvous descriptor fetch request. */
const char *descp;
const char *query = url + strlen("/tor/rendezvous2/");
- if (strlen(query) == REND_DESC_ID_V2_LEN_BASE32) {
+ if (rend_valid_descriptor_id(query)) {
log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'",
safe_str(escaped(query)));
switch (rend_cache_lookup_v2_desc_as_dir(query, &descp)) {
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 5b103bcf52..bee67cf749 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -1369,7 +1369,7 @@ dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
else
uptime = real_uptime(router, now);
- return (router->wants_to_be_hs_dir &&
+ return (router->wants_to_be_hs_dir && router->dir_port &&
uptime >= get_options()->MinUptimeHidServDirectoryV2 &&
router_is_active(router, node, now));
}
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 549dec1131..e037794fc7 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -2243,7 +2243,8 @@ networkstatus_format_signatures(networkstatus_t *consensus,
for_detached_signatures ? flavor_name : "",
digest_name, id, sk);
}
- base64_encode(buf, sizeof(buf), sig->signature, sig->signature_len);
+ base64_encode(buf, sizeof(buf), sig->signature, sig->signature_len,
+ BASE64_ENCODE_MULTILINE);
strlcat(buf, "-----END SIGNATURE-----\n", sizeof(buf));
smartlist_add(elements, tor_strdup(buf));
} SMARTLIST_FOREACH_END(sig);
@@ -3458,7 +3459,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
char kbuf[128];
base64_encode(kbuf, sizeof(kbuf),
(const char*)ri->onion_curve25519_pkey->public_key,
- CURVE25519_PUBKEY_LEN);
+ CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
}
diff --git a/src/or/dns.c b/src/or/dns.c
index cc4a169422..db77d20783 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -24,7 +24,7 @@
#include "relay.h"
#include "router.h"
#include "ht.h"
-#include "../common/sandbox.h"
+#include "sandbox.h"
#ifdef HAVE_EVENT2_DNS_H
#include <event2/event.h>
#include <event2/dns.h>
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 30108b6041..ebf675166b 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -141,8 +141,7 @@ entry_guard_set_status(entry_guard_t *e, const node_t *node,
}
if (node) {
- int is_dir = node_is_dir(node) && node->rs &&
- node->rs->version_supports_microdesc_cache;
+ int is_dir = node_is_dir(node);
if (options->UseBridges && node_is_a_configured_bridge(node))
is_dir = 1;
if (e->is_dir_cache != is_dir) {
@@ -398,10 +397,10 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend,
entry->bad_since = 0;
entry->can_retry = 1;
}
- entry->is_dir_cache = node->rs &&
- node->rs->version_supports_microdesc_cache;
+ entry->is_dir_cache = node_is_dir(node);
if (get_options()->UseBridges && node_is_a_configured_bridge(node))
entry->is_dir_cache = 1;
+
return NULL;
}
} else if (!for_directory) {
@@ -432,8 +431,7 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend,
node_describe(node));
strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname));
memcpy(entry->identity, node->identity, DIGEST_LEN);
- entry->is_dir_cache = node_is_dir(node) && node->rs &&
- node->rs->version_supports_microdesc_cache;
+ entry->is_dir_cache = node_is_dir(node);
if (get_options()->UseBridges && node_is_a_configured_bridge(node))
entry->is_dir_cache = 1;
@@ -442,7 +440,8 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend,
* don't all select them on the same day, and b) avoid leaving a
* precise timestamp in the state file about when we first picked
* this guard. For details, see the Jan 2010 or-dev thread. */
- entry->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30);
+ time_t now = time(NULL);
+ entry->chosen_on_date = crypto_rand_time_range(now - 3600*24*30, now);
entry->chosen_by_version = tor_strdup(VERSION);
/* Are we picking this guard because all of our current guards are
@@ -571,22 +570,6 @@ remove_obsolete_entry_guards(time_t now)
} else if (tor_version_parse(ver, &v)) {
msg = "does not seem to be from any recognized version of Tor";
version_is_bad = 1;
- } else {
- char *tor_ver = NULL;
- tor_asprintf(&tor_ver, "Tor %s", ver);
- if ((tor_version_as_new_as(tor_ver, "0.1.0.10-alpha") &&
- !tor_version_as_new_as(tor_ver, "0.1.2.16-dev")) ||
- (tor_version_as_new_as(tor_ver, "0.2.0.0-alpha") &&
- !tor_version_as_new_as(tor_ver, "0.2.0.6-alpha")) ||
- /* above are bug 440; below are bug 1217 */
- (tor_version_as_new_as(tor_ver, "0.2.1.3-alpha") &&
- !tor_version_as_new_as(tor_ver, "0.2.1.23")) ||
- (tor_version_as_new_as(tor_ver, "0.2.2.0-alpha") &&
- !tor_version_as_new_as(tor_ver, "0.2.2.7-alpha"))) {
- msg = "was selected without regard for guard bandwidth";
- version_is_bad = 1;
- }
- tor_free(tor_ver);
}
if (!version_is_bad && entry->chosen_on_date + guard_lifetime < now) {
/* It's been too long since the date listed in our state file. */
@@ -989,39 +972,6 @@ entry_list_is_constrained(const or_options_t *options)
return 0;
}
-/** Return true iff this node can answer directory questions about
- * microdescriptors. */
-static int
-node_understands_microdescriptors(const node_t *node)
-{
- tor_assert(node);
- if (node->rs && node->rs->version_supports_microdesc_cache)
- return 1;
- if (node->ri && tor_version_supports_microdescriptors(node->ri->platform))
- return 1;
- return 0;
-}
-
-/** Return true iff <b>node</b> is able to answer directory questions
- * of type <b>dirinfo</b>. Always returns true if <b>dirinfo</b> is
- * NO_DIRINFO (zero). */
-static int
-node_can_handle_dirinfo(const node_t *node, dirinfo_type_t dirinfo)
-{
- /* Checking dirinfo for any type other than microdescriptors isn't required
- yet, since we only choose directory guards that can support microdescs,
- routerinfos, and networkstatuses, AND we don't use directory guards if
- we're configured to do direct downloads of anything else. The only case
- where we might have a guard that doesn't know about a type of directory
- information is when we're retrieving directory information from a
- bridge. */
-
- if ((dirinfo & MICRODESC_DIRINFO) &&
- !node_understands_microdescriptors(node))
- return 0;
- return 1;
-}
-
/** Pick a live (up and listed) entry guard from entry_guards. If
* <b>state</b> is non-NULL, this is for a specific circuit --
* make sure not to pick this circuit's exit or any node in the
@@ -1077,6 +1027,8 @@ populate_live_entry_guards(smartlist_t *live_entry_guards,
int retval = 0;
entry_is_live_flags_t entry_flags = 0;
+ (void) dirinfo_type;
+
{ /* Set the flags we want our entry node to have */
if (need_uptime) {
entry_flags |= ENTRY_NEED_UPTIME;
@@ -1108,9 +1060,6 @@ populate_live_entry_guards(smartlist_t *live_entry_guards,
continue; /* don't pick the same node for entry and exit */
if (smartlist_contains(exit_family, node))
continue; /* avoid relays that are family members of our exit */
- if (dirinfo_type != NO_DIRINFO &&
- !node_can_handle_dirinfo(node, dirinfo_type))
- continue; /* this node won't be able to answer our dir questions */
smartlist_add(live_entry_guards, (void*)node);
if (!entry->made_contact) {
/* Always start with the first not-yet-contacted entry
@@ -1491,8 +1440,9 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
}
} else {
if (state_version) {
+ time_t now = time(NULL);
+ e->chosen_on_date = crypto_rand_time_range(now - 3600*24*30, now);
e->chosen_by_version = tor_strdup(state_version);
- e->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30);
}
}
if (e->path_bias_disabled && !e->bad_since)
@@ -2484,11 +2434,9 @@ any_bridge_supports_microdescriptors(void)
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) {
node = node_get_by_id(e->identity);
if (node && node->is_running &&
- node_is_bridge(node) && node_is_a_configured_bridge(node) &&
- node_understands_microdescriptors(node)) {
+ node_is_bridge(node) && node_is_a_configured_bridge(node)) {
/* This is one of our current bridges, and we know enough about
- * it to know that it will be able to answer our microdescriptor
- * questions. */
+ * it to know that it will be able to answer our questions. */
return 1;
}
} SMARTLIST_FOREACH_END(e);
diff --git a/src/or/include.am b/src/or/include.am
index 9245ade64f..6bbf78871c 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -15,7 +15,7 @@ else
tor_platform_source=
endif
-EXTRA_DIST+= src/or/ntmain.c src/or/or_sha1.i src/or/Makefile.nmake
+EXTRA_DIST+= src/or/ntmain.c src/or/Makefile.nmake
if USE_EXTERNAL_EVDNS
evdns_source=
@@ -83,16 +83,11 @@ LIBTOR_A_SOURCES = \
src/or/torcert.c \
src/or/onion_ntor.c \
$(evdns_source) \
- $(tor_platform_source) \
- src/or/config_codedigest.c
+ $(tor_platform_source)
src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES)
src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES)
-#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \
-# ../common/libor-event.a
-#src_or_libtor_a_LIBADD = src/trunnel/libor-trunnel.a
-
src_or_tor_SOURCES = src/or/tor_main.c
AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or
@@ -102,7 +97,7 @@ AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
-DBINDIR="\"$(bindir)\""
-src_or_libtor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_or_libtor_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
# -L flags need to go in LDFLAGS. -l flags need to go in LDADD.
@@ -119,7 +114,7 @@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \
if COVERAGE_ENABLED
src_or_tor_cov_SOURCES = src/or/tor_main.c
-src_or_tor_cov_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_or_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_or_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_or_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
@@ -127,9 +122,9 @@ src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@
-TESTING_TOR_BINARY = ./src/or/tor-cov
+TESTING_TOR_BINARY = $(top_builddir)/src/or/tor-cov
else
-TESTING_TOR_BINARY = ./src/or/tor
+TESTING_TOR_BINARY = $(top_builddir)/src/or/tor
endif
ORHEADERS = \
@@ -198,36 +193,24 @@ ORHEADERS = \
noinst_HEADERS+= $(ORHEADERS) micro-revision.i
-src/or/config_codedigest.o: src/or/or_sha1.i
-
micro-revision.i: FORCE
- @rm -f micro-revision.tmp; \
- if test -d "$(top_srcdir)/.git" && \
- test -x "`which git 2>&1;true`"; then \
- HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \
- echo \"$$HASH\" > micro-revision.tmp; \
- fi; \
- if test ! -f micro-revision.tmp ; then \
- if test ! -f micro-revision.i ; then \
- echo '""' > micro-revision.i; \
- fi; \
- elif test ! -f micro-revision.i || \
- test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \
- mv micro-revision.tmp micro-revision.i; \
- fi; true
-
-src/or/or_sha1.i: $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS)
- $(AM_V_GEN)if test "@SHA1SUM@" != none; then \
- (cd "$(srcdir)" && "@SHA1SUM@" $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS) ) | \
- "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > src/or/or_sha1.i; \
- elif test "@OPENSSL@" != none; then \
- (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS)) | \
- "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > src/or/or_sha1.i; \
- else \
- rm src/or/or_sha1.i; \
- touch src/or/or_sha1.i; \
- fi
-
-CLEANFILES+= micro-revision.i src/or/micro-revision.i
+ $(AM_V_at)rm -f micro-revision.tmp; \
+ if test -d "$(top_srcdir)/.git" && \
+ test -x "`which git 2>&1;true`"; then \
+ HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \
+ echo \"$$HASH\" > micro-revision.tmp; \
+ fi; \
+ if test ! -f micro-revision.tmp; then \
+ if test ! -f micro-revision.i; then \
+ echo '""' > micro-revision.i; \
+ fi; \
+ elif test ! -f micro-revision.i || \
+ test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \
+ mv micro-revision.tmp micro-revision.i; \
+ fi; \
+ rm -f micro-revision.tmp; \
+ true
+
+CLEANFILES+= micro-revision.i src/or/micro-revision.i micro-revision.tmp
FORCE:
diff --git a/src/or/main.c b/src/or/main.c
index 4fac17a59c..bbee8e0fb9 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -65,7 +65,7 @@
#include <openssl/crypto.h>
#endif
#include "memarea.h"
-#include "../common/sandbox.h"
+#include "sandbox.h"
#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
@@ -99,6 +99,7 @@ static void second_elapsed_callback(periodic_timer_t *timer, void *args);
static int conn_close_if_marked(int i);
static void connection_start_reading_from_linked_conn(connection_t *conn);
static int connection_should_read_from_linked_conn(connection_t *conn);
+static int run_main_loop_until_done(void);
/********* START VARIABLES **********/
@@ -132,10 +133,6 @@ static uint64_t stats_n_bytes_written = 0;
time_t time_of_process_start = 0;
/** How many seconds have we been running? */
long stats_n_seconds_working = 0;
-/** When do we next launch DNS wildcarding checks? */
-static time_t time_to_check_for_correct_dns = 0;
-/** When do we next make sure our Ed25519 keys aren't about to expire? */
-static time_t time_to_check_ed_keys = 0;
/** How often will we honor SIGNEWNYM requests? */
#define MAX_SIGNEWNYM_RATE 10
@@ -1205,7 +1202,49 @@ get_signewnym_epoch(void)
return newnym_epoch;
}
-static time_t time_to_check_descriptor = 0;
+typedef struct {
+ time_t last_rotated_x509_certificate;
+ time_t check_v3_certificate;
+ time_t check_listeners;
+ time_t download_networkstatus;
+ time_t try_getting_descriptors;
+ time_t reset_descriptor_failures;
+ time_t add_entropy;
+ time_t write_bridge_status_file;
+ time_t downrate_stability;
+ time_t save_stability;
+ time_t clean_caches;
+ time_t recheck_bandwidth;
+ time_t check_for_expired_networkstatus;
+ time_t write_stats_files;
+ time_t write_bridge_stats;
+ time_t check_port_forwarding;
+ time_t launch_reachability_tests;
+ time_t retry_dns_init;
+ time_t next_heartbeat;
+ time_t check_descriptor;
+ /** When do we next launch DNS wildcarding checks? */
+ time_t check_for_correct_dns;
+ /** When do we next make sure our Ed25519 keys aren't about to expire? */
+ time_t check_ed_keys;
+
+} time_to_t;
+
+static time_to_t time_to = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/** Reset all the time_to's so we'll do all our actions again as if we
+ * just started up.
+ * Useful if our clock just moved back a long time from the future,
+ * so we don't wait until that future arrives again before acting.
+ */
+void
+reset_all_main_loop_timers(void)
+{
+ memset(&time_to, 0, sizeof(time_to));
+}
+
/**
* Update our schedule so that we'll check whether we need to update our
* descriptor immediately, rather than after up to CHECK_DESCRIPTOR_INTERVAL
@@ -1214,7 +1253,7 @@ static time_t time_to_check_descriptor = 0;
void
reschedule_descriptor_update_check(void)
{
- time_to_check_descriptor = 0;
+ time_to.check_descriptor = 0;
}
/** Perform regular maintenance tasks. This function gets run once per
@@ -1223,26 +1262,7 @@ reschedule_descriptor_update_check(void)
static void
run_scheduled_events(time_t now)
{
- static time_t last_rotated_x509_certificate = 0;
- static time_t time_to_check_v3_certificate = 0;
- static time_t time_to_check_listeners = 0;
- static time_t time_to_download_networkstatus = 0;
- static time_t time_to_try_getting_descriptors = 0;
- static time_t time_to_reset_descriptor_failures = 0;
- static time_t time_to_add_entropy = 0;
- static time_t time_to_write_bridge_status_file = 0;
- static time_t time_to_downrate_stability = 0;
- static time_t time_to_save_stability = 0;
- static time_t time_to_clean_caches = 0;
- static time_t time_to_recheck_bandwidth = 0;
- static time_t time_to_check_for_expired_networkstatus = 0;
- static time_t time_to_write_stats_files = 0;
- static time_t time_to_write_bridge_stats = 0;
- static time_t time_to_check_port_forwarding = 0;
- static time_t time_to_launch_reachability_tests = 0;
static int should_init_bridge_stats = 1;
- static time_t time_to_retry_dns_init = 0;
- static time_t time_to_next_heartbeat = 0;
const or_options_t *options = get_options();
int is_server = server_mode(options);
@@ -1282,7 +1302,7 @@ run_scheduled_events(time_t now)
router_upload_dir_desc_to_dirservers(0);
}
- if (is_server && time_to_check_ed_keys < now) {
+ if (is_server && time_to.check_ed_keys < now) {
if (should_make_new_ed_keys(options, now)) {
if (load_ed_keys(options, now) < 0 ||
generate_ed_link_cert(options, now)) {
@@ -1291,22 +1311,22 @@ run_scheduled_events(time_t now)
exit(0);
}
}
- time_to_check_ed_keys = now + 30;
+ time_to.check_ed_keys = now + 30;
}
if (!should_delay_dir_fetches(options, NULL) &&
- time_to_try_getting_descriptors < now) {
+ time_to.try_getting_descriptors < now) {
update_all_descriptor_downloads(now);
update_extrainfo_downloads(now);
if (router_have_minimum_dir_info())
- time_to_try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL;
+ time_to.try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL;
else
- time_to_try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL;
+ time_to.try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL;
}
- if (time_to_reset_descriptor_failures < now) {
+ if (time_to.reset_descriptor_failures < now) {
router_reset_descriptor_download_failures();
- time_to_reset_descriptor_failures =
+ time_to.reset_descriptor_failures =
now + DESCRIPTOR_FAILURE_RESET_INTERVAL;
}
@@ -1315,28 +1335,29 @@ run_scheduled_events(time_t now)
/* 1b. Every MAX_SSL_KEY_LIFETIME_INTERNAL seconds, we change our
* TLS context. */
- if (!last_rotated_x509_certificate)
- last_rotated_x509_certificate = now;
- if (last_rotated_x509_certificate+MAX_SSL_KEY_LIFETIME_INTERNAL < now) {
+ if (!time_to.last_rotated_x509_certificate)
+ time_to.last_rotated_x509_certificate = now;
+ if (time_to.last_rotated_x509_certificate +
+ MAX_SSL_KEY_LIFETIME_INTERNAL < now) {
log_info(LD_GENERAL,"Rotating tls context.");
if (router_initialize_tls_context() < 0) {
log_warn(LD_BUG, "Error reinitializing TLS context");
/* XXX is it a bug here, that we just keep going? -RD */
}
- last_rotated_x509_certificate = now;
+ time_to.last_rotated_x509_certificate = now;
/* We also make sure to rotate the TLS connections themselves if they've
* been up for too long -- but that's done via is_bad_for_new_circs in
* connection_run_housekeeping() above. */
}
- if (time_to_add_entropy < now) {
- if (time_to_add_entropy) {
+ if (time_to.add_entropy < now) {
+ if (time_to.add_entropy) {
/* We already seeded once, so don't die on failure. */
- crypto_seed_rng(0);
+ crypto_seed_rng();
}
/** How often do we add more entropy to OpenSSL's RNG pool? */
#define ENTROPY_INTERVAL (60*60)
- time_to_add_entropy = now + ENTROPY_INTERVAL;
+ time_to.add_entropy = now + ENTROPY_INTERVAL;
}
/* 1c. If we have to change the accounting interval or record
@@ -1344,10 +1365,10 @@ run_scheduled_events(time_t now)
if (accounting_is_enabled(options))
accounting_run_housekeeping(now);
- if (time_to_launch_reachability_tests < now &&
+ if (time_to.launch_reachability_tests < now &&
(authdir_mode_tests_reachability(options)) &&
!net_is_disabled()) {
- time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL;
+ time_to.launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL;
/* try to determine reachability of the other Tor relays */
dirserv_test_reachability(now);
}
@@ -1355,29 +1376,29 @@ run_scheduled_events(time_t now)
/* 1d. Periodically, we discount older stability information so that new
* stability info counts more, and save the stability information to disk as
* appropriate. */
- if (time_to_downrate_stability < now)
- time_to_downrate_stability = rep_hist_downrate_old_runs(now);
+ if (time_to.downrate_stability < now)
+ time_to.downrate_stability = rep_hist_downrate_old_runs(now);
if (authdir_mode_tests_reachability(options)) {
- if (time_to_save_stability < now) {
- if (time_to_save_stability && rep_hist_record_mtbf_data(now, 1)<0) {
+ if (time_to.save_stability < now) {
+ if (time_to.save_stability && rep_hist_record_mtbf_data(now, 1)<0) {
log_warn(LD_GENERAL, "Couldn't store mtbf data.");
}
#define SAVE_STABILITY_INTERVAL (30*60)
- time_to_save_stability = now + SAVE_STABILITY_INTERVAL;
+ time_to.save_stability = now + SAVE_STABILITY_INTERVAL;
}
}
/* 1e. Periodically, if we're a v3 authority, we check whether our cert is
* close to expiring and warn the admin if it is. */
- if (time_to_check_v3_certificate < now) {
+ if (time_to.check_v3_certificate < now) {
v3_authority_check_key_expiry();
#define CHECK_V3_CERTIFICATE_INTERVAL (5*60)
- time_to_check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL;
+ time_to.check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL;
}
/* 1f. Check whether our networkstatus has expired.
*/
- if (time_to_check_for_expired_networkstatus < now) {
+ if (time_to.check_for_expired_networkstatus < now) {
networkstatus_t *ns = networkstatus_get_latest_consensus();
/*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in
* networkstatus_get_reasonably_live_consensus(), but that value is way
@@ -1388,68 +1409,68 @@ run_scheduled_events(time_t now)
router_dir_info_changed();
}
#define CHECK_EXPIRED_NS_INTERVAL (2*60)
- time_to_check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL;
+ time_to.check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL;
}
/* 1g. Check whether we should write statistics to disk.
*/
- if (time_to_write_stats_files < now) {
+ if (time_to.write_stats_files < now) {
#define CHECK_WRITE_STATS_INTERVAL (60*60)
- time_t next_time_to_write_stats_files = (time_to_write_stats_files > 0 ?
- time_to_write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL;
+ time_t next_time_to_write_stats_files = (time_to.write_stats_files > 0 ?
+ time_to.write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL;
if (options->CellStatistics) {
time_t next_write =
- rep_hist_buffer_stats_write(time_to_write_stats_files);
+ rep_hist_buffer_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->DirReqStatistics) {
- time_t next_write = geoip_dirreq_stats_write(time_to_write_stats_files);
+ time_t next_write = geoip_dirreq_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->EntryStatistics) {
- time_t next_write = geoip_entry_stats_write(time_to_write_stats_files);
+ time_t next_write = geoip_entry_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->HiddenServiceStatistics) {
- time_t next_write = rep_hist_hs_stats_write(time_to_write_stats_files);
+ time_t next_write = rep_hist_hs_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->ExitPortStatistics) {
- time_t next_write = rep_hist_exit_stats_write(time_to_write_stats_files);
+ time_t next_write = rep_hist_exit_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->ConnDirectionStatistics) {
- time_t next_write = rep_hist_conn_stats_write(time_to_write_stats_files);
+ time_t next_write = rep_hist_conn_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->BridgeAuthoritativeDir) {
- time_t next_write = rep_hist_desc_stats_write(time_to_write_stats_files);
+ time_t next_write = rep_hist_desc_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
- time_to_write_stats_files = next_time_to_write_stats_files;
+ time_to.write_stats_files = next_time_to_write_stats_files;
}
/* 1h. Check whether we should write bridge statistics to disk.
*/
if (should_record_bridge_info(options)) {
- if (time_to_write_bridge_stats < now) {
+ if (time_to.write_bridge_stats < now) {
if (should_init_bridge_stats) {
/* (Re-)initialize bridge statistics. */
geoip_bridge_stats_init(now);
- time_to_write_bridge_stats = now + WRITE_STATS_INTERVAL;
+ time_to.write_bridge_stats = now + WRITE_STATS_INTERVAL;
should_init_bridge_stats = 0;
} else {
/* Possibly write bridge statistics to disk and ask when to write
* them next time. */
- time_to_write_bridge_stats = geoip_bridge_stats_write(
- time_to_write_bridge_stats);
+ time_to.write_bridge_stats = geoip_bridge_stats_write(
+ time_to.write_bridge_stats);
}
}
} else if (!should_init_bridge_stats) {
@@ -1459,19 +1480,19 @@ run_scheduled_events(time_t now)
}
/* Remove old information from rephist and the rend cache. */
- if (time_to_clean_caches < now) {
+ if (time_to.clean_caches < now) {
rep_history_clean(now - options->RephistTrackTime);
rend_cache_clean(now);
rend_cache_clean_v2_descs_as_dir(now, 0);
microdesc_cache_rebuild(NULL, 0);
#define CLEAN_CACHES_INTERVAL (30*60)
- time_to_clean_caches = now + CLEAN_CACHES_INTERVAL;
+ time_to.clean_caches = now + CLEAN_CACHES_INTERVAL;
}
#define RETRY_DNS_INTERVAL (10*60)
/* If we're a server and initializing dns failed, retry periodically. */
- if (time_to_retry_dns_init < now) {
- time_to_retry_dns_init = now + RETRY_DNS_INTERVAL;
+ if (time_to.retry_dns_init < now) {
+ time_to.retry_dns_init = now + RETRY_DNS_INTERVAL;
if (is_server && has_dns_init_failed())
dns_init();
}
@@ -1486,9 +1507,9 @@ run_scheduled_events(time_t now)
/* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */
- if (time_to_check_descriptor < now && !options->DisableNetwork) {
+ if (time_to.check_descriptor < now && !options->DisableNetwork) {
static int dirport_reachability_count = 0;
- time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
+ time_to.check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
check_descriptor_bandwidth_changed(now);
check_descriptor_ipaddress_changed(now);
mark_my_descriptor_dirty_if_too_old(now);
@@ -1502,18 +1523,18 @@ run_scheduled_events(time_t now)
consider_testing_reachability(1, dirport_reachability_count==0);
if (++dirport_reachability_count > 5)
dirport_reachability_count = 0;
- } else if (time_to_recheck_bandwidth < now) {
+ } else if (time_to.recheck_bandwidth < now) {
/* If we haven't checked for 12 hours and our bandwidth estimate is
* low, do another bandwidth test. This is especially important for
* bridges, since they might go long periods without much use. */
const routerinfo_t *me = router_get_my_routerinfo();
- if (time_to_recheck_bandwidth && me &&
+ if (time_to.recheck_bandwidth && me &&
me->bandwidthcapacity < me->bandwidthrate &&
me->bandwidthcapacity < 51200) {
reset_bandwidth_test();
}
#define BANDWIDTH_RECHECK_INTERVAL (12*60*60)
- time_to_recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL;
+ time_to.recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL;
}
}
@@ -1531,8 +1552,8 @@ run_scheduled_events(time_t now)
#define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60)
if (!should_delay_dir_fetches(options, NULL) &&
- time_to_download_networkstatus < now) {
- time_to_download_networkstatus =
+ time_to.download_networkstatus < now) {
+ time_to.download_networkstatus =
now + networkstatus_dl_check_interval(options);
update_networkstatus_downloads(now);
}
@@ -1562,9 +1583,9 @@ run_scheduled_events(time_t now)
connection_expire_held_open();
/* 3d. And every 60 seconds, we relaunch listeners if any died. */
- if (!net_is_disabled() && time_to_check_listeners < now) {
+ if (!net_is_disabled() && time_to.check_listeners < now) {
retry_all_listeners(NULL, NULL, 0);
- time_to_check_listeners = now+60;
+ time_to.check_listeners = now+60;
}
/* 4. Every second, we try a new circuit if there are no valid
@@ -1616,28 +1637,29 @@ run_scheduled_events(time_t now)
* to us. */
if (!net_is_disabled() &&
public_server_mode(options) &&
- time_to_check_for_correct_dns < now &&
+ time_to.check_for_correct_dns < now &&
! router_my_exit_policy_is_reject_star()) {
- if (!time_to_check_for_correct_dns) {
- time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120);
+ if (!time_to.check_for_correct_dns) {
+ time_to.check_for_correct_dns =
+ crypto_rand_time_range(now + 60, now + 180);
} else {
dns_launch_correctness_checks();
- time_to_check_for_correct_dns = now + 12*3600 +
+ time_to.check_for_correct_dns = now + 12*3600 +
crypto_rand_int(12*3600);
}
}
/* 10. write bridge networkstatus file to disk */
if (options->BridgeAuthoritativeDir &&
- time_to_write_bridge_status_file < now) {
+ time_to.write_bridge_status_file < now) {
networkstatus_dump_bridge_status_to_file(now);
#define BRIDGE_STATUSFILE_INTERVAL (30*60)
- time_to_write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL;
+ time_to.write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL;
}
/* 11. check the port forwarding app */
if (!net_is_disabled() &&
- time_to_check_port_forwarding < now &&
+ time_to.check_port_forwarding < now &&
options->PortForwarding &&
is_server) {
#define PORT_FORWARDING_CHECK_INTERVAL 5
@@ -1650,7 +1672,7 @@ run_scheduled_events(time_t now)
SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp));
smartlist_free(ports_to_forward);
}
- time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
+ time_to.check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
}
/* 11b. check pending unconfigured managed proxies */
@@ -1659,10 +1681,10 @@ run_scheduled_events(time_t now)
/* 12. write the heartbeat message */
if (options->HeartbeatPeriod &&
- time_to_next_heartbeat <= now) {
- if (time_to_next_heartbeat) /* don't log the first heartbeat */
+ time_to.next_heartbeat <= now) {
+ if (time_to.next_heartbeat) /* don't log the first heartbeat */
log_heartbeat(now);
- time_to_next_heartbeat = now+options->HeartbeatPeriod;
+ time_to.next_heartbeat = now+options->HeartbeatPeriod;
}
}
@@ -1762,8 +1784,6 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN ||
seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
circuit_note_clock_jumped(seconds_elapsed);
- /* XXX if the time jumps *back* many months, do our events in
- * run_scheduled_events() recover? I don't think they do. -RD */
} else if (seconds_elapsed > 0)
stats_n_seconds_working += seconds_elapsed;
@@ -1887,7 +1907,7 @@ dns_servers_relaunch_checks(void)
{
if (server_mode(get_options())) {
dns_reset_correctness_checks();
- time_to_check_for_correct_dns = 0;
+ time_to.check_for_correct_dns = 0;
}
}
@@ -1971,7 +1991,6 @@ do_hup(void)
int
do_main_loop(void)
{
- int loop_result;
time_t now;
/* initialize dns resolve map, spawn workers if needed */
@@ -2117,51 +2136,78 @@ do_main_loop(void)
}
#endif
- for (;;) {
- if (nt_service_is_stopping())
- return 0;
+ return run_main_loop_until_done();
+}
+
+/**
+ * Run the main loop a single time. Return 0 for "exit"; -1 for "exit with
+ * error", and 1 for "run this again."
+ */
+static int
+run_main_loop_once(void)
+{
+ int loop_result;
+
+ if (nt_service_is_stopping())
+ return 0;
#ifndef _WIN32
- /* Make it easier to tell whether libevent failure is our fault or not. */
- errno = 0;
+ /* Make it easier to tell whether libevent failure is our fault or not. */
+ errno = 0;
#endif
- /* All active linked conns should get their read events activated. */
- SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn,
- event_active(conn->read_event, EV_READ, 1));
- called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0;
-
- update_approx_time(time(NULL));
-
- /* poll until we have an event, or the second ends, or until we have
- * some active linked connections to trigger events for. */
- loop_result = event_base_loop(tor_libevent_get_base(),
- called_loop_once ? EVLOOP_ONCE : 0);
-
- /* let catch() handle things like ^c, and otherwise don't worry about it */
- if (loop_result < 0) {
- int e = tor_socket_errno(-1);
- /* let the program survive things like ^z */
- if (e != EINTR && !ERRNO_IS_EINPROGRESS(e)) {
- log_err(LD_NET,"libevent call with %s failed: %s [%d]",
- tor_libevent_get_method(), tor_socket_strerror(e), e);
- return -1;
+ /* All active linked conns should get their read events activated. */
+ SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn,
+ event_active(conn->read_event, EV_READ, 1));
+ called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0;
+
+ update_approx_time(time(NULL));
+
+ /* poll until we have an event, or the second ends, or until we have
+ * some active linked connections to trigger events for. */
+ loop_result = event_base_loop(tor_libevent_get_base(),
+ called_loop_once ? EVLOOP_ONCE : 0);
+
+ /* let catch() handle things like ^c, and otherwise don't worry about it */
+ if (loop_result < 0) {
+ int e = tor_socket_errno(-1);
+ /* let the program survive things like ^z */
+ if (e != EINTR && !ERRNO_IS_EINPROGRESS(e)) {
+ log_err(LD_NET,"libevent call with %s failed: %s [%d]",
+ tor_libevent_get_method(), tor_socket_strerror(e), e);
+ return -1;
#ifndef _WIN32
- } else if (e == EINVAL) {
- log_warn(LD_NET, "EINVAL from libevent: should you upgrade libevent?");
- if (got_libevent_error())
- return -1;
+ } else if (e == EINVAL) {
+ log_warn(LD_NET, "EINVAL from libevent: should you upgrade libevent?");
+ if (got_libevent_error())
+ return -1;
#endif
- } else {
- if (ERRNO_IS_EINPROGRESS(e))
- log_warn(LD_BUG,
- "libevent call returned EINPROGRESS? Please report.");
- log_debug(LD_NET,"libevent call interrupted.");
- /* You can't trust the results of this poll(). Go back to the
- * top of the big for loop. */
- continue;
- }
+ } else {
+ if (ERRNO_IS_EINPROGRESS(e))
+ log_warn(LD_BUG,
+ "libevent call returned EINPROGRESS? Please report.");
+ log_debug(LD_NET,"libevent call interrupted.");
+ /* You can't trust the results of this poll(). Go back to the
+ * top of the big for loop. */
+ return 1;
}
}
+
+ return 1;
+}
+
+/** Run the run_main_loop_once() function until it declares itself done,
+ * and return its final return value.
+ *
+ * Shadow won't invoke this function, so don't fill it up with things.
+ */
+static int
+run_main_loop_until_done(void)
+{
+ int loop_result = 1;
+ do {
+ loop_result = run_main_loop_once();
+ } while (loop_result == 1);
+ return loop_result;
}
#ifndef _WIN32 /* Only called when we're willing to use signals */
@@ -2321,12 +2367,13 @@ dumpstats(int severity)
if (conn->type == CONN_TYPE_OR) {
or_connection_t *or_conn = TO_OR_CONN(conn);
if (or_conn->tls) {
- tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len,
- &wbuf_cap, &wbuf_len);
- tor_log(severity, LD_GENERAL,
- "Conn %d: %d/%d bytes used on OpenSSL read buffer; "
- "%d/%d bytes used on write buffer.",
- i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap);
+ if (tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len,
+ &wbuf_cap, &wbuf_len) == 0) {
+ tor_log(severity, LD_GENERAL,
+ "Conn %d: %d/%d bytes used on OpenSSL read buffer; "
+ "%d/%d bytes used on write buffer.",
+ i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap);
+ }
}
}
}
@@ -2490,10 +2537,11 @@ tor_init(int argc, char *argv[])
if (!strcmp(cl->key, "--quiet") ||
!strcmp(cl->key, "--dump-config"))
quiet = 2;
- /* --version, --digests, and --help imply --hush */
+ /* The following options imply --hush */
if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") ||
!strcmp(cl->key, "--list-torrc-options") ||
!strcmp(cl->key, "--library-versions") ||
+ !strcmp(cl->key, "--hash-password") ||
!strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) {
if (quiet < 1)
quiet = 1;
@@ -2811,6 +2859,7 @@ do_dump_config(void)
const char *arg = options->command_arg;
int how;
char *opts;
+
if (!strcmp(arg, "short")) {
how = OPTIONS_DUMP_MINIMAL;
} else if (!strcmp(arg, "non-builtin")) {
@@ -2818,8 +2867,9 @@ do_dump_config(void)
} else if (!strcmp(arg, "full")) {
how = OPTIONS_DUMP_ALL;
} else {
- printf("%s is not a recognized argument to --dump-config. "
- "Please select 'short', 'non-builtin', or 'full'", arg);
+ fprintf(stderr, "No valid argument to --dump-config found!\n");
+ fprintf(stderr, "Please select 'short', 'non-builtin', or 'full'.\n");
+
return -1;
}
@@ -3120,7 +3170,8 @@ tor_main(int argc, char *argv[])
result = 0;
break;
case CMD_VERIFY_CONFIG:
- printf("Configuration was valid\n");
+ if (quiet_level == 0)
+ printf("Configuration was valid\n");
result = 0;
break;
case CMD_DUMP_CONFIG:
diff --git a/src/or/main.h b/src/or/main.h
index f77b4711c5..542eab6565 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -52,6 +52,7 @@ void directory_info_has_arrived(time_t now, int from_cache);
void ip_address_changed(int at_interface);
void dns_servers_relaunch_checks(void);
+void reset_all_main_loop_timers(void);
void reschedule_descriptor_update_check(void);
MOCK_DECL(long,get_uptime,(void));
diff --git a/src/or/or.h b/src/or/or.h
index 6b3ce77713..81e1c1c1db 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -88,7 +88,7 @@
#include "crypto.h"
#include "tortls.h"
-#include "../common/torlog.h"
+#include "torlog.h"
#include "container.h"
#include "torgzip.h"
#include "address.h"
@@ -794,17 +794,34 @@ typedef struct rend_data_t {
/** Onion address (without the .onion part) that a client requests. */
char onion_address[REND_SERVICE_ID_LEN_BASE32+1];
+ /** Descriptor ID for each replicas computed from the onion address. If
+ * the onion address is empty, this array MUST be empty. We keep them so
+ * we know when to purge our entry in the last hsdir request table. */
+ char descriptor_id[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS][DIGEST_LEN];
+
/** (Optional) descriptor cookie that is used by a client. */
char descriptor_cookie[REND_DESC_COOKIE_LEN];
/** Authorization type for accessing a service used by a client. */
rend_auth_type_t auth_type;
+ /** Descriptor ID for a client request. The control port command HSFETCH
+ * uses this. It's set if the descriptor query should only use this
+ * descriptor ID. */
+ char desc_id_fetch[DIGEST_LEN];
+
/** Hash of the hidden service's PK used by a service. */
char rend_pk_digest[DIGEST_LEN];
/** Rendezvous cookie used by both, client and service. */
char rend_cookie[REND_COOKIE_LEN];
+
+ /** List of HSDir fingerprints on which this request has been sent to.
+ * This contains binary identity digest of the directory. */
+ smartlist_t *hsdirs_fp;
+
+ /** Number of streams associated with this rendezvous circuit. */
+ int nr_streams;
} rend_data_t;
/** Time interval for tracking replays of DH public keys received in
@@ -1734,6 +1751,9 @@ typedef struct control_connection_t {
* connection. */
unsigned int is_owning_control_connection:1;
+ /** List of ephemeral onion services belonging to this connection. */
+ smartlist_t *ephemeral_onion_services;
+
/** If we have sent an AUTHCHALLENGE reply on this connection and
* have not received a successful AUTHENTICATE command, points to
* the value which the client must send to authenticate itself;
@@ -2145,9 +2165,6 @@ typedef struct routerstatus_t {
* if the number of traits we care about ever becomes incredibly big. */
unsigned int version_known:1;
- /** True iff this router is a version that, if it caches directory info,
- * we can get microdescriptors from. */
- unsigned int version_supports_microdesc_cache:1;
/** True iff this router has a version that allows it to accept EXTEND2
* cells */
unsigned int version_supports_extend2_cells:1;
@@ -3201,6 +3218,9 @@ typedef struct or_circuit_t {
* to the specification? */
unsigned int remaining_relay_early_cells : 4;
+ /* We have already received an INTRODUCE1 cell on this circuit. */
+ unsigned int already_received_introduce1 : 1;
+
/** True iff this circuit was made with a CREATE_FAST cell. */
unsigned int is_first_hop : 1;
@@ -3404,8 +3424,6 @@ typedef struct {
char *Address; /**< OR only: configured address for this onion router. */
char *PidFile; /**< Where to store PID of Tor process. */
- int DynamicDHGroups; /**< Dynamic generation of prime moduli for use in DH.*/
-
routerset_t *ExitNodes; /**< Structure containing nicknames, digests,
* country codes and IP address patterns of ORs to
* consider as exits. */
@@ -4842,12 +4860,13 @@ typedef struct rend_encoded_v2_service_descriptor_t {
* introduction point. See also rend_intro_point_t.unreachable_count. */
#define MAX_INTRO_POINT_REACHABILITY_FAILURES 5
-/** The maximum number of distinct INTRODUCE2 cells which a hidden
- * service's introduction point will receive before it begins to
- * expire.
- *
- * XXX023 Is this number at all sane? */
-#define INTRO_POINT_LIFETIME_INTRODUCTIONS 16384
+/** The minimum and maximum number of distinct INTRODUCE2 cells which a
+ * hidden service's introduction point will receive before it begins to
+ * expire. */
+#define INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS 16384
+/* Double the minimum value so the interval is [min, min * 2]. */
+#define INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS \
+ (INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS * 2)
/** The minimum number of seconds that an introduction point will last
* before expiring due to old age. (If it receives
@@ -4901,6 +4920,12 @@ typedef struct rend_intro_point_t {
*/
int accepted_introduce2_count;
+ /** (Service side only) Number of maximum INTRODUCE2 cells that this IP
+ * will accept. This is a random value between
+ * INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS and
+ * INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS. */
+ int max_introductions;
+
/** (Service side only) The time at which this intro point was first
* published, or -1 if this intro point has not yet been
* published. */
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 162e0ac53e..59e938e89c 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -141,7 +141,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
int r, v3_shift = 0;
char payload[RELAY_PAYLOAD_SIZE];
char tmp[RELAY_PAYLOAD_SIZE];
- rend_cache_entry_t *entry;
+ rend_cache_entry_t *entry = NULL;
crypt_path_t *cpath;
off_t dh_offset;
crypto_pk_t *intro_key = NULL;
@@ -158,8 +158,13 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
tor_assert(!(rendcirc->build_state->onehop_tunnel));
#endif
- if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
- &entry) < 1) {
+ r = rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
+ &entry);
+ /* An invalid onion address is not possible else we have a big issue. */
+ tor_assert(r != -EINVAL);
+ if (r < 0 || !rend_client_any_intro_points_usable(entry)) {
+ /* If the descriptor is not found or the intro points are not usable
+ * anymore, trigger a fetch. */
log_info(LD_REND,
"query %s didn't have valid rend desc in cache. "
"Refetching descriptor.",
@@ -469,9 +474,8 @@ rend_client_introduction_acked(origin_circuit_t *circ,
/** Contains the last request times to hidden service directories for
* certain queries; each key is a string consisting of the
- * concatenation of a base32-encoded HS directory identity digest, a
- * base32-encoded HS descriptor ID, and a hidden service address
- * (without the ".onion" part); each value is a pointer to a time_t
+ * concatenation of a base32-encoded HS directory identity digest and
+ * base32-encoded HS descriptor ID; each value is a pointer to a time_t
* holding the time of the last request for that descriptor ID to that
* HS directory. */
static strmap_t *last_hid_serv_requests_ = NULL;
@@ -487,19 +491,16 @@ get_last_hid_serv_requests(void)
}
#define LAST_HID_SERV_REQUEST_KEY_LEN (REND_DESC_ID_V2_LEN_BASE32 + \
- REND_DESC_ID_V2_LEN_BASE32 + \
- REND_SERVICE_ID_LEN_BASE32)
+ REND_DESC_ID_V2_LEN_BASE32)
/** Look up the last request time to hidden service directory <b>hs_dir</b>
- * for descriptor ID <b>desc_id_base32</b> for the service specified in
- * <b>rend_query</b>. If <b>set</b> is non-zero,
- * assign the current time <b>now</b> and return that. Otherwise, return
- * the most recent request time, or 0 if no such request has been sent
- * before. */
+ * for descriptor ID <b>desc_id_base32</b>. If <b>set</b> is non-zero,
+ * assign the current time <b>now</b> and return that. Otherwise, return the
+ * most recent request time, or 0 if no such request has been sent before.
+ */
static time_t
lookup_last_hid_serv_request(routerstatus_t *hs_dir,
const char *desc_id_base32,
- const rend_data_t *rend_query,
time_t now, int set)
{
char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
@@ -508,10 +509,9 @@ lookup_last_hid_serv_request(routerstatus_t *hs_dir,
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32),
hs_dir->identity_digest, DIGEST_LEN);
- tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s%s",
+ tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s",
hsdir_id_base32,
- desc_id_base32,
- rend_query->onion_address);
+ desc_id_base32);
/* XXX023 tor_assert(strlen(hsdir_desc_comb_id) ==
LAST_HID_SERV_REQUEST_KEY_LEN); */
if (set) {
@@ -552,20 +552,23 @@ directory_clean_last_hid_serv_requests(time_t now)
}
}
-/** Remove all requests related to the hidden service named
- * <b>onion_address</b> from the history of times of requests to
- * hidden service directories.
+/** Remove all requests related to the descriptor ID <b>desc_id</b> from the
+ * history of times of requests to hidden service directories.
+ * <b>desc_id</b> is an unencoded descriptor ID of size DIGEST_LEN.
*
* This is called from rend_client_note_connection_attempt_ended(), which
- * must be idempotent, so any future changes to this function must leave
- * it idempotent too.
- */
+ * must be idempotent, so any future changes to this function must leave it
+ * idempotent too. */
static void
-purge_hid_serv_from_last_hid_serv_requests(const char *onion_address)
+purge_hid_serv_from_last_hid_serv_requests(const char *desc_id)
{
strmap_iter_t *iter;
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
- /* XXX023 tor_assert(strlen(onion_address) == REND_SERVICE_ID_LEN_BASE32); */
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+
+ /* Key is stored with the base32 encoded desc_id. */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
for (iter = strmap_iter_init(last_hid_serv_requests);
!strmap_iter_done(iter); ) {
const char *key;
@@ -573,9 +576,9 @@ purge_hid_serv_from_last_hid_serv_requests(const char *onion_address)
strmap_iter_get(iter, &key, &val);
/* XXX023 tor_assert(strlen(key) == LAST_HID_SERV_REQUEST_KEY_LEN); */
if (tor_memeq(key + LAST_HID_SERV_REQUEST_KEY_LEN -
- REND_SERVICE_ID_LEN_BASE32,
- onion_address,
- REND_SERVICE_ID_LEN_BASE32)) {
+ REND_DESC_ID_V2_LEN_BASE32,
+ desc_id_base32,
+ REND_DESC_ID_V2_LEN_BASE32)) {
iter = strmap_iter_next_rmv(last_hid_serv_requests, iter);
tor_free(val);
} else {
@@ -604,64 +607,53 @@ rend_client_purge_last_hid_serv_requests(void)
}
}
-/** Determine the responsible hidden service directories for <b>desc_id</b>
- * and fetch the descriptor with that ID from one of them. Only
- * send a request to a hidden service directory that we have not yet tried
- * during this attempt to connect to this hidden service; on success, return 1,
- * in the case that no hidden service directory is left to ask for the
- * descriptor, return 0, and in case of a failure -1. */
-static int
-directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
+/** This returns a good valid hs dir that should be used for the given
+ * descriptor id.
+ *
+ * Return NULL on error else the hsdir node pointer. */
+static routerstatus_t *
+pick_hsdir(const char *desc_id, const char *desc_id_base32)
{
smartlist_t *responsible_dirs = smartlist_new();
smartlist_t *usable_responsible_dirs = smartlist_new();
const or_options_t *options = get_options();
routerstatus_t *hs_dir;
- char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
time_t now = time(NULL);
- char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
-#ifdef ENABLE_TOR2WEB_MODE
- const int tor2web_mode = options->Tor2webMode;
- const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS;
-#else
- const int how_to_fetch = DIRIND_ANONYMOUS;
-#endif
int excluded_some;
- tor_assert(desc_id);
- tor_assert(rend_query);
- /* Determine responsible dirs. Even if we can't get all we want,
- * work with the ones we have. If it's empty, we'll notice below. */
- hid_serv_get_responsible_directories(responsible_dirs, desc_id);
- base32_encode(desc_id_base32, sizeof(desc_id_base32),
- desc_id, DIGEST_LEN);
+ tor_assert(desc_id);
+ tor_assert(desc_id_base32);
- /* Only select those hidden service directories to which we did not send
- * a request recently and for which we have a router descriptor here. */
+ /* Determine responsible dirs. Even if we can't get all we want, work with
+ * the ones we have. If it's empty, we'll notice below. */
+ hid_serv_get_responsible_directories(responsible_dirs, desc_id);
/* Clean request history first. */
directory_clean_last_hid_serv_requests(now);
- SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, {
- time_t last = lookup_last_hid_serv_request(
- dir, desc_id_base32, rend_query, 0, 0);
- const node_t *node = node_get_by_id(dir->identity_digest);
- if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
- !node || !node_has_descriptor(node)) {
- SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
- continue;
- }
- if (! routerset_contains_node(options->ExcludeNodes, node)) {
- smartlist_add(usable_responsible_dirs, dir);
- }
- });
+ /* Only select those hidden service directories to which we did not send a
+ * request recently and for which we have a router descriptor here. */
+ SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) {
+ time_t last = lookup_last_hid_serv_request(dir, desc_id_base32,
+ 0, 0);
+ const node_t *node = node_get_by_id(dir->identity_digest);
+ if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
+ !node || !node_has_descriptor(node)) {
+ SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
+ continue;
+ }
+ if (!routerset_contains_node(options->ExcludeNodes, node)) {
+ smartlist_add(usable_responsible_dirs, dir);
+ }
+ } SMARTLIST_FOREACH_END(dir);
excluded_some =
smartlist_len(usable_responsible_dirs) < smartlist_len(responsible_dirs);
hs_dir = smartlist_choose(usable_responsible_dirs);
- if (! hs_dir && ! options->StrictNodes)
+ if (!hs_dir && !options->StrictNodes) {
hs_dir = smartlist_choose(responsible_dirs);
+ }
smartlist_free(responsible_dirs);
smartlist_free(usable_responsible_dirs);
@@ -674,23 +666,69 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
"requested hidden service: they are all either down or "
"excluded, and StrictNodes is set.");
}
- return 0;
+ } else {
+ /* Remember that we are requesting a descriptor from this hidden service
+ * directory now. */
+ lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1);
+ }
+
+ return hs_dir;
+}
+
+/** Determine the responsible hidden service directories for <b>desc_id</b>
+ * and fetch the descriptor with that ID from one of them. Only
+ * send a request to a hidden service directory that we have not yet tried
+ * during this attempt to connect to this hidden service; on success, return 1,
+ * in the case that no hidden service directory is left to ask for the
+ * descriptor, return 0, and in case of a failure -1. */
+static int
+directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query,
+ routerstatus_t *rs_hsdir)
+{
+ routerstatus_t *hs_dir = rs_hsdir;
+ char *hsdir_fp;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
+#ifdef ENABLE_TOR2WEB_MODE
+ const int tor2web_mode = get_options()->Tor2webMode;
+ const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS;
+#else
+ const int how_to_fetch = DIRIND_ANONYMOUS;
+#endif
+
+ tor_assert(desc_id);
+
+ base32_encode(desc_id_base32, sizeof(desc_id_base32),
+ desc_id, DIGEST_LEN);
+
+ /* Automatically pick an hs dir if none given. */
+ if (!rs_hsdir) {
+ hs_dir = pick_hsdir(desc_id, desc_id_base32);
+ if (!hs_dir) {
+ /* No suitable hs dir can be found, stop right now. */
+ return 0;
+ }
}
- /* Remember that we are requesting a descriptor from this hidden service
- * directory now. */
- lookup_last_hid_serv_request(hs_dir, desc_id_base32, rend_query, now, 1);
+ /* Add a copy of the HSDir identity digest to the query so we can track it
+ * on the control port. */
+ hsdir_fp = tor_memdup(hs_dir->identity_digest,
+ sizeof(hs_dir->identity_digest));
+ smartlist_add(rend_query->hsdirs_fp, hsdir_fp);
- /* Encode descriptor cookie for logging purposes. */
+ /* Encode descriptor cookie for logging purposes. Also, if the cookie is
+ * malformed, no fetch is triggered thus this needs to be done before the
+ * fetch request. */
if (rend_query->auth_type != REND_NO_AUTH) {
if (base64_encode(descriptor_cookie_base64,
sizeof(descriptor_cookie_base64),
- rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN)<0) {
+ rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN,
+ 0)<0) {
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
return 0;
}
- /* Remove == signs and newline. */
- descriptor_cookie_base64[strlen(descriptor_cookie_base64)-3] = '\0';
+ /* Remove == signs. */
+ descriptor_cookie_base64[strlen(descriptor_cookie_base64)-2] = '\0';
} else {
strlcpy(descriptor_cookie_base64, "(none)",
sizeof(descriptor_cookie_base64));
@@ -721,16 +759,144 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
return 1;
}
+/** Fetch a v2 descriptor using the given descriptor id. If any hsdir(s) are
+ * given, they will be used instead.
+ *
+ * On success, 1 is returned. If no hidden service is left to ask, return 0.
+ * On error, -1 is returned. */
+static int
+fetch_v2_desc_by_descid(const char *desc_id, const rend_data_t *rend_query,
+ smartlist_t *hsdirs)
+{
+ int ret;
+
+ tor_assert(rend_query);
+
+ if (!hsdirs) {
+ ret = directory_get_from_hs_dir(desc_id, rend_query, NULL);
+ goto end; /* either success or failure, but we're done */
+ }
+
+ /* Using the given hsdir list, trigger a fetch on each of them. */
+ SMARTLIST_FOREACH_BEGIN(hsdirs, routerstatus_t *, hs_dir) {
+ /* This should always be a success. */
+ ret = directory_get_from_hs_dir(desc_id, rend_query, hs_dir);
+ tor_assert(ret);
+ } SMARTLIST_FOREACH_END(hs_dir);
+
+ /* Everything went well. */
+ ret = 0;
+
+ end:
+ return ret;
+}
+
+/** Fetch a v2 descriptor using the onion address in the given query object.
+ * This will compute the descriptor id for each replicas and fetch it on the
+ * given hsdir(s) if any or the responsible ones that are choosen
+ * automatically.
+ *
+ * On success, 1 is returned. If no hidden service is left to ask, return 0.
+ * On error, -1 is returned. */
+static int
+fetch_v2_desc_by_addr(rend_data_t *query, smartlist_t *hsdirs)
+{
+ char descriptor_id[DIGEST_LEN];
+ int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
+ int i, tries_left, ret;
+
+ tor_assert(query);
+
+ /* Randomly iterate over the replicas until a descriptor can be fetched
+ * from one of the consecutive nodes, or no options are left. */
+ for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) {
+ replicas_left_to_try[i] = i;
+ }
+
+ tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
+ while (tries_left > 0) {
+ int rand = crypto_rand_int(tries_left);
+ int chosen_replica = replicas_left_to_try[rand];
+ replicas_left_to_try[rand] = replicas_left_to_try[--tries_left];
+
+ ret = rend_compute_v2_desc_id(descriptor_id, query->onion_address,
+ query->auth_type == REND_STEALTH_AUTH ?
+ query->descriptor_cookie : NULL,
+ time(NULL), chosen_replica);
+ if (ret < 0) {
+ /* Normally, on failure the descriptor_id is untouched but let's be
+ * safe in general in case the function changes at some point. */
+ goto end;
+ }
+
+ if (tor_memcmp(descriptor_id, query->descriptor_id[chosen_replica],
+ sizeof(descriptor_id)) != 0) {
+ /* Not equal from what we currently have so purge the last hid serv
+ * request cache and update the descriptor ID with the new value. */
+ purge_hid_serv_from_last_hid_serv_requests(
+ query->descriptor_id[chosen_replica]);
+ memcpy(query->descriptor_id[chosen_replica], descriptor_id,
+ sizeof(query->descriptor_id[chosen_replica]));
+ }
+
+ /* Trigger the fetch with the computed descriptor ID. */
+ ret = fetch_v2_desc_by_descid(descriptor_id, query, hsdirs);
+ if (ret != 0) {
+ /* Either on success or failure, as long as we tried a fetch we are
+ * done here. */
+ goto end;
+ }
+ }
+
+ /* If we come here, there are no hidden service directories left. */
+ log_info(LD_REND, "Could not pick one of the responsible hidden "
+ "service directories to fetch descriptors, because "
+ "we already tried them all unsuccessfully.");
+ ret = 0;
+
+ end:
+ memwipe(descriptor_id, 0, sizeof(descriptor_id));
+ return ret;
+}
+
+/** Fetch a v2 descriptor using the given query. If any hsdir are specified,
+ * use them for the fetch.
+ *
+ * On success, 1 is returned. If no hidden service is left to ask, return 0.
+ * On error, -1 is returned. */
+int
+rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs)
+{
+ int ret;
+
+ tor_assert(query);
+
+ /* Depending on what's available in the rend data query object, we will
+ * trigger a fetch by HS address or using a descriptor ID. */
+
+ if (query->onion_address[0] != '\0') {
+ ret = fetch_v2_desc_by_addr(query, hsdirs);
+ } else if (!tor_digest_is_zero(query->desc_id_fetch)) {
+ ret = fetch_v2_desc_by_descid(query->desc_id_fetch, query, hsdirs);
+ } else {
+ /* Query data is invalid. */
+ ret = -1;
+ goto error;
+ }
+
+ error:
+ return ret;
+}
+
/** Unless we already have a descriptor for <b>rend_query</b> with at least
* one (possibly) working introduction point in it, start a connection to a
* hidden service directory to fetch a v2 rendezvous service descriptor. */
void
-rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
+rend_client_refetch_v2_renddesc(rend_data_t *rend_query)
{
- char descriptor_id[DIGEST_LEN];
- int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
- int i, tries_left;
+ int ret;
rend_cache_entry_t *e = NULL;
+
tor_assert(rend_query);
/* Are we configured to fetch descriptors? */
if (!get_options()->FetchHidServDescriptors) {
@@ -739,7 +905,7 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
return;
}
/* Before fetching, check if we already have a usable descriptor here. */
- if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0 &&
+ if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) == 0 &&
rend_client_any_intro_points_usable(e)) {
log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we "
"already have a usable descriptor here. Not fetching.");
@@ -747,44 +913,12 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
}
log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
safe_str_client(rend_query->onion_address));
- /* Randomly iterate over the replicas until a descriptor can be fetched
- * from one of the consecutive nodes, or no options are left. */
- tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
- for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++)
- replicas_left_to_try[i] = i;
- while (tries_left > 0) {
- int rand = crypto_rand_int(tries_left);
- int chosen_replica = replicas_left_to_try[rand];
- replicas_left_to_try[rand] = replicas_left_to_try[--tries_left];
- if (rend_compute_v2_desc_id(descriptor_id, rend_query->onion_address,
- rend_query->auth_type == REND_STEALTH_AUTH ?
- rend_query->descriptor_cookie : NULL,
- time(NULL), chosen_replica) < 0) {
- log_warn(LD_REND, "Internal error: Computing v2 rendezvous "
- "descriptor ID did not succeed.");
- /*
- * Hmm, can this write anything to descriptor_id and still fail?
- * Let's clear it just to be safe.
- *
- * From here on, any returns should goto done which clears
- * descriptor_id so we don't leave key-derived material on the stack.
- */
- goto done;
- }
- if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0)
- goto done; /* either success or failure, but we're done */
+ ret = rend_client_fetch_v2_desc(rend_query, NULL);
+ if (ret <= 0) {
+ /* Close pending connections on error or if no hsdir can be found. */
+ rend_client_desc_trynow(rend_query->onion_address);
}
- /* If we come here, there are no hidden service directories left. */
- log_info(LD_REND, "Could not pick one of the responsible hidden "
- "service directories to fetch descriptors, because "
- "we already tried them all unsuccessfully.");
- /* Close pending connections. */
- rend_client_desc_trynow(rend_query->onion_address);
-
- done:
- memwipe(descriptor_id, 0, sizeof(descriptor_id));
-
return;
}
@@ -845,7 +979,7 @@ rend_client_cancel_descriptor_fetches(void)
*/
int
rend_client_report_intro_point_failure(extend_info_t *failed_intro,
- const rend_data_t *rend_query,
+ rend_data_t *rend_query,
unsigned int failure_type)
{
int i, r;
@@ -853,17 +987,26 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro,
connection_t *conn;
r = rend_cache_lookup_entry(rend_query->onion_address, -1, &ent);
- if (r<0) {
- log_warn(LD_BUG, "Malformed service ID %s.",
- escaped_safe_str_client(rend_query->onion_address));
- return -1;
- }
- if (r==0) {
- log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.",
- escaped_safe_str_client(rend_query->onion_address));
- rend_client_refetch_v2_renddesc(rend_query);
- return 0;
+ if (r < 0) {
+ /* Either invalid onion address or cache entry not found. */
+ switch (-r) {
+ case EINVAL:
+ log_warn(LD_BUG, "Malformed service ID %s.",
+ escaped_safe_str_client(rend_query->onion_address));
+ return -1;
+ case ENOENT:
+ log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.",
+ escaped_safe_str_client(rend_query->onion_address));
+ rend_client_refetch_v2_renddesc(rend_query);
+ return 0;
+ default:
+ log_warn(LD_BUG, "Unknown cache lookup returned code: %d", r);
+ return -1;
+ }
}
+ /* The intro points are not checked here if they are usable or not because
+ * this is called when an intro point circuit is closed thus there must be
+ * at least one intro point that is usable and is about to be flagged. */
for (i = 0; i < smartlist_len(ent->parsed->intro_nodes); i++) {
rend_intro_point_t *intro = smartlist_get(ent->parsed->intro_nodes, i);
@@ -1062,7 +1205,7 @@ rend_client_desc_trynow(const char *query)
continue;
assert_connection_ok(base_conn, now);
if (rend_cache_lookup_entry(rend_data->onion_address, -1,
- &entry) == 1 &&
+ &entry) == 0 &&
rend_client_any_intro_points_usable(entry)) {
/* either this fetch worked, or it failed but there was a
* valid entry from before which we should reuse */
@@ -1086,27 +1229,28 @@ rend_client_desc_trynow(const char *query)
"unavailable (try again later).",
safe_str_client(query));
connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED);
- rend_client_note_connection_attempt_ended(query);
+ rend_client_note_connection_attempt_ended(rend_data);
}
} SMARTLIST_FOREACH_END(base_conn);
}
-/** Clear temporary state used only during an attempt to connect to
- * the hidden service named <b>onion_address</b>. Called when a
- * connection attempt has ended; it is possible for this to be called
- * multiple times while handling an ended connection attempt, and
- * any future changes to this function must ensure it remains
- * idempotent.
- */
+/** Clear temporary state used only during an attempt to connect to the
+ * hidden service with <b>rend_data</b>. Called when a connection attempt
+ * has ended; it is possible for this to be called multiple times while
+ * handling an ended connection attempt, and any future changes to this
+ * function must ensure it remains idempotent. */
void
-rend_client_note_connection_attempt_ended(const char *onion_address)
+rend_client_note_connection_attempt_ended(const rend_data_t *rend_data)
{
+ unsigned int have_onion = 0;
rend_cache_entry_t *cache_entry = NULL;
- rend_cache_lookup_entry(onion_address, -1, &cache_entry);
- log_info(LD_REND, "Connection attempt for %s has ended; "
- "cleaning up temporary state.",
- safe_str_client(onion_address));
+ if (*rend_data->onion_address != '\0') {
+ /* Ignore return value; we find an entry, or we don't. */
+ (void) rend_cache_lookup_entry(rend_data->onion_address, -1,
+ &cache_entry);
+ have_onion = 1;
+ }
/* Clear the timed_out flag on all remaining intro points for this HS. */
if (cache_entry != NULL) {
@@ -1116,7 +1260,20 @@ rend_client_note_connection_attempt_ended(const char *onion_address)
}
/* Remove the HS's entries in last_hid_serv_requests. */
- purge_hid_serv_from_last_hid_serv_requests(onion_address);
+ if (have_onion) {
+ unsigned int replica;
+ for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id);
+ replica++) {
+ const char *desc_id = rend_data->descriptor_id[replica];
+ purge_hid_serv_from_last_hid_serv_requests(desc_id);
+ }
+ log_info(LD_REND, "Connection attempt for %s has ended; "
+ "cleaning up temporary state.",
+ safe_str_client(rend_data->onion_address));
+ } else {
+ /* We only have an ID for a fetch. Probably used by HSFETCH. */
+ purge_hid_serv_from_last_hid_serv_requests(rend_data->desc_id_fetch);
+ }
}
/** Return a newly allocated extend_info_t* for a randomly chosen introduction
@@ -1126,13 +1283,17 @@ rend_client_note_connection_attempt_ended(const char *onion_address)
extend_info_t *
rend_client_get_random_intro(const rend_data_t *rend_query)
{
+ int ret;
extend_info_t *result;
rend_cache_entry_t *entry;
- if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) {
- log_warn(LD_REND,
- "Query '%s' didn't have valid rend desc in cache. Failing.",
- safe_str_client(rend_query->onion_address));
+ ret = rend_cache_lookup_entry(rend_query->onion_address, -1, &entry);
+ if (ret < 0 || !rend_client_any_intro_points_usable(entry)) {
+ log_warn(LD_REND,
+ "Query '%s' didn't have valid rend desc in cache. Failing.",
+ safe_str_client(rend_query->onion_address));
+ /* XXX: Should we refetch the descriptor here if the IPs are not usable
+ * anymore ?. */
return NULL;
}
diff --git a/src/or/rendclient.h b/src/or/rendclient.h
index 098c61d0a1..6118924e1d 100644
--- a/src/or/rendclient.h
+++ b/src/or/rendclient.h
@@ -19,7 +19,8 @@ void rend_client_rendcirc_has_opened(origin_circuit_t *circ);
int rend_client_introduction_acked(origin_circuit_t *circ,
const uint8_t *request,
size_t request_len);
-void rend_client_refetch_v2_renddesc(const rend_data_t *rend_query);
+void rend_client_refetch_v2_renddesc(rend_data_t *rend_query);
+int rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs);
void rend_client_cancel_descriptor_fetches(void);
void rend_client_purge_last_hid_serv_requests(void);
@@ -28,7 +29,7 @@ void rend_client_purge_last_hid_serv_requests(void);
#define INTRO_POINT_FAILURE_UNREACHABLE 2
int rend_client_report_intro_point_failure(extend_info_t *failed_intro,
- const rend_data_t *rend_query,
+ rend_data_t *rend_query,
unsigned int failure_type);
int rend_client_rendezvous_acked(origin_circuit_t *circ,
@@ -39,7 +40,7 @@ int rend_client_receive_rendezvous(origin_circuit_t *circ,
size_t request_len);
void rend_client_desc_trynow(const char *query);
-void rend_client_note_connection_attempt_ended(const char *onion_address);
+void rend_client_note_connection_attempt_ended(const rend_data_t *rend_data);
extend_info_t *rend_client_get_random_intro(const rend_data_t *rend_query);
int rend_client_any_intro_points_usable(const rend_cache_entry_t *entry);
@@ -51,7 +52,6 @@ int rend_parse_service_authorization(const or_options_t *options,
rend_service_authorization_t *rend_client_lookup_service_authorization(
const char *onion_address);
void rend_service_authorization_free_all(void);
-rend_data_t *rend_data_dup(const rend_data_t *request);
#endif
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 866f4fb026..ec4353c409 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -155,10 +155,10 @@ rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
}
/* Calculate current time-period. */
time_period = get_time_period(now, 0, service_id_binary);
- /* Calculate secret-id-part = h(time-period | replica). */
+ /* Calculate secret-id-part = h(time-period | desc-cookie | replica). */
get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie,
replica);
- /* Calculate descriptor ID. */
+ /* Calculate descriptor ID: H(permanent-id | secret-id-part) */
rend_get_descriptor_id_bytes(desc_id_out, service_id_binary, secret_id_part);
return 0;
}
@@ -529,7 +529,8 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
}
/* Base64-encode introduction points. */
ipos_base64 = tor_calloc(ipos_len, 2);
- if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len)<0) {
+ if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len,
+ BASE64_ENCODE_MULTILINE)<0) {
log_warn(LD_REND, "Could not encode introduction point string to "
"base64. length=%d", (int)ipos_len);
tor_free(ipos_base64);
@@ -646,7 +647,6 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
rend_encoded_v2_service_descriptor_free(enc);
goto err;
}
- desc_str[written++] = '\n';
desc_str[written++] = 0;
/* Check if we can parse our own descriptor. */
if (!rend_desc_v2_is_parsable(enc)) {
@@ -920,36 +920,70 @@ rend_valid_service_id(const char *query)
return 1;
}
-/** If we have a cached rend_cache_entry_t for the service ID <b>query</b>
- * with <b>version</b>, set *<b>e</b> to that entry and return 1.
- * Else return 0. If <b>version</b> is nonnegative, only return an entry
- * in that descriptor format version. Otherwise (if <b>version</b> is
- * negative), return the most recent format we have.
- */
+/** Return true iff <b>query</b> is a syntactically valid descriptor ID.
+ * (as generated by rend_get_descriptor_id_bytes). */
+int
+rend_valid_descriptor_id(const char *query)
+{
+ if (strlen(query) != REND_DESC_ID_V2_LEN_BASE32) {
+ goto invalid;
+ }
+ if (strspn(query, BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) {
+ goto invalid;
+ }
+
+ return 1;
+
+ invalid:
+ return 0;
+}
+
+/** Lookup in the client cache the given service ID <b>query</b> for
+ * <b>version</b>.
+ *
+ * Return 0 if found and if <b>e</b> is non NULL, set it with the entry
+ * found. Else, a negative value is returned and <b>e</b> is untouched.
+ * -EINVAL means that <b>query</b> is not a valid service id.
+ * -ENOENT means that no entry in the cache was found. */
int
rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
{
- char key[REND_SERVICE_ID_LEN_BASE32+2]; /* <version><query>\0 */
+ int ret = 0;
+ char key[REND_SERVICE_ID_LEN_BASE32 + 2]; /* <version><query>\0 */
+ rend_cache_entry_t *entry = NULL;
+ static const int default_version = 2;
+
tor_assert(rend_cache);
- if (!rend_valid_service_id(query))
- return -1;
- *e = NULL;
- if (version != 0) {
- tor_snprintf(key, sizeof(key), "2%s", query);
- *e = strmap_get_lc(rend_cache, key);
+ tor_assert(query);
+
+ if (!rend_valid_service_id(query)) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ switch (version) {
+ case 0:
+ log_warn(LD_REND, "Cache lookup of a v0 renddesc is deprecated.");
+ break;
+ case 2:
+ /* Default is version 2. */
+ default:
+ tor_snprintf(key, sizeof(key), "%d%s", default_version, query);
+ entry = strmap_get_lc(rend_cache, key);
+ break;
}
- if (!*e && version != 2) {
- tor_snprintf(key, sizeof(key), "0%s", query);
- *e = strmap_get_lc(rend_cache, key);
+ if (!entry) {
+ ret = -ENOENT;
+ goto end;
}
- if (!*e)
- return 0;
- tor_assert((*e)->parsed && (*e)->parsed->intro_nodes);
- /* XXX023 hack for now, to return "not found" if there are no intro
- * points remaining. See bug 997. */
- if (! rend_client_any_intro_points_usable(*e))
- return 0;
- return 1;
+ tor_assert(entry->parsed && entry->parsed->intro_nodes);
+
+ if (e) {
+ *e = entry;
+ }
+
+ end:
+ return ret;
}
/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
@@ -1121,12 +1155,14 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
* If the descriptor's descriptor ID doesn't match <b>desc_id_base32</b>,
* reject it.
*
- * Return an appropriate rend_cache_store_status_t.
+ * Return an appropriate rend_cache_store_status_t. If entry is not NULL,
+ * set it with the cache entry pointer of the descriptor.
*/
rend_cache_store_status_t
rend_cache_store_v2_desc_as_client(const char *desc,
const char *desc_id_base32,
- const rend_data_t *rend_query)
+ const rend_data_t *rend_query,
+ rend_cache_entry_t **entry)
{
/*XXXX this seems to have a bit of duplicate code with
* rend_cache_store_v2_desc_as_dir(). Fix that. */
@@ -1159,6 +1195,9 @@ rend_cache_store_v2_desc_as_client(const char *desc,
tor_assert(desc);
tor_assert(desc_id_base32);
memset(want_desc_id, 0, sizeof(want_desc_id));
+ if (entry) {
+ *entry = NULL;
+ }
if (base32_decode(want_desc_id, sizeof(want_desc_id),
desc_id_base32, strlen(desc_id_base32)) != 0) {
log_warn(LD_BUG, "Couldn't decode base32 %s for descriptor id.",
@@ -1177,7 +1216,8 @@ rend_cache_store_v2_desc_as_client(const char *desc,
log_warn(LD_REND, "Couldn't compute service ID.");
goto err;
}
- if (strcmp(rend_query->onion_address, service_id)) {
+ if (rend_query->onion_address[0] != '\0' &&
+ strcmp(rend_query->onion_address, service_id)) {
log_warn(LD_REND, "Received service descriptor for service ID %s; "
"expected descriptor for service ID %s.",
service_id, safe_str(rend_query->onion_address));
@@ -1190,7 +1230,7 @@ rend_cache_store_v2_desc_as_client(const char *desc,
}
/* Decode/decrypt introduction points. */
- if (intro_content) {
+ if (intro_content && intro_size > 0) {
int n_intro_points;
if (rend_query->auth_type != REND_NO_AUTH &&
!tor_mem_is_zero(rend_query->descriptor_cookie,
@@ -1224,7 +1264,7 @@ rend_cache_store_v2_desc_as_client(const char *desc,
"service descriptor for %s. This is probably a (misguided) "
"attempt to improve reliability, but it could also be an "
"attempt to do a guard enumeration attack. Rejecting.",
- safe_str_client(rend_query->onion_address));
+ safe_str_client(service_id));
goto err;
}
@@ -1270,9 +1310,15 @@ rend_cache_store_v2_desc_as_client(const char *desc,
rend_cache_increment_allocation(rend_cache_entry_allocation(e));
log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
safe_str_client(service_id), (int)encoded_size);
+ if (entry) {
+ *entry = e;
+ }
return RCS_OKAY;
okay:
+ if (entry) {
+ *entry = e;
+ }
retval = RCS_OKAY;
err:
@@ -1354,7 +1400,116 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
rend_data_t *
rend_data_dup(const rend_data_t *data)
{
+ rend_data_t *data_dup;
tor_assert(data);
- return tor_memdup(data, sizeof(rend_data_t));
+ data_dup = tor_memdup(data, sizeof(rend_data_t));
+ data_dup->hsdirs_fp = smartlist_new();
+ SMARTLIST_FOREACH(data->hsdirs_fp, char *, fp,
+ smartlist_add(data_dup->hsdirs_fp,
+ tor_memdup(fp, DIGEST_LEN)));
+ return data_dup;
+}
+
+/** Compute descriptor ID for each replicas and save them. A valid onion
+ * address must be present in the <b>rend_data</b>.
+ *
+ * Return 0 on success else -1. */
+static int
+compute_desc_id(rend_data_t *rend_data)
+{
+ int ret;
+ unsigned replica;
+ time_t now = time(NULL);
+
+ tor_assert(rend_data);
+
+ /* Compute descriptor ID for each replicas. */
+ for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id);
+ replica++) {
+ ret = rend_compute_v2_desc_id(rend_data->descriptor_id[replica],
+ rend_data->onion_address,
+ rend_data->descriptor_cookie,
+ now, replica);
+ if (ret < 0) {
+ goto end;
+ }
+ }
+
+ end:
+ return ret;
+}
+
+/** Allocate and initialize a rend_data_t object for a service using the
+ * given arguments. Only the <b>onion_address</b> is not optional.
+ *
+ * Return a valid rend_data_t pointer. */
+rend_data_t *
+rend_data_service_create(const char *onion_address, const char *pk_digest,
+ const uint8_t *cookie, rend_auth_type_t auth_type)
+{
+ rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data));
+
+ /* We need at least one else the call is wrong. */
+ tor_assert(onion_address != NULL);
+
+ if (pk_digest) {
+ memcpy(rend_data->rend_pk_digest, pk_digest,
+ sizeof(rend_data->rend_pk_digest));
+ }
+ if (cookie) {
+ memcpy(rend_data->rend_cookie, cookie,
+ sizeof(rend_data->rend_cookie));
+ }
+
+ strlcpy(rend_data->onion_address, onion_address,
+ sizeof(rend_data->onion_address));
+ rend_data->auth_type = auth_type;
+ /* Won't be used but still need to initialize it for rend_data dup and
+ * free. */
+ rend_data->hsdirs_fp = smartlist_new();
+
+ return rend_data;
+}
+
+/** Allocate and initialize a rend_data_t object for a client request using
+ * the given arguments. Either an onion address or a descriptor ID is
+ * needed. Both can be given but only the onion address will be used to make
+ * the descriptor fetch.
+ *
+ * Return a valid rend_data_t pointer or NULL on error meaning the
+ * descriptor IDs couldn't be computed from the given data. */
+rend_data_t *
+rend_data_client_create(const char *onion_address, const char *desc_id,
+ const char *cookie, rend_auth_type_t auth_type)
+{
+ rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data));
+
+ /* We need at least one else the call is wrong. */
+ tor_assert(onion_address != NULL || desc_id != NULL);
+
+ if (cookie) {
+ memcpy(rend_data->descriptor_cookie, cookie,
+ sizeof(rend_data->descriptor_cookie));
+ }
+ if (desc_id) {
+ memcpy(rend_data->desc_id_fetch, desc_id,
+ sizeof(rend_data->desc_id_fetch));
+ }
+ if (onion_address) {
+ strlcpy(rend_data->onion_address, onion_address,
+ sizeof(rend_data->onion_address));
+ if (compute_desc_id(rend_data) < 0) {
+ goto error;
+ }
+ }
+
+ rend_data->auth_type = auth_type;
+ rend_data->hsdirs_fp = smartlist_new();
+
+ return rend_data;
+
+ error:
+ rend_data_free(rend_data);
+ return NULL;
}
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index 8396cc3551..0ed7adc710 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -16,6 +16,12 @@
static INLINE void
rend_data_free(rend_data_t *data)
{
+ if (!data) {
+ return;
+ }
+ /* Cleanup the HSDir identity digest. */
+ SMARTLIST_FOREACH(data->hsdirs_fp, char *, d, tor_free(d));
+ smartlist_free(data->hsdirs_fp);
tor_free(data);
}
@@ -37,6 +43,7 @@ void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove);
void rend_cache_purge(void);
void rend_cache_free_all(void);
int rend_valid_service_id(const char *query);
+int rend_valid_descriptor_id(const char *query);
int rend_cache_lookup_entry(const char *query, int version,
rend_cache_entry_t **entry_out);
int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc);
@@ -50,7 +57,8 @@ typedef enum {
rend_cache_store_status_t rend_cache_store_v2_desc_as_dir(const char *desc);
rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc,
const char *desc_id_base32,
- const rend_data_t *rend_query);
+ const rend_data_t *rend_query,
+ rend_cache_entry_t **entry);
int rend_encode_v2_descriptors(smartlist_t *descs_out,
rend_service_descriptor_t *desc, time_t now,
uint8_t period, rend_auth_type_t auth_type,
@@ -65,5 +73,14 @@ void rend_get_descriptor_id_bytes(char *descriptor_id_out,
const char *secret_id_part);
size_t rend_cache_get_total_allocation(void);
+rend_data_t *rend_data_dup(const rend_data_t *data);
+rend_data_t *rend_data_client_create(const char *onion_address,
+ const char *desc_id,
+ const char *cookie,
+ rend_auth_type_t auth_type);
+rend_data_t *rend_data_service_create(const char *onion_address,
+ const char *pk_digest,
+ const uint8_t *cookie,
+ rend_auth_type_t auth_type);
#endif
diff --git a/src/or/rendmid.c b/src/or/rendmid.c
index 9f6ff86c47..2451acb514 100644
--- a/src/or/rendmid.c
+++ b/src/or/rendmid.c
@@ -149,6 +149,20 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request,
goto err;
}
+ /* We have already done an introduction on this circuit but we just
+ received a request for another one. We block it since this might
+ be an attempt to DoS a hidden service (#15515). */
+ if (circ->already_received_introduce1) {
+ log_fn(LOG_PROTOCOL_WARN, LD_REND,
+ "Blocking multiple introductions on the same circuit. "
+ "Someone might be trying to attack a hidden service through "
+ "this relay.");
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
+ return -1;
+ }
+
+ circ->already_received_introduce1 = 1;
+
/* We could change this to MAX_HEX_NICKNAME_LEN now that 0.0.9.x is
* obsolete; however, there isn't much reason to do so, and we're going
* to revise this protocol anyway.
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 6c934c8c12..0329d70924 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -15,6 +15,7 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
+#include "control.h"
#include "directory.h"
#include "main.h"
#include "networkstatus.h"
@@ -42,9 +43,15 @@ static int intro_point_accepted_intro_count(rend_intro_point_t *intro);
static int intro_point_should_expire_now(rend_intro_point_t *intro,
time_t now);
struct rend_service_t;
+static int rend_service_derive_key_digests(struct rend_service_t *s);
static int rend_service_load_keys(struct rend_service_t *s);
static int rend_service_load_auth_keys(struct rend_service_t *s,
const char *hfname);
+static struct rend_service_t *rend_service_get_by_pk_digest(
+ const char* digest);
+static struct rend_service_t *rend_service_get_by_service_id(const char *id);
+static const char *rend_service_escaped_dir(
+ const struct rend_service_t *s);
static ssize_t rend_service_parse_intro_for_v0_or_v1(
rend_intro_cell_t *intro,
@@ -65,7 +72,7 @@ static ssize_t rend_service_parse_intro_for_v3(
/** 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 {
+struct rend_service_port_config_s {
/* The incoming HS virtual port we're mapping */
uint16_t virtual_port;
/* Is this an AF_UNIX port? */
@@ -76,7 +83,7 @@ typedef struct rend_service_port_config_t {
tor_addr_t real_addr;
/* The socket path to connect to, if is_unix_addr */
char unix_addr[FLEXIBLE_ARRAY_MEMBER];
-} rend_service_port_config_t;
+};
/** Try to maintain this many intro points per service by default. */
#define NUM_INTRO_POINTS_DEFAULT 3
@@ -90,7 +97,7 @@ typedef struct rend_service_port_config_t {
#define MAX_INTRO_CIRCS_PER_PERIOD 10
/** How many times will a hidden service operator attempt to connect to
* a requested rendezvous point before giving up? */
-#define MAX_REND_FAILURES 8
+#define MAX_REND_FAILURES 1
/** How many seconds should we spend trying to connect to a requested
* rendezvous point before giving up? */
#define MAX_REND_TIMEOUT 30
@@ -102,7 +109,8 @@ typedef struct rend_service_port_config_t {
/** Represents a single hidden service running at this OP. */
typedef struct rend_service_t {
/* Fields specified in config file */
- char *directory; /**< where in the filesystem it stores it */
+ char *directory; /**< where in the filesystem it stores it. Will be NULL if
+ * this service is ephemeral. */
int dir_group_readable; /**< if 1, allow group read
permissions on directory */
smartlist_t *ports; /**< List of rend_service_port_config_t */
@@ -139,8 +147,23 @@ typedef struct rend_service_t {
/** If true, we don't close circuits for making requests to unsupported
* ports. */
int allow_unknown_ports;
+ /** The maximum number of simultanious streams-per-circuit that are allowed
+ * to be established, or 0 if no limit is set.
+ */
+ int max_streams_per_circuit;
+ /** If true, we close circuits that exceed the max_streams_per_circuit
+ * limit. */
+ int max_streams_close_circuit;
} rend_service_t;
+/** Returns a escaped string representation of the service, <b>s</b>.
+ */
+static const char *
+rend_service_escaped_dir(const struct rend_service_t *s)
+{
+ return (s->directory) ? escaped(s->directory) : "[EPHEMERAL]";
+}
+
/** A list of rend_service_t's for services run on this OP.
*/
static smartlist_t *rend_service_list = NULL;
@@ -173,7 +196,7 @@ rend_authorized_client_free(rend_authorized_client_t *client)
return;
if (client->client_key)
crypto_pk_free(client->client_key);
- tor_strclear(client->client_name);
+ memwipe(client->client_name, 0, strlen(client->client_name));
tor_free(client->client_name);
memwipe(client->descriptor_cookie, 0, sizeof(client->descriptor_cookie));
tor_free(client);
@@ -195,7 +218,8 @@ rend_service_free(rend_service_t *service)
return;
tor_free(service->directory);
- SMARTLIST_FOREACH(service->ports, void*, p, tor_free(p));
+ SMARTLIST_FOREACH(service->ports, rend_service_port_config_t*, p,
+ rend_service_port_config_free(p));
smartlist_free(service->ports);
if (service->private_key)
crypto_pk_free(service->private_key);
@@ -232,8 +256,9 @@ rend_service_free_all(void)
}
/** Validate <b>service</b> and add it to rend_service_list if possible.
+ * Return 0 on success and -1 on failure.
*/
-static void
+static int
rend_add_service(rend_service_t *service)
{
int i;
@@ -241,20 +266,38 @@ rend_add_service(rend_service_t *service)
service->intro_nodes = smartlist_new();
+ if (service->max_streams_per_circuit < 0) {
+ log_warn(LD_CONFIG, "Hidden service (%s) configured with negative max "
+ "streams per circuit; ignoring.",
+ rend_service_escaped_dir(service));
+ rend_service_free(service);
+ return -1;
+ }
+
+ if (service->max_streams_close_circuit < 0 ||
+ service->max_streams_close_circuit > 1) {
+ log_warn(LD_CONFIG, "Hidden service (%s) configured with invalid "
+ "max streams handling; ignoring.",
+ rend_service_escaped_dir(service));
+ rend_service_free(service);
+ return -1;
+ }
+
if (service->auth_type != REND_NO_AUTH &&
smartlist_len(service->clients) == 0) {
log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no "
"clients; ignoring.",
- escaped(service->directory));
+ rend_service_escaped_dir(service));
rend_service_free(service);
- return;
+ return -1;
}
if (!smartlist_len(service->ports)) {
log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured; "
"ignoring.",
- escaped(service->directory));
+ rend_service_escaped_dir(service));
rend_service_free(service);
+ return -1;
} else {
int dupe = 0;
/* XXX This duplicate check has two problems:
@@ -272,14 +315,17 @@ rend_add_service(rend_service_t *service)
* lock file. But this is enough to detect a simple mistake that
* at least one person has actually made.
*/
- SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
- dupe = dupe ||
- !strcmp(ptr->directory, service->directory));
- if (dupe) {
- log_warn(LD_REND, "Another hidden service is already configured for "
- "directory %s, ignoring.", service->directory);
- rend_service_free(service);
- return;
+ if (service->directory != NULL) { /* Skip dupe for ephemeral services. */
+ SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
+ dupe = dupe ||
+ !strcmp(ptr->directory, service->directory));
+ if (dupe) {
+ log_warn(LD_REND, "Another hidden service is already configured for "
+ "directory %s, ignoring.",
+ rend_service_escaped_dir(service));
+ rend_service_free(service);
+ return -1;
+ }
}
smartlist_add(rend_service_list, service);
log_debug(LD_REND,"Configuring service with directory \"%s\"",
@@ -305,7 +351,9 @@ rend_add_service(rend_service_t *service)
#endif /* defined(HAVE_SYS_UN_H) */
}
}
+ return 0;
}
+ /* NOTREACHED */
}
/** Return a new rend_service_port_config_t with its path set to
@@ -324,15 +372,17 @@ rend_service_port_config_new(const char *socket_path)
return conf;
}
-/** Parses a real-port to virtual-port mapping and returns a new
- * rend_service_port_config_t.
+/** Parses a real-port to virtual-port mapping separated by the provided
+ * separator and returns a new rend_service_port_config_t, or NULL and an
+ * optional error string on failure.
*
- * The format is: VirtualPort (IP|RealPort|IP:RealPort|'socket':path)?
+ * The format is: VirtualPort SEP (IP|RealPort|IP:RealPort|'socket':path)?
*
* IP defaults to 127.0.0.1; RealPort defaults to VirtualPort.
*/
-static rend_service_port_config_t *
-parse_port_config(const char *string)
+rend_service_port_config_t *
+rend_service_parse_port_config(const char *string, const char *sep,
+ char **err_msg_out)
{
smartlist_t *sl;
int virtport;
@@ -343,19 +393,24 @@ parse_port_config(const char *string)
rend_service_port_config_t *result = NULL;
unsigned int is_unix_addr = 0;
char *socket_path = NULL;
+ char *err_msg = NULL;
sl = smartlist_new();
- smartlist_split_string(sl, string, " ",
+ smartlist_split_string(sl, string, sep,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) {
- log_warn(LD_CONFIG, "Bad syntax in hidden service port configuration.");
+ if (err_msg_out)
+ err_msg = tor_strdup("Bad syntax in hidden service port configuration.");
+
goto err;
}
virtport = (int)tor_parse_long(smartlist_get(sl,0), 10, 1, 65535, NULL,NULL);
if (!virtport) {
- log_warn(LD_CONFIG, "Missing or invalid port %s in hidden service port "
- "configuration", escaped(smartlist_get(sl,0)));
+ if (err_msg_out)
+ tor_asprintf(&err_msg, "Missing or invalid port %s in hidden service "
+ "port configuration", escaped(smartlist_get(sl,0)));
+
goto err;
}
@@ -369,10 +424,11 @@ parse_port_config(const char *string)
addrport = smartlist_get(sl,1);
ret = config_parse_unix_port(addrport, &socket_path);
if (ret < 0 && ret != -ENOENT) {
- if (ret == -EINVAL) {
- log_warn(LD_CONFIG,
- "Empty socket path in hidden service port configuration.");
- }
+ if (ret == -EINVAL)
+ if (err_msg_out)
+ err_msg = tor_strdup("Empty socket path in hidden service port "
+ "configuration.");
+
goto err;
}
if (socket_path) {
@@ -380,8 +436,10 @@ parse_port_config(const char *string)
} else if (strchr(addrport, ':') || strchr(addrport, '.')) {
/* else try it as an IP:port pair if it has a : or . in it */
if (tor_addr_port_lookup(addrport, &addr, &p)<0) {
- log_warn(LD_CONFIG,"Unparseable address in hidden service port "
- "configuration.");
+ if (err_msg_out)
+ err_msg = tor_strdup("Unparseable address in hidden service port "
+ "configuration.");
+
goto err;
}
realport = p?p:virtport;
@@ -389,8 +447,11 @@ parse_port_config(const char *string)
/* No addr:port, no addr -- must be port. */
realport = (int)tor_parse_long(addrport, 10, 1, 65535, NULL, NULL);
if (!realport) {
- log_warn(LD_CONFIG,"Unparseable or out-of-range port %s in hidden "
- "service port configuration.", escaped(addrport));
+ if (err_msg_out)
+ tor_asprintf(&err_msg, "Unparseable or out-of-range port %s in "
+ "hidden service port configuration.",
+ escaped(addrport));
+
goto err;
}
tor_addr_from_ipv4h(&addr, 0x7F000001u); /* Default to 127.0.0.1 */
@@ -408,6 +469,7 @@ parse_port_config(const char *string)
}
err:
+ if (err_msg_out) *err_msg_out = err_msg;
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
if (socket_path) tor_free(socket_path);
@@ -415,6 +477,13 @@ parse_port_config(const char *string)
return result;
}
+/** Release all storage held in a rend_service_port_config_t. */
+void
+rend_service_port_config_free(rend_service_port_config_t *p)
+{
+ tor_free(p);
+}
+
/** Set up rend_service_list, based on the values of HiddenServiceDir and
* HiddenServicePort in <b>options</b>. Return 0 on success and -1 on
* failure. (If <b>validate_only</b> is set, parse, warn and return as
@@ -439,113 +508,145 @@ rend_config_services(const or_options_t *options, int validate_only)
if (service) { /* register the one we just finished parsing */
if (validate_only)
rend_service_free(service);
- else
- rend_add_service(service);
- }
- service = tor_malloc_zero(sizeof(rend_service_t));
- service->directory = tor_strdup(line->value);
- service->ports = smartlist_new();
- service->intro_period_started = time(NULL);
- service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
- continue;
- }
- if (!service) {
- log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
- line->key);
- rend_service_free(service);
- return -1;
- }
- if (!strcasecmp(line->key, "HiddenServicePort")) {
- portcfg = parse_port_config(line->value);
- if (!portcfg) {
- rend_service_free(service);
- return -1;
- }
- smartlist_add(service->ports, portcfg);
- } else if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) {
- service->allow_unknown_ports = (int)tor_parse_long(line->value,
- 10, 0, 1, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s",
- line->value);
- rend_service_free(service);
- return -1;
- }
- log_info(LD_CONFIG,
- "HiddenServiceAllowUnknownPorts=%d for %s",
- (int)service->allow_unknown_ports, service->directory);
- } else if (!strcasecmp(line->key,
- "HiddenServiceDirGroupReadable")) {
- service->dir_group_readable = (int)tor_parse_long(line->value,
- 10, 0, 1, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceDirGroupReadable should be 0 or 1, not %s",
- line->value);
- rend_service_free(service);
- return -1;
- }
- log_info(LD_CONFIG,
- "HiddenServiceDirGroupReadable=%d for %s",
- service->dir_group_readable, service->directory);
- } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
- /* Parse auth type and comma-separated list of client names and add a
- * rend_authorized_client_t for each client to the service's list
- * of authorized clients. */
- smartlist_t *type_names_split, *clients;
- const char *authname;
- int num_clients;
- if (service->auth_type != REND_NO_AUTH) {
- log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient "
- "lines for a single service.");
- rend_service_free(service);
- return -1;
- }
- type_names_split = smartlist_new();
- smartlist_split_string(type_names_split, line->value, " ", 0, 2);
- if (smartlist_len(type_names_split) < 1) {
- log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This "
- "should have been prevented when parsing the "
- "configuration.");
- smartlist_free(type_names_split);
- rend_service_free(service);
- return -1;
- }
- authname = smartlist_get(type_names_split, 0);
- if (!strcasecmp(authname, "basic")) {
- service->auth_type = REND_BASIC_AUTH;
- } else if (!strcasecmp(authname, "stealth")) {
- service->auth_type = REND_STEALTH_AUTH;
- } else {
- log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
- "unrecognized auth-type '%s'. Only 'basic' or 'stealth' "
- "are recognized.",
- (char *) smartlist_get(type_names_split, 0));
- SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
- smartlist_free(type_names_split);
- rend_service_free(service);
- return -1;
- }
- service->clients = smartlist_new();
- if (smartlist_len(type_names_split) < 2) {
- log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
- "auth-type '%s', but no client names.",
- service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
- SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
- smartlist_free(type_names_split);
- continue;
- }
- clients = smartlist_new();
- smartlist_split_string(clients, smartlist_get(type_names_split, 1),
- ",", SPLIT_SKIP_SPACE, 0);
- SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
- smartlist_free(type_names_split);
- /* Remove duplicate client names. */
- num_clients = smartlist_len(clients);
- smartlist_sort_strings(clients);
- smartlist_uniq_strings(clients);
- if (smartlist_len(clients) < num_clients) {
+ else
+ rend_add_service(service);
+ }
+ service = tor_malloc_zero(sizeof(rend_service_t));
+ service->directory = tor_strdup(line->value);
+ service->ports = smartlist_new();
+ service->intro_period_started = time(NULL);
+ service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
+ continue;
+ }
+ if (!service) {
+ log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
+ line->key);
+ rend_service_free(service);
+ return -1;
+ }
+ if (!strcasecmp(line->key, "HiddenServicePort")) {
+ char *err_msg = NULL;
+ portcfg = rend_service_parse_port_config(line->value, " ", &err_msg);
+ if (!portcfg) {
+ if (err_msg)
+ log_warn(LD_CONFIG, "%s", err_msg);
+ tor_free(err_msg);
+ rend_service_free(service);
+ return -1;
+ }
+ tor_assert(!err_msg);
+ smartlist_add(service->ports, portcfg);
+ } else if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) {
+ service->allow_unknown_ports = (int)tor_parse_long(line->value,
+ 10, 0, 1, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s",
+ line->value);
+ rend_service_free(service);
+ return -1;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceAllowUnknownPorts=%d for %s",
+ (int)service->allow_unknown_ports, service->directory);
+ } else if (!strcasecmp(line->key,
+ "HiddenServiceDirGroupReadable")) {
+ service->dir_group_readable = (int)tor_parse_long(line->value,
+ 10, 0, 1, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceDirGroupReadable should be 0 or 1, not %s",
+ line->value);
+ rend_service_free(service);
+ return -1;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceDirGroupReadable=%d for %s",
+ service->dir_group_readable, service->directory);
+ } else if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
+ service->max_streams_per_circuit = (int)tor_parse_long(line->value,
+ 10, 0, 65535, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceMaxStreams should be between 0 and %d, not %s",
+ 65535, line->value);
+ rend_service_free(service);
+ return -1;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceMaxStreams=%d for %s",
+ service->max_streams_per_circuit, service->directory);
+ } else if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
+ service->max_streams_close_circuit = (int)tor_parse_long(line->value,
+ 10, 0, 1, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, not %s",
+ line->value);
+ rend_service_free(service);
+ return -1;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceMaxStreamsCloseCircuit=%d for %s",
+ (int)service->max_streams_close_circuit, service->directory);
+
+ } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
+ /* Parse auth type and comma-separated list of client names and add a
+ * rend_authorized_client_t for each client to the service's list
+ * of authorized clients. */
+ smartlist_t *type_names_split, *clients;
+ const char *authname;
+ int num_clients;
+ if (service->auth_type != REND_NO_AUTH) {
+ log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient "
+ "lines for a single service.");
+ rend_service_free(service);
+ return -1;
+ }
+ type_names_split = smartlist_new();
+ smartlist_split_string(type_names_split, line->value, " ", 0, 2);
+ if (smartlist_len(type_names_split) < 1) {
+ log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This "
+ "should have been prevented when parsing the "
+ "configuration.");
+ smartlist_free(type_names_split);
+ rend_service_free(service);
+ return -1;
+ }
+ authname = smartlist_get(type_names_split, 0);
+ if (!strcasecmp(authname, "basic")) {
+ service->auth_type = REND_BASIC_AUTH;
+ } else if (!strcasecmp(authname, "stealth")) {
+ service->auth_type = REND_STEALTH_AUTH;
+ } else {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
+ "unrecognized auth-type '%s'. Only 'basic' or 'stealth' "
+ "are recognized.",
+ (char *) smartlist_get(type_names_split, 0));
+ SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
+ smartlist_free(type_names_split);
+ rend_service_free(service);
+ return -1;
+ }
+ service->clients = smartlist_new();
+ if (smartlist_len(type_names_split) < 2) {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
+ "auth-type '%s', but no client names.",
+ service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
+ SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
+ smartlist_free(type_names_split);
+ continue;
+ }
+ clients = smartlist_new();
+ smartlist_split_string(clients, smartlist_get(type_names_split, 1),
+ ",", SPLIT_SKIP_SPACE, 0);
+ SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
+ smartlist_free(type_names_split);
+ /* Remove duplicate client names. */
+ num_clients = smartlist_len(clients);
+ smartlist_sort_strings(clients);
+ smartlist_uniq_strings(clients);
+ if (smartlist_len(clients) < num_clients) {
log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
"duplicate client name(s); removing.",
num_clients - smartlist_len(clients));
@@ -632,12 +733,35 @@ rend_config_services(const or_options_t *options, int validate_only)
if (old_service_list && !validate_only) {
smartlist_t *surviving_services = smartlist_new();
+ /* Preserve the existing ephemeral services.
+ *
+ * This is the ephemeral service equivalent of the "Copy introduction
+ * points to new services" block, except there's no copy required since
+ * the service structure isn't regenerated.
+ *
+ * After this is done, all ephemeral services will be:
+ * * Removed from old_service_list, so the equivalent non-ephemeral code
+ * will not attempt to preserve them.
+ * * Added to the new rend_service_list (that previously only had the
+ * services listed in the configuration).
+ * * Added to surviving_services, which is the list of services that
+ * will NOT have their intro point closed.
+ */
+ SMARTLIST_FOREACH(old_service_list, rend_service_t *, old, {
+ if (!old->directory) {
+ SMARTLIST_DEL_CURRENT(old_service_list, old);
+ smartlist_add(surviving_services, old);
+ smartlist_add(rend_service_list, old);
+ }
+ });
+
/* Copy introduction points to new services. */
/* XXXX This is O(n^2), but it's only called on reconfigure, so it's
* probably ok? */
SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, new) {
SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) {
- if (!strcmp(old->directory, new->directory)) {
+ if (new->directory && old->directory &&
+ !strcmp(old->directory, new->directory)) {
smartlist_add_all(new->intro_nodes, old->intro_nodes);
smartlist_clear(old->intro_nodes);
smartlist_add(surviving_services, old);
@@ -685,6 +809,125 @@ rend_config_services(const or_options_t *options, int validate_only)
return 0;
}
+/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, with
+ * <b>max_streams_per_circuit</b> streams allowed per rendezvous circuit,
+ * and circuit closure on max streams being exceeded set by
+ * <b>max_streams_close_circuit</b>.
+ *
+ * Regardless of sucess/failure, callers should not touch pk/ports after
+ * calling this routine, and may assume that correct cleanup has been done
+ * on failure.
+ *
+ * Return an appropriate rend_service_add_ephemeral_status_t.
+ */
+rend_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,
+ char **service_id_out)
+{
+ *service_id_out = NULL;
+ /* Allocate the service structure, and initialize the key, and key derived
+ * parameters.
+ */
+ rend_service_t *s = tor_malloc_zero(sizeof(rend_service_t));
+ s->directory = NULL; /* This indicates the service is ephemeral. */
+ s->private_key = pk;
+ s->auth_type = REND_NO_AUTH;
+ s->ports = ports;
+ s->intro_period_started = time(NULL);
+ s->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
+ s->max_streams_per_circuit = max_streams_per_circuit;
+ s->max_streams_close_circuit = max_streams_close_circuit;
+ if (rend_service_derive_key_digests(s) < 0) {
+ rend_service_free(s);
+ return RSAE_BADPRIVKEY;
+ }
+
+ if (!s->ports || smartlist_len(s->ports) == 0) {
+ log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified.");
+ rend_service_free(s);
+ return RSAE_BADVIRTPORT;
+ }
+
+ /* Enforcing pk/id uniqueness should be done by rend_service_load_keys(), but
+ * it's not, see #14828.
+ */
+ if (rend_service_get_by_pk_digest(s->pk_digest)) {
+ log_warn(LD_CONFIG, "Onion Service private key collides with an "
+ "existing service.");
+ rend_service_free(s);
+ return RSAE_ADDREXISTS;
+ }
+ if (rend_service_get_by_service_id(s->service_id)) {
+ log_warn(LD_CONFIG, "Onion Service id collides with an existing service.");
+ rend_service_free(s);
+ return RSAE_ADDREXISTS;
+ }
+
+ /* Initialize the service. */
+ if (rend_add_service(s)) {
+ rend_service_free(s);
+ return RSAE_INTERNAL;
+ }
+ *service_id_out = tor_strdup(s->service_id);
+
+ log_debug(LD_CONFIG, "Added ephemeral Onion Service: %s", s->service_id);
+ return RSAE_OKAY;
+}
+
+/** Remove the ephemeral service <b>service_id</b> if possible. Returns 0 on
+ * success, and -1 on failure.
+ */
+int
+rend_service_del_ephemeral(const char *service_id)
+{
+ rend_service_t *s;
+ if (!rend_valid_service_id(service_id)) {
+ log_warn(LD_CONFIG, "Requested malformed Onion Service id for removal.");
+ return -1;
+ }
+ if ((s = rend_service_get_by_service_id(service_id)) == NULL) {
+ log_warn(LD_CONFIG, "Requested non-existent Onion Service id for "
+ "removal.");
+ return -1;
+ }
+ if (s->directory) {
+ log_warn(LD_CONFIG, "Requested non-ephemeral Onion Service for removal.");
+ return -1;
+ }
+
+ /* Kill the intro point circuit for the Onion Service, and remove it from
+ * the list. Closing existing connections is the application's problem.
+ *
+ * XXX: As with the comment in rend_config_services(), a nice abstraction
+ * would be ideal here, but for now just duplicate the code.
+ */
+ SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
+ if (!circ->marked_for_close &&
+ circ->state == CIRCUIT_STATE_OPEN &&
+ (circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
+ circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
+ origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
+ tor_assert(oc->rend_data);
+ if (!tor_memeq(s->pk_digest, oc->rend_data->rend_pk_digest, DIGEST_LEN))
+ continue;
+ log_debug(LD_REND, "Closing intro point %s for service %s.",
+ safe_str_client(extend_info_describe(
+ oc->build_state->chosen_exit)),
+ oc->rend_data->onion_address);
+ circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
+ }
+ } SMARTLIST_FOREACH_END(circ);
+ smartlist_remove(rend_service_list, s);
+ rend_service_free(s);
+
+ log_debug(LD_CONFIG, "Removed ephemeral Onion Service: %s", service_id);
+
+ return 0;
+}
+
/** Replace the old value of <b>service</b>-\>desc with one that reflects
* the other fields in service.
*/
@@ -769,6 +1012,7 @@ rend_service_add_filenames_to_list(smartlist_t *lst, const rend_service_t *s)
{
tor_assert(lst);
tor_assert(s);
+ tor_assert(s->directory);
smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"private_key",
s->directory);
smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"hostname",
@@ -787,11 +1031,31 @@ rend_services_add_filenames_to_lists(smartlist_t *open_lst,
if (!rend_service_list)
return;
SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) {
- rend_service_add_filenames_to_list(open_lst, s);
- smartlist_add(stat_lst, tor_strdup(s->directory));
+ if (s->directory) {
+ rend_service_add_filenames_to_list(open_lst, s);
+ smartlist_add(stat_lst, tor_strdup(s->directory));
+ }
} SMARTLIST_FOREACH_END(s);
}
+/** Derive all rend_service_t internal material based on the service's key.
+ * Returns 0 on sucess, -1 on failure.
+ */
+static int
+rend_service_derive_key_digests(struct rend_service_t *s)
+{
+ if (rend_get_service_id(s->private_key, s->service_id)<0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
+ return -1;
+ }
+ if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
+ log_warn(LD_BUG, "Couldn't compute hash of public key.");
+ return -1;
+ }
+
+ return 0;
+}
+
/** Load and/or generate private keys for the hidden service <b>s</b>,
* possibly including keys for client authorization. Return 0 on success, -1
* on failure. */
@@ -830,15 +1094,10 @@ rend_service_load_keys(rend_service_t *s)
if (!s->private_key)
return -1;
- /* Create service file */
- if (rend_get_service_id(s->private_key, s->service_id)<0) {
- log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
- return -1;
- }
- if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
- log_warn(LD_BUG, "Couldn't compute hash of public key.");
+ if (rend_service_derive_key_digests(s) < 0)
return -1;
- }
+
+ /* Create service file */
if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname))
>= sizeof(fname)) {
@@ -941,7 +1200,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
}
if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
client->descriptor_cookie,
- REND_DESC_COOKIE_LEN) < 0) {
+ REND_DESC_COOKIE_LEN, 0) < 0) {
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
goto err;
}
@@ -968,7 +1227,6 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
client->client_key = prkey;
}
/* Add entry to client_keys file. */
- desc_cook_out[strlen(desc_cook_out)-1] = '\0'; /* Remove newline. */
written = tor_snprintf(buf, sizeof(buf),
"client-name %s\ndescriptor-cookie %s\n",
client->client_name, desc_cook_out);
@@ -1023,12 +1281,11 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
((int)s->auth_type - 1) << 4;
if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
extended_desc_cookie,
- REND_DESC_COOKIE_LEN+1) < 0) {
+ REND_DESC_COOKIE_LEN+1, 0) < 0) {
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
goto err;
}
- desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and
- newline. */
+ desc_cook_out[strlen(desc_cook_out)-2] = '\0'; /* Remove A=. */
tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
service_id, desc_cook_out, client->client_name);
}
@@ -1052,7 +1309,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
abort_writing_to_file(open_hfile);
done:
if (client_keys_str) {
- tor_strclear(client_keys_str);
+ memwipe(client_keys_str, 0, strlen(client_keys_str));
tor_free(client_keys_str);
}
strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
@@ -1080,6 +1337,20 @@ rend_service_get_by_pk_digest(const char* digest)
return NULL;
}
+/** Return the service whose service id is <b>id</b>, or NULL if no such
+ * service exists.
+ */
+static struct rend_service_t *
+rend_service_get_by_service_id(const char *id)
+{
+ tor_assert(strlen(id) == REND_SERVICE_ID_LEN_BASE32);
+ SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s, {
+ if (tor_memeq(s->service_id, id, REND_SERVICE_ID_LEN_BASE32))
+ return s;
+ });
+ return NULL;
+}
+
/** Return 1 if any virtual port in <b>service</b> wants a circuit
* to have good uptime. Else return 0.
*/
@@ -1098,11 +1369,13 @@ rend_service_requires_uptime(rend_service_t *service)
return 0;
}
-/** Check client authorization of a given <b>descriptor_cookie</b> for
- * <b>service</b>. Return 1 for success and 0 for failure. */
+/** Check client authorization of a given <b>descriptor_cookie</b> of
+ * length <b>cookie_len</b> for <b>service</b>. Return 1 for success
+ * and 0 for failure. */
static int
rend_check_authorization(rend_service_t *service,
- const char *descriptor_cookie)
+ const char *descriptor_cookie,
+ size_t cookie_len)
{
rend_authorized_client_t *auth_client = NULL;
tor_assert(service);
@@ -1113,6 +1386,13 @@ rend_check_authorization(rend_service_t *service,
return 0;
}
+ if (cookie_len != REND_DESC_COOKIE_LEN) {
+ log_info(LD_REND, "Descriptor cookie is %lu bytes, but we expected "
+ "%lu bytes. Dropping cell.",
+ (unsigned long)cookie_len, (unsigned long)REND_DESC_COOKIE_LEN);
+ return 0;
+ }
+
/* Look up client authorization by descriptor cookie. */
SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, client, {
if (tor_memeq(client->descriptor_cookie, descriptor_cookie,
@@ -1124,7 +1404,7 @@ rend_check_authorization(rend_service_t *service,
if (!auth_client) {
char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64),
- descriptor_cookie, REND_DESC_COOKIE_LEN);
+ descriptor_cookie, REND_DESC_COOKIE_LEN, 0);
log_info(LD_REND, "No authorization found for descriptor cookie '%s'! "
"Dropping cell!",
descriptor_cookie_base64);
@@ -1158,16 +1438,17 @@ rend_service_note_removing_intro_point(rend_service_t *service,
/* This intro point was never used. Don't change
* n_intro_points_wanted. */
} else {
+
/* We want to increase the number of introduction points service
* operates if intro was heavily used, or decrease the number of
* intro points if intro was lightly used.
*
* We consider an intro point's target 'usage' to be
- * INTRO_POINT_LIFETIME_INTRODUCTIONS introductions in
+ * maximum of INTRODUCE2 cells divided by
* INTRO_POINT_LIFETIME_MIN_SECONDS seconds. To calculate intro's
- * fraction of target usage, we divide the fraction of
- * _LIFETIME_INTRODUCTIONS introductions that it has handled by
- * the fraction of _LIFETIME_MIN_SECONDS for which it existed.
+ * fraction of target usage, we divide the amount of INTRODUCE2 cells
+ * that it has handled by the fraction of _LIFETIME_MIN_SECONDS for
+ * which it existed.
*
* Then we multiply that fraction of desired usage by a fudge
* factor of 1.5, to decide how many new introduction points
@@ -1189,7 +1470,7 @@ rend_service_note_removing_intro_point(rend_service_t *service,
intro_point_accepted_intro_count(intro) /
(double)(now - intro->time_published);
const double intro_point_target_usage =
- INTRO_POINT_LIFETIME_INTRODUCTIONS /
+ intro->max_introductions /
(double)INTRO_POINT_LIFETIME_MIN_SECONDS;
const double fractional_n_intro_points_wanted_to_replace_this_one =
(1.5 * (intro_point_usage / intro_point_target_usage));
@@ -1459,7 +1740,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
if (service->clients) {
if (parsed_req->version == 3 && parsed_req->u.v3.auth_len > 0) {
if (rend_check_authorization(service,
- (const char*)parsed_req->u.v3.auth_data)) {
+ (const char*)parsed_req->u.v3.auth_data,
+ parsed_req->u.v3.auth_len)) {
log_info(LD_REND, "Authorization data in INTRODUCE2 cell are valid.");
} else {
log_info(LD_REND, "The authorization data that are contained in "
@@ -1523,13 +1805,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
hexcookie, serviceid);
tor_assert(launched->build_state);
/* Fill in the circuit's state. */
- launched->rend_data = tor_malloc_zero(sizeof(rend_data_t));
- memcpy(launched->rend_data->rend_pk_digest,
- circuit->rend_data->rend_pk_digest,
- DIGEST_LEN);
- memcpy(launched->rend_data->rend_cookie, parsed_req->rc, REND_COOKIE_LEN);
- strlcpy(launched->rend_data->onion_address, service->service_id,
- sizeof(launched->rend_data->onion_address));
+
+ launched->rend_data =
+ rend_data_service_create(service->service_id,
+ circuit->rend_data->rend_pk_digest,
+ parsed_req->rc, service->auth_type);
launched->build_state->service_pending_final_cpath_ref =
tor_malloc_zero(sizeof(crypt_path_reference_t));
@@ -1940,6 +2220,16 @@ rend_service_parse_intro_for_v2(
goto err;
}
+ if (128 != crypto_pk_keysize(extend_info->onion_key)) {
+ if (err_msg_out) {
+ tor_asprintf(err_msg_out,
+ "invalid onion key size in version %d INTRODUCE%d cell",
+ intro->version,
+ (intro->type));
+ }
+
+ goto err;
+ }
ver_specific_len = 7+DIGEST_LEN+2+klen;
@@ -2491,10 +2781,9 @@ rend_service_launch_establish_intro(rend_service_t *service,
intro->extend_info = extend_info_dup(launched->build_state->chosen_exit);
}
- launched->rend_data = tor_malloc_zero(sizeof(rend_data_t));
- strlcpy(launched->rend_data->onion_address, service->service_id,
- sizeof(launched->rend_data->onion_address));
- memcpy(launched->rend_data->rend_pk_digest, service->pk_digest, DIGEST_LEN);
+ launched->rend_data = rend_data_service_create(service->service_id,
+ service->pk_digest, NULL,
+ service->auth_type);
launched->intro_key = crypto_pk_dup_key(intro->intro_key);
if (launched->base_.state == CIRCUIT_STATE_OPEN)
rend_service_intro_has_opened(launched);
@@ -2879,14 +3168,16 @@ find_intro_point(origin_circuit_t *circ)
return NULL;
}
-/** Determine the responsible hidden service directories for the
- * rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them;
- * <b>service_id</b> and <b>seconds_valid</b> are only passed for logging
- * purposes. */
-static void
+/** Upload the rend_encoded_v2_service_descriptor_t's in <b>descs</b>
+ * associated with the rend_service_descriptor_t <b>renddesc</b> to
+ * the responsible hidden service directories OR the hidden service
+ * directories specified by <b>hs_dirs</b>; <b>service_id</b> and
+ * <b>seconds_valid</b> are only passed for logging purposes.
+ */
+void
directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
- smartlist_t *descs, const char *service_id,
- int seconds_valid)
+ smartlist_t *descs, smartlist_t *hs_dirs,
+ const char *service_id, int seconds_valid)
{
int i, j, failed_upload = 0;
smartlist_t *responsible_dirs = smartlist_new();
@@ -2894,14 +3185,21 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
routerstatus_t *hs_dir;
for (i = 0; i < smartlist_len(descs); i++) {
rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i);
- /* Determine responsible dirs. */
- if (hid_serv_get_responsible_directories(responsible_dirs,
- desc->desc_id) < 0) {
- log_warn(LD_REND, "Could not determine the responsible hidden service "
- "directories to post descriptors to.");
- smartlist_free(responsible_dirs);
- smartlist_free(successful_uploads);
- return;
+ /** If any HSDirs are specified, they should be used instead of
+ * the responsible directories */
+ if (hs_dirs && smartlist_len(hs_dirs) > 0) {
+ smartlist_add_all(responsible_dirs, hs_dirs);
+ } else {
+ /* Determine responsible dirs. */
+ if (hid_serv_get_responsible_directories(responsible_dirs,
+ desc->desc_id) < 0) {
+ log_warn(LD_REND, "Could not determine the responsible hidden service "
+ "directories to post descriptors to.");
+ control_event_hs_descriptor_upload(service_id,
+ "UNKNOWN",
+ "UNKNOWN");
+ goto done;
+ }
}
for (j = 0; j < smartlist_len(responsible_dirs); j++) {
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
@@ -2941,6 +3239,9 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
hs_dir->nickname,
hs_dir_ip,
hs_dir->or_port);
+ control_event_hs_descriptor_upload(service_id,
+ hs_dir->identity_digest,
+ desc_id_base32);
tor_free(hs_dir_ip);
/* Remember successful upload to this router for next time. */
if (!smartlist_contains_digest(successful_uploads,
@@ -2968,6 +3269,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
}
});
}
+ done:
smartlist_free(responsible_dirs);
smartlist_free(successful_uploads);
}
@@ -3032,7 +3334,7 @@ upload_service_descriptor(rend_service_t *service)
rend_get_service_id(service->desc->pk, serviceid);
log_info(LD_REND, "Launching upload for hidden service %s",
serviceid);
- directory_post_to_hs_dir(service->desc, descs, serviceid,
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
seconds_valid);
/* Free memory for descriptors. */
for (i = 0; i < smartlist_len(descs); i++)
@@ -3061,7 +3363,7 @@ upload_service_descriptor(rend_service_t *service)
smartlist_free(client_cookies);
return;
}
- directory_post_to_hs_dir(service->desc, descs, serviceid,
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
seconds_valid);
/* Free memory for descriptors. */
for (i = 0; i < smartlist_len(descs); i++)
@@ -3113,7 +3415,7 @@ intro_point_should_expire_now(rend_intro_point_t *intro,
}
if (intro_point_accepted_intro_count(intro) >=
- INTRO_POINT_LIFETIME_INTRODUCTIONS) {
+ intro->max_introductions) {
/* This intro point has been used too many times. Expire it now. */
return 1;
}
@@ -3122,9 +3424,8 @@ intro_point_should_expire_now(rend_intro_point_t *intro,
/* This intro point has been published, but we haven't picked an
* expiration time for it. Pick one now. */
int intro_point_lifetime_seconds =
- INTRO_POINT_LIFETIME_MIN_SECONDS +
- crypto_rand_int(INTRO_POINT_LIFETIME_MAX_SECONDS -
- INTRO_POINT_LIFETIME_MIN_SECONDS);
+ crypto_rand_int_range(INTRO_POINT_LIFETIME_MIN_SECONDS,
+ INTRO_POINT_LIFETIME_MAX_SECONDS);
/* Start the expiration timer now, rather than when the intro
* point was first published. There shouldn't be much of a time
@@ -3309,7 +3610,8 @@ rend_services_introduce(void)
log_warn(LD_REND,
"Could only establish %d introduction points for %s; "
"wanted %u.",
- smartlist_len(service->intro_nodes), service->service_id,
+ smartlist_len(service->intro_nodes),
+ safe_str_client(service->service_id),
n_intro_points_to_open);
break;
}
@@ -3320,10 +3622,14 @@ rend_services_introduce(void)
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
intro->extend_info = extend_info_from_node(node, 0);
intro->intro_key = crypto_pk_new();
- tor_assert(!crypto_pk_generate_key(intro->intro_key));
+ const int fail = crypto_pk_generate_key(intro->intro_key);
+ tor_assert(!fail);
intro->time_published = -1;
intro->time_to_expire = -1;
intro->time_expiring = -1;
+ intro->max_introductions =
+ crypto_rand_int_range(INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS,
+ INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS);
smartlist_add(service->intro_nodes, intro);
log_info(LD_REND, "Picked router %s as an intro point for %s.",
safe_str_client(node_describe(node)),
@@ -3547,6 +3853,25 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
serviceid, (unsigned)circ->base_.n_circ_id);
return -2;
}
+ if (service->max_streams_per_circuit > 0) {
+ /* Enforce the streams-per-circuit limit, and refuse to provide a
+ * mapping if this circuit will exceed the limit. */
+#define MAX_STREAM_WARN_INTERVAL 600
+ static struct ratelim_t stream_ratelim =
+ RATELIM_INIT(MAX_STREAM_WARN_INTERVAL);
+ if (circ->rend_data->nr_streams >= service->max_streams_per_circuit) {
+ log_fn_ratelim(&stream_ratelim, LOG_WARN, LD_REND,
+ "Maximum streams per circuit limit reached on rendezvous "
+ "circuit %u; %s. Circuit has %d out of %d streams.",
+ (unsigned)circ->base_.n_circ_id,
+ service->max_streams_close_circuit ?
+ "closing circuit" :
+ "ignoring open stream request",
+ circ->rend_data->nr_streams,
+ service->max_streams_per_circuit);
+ return service->max_streams_close_circuit ? -2 : -1;
+ }
+ }
matching_ports = smartlist_new();
SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
{
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 754f7c358c..b540d2c8ad 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -15,6 +15,7 @@
#include "or.h"
typedef struct rend_intro_cell_s rend_intro_cell_t;
+typedef struct rend_service_port_config_s rend_service_port_config_t;
#ifdef RENDSERVICE_PRIVATE
@@ -101,5 +102,29 @@ int rend_service_set_connection_addr_port(edge_connection_t *conn,
void rend_service_dump_stats(int severity);
void rend_service_free_all(void);
+rend_service_port_config_t *rend_service_parse_port_config(const char *string,
+ const char *sep,
+ char **err_msg_out);
+void rend_service_port_config_free(rend_service_port_config_t *p);
+
+/** Return value from rend_service_add_ephemeral. */
+typedef enum {
+ 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,
+ smartlist_t *ports,
+ int max_streams_per_circuit,
+ int max_streams_close_circuit,
+ char **service_id_out);
+int rend_service_del_ephemeral(const char *service_id);
+
+void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
+ smartlist_t *descs, smartlist_t *hs_dirs,
+ const char *service_id, int seconds_valid);
+
#endif
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 34908828a5..fe0997c891 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -3121,6 +3121,50 @@ rep_hist_hs_stats_write(time_t now)
return start_of_hs_stats_interval + WRITE_STATS_INTERVAL;
}
+#define MAX_LINK_PROTO_TO_LOG 4
+static uint64_t link_proto_count[MAX_LINK_PROTO_TO_LOG+1][2];
+
+/** Note that we negotiated link protocol version <b>link_proto</b>, on
+ * a connection that started here iff <b>started_here</b> is true.
+ */
+void
+rep_hist_note_negotiated_link_proto(unsigned link_proto, int started_here)
+{
+ started_here = !!started_here; /* force to 0 or 1 */
+ if (link_proto > MAX_LINK_PROTO_TO_LOG) {
+ log_warn(LD_BUG, "Can't log link protocol %u", link_proto);
+ return;
+ }
+
+ link_proto_count[link_proto][started_here]++;
+}
+
+/** Log a heartbeat message explaining how many connections of each link
+ * protocol version we have used.
+ */
+void
+rep_hist_log_link_protocol_counts(void)
+{
+ log_notice(LD_HEARTBEAT,
+ "Since startup, we have initiated "
+ U64_FORMAT" v1 connections, "
+ U64_FORMAT" v2 connections, "
+ U64_FORMAT" v3 connections, and "
+ U64_FORMAT" v4 connections; and received "
+ U64_FORMAT" v1 connections, "
+ U64_FORMAT" v2 connections, "
+ U64_FORMAT" v3 connections, and "
+ U64_FORMAT" v4 connections.",
+ U64_PRINTF_ARG(link_proto_count[1][1]),
+ U64_PRINTF_ARG(link_proto_count[2][1]),
+ U64_PRINTF_ARG(link_proto_count[3][1]),
+ U64_PRINTF_ARG(link_proto_count[4][1]),
+ U64_PRINTF_ARG(link_proto_count[1][0]),
+ U64_PRINTF_ARG(link_proto_count[2][0]),
+ U64_PRINTF_ARG(link_proto_count[3][0]),
+ U64_PRINTF_ARG(link_proto_count[4][0]));
+}
+
/** Free all storage held by the OR/link history caches, by the
* bandwidth history arrays, by the port history, or by statistics . */
void
diff --git a/src/or/rephist.h b/src/or/rephist.h
index 42710c4ed6..f94b4e8ff1 100644
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@ -108,5 +108,9 @@ void rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey);
void rep_hist_free_all(void);
+void rep_hist_note_negotiated_link_proto(unsigned link_proto,
+ int started_here);
+void rep_hist_log_link_protocol_counts(void);
+
#endif
diff --git a/src/or/router.c b/src/or/router.c
index 00cd0578c6..6532f97d24 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -687,7 +687,9 @@ router_initialize_tls_context(void)
if (!lifetime) { /* we should guess a good ssl cert lifetime */
/* choose between 5 and 365 days, and round to the day */
- lifetime = 5*24*3600 + crypto_rand_int(361*24*3600);
+ unsigned int five_days = 5*24*3600;
+ unsigned int one_year = 365*24*3600;
+ lifetime = crypto_rand_int_range(five_days, one_year);
lifetime -= lifetime % (24*3600);
if (crypto_rand_int(2)) {
@@ -1817,12 +1819,15 @@ router_pick_published_address(const or_options_t *options, uint32_t *addr)
return 0;
}
-/** If <b>force</b> is true, or our descriptor is out-of-date, rebuild a fresh
- * routerinfo, signed server descriptor, and extra-info document for this OR.
- * Return 0 on success, -1 on temporary error.
+/** Build a fresh routerinfo, signed server descriptor, and extra-info document
+ * for this OR. Set r to the generated routerinfo, e to the generated
+ * extra-info document. Return 0 on success, -1 on temporary error. Failure to
+ * generate an extra-info document is not an error and is indicated by setting
+ * e to NULL. Caller is responsible for freeing generated documents if 0 is
+ * returned.
*/
int
-router_rebuild_descriptor(int force)
+router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
{
routerinfo_t *ri;
extrainfo_t *ei;
@@ -1831,20 +1836,11 @@ router_rebuild_descriptor(int force)
int hibernating = we_are_hibernating();
const or_options_t *options = get_options();
- if (desc_clean_since && !force)
- return 0;
-
- if (router_pick_published_address(options, &addr) < 0 ||
- router_get_advertised_or_port(options) == 0) {
- /* Stop trying to rebuild our descriptor every second. We'll
- * learn that it's time to try again when ip_address_changed()
- * marks it dirty. */
- desc_clean_since = time(NULL);
+ if (router_pick_published_address(options, &addr) < 0) {
+ log_warn(LD_CONFIG, "Don't know my address while generating descriptor");
return -1;
}
- log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : "");
-
ri = tor_malloc_zero(sizeof(routerinfo_t));
ri->cache_info.routerlist_index = -1;
ri->nickname = tor_strdup(options->Nickname);
@@ -2053,6 +2049,41 @@ router_rebuild_descriptor(int force)
tor_assert(! routerinfo_incompatible_with_extrainfo(ri, ei, NULL, NULL));
}
+ *r = ri;
+ *e = ei;
+ return 0;
+}
+
+/** If <b>force</b> is true, or our descriptor is out-of-date, rebuild a fresh
+ * routerinfo, signed server descriptor, and extra-info document for this OR.
+ * Return 0 on success, -1 on temporary error.
+ */
+int
+router_rebuild_descriptor(int force)
+{
+ routerinfo_t *ri;
+ extrainfo_t *ei;
+ uint32_t addr;
+ const or_options_t *options = get_options();
+
+ if (desc_clean_since && !force)
+ return 0;
+
+ if (router_pick_published_address(options, &addr) < 0 ||
+ router_get_advertised_or_port(options) == 0) {
+ /* Stop trying to rebuild our descriptor every second. We'll
+ * learn that it's time to try again when ip_address_changed()
+ * marks it dirty. */
+ desc_clean_since = time(NULL);
+ return -1;
+ }
+
+ log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : "");
+
+ if (router_build_fresh_descriptor(&ri, &ei) < 0) {
+ return -1;
+ }
+
routerinfo_free(desc_routerinfo);
desc_routerinfo = ri;
extrainfo_free(desc_extrainfo);
@@ -2377,7 +2408,8 @@ router_dump_router_to_string(routerinfo_t *router,
char ed_cert_base64[256];
if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64),
(const char*)router->signing_key_cert->encoded,
- router->signing_key_cert->encoded_len) < 0) {
+ router->signing_key_cert->encoded_len,
+ BASE64_ENCODE_MULTILINE) < 0) {
log_err(LD_BUG,"Couldn't base64-encode signing key certificate!");
goto err;
}
@@ -2416,7 +2448,8 @@ router_dump_router_to_string(routerinfo_t *router,
goto err;
}
- if (base64_encode(buf, sizeof(buf), (const char*)tap_cc, tap_cc_len) < 0) {
+ if (base64_encode(buf, sizeof(buf), (const char*)tap_cc, tap_cc_len,
+ BASE64_ENCODE_MULTILINE) < 0) {
log_warn(LD_BUG,"base64_encode(rsa_crosscert) failed!");
tor_free(tap_cc);
goto err;
@@ -2448,7 +2481,8 @@ router_dump_router_to_string(routerinfo_t *router,
tor_assert(sign == 0 || sign == 1);
if (base64_encode(buf, sizeof(buf),
- (const char*)cert->encoded, cert->encoded_len)<0) {
+ (const char*)cert->encoded, cert->encoded_len,
+ BASE64_ENCODE_MULTILINE)<0) {
log_warn(LD_BUG,"base64_encode(ntor_crosscert) failed!");
tor_cert_free(cert);
goto err;
@@ -2555,7 +2589,7 @@ router_dump_router_to_string(routerinfo_t *router,
char kbuf[128];
base64_encode(kbuf, sizeof(kbuf),
(const char *)router->onion_curve25519_pkey->public_key,
- CURVE25519_PUBKEY_LEN);
+ CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
}
@@ -2831,7 +2865,8 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
char ed_cert_base64[256];
if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64),
(const char*)extrainfo->signing_key_cert->encoded,
- extrainfo->signing_key_cert->encoded_len) < 0) {
+ extrainfo->signing_key_cert->encoded_len,
+ BASE64_ENCODE_MULTILINE) < 0) {
log_err(LD_BUG,"Couldn't base64-encode signing key certificate!");
goto err;
}
diff --git a/src/or/router.h b/src/or/router.h
index 695cfd38e5..61b35d6b5a 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -89,6 +89,7 @@ const uint8_t *router_get_my_id_digest(void);
int router_extrainfo_digest_is_me(const char *digest);
int router_is_me(const routerinfo_t *router);
int router_pick_published_address(const or_options_t *options, uint32_t *addr);
+int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e);
int router_rebuild_descriptor(int force);
char *router_dump_router_to_string(routerinfo_t *router,
const crypto_pk_t *ident_key,
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index a5310519d5..7eba13a544 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -38,8 +38,9 @@
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
-#include "../common/sandbox.h"
+#include "sandbox.h"
#include "torcert.h"
+
// #define DEBUG_ROUTERLIST
/****************************************************************************/
@@ -1500,9 +1501,6 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
if ((type & EXTRAINFO_DIRINFO) &&
!router_supports_extrainfo(node->identity, is_trusted_extrainfo))
continue;
- if ((type & MICRODESC_DIRINFO) && !is_trusted &&
- !node->rs->version_supports_microdesc_cache)
- continue;
if (for_guard && node->using_as_guard)
continue; /* Don't make the same node a guard twice. */
if (try_excluding &&
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 71a0baa0bd..c3dc241573 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -686,7 +686,8 @@ router_get_dirobj_signature(const char *digest,
goto truncated;
i = strlen(buf);
- if (base64_encode(buf+i, buf_len-i, signature, siglen) < 0) {
+ if (base64_encode(buf+i, buf_len-i, signature, siglen,
+ BASE64_ENCODE_MULTILINE) < 0) {
log_warn(LD_BUG,"couldn't base64-encode signature");
goto err;
}
@@ -2273,10 +2274,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
tor_assert(tok->n_args == 1);
rs->version_known = 1;
if (strcmpstart(tok->args[0], "Tor ")) {
- rs->version_supports_microdesc_cache = 1;
} else {
- rs->version_supports_microdesc_cache =
- tor_version_supports_microdescriptors(tok->args[0]);
rs->version_supports_extend2_cells =
tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha");
}
@@ -3647,7 +3645,9 @@ router_parse_addr_policy_item_from_string,(const char *s, int assume_action))
{
directory_token_t *tok = NULL;
const char *cp, *eos;
- /* Longest possible policy is "accept ffff:ffff:..255/ffff:...255:0-65535".
+ /* Longest possible policy is
+ * "accept6 ffff:ffff:..255/ffff:...255:10000-65535",
+ * which contains 2 max-length IPv6 addresses, plus 21 characters.
* But note that there can be an arbitrary amount of space between the
* accept and the address:mask/port element. */
char line[TOR_ADDR_BUF_LEN*2 + 32];
@@ -4567,14 +4567,6 @@ microdescs_parse_from_string(const char *s, const char *eos,
return result;
}
-/** Return true iff this Tor version can answer directory questions
- * about microdescriptors. */
-int
-tor_version_supports_microdescriptors(const char *platform)
-{
- return tor_version_as_new_as(platform, "0.2.3.1-alpha");
-}
-
/** Parse the Tor version of the platform string <b>platform</b>,
* and compare it to the version in <b>cutoff</b>. Return 1 if
* the router is at least as new as the cutoff, else return 0.
@@ -4887,8 +4879,7 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
tor_assert(tok == smartlist_get(tokens, 0));
tor_assert(tok->n_args == 1);
- if (strlen(tok->args[0]) != REND_DESC_ID_V2_LEN_BASE32 ||
- strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) {
+ if (!rend_valid_descriptor_id(tok->args[0])) {
log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
goto err;
}
@@ -5124,7 +5115,7 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed,
size_t intro_points_encoded_size)
{
const char *current_ipo, *end_of_intro_points;
- smartlist_t *tokens;
+ smartlist_t *tokens = NULL;
directory_token_t *tok;
rend_intro_point_t *intro;
extend_info_t *info;
@@ -5133,8 +5124,10 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed,
tor_assert(parsed);
/** Function may only be invoked once. */
tor_assert(!parsed->intro_nodes);
- tor_assert(intro_points_encoded);
- tor_assert(intro_points_encoded_size > 0);
+ if (!intro_points_encoded || intro_points_encoded_size == 0) {
+ log_warn(LD_REND, "Empty or zero size introduction point list");
+ goto err;
+ }
/* Consider one intro point after the other. */
current_ipo = intro_points_encoded;
end_of_intro_points = intro_points_encoded + intro_points_encoded_size;
@@ -5238,8 +5231,10 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed,
done:
/* Free tokens and clear token list. */
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
- smartlist_free(tokens);
+ if (tokens) {
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
+ smartlist_free(tokens);
+ }
if (area)
memarea_drop_all(area);
diff --git a/src/or/routerparse.h b/src/or/routerparse.h
index 7776063f03..85e4b7d88e 100644
--- a/src/or/routerparse.h
+++ b/src/or/routerparse.h
@@ -44,7 +44,6 @@ MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
(const char *s, int assume_action));
version_status_t tor_version_is_obsolete(const char *myversion,
const char *versionlist);
-int tor_version_supports_microdescriptors(const char *platform);
int tor_version_as_new_as(const char *platform, const char *cutoff);
int tor_version_parse(const char *s, tor_version_t *out);
int tor_version_compare(tor_version_t *a, tor_version_t *b);
diff --git a/src/or/status.c b/src/or/status.c
index 2acdd28249..8f7be0aa3c 100644
--- a/src/or/status.c
+++ b/src/or/status.c
@@ -136,8 +136,10 @@ log_heartbeat(time_t now)
"Average packaged cell fullness: %2.3f%%. "
"TLS write overhead: %.f%%", fullness_pct, overhead_pct);
- if (public_server_mode(options))
+ if (public_server_mode(options)) {
rep_hist_log_circuit_handshake_stats(now);
+ rep_hist_log_link_protocol_counts();
+ }
circuit_log_ancient_one_hop_circuits(1800);
diff --git a/src/or/transports.c b/src/or/transports.c
index 6f07054ea8..ba2c784c2c 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -1388,6 +1388,11 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
} else {
smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=");
}
+
+ /* All new versions of tor will keep stdin open, so PTs can use it
+ * as a reliable termination detection mechanism.
+ */
+ smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1");
} else {
/* If ClientTransportPlugin has a HTTPS/SOCKS proxy configured, set the
* TOR_PT_PROXY line.