s6-dns: the s6dns_message library interface
Keywords: s6-dns dns s6dns_message library libs6dns

libs6dns
s6-dns
Software
skarnet.org

# The `s6dns_message` library interface

The following functions are declared in the `s6-dns/s6dns-message.h` header, and implemented in the `libs6dns.a` or `libs6dns.so` library.

## General information

`s6dns_message` provides functions to read and parse DNS messages sent by servers and caches and containing answers to queries.

## Data structures

A `s6dns_message_header_t` is a structure containing the header of a received DNS packet, broken down for easy access to the bits.

A `s6dns_message_rr_t` is a structure containing the information about a resource record given by an answer packet - all of it, except the value of the answer itself, which is rtype-specific and has to be decoded by rtype-specific functions.

A `s6dns_message_rr_func_t` is the type of such a function. The prototype is
`int f (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *data)`

- *rr contains the header information of the resource record, including the domain to which it applies, the rtype, and the ttl.
- packet points to the whole DNS packet the RR is a part of.
- packetlen is the total length of said packet.
- pos is the offset of the RR in the DNS packet, i.e. f should start parsing at packet + pos.
- section is the section the RR belongs to: 2 for answer, 3 for authority, 4 for additional.
- data is a user-specified pointer; it is typically used to write the decoded output, in a rtype-specific way.
- f must return 1 or more if it succeeds in decoding the packet. Else it must set errno appropriately, and return -1 if the error is local, unrelated to the packet - for instance, it has run out of memory - or 0 if it cannot decode the RR because of the packet contents.

Various structures designed to store specific resource record types are also provided. The list includes:

- `s6dns_message_rr_hinfo` for HINFO RRs
- `s6dns_message_rr_mx` for MX RRs
- `s6dns_message_rr_soa` for SOA RRs
- `s6dns_message_rr_srv` for SRV RRs
- `s6dns_message_rr_caa` for CAA RRs

## Functions

### Header management

`void s6dns_message_header_pack (char *s, s6dns_message_header_t const *h)`
Packs the header *h into the 12 bytes pointed to by s.

`void s6dns_message_header_unpack (char const *s, s6dns_message_header_t *h)`
Unpacks the 12 bytes pointed to by s into the structure *h.

### Low-level RR decoding

The following primitives are used in the implementation of `s6dns_message_rr_func_t`-typed functions, to read and decode information stored in a DNS packet. Their arguments are:

1. A pointer to the structure where the information is going to be stored
2. A read-only pointer to the beginning of the DNS packet
3. The length of the DNS packet
4. A pointer to an integer containing the current position in the DNS packet, i.e. where the information is going to be read. If the functions succeed, they automatically update the position so information can be read sequentially.

`int s6dns_message_get_string (s6dns_domain_t *d, char const *packet, unsigned int packetlen, unsigned int *pos)`
Reads a character-string and stores it into *d. Returns 1 on success and 0 on failure. Note that *d does not contain a domain, but the `s6dns_domain_t` structure is adapted to store strings that do not exceed 255 characters. d→s can be used to access the string, and d→len its length.

`int s6dns_message_get_strings (char *s, unsigned int rdlength, char const *packet, unsigned int packetlen, unsigned int *pos`
This function takes an additional parameter rdlength. It reads a series of character-strings and stores their concatenation into the string s, which must be preallocated; it can never store more than rdlength bytes. It returns -1 if it fails; on success, it returns the number of bytes written. The rdlength parameter must be the length of the resource record containing the series of character-strings.

`size_t s6dns_message_get_domain_nodecode (char *out, size_t outmax, char const *packet, unsigned int packetlen, unsigned int *pos)`
Reads a domain and stores it, in packet form, into *out, which is a character array that must have been preallocated by the user; outmax is the maximum number of characters that can be stored in out. As a special case, out can be NULL, in which case nothing will be written, but the domain will still be parsed and pos will still be updated. The function returns the number of characters written to out (or that would have been). If 0, it denotes a failure, and errno is set:

- EPROTO: there is no proper domain to be read in position *pos of packet. Either the packet is malformed or the function is being misused.
- EPROTONOSUPPORT: the domain encoding uses an extension that the function does not recognize.
- ENAMETOOLONG: the domain cannot be stored in outmax characters.

`unsigned int s6dns_message_get_domain (s6dns_domain_t *d, char const *packet, unsigned int packetlen, unsigned int *pos)`
Reads a domain and stores it, in string form, into *d. Returns 1 on success and 0 on failure, and sets errno just like the function above.

`int s6dns_message_get_hinfo (s6dns_message_rr_hinfo_t *p, char const *packet, unsigned int packetlen, unsigned int *pos)`
Reads a HINFO RR and stores it into *p. Returns 1 on success or 0 on failure.

`int s6dns_message_get_mx (s6dns_message_rr_mx_t *p, char const *packet, unsigned int packetlen, unsigned int *pos)`
Reads a MX RR and stores it into *p. Returns 1 on success or 0 on failure.

`int s6dns_message_get_soa (s6dns_message_rr_soa_t *p, char const *packet, unsigned int packetlen, unsigned int *pos)`
Reads a SOA RR and stores it into *p. Returns 1 on success or 0 on failure.

`int s6dns_message_get_srv (s6dns_message_rr_srv_t *p, char const *packet, unsigned int packetlen, unsigned int *pos)`
Reads a SRV RR and stores it into *p. Returns 1 on success or 0 on failure.

`int s6dns_message_get_caa (s6dns_message_rr_caa_t *p, char const *packet, unsigned int packetlen, unsigned int *pos, uint16_t rdlength)`
Reads a CAA RR and stores it into *p. Returns 1 on success or 0 on failure. rdlength is the length of the RDATA field of the record; this value is needed to properly parse a CAA record.

### High-level RR-specific parsing functions

`s6dns_message_func_t s6dns_message_parse_answer_strings`
Parses character-strings located in the answer section of the packet. The data argument is interpreted as a pointer to a `s6dns_mpag_t`, which is a structure defined in the `s6-dns/s6dns-message.h` header and used to store multiple character-strings.

`s6dns_message_func_t s6dns_message_parse_answer_domain`
Parses domains located in the answer section of the packet. The data argument is interpreted as a pointer to a `s6dns_dpag_t`, which is a structure defined in the `s6-dns/s6dns-message.h` header and used to store multiple domains.

`s6dns_message_func_t s6dns_message_parse_answer_a`
Parses A RRs located in the answer section of the packet. The data argument is interpreted as a pointer to a stralloc, and 4 bytes are appended to this stralloc for every IPv4 address found.

`s6dns_message_func_t s6dns_message_parse_answer_aaaa`
Parses AAAA RRs located in the answer section of the packet. The data argument is interpreted as a pointer to a stralloc, and 16 bytes are appended to this stralloc for every IPv6 address found.

`s6dns_message_func_t s6dns_message_parse_answer_hinfo`
Parses HINFO RRs located in the answer section of the packet. The data argument is interpreted as a pointer to a genalloc containing `s6dns_message_rr_hinfo_t` structures.

`s6dns_message_func_t s6dns_message_parse_answer_mx`
Parses MX RRs located in the answer section of the packet. The data argument is interpreted as a pointer to a genalloc containing `s6dns_message_rr_mx_t` structures.

`s6dns_message_func_t s6dns_message_parse_answer_soa`
Parses SOA RRs located in the answer section of the packet. The data argument is interpreted as a pointer to a genalloc containing `s6dns_message_rr_soa_t` structures.

`s6dns_message_func_t s6dns_message_parse_answer_srv`
Parses SRV RRs located in the answer section of the packet. The data argument is interpreted as a pointer to a genalloc containing `s6dns_message_rr_srv_t` structures.

`s6dns_message_func_t s6dns_message_parse_answer_caa`
Parses CAA RRs located in the answer section of the packet. The data argument is interpreted as a pointer to a genalloc containing `s6dns_message_rr_caa_t` structures.

### High-level packet parsing

`int s6dns_message_parse (s6dns_message_header_t *h, char const *packet, unsigned int packetlen, s6dns_message_rr_func_t *f, void *data)`
This function parses the DNS packet packet of length packetlen. It stores the packet header into *h. Then, for every RR in the answer, authority or additional section of the packet, it calls f with the relevant parameters. data is the extra pointer given to f to store information. If the parsing succeeds, the function returns 2 if the packet contains an answer section and 1 if it does not. Otherwise, it returns -1 if there is a local error unrelated to the packet, or 0 if no appropriate answer can be decoded from the packet. errno then contains one of the following values:

- EPROTO: the packet is malformed, syntax error
- EBADMSG: rcode 1, query format error
- EBUSY: rcode 2, server failure
- ENOENT: rcode 3, nxdomain
- ENOTSUP: rcode 4, query type not implemented
- ECONNREFUSED: rcode 5, operation refused by server
- EIO: unknown rcode
- any other value being set by f if it returns a failure code.