SPCDNS: The sane DNS encoding/decoding library
SPCDNS implements a simple yet sane API to encode DNS queries and to decode
DNS replies. The library (v1.0) currently supports the decoding of 30 DNS
resource records, which is more than just about all other DNS resolving
libraries I've seen (c-ares, udns, adns, libdns and djbdns [1]).
SPCDNS is *NOT* a general purpose DNS resolving library (although code is
provided to make DNS queries, it is simple and fairly stupid). This code
exists to provide a simple method of encoding and decoding the DNS protocol
and thus the network side of things is a bit lacking I'll admit. But that
is beyond what I wanted for this project anyway.
In the "src/" directory you'll find the following:
dns.h
Defines the various DNS RR types, a structure for an
in-memory representation of each RR type (which is what
you'll get back when you call the decoding routine), and the
definitions for two functions, dns_encode() and dns_decode()
which pretty much do what they say.
codec.c
The actual implementations of dns_encode() and dns_decode().
This is the only file that's needed to encode and decode the
raw DNS protocol. The routines are thread safe, do *not*
allocate memory (see below for more details) and do not use
signals. It also does not use code from any other file in
this package.
mappings.h
mappings.c
These files provide definitions and implementation of a few
helpful routines that return string representations of the
DNS RR types, classes, opcodes and errors. Again, thread
safe and no memory allocations made.
netsimple.h
netsimple.c
These files provide definitions and implementations for
making simple DNS queries to a given server. This code is
*simple* and *dumb*, it may be good for light usage but was
written to get actual DNS packets from a DNS server for
testing. This uses UDP and is thus limited to a query of
512 bytes or less.
output.h
output.c
These files provide definitions and implementations for
utility functions to print query results using C stdio.
It was factored out of the unit tests for debugging client
applications making using of libspcdns.a and libspcdns.so.
They are found in the libspcdnsmisc.a and libspcdnsmisc.so
libraries.
luadns.c
Lua [2] bindings for this library. It exports the routines
found in codec.c, mappings.c and netsimple.c. Not all the
DNS RR types decoded are supported as of yet, but the major
ones commonly used are supported.
test.c
An example program showing how to use the API to construct
and send a query, and to decode the response.
You'll probably want to check the Makefile to make sure the right compiler
and locations are set. Or not. You don't *HAVE* to use the included
Makefile. It's really just a set of suggestions anyway.
A NOTE ABOUT MEMORY ALLOCATIONS
The dns_encode() and dns_decode() functions do no memory allocation; they
use what you give them. In the case of dns_decode(), the block of memory
passed in must be big enough to handle not only the dns_query_t structure,
but multiple dns_answer_t structures and text strings representing domain
names and the occasional string or two (say, for TXT or NAPTR records). In
testing, I've found that 4K for decoding appears to be enough memory to
handle DNS requests made via UDP (although the test.c program uses an 8K
buffer).
This block of memory should be properly aligned and to help make that easier
I've defined two data types that should allow proper alignment, along with
some useful constants to declare buffers of proper alignment and size.
dns_packet_t reply [DNS_BUFFER_UDP];
dns_decoded_t decoded[DNS_DECODEBUF_4K];
dns_query_t *result;
dns_rcode rc;
size_t replysize;
size_t decodesize;
/* assume reply contains a DNS packet, and replysize is set */
decodesize = sizeof(decoded);
rc = dns_decode(decoded,&decodesize,reply,replysize);
if (rc != RCODE_OKAY)
{
/* handle error */
}
result = (dns_query_t *)decoded;
/* go with processing the result */
Do *NOT* assume that DNS_DECODEBUF_4K is equal to 4096---it's not. It
*DOES*, however, result in at least a 4K block of memory made up of
DNS_DECODEBUF_4K worth of dns_decoded_t types. By the same token, do *NOT*
assume that DNS_BUFFER_UDP is 512, but it too, does result in a buffer of at
least 512 bytes made up of DNS_BUFFER_UDP dns_packet_t types.
And while passing in a char * declared buffer to dns_decode() may appear to
work, it only works on *YOUR* system; it may not work on other systems.
A NOTE ABOUT DOMAIN NAMES
The dns_encode() function assumes the domain passed is a fully qualified
domain name. If you see an RCODE_NAME_ERROR when calling this function, you
are probably not passing in a FQDN (if you are and are still getting that
error, it's most likely a domain name segment exceeding the 63 character DNS
limit).
SOME NOTES ABOUT THE LUA BINDINGS
The Lua bindings (for Lua 5.1, 5.2 or 5.3) are loaded into a Lua script with
the following:
local dns = require "org.conman.dns"
This loads the bindings into a global Lua table called "org.conman.dns" to
avoid name conflicts with other Lua DNS bindings and/or libraries. Doing a
"make install-lua" will install this file under:
/usr/local/lib/lua/<Lua version>/org/conman/
(assuming you didn't change the LUA setting in the Makefile)
and thus place it under the appropriate namespace so Lua can find it.
The file "lua/test.lua" shows the best use of the Lua bindings (and is close
enough to what "src/test.c" does). Better network handling could be done
using LuaSocket, but for that, you are on your own.
UNIT TESTING
The dotest program allows unit-testing SPCDNS when making changes or
integrating it with client applications.
Note: the unit test program, dotest, requires a fully-qualified canonical DNS
name. This means "www.example.com." NOT "www.example.com". If this is not
present, the dns_encode_domain function returns RCODE_NAME_ERROR and the test
will fail with a mysterious error message.
Sample Output:
This is an example of a working run, querying for Google's web server, using a
Google Public DNS Server [4].
$ ./dotest -d -s 8.8.8.8 www.google.com.
OUTGOING:
... MEMORY DUMP...
INCOMING:
... MEMORY DUMP...
Bytes used: 680
; Questions = 1
; Answers = 5
; Name Servers = 0
; Additional Records = 0
; Authoritative Result = false
; Truncated Result = false
; Recursion Desired = true
; Recursion Available = true
; Result = No error
;;; QUESTIONS
;www.google.com. IN A
;;; ANSWERS
www.google.com. 185 IN A 74.125.239.49
www.google.com. 185 IN A 74.125.239.52
www.google.com. 185 IN A 74.125.239.50
www.google.com. 185 IN A 74.125.239.51
www.google.com. 185 IN A 74.125.239.48
;;; NAMESERVERS
;;; ADDITIONAL
A FINAL NOTE
If you have any problems, questions or enhancements, please send them my
way, to sean@conman.org.
Thank you.
Other contributors include, but are not limited to:
Matthew Hall <mhall@mhcomputing.net>
[1] http://c-ares.haxx.se/
http://www.corpit.ru/mjt/udns.html
http://www.chiark.greenend.org.uk/~ian/adns/
http://www.25thandclement.com/~william/projects/dns.c.html
http://cr.yp.to/djbdns.html
[2] http://www.lua.org/
[3] http://w3.impa.br/~diego/software/luasocket/
[4] https://developers.google.com/speed/public-dns/