Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions changes/anyone-hosts-autoupdate
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
o New features (anyone-hosts auto-update):
- The client now automatically fetches a fresh copy of the anyone_hosts
DNS mapping file from the .anyone DNS service nodes when new
directory information arrives and on a periodic schedule. Fetches
are routed by .anyone name over hidden-service circuits. A fetched
file is installed only if it carries a valid signature from a
trusted DNS signer, is unexpired, contains at least one mapping, and
is not older than the installed mapping. New configuration options
AnyoneHostsUpdate, AnyoneHostsUpdateInterval, and AnyoneHostsURL
control the behaviour.

o Minor features (documentation):
- DNSMappingFileMaxSize now documents bit-based units (KBits, MBits,
GBits, TBits) in the man page, consistent with other MEMUNIT options.
- Clarified that DNSMappingFileMaxSize only limits loading an existing
file; a missing file is always recreated with the default mapping.
26 changes: 24 additions & 2 deletions doc/man/anon.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1222,10 +1222,32 @@ The following options are useful only for clients (that is, if
addresses/ports. See <<SocksPort,SocksPort>> for an explanation of isolation
flags. (Default: 0)

[[DNSMappingFileMaxSize]] **DNSMappingFileMaxSize** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**::
[[DNSMappingFileMaxSize]] **DNSMappingFileMaxSize** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**::
The maximum size allowed for the `anyone_hosts` DNS mapping file in the
data directory. If the file exceeds this size, it is rejected. Set this
option to 0 to disable the size limit. (Default: 10 MB)
option to 0 to disable the size limit. This limit applies only when
reading or accepting an existing file; if the file is absent, a new
default file is created regardless of this setting. (Default: 10 MB)

[[AnyoneHostsUpdate]] **AnyoneHostsUpdate** **0**|**1**::
If 1, the client will automatically attempt to fetch a fresh copy of
the `anyone_hosts` DNS mapping file from the .anyone DNS service nodes
when new directory information arrives and on a periodic schedule.
Fetched files are installed only if they carry a valid signature from
a trusted DNS signer, have not expired, and are not older than the
currently installed mapping. (Default: 1)

[[AnyoneHostsUpdateInterval]] **AnyoneHostsUpdateInterval** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**::
How often to attempt fetching a fresh `anyone_hosts` file. The actual
schedule is randomly jittered by up to +/-10% around this value.
The minimum is 1 hour. (Default: 12 hours)

[[AnyoneHostsURL]] **AnyoneHostsURL** __address__::
A .anyone onion address of a DNS service node to try when fetching the
`anyone_hosts` file. This option may be specified multiple times; the
addresses are tried in order before the built-in defaults. A failing
address is skipped on the next attempt; a working address keeps being
used. (Default: none)

[[DownloadExtraInfo]] **DownloadExtraInfo** **0**|**1**::
If true, Tor downloads and caches "extra-info" documents. These documents
Expand Down
20 changes: 20 additions & 0 deletions src/app/config/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ static const config_var_t option_vars_[] = {
V(AlternateBridgeAuthority, LINELIST, NULL),
V(AlternateDirAuthority, LINELIST, NULL),
OBSOLETE("AlternateHSAuthority"),
V(AnyoneHostsUpdate, BOOL, "1"),
V(AnyoneHostsUpdateInterval, INTERVAL, "12 hours"),
V(AnyoneHostsURL, LINELIST, NULL),
V(AssumeReachable, BOOL, "0"),
V(AssumeReachableIPv6, AUTOBOOL, "auto"),
OBSOLETE("AuthDirBadDir"),
Expand Down Expand Up @@ -4062,6 +4065,23 @@ options_validate_cb(const void *old_options_, void *options_, char **msg)
return -1;
}

if (options->AnyoneHostsURL) {
const config_line_t *cl;
for (cl = options->AnyoneHostsURL; cl; cl = cl->next) {
const char *v = cl->value;
if (!v || !strlen(v) || strcmpend(v, ".anyone") ||
strstr(v, "://") || strchr(v, '/') || strchr(v, ':') ||
strpbrk(v, " \t\r\n")) {
REJECT("AnyoneHostsURL entries must be bare .anyone hostnames "
"(no scheme, port, path, or whitespace) ending in "
"\".anyone\".");
}
}
}
if (options->AnyoneHostsUpdateInterval < 3600) {
REJECT("AnyoneHostsUpdateInterval must be at least 1 hour.");
}

return 0;
}

Expand Down
8 changes: 8 additions & 0 deletions src/app/config/or_options_st.h
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,14 @@ struct or_options_t {
/** Maximum size in bytes allowed for the anyone_hosts DNS mapping file.
* A value of 0 disables the size limit. */
uint64_t DNSMappingFileMaxSize;
/** If true, automatically update the anyone_hosts DNS mapping file when
* new directory information arrives or on a periodic schedule. */
int AnyoneHostsUpdate;
/** How often (in seconds) to check for a new anyone_hosts file. */
int AnyoneHostsUpdateInterval;
/** List of .anyone service addresses to try when fetching the
* anyone_hosts file. Tried in order before the built-in defaults. */
struct config_line_t *AnyoneHostsURL;

/** If true, do not accept any requests to connect to internal addresses
* over randomly chosen exits. */
Expand Down
18 changes: 18 additions & 0 deletions src/core/mainloop/mainloop.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#include "core/or/connection_or.h"
#include "core/or/dos.h"
#include "core/or/status.h"
#include "feature/anyone/anyone_hosts_update.h"
#include "feature/client/addressmap.h"
#include "feature/client/bridges.h"
#include "feature/client/dnsserv.h"
Expand Down Expand Up @@ -1381,6 +1382,7 @@ CALLBACK(write_stats_file);
CALLBACK(control_per_second_events);
CALLBACK(second_elapsed);
CALLBACK(manage_vglite);
CALLBACK(update_anyone_hosts);

#undef CALLBACK

Expand All @@ -1406,6 +1408,9 @@ STATIC periodic_event_item_t mainloop_periodic_events[] = {
/* Update vanguards-lite once per hour, if we have networking */
CALLBACK(manage_vglite, NET_PARTICIPANT, FL(NEED_NET)),

/* Periodically update the anyone_hosts DNS mapping file. */
CALLBACK(update_anyone_hosts, CLIENT, FL(NEED_NET)),

/* XXXX Do we have a reason to do this on a callback? Does it do any good at
* all? For now, if we're dormant, we can let our listeners decay. */
CALLBACK(retry_listeners, NET_PARTICIPANT, FL(NEED_NET)),
Expand Down Expand Up @@ -1553,6 +1558,8 @@ initialize_periodic_events(void)
NAMED_CALLBACK(launch_descriptor_fetches);
NAMED_CALLBACK(check_dns_honesty);
NAMED_CALLBACK(save_state);

anyone_hosts_update_init();
}

STATIC void
Expand Down Expand Up @@ -1691,6 +1698,17 @@ manage_vglite_callback(time_t now, const or_options_t *options)
return VANGUARDS_LITE_INTERVAL;
}

/** Periodic-event callback: attempt to update the anyone_hosts DNS mapping
* file, and return the number of seconds until the next run
* (AnyoneHostsUpdateInterval, jittered). Whether an update may actually
* run (enabled, client-only, timing) is decided inside the anyone module.
*/
static int
update_anyone_hosts_callback(time_t now, const or_options_t *options)
{
return anyone_hosts_update_callback(now, options);
}

/** Perform regular maintenance tasks. This function gets run once per
* second.
*/
Expand Down
37 changes: 37 additions & 0 deletions src/core/or/connection_edge.c
Original file line number Diff line number Diff line change
Expand Up @@ -3640,6 +3640,43 @@ connection_ap_make_link(connection_t *partner,
return conn;
}

/** Like connection_ap_make_link(), but for a target that is an onion-routed
* hostname (a .anyone name) rather than a numeric address.
*
* connection_ap_make_link() queues the stream for direct circuit attachment,
* which treats the address as an exit-resolvable hostname; a .anyone name
* would reach the exit unresolved and fail. Instead, send the linked stream
* through connection_ap_handshake_rewrite_and_attach(), the same path SOCKS
* client streams take, so the name is translated via the anyone_hosts
* mapping and attached to a hidden-service circuit.
*
* Return the new entry connection, or NULL on error. On error the stream
* has already been marked for close, so the partner connection will see EOF.
*/
entry_connection_t *
connection_ap_make_link_onion(connection_t *partner,
char *address, uint16_t port,
int session_group, int isolation_flags)
{
entry_connection_t *conn =
connection_ap_make_link(partner, address, port, NULL /* digest */,
session_group, isolation_flags,
0 /* use_begindir */, 0 /* want_onehop */);
if (!conn)
return NULL;

/* Undo the direct-attach queueing and take the rewrite path instead. */
connection_ap_mark_as_non_pending_circuit(conn);
/* The rewrite path refuses onion targets unless the stream's entry
* configuration allows them; internal fetches always may. */
conn->entry_cfg.onion_traffic = 1;
if (connection_ap_handshake_rewrite_and_attach(conn, NULL, NULL) < 0) {
/* The connection has already been marked unattached and closed. */
return NULL;
}
return conn;
}

/** Notify any interested controller connections about a new hostname resolve
* or resolve error. Takes the same arguments as does
* connection_ap_handshake_socks_resolved(). */
Expand Down
5 changes: 5 additions & 0 deletions src/core/or/connection_edge.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ entry_connection_t *connection_ap_make_link(connection_t *partner,
int session_group,
int isolation_flags,
int use_begindir, int want_onehop);
entry_connection_t *connection_ap_make_link_onion(connection_t *partner,
char *address,
uint16_t port,
int session_group,
int isolation_flags);
void connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
size_t replylen,
int endreason);
Expand Down
Loading
Loading