The problem with libresolv

The BIND name server software comes with its own client library,
named libresolv.

As can be expected from an ISC product, libresolv is not good software.
Here are a few reasons why.

libresolv's security model is flawed.

The same people who wrote BIND wrote libresolv. That is the amount of
trust you can place in libresolv. Ten years ago, the security status
of libresolv looked like
this. I am not
confident that is has improved: bugs in the software may have been
fixed, but new ones will appear, and most importantly, the security
management policy at ISC is still the same: security holes will be
denied instead of acknowledged and worked upon.

If you find a bug, and a fortiori a security hole, in
s6-dns, you can be sure it will be fixed promptly with apologies
from the author. skarnet.org doesn't do obfuscation, and never lets
politics get in the way of technical quality.

libresolv is unboundedly synchronous.

You'd expect a real DNS client library to do better in this aspect
than getaddrinfo(), but no: libresolv's
function calls are still purely synchronous and may uncontrollably
block if the network is unresponsive.

Additionally, libresolv only provides a synchronous
interface to clients. Despite the fundamentally asynchronous nature
of DNS, and the need to implement asynchronous primitives
internally, only blocking calls are made available in the API.
This forces users to stack yet another piece of software on top
of their dependencies if they need asynchronous DNS resolution.

It is too big for what it does.

The libresolv-2.13.so binary file compiled for an x86_64
Debian Linux system is roughly 79k bytes. The libs6dns.so.2.0
file, for the same system, is a little bit smaller, while offering
more functionality: high-level answer parsing, user-friendly
formatting, and in-depth tracing of the DNS resolution process.
What is all that code in libresolv for ?

The API is cumbersome to use.

Some examples of less-than-ideal interfaces for the users:

There is a flag in libresolv's global state structure that
determines if queries are to be sent recursive or iterative. Which
means that a program that needs to make both recursive and iterative
queries must duplicate this state structure and use one for recursive
queries, one for iterative queries. Why this complexity ? The
recursive flag should be given for every query, not be a part
of the resolver state.

When a libresolv answer arrives but doesn't fit into the
user-supplied buffer, the query is discarded and has to be retried.
Does "network efficiency" ring a bell ? Also, the length of
the original answer is returned by the first call, which is a good
thing: the user can now provide a large enough buffer so the call
succeeds the next time, right ? Wrong. The next answer can
be different from the first, and in particular, longer, which
means that the next query can still fail.

There is a reason why system calls and local functions take
user-supplied buffers as arguments. They are relatively fast, it is
not too costly to call them again if the buffer is too small the
first time, and the result is consistent, i.e. after the first call,
the right buffer length is known. But functions making
network exchanges with variable-length results from one call to
another ? Those need heap-allocated storage. It is
good design to avoid using the heap whenever possible, but it is
not good design to waste network round-trips to save a malloc().

Conclusion

Like many other "standards", and C library interfaces in particular,
libresolv is at best a mediocre one, that people use because
there has been nothing better so far.

s6-dns tries to be an alternative solution,
based on solid design principles and a reliable code base.
It just works, and it makes DNS resolution nice and easy for
users.