Skip to content

WIP: dns auto-update feature#145

Open
jim-toth wants to merge 11 commits into
mainfrom
anyone-dns-autoupdate
Open

WIP: dns auto-update feature#145
jim-toth wants to merge 11 commits into
mainfrom
anyone-dns-autoupdate

Conversation

@jim-toth

@jim-toth jim-toth commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds automatic updating of the anyone_hosts DNS mapping file. The client now fetches a fresh copy of the mapping from the .anyone DNS service nodes — triggered when new directory information arrives and/or on a periodic schedule — so operators no longer need to ship/update the file manually.

What's included

New feature: anyone_hosts auto-update

  • New periodic + consensus-triggered fetch logic in src/feature/anyone/anyone_hosts_update.c.
  • Fetches run as anonymized streams over a 3-hop circuit to a .anyone onion service (plain HTTP on port 80), with an ordered URL fallback list (config → previously saved mapping addresses → built-in default).
  • Response handling enforces a size cap (DNSMappingFileMaxSize), a configurable signature policy, and writes the file atomically.
  • New directory purpose DIR_PURPOSE_FETCH_ANYONE_HOSTS wired through dirclient/dircommon.

New configuration options

  • AnyoneHostsUpdate, AnyoneHostsURL, AnyoneHostsUpdateInterval, AnyoneHostsFetchPath, AnyoneHostsUpdateTrigger, AnyoneHostsSignatureRequirement.

Directory client: name-based onion routing

  • Added an anon_onion_address field + directory_request_set_anon_onion_address() setter so a directory request can be routed to an onion service by name rather than by IP. directory_initiate_request() uses this address for the connection and skips the numeric-address requirement for these fetches.

Documentation

  • DNSMappingFileMaxSize now documents bit-based units (KBits/MBits/GBits/TBits), consistent with other MEMUNIT options.
  • Clarified that DNSMappingFileMaxSize only limits loading an existing file; a missing file is always recreated with the default mapping.

Code review fixes (this revision)

  • Renamed the module callback to anyone_hosts_update_callback to resolve a symbol collision with the mainloop wrapper.
  • Replaced the unused X-Anyone-Onion header approach with proper name-based onion routing so fetches actually launch.
  • Restricted mapping-line parsing to entries ending in .anyone.
  • Corrected the scheduling doc comment and added a missing confline.h include (build fix).

Testing

  • Builds cleanly and the full unit suite passes: 1489 tests ok (41 skipped) (Debian bookworm dev container).

Manual testing checklist

Setup

  • Build with unit tests enabled and start a client with AnyoneHostsUpdate 1.
  • Confirm a baseline anyone_hosts file exists (or is recreated with the default mapping when missing).
  • Set Log info (or notice) to observe fetch/scheduling log lines.

Periodic trigger

  • With AnyoneHostsUpdateTrigger periodic and a short AnyoneHostsUpdateInterval, verify a fetch is launched after the interval elapses.
  • Confirm the anyone_hosts file is updated on disk (mtime/contents change) after a successful fetch.
  • Confirm only one fetch runs at a time (no overlapping/duplicate fetches while one is in progress).

Consensus trigger

  • With AnyoneHostsUpdateTrigger consensus, verify a fetch is kicked when fresh directory info / a new consensus arrives.
  • With AnyoneHostsUpdateTrigger both, verify both paths can trigger a fetch.

URL fallback ordering

  • With a valid AnyoneHostsURL set, verify it is tried first.
  • Point AnyoneHostsURL at an unreachable/invalid .anyone address; verify it falls back to addresses from the saved mapping, then the built-in default.
  • Verify the minimum retry interval is honored after a failed attempt (no rapid retry storm).

Routing

  • Confirm the fetch goes out as an anonymized stream (3-hop) to the onion address, not a direct/begindir connection.
  • Confirm requests are routed by .anyone name (no spurious "without a remote DirPort" warnings for these fetches).

Size cap & signature policy

  • Serve a file larger than DNSMappingFileMaxSize; verify it is rejected and the existing file is left intact.
  • AnyoneHostsSignatureRequirement strict: verify an unsigned/invalid-signature file is rejected.
  • AnyoneHostsSignatureRequirement verify: verify a valid signature is accepted and an invalid one rejected.
  • AnyoneHostsSignatureRequirement any: verify the file is accepted regardless of signature.
  • Verify writes are atomic (interrupting a fetch leaves the previous file uncorrupted).

Disable / config validation

  • With AnyoneHostsUpdate 0, verify no fetches are scheduled or launched.
  • Provide invalid values for AnyoneHostsUpdateTrigger / AnyoneHostsSignatureRequirement; verify config validation rejects them with a clear error.

Documentation

  • Verify the man page renders the new options and the updated DNSMappingFileMaxSize wording.

Copilot AI review requested due to automatic review settings June 3, 2026 13:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an automatic update mechanism for the anyone_hosts DNS mapping file, integrating it into the directory-client response handling and the mainloop periodic/consensus-triggered flow.

Changes:

  • Add a new directory connection purpose for fetching anyone_hosts and route it through the dirclient response-handling path.
  • Introduce a new feature/anyone module to schedule/trigger anyone_hosts fetch attempts (periodic + consensus-triggered).
  • Add new configuration options and manpage/docs to control auto-update behavior and signature acceptance policy.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/include.am Wires the new feature/anyone build include into the overall build.
src/feature/dircommon/directory.h Adds a new DIR_PURPOSE_FETCH_ANYONE_HOSTS purpose constant.
src/feature/dircommon/directory.c Treats the new purpose as requiring anonymity.
src/feature/dirclient/dirclient.c Adds request/response handling for fetching, verifying, and atomically writing the anyone_hosts file; triggers update kick after consensus.
src/feature/anyone/include.am Adds build rules for the new anyone-hosts update module.
src/feature/anyone/anyone_hosts_update.h Declares the anyone-hosts update subsystem API.
src/feature/anyone/anyone_hosts_update.c Implements URL list building and the periodic/consensus-triggered fetch initiation logic.
src/core/mainloop/mainloop.c Registers a new periodic event and initializes the anyone-hosts update subsystem.
src/app/config/or_options_st.h Adds new config fields controlling anyone-hosts auto-update and signature policy.
src/app/config/config.c Adds default values and validation for the new configuration options.
doc/man/anon.1.txt Documents new options and expands documented units for DNSMappingFileMaxSize.
changes/anyone-hosts-autoupdate Adds a changelog entry for the new feature and documentation updates.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/core/mainloop/mainloop.c
Comment thread src/feature/anyone/anyone_hosts_update.c Outdated
Comment thread src/feature/anyone/anyone_hosts_update.c
Comment thread src/feature/anyone/anyone_hosts_update.c Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 8 comments.

Comment thread src/feature/anyone/anyone_hosts_update.c
Comment thread src/feature/anyone/anyone_hosts_update.c
Comment thread src/feature/anyone/anyone_hosts_update.c
Comment thread src/feature/anyone/anyone_hosts_update.c
Comment thread src/feature/anyone/anyone_hosts_update.c
Comment on lines +214 to +219
fetch_in_progress = 1;
last_attempt_time = now;

directory_initiate_request(req);
directory_request_free(req);

Comment thread src/feature/dirclient/dirclient.c
Comment thread src/core/mainloop/mainloop.c Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Comment thread src/app/config/config.c
Comment thread src/feature/dirclient/dirclient.c Outdated
/* Atomically write the file using a temp file + rename. */
char *hosts_fname = get_datadir_fname("anyone_hosts");
char *hosts_fname_tmp = get_datadir_fname("anyone_hosts.tmp");
int write_ok = (write_str_to_file(hosts_fname_tmp, body, 0) == 0);

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Comment thread src/feature/dircommon/directory.h
Comment thread src/feature/dirclient/dirclient.c Outdated
Comment thread src/feature/anyone/anyone_hosts_update.c Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Comment thread src/feature/anyone/anyone_hosts_update.c
Comment thread src/core/mainloop/mainloop.c
Comment thread src/feature/dirclient/dirclient.c

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Comment thread src/core/mainloop/mainloop.c
Comment thread src/app/config/config.c
jim-toth and others added 2 commits June 19, 2026 00:56
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Jim Toth <jim@memeticblock.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Jim Toth <jim@memeticblock.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Comment thread src/app/config/config.c
Comment on lines +180 to +185
/**
* Launch one fetch if conditions are met. Called both from the consensus
* hook (anyone_hosts_update_maybe_kick) and from the periodic callback.
*/
static void
maybe_launch_fetch(time_t now)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Jim Toth <jim@memeticblock.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.

Comment thread src/app/config/config.c
Comment on lines +4098 to +4103
if (!options->AnyoneHostsFetchPath ||
!strlen(options->AnyoneHostsFetchPath) ||
options->AnyoneHostsFetchPath[0] != '/') {
REJECT("AnyoneHostsFetchPath must be a non-empty absolute path "
"beginning with '/'.");
}
Comment on lines +100 to +113
SMARTLIST_FOREACH_BEGIN(_lines, const char *, _line) { \
const char *_sp = strchr(_line, ' '); \
if (_sp && *(_sp + 1) && \
strcmpstart(_line, "anyone-hosts-") != 0) { \
const char *_addr = _sp + 1; \
size_t _alen = strlen(_addr); \
if (_alen >= 7 && !strcmp(_addr + _alen - 7, ".anyone")) \
smartlist_add(urls, tor_strdup(_addr)); \
} \
} SMARTLIST_FOREACH_END(_line); \
SMARTLIST_FOREACH(_lines, char *, _s, tor_free(_s)); \
smartlist_free(_lines); \
tor_free(_copy); \
} while (0)
int idx = current_url_index % smartlist_len(urls);
const char *onion_addr = smartlist_get(urls, idx);

log_info(LD_DIR, "Launching anyone_hosts fetch from %s", onion_addr);
Comment thread src/feature/anyone/anyone_hosts_update.c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants