0.4.0 - 2026-11-05
  o Security (from an audit of 0.3.0):
    - Dropping privileges: without Group, use the user's primary group
      instead of keeping group 0; set the supplementary groups to the
      user's (as initgroups(3) does) instead of keeping root's.
    - Exit test listener: at most 16 connections from one address, and 15
      seconds (not 30) to send the request. One host could take all the
      handlers and stop the exit tests. Bad requests are logged at debug
      level.
    - DNS: DNSRateLimit is now 100 by default (it was off), loopback is
      never limited, and ANY gets a single RRset (RFC 8482): the A record
      for a listed name, the SOA at the apex. A public server could be used
      to amplify spoofed floods.
    - Exit test results loaded from the state directory are cut down to the
      16 most recent addresses per relay, like new ones.
    - The statistics socket is mode 0660 (it was 0666), owned by the user
      and group we drop privileges to.

  o Tor:
    - TorControlAddress and TorSocksAddress accept Unix sockets,
      unix:/path, and IPv6 addresses, which failed to connect before.
      Debian's tor package works without changes to the torrc:
        TorControlAddress unix:/run/tor/control
        TorSocksAddress unix:/run/tor/socks
      with TorDNSEL in the debian-tor group.
    - Retry the control connection at least once a minute (it was every 5
      minutes at worst), so that a restarted Tor is noticed quickly.

  o Other:
    - The exit-addresses file is synced to disk before it replaces the old
      one, and is read correctly even if it isn't sorted.

  o Upgrading:
    - With DNSRateLimit unset, UDP is now limited to 100 queries a second
      per client network; set it to 0 for the old behaviour.
    - The statistics socket is no longer world-accessible; run whatever
      reads it (contrib/cacti-input.pl) as TorDNSEL's user or group.

0.3.1 - 2026-11-05
  o Web interface: terminating the web server (at shutdown, say) could hang
    forever if a request had just been served: the handler's exit signal
    was thrown away.

0.3.0 - 2026-10-28
  o Security (from an audit of 0.2.2):
    - Control port: never use plain COOKIE authentication, which would send
      the contents of whatever file the other end names; read the cookie file
      only if it is a regular file of exactly 32 bytes; give up on the
      handshake after 30 seconds and on QUIT after 5. Something other than
      Tor on the control port could read files, exhaust our memory with
      /dev/zero, or keep us from ever reconnecting.
    - Exit tests: each cookie is accepted once, and a relay gets at most 16
      tested exit addresses. A malicious exit could replay its test's cookie
      from other addresses (open proxies, shared NAT) and have them listed.
    - DNS over TCP: at most 8 connections per client network, 100 queries
      per connection, a 2 second idle timeout when busy, and a timeout on
      sending. One client could take all the connections.
    - The UDP rate limiter tracks at most 100,000 networks.
    - Web interface: at most 8 connections per client, and 3 seconds to send
      the request.
    - Exit test listener: a header line without a colon no longer escapes
      the request handler as an exception.
    - Startup: umask 077; refuse state and runtime directories writable by
      other users; create the PID file with O_EXCL, never through a symbolic
      link.
    - The systemd unit has more sandboxing, including a system call filter.

  o New features:
    - "exit" queries, {IP}.{zone}, the form of the Tor Project's exit list
      service: listed iff IP is a Tor exit, with the relays' fingerprints in
      TXT records. Also /api/v1/exit on the web interface.
    - DNSClientSubnet (on by default): "me" queries are about the client
      subnet a resolver gives with the EDNS client subnet option (RFC 7871).
    - HTTPTrustedProxy: believe X-Forwarded-For from these reverse proxies.
    - HTTPMetricsAllowFrom: Prometheus metrics at /metrics for these clients,
      including the number of running exits and rate-limited queries.

  o Other:
    - Root without CAP_SYS_RESOURCE (e.g. in a container) no longer fails to
      start when it can't raise the hard limit on open files.
    - The statistics socket also reports RateLimited.

0.2.2 - 2026-10-24
  o Security:
    - Authenticate to Tor's control port with SAFECOOKIE when Tor offers it
      (whenever cookie authentication is on), so that the cookie itself is
      never sent, and refuse to authenticate if Tor can't prove it knows the
      cookie. Needs cryptohash-sha256.
    - New option DNSRateLimit: answer at most that many UDP queries per
      second from each client network (IPv4 /24, IPv6 /56); of the queries
      over the limit, every second one gets a truncated response and the
      rest are dropped. Off by default.

  o DNS:
    - Answer over TCP too (RFC 7766), on the same address as UDP: queries
      may be pipelined; at most 128 connections, each closed after 10 idle
      seconds. Firewalls must let TCP to DNSListenAddress through.
    - UDP responses may be as large as the client's EDNS(0) payload size
      (at most 1232 bytes); larger ones are truncated (TC bit) instead of
      being replaced by SERVFAIL, so that clients retry over TCP.
    - Answer QCLASS * queries like class IN ones instead of with NXDOMAIN.

  o Internals:
    - DNS and SOCKS decoding are pure and total, instead of catching
      exceptions in IO.
    - The main thread and the network state manager use the strict StateT.
    - Asynchronous exceptions are recognised by SomeAsyncException.
    - ADDRMAP events: take the expiry from EXPIRES (UTC), not local time.

  o Testing:
    - QuickCheck properties for the DNS decoder (arbitrary and damaged
      input, round trips), the SOCKS and AUTHCHALLENGE parsers and the rate
      limiter. Needs QuickCheck.
    - Unit tests for DNS over TCP on real loopback connections, truncation,
      rate limiting, SAFECOOKIE and ADDRMAP; the integration test asks over
      TCP too.

0.2.1 - 2026-10-23
  o Building:
    - Don't derive Typeable, which GHC 9.12 and newer warn about.

  o Testing:
    - CI: build the "Ubuntu packages" and integration jobs with Ubuntu's
      GHC rather than the ghcup GHC the runner image puts first on the
      PATH; install chutney's Python dependencies in a virtual
      environment; test against both the last chutney with the old command
      line and current chutney; keep the logs of failed integration runs.
    - The integration test reports why chutney failed instead of exiting
      silently, and refuses working directories too deep for Tor's control
      sockets.

0.2.0 - 2026-10-20
  o Building:
    - Port to GHC 9.4 and newer (tested with 9.4.7 and 9.10.3) and current
      libraries (mtl 2.3, network 3, conduit 1.3, unix 2.8, binary 0.8). The
      code base targeted GHC 7.6.
    - Modern cabal file (library, executable, test suite); GitHub Actions CI
      over GHC 9.4 - 9.14, Ubuntu's own Haskell packages, and an end-to-end
      test against a private Tor network built with chutney.
    - Drop the libcrypto dependency: random bytes come from the kernel's
      CSPRNG device, opened before chroot.
    - Link with the threaded RTS, which uses epoll/kqueue; the select(2)
      FD_SETSIZE cap on file descriptors no longer applies.

  o Tor compatibility (tested with Tor 0.4.9.6):
    - Active exit tests work again. Tor refuses .exit hostnames on its SOCKS
      port since 0.3.x, so every test failed; tests now install a MAPADDRESS
      mapping to ADDRESS.FINGERPRINT.exit through the controller.
    - Follow NEWCONSENSUS events instead of NS events. Since Tor 0.4.8 NS
      events may never mention stable relays again, so they were dropped from
      our view after 24 hours.
    - Parse both forms of router status entries and the current descriptor
      and exit policy syntax ("private", IPv6 rules, *4/*6).

  o New features (doc/torel-design.md):
    - Query type 2, "{IP}.{listname}.list.{zone}", with lists defined by the
      new ExitList option.
    - Query type 3, "{IP}.{port}.me.{zone}", answered with a TTL of 0.
    - A web interface (HTTPListenAddress, HTTPPublicAddress) with JSON
      endpoints for every query type, /api/ip, /torbulkexitlist and
      /exit-addresses.

  o DNS:
    - Support EDNS(0) (OPT echo, BADVERS) and answer TXT queries with an
      explanation of the listing (RFC 5782).
    - Answer prefixes of valid query names with NOERROR/NODATA, which keeps
      resolvers using QNAME minimisation working.
    - IPv6 listen addresses ("[::]:53"); queries from IPv6 hosts.
    - Don't crash the decoder on unknown opcodes or response codes.

  o Bug fixes:
    - --reconfigure works again with network 3 and current bytestring.
    - Malformed values of optional config options are reported instead of
      being silently ignored.

0.1.1 - 2011-06-28
  - Correctly label the release version and Changelog.
  - Add updated website and maintainers in tordnsel.cabal.
  - Document the tordnsel init script for Debian. 

0.1.0 - 2011-06-28
  o Deployment:
    - Add support for logging messages to stdout, stderr, syslog, or to a file.
      Syslog logging should be useful for running in a chroot.
    - Display better error messages for config file parsing, directory parsing,
      Tor controller errors, I/O errors, and almost every error condition.
    - Add a man page fully documenting config options, signals, files, sockets,
      and exit codes.
    - Add support for reloading the configuration by reloading the config file
      when we receive SIGHUP, or by listening for the contents of the config
      file on a Unix domain socket. The latter is useful for running in a
      chroot, where the process can't access its own config file.
    - Implement the reload command in the sample init.d script using the new
      --reconfigure command-line option, which reloads the config file through
      a Unix domain socket for chroot-friendliness.
    - Exit gracefully when we receive SIGINT or SIGTERM.
    - Add a --verify-config command-line option for checking whether the config
      file is well-formed without starting TorDNSEL. Apply it in the sample
      init.d script.
    - Add --help and --version command-line options.

  o Reliability:
    - Implement Erlang-style thread links and monitors for error handling.
    - Refactor every thread to support a start/reconfigure/terminate API.
    - Each thread now runs in a fault-tolerant supervision hierarchy in which
      the thread is responsible for handling errors in its children, and its
      supervisor handles errors in it. If a thread dies unexpectedly, the reason
      it died is logged and its supervisor attempts to restart it when possible.

  o New required options:
    - Create a new required RuntimeDirectory option for the statistics and
      reconfigure sockets.
    - Rename the AuthoritativeZone option to ZoneOfAuthority, since name servers
      are authoritative, not zones.

  o Performance:
    - Share copies of exit policy rules and exit policies with a hash table of
      weak pointers. According to nickm, only 5% of them are distinct.
    - Squash some space leaks in network state updates.

  o Standards conformance:
    - Stop requiring that reserved bits in the DNS header be 0.

  o Active tests:
    - Replace the ConcurrentExitTests option with EnableActiveTesting, since we
      now automatically detect limits imposed on open file descriptors by
      FD_SETSIZE and resource limits.
    - Refactor the exit test initiator to keep a history of scheduled exit tests
      and dynamically adjust the rate at which tests are initiated. This should
      smooth out the pubkey crypto demands on Tor that were maxing out CPU
      utilization.
    - Make a better effort to avoid redundant testing by storing pending tests
      in a distinct queue.
    - Stop testing every node periodically between descriptor publications.
      Instead, every hour attempt to test through any exit nodes that haven't
      been successfully tested since they last published a descriptor. This
      should have a similar effect of catching nodes that slipped through an
      earlier attempted test.
    - Regenerate the exit-addresses store every time a new network status
      consensus is received instead of every 15 minutes.
    - Reduce the maximum relay age from 48 hours to 24 hours.  This should
      cut down the length of time a relay is listed in the exitlist after
      it has been disabled or changed to a non-exit relay.

  o Controller:
    - Close controller connections cleanly with the QUIT command.
    - Add support for authenticating with PROTOCOLINFO.
    - Set the new FetchDirInfoEarly option to enable fetching dir info on the
      mirror schedule, preferably from authorities.
    - Ensure that config options we set are rolled back to their previous state
      when a controller connection is closed cleanly.

0.0.6 - 2007-07-28
  - Support an optional A record for our authoritative zone.
  - Return our own NS record in the appropriate places.
  - Don't modify the group ownership of our state directory.
  - Rebuild the exit-addresses file every 15 minutes so LastStatus items stay
    up to date.
  - Ensure exit tests are scheduled when we receive new descriptors.
  - Generate cookies using OpenSSL's strong pseudo-random number generator.
  - Add a "Connection: close" header to test requests and responses.
  - Test once through each port exit policies allow, recording varying exit
    addresses for individual nodes.
  - Periodically (currently, every 150 minutes) test through every node
    marked running.
  - Make cumulative DNS load statistics available via a Unix stream socket
    located in our state directory. Add a sample data input script for Cacti.
  - Increase the test result retention period to 48 hours.
  - Ensure the controller socket is closed immediately when connect fails.
  - Rate limit exit test initiation to one node every three seconds. This
    should reduce the load we put on Tor, particularly during periodic tests.

0.0.5 - 2007-06-12
  - Set QR in responses as we did in 0.0.3 and earlier. Fixes a bug
    introduced in 0.0.4.
  - Return a SOA record when queries ask for one.
  - Ensure that the state directory has correct ownership and permissions.
  - Include a Debian-style init script and instructions for setting up
    TorDNSEL with it. (patch from Jacob Appelbaum)

0.0.4 - 2007-06-02
  - Return REFUSED instead of SERVFAIL for queries outside the authoritative
    zone.
  - Return NODATA for queries matching the authoritative zone.
  - Return NODATA for positive results when queries ask for non-A records.
  - Support queries with a type of *.
  - Return a SOA record with NXDOMAIN and NODATA responses to support
    negative caching.
  - Don't send responses that exceed 512 bytes.
  - Drop incoming messages with QR set.
  - Implement compression for domain names in responses.
  - Add a required SOARName option to specify the RNAME field in SOA records.

0.0.3 - 2007-04-27
  - Implement active testing through exit nodes to determine the IP address
    connections actually originate from. Store this information in state
    files so external scripts have access to it.
  - Always return an A record with the value 127.0.0.2 when queried about
    127.0.0.2. This conforms to the convention for testing DNSBLs.
  - Fix a bug that could kill the Tor controller thread when Tor is
    restarted. Tor closing the control connection created a race condition
    allowing an asynchronous exception to escape an exception handler and
    kill the Tor controller thread.

0.0.2 - 2007-04-14
  - Fix another bug caused by the incorrect Ord instance for PortNumber.
    We don't want to accept DNS queries from privileged ports so we can
    avoid a potential denial of service caused by an attacker spoofing our
    own IP address in the query.

0.0.1 - 2007-04-13
  - Ignore incoming descriptors with published times older than ones we
    already have.
  - Consider any router that has published a descriptor within the last 48
    hours to be running. This means we don't use network status info anymore,
    but we'll still keep track of it in case it becomes useful.
  - Set FetchUselessDescriptors so we receive descriptors for non-running
    routers.
  - Fix a bug caused by an erroneous Ord instance for PortNumber in
    Network.Socket. PortNumbers were being compared in host order even
    though they're stored in network order. This caused some queries to
    incorrectly match exit policy rules and others to miss matches.

0.0.0 - 2007-04-11
  - Initial release.
