Introduction

The current mechanism for resolving host names to IP addresses for Traffic Server is contained the HostDB and DNS
libraries. These take hostnames and provide IP addresses for them.

The current implementation is generally considered inadequate, both from a functionality point of view and difficulty in
working with it in other parts of Traffic Server. As Traffic Server is used in more complex situtations this inadequacy
presents increasing problems.

Goals

Updating the host name resolution (currently referred to as "HostDB") has several functions goals

  • Enable additional processing layers to be easily added.
  • Enable plugins to directly access the name resolution logic
  • Enable plugins to provide name resolution
  • Asynchronous (immediate resolve or callback on block)
  • Minimize allocations – in particular no allocations for cached resolutions
  • Simplify interactions with the resolution, particularly with regard to nameservers, origin server failover, and
    address family handling.

It is also necessary to support a number of specific features that are either currently available or strongly desired.

  • SplitDNS or its equivalent
  • Use of a hosts file (e.g. ``/etc/hosts``)
  • Simultaneous IPv4 and IPv6 queries
  • IP family control
  • Negative caching
  • Server connection failures
  • Query failures
  • Nameserver failures.
  • Address validity time out control
  • Address round robin support
  • SRV record support (weighted records)
  • Nameserver round robin
  • Plugin access to nameserver data (add, remove, enumerate)
  • Plugin provision of resolvers.
  • Hooks for plugin detection / recovery from resolution events.

One issue is persistence of the cached resolutions. This creates problems for the current implementation (because of
size limits it imposes on the cached data) but also allows for quicker restarts in a busy environment.

Basics

The basic design is to separate the functionality into chainable layers so that a resolver with the desired attributes
can be assembled from those layers. The core interface is that of a lazy iterator. This object returns one of four
results when asked for an address

  • An IP address
  • Done(no more addresses are available)
  • Wait(an address may be available in the future)
  • Fail (no address is available and none will be so in the future)

Each layer (except the bottom) uses this API and also provides it. This enables higher level logic such as the state
machine to simply use the resolver as a list without having to backtrack states in the case of failures, or have special
cases for different resolution sources.

To perform a resolution, a client creates a query object (potentially on the stack), initializes it with the required
data (at least the hostname) and then starts the resolution. Methods on the query object allow its state and IP address
data to be accessed.

Required Resolvers

Nameserver
A bottom level resolver that directly queries a nameserver for DNS data. This contains much of the functionality
currently in the iocore/dns directory.

SplitDNS
A resolver that directs requests to one of several resolvers. To emulate current behavior these would be Nameserver
instances. This should also handle splitting based on IPv4 vs. IPv6 queries.

NameserverGroup
A grouping mechanism for Nameserver instances that provides failover, round robin, and ordering capabilities. It may be
reasonable to merge this with the SplitDNS resolver.

HostFile
A resolver that uses a local file to resolve names.

AddressCache
A resolver that also has a cache for resolution results. It requires another resolver instance to perform the actual
resolution.

Preloaded
A resolver that can contain one or more explicitly set IP addresses which are returned. When those are exhausted it
falls back to another resolver.

Configuration

To configuration the resolution, each resolver would be assigned a tag. It is not, however, sufficient to simply provide
the list of resolver tags because some resolvers require additional configuration. Unfortunately this will likely
require a separate configuration file outside of records.config, although we would be able to remove
splitdns.config. In this case we would need chain start / end markers around a list of resolver tags. Each tag
would the be able to take additional resolver configuration data. For instance, for a SplitDNS resolver the nameservers.

Examples

Transparent operations would benefit from the Preloaded resolver. This would be loaded with the origin host address
provided by the client connection. This could be done early in processing and then no more logic would be required to
skip DNS processing as it would happen without additional action by the state machine. It would handle the problem of de
facto denial of service if an origin server becomes unavailable in that configuration, as Preloaded would switch to
alternate addresses automatically.

Adding host file access would be easier as well, as it could be done in a much more modular fashion and then added to
the stack at configuration time. Whether such addresses were cached would be controlled by chain arrangement rather yet
more configuration knobs.

The default configuration would be Preloaded : AddressCache : Nameserver.

In all cases the state machine makes requests against the request object to get IP addresses as needed.

Issues

Request object allocation

The biggest hurdle is being able to unwind a resolver chain when a block is encountered. There are some ways to deal with this.

1) Set a maximum resolver chain length and declare the request instance so that there is storage for state for that many
resolvers. If needed and additional value of maximum storage per chain could be set as well. The expected number of
elements in a chain is expected to be limited, 10 would likely be a reasonable limit. If settable at source
configuration time this should be sufficient.

2) Embed class allocators in resolver chains and mark the top / outermost / first resolver. The maximum state size for a
resolution can be calculated when the chain is created and then the top level resolver can use an allocation pool to
efficiently allocate request objects. This has an advantage that with a wrapper class the request object can be passed
along cheaply. Whether that's an advantage in practice is unclear.

Plugin resolvers

If plugins can provide resolvers, how can these can integrated in to existing resolver chains for use by the HTTP SM for
instance?

Feedback

It should be possible for a client to provide feedback about addresses (e.g., the origin server at this address is not
available). Not all resolvers will handle feedback but some will and that must be possible.

Related to this is that caching resolvers (such as AddressCache) must be able to iterator over all resolved addresses
even if their client does not ask for them. In effect they must background fill the address data.