
                        Tor Directory List Format
                         Tim Wilson-Brown (teor)

Table of Contents

    1. Scope and Preliminaries
        1.1. Format Overview
        1.2. Acknowledgements
        1.3. Format Versions
        1.4. Future Plans
    2. Format Details
        2.1. Nonterminals
        2.2. List Header
            2.2.1. List Header Format
        2.3. List Generation
            2.3.1. List Generation Format
        2.4. Directory Entry
            2.4.1. Directory Entry Format
    3. Usage Considerations
        3.1. Caching
        3.2. Retrieving Directory Information
        3.3. Fallback Reliability
    A.1. Sample Data
        A.1.1. Sample Fallback List Header
        A.1.2. Sample Fallback List Generation
        A.1.3. Sample Fallback Entries

1. Scope and Preliminaries

   This document describes the format of Tor's directory lists, which are
   compiled and hard-coded into the tor binary. There is currently one
   list: the fallback directory mirrors. This list is also parsed by other
   libraries, like stem and metrics-lib. Alternate Tor implementations can
   use this list to bootstrap from the latest public Tor directory
   information.

   The FallbackDir feature was introduced by proposal 210, and was first
   supported by Tor in Tor version 0.2.4.7-alpha. The first hard-coded
   list was shipped in 0.2.8.1-alpha.

   The hard-coded fallback directory list is located in the tor source
   repository at:

       src/app/config/fallback_dirs.inc

   In Tor 0.3.4 and earlier, the list is located at:

       src/or/fallback_dirs.inc

   This document describes version 2.0.0 and later of the directory list
   format.

   Legacy, semi-structured versions of the fallback list were released with
   Tor 0.2.8.1-alpha through Tor 0.3.1.9. We call this format version 1.
   Stem and Relay Search have parsers for this legacy format.

1.1. Format Overview

   A directory list is a C code fragment containing an array of C string
   constants. Each double-quoted C string constant is a valid torrc
   FallbackDir entry. Each entry contains various data fields.

   Directory lists do not include the C array's declaration, or the array's
   terminating NULL. Entries in directory lists do not include the
   FallbackDir torrc option. These are handled by the including C code.

   Directory lists also include C-style comments and whitespace. The
   presence of whitespace may be significant, but the amount of whitespace
   is never significant. The type of whitespace is not significant to the
   C compiler or Tor C string parser. However, other parsers MAY rely on
   the distinction between newlines and spaces. (And that the only
   whitespace characters in the list are newlines and spaces.)

   The directory entry C string constants are split over multiple lines for
   readability. Structured C-style comments are used to provide additional
   data fields. This information is not used by Tor, but may be of interest
   to other libraries.

   The order of directory entries and data fields is not significant,
   except where noted below.

1.2. Acknowledgements

   The original fallback directory script and format was created by
   weasel. The current script uses code written by gsathya & karsten.

   This specification was revised after feedback from:

     Damian Johnson ("atagar")
     Iain R. Learmonth ("irl")

1.3. Format Versions

   The directory list format uses semantic versioning: https://semver.org

   In particular:
     * major versions are used for incompatible changes, like
       removing non-optional fields
     * minor versions are used for compatible changes, like adding
       fields
     * patch versions are for bug fixes, like fixing an
       incorrectly-formatted Summary item

   1.0.0 - The legacy fallback directory list format

   2.0.0 - Adds name and extrainfo structured comments, and section separator
           comments to make the list easier to parses. Also adds a source list
           comment to the header.

   3.0.0 - Modifies the format of the source list comment.

1.4. Future Plans

   Tor also has an auth_dirs.inc file, but it is not yet in this format.
   Tor uses slightly different formats for authorities and fallback
   directory mirrors, so we will need to make some changes to tor so that
   it parses this format. (We will also need to add authority-specific
   information to this format.) See #24818 for details.

   We want to add a torrc option so operators can opt-in their relays as
   fallback directory mirrors. This gives us a signed opt-in confirmation.
   (We can also continue to accept whitelist entries, and do other checks.)
   We need to write a short proposal, and make some changes to tor and the
   fallback update script. See #24839 for details.

2. Format Details

   Directory lists contain the following sections:

     - List Header (exactly once)
     - List Generation (exactly once, may be empty)
     - Directory Entry (zero or more times)

   Each section (or entry) ends with a separator.

2.1. Nonterminals

   The following nonterminals are defined in the Onionoo details document
   specification:

     dir_address
     fingerprint
     nickname

   See https://metrics.torproject.org/onionoo.html#details

   The following nonterminals are defined in the "Tor directory protocol"
   specification in dir-spec.txt:

     Keyword
     ArgumentChar
     NL      (newline)
     SP      (space)
     bool    (must not be confused with Onionoo's JSON "boolean")

   We derive the following nonterminals from Onionoo and dir-spec.txt:

     ipv4_or_port ::= port from an IPv4 or_addresses item

       The ipv4_or_port is the port part of an IPv4 address from the
       Onionoo or_addresses list.

     ipv6_or_address ::= an IPv6 or_addresses item

       The ipv6_or_address is an IPv6 address and port from the Onionoo
       or_addresses list. The address MAY be in the canonical RFC 5952
       IPv6 address format.

   A key-value pair:

     value ::= Zero or more ArgumentChar, excluding the following strings:
                 * a double quotation mark (DQUOTE), and
                 * the C comment terminators ("/*" and "*/").

               Note that the C++ comment ("//") and equals sign ("=") are
               not excluded, because they are reserved for future use in
               base64 values.

     key_value ::= Keyword "=" value

   We also define these additional nonterminals:

     number ::= An optional negative sign ("-"), followed by one or more
                numeric characters ([0-9]), with an optional decimal part
                (".", followed by one or more numeric characters).

     separator ::= "/*" SP+ "=====" SP+ "*/"

2.2. List Header

   The list header consists of a number of key-value pairs, embedded in
   C-style comments.

2.2.1. List Header Format

     "/*" SP+ "type=" Keyword SP+ "*/" SP* NL

       [At start, exactly once.]

       The type of directory entries in the list. Parsers SHOULD exit with
       an error if this is not the first line of the list, or if the value
       is anything other than "fallback".

     "/*" SP+ "version=" version_number SP+ "*/" SP* NL

       [In second position, exactly once.]

       The version of the directory list format.

       version_number is a semantic version, see the "Format Versions"
       section for details.

       Version 1.0.0 represents the undocumented, legacy fallback list
       format(s). Version 2.0.0 and later are documented by this
       specification.

     "/*" SP+ "timestamp=" number SP+ "*/" SP* NL

       [Exactly once.]

       A positive integer that indicates when this directory list was
       generated. This timestamp is guaranteed to increase for every
       version 2.0.0 and later directory list.

       The current timestamp format is YYYYMMDDHHMMSS, as an integer.

     "/*" SP+ "source=" Keyword ("," Keyword)* SP+ "*/" SP* NL

       [Zero or one time.]

       A list of the sources of the directory entries in the list.

       As of version 3.0.0, the possible sources are:
         * "offer-list" - the fallback_offer_list file in the fallback-scripts
                          repository.
         * "descriptor" - one or more signed descriptors, each containing an
                          "offer-fallback-dir" line. This feature will be
                          implemented in ticket #24839.
         * "fallback"   - a fallback_dirs.inc file from a tor repository.
                          Used in check_existing mode.

       Before #24839 is implemented, the default is "offer-list". During the
       transition to signed offers, it will be "descriptor,offer-list".
       Afterwards, it will be "descriptor".

       In version 2.0.0, only one source name was allowed after "source=",
       and the deprecated "whitelist" source name was used instead of
       "offer-list".

       This line was added in version 2.0.0 of this specification. The format
       of this line was modified in version 3.0.0 of this specification.

     "/*" SP+ key_value SP+ "*/" SP* NL

       [Zero or more times.]

       Future releases may include additional header fields. Parsers MUST NOT
       rely on the order of these additional fields. Additional header fields
       will be accompanied by a minor version increment.

     separator SP* NL

       The list header ends with the section separator.

2.3. List Generation

   The list generation information consists of human-readable prose
   describing the content and origin of this directory list. It is contained
   in zero or more C-style comments, and may contain multi-line comments and
   uncommented C code.

   In particular, this section may contain C-style comments that contain
   an equals ("=") character. It may also be entirely empty.

   Future releases may arbitrarily change the content of this section.
   Parsers MUST NOT rely on a version increment when the format changes.

2.3.1. List Generation Format

   In general, parsers MUST NOT rely on the format of this section.

   Parsers MAY rely on the following details:

   The list generation section MUST NOT be a valid directory entry.

   The list generation summary MUST end with a section separator:

     separator SP* NL

   There MUST NOT be any section separators in the list generation
   section, other than the terminating section separator.

2.4. Directory Entry

   A directory entry consists of a C string constant, and one or more
   C-style comments. The C string constant is a valid argument to the
   DirAuthority or FallbackDir torrc option. The section also contains
   additional key-value fields in C-style comments.

   The list of fallback entries does not include the directory
   authorities: they are in a separate list. (The Tor implementation combines
   these lists after parsing them, and applies the DirAuthorityFallbackRate
   to their weights.)

2.4.1. Directory Entry Format

     If a directory entry does not conform to this format, the entry SHOULD
     be ignored by parsers.

     DQUOTE dir_address SP+ "orport=" ipv4_or_port SP+
       "id=" fingerprint DQUOTE SP* NL

       [At start, exactly once, on a single line.]

       This line consists of the following fields:

       dir_address

         An IPv4 address and DirPort for this directory, as defined by
         Onionoo. In this format version, all IPv4 addresses and DirPorts
         are guaranteed to be non-zero. (For IPv4 addresses, this means
         that they are not equal to "0.0.0.0".)

       ipv4_or_port

         An IPv4 ORPort for this directory, derived from Onionoo. In this
         format version, all IPv4 ORPorts are guaranteed to be non-zero.

       fingerprint

         The relay fingerprint of this directory, as defined by Onionoo.
         All relay fingerprints are guaranteed to have one or more non-zero
         digits.

     Note:

       Each double-quoted C string line that occurs after the first line,
       starts with space inside the quotes. This is a requirement of the
       Tor implementation.

     DQUOTE SP+ "ipv6=" ipv6_or_address DQUOTE SP* NL

       [Zero or one time.]

       The IPv6 address and ORPort for this directory, as defined by
       Onionoo. If present, IPv6 addresses and ORPorts are guaranteed to be
       non-zero. (For IPv6 addresses, this means that they are not equal to
       "[::]".)

     DQUOTE SP+ "weight=" number DQUOTE SP* NL

       [Zero or one time.]

       A non-negative, real-numbered weight for this directory.
       The default fallback weight is 1.0, and the default
       DirAuthorityFallbackRate is 1.0 in legacy Tor versions, and 0.1 in
       recent Tor versions.

       weight was removed in version 2.0.0, but is documented because it
       may be of interest to libraries implementing Tor's fallback
       behaviour.

     DQUOTE SP+ key_value DQUOTE SP* NL

       [Zero or more times.]

       Future releases may include additional data fields in double-quoted
       C string constants. Parsers MUST NOT rely on the order of these
       additional fields. Additional data fields will be accompanied by a
       minor version increment.

     "/*" SP+ "nickname=" nickname* SP+ "*/" SP* NL

       [Exactly once.]

       The nickname for this directory, as defined by Onionoo. An
       empty nickname indicates that the nickname is unknown.

       The first fallback list in the 2.0.0 format had nickname lines, but
       they were all empty.

     "/*" SP+ "extrainfo=" bool SP+ "*/" SP* NL

       [Exactly once.]

       An integer flag that indicates whether this directory caches
       extra-info documents. Set to 1 if the directory claimed that it
       cached extra-info documents in its descriptor when the list was
       created. 0 indicates that it did not, or its descriptor was not
       available.

       The first fallback list in the 2.0.0 format had extrainfo lines, but
       they were all zero.

     "/*" SP+ key_value SP+ "*/" SP* NL

       [Zero or more times.]

       Future releases may include additional data fields in C-style
       comments. Parsers MUST NOT rely on the order of these additional
       fields. Additional data fields will be accompanied by a minor version
       increment.

     separator SP* NL

       [Exactly once.]

       Each directory entry ends with the section separator.

     "," SP* NL

       [Exactly once.]

       The comma terminates the C string constant. (Multiple C string
       constants separated by whitespace or comments are coalesced by
       the C compiler.)

3. Usage Considerations

   This section contains recommended library behaviours. It does not affect
   the format of directory lists.

3.1. Caching

   The fallback list typically changes once every 6-12 months. The data in
   the list represents the state of the fallback directory entries when the
   list was created. Fallbacks can and do change their details over time.

   Libraries SHOULD parse and cache the most recent version of these lists
   during their build or release processes. Libraries MUST NOT retrieve the
   lists by default every time they are deployed or executed.

   The latest fallback list can be retrieved from:

     https://gitweb.torproject.org/tor.git/plain/src/or/fallback_dirs.inc

   Libraries MUST NOT rely on the availability of the server that hosts
   these lists.

   The list can also be retrieved using:

     git clone https://git.torproject.org/tor.git

   If you just want the latest list, you may wish to perform a shallow
   clone.

3.2. Retrieving Directory Information

   Some libraries retrieve directory documents directly from the Tor
   Directory Authorities. The directory authorities are designed to support
   Tor relay and client bootstrap, and MAY choose to rate-limit library
   access. Libraries MAY provide a user-agent in their requests, if they
   are not intended to support anonymous operation. (User agents are a
   fingerprinting vector.)

   Libraries SHOULD consider the potential load on the authorities, and
   whether other sources can meet their needs.

   Libraries that require high-uptime availability of Tor directory
   information should investigate the following options:

     * OnionOO: https://metrics.torproject.org/onionoo.html
       * Third-party OnionOO mirrors are also available
     * CollecTor: https://collector.torproject.org/
     * Fallback Directory Mirrors

   Onionoo and CollecTor are typically updated every hour on a regular
   schedule. Fallbacks update their own directory information at random
   intervals, see dir-spec for details.

3.3. Fallback Reliability

   The fallback list is typically regenerated when the fallback failure
   rate exceeds 25%. Libraries SHOULD NOT rely on any particular fallback
   being available, or some proportion of fallbacks being available.

   Libraries that use fallbacks MAY wish to query an authority after a
   few fallback queries fail. For example, Tor clients try 3-4 fallbacks
   before trying an authority.

A.1. Sample Data

   A sample version 2.0.0 fallback list is available here:

     https://trac.torproject.org/projects/tor/raw-attachment/ticket/22759/fallback_dirs_new_format_version.4.inc

   A sample transitional version 2.0.0 fallback list is available here:

     https://raw.githubusercontent.com/teor2345/tor/fallback-format-2-v4/src/or/fallback_dirs.inc

A.1.1. Sample Fallback List Header

/* type=fallback */
/* version=2.0.0 */
/* ===== */

A.1.2. Sample Fallback List Generation

/* Whitelist & blacklist excluded 1326 of 1513 candidates. */
/* Checked IPv4 DirPorts served a consensus within 15.0s. */
/*
Final Count: 151 (Eligible 187, Target 392 (1963 * 0.20), Max 200)
Excluded: 36 (Same Operator 27, Failed/Skipped Download 9, Excess 0)
Bandwidth Range: 1.3 - 40.0 MByte/s
*/
/*
Onionoo Source: details Date: 2017-05-16 07:00:00 Version: 4.0
URL: https:onionoo.torproject.orgdetails?fields=fingerprint%2Cnickname%2Ccontact%2Clast_changed_address_or_port%2Cconsensus_weight%2Cadvertised_bandwidth%2Cor_addresses%2Cdir_address%2Crecommended_version%2Cflags%2Ceffective_family%2Cplatform&flag=V2Dir&type=relay&last_seen_days=-0&first_seen_days=30-
*/
/*
Onionoo Source: uptime Date: 2017-05-16 07:00:00 Version: 4.0
URL: https:onionoo.torproject.orguptime?first_seen_days=30-&flag=V2Dir&type=relay&last_seen_days=-0
*/
/* ===== */

A.1.3. Sample Fallback Entries

"176.10.104.240:80 orport=443 id=0111BA9B604669E636FFD5B503F382A4B7AD6E80"
/* nickname=foo */
/* extrainfo=1 */
/* ===== */
,
"5.9.110.236:9030 orport=9001 id=0756B7CD4DFC8182BE23143FAC0642F515182CEB"
" ipv6=[2a01:4f8:162:51e2::2]:9001"
/* nickname= */
/* extrainfo=0 */
/* ===== */
,
