The skadns library interface

The skadns library provides an API for asynchronous DNS resolution.




Check the s6-dns/skadns.h header for the exact function prototypes.

Make sure your application is not disturbed by children it doesn't know it has. This means paying some attention to the SIGCHLD handler, if any, and to the way you perform waitpid()s. The best practice is to use a self-pipe to handle SIGCHLD (as well as other signals the application needs to trap), and to always use wait_nohang() to reap children, simply ignoring pids you don't know.

If your (badly programmed) application has trouble handling unknown children, consider using a skadnsd service.

A programming example

The src/clients/s6dns_generic_filter_main.c file in the s6-dns package, used in the s6-dns*-filter programs, illustrates how to use the skadns library.

Starting and ending a session

skadns_t a = SKADNS_ZERO ;
tain_t deadline, stamp ;

tain_now(&stamp) ;
tain_addsec(&deadline, &stamp, 2)

// char const *path = SKADNS_IPCPATH ;
// skadns_start(&a, path, &deadline, &stamp) ;
skadns_startf(&a, &deadline, &stamp) ;

skadns_start starts a session with a skadnsd service, listening on path.
skadns_startf starts a session with a skadnsd process as a child (which is the simplest usage).
a is a skadns_t structure that must be declared and initialized to SKADNS_ZERO. stamp must be an accurate enough timestamp.
If the session initialization fails, the function returns 0 and errno is set; else the function returns 1.

If the absolute time deadline is reached and the function has not returned yet, it immediately returns 0 with errno set to ETIMEDOUT. Only local interprocess communications are involved; unless your system is heavily overloaded, the function should return near-instantly. One or two seconds of delay between stamp and deadline should be enough: if the function takes more than that to return, then there is a problem with the underlying processes.

You can have more than one session open in parallel, by declaring several distinct skadns_t structures and calling skadns_startf (or skadns_start) more than once. However, this is only useful if you need to perform more than SKADNS_MAXCONCURRENCY, i.e. a thousand, concurrent requests. In most situations, a single skadns session will be enough.

skadns_end(&a) ;

skadns_end frees all the resources used by the session. The a structure is then reusable for another session.

Sending a DNS query

s6dns_domain_t d ;
uint16_t qtype ;
uint16_t id ;
tain_t limit, deadline, stamp ;

skadns_send(&a, &id, &d, qtype, &limit, &deadline, &stamp) ;

skadns_send starts the asynchronous resolution of domain d with query type qtype. d must be encoded in packet form. stamp must be an accurate enough timetamp. If the resolution hasn't completed by deadline limit, it will automatically fail with a status set to ETIMEDOUT.

Like skadns_startf(), the skadns_send() call is synchronous but should not be blocking; however, if it hasn't returned by deadline deadline, it then returns 0 with errno set to ETIMEDOUT. On failure, the call returns 0 and sets errno. On success, it returns 1 and id is set to a 16-bit number identifying the query.

Cancelling a query

skadns_cancel(&a, id, &deadline, &stamp) ;

skadns_cancel cancels the resolution identified by id. stamp must be an accurate enough timestamp. The call returns 1 on success, or 0 (and sets errno) on failure. It is synchronous but should return almost-instantly; if it hasn't returned by deadline, it then returns 0 ETIMEDOUT.

After a query has been successfully canceled, its id can be discarded.

Asynchronously waiting for answers

(from now on, the functions are listed with their prototypes instead of usage examples.)

int skadns_fd (skadns_t const *a)

Returns a file descriptor to select on for reading. Do not read() it though.

int skadns_update (skadns_t *a)

Call this function whenever the fd checks readability: it will update a's internal structures with information from the skadnsd daemon. It returns -1 if an error occurs; in case of success, it returns the number of identifiers for which something happened.

When skadns_update returns, genalloc_s(uint16_t, &a->list) points to an array of genalloc_len(uint16_t, &a->list) 16-bit unsigned integers. Those integers are valid ids that can be used with the following functions.

char const *skadns_packet (skadns_t *a, uint16_t id)
int skadns_packetlen (skadns_t *a, uint16_t id)

skadns_packet() returns a pointer to the DNS packet containing the answer to the query identified by id, and skadns_packetlen() returns its length. If an error has occurred, skadns_packet() returns NULL and skadns_packetlen() returns -1; either call sets errno to a value identifying the error. Some errno values have a special meaning:

int skadns_release (skadns_t *a, uint16_t id)

skadns_release() frees the cell holding the result of query id. It returns 1 on success, then id can be discarded - further calls to skadns_send() may reuse the same number.

skadns_release() may fail, and return 0. This signals a programming error and shouldn't be relied on - however, if it happens, errno can help you identify the problem: