This document describes the design of the DoT and DoH implementation for BIND9. This will be developed in the 9.17 development branch, and backported to 9.16 (with some exceptions). Unfortunately external Gitlab users cannot comment directly on wiki pages, but comments are welcome here: #1144/
BIND 9.16 introduced the new framework for processing incoming requests - netmgr. Previously, the code responsible for processing incoming packets and e.g. reading from sockets and accepting new TCP connections was deeply intertwined - which made it complex, error-prone and made it very hard to add new transports to the code. The idea behind netmgr is to separate the transport layer from the packet processing layer completely.
The code currently supports Do53 for both UDP and TCP - and from the packet processing code perspective there's one single transport-agnostic function that gets a buffer with whole DNS packet (with the 2-byte length stripped for TCP). The DoT and DoH code will also be based on that principle - the transport layer processing will be kept in netmgr, while the query processing code will just receive a buffer with a binary packet.
The netmgr is layered in design - the tcpdns module which processes incoming TCP packets uses the netmgr itself, and it can use any type of streaming socket the netmgr supports - TCP for now, TLS for DoT.
The implementation will use two external libraries - OpenSSL for encryption and NGHTTP2 for http/2. OpenSSL was chosen because it's already used extensively in BIND9 code (and using two crypto libraries simultaneously doesn't make any sense), NGHTTP2 was chosen as the most popular and well-maintained implementation of http/2 protocol - and DoH mandates http/2 usage.
The TLS layer will be added as another streaming socket type to netmgr - along with currently implemented TCP. The only difference will be in new connect - isc_nm_connecttls(), and listen - isc_nm_listentls() functions which will accept TLS context as one of their parameters. Additional generic functions will be added to allow the client code to e.g. get information about connections' TLS parameters. The TLS layer will be using OpenSSL library, using its BIO mechanism to provide IO over netmgr stream sockets.
As it was stated above, the tcpdns module uses netmgr itself to communicate over any streaming socket supported by netmgr. That makes adding TLS support quite trivial - the isc_nm_listentcpdns() function will accept additional TLS context parameter which, if not null, will make the function call isc_nm_listentls() instead of isc_nm_listentcp() - the rest will be unchanged as it uses generic stream socket functions.
DoT client will be built into dig/delv - it'll be using netmgr isc_nm_connecttls() function and then use the acquired socket as any other stream socket. The +dot option will tell dig to use DoT and set the default port to 853.
The http/2 layer will be built on top of any streaming socket supported by the netmgr - although the HTTP2 standard itself does not mandate TLS usage, in practice all clients support it and some don't support HTTP2 over an unencrypted connection at all.
Internally the server will be based on nghttp2 library, externally it will be based on URI-callback model - the client (DoH server, statistics channel server) will launch a listener and then add callbacks to specific URIs, keeping the HTTP processing itself separate.
BIND9 has a very limited http/1 server built in - it's used to provide statistics channel in JSON and XML formats. With a proper http/2 server, built-in statistics channel will be served with it too
This feature will not be backported to 9.16.
DoH server will be using netmgr http/2 on one side, and the libns client library - specifically, ns_client_request() function, on the other - when an http request is received it will parse the incoming packet to a binary buffer, pass it to request processing function and, when the response is ready, pass it on to http server to be sent to the requesting client.
when BIND 9 is compiled with JSON-C library, the DoH server in BIND 9 should be able to also serve application/dns-json payload.
DoH client will be built into dig/delv, using netmgr http2 layer (nghttp2 library provides both http2 client and server). DoH usage will be enabled by the '@<server_addr>' option - e.g. dig isc.org @https://public-dns.isc.org/query
DoT and DoH will have to extend BIND9 configuration syntax. Since it's virtually impossible to change the option syntax while keeping backward compatibility the proposed design might change after consulting the community.
Since TLS will be used by various services, it'd be convenient to introduce a single configuration option describing TLS parameters - most notably, key and certificate file. The proposed syntax for the option is:
I am convinced that we should redesign listen-on to be IP version agnostic (and adjust other options v4 vs v6 accordingly) in BIND 9.17+, in BIND 9.16 we need both listen-on and listen-on-v6 (@ondrej)
Additional 'dot' and 'doh' performance counters will be added, similar to the ones for TCP. It's important to note, that since DoT and DoH use TCP as its underlying transport, e.g. DoT connections will be also counted as TCP connections.
Conformance testing will be performed using existing tools that support DoT and DoH - such as kdig. Internally, system tests (most likely based on the current 'tcp' test) will be created.
for system tests, the new pytest framework should be used - requests + dnspython could be used to glue the DoH testing client, and hyper-h2 and dnspython could be used to glue the DoH testing server.
The DoT performance will be tested using flamethrower - a DNS performance testing tool with DoT support.
The DoH performance will be tested using JMeter - an HTTP performance tool.
what about bulldohzer for DoH performance testing?