published on in Uncategorized

Encoding DNS URI records for DNSMASQ

Dnsmasq can be configured to add various types of records like SRV, PTR, and NAPTR to its internal DNS server by various directives in its configuration file. But what if there’s a less common type of DNS record that you want to serve, which dnsmasq doesn’t have a specific configuration directive to handle?

Handily, dnsmasq also supports serving arbitrary DNS resource records using the dns-rr option. However you have to supply the binary value of the response encoded in hexadecimal. Here’s an example of how to do this for a URI record with Python. The URI record type is described in RFC 7553 which describes the binary value of the response (“wire format”) as:

The RDATA for a URI RR consists of a 2-octet Priority field, a 2-octet Weight field, and a variable-length Target field.

Priority and Weight are unsigned integers in network byte order.

The remaining data in the RDATA contains the Target field. The Target field contains the URI as a sequence of octets (without the enclosing double-quote characters used in the presentation format).

So, we need to encode the priority as 2 bytes, then the weight as 2 bytes, then the URI itself. We can use Python’s struct module for encoding the integers, and the binascii module to encode the strings as hex:

import binascii
import struct
record_name = b'_something.example.com'
record_type = b'256'  # Assigned type number for URI records
priority = 10
weight = 5
value = b'http://www.example.com/'
print(b','.join((
    b'dns-rr=' + record_name,
    record_type,
    # The ! here uses network byte order, followed by two 'H's for unsigned 2-byte values.
    binascii.hexlify(struct.pack('!HH', priority, weight))
    + binascii.hexlify(value)
)).decode())

This will output:

dns-rr=_something.example.com,256,000a0005687474703a2f2f7777772e6578616d706c652e636f6d2f

which can be copied straight into dnsmasq’s configuration file.