The s6dns_engine library interface

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

General information

s6dns_engine is the nitty-gritty of DNS query management. These are the low-level asynchronous primitives sending DNS queries over the network, and getting answers.

s6dns_engine has been inspired by Dan J. Bernstein's dns_transmit library, but does not borrow any code from it. Unlike dns_transmit, s6dns_engine does not assume that network send operations are instant successes; s6dns_engine descriptors can be selected for writing as well as for reading. Also, if the underlying skalibs has been compiled with IPv6 support, s6dns_engine supports native IPv6 transport.

The s6dns_engine functions are used in the implementation of the s6dns_resolven_loop function - which is nothing more than a simple event loop around the s6dns_engine primitives - and the skadnsd daemon. Both pieces of code are good examples of how to use s6dns_engine.

However, unless you're implementing a DNS cache, you probably should not call the s6dns_engine functions directly: they are very low-level. If you need synchronous resolution, use the s6dns_resolve functions. If you need asynchronous DNS operations, use the skadns functions, which are designed to provide a higher level interface to multiple asynchronous DNS queries.

Data structures

A s6dns_engine_t structure holds all the data necessary to manage a DNS query (and response). It must be initialized to S6DNS_ENGINE_ZERO when first declared, and recycled with s6dns_engine_recycle() after each use. It contains a stralloc, so it must be freed with s6dns_engine_free() before being discarded, to avoid memory leaks.


s6dns_engine_t life cycle

int s6dns_engine_init (s6dns_engine_t *dt, s6dns_ip46list_t const *servers, uint32_t options, char const *q, unsigned int qlen, uint16_t qtype, tain_t const *deadline, tain_t const *stamp)

Initializes dt with query q of length qlen and type qtype. If d is an encoded s6dns_domain_t, then d.s and d.len are appropriate candidates for arguments q and qlen respectively.

options can be 0 or an OR'ed combination of the following, defined in s6-dns/s6dns-constants.h:

servers must point to a list of IP addresses as defined in s6-dns/s6dns-ip46.h. Such a list can be obtained from the /etc/resolv.conf file via the s6dns_rci_fill() call when performing a recursive query, or it must be constructed from a list of relevant NS addresses when performing an iterative query.

stamp must be an accurate enough timestamp. deadline sets up a deadline for the query: if the query hasn't been satisfactorily answered by deadline - no matter how many round-trips to network servers the library performs internally - then it will be considered failed, and a timeout will be reported.

The function returns 1 if all went well, and 0 if an error occurred. It returns instantly; it does not perform any network operation, it just prepares dt to send a query. The actual data sending will take place on the next s6dns_engine_event() call.

void s6dns_engine_recycle (s6dns_engine_t *dt)

Recycles dt, making it ready for another use. This function does not deallocate the heap memory used by dt, so it's faster than s6dns_engine_free() and does not cause heap fragmentation.

void s6dns_engine_free (s6dns_engine_t *dt)

Frees the heap memory used by dt. Also makes dt ready for another use. It's advised to only use this function when certain that dt will not be reused.

Before the iopause()

The descriptor to select on is available as the fd field in the s6dns_engine_t structure. dt→fd should be read every iteration, because it can change between iterations even if no event or timeout is reported for dt.

void s6dns_engine_nextdeadline (s6dns_engine_t const *dt, tain_t *a)

If dt needs handling before the absolute date *a, then *a is updated so it contains the earlier date. This is useful to compute the next deadline in an iopause() loop.

int s6dns_engine_isreadable (s6dns_engine_t const *dt)

Returns nonzero iff dt→fd is to be selected for reading. Should be called in every iteration.

int s6dns_engine_iswritable (s6dns_engine_t const *dt)

Returns nonzero iff dt→fd is to be selected for writing. Should be called in every iteration.

After the iopause()

int s6dns_engine_timeout (s6dns_engine_t *dt, tain_t const *stamp)

This function should be called if your selecting function returned 0, which means that an event timed out. stamp should contain the current time. The function returns -1 if an error occurred, 1 if dt actually timed out, and 0 if nothing special happened to dt (and your iopause timeout was caused by something else). If the return value is not 0, dt is automatically recycled.

int s6dns_engine_event (s6dns_engine_t *dt, tain_t const *stamp)

This function should be called if your selecting function returned a positive number, which means that some event got triggered. stamp should contain the current time. The function returns -1 if an error occurred (and dt is automatically recycled). It returns 0 if no answer has arrived yet, and 1 if an answer is available.

The s6dns_engine_timeout() and s6dns_engine_event() functions, when returning -1, make use of the following error codes:

char *s6dns_engine_packet (s6dns_engine_t const *dt)

Points to the response packet received from the network, if s6dns_engine_event() returned 1.

unsigned int s6dns_engine_packetlen (s6dns_engine_t const *dt)

Gives the length of the response packet, if s6dns_engine_event() returned 1.

You should recycle or free dt after reading the response packet.