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

libs6dns
s6-dns
Software
skarnet.org

# The `s6dns_resolve` library interface

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

## General information

`s6dns_resolve` provides functions and macros - mostly macros - to perform high level synchronous DNS resolution.

All the functions declared here make synchronous calls to the network, so they can block for a non-negligible amount of time. To avoid unbounded waiting times, they always take 2 arguments at the end, deadline and stamp. deadline is the read-only address of a tain_t containing an absolute time which is the deadline for the function, and stamp is the read-write address of a `tain_t` being an accurate enough representation of the current absolute time. If the function has not returned by *deadline, then it immediately returns with a failure code and errno set to ETIMEDOUT. In every case, *stamp is automatically updated so it always represents the absolute time accurately enough.

In a single-threaded program, the STAMP global variable can be used to store the current time. Macros ending with `_g` use this variable automatically so you don't have to provide the stamp argument everytime. Additionally, several resolution functions make implicit use of global variables such as:

- `s6dns_engine_here`: a global s6dns_engine_t storing the current query, for sequential queries.
- `s6dns_debughook_zero`: a global `s6dns_debughook_t` meaning no debugging is needed.
- `s6dns_rci_here`: a global s6dns_rci_t containing the current resolv.conf information.

Reentrant, non-global-using functions are also provided, with the `_r` suffix. In other words, if foobarfunc is a resolution function, the following prototypes are generally provided, from the simplest to the most complex:

- foobarfunc_g: only the relevant parameters and a deadline must be given, the function uses the STAMP global and maybe DNS-specific globals
- foobarfunc: a deadline and stamp must be given, the function uses all the DNS-specific globals
- foobarfunc_r_g: No DNS-specific globals are used, the information must be given via parameters, but the STAMP global is used
- foobarfunc_r: fully reentrant function using no globals at all

For each set of four functions, only one is documented here. The other prototypes can be found in the `s6-dns/s6dns-resolve.h` file.

Some `errno` codes reported by these functions do not have exactly the system meaning given by strerror(). To get a user-friendly error message, use `s6dns_constants_error_str(errno)` instead. The `s6dns_constants_error_str` function is declared in the `s6dns-constants.h` header.

## Functions

### Single query resolution

These functions are ordered from the lowest level to the highest level.

#### Basic wrapper around s6dns_engine

`int s6dns_resolve_loop_r_g (s6dns_engine_t *dt, tain_t const *deadline)`
Resolves the query stored in `dt`. Returns 1 on success or 0 on failure.

#### Generic resolution functions

`int s6dns_resolve_core_g (s6dns_domain_t const *d, uint16_t qtype, tain_t const *deadline)`
Resolves the query on domain *d (in packet form), of type qtype. Returns 0 on failure, or 1 on success, in which case `s6dns_engine_here` contains the answer.

`int s6dns_resolve_parse_g (s6dns_domain_t const *d, uint16_t qtype, s6dns_message_rr_func_t *f, void *data, tain_t const *deadline)`
Resolves the query on domain *d (in packet form), of type qtype, then parses the answer with function f and stores the result into data. Returns 1 if it succeeds, 0 if no data can be extracted from the answer, or -1 if an error occurs. Sets errno in the last two cases.

Note that the function can return 1 without appending anything to data. This means that the servers confirmed that the domain exists, but f has not been able to find any data relevant to the query in the answer. This is very different from NXDOMAIN, which means that the servers deny the actual existence of the domain, and which is reported here as a return code of 0 with errno set to ENOENT.

`int s6dns_resolvenoq_g (char const *name, size_t len, uint16_t qtype, s6dns_message_rr_func_t *f, void *data, tain_t const *deadline)`
Performs a query of type qtype on name name of length len, without qualifying it. Parses the answer with function f and stores the result into data. Returns 1 if it succeeds, 0 if no data can be extracted, or -1 if an error occurs. Sets errno in the last two cases.

`int s6dns_resolveq_g (char const *name, size_t len, uint16_t qtype, s6dns_message_rr_func_t *f, void *data, tain_t const *deadline)`
Performs a query of type qtype on name name of length len, qualifying it first. Parses the answer with function f and stores the result into data. Returns 1 if it succeeds, 0 if none of the FQDNs can get a positive answer, or -1 if an error occurs. Sets errno in the last two cases.

`int s6dns_resolve_g (char const *name, size_t len, uint16_t qtype, s6dns_message_rr_func_t *f, void *data, int qualif, tain_t const *deadline)`
Performs a query of type qtype on name name of length len. Qualifies name first if qualif is nonzero; else, does not qualify it. Parses the answer with function f and stores the result into data. Returns 1 if it succeeds, 0 if none of the FQDNs can get a positive answer, or -1 if an error occurs. Sets errno in the last two cases.

#### High-level type-specific functions

`int s6dns_resolve_a_g (stralloc *ips, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs an A query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the IPs are appended to the stralloc *ips, using 4 bytes per answer.

`int s6dns_resolve_aaaa_g (stralloc *ips, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs an AAAA query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the IPs are appended to the stralloc *ips, using 16 bytes per answer.

`int s6dns_resolve_aaaaa_g (genalloc *ips, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs an AAAA query and an A query at the same time on name name of length len, qualifying it first iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or a positive number if it succeeds: 1 if IPv4 addresses were found, 2 if IPv6 addresses were found, and 3 if both were found. The IPs are appended to the genalloc *ips, which contains an array of `ip46full_t`, the skalibs structure used to store IPv4 and IPv6 addresses indiscriminately.

`int s6dns_resolve_ptr_g (genalloc *ds, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs a PTR query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the domains are appended to the genalloc *ds, which contains an array of `s6dns_domain_t`.

`int s6dns_resolve_name4_g (genalloc *ds, char const *ip, tain_t const *deadline)`
Performs a PTR query on the `in-addr.arpa.` name representing the IPv4 address ip (4 network-order bytes). Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the domains are appended to the genalloc *ds, which contains an array of `s6dns_domain_t`.

`int s6dns_resolve_name6_g (genalloc *ds, char const *ip, tain_t const *deadline)`
Performs a PTR query on the `ip6.arpa.` name representing the IPv6 address ip (16 network-order bytes). Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the domains are appended to the genalloc *ds, which contains an array of `s6dns_domain_t`.

`int s6dns_resolve_name46_g (genalloc *ds, ip46full_t const *ip, tain_t const *deadline)`
Calls `s6dns_resolve_name6_g()` or `s6dns_resolve_name4_g()` depending on which ip is an IPv6 or IPv4 address.

`int s6dns_resolve_ns_g (genalloc *ds, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs a NS query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the domains are appended to the genalloc *ds, which contains an array of `s6dns_domain_t`.

`int s6dns_resolve_cname_g (genalloc *ds, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs a CNAME query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the domains are appended to the genalloc *ds, which contains an array of `s6dns_domain_t`.

`int s6dns_resolve_hinfo_g (genalloc *hinfos, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs an HINFO query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the domains are appended to the genalloc *hinfos, which contains an array of `s6dns_message_rr_hinfo_t`.

`int s6dns_resolve_mx_g (genalloc *mxs, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs an MX query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the domains are appended to the genalloc *mxs, which contains an array of `s6dns_message_rr_mx_t`.

`int s6dns_resolve_soa_g (genalloc *soas, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs an SOA query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the domains are appended to the genalloc *soas, which contains an array of `s6dns_message_rr_soa_t`.

`int s6dns_resolve_srv_g (genalloc *srvs, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs an SRV query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the domains are appended to the genalloc *srvs, which contains an array of `s6dns_message_rr_srv_t`.

`int s6dns_resolve_caa_g (genalloc *caas, char const *name, size_t len, int qualif, tain_t const *deadline)`
Performs a CAA query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case the domains are appended to the genalloc *caas, which contains an array of `s6dns_message_rr_caa_t`.

`int s6dns_resolve_txt_g (stralloc *sa, genalloc *offsets, char const *name, unsigned int len, int qualif, tain_t const *deadline)`
Performs an TXT query on name name of length len, qualifying it iff qualif is nonzero. Returns -1 if an error occurs, or 0 if no answer can be obtained from servers, or 1 if it succeeds, in which case:

- The resulting strings are all stored into stralloc *sa. Every string is terminated by a null character.
- A series of unsigned ints is appended to genalloc *offsets. Every integer represents the offset in *sa at which a string is stored. The number of appended integers is the total number of answers.

#### Parallel resolution

`int s6dns_resolven_loop_g (s6dns_engine_t *dtl, unsigned int n, unsigned int or, tain_t const *deadline)`
Resolves the n queries stored in the array pointed to by dtl, in parallel. If or is zero, it does not return before all answers have arrived. If or is 1, it returns when an answer arrives, but does not return if a query generates an error (unless all queries do so). If or is 2, it returns when an answer arrives or an error occurs. Other values of or are unspecified yet.

The return code is as follows:

- If or is 0: -1 on a global error (i.e. not specific to a query), or a non-negative number which is the total of successful queries.
- If or is 1 or 2: -1 on a global error, or a non-negative number which is the index of the query that triggered the event.

If or is 1, a return code of -1 with errno set to ENOENT means that all the queries failed.

After the function returns, the `status` field of each `s6dns_engine_t` contains the error code relative to the query. A status of 0 means that an answer has properly arrived; EAGAIN means that the query is still pending (and the s6dns_engine_t has not been recycled); ECONNABORTED means that the query has not been properly initialized. Other codes report various network problems.

`int s6dns_resolven_parse_g (s6dns_resolve_t const *list, unsigned int n, tain_t const *deadline)`
Performs n complete resolutions in parallel, parsing the results. Returns 1 in case of success or 0 if a global error occurred.

list is a pointer to an array of n `s6dns_resolve_t`, which is a structure containing at least the following fields:

- `q` : a `s6dns_domain_t` containing the query. It must be in encoded form.
- `qtype` : a `uint16` containing the query type. A list of valid query types can be found in the `s6dns-constants.h` header.
- `options` : a `uint32` containing options passed to s6dns_engine_init().
- `deadline` : a `tain_t` containing the deadline for this query, i.e. the query will fail with an ETIMEDOUT code if no answer has arrived by then. Note that the deadline argument given to s6dns_resolven_parse() is a global deadline that can make the function return with 0 ETIMEDOUT, but is independent from this field, which is local to every query.
- `parsefunc` : a `s6dns_message_rr_func_t` function that will be used to parse the answer.
- `data` : a `void *` that will be passed as an additional pointer to the `parsefunc` function; it is generally used to store the parsing result.
- `status` : an `int`. It does not need to be initialized. It will contain the error code for the query after the function returns. ECONNABORTED means that the query could not be started at all; EAGAIN means that the query was still pending when an error happened; other codes report various network problems.

The s6dns_resolven_parse() function is a simple, convenient way to perform several resolutions in parallel to avoid the waiting time incurred by serial resolutions. However, it is still a synchronous function, and cannot replace a real asynchronous DNS library: for more complex parallel resolution needs, use the skadns library.