If you’ve been following the development of HTTP/3 protocol, you might have also noticed that there is ongoing process on IETF for adding two new DNS resource record types: SVCB and HTTPS. TL;DR; these allow you to announce some details about your service and reduces DNS and TCP round trips while negotiating, so least in theory these help your site to work faster.

As I noticed CloudFlare announcing on Twitter today that they’re now pushing HTTPS DNS records to their customers, I decided that maybe I should too. CloudFlare has quite extensive blog post on this topic, I’ll just focus here on DIY part, ie. how to setup these records with bind9. You might want to read their blog first, as it gives much better insight.

So, with these records your DNS server can announce extra details about your site to your visitors:

Oh, hello visitor! Did you know, that site you’re about to visit supports HTTP/3 (and HTTP/2), you should connect directly with HTTP/3 to somehost.myserviceprovider.net.

And yes, HTTPS record also allows you to define other domain that actually provides the service, and which the client should connect directly. In other words, while APEX can’t be CNAME, there is now (or in future) a way to circumvent that limitation. And… it also should work with HTTP/2, when your device begins to support that.

Unfortunately, as far as I know currently this feature is only supported by iOS 14… but that’s not reason to not try something new. :) (HTTPSVC Status tracker for Chrome)

As bind9 version I’m using (9.11) lacks support for these new RR types, I’m going to use the RFC3597: Handling of Unknown DNS Resource Record (RR) Types -way to set up zone. (For the record, I’m not aware if newer bind9 versions support HTTPS/SVCB records, there is one unmerged merge request at their GitLab repository.)

As I’m not going to write extensive tutorial, I’ll skip some parts, and borrow something from CloudFlare blog: let’s assume we’d like to set up this record, but our DNS server doesn’t support that yet:

example.com 3600 IN HTTPS 1 . alpn=”h3,h2”
;                   ^--------------------------- RRTYPE
;                         ^--------------------- priority
;                           ^------------------- FQDN, where service is hosted (. = "look A/AAAA" for this domain), you can define eg. CDN here
;                             ^----------------- options, here "h3 and h2 supported"

In RFC3597 you define new/unknown records in hexadecimals, which isn’t pretty, but works. In the RFC they tell how you should enter your data to zone:

    5.  Text Representation

   In the "type" field of a master file line, an unknown RR type is
   represented by the word "TYPE" immediately followed by the decimal RR
   type number, with no intervening whitespace.  In the "class" field,
   an unknown class is similarly represented as the word "CLASS"
   immediately followed by the decimal class number.

   This convention allows types and classes to be distinguished from
   each other and from TTL values, allowing the "[<TTL>] [<class>]
   <type> <RDATA>" and "[<class>] [<TTL>] <type> <RDATA>" forms of
   [RFC1035] to both be unambiguously parsed.

   The RDATA section of an RR of unknown type is represented as a
   sequence of white space separated words as follows:

      The special token \# (a backslash immediately followed by a hash
      sign), which identifies the RDATA as having the generic encoding
      defined herein rather than a traditional type-specific encoding.

      An unsigned decimal integer specifying the RDATA length in octets.

      Zero or more words of hexadecimal data encoding the actual RDATA
      field, each containing an even number of hexadecimal digits.

   If the RDATA is of zero length, the text representation contains only
   the \# token and the single zero representing the length.

   An implementation MAY also choose to represent some RRs of known type
   using the above generic representations for the type, class and/or
   RDATA, which carries the benefit of making the resulting master file
   portable to servers where these types are unknown.  Using the generic
   representation for the RDATA of an RR of known type can also be
   useful in the case of an RR type where the text format varies
   depending on a version, protocol, or similar field (or several)
   embedded in the RDATA when such a field has a value for which no text
   format is known, e.g., a LOC RR [RFC1876] with a VERSION other than
   0.

   Even though an RR of known type represented in the \# format is
   effectively treated as an unknown type for the purpose of parsing the
   RDATA text representation, all further processing by the server MUST
   treat it as a known type and take into account any applicable type-
   specific rules regarding compression, canonicalization, etc.

   The following are examples of RRs represented in this manner,
   illustrating various combinations of generic and type-specific
   encodings for the different fields of the master file format:

      a.example.   CLASS32     TYPE731         \# 6 abcd (
                                               ef 01 23 45 )
      b.example.   HS          TYPE62347       \# 0
      e.example.   IN          A               \# 4 0A000001
      e.example.   CLASS1      TYPE1           10.0.0.2

Ie. the format for HTTPS, RR type value 65 (see spec), record would be

<selector> IN TYPE65 \# <length of the data> <hex-encoded data>

As we need to do some conversion for the RDATA part of HTTPS record, I wrote small script for generating these records…

#!/bin/bash
set -e

RDATA="$@"

HEX="$(echo -n "${RDATA}" | od -A n -t x1 |tr -d '[:space:]')"
LEN="$(echo "${HEX}" |wc -c)"

echo ";@ IN HTTPS ${RDATA}"
echo "@ IN TYPE65 \\# $((${LEN} / 2)) ${HEX}"

so… with the example record borrowed from CF blog,

example.com 3600 IN HTTPS 1 . alpn=”h3,h2”

we would run

$ sh tmp.sh '1 . alpn="h3,h2"'
;@ IN HTTPS 1 . alpn="h3,h2"
@ IN TYPE65 \# 16 31202e20616c706e3d2268332c683222

Yay! This is something bind is willing to consume. Actual HTTPS record is also provided as comment - when your DNS server begins to support it, it’s already there.

My own setup is currently

;@ IN HTTPS 1 ypcs.github.io. alpn="h2"                                         
@ IN TYPE65 \# 27 3120797063732e6769746875622e696f2e20616c706e3d22683222 

as my site is currently hosted at GitHub pages, so I now announce at APEX that “please, look at github.io., and you can use HTTP/2”.

To check record using dig,

$ dig ypcs.fi TYPE65 +short
\# 27 3120797063732E6769746875622E696F2E20616C706E3D22683222

Feedback / comments?

Ping me (@ypcs) on Twitter