[Zrouter-src] ZRouter.org: push to ZRouter contrib/spcdns/LICENSE contrib/spcd...

zrouter-src at zrouter.org zrouter-src at zrouter.org
Wed Sep 19 12:22:03 UTC 2012


details:   http://zrouter.org/hg/zrouter//rev/fee6b517c6d4
changeset: 426:fee6b517c6d4
user:      Aleksandr Rybalko <ray at ddteam.net>
date:      Wed Sep 19 15:25:04 2012 +0300
description:
Add spcdns, we can use it to get smallest dig like tool.
And for correct link test.

diffstat:

 contrib/spcdns/LICENSE         |   165 +++
 contrib/spcdns/Makefile        |   128 +++
 contrib/spcdns/README          |   156 +++
 contrib/spcdns/lua/dns.lua     |    54 +
 contrib/spcdns/lua/mx.lua      |   102 ++
 contrib/spcdns/lua/test.lua    |   213 +++++
 contrib/spcdns/src/codec.c     |  1695 ++++++++++++++++++++++++++++++++++++++++
 contrib/spcdns/src/dns.h       |   990 +++++++++++++++++++++++
 contrib/spcdns/src/luadns.c    |   768 ++++++++++++++++++
 contrib/spcdns/src/mappings.c  |   373 ++++++++
 contrib/spcdns/src/mappings.h  |    55 +
 contrib/spcdns/src/netsimple.c |   156 +++
 contrib/spcdns/src/netsimple.h |    75 +
 contrib/spcdns/src/test.c      |   563 +++++++++++++
 14 files changed, 5493 insertions(+), 0 deletions(-)

diffs (5549 lines):

diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/LICENSE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/LICENSE	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/Makefile	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,128 @@
+
+#######################################################################
+#
+# Copyright 2010 by Sean Conner.  All Rights Reserved.
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or (at your
+# option) any later version.
+#
+# This library is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, see <http://www.gnu.org/licenses/>.
+#
+######################################################################
+
+#================================================
+# Linux
+#================================================
+
+CC     = gcc -g -std=c99
+CFLAGS = -Wall -Wextra -pedantic
+#CFLAGS = -Os -fomit-frame-pointer -DNDEBUG
+PIC    = -fpic
+LFLAGS = -lm 
+LUA    = /usr/local/lib/lua/5.1
+AR     = ar cr
+RANLIB = ranlib
+
+#=================================================
+# Solaris
+#=================================================
+
+#CC     = cc -g -xc99
+#CFLAGS =
+#PIC    = -fpic
+#LFLAGS = -lm -lnsl -lsocket
+#LUA    = /usr/local/lib/lua/5.1
+#AR     = ar cr
+#RANLIB = ranlib
+
+#=================================================
+
+dotest : built/dotest 
+
+lua : built/dns.so
+
+lib : built/libspcdns.a
+
+so : built/libspcdns.so
+
+all : built/dotest built/dns.so built/libspcdns.a built/libspcdns.so
+
+#==================================================
+
+built/libspcdns.a : built/codec.o built/mappings.o
+	$(AR) $@ built/codec.o built/mappings.o
+	$(RANLIB) $@
+
+built/libspcdns.so : built/codec.pic.o built/mappings.pic.o
+	$(CC) -shared -o $@ built/codec.pic.o built/mappings.pic.o 
+	
+built/codec.o : src/codec.c src/dns.h
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+built/codec.pic.o : src/codec.c src/dns.h
+	$(CC) $(CFLAGS) $(PIC) -c -o $@ $<
+	
+built/mappings.o : src/mappings.c src/mappings.h
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+built/mappings.pic.o : src/mappings.c src/mappings.h
+	$(CC) $(CFLAGS) $(PIC) -c -o $@ $<
+
+built/netsimple.o : src/netsimple.c src/netsimple.h
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+built/netsimple.pic.o : src/netsimple.c src/netsimple.h
+	$(CC) $(CFLAGS) $(PIC) -c -o $@ $<
+
+#==============================================================
+
+
+built/dotest : built/test.o 		\
+		built/codec.o 		\
+		built/mappings.o	\
+		built/netsimple.o
+	$(CC) -o $@ built/test.o 	\
+		built/codec.o		\
+		built/mappings.o	\
+		built/netsimple.o	\
+		$(LFLAGS)
+
+built/test.o : src/test.c src/dns.h src/mappings.h src/netsimple.h
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+#=============================================================
+
+built/dns.so : built/luadns.o 		\
+		built/codec.pic.o 	\
+		built/mappings.pic.o	\
+		built/netsimple.pic.o
+	$(CC) -o $@ -shared 	 	\
+		built/luadns.o		\
+		built/codec.pic.o	\
+		built/mappings.pic.o 	\
+		built/netsimple.pic.o
+
+	
+built/luadns.o : src/luadns.c src/dns.h src/mappings.h
+	$(CC) $(CFLAGS) $(PIC) -c -o $@ $<
+
+#===========================================================
+
+install-lua: built/dns.so
+	install -d $(LUA)/org/conman
+	install built/dns.so $(LUA)/org/conman
+	
+clean:
+	/bin/rm -rf built/*
+	/bin/rm -rf *~ src/*~ lua/*~
+
+tarball:
+	(cd .. ; tar czvf /tmp/spcdns.tar.gz -X spcdns/.exclude spcdns/ )
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/README
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/README	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,156 @@
+
+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.
+
+	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 are loaded into a Lua script with the following:
+
+	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/5.1/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.
+
+A FINAL NOTE
+
+If you have any problems, questions or enhancements, please send them my
+way, to sean at conman.org.  
+
+Thank you.
+
+[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/
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/lua/dns.lua
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/lua/dns.lua	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,54 @@
+#! /usr/bin/env lua
+-- *************************************************************************
+--
+-- Copyright 2010 by Sean Conner.  All Rights Reserved.
+--
+-- This library is free software; you can redistribute it and/or modify it
+-- under the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 3 of the License, or (at your
+-- option) any later version.
+-- 
+-- This library is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+-- License for more details.
+-- 
+-- You should have received a copy of the GNU Lesser General Public License
+-- along with this library; if not, see <http://www.gnu.org/licenses/>.
+--
+-- **************************************************************************
+
+dns  = require "org.conman.dns"
+
+e = dns.encode {
+	id       = 1234,
+	query    = true,
+	rd       = true,
+	opcode   = 'query',
+	question = {
+		name  = 'yahoo.com.',
+		type  = 'mx',
+		class = 'in'
+	}
+}
+
+r,err = dns.query('127.0.0.1',e)
+
+if r == nil then
+  print("error:",err)
+  os.exit(1)
+end
+
+d = dns.decode(r)
+
+for i = 1 , #d.answers do
+  print(string.format("%s %d %s %s %d %s",
+  			d.answers[i].name,
+  			d.answers[i].ttl,
+  			d.answers[i].class,
+  			d.answers[i].type,
+  			d.answers[i].preference,
+  			d.answers[i].exchange
+  		))
+end
+
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/lua/mx.lua
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/lua/mx.lua	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,102 @@
+#!/usr/bin/env lua
+-- *************************************************************************
+--
+-- Copyright 2010 by Sean Conner.  All Rights Reserved.
+--
+-- This library is free software; you can redistribute it and/or modify it
+-- under the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 3 of the License, or (at your
+-- option) any later version.
+-- 
+-- This library is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+-- License for more details.
+-- 
+-- You should have received a copy of the GNU Lesser General Public License
+-- along with this library; if not, see <http://www.gnu.org/licenses/>.
+--
+-- **************************************************************************
+
+require "org.conman.dns"
+
+local SERVER = "127.0.0.1"
+local dns    = org.conman.dns
+
+-- **************************************************************
+
+local function query(host,type)
+  local e = dns.encode {
+  		id       = math.random(),
+  		query    = true,
+  		rd       = true,
+  		opcode   = 'query',
+  		question = {
+  			name  = host,
+  			type  = type,
+  			class = 'in'
+  		}
+  	}
+  	
+  local r,err = dns.query(SERVER,e)
+  
+  if r == nil then
+    print("error:",err)
+    return nil
+  end
+  
+  return dns.decode(r)
+end
+
+-- ****************************************************************
+
+local function query_a(host)
+  local a,err = query(host,'a')
+  
+  if a == nil then
+    print("error:",err)
+    return nil
+  end
+  
+  return a.answers[1]
+end
+
+-- ****************************************************************
+
+local function query_mx(host)
+  local mx,err = query(host,'mx')
+  
+  if mx == nil then
+    print("error:",err)
+    return nil
+  end
+  
+  table.sort(mx.answers,function(a,b) return a.preference < b.preference end)
+  
+  for i = 1 , #mx.answers do
+    mx.answers[i].ADDRESS = mx.additional[mx.answers[i].exchange]
+    if mx.answers[i].ADDRESS == nil then
+      mx.answers[i].ADDRESS = query_a(mx.answers[i].exchange)
+    end
+  end
+  
+  return mx.answers
+end
+
+-- **************************************************************
+
+if #arg == 0 then
+  io.stderr:write(string.format("usage: %s domain\n",arg[0]))
+  os.exit(1)
+end
+
+local results = query_mx(arg[1])
+
+for i = 1 , #results do
+  local mx,ip
+  
+  mx = results[i].exchange
+  ip = results[i].ADDRESS.address or "(none)"
+  
+  print(mx,ip)
+end
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/lua/test.lua
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/lua/test.lua	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,213 @@
+#!/usr/bin/env lua
+-- *************************************************************************
+--
+-- Copyright 2010 by Sean Conner.  All Rights Reserved.
+--
+-- This library is free software; you can redistribute it and/or modify it
+-- under the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 3 of the License, or (at your
+-- option) any later version.
+-- 
+-- This library is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+-- License for more details.
+-- 
+-- You should have received a copy of the GNU Lesser General Public License
+-- along with this library; if not, see <http://www.gnu.org/licenses/>.
+--
+-- **************************************************************************
+
+require "org.conman.dns"
+
+local SERVER = "127.0.0.1"
+local dns    = org.conman.dns
+
+-- **************************************************************
+
+local function query(host,type)
+  local e
+  local r
+  local err
+  
+  e,err  = dns.encode {
+  		id       = math.random(),
+  		query    = true,
+  		rd       = true,
+  		opcode   = 'query',
+  		question = {
+  			name  = host,
+  			type  = type,
+  			class = 'in'
+  		}
+  	}
+  
+  if e == nil then
+    return e,err
+  end
+
+  r,err = dns.query(SERVER,e)
+  
+  if r == nil then
+    return r,err
+  end
+  
+  return dns.decode(r)
+end
+
+-- ****************************************************************
+
+local function print_txt(rec)
+  if type(rec.txt) == 'string' then
+    return rec.txt
+  elseif type(rec.txt) == 'table' then
+    local s = "("
+    for i = 1 , #rec.txt do
+      s = s .. string.format("\n\t\t\t%q",rec.txt[i])
+    end
+    s = s .. "\n\t\t)"
+    return s
+  else
+    return ""
+  end
+end
+
+local callbacks = 
+{
+  NS    = function(rec) return rec.nsdname end,
+  A     = function(rec) return rec.address end,
+  AAAA  = function(rec) return rec.address end,
+  CNAME = function(rec) return rec.cname end,
+  MX    = function(rec) return string.format("%5d %s",rec.preference,rec.exchange) end,
+  PTR   = function(rec) return rec.ptr end,
+  HINFO = function(rec) return string.format("%q %q",rec.cpu,rec.os) end,
+  SPF   = print_txt,
+  TXT   = print_txt,
+  SOA   = function(rec) return string.format([[
+%s %s (
+		%10d   ; Serial
+		%10d   ; Refresh
+		%10d   ; Retry
+		%10d   ; Expire
+		%10d ) ; Miminum
+]],
+		rec.mname,
+		rec.rname,
+		rec.serial,
+		rec.refresh,
+		rec.retry,
+		rec.expire,
+		rec.minimum ) end,
+  NAPTR = function(rec) return string.format([[
+%5d %5d (
+		%q
+		%q
+		%q
+		%s )
+]],
+		rec.order,
+		rec.preference,
+		rec.flags,
+		rec.services,
+		rec.regexp,
+		rec.replacement) end,
+  SRV = function(rec) return string.format(
+			"%5d %5d %5d %s",
+			rec.priority,
+			rec.weight,
+			rec.port,
+			rec.target) end,
+  LOC = function(rec) return string.format([[
+(
+		%3d %2d %2d %s ; Latitude
+		%3d %2d %2d %s ; Longitude
+		%11d ; Altitude
+		%11d ; Size
+		%11d ; Horizontal Precision
+		%11d ; Vertical Precision
+		)
+]],
+		rec.latitude.deg,
+		rec.latitude.min,
+		rec.latitude.sec,
+		rec.latitude.hemisphere,
+		rec.longitude.deg,
+		rec.longitude.min,
+		rec.longitude.sec,
+		rec.longitude.hemisphere,
+		rec.altitude,
+		rec.size,
+		rec.horiz_pre,
+		rec.vert_pre ) end
+}
+
+local function print_answers(tag,recs)
+  io.stdout:write(string.format("\n;;; %s\n\n",tag))
+  
+  for i = 1 , #recs do
+    s = string.format("%-16s\t%d\t%s\t%s\t",
+    		recs[i].name,
+    		recs[i].ttl,
+    		recs[i].class,
+    		recs[i].type
+    	)
+    s = s .. callbacks[recs[i].type](recs[i]) .. "\n"
+    io.stdout:write(s)
+  end
+end
+
+-- **********************************************************************
+
+
+if #arg == 0 then
+  io.stderr:write(string.format("usage: %s type domain\n",arg[0]))
+  os.exit(1)
+end
+
+local results,err
+
+results,err = query(arg[2],arg[1])
+
+if results == nil then
+  io.stderr:write(string.format(
+  	"error: query(%s,%s) = %s",
+  	arg[2],
+  	arg[1],
+  	err
+  	))
+  os.exit(1)
+end
+
+io.stdout:write(string.format([[
+; Questions            = 1
+; Answers              = %d
+; Name Servers         = %d
+; Additional Records   = %d
+; Authoritative Result = %s
+; Truncated Result     = %s
+; Recursion Desired    = %s
+; Recursion Available  = %s
+; Result               = %s
+
+;;; QUESTIONS
+
+; %s %s %s
+]],
+	#results.answers,
+	#results.nameservers,
+	#results.additional,
+	tostring(results.aa),
+	tostring(results.tc),
+	tostring(results.rd),
+	tostring(results.ra),
+	dns.strerror(results.rcode),
+	results.question.name,
+	results.question.class,
+	results.question.type
+))
+
+print_answers("ANSWERS"     , results.answers)
+print_answers("NAMESERVERS" , results.nameservers)
+print_answers("ADDITIONAL"  , results.additional)
+
+os.exit(0)
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/src/codec.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/src/codec.c	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,1695 @@
+/*************************************************************************
+*
+* Copyright 2010 by Sean Conner.  All Rights Reserved.
+*
+* This library is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or (at your
+* option) any later version.
+*
+* This library is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+* License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this library; if not, see <http://www.gnu.org/licenses/>.
+*
+**************************************************************************/
+
+/**********************************************************************
+*
+* Implements the code to encode a DNS query (*NOTE* only queries at this
+* time) and to decode responses from a DNS server.  This exports two
+* functions:
+*
+*  dns_encode()
+*
+*	This function takes a filled in dns_query_t structure (assumed to be
+*	filled out correctly and creates the wire representation of that
+*	query into a buffer supplied to the routine.
+*
+*	THIS ROUTINE DOES NOT ALLOCATE MEMORY, NOR DOES IT USE GLOBAL
+*	VARAIBLES. IT IS THEREFORE THREAD SAFE.
+*
+*	See test.c for an example of calling this routine.
+*
+*  dns_decode()
+*
+*	This function takes the wire representation of a response, decodes
+*	and returns a dns_query_t filled out with the various records.  You
+*	supply a block of memory sufficient enough to store the dns_query_t
+*	and any various strings/structures used in the dns_query_t (I've
+*	found 8K to be more than enough for decoding a UDP response but
+*	that's a very conservative value; 4K may be good enough).
+*
+*	THIS ROUTINE DOES NOT ALLOCATE MEMORY, NOR DOES IT USE GLOBAL
+*	VARIABLES.  IT IS THEREFORE THREAD SAFE.
+*
+*	See test.c for an example of calling this routine.
+*
+*	This code is written using C99.
+*
+* The code in here requires no other code from this project.
+*
+****************************************************************************/
+
+#define _GNU_SOURCE
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+#include <assert.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "dns.h"
+
+/*----------------------------------------------------------------------------
+; The folowing are used for memory allocation.  dns_decoded_t should be fine
+; for alignment size, as it's good enough for alignment.  If some odd-ball
+; system comes up that requires more strict alignment, then I'll change this
+; to something like a long double or something silly like that.
+;
+; see the comment align_memory() for more details
+;-----------------------------------------------------------------------------*/
+
+#define MEM_ALIGN	sizeof(dns_decoded_t)
+#define MEM_MASK	~(sizeof(dns_decoded_t) - 1uL)
+
+/************************************************************************/
+
+typedef struct block
+{
+  size_t   size;
+  uint8_t *ptr;
+} block_t;
+
+struct idns_header
+{
+  uint16_t id;
+  uint8_t  opcode;
+  uint8_t  rcode;
+  uint16_t qdcount;
+  uint16_t ancount;
+  uint16_t nscount;
+  uint16_t arcount;
+} __attribute__ ((packed));
+
+typedef struct idns_context
+{
+  block_t      packet;
+  block_t      parse;
+  block_t      dest;	/* see comments in align_memory() */
+  dns_query_t *response;
+  bool         edns;
+} idns_context;
+
+/***********************************************************************/
+
+static        dns_rcode_t  dns_encode_domain	(block_t *const restrict,const dns_question_t *const restrict) __attribute__ ((nothrow,nonnull));
+static inline dns_rcode_t  encode_edns0rr_nsid	(block_t *const restrict,const edns0_opt_t    *const restrict) __attribute__ ((nothrow,nonnull));
+static inline dns_rcode_t  encode_edns0rr_raw	(block_t *const restrict,const edns0_opt_t    *const restrict) __attribute__ ((nothrow,nonnull));
+static inline dns_rcode_t  encode_rr_opt    	(block_t *const restrict,const dns_query_t    *const restrict,const dns_edns0opt_t *const restrict) __attribute__ ((nothrow,nonnull));
+
+static        bool	   align_memory	(block_t *const)		__attribute__ ((nothrow,nonnull,   warn_unused_result));
+static        void        *alloc_struct	(block_t *const,const size_t)	__attribute__ ((nothrow,nonnull(1),warn_unused_result,malloc));
+
+static inline void         write_uint16 (block_t *const,uint16_t)                                         __attribute__ ((nothrow,nonnull(1)));
+static inline void         write_uint32 (block_t *const,uint32_t)                                         __attribute__ ((nothrow,nonnull(1)));
+static inline uint16_t	   read_uint16	(block_t *const)		                                  __attribute__ ((nothrow,nonnull));
+static inline uint32_t	   read_uint32	(block_t *const)		                                  __attribute__ ((nothrow,nonnull));
+static        dns_rcode_t  read_raw	(idns_context *const restrict,uint8_t    **restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static        dns_rcode_t  read_string	(idns_context *const restrict,const char **restrict)              __attribute__ ((nothrow,nonnull(1,2)));
+static        dns_rcode_t  read_domain	(idns_context *const restrict,const char **restrict)	          __attribute__ ((nothrow,nonnull));
+
+static inline dns_rcode_t  decode_edns0rr_nsid	(idns_context *const restrict,edns0_opt_t *const restrict) __attribute__ ((nothrow,nonnull));
+static inline dns_rcode_t  decode_edns0rr_raw	(idns_context *const restrict,edns0_opt_t *const restrict) __attribute__ ((nothrow,nonnull));
+
+static        dns_rcode_t  decode_question(idns_context *const restrict,dns_question_t *const restrict)		     __attribute__ ((nothrow,nonnull));
+static inline dns_rcode_t  decode_rr_soa  (idns_context *const restrict,dns_soa_t      *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_a	  (idns_context *const restrict,dns_a_t        *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_wks  (idns_context *const restrict,dns_wks_t      *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_mx	  (idns_context *const restrict,dns_mx_t       *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_txt  (idns_context *const restrict,dns_txt_t      *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_hinfo(idns_context *const restrict,dns_hinfo_t    *const restrict)              __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_naptr(idns_context *const restrict,dns_naptr_t    *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_aaaa (idns_context *const restrict,dns_aaaa_t     *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_srv  (idns_context *const restrict,dns_srv_t      *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_sig  (idns_context *const restrict,dns_sig_t      *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_minfo(idns_context *const restrict,dns_minfo_t    *const restrict)              __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_gpos (idns_context *const restrict,dns_gpos_t     *const restrict)              __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_loc  (idns_context *const restrict,dns_loc_t      *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static inline dns_rcode_t  decode_rr_opt  (idns_context *const restirct,dns_edns0opt_t *const restrict,const size_t) __attribute__ ((nothrow,nonnull(1,2)));
+static        dns_rcode_t  decode_answer  (idns_context *const restrict,dns_answer_t   *const restirct)              __attribute__ ((nothrow,nonnull(1,2)));
+
+/***********************************************************************/
+
+#ifndef NDEBUG
+  static int query_okay  (const dns_query_t *const)  __attribute__ ((unused));
+  static int pblock_okay (const block_t *const)      __attribute__ ((unused));
+  static int block_okay  (const block_t)             __attribute__ ((unused));
+  static int context_okay(const idns_context *const) __attribute__ ((unused));
+  
+  static int query_okay(const dns_query_t *const query)
+  {
+    assert(query          != NULL);
+    assert(query->id      >= 0);
+    assert(query->id      <= UINT16_MAX);
+    assert(query->opcode  <= 2);
+    assert(query->rcode   <= 5);
+    assert(query->qdcount <= UINT16_MAX);
+    assert(query->ancount <= UINT16_MAX);
+    assert(query->nscount <= UINT16_MAX);
+    assert(query->arcount <= UINT16_MAX);
+
+    if (query->query)
+    {
+      assert((query->opcode == OP_QUERY) || (query->opcode == OP_IQUERY));
+      assert(!query->aa);
+      assert(!query->tc);
+      assert(!query->ra);
+      assert(query->rcode == RCODE_OKAY);
+    }
+    return 1;
+  }
+  
+  static int pblock_okay(const block_t *const block)
+  {
+    assert(block       != NULL);
+    assert(block->ptr  != NULL);
+    assert(block->size >  0);
+    return 1;
+  }
+  
+  static int block_okay(const block_t block)
+  {
+    assert(block.ptr  != NULL);
+    assert(block.size >  0);
+    return 1;
+  }
+  
+  static int context_okay(const idns_context *const data)
+  {
+    assert(data     != NULL);
+    assert(data->response != NULL);
+    assert(block_okay(data->packet));
+    assert(block_okay(data->parse));
+    assert(block_okay(data->dest));
+    return 1;
+  }
+#endif
+
+/*******************************************************************/
+
+dns_rcode_t dns_encode(
+	dns_packet_t      *const restrict dest,
+	size_t            *const restrict plen,
+	const dns_query_t *const restrict query
+)
+{
+  struct idns_header *header;
+  uint8_t            *buffer;
+  block_t             data;
+  dns_rcode_t         rc;
+  
+  assert(dest  != NULL);
+  assert(plen  != NULL);
+  assert(*plen >= sizeof(struct idns_header));
+  assert(query_okay(query));
+  
+  memset(dest,0,*plen);
+  
+  buffer = (uint8_t *)dest;
+  header = (struct idns_header *)buffer;
+  
+  header->id      = htons(query->id);
+  header->opcode  = (query->opcode & 0x0F) << 3;
+  header->rcode   = (query->rcode  & 0x0F);
+  header->qdcount = htons(query->qdcount);
+  header->ancount = htons(query->ancount);
+  header->nscount = htons(query->nscount);
+  header->arcount = htons(query->arcount);
+
+  /*-----------------------------------------------------------------------
+  ; I'm not bothering with symbolic constants for the flags; they're only
+  ; used in two places in the code (the other being dns_encode()) and
+  ; they're not going to change.  It's also obvious from the context what
+  ; they're refering to.
+  ;-----------------------------------------------------------------------*/
+  
+  if (!query->query) header->opcode |= 0x80;
+  if (query->aa)     header->opcode |= 0x04;
+  if (query->tc)     header->opcode |= 0x02;
+  if (query->rd)     header->opcode |= 0x01;
+  if (query->ra)     header->rcode  |= 0x80;
+  if (query->ad)     header->rcode  |= 0x20;
+  if (query->cd)     header->rcode  |= 0x10;
+  
+  data.size = *plen - sizeof(struct idns_header);
+  data.ptr  = &buffer[sizeof(struct idns_header)];
+  
+  for (size_t i = 0 ; i < query->qdcount ; i++)
+  {
+    rc = dns_encode_domain(&data,&query->questions[i]);
+    if (rc != RCODE_OKAY)
+      return rc;
+  }
+  
+  /*------------------------------------------------------------
+  ; at some point we may want to encode answers, nameservers,
+  ; and additional records, but for now, we skip 'em, except
+  ; for EDNS stuff.
+  ;-----------------------------------------------------------*/
+  
+  assert(query->ancount == 0);
+  assert(query->nscount == 0);
+  
+  for (size_t i = 0 ; i < query->arcount ; i++)
+  {
+    switch(query->additional[i].generic.type)
+    {
+      case RR_OPT: rc = encode_rr_opt(&data,query,&query->additional[i].opt); break;
+      default:     assert(0); break;
+    }
+    
+    if (rc != RCODE_OKAY)
+      return rc;
+  }
+  
+  *plen = (size_t)(data.ptr - buffer);
+  return RCODE_OKAY;
+}
+
+/*********************************************************************/
+
+static dns_rcode_t dns_encode_domain(
+	block_t              *const restrict data,
+	const dns_question_t *const restrict pquestion
+)
+{
+  size_t   len;
+  size_t   delta;
+  uint8_t *start;
+  uint8_t *end;
+  uint8_t *back_ptr;
+  
+  assert(pblock_okay(data));
+  assert(pquestion        != NULL);
+  assert(pquestion->name  != NULL);
+  assert(pquestion->class >= 1);
+  assert(pquestion->class <= 4);
+  
+  len = strlen(pquestion->name);
+  
+  if (pquestion->name[len - 1] != '.')	/* name must be fully qualified */
+    return RCODE_NAME_ERROR;
+  
+  if (data->size < len + 5)	/* not enough space */
+    return RCODE_NO_MEMORY;
+  
+  memcpy(&data->ptr[1],pquestion->name,len);
+  data->size -= (len + 5);
+  
+  back_ptr = data->ptr;
+  start    = &data->ptr[1];
+  end      = &data->ptr[1];
+  
+  while(len)
+  {
+    end   = memchr(start,'.',len);
+    assert(end != NULL);	/* must be true---checked above */
+  
+    delta = (size_t)(end - start);
+    assert(delta <= len);
+    
+    if (delta > 63)
+      return RCODE_NAME_ERROR;
+  
+    *back_ptr  = (uint8_t)delta;
+    back_ptr   = end;
+    start      = end + 1;
+    len       -= (delta + 1);
+  }
+  
+  *back_ptr  = 0;
+  data->ptr  = end + 1;
+  
+  data->ptr[0] = (pquestion->type  >> 8);
+  data->ptr[1] = (pquestion->type  &  0xFF);
+  data->ptr[2] = (pquestion->class >> 8);
+  data->ptr[3] = (pquestion->class &  0xFF);
+  data->ptr += 4;
+  
+  return RCODE_OKAY;
+}
+
+/*************************************************************************/
+
+static inline dns_rcode_t encode_edns0rr_nsid(
+	block_t           *const restrict data,
+	const edns0_opt_t *const restrict opt
+)
+{
+  size_t newlen;
+  
+  assert(pblock_okay(data));
+  assert(opt       != NULL);
+  assert(opt->code == EDNS0RR_NSID);
+  assert(opt->len  <= UINT16_MAX);
+  
+  /*------------------------------------------------------------------------
+  ; RFC-5001 specifies that the data for an NSID RR is the hexstring of the
+  ; data, and no other meaning from the strings is to be inferred.  So we
+  ; encode the data to save you from doing it.
+  ;------------------------------------------------------------------------*/
+  
+  newlen = opt->len * 2;
+  if (data->size < newlen + sizeof(uint16_t) + sizeof(uint16_t))
+    return RCODE_NO_MEMORY;
+  
+  char   buffer[newlen + 1];
+  size_t nidx;
+  size_t i;
+  
+  for (i = nidx = 0 ; i < opt->len ; i++ , nidx += 2)
+    sprintf(&buffer[nidx],"%02X",opt->data[i]);
+  
+  assert(newlen == strlen(buffer));
+  
+  write_uint16(data,opt->code);
+  write_uint16(data,newlen);
+  memcpy(data->ptr,buffer,newlen);
+  data->ptr  += newlen;
+  data->size -= newlen;
+  return RCODE_OKAY;
+}
+
+/**********************************************************************/
+
+static inline dns_rcode_t encode_edns0rr_raw(
+	block_t           *const restrict data,
+	const edns0_opt_t *const restrict opt
+)
+{
+  assert(pblock_okay(data));
+  assert(opt       != NULL);
+  assert(opt->code <= UINT16_MAX);
+  assert(opt->len  <= UINT16_MAX);
+  
+  if (data->size < opt->len + sizeof(uint16_t) + sizeof(uint16_t))
+    return RCODE_NO_MEMORY;
+  
+  write_uint16(data,opt->code);
+  write_uint16(data,opt->len);
+  memcpy(data->ptr,opt->data,opt->len);
+  data->ptr  += opt->len;
+  data->size -= opt->len;
+  return RCODE_OKAY;
+}
+
+/*************************************************************************/
+
+static inline dns_rcode_t encode_rr_opt(
+	block_t              *const restrict data,
+	const dns_query_t    *const restrict query,
+	const dns_edns0opt_t *const restrict opt
+)
+{
+  uint8_t *prdlen;
+  size_t   rdlen;
+  size_t   i;
+  
+  assert(pblock_okay(data));
+  assert(query            != NULL);
+  assert(opt              != NULL);
+  assert(opt->class       == CLASS_UNKNOWN);
+  assert(opt->ttl         == 0);
+  assert(opt->version     == 0);
+  assert(opt->udp_payload <= UINT16_MAX);
+  
+  if (data->size < 11)
+    return RCODE_NO_MEMORY;
+
+  data->ptr[0] = 00;	/* root domain */
+  data->ptr++;
+  data->size--;
+
+  write_uint16(data,RR_OPT);
+  write_uint16(data,opt->udp_payload);
+  data->ptr[0] = query->rcode >> 4;
+  data->ptr[1] = opt->version;
+  data->ptr[2] = 0;
+  data->ptr[3] = 0;
+  
+  if (opt->fdo) data->ptr[2] |= 0x80;
+    
+  data->ptr  += 4;
+  data->size -= 4;
+  
+  /*----------------------------------------------------------------------
+  ; save the location for RDLEN, and set it to 0 for now.  After we encode
+  ; the rest of the packet, we'll patch this with the correct length.
+  ;----------------------------------------------------------------------*/
+  
+  prdlen = data->ptr;
+  write_uint16(data,0);	/* place holder for now */
+  
+  for (i = 0 ; i < opt->numopts; i++)
+  {
+    dns_rcode_t rc;
+    
+    switch(opt->opts[i].code)
+    {
+      case EDNS0RR_NSID: rc = encode_edns0rr_nsid(data,&opt->opts[i]); break;
+      default:           rc = encode_edns0rr_raw (data,&opt->opts[i]); break;
+    }
+    
+    if (rc != RCODE_OKAY) return rc;
+  }
+  
+  rdlen     = (size_t)(data->ptr - prdlen) - sizeof(uint16_t);
+  prdlen[0] = (rdlen >> 8) & 0xFF;
+  prdlen[1] = (rdlen     ) & 0xFF;
+  
+  return RCODE_OKAY;
+}
+
+/*************************************************************************
+*
+* Memory allocations are done quickly.  The dns_decode() routine is given a
+* block of memory to carve allocations out of (4k appears to be good eough;
+* 8k is more than enough for UDP packets) and there's no real intelligence
+* here---just a quick scheme.  String information is just allocated starting
+* at the next available location (referenced in context->dest) whereas the
+* few structures that do need allocating require the free pointer to be
+* adjusted to a proper memory alignment.  If you need alignments, call
+* alloc_struct(), otherwise for strings, use context->dest directly.  You
+* *can* use align_memory() directly, just be sure you know what you are
+* doing.
+*
+******************************************************************************/
+
+static bool align_memory(block_t *const pool)
+{
+  size_t newsize;
+  size_t delta;
+  
+  assert(pblock_okay(pool));
+  
+  if (pool->size < MEM_ALIGN)
+    return false;
+  
+  newsize = pool->size & MEM_MASK;
+  if (newsize == pool->size)
+    return true;
+  
+  assert(newsize < pool->size);
+  delta = (newsize + MEM_ALIGN) - pool->size;
+  assert(delta < pool->size);
+  
+  pool->ptr  += delta;
+  pool->size -= delta;
+  
+  return true;
+}
+
+/*************************************************************************/  
+
+static void *alloc_struct(block_t *const pool,const size_t size)
+{
+  uint8_t *ptr;
+  
+  assert(pblock_okay(pool));
+  
+  if (pool->size == 0)      return NULL;
+  if (!align_memory(pool))  return NULL;
+  if (pool->size < size)    return NULL;
+  
+  ptr         = pool->ptr;
+  pool->ptr  += size;
+  pool->size -= size;
+  return (void *)ptr;
+}
+
+/***********************************************************************/
+
+static inline void write_uint16(block_t *const parse,uint16_t value)
+{
+  assert(pblock_okay(parse));
+  assert(parse->size >= 2);
+  
+  parse->ptr[0] = (value >> 8) & 0xFF;
+  parse->ptr[1] = (value     ) & 0xFF;
+  parse->ptr  += 2;
+  parse->size -= 2;
+}
+
+/***********************************************************************/
+
+static inline void write_uint32(block_t *const parse,uint32_t value)
+{
+  assert(pblock_okay(parse));
+  assert(parse->size >= 4);
+  
+  parse->ptr[0] = (value >> 24) & 0xFF;
+  parse->ptr[1] = (value >> 16) & 0xFF;
+  parse->ptr[2] = (value >>  8) & 0xFF;
+  parse->ptr[3] = (value      ) & 0xFF;
+  parse->ptr  += 4;
+  parse->size -= 4;
+}
+
+/***********************************************************************/
+
+static inline uint16_t read_uint16(block_t *const parse)
+{
+  uint16_t val;
+  
+  /*------------------------------------------------------------------------
+  ; caller is reponsible for making sure there's at least two bytes to read
+  ;------------------------------------------------------------------------*/
+  
+  assert(pblock_okay(parse));
+  assert(parse->size >= 2);
+  
+  val = (parse->ptr[0] << 8) 
+      | (parse->ptr[1]     );
+  parse->ptr  += 2;
+  parse->size -= 2;
+  return val;
+}
+
+/********************************************************************/  
+
+static inline uint32_t read_uint32(block_t *const parse)
+{
+  uint32_t val;
+
+  /*------------------------------------------------------------------------
+  ; caller is reponsible for making sure there's at least four bytes to read
+  ;------------------------------------------------------------------------*/
+  
+  assert(pblock_okay(parse));  
+  assert(parse->size >= 4);
+  
+  val = (parse->ptr[0] << 24) 
+      | (parse->ptr[1] << 16) 
+      | (parse->ptr[2] <<  8)
+      | (parse->ptr[3]      );
+  parse->ptr  += 4;
+  parse->size -= 4;
+  return val;
+}
+
+/********************************************************************/
+
+static dns_rcode_t read_raw(
+	idns_context  *const restrict data,
+	uint8_t      **restrict       result,
+	const size_t                  len
+)
+{
+  assert(context_okay(data));
+  assert(result != NULL);
+  
+  if (len > 0)
+  {  
+    if (len > data->parse.size)
+      return RCODE_FORMAT_ERROR;
+
+    /*--------------------------------------------------------------------
+    ; Called when we don't know the contents of the data; it's aligned so
+    ; that if the data is actually structured, it can probably be read
+    ; directly by the clients of this code.
+    ;--------------------------------------------------------------------*/
+    
+    if (!align_memory(&data->dest))
+      return RCODE_NO_MEMORY;
+
+    if (len > data->dest.size)
+      return RCODE_NO_MEMORY;
+    
+    *result = data->dest.ptr;
+    memcpy(data->dest.ptr,data->parse.ptr,len);
+    data->parse.ptr  += len;
+    data->parse.size -= len;
+    data->dest.ptr   += len;
+    data->dest.size  -= len;
+  }
+  else
+    *result = NULL;
+    
+  return RCODE_OKAY;
+}
+
+/********************************************************************/
+
+static dns_rcode_t read_string(
+	idns_context  *const restrict data,
+	const char   **restrict       result
+)
+{
+  size_t len;
+  
+  assert(context_okay(data));
+  assert(result != NULL);
+
+  len = *data->parse.ptr;
+  
+  if (data->dest.size < len + 1) /* adjust for NUL byte */
+    return RCODE_NO_MEMORY;
+  
+  if (data->parse.size < len + 1) /* adjust for length byte */
+    return RCODE_FORMAT_ERROR;
+  
+  *result = (char *)data->dest.ptr;
+  memcpy(data->dest.ptr,&data->parse.ptr[1],len);
+  
+  data->parse.ptr  += (len + 1);
+  data->parse.size -= (len + 1);
+  data->dest.ptr   += len;
+  data->dest.size  -= len;
+  *data->dest.ptr++ = '\0';
+  data->dest.size--;
+  
+  return RCODE_OKAY; 
+}
+
+/********************************************************************/
+
+static dns_rcode_t read_domain(
+	idns_context  *const restrict data,
+	const char   **restrict       result
+)
+{
+  block_t *parse = &data->parse;
+  block_t  tmp;
+  size_t   len;
+  int      loop;	/* loop detection */
+  
+  assert(context_okay(data));
+  assert(result != NULL);
+  
+  *result = (char *)data->dest.ptr;
+  loop    = 0;
+  
+  do
+  {
+    /*----------------------------
+    ; read in a domain segment
+    ;-----------------------------*/
+    
+    if (*parse->ptr < 64)
+    {
+      len = *parse->ptr;
+      
+      if (parse->size < len + 1)
+        return RCODE_FORMAT_ERROR;
+
+      if (data->dest.size < len)
+        return RCODE_NO_MEMORY;
+      
+      if (len)
+      {
+        memcpy(data->dest.ptr,&parse->ptr[1],len);
+        parse->ptr         += (len + 1);
+        parse->size        -= (len + 1);
+      }
+
+      data->dest.size   -= (len + 1);
+      data->dest.ptr    += len;
+      *data->dest.ptr++  = '.';
+    }
+    
+    /*------------------------------------------
+    ; compressed segment---follow the pointer
+    ;------------------------------------------*/
+    
+    else if (*parse->ptr >= 192)
+    {
+      if (++loop == 256)
+        return RCODE_FORMAT_ERROR;
+      
+      if (parse->size < 2)
+        return RCODE_FORMAT_ERROR;
+      
+      len = read_uint16(parse) & 0x3FFF;
+      
+      if (len >= data->packet.size)
+        return RCODE_FORMAT_ERROR;
+      
+      tmp.ptr = &data->packet.ptr[len];
+      tmp.size = data->packet.size - (size_t)(tmp.ptr - data->packet.ptr);
+      parse    = &tmp;
+    }
+    
+    /*-----------------------------------------------------------------------
+    ; EDNS0 extended labeles, RFC-2671; the only extension proposed so far,
+    ; RFC-2673, was changed from Proposed to Experimental in RFC-3363, so
+    ; I'm not including support for it at this time.
+    ;-----------------------------------------------------------------------*/
+
+    else if ((*parse->ptr >= 64) && (*parse->ptr <= 127))
+      return RCODE_FORMAT_ERROR;
+
+    /*------------------------------------
+    ; reserved for future developments
+    ;------------------------------------*/
+
+    else
+      return RCODE_FORMAT_ERROR;
+  } while(*parse->ptr);
+  
+  parse->ptr++;
+  parse->size--;
+  *data->dest.ptr++ = '\0';
+  data->dest.size--;
+  
+  return RCODE_OKAY;
+}
+
+/********************************************************************/
+
+static inline dns_rcode_t decode_edns0rr_nsid(
+	idns_context *const restrict data,
+	edns0_opt_t  *const restrict opt
+)
+{
+  static const char hexdigits[] = "0123456789ABCDEF";
+  
+  if (opt->len % 2 == 1)
+    return RCODE_FORMAT_ERROR;
+    
+  if (data->dest.size < opt->len / 2)
+    return RCODE_NO_MEMORY;
+  
+  for (size_t i = 0 ; i < opt->len ; i += 2)
+  {
+    const char *phexh;
+    const char *phexl;
+    
+    if (!isxdigit(data->parse.ptr[i]))   return RCODE_FORMAT_ERROR;
+    if (!isxdigit(data->parse.ptr[i+1])) return RCODE_FORMAT_ERROR;
+    
+    phexh = memchr(hexdigits,toupper(data->parse.ptr[i])  ,16);
+    phexl = memchr(hexdigits,toupper(data->parse.ptr[i+1]),16);
+    
+    /*------------------------------------------------------------------
+    ; phexh and phexl should not be NULL, unless isxdigit() is buggy, and
+    ; that is something I'm not assuming.
+    ;--------------------------------------------------------------------*/
+    
+    assert(phexh != NULL);
+    assert(phexl != NULL);
+    
+    *data->dest.ptr = ((phexh - hexdigits) << 4)
+                    | ((phexl - hexdigits)     );
+    data->dest.ptr++;
+    data->dest.size--;
+  }
+  
+  data->parse.ptr  += opt->len;
+  data->parse.size -= opt->len;
+  opt->len         /= 2;
+  return RCODE_OKAY;
+}
+
+/***********************************************************************/
+
+static inline dns_rcode_t decode_edns0rr_raw(
+	idns_context *const restrict data,
+	edns0_opt_t  *const restrict opt
+)
+{
+  if (data->dest.size < opt->len)
+    return RCODE_NO_MEMORY;
+  
+  memcpy(data->dest.ptr,data->parse.ptr,opt->len);
+  data->parse.ptr  += opt->len;
+  data->parse.size -= opt->len;
+  data->dest.ptr   += opt->len;
+  data->dest.size  -= opt->len;
+  return RCODE_OKAY;
+}
+
+/*************************************************************/
+
+static dns_rcode_t decode_question(
+	idns_context   *const restrict data,
+	dns_question_t *const restrict pquest
+)
+{
+  dns_rcode_t rc;
+  
+  assert(context_okay(data));
+  assert(pquest != NULL);
+  
+  rc = read_domain(data,&pquest->name);
+  if (rc != RCODE_OKAY)
+    return rc;
+  
+  if (data->parse.size < 4)
+    return RCODE_FORMAT_ERROR;
+    
+  pquest->type  = (dns_type_t) read_uint16(&data->parse);
+  pquest->class = (dns_class_t)read_uint16(&data->parse);
+  
+  /*-------------------------------------------------------
+  ; OPT RRs can never be the target of a question as it's
+  ; more of a pseudo RR than a real live boy, um, RR.
+  ;--------------------------------------------------------*/
+  
+  if (pquest->type == RR_OPT)
+    return RCODE_FORMAT_ERROR;
+    
+  return RCODE_OKAY;
+}
+
+/************************************************************************/
+
+static inline dns_rcode_t decode_rr_soa(
+	idns_context *const restrict data,
+	dns_soa_t    *const restrict psoa,
+	const size_t                 len
+)
+{
+  dns_rcode_t rc;
+  
+  assert(context_okay(data));
+  assert(psoa != NULL);
+  
+  rc = read_domain(data,&psoa->mname);
+  if (rc != RCODE_OKAY) return rc;
+  rc = read_domain(data,&psoa->rname);
+  if (rc != RCODE_OKAY) return rc;
+  
+  if (len < 20)
+    return RCODE_FORMAT_ERROR;
+  
+  psoa->serial  = read_uint32(&data->parse);
+  psoa->refresh = read_uint32(&data->parse);
+  psoa->retry   = read_uint32(&data->parse);
+  psoa->expire  = read_uint32(&data->parse);
+  psoa->minimum = read_uint32(&data->parse);
+  
+  return RCODE_OKAY; 
+}
+
+/***********************************************************************/
+
+static inline dns_rcode_t decode_rr_a(
+	idns_context *const restrict data,
+	dns_a_t      *const restrict pa,
+	const size_t                 len
+)
+{
+  assert(context_okay(data));
+  assert(pa != NULL);
+
+  if (len != 4) return RCODE_FORMAT_ERROR;
+  memcpy(&pa->address,data->parse.ptr,4);
+  data->parse.ptr  += 4;
+  data->parse.size -= 4;
+  return RCODE_OKAY;
+}
+
+/***********************************************************************/
+
+static inline dns_rcode_t decode_rr_aaaa(
+	idns_context *const restrict data,
+	dns_aaaa_t   *const restrict pa,
+	const size_t                 len
+)
+{
+  assert(context_okay(data));
+  assert(pa != NULL);
+  
+  if (len != 16) return RCODE_FORMAT_ERROR;
+  memcpy(pa->address.s6_addr,data->parse.ptr,16);
+  data->parse.ptr  += 16;
+  data->parse.size -= 16;
+  return RCODE_OKAY;
+}
+
+/**********************************************************************/
+
+static inline dns_rcode_t decode_rr_wks(
+	idns_context *const restrict data,
+	dns_wks_t    *const restrict pwks,
+	const size_t                 len
+)
+{
+  assert(context_okay(data));
+  assert(pwks != NULL);
+  
+  if (len < 6) return RCODE_FORMAT_ERROR;
+
+  memcpy(&pwks->address,data->parse.ptr,4);
+  data->parse.ptr  += 4;
+  data->parse.size -= 4;
+  pwks->protocol = read_uint16(&data->parse);
+  
+  pwks->numbits = len - 6;  
+  return read_raw(data,&pwks->bits,pwks->numbits);
+}
+
+/*********************************************************************/
+
+static inline dns_rcode_t decode_rr_mx(
+	idns_context *const restrict data,
+	dns_mx_t     *const restrict pmx,
+	const size_t                 len
+)
+{
+  assert(context_okay(data));
+  assert(pmx != NULL);
+
+  if (len < 4) return RCODE_FORMAT_ERROR;
+  
+  pmx->preference = read_uint16(&data->parse);
+  return read_domain(data,&pmx->exchange);
+}
+
+/**********************************************************************/
+
+static inline dns_rcode_t decode_rr_txt(
+	idns_context *const restrict data,
+	dns_txt_t    *const restrict ptxt,
+	const size_t                 len
+)
+{
+  block_t tmp;
+  size_t  worklen;
+  size_t  items;
+  size_t  slen;
+  
+  assert(context_okay(data));
+  assert(ptxt != NULL);
+  
+  /*--------------------------------------------------------------------
+  ; collapse multiple strings (which are allowed per the spec) into one
+  ; large string.  Cache the length as well, as some records might prefer
+  ; the length to be there (in case of binary data)
+  ;---------------------------------------------------------------------*/
+  
+  tmp       = data->parse;
+  worklen   = len;
+  ptxt->len = 0;
+  
+  for (items = 0 ; worklen ; )
+  {
+    slen = *tmp.ptr + 1;
+    
+    if (tmp.size < slen)
+      return RCODE_FORMAT_ERROR;
+    
+    items++;
+    ptxt->len += slen - 1;
+    tmp.ptr   += slen;
+    tmp.size  -= slen;
+    worklen   -= slen;
+  }
+  
+  ptxt->text = (const char *)data->dest.ptr;
+  
+  for (size_t i = 0 ; i < items ; i++)
+  {
+    slen = *data->parse.ptr;
+    if (data->dest.size < slen)
+      return RCODE_NO_MEMORY;
+      
+    memcpy(data->dest.ptr,&data->parse.ptr[1],slen);
+    data->dest.ptr   += slen;
+    data->dest.size  -= slen;
+    data->parse.ptr  += (slen + 1);
+    data->parse.size -= (slen + 1);
+  }
+  
+  if (data->dest.size == 0)
+    return RCODE_NO_MEMORY;
+  
+  *data->dest.ptr++ = '\0';
+  data->dest.size--;
+  
+  return RCODE_OKAY;
+}
+
+/**********************************************************************/
+
+static inline dns_rcode_t decode_rr_hinfo(
+	idns_context *const restrict data,
+	dns_hinfo_t  *const restrict phinfo
+)
+{
+  dns_rcode_t rc;
+  
+  assert(context_okay(data));
+  assert(phinfo != NULL);
+  
+  rc = read_string(data,&phinfo->cpu);
+  if (rc != RCODE_OKAY) return rc;
+  rc = read_string(data,&phinfo->os);
+  return rc;
+}
+
+/**********************************************************************/
+
+static inline dns_rcode_t decode_rr_srv(
+	idns_context *const restrict data,
+	dns_srv_t    *const restrict psrv,
+	const size_t                 len
+)
+{
+  assert(context_okay(data));
+  assert(psrv != NULL);
+  
+  if (len < 7)
+    return RCODE_FORMAT_ERROR;
+  
+  psrv->priority = read_uint16(&data->parse);
+  psrv->weight   = read_uint16(&data->parse);
+  psrv->port     = read_uint16(&data->parse);
+  return read_domain(data,&psrv->target);
+}
+
+/**********************************************************************/
+
+static inline dns_rcode_t decode_rr_naptr(
+	idns_context *const restrict data,
+	dns_naptr_t  *const restrict pnaptr,
+	const size_t                 len
+)
+{
+  dns_rcode_t rc;
+  
+  assert(context_okay(data));
+  assert(pnaptr != NULL);
+  
+  if (len < 4)
+    return RCODE_FORMAT_ERROR;
+  
+  pnaptr->order      = read_uint16(&data->parse);
+  pnaptr->preference = read_uint16(&data->parse);
+  
+  rc = read_string(data,&pnaptr->flags);
+  if (rc != RCODE_OKAY) return rc;
+  rc = read_string(data,&pnaptr->services);
+  if (rc != RCODE_OKAY) return rc;
+  rc = read_string(data,&pnaptr->regexp);
+  if (rc != RCODE_OKAY) return rc;
+  return read_domain(data,&pnaptr->replacement);
+}
+
+/********************************************************************/
+
+static inline dns_rcode_t decode_rr_sig(
+	idns_context *const restrict data,
+	dns_sig_t    *const restrict psig,
+	const size_t                 len
+)
+{
+  uint8_t     *start;
+  size_t       sofar;
+  dns_rcode_t  rc;
+  
+  assert(context_okay(data));
+  assert(psig != NULL);
+  
+  if (len < 18)
+    return RCODE_FORMAT_ERROR;
+  
+  /*-----------------------------------------------------------------------
+  ; The signature portion doesn't have a length code.  Because of that, we
+  ; need to track how much data is left so we can pull it out.  We record
+  ; the start of the parsing area, and once we get past the signer, we can
+  ; calculate the remainder data to pull out.
+  ;------------------------------------------------------------------------*/
+
+  start = data->parse.ptr;
+  
+  psig->covered      = read_uint16(&data->parse);
+  psig->algorithm    = *data->parse.ptr++; data->parse.size--;
+  psig->labels       = *data->parse.ptr++; data->parse.size--;
+  psig->originttl    = read_uint32(&data->parse);
+  psig->sigexpire    = read_uint32(&data->parse);
+  psig->timesigned   = read_uint32(&data->parse);
+  psig->keyfootprint = read_uint16(&data->parse);
+  
+  rc = read_domain(data,&psig->signer);
+  if (rc != RCODE_OKAY) return rc;
+  
+  sofar = (size_t)(data->parse.ptr - start);
+  if (sofar > len) return RCODE_FORMAT_ERROR;
+  
+  psig->sigsize = len - sofar;
+  return read_raw(data,&psig->signature,psig->sigsize);
+}
+
+/******************************************************************/
+
+static inline dns_rcode_t decode_rr_minfo(
+		idns_context *const restrict data,
+		dns_minfo_t  *const restrict pminfo
+)
+{
+  dns_rcode_t rc;
+  
+  assert(context_okay(data));
+  assert(pminfo != NULL);
+  
+  rc = read_domain(data,&pminfo->rmailbx);
+  if (rc != RCODE_OKAY) return rc;
+  return read_domain(data,&pminfo->emailbx);
+}
+
+/*****************************************************************/
+
+static dns_rcode_t dloc_double(idns_context *const restrict,double *const restrict) __attribute__ ((nonnull));
+
+static dns_rcode_t dloc_double(
+		idns_context *const restrict data,
+		double       *const restrict pvalue
+)
+{
+  size_t len;
+ 
+  assert(context_okay(data));
+  assert(pvalue != NULL);
+ 
+  len = *data->parse.ptr;
+  if (len > data->parse.size - 1)
+    return RCODE_FORMAT_ERROR;
+
+  char buffer[len + 1];
+  memcpy(buffer,&data->parse.ptr[1],len);
+  buffer[len++] = '\0';
+  
+  data->parse.ptr += len;
+  data->parse.size -= len;
+  
+  errno = 0;
+  *pvalue = strtod(buffer,NULL);
+  if (errno) return RCODE_FORMAT_ERROR;
+  
+  return RCODE_OKAY;
+}
+
+/****************************************************************/
+
+static void dgpos_angle(dnsgpos_angle *const restrict,double) __attribute__ ((nonnull(1)));
+
+static void dgpos_angle(
+	dnsgpos_angle *const restrict pa,
+	double                        v
+)
+{
+  double ip;
+  
+  v = modf(v,&ip) *   60.0; pa->deg = ip;
+  v = modf(v,&ip) *   60.0; pa->min = ip;
+  v = modf(v,&ip) * 1000.0; pa->sec = ip;
+  pa->frac = v;
+}
+
+/*****************************************************************/
+
+static inline dns_rcode_t decode_rr_gpos(
+		idns_context *const restrict data,
+		dns_gpos_t   *const restrict pgpos
+)
+{
+  dns_rcode_t rc;
+  double      lat;
+  double      lng;
+
+  assert(context_okay(data));
+  assert(pgpos != NULL);
+  
+  rc = dloc_double(data,&lng);
+  if (rc != RCODE_OKAY) return rc;
+  rc = dloc_double(data,&lat);
+  if (rc != RCODE_OKAY) return rc;
+  
+  if (lng < 0.0)
+  {
+    pgpos->longitude.nw = true;
+    lng                 = fabs(lng);
+  }
+  
+  if (lat >= 0.0)
+    pgpos->latitude.nw = true;
+  else
+    lat = fabs(lat);
+    
+  dgpos_angle(&pgpos->longitude,lng);
+  dgpos_angle(&pgpos->latitude, lat);
+  
+  return dloc_double(data,&pgpos->altitude);
+}
+
+/**************************************************************************
+*
+* You really, no, I mean it, *REALLY* need to read RFC-1876 to understand
+* all the crap that's going on for deciphering RR_LOC.
+*
+**************************************************************************/
+
+#define LOC_BIAS	(((unsigned long)INT32_MAX) + 1uL)
+#define LOC_LAT_MAX	((unsigned long)( 90uL * 3600000uL))
+#define LOC_LNG_MAX	((unsigned long)(180uL * 3600000uL))
+#define LOC_ALT_BIAS	(10000000L)
+
+static int dloc_scale(unsigned long *const restrict,const int) __attribute__ ((nonnull(1)));
+
+static int dloc_scale(
+	unsigned long *const restrict presult,
+	const int                     scale
+)
+{
+  int spow;
+  int smul;
+  
+  assert(presult != NULL);
+  
+  smul = scale >> 4;
+  spow = scale & 0x0F;
+  
+  if ((spow > 9) || (smul > 9))
+    return RCODE_FORMAT_ERROR;
+  
+  *presult = (unsigned long)(pow(10.0,spow) * smul);
+  return RCODE_OKAY;
+}
+
+/**************************************************************/
+
+static void dloc_angle(dnsgpos_angle *const restrict,const long) __attribute__ ((nonnull(1)));
+
+static void dloc_angle(
+	dnsgpos_angle *const restrict pa,
+	const long                    v
+)
+{
+  ldiv_t partial;
+  
+  assert(pa != NULL);
+  
+  partial  = ldiv(v,1000L);
+  pa->frac = partial.rem;
+  partial  = ldiv(partial.quot,60L);
+  pa->sec  = partial.rem;
+  partial  = ldiv(partial.quot,60L);
+  pa->min  = partial.rem;
+  pa->deg  = partial.quot;
+}
+
+/*************************************************************/
+
+static inline dns_rcode_t decode_rr_loc(
+		idns_context *const restrict data,
+		dns_loc_t    *const restrict ploc,
+		const size_t                 len
+)
+{
+  dns_rcode_t    rc;
+  unsigned long  lat;
+  unsigned long  lng;
+  
+  assert(context_okay(data));
+  assert(ploc != NULL);
+  
+  if (len < 16) return RCODE_FORMAT_ERROR;
+
+  ploc->version = data->parse.ptr[0];
+  
+  if (ploc->version != 0)
+    return RCODE_FORMAT_ERROR;
+  
+  rc = dloc_scale(&ploc->size,data->parse.ptr[1]);
+  if (rc != RCODE_OKAY) return rc;
+  rc = dloc_scale(&ploc->horiz_pre,data->parse.ptr[2]);
+  if (rc != RCODE_OKAY) return rc;
+  rc = dloc_scale(&ploc->vert_pre,data->parse.ptr[3]);
+  if (rc != RCODE_OKAY) return rc;
+  
+  data->parse.ptr += 4;
+  
+  lat            = read_uint32(&data->parse);
+  lng            = read_uint32(&data->parse);
+  ploc->altitude = read_uint32(&data->parse) - LOC_ALT_BIAS;
+  
+  if (lat >= LOC_BIAS)	/* north */
+  {
+    ploc->latitude.nw = true;
+    lat -= LOC_BIAS;
+  }
+  else
+    lat = LOC_BIAS - lat;
+  
+  if (lng >= LOC_BIAS)	/* east */
+    lng -= LOC_BIAS;
+  else
+  {
+    ploc->longitude.nw = true;
+    lng = LOC_BIAS - lng;
+  }
+  
+  if (lat > LOC_LAT_MAX)
+    return RCODE_FORMAT_ERROR;
+  
+  if (lng > LOC_LNG_MAX)
+    return RCODE_FORMAT_ERROR;
+  
+  dloc_angle(&ploc->latitude ,lat);
+  dloc_angle(&ploc->longitude,lng);
+  
+  return RCODE_OKAY;
+}
+
+/***************************************************************/
+
+static inline dns_rcode_t decode_rr_opt(
+                idns_context   *const restrict data,
+                dns_edns0opt_t *const restrict opt,
+                const size_t                   len
+)
+{
+  assert(data != NULL);
+  assert(opt  != NULL);
+  
+  if (data->edns) /* there can be only one */
+    return RCODE_FORMAT_ERROR;
+  
+  data->edns   = true;
+  opt->numopts = 0;
+  opt->opts    = NULL;
+  
+  if (len)
+  {
+    uint8_t *scan;
+    size_t   length;
+    
+    assert(context_okay(data));
+    assert(len > 4);
+    
+    for (scan = data->parse.ptr , opt->numopts = 0 , length = len ; length > 0 ; )
+    {
+      size_t size;
+      
+      opt->numopts++;
+      size    = ((scan[2] << 8) | (scan[3])) + 4;
+      scan   += size;
+
+      if (size > length)
+        return RCODE_FORMAT_ERROR;
+
+      length -= size;
+    }
+    
+    opt->opts = alloc_struct(&data->dest,sizeof(edns0_opt_t) * opt->numopts);
+    if (opt->opts == NULL)
+      return RCODE_NO_MEMORY;
+    
+    for (size_t i = 0 ; i < opt->numopts ; i++)
+    {
+      dns_rcode_t rc;
+      
+      opt->opts[i].code = read_uint16(&data->parse);
+      opt->opts[i].len  = read_uint16(&data->parse);
+      
+      /*-----------------------------------------------------------------
+      ; much like in read_raw(), we don't necessarily know the data we're
+      ; reading, so why not align it?
+      ;------------------------------------------------------------------*/
+
+      if (!align_memory(&data->dest))
+        return RCODE_NO_MEMORY;
+
+      opt->opts[i].data = data->dest.ptr;
+      
+      switch(opt->opts[i].code)
+      {
+        case EDNS0RR_NSID: rc = decode_edns0rr_nsid(data,&opt->opts[i]); break;
+        default:           rc = decode_edns0rr_raw (data,&opt->opts[i]); break;
+      }
+      
+      if (rc != RCODE_OKAY) return rc;
+    }
+  }
+  
+  return RCODE_OKAY;
+}
+
+/**********************************************************************/
+
+static dns_rcode_t decode_answer(
+		idns_context *const restrict data,
+		dns_answer_t *const restrict pans
+)
+{
+  size_t      len;
+  size_t      rest;
+  dns_rcode_t rc;
+  
+  assert(context_okay(data));
+  assert(pans != NULL);
+  
+  rc = read_domain(data,&pans->generic.name);
+  if (rc != RCODE_OKAY)
+    return rc;
+  
+  if (data->parse.size < 10)
+    return RCODE_FORMAT_ERROR;
+    
+  pans->generic.type = read_uint16(&data->parse);
+  
+  /*-----------------------------------------------------------------
+  ; RR_OPT is annoying, since the defined class and ttl fields are
+  ; interpreted completely differently.  Thanks a lot, Paul Vixie!  So we
+  ; need to special case this stuff a bit.
+  ;----------------------------------------------------------------*/
+  
+  if (pans->generic.type == RR_OPT)
+  {
+    pans->generic.class   = CLASS_UNKNOWN;
+    pans->generic.ttl     = 0;
+    pans->opt.udp_payload = read_uint16(&data->parse);
+    data->response->rcode = (data->parse.ptr[0] << 4) | data->response->rcode;
+
+    if (data->parse.ptr[1] != 0)	/* version */
+      return RCODE_FORMAT_ERROR;
+    
+    if ((data->parse.ptr[2] & 0x80) == 0x80)	/* RFC-3225 */
+      pans->opt.fdo = true;
+    if ((data->parse.ptr[2] & 0x7F) != 0)
+      return RCODE_FORMAT_ERROR;
+    if (data->parse.ptr[3] != 0)
+      return RCODE_FORMAT_ERROR;
+
+    data->parse.ptr  += 4;
+    data->parse.size -= 4;
+  }
+  else
+  {
+    pans->generic.class = read_uint16(&data->parse);
+    pans->generic.ttl   = read_uint32(&data->parse);
+  }
+  
+  len  = read_uint16(&data->parse);
+  rest = data->packet.size - (data->parse.ptr - data->packet.ptr);
+  
+  if (len > rest) 
+    return RCODE_FORMAT_ERROR;
+
+  switch(pans->generic.type)
+  {
+    case RR_A:     return decode_rr_a    (data,&pans->a    ,len);
+    case RR_SOA:   return decode_rr_soa  (data,&pans->soa  ,len);
+    case RR_NAPTR: return decode_rr_naptr(data,&pans->naptr,len);
+    case RR_AAAA:  return decode_rr_aaaa (data,&pans->aaaa ,len);
+    case RR_SRV:   return decode_rr_srv  (data,&pans->srv  ,len);
+    case RR_WKS:   return decode_rr_wks  (data,&pans->wks  ,len);
+    case RR_GPOS:  return decode_rr_gpos (data,&pans->gpos);
+    case RR_LOC:   return decode_rr_loc  (data,&pans->loc  ,len);
+    case RR_OPT:   return decode_rr_opt  (data,&pans->opt  ,len);
+    
+    /*----------------------------------------------------------------------	
+    ; The following record types all share the same structure (although the
+    ; last field name is different, depending upon the record), so they can
+    ; share the same call site.  It's enough to shave some space in the
+    ; executable while being a cheap and non-obscure size optimization, or
+    ; a gross hack, depending upon your view.
+    ;----------------------------------------------------------------------*/
+    
+    case RR_PX:
+    case RR_RP:
+    case RR_MINFO: return decode_rr_minfo(data,&pans->minfo);
+    
+    case RR_AFSDB:
+    case RR_RT:
+    case RR_MX: return decode_rr_mx(data,&pans->mx,len);
+    
+    case RR_NSAP:
+    case RR_ISDN:
+    case RR_HINFO: return decode_rr_hinfo(data,&pans->hinfo);    
+    
+    case RR_X25:
+    case RR_SPF:
+    case RR_TXT: return decode_rr_txt(data,&pans->txt,len);
+    
+    case RR_NSAP_PTR:
+    case RR_MD:
+    case RR_MF:
+    case RR_MB:
+    case RR_MG:
+    case RR_MR:
+    case RR_NS:
+    case RR_PTR:
+    case RR_CNAME: return read_domain(data,&pans->cname.cname);
+    
+    case RR_NULL:
+    default: 
+         pans->x.size = len;
+         return read_raw(data,&pans->x.rawdata,len);
+  }
+  
+  assert(0);
+  return RCODE_OKAY;
+}
+
+/***********************************************************************/
+
+dns_rcode_t dns_decode(
+	      dns_decoded_t *const restrict presponse,
+	      size_t        *const restrict prsize,
+	const dns_packet_t  *const restrict buffer,
+	const size_t                        len
+)
+{
+  const struct idns_header *header;
+  dns_query_t              *response;
+  idns_context              context;
+  dns_rcode_t               rc;
+
+  assert(presponse != NULL);
+  assert(prsize    != NULL);
+  assert(*prsize   >= sizeof(dns_query_t));
+  assert(buffer    != NULL);
+  assert(len       >= sizeof(struct idns_header));
+  
+  context.packet.ptr  = (uint8_t *)buffer;
+  context.packet.size = len;
+  context.parse.ptr   = &context.packet.ptr[sizeof(struct idns_header)];
+  context.parse.size  = len - sizeof(struct idns_header);
+  context.dest.ptr    = (uint8_t *)presponse;
+  context.dest.size   = *prsize;
+  context.edns        = false;
+  
+  /*--------------------------------------------------------------------------
+  ; we use the block of data given to store the results.  context.dest
+  ; contains this block and allocations are doled out from this.  This odd
+  ; bit here sets the structure to the start of the block we're using, and
+  ; then "allocates" the size f the structure in the context variable.  I do
+  ; this as a test of the allocation routines when the address is already
+  ; aligned (an assumption I'm making)---the calls to assert() ensure this
+  ; behavior.
+  ;--------------------------------------------------------------------------*/
+  
+  response         = (dns_query_t *)context.dest.ptr;
+  context.response = alloc_struct(&context.dest,sizeof(dns_query_t));
+  
+  assert(context.response != NULL);
+  assert(context.response == response);
+  
+  memset(response,0,sizeof(dns_query_t));
+  response->questions   = NULL;
+  response->answers     = NULL;
+  response->nameservers = NULL;
+  response->additional  = NULL;
+  
+  header = (struct idns_header *)buffer;
+  
+  if ((header->rcode & 0x40) != 0x00)	/* Z bit must be zero */
+    return RCODE_FORMAT_ERROR;
+  
+  response->id      = ntohs(header->id);
+  response->opcode  = (header->opcode >> 3) & 0x0F;
+  response->query   = (header->opcode & 0x80) != 0x80;
+  response->aa      = (header->opcode & 0x04) == 0x04;
+  response->tc      = (header->opcode & 0x02) == 0x02;
+  response->rd      = (header->opcode & 0x01) == 0x01;
+  response->ra      = (header->rcode  & 0x80) == 0x80;
+  response->ad      = (header->rcode  & 0x20) == 0x20;
+  response->cd      = (header->rcode  & 0x10) == 0x10;
+  response->rcode   = (header->rcode  & 0x0F);
+  response->qdcount = ntohs(header->qdcount);
+  response->ancount = ntohs(header->ancount);
+  response->nscount = ntohs(header->nscount);
+  response->arcount = ntohs(header->arcount);
+
+  response->questions   = alloc_struct(&context.dest,response->qdcount * sizeof(dns_question_t));
+  response->answers     = alloc_struct(&context.dest,response->ancount * sizeof(dns_answer_t));
+  response->nameservers = alloc_struct(&context.dest,response->nscount * sizeof(dns_answer_t));
+  response->additional  = alloc_struct(&context.dest,response->arcount * sizeof(dns_answer_t));
+  
+  if (
+          (response->qdcount && (response->questions   == NULL))
+       || (response->ancount && (response->answers     == NULL))
+       || (response->nscount && (response->nameservers == NULL))
+       || (response->arcount && (response->additional  == NULL))
+     )
+  {
+    return RCODE_NO_MEMORY;
+  }
+  
+  for (size_t i = 0 ; i < response->qdcount ; i++)
+  {
+    rc = decode_question(&context,&response->questions[i]);
+    if (rc != RCODE_OKAY)
+      return rc;
+  }
+
+  for (size_t i = 0 ; i < response->ancount ; i++)
+  {
+    rc = decode_answer(&context,&response->answers[i]);
+    if (rc != RCODE_OKAY)
+      return rc;
+  }
+  
+  /*-------------------------------------------------------------
+  ; RR OPT can only appear once, and only in the additional info
+  ; section.  Check that we haven't seen one before.
+  ;-------------------------------------------------------------*/
+  
+  if (context.edns) return RCODE_FORMAT_ERROR;
+  
+  for (size_t i = 0 ; i < response->nscount ; i++)
+  {
+    rc = decode_answer(&context,&response->nameservers[i]);
+    if (rc != RCODE_OKAY)
+      return rc;
+  }
+  
+  if (context.edns) return RCODE_FORMAT_ERROR;
+  
+  for (size_t i = 0 ; i < response->arcount ; i++)
+  {
+    rc = decode_answer(&context,&response->additional[i]);
+    if (rc != RCODE_OKAY)
+      return rc;
+  }
+
+  *prsize = (size_t)(context.dest.ptr - (uint8_t *)presponse);
+  return RCODE_OKAY;
+}
+
+/************************************************************************/
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/src/dns.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/src/dns.h	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,990 @@
+/*************************************************************************
+*
+* Copyright 2010 by Sean Conner.  All Rights Reserved.
+*
+* This library is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or (at your
+* option) any later version.
+*
+* This library is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+* License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this library; if not, see <http://www.gnu.org/licenses/>.
+*
+**************************************************************************/
+
+/*************************************************************************
+*
+* Definitions for all things related to the DNS protocol (and not to the
+* network transport thereof---that's for another episode).
+*
+* I've scoured the Internet and I think I've located every DNS RR type that
+* exists.  And for the majority of them, I've made an in-memory
+* representation of said record for easy access to the contents (for when I
+* do get around to decoding them from their wire representation).  For
+* records that I do not decode (and you'll need to check codec.c to see
+* which ones aren't being decoded) you'll get back a dns_x_t, which has the
+* common portion of the RR decoded (which includes the ID, type, class and
+* TTL) plus the remainder of the raw packet.  
+*
+* My eventual intent is to decode those records that I can find definitions
+* for, and decipher the sometimes dry and dense RFCs that describe said RRs. 
+* I'm well on my way with support for about half the known records (which
+* includes the ones most likely to be found in 99% of all zone files).
+*
+* This file assumes C99.  You must include the following files before
+* including this one:
+*
+* #include <stdbool.h>
+* #include <stdint.h>
+* #include <stddef.h>
+* #incldue <arpa/inet.h>
+*
+***************************************************************************/
+
+#ifndef DNS_H
+#define DNS_H
+
+#ifdef __cplusplus
+#  define class dclass
+#  define restrict
+   extern "C" {
+#endif
+
+#ifndef __GNUC__
+#  define __attribute__(x)
+#endif
+
+/****************************************************************************
+* Buffers passed to these routines should be declared as one of these two
+* types with one of the given sizes below, depending upon what the buffer is
+* being used for.  This is to ensure things work out fine and don't blow up
+* (like a segfault).  The 4K size *should* be okay for UDP packets, but if
+* you are worried, 8K is more than enough to handle responses from UDP. 
+* Larger sizes may be required for TCP.
+*
+* A declaration would looke something like:
+*
+*	dns_packet_t  query_packet    [DNS_BUFFER_UDP];
+*	dns_decoded_t decoded_response[DNS_DECODEBUF_4K];
+*
+* Alternatively, you can do this:
+*
+*	dns_packet_t  *pquery_packet;
+*	dns_decoded_t *pdecoded_response;
+*
+*	pquery_packet     = malloc(MAX_DNS_QUERY_SIZE);
+*	pdecoded_response = malloc(4192);
+*
+*************************************************************************/
+
+typedef uintptr_t dns_packet_t;
+typedef uintptr_t dns_decoded_t;
+
+#define DNS_BUFFER_UDP		(  512uL / sizeof(dns_packet_t))
+#define DNS_DECODEBUF_4K	( 4096uL / sizeof(dns_decoded_t))
+#define DNS_DECODEBUF_8K	( 8192uL / sizeof(dns_decoded_t))
+#define DNS_DECODEBUF_16k	(16384uL / sizeof(dns_decoded_t))
+
+/************************************************************************
+* Various upper limits in the DNS protocol
+************************************************************************/
+
+#define MAX_DNS_QUERY_SIZE	512
+#define MAX_DOMAIN_SEGMENT	 64
+#define MAX_STRING_LEN		256
+#define MAX_UDP_PACKET_SIZE    1492
+
+/***************************************************************************
+* I've specified where each RR, Class and error codes are defined.  Also, 
+* for the RRs, I've marked if I have decode support as well as experimental
+* and obsolete information as follows:
+*
+*	+	Decoding support
+*	O	Obsolete 
+*	E	Experimental 
+*
+***************************************************************************/
+
+typedef enum dns_type
+{
+  RR_A          =   1,	/* IPv4 Address 		      + RFC-1035 */
+  RR_NS         =   2,	/* Name server			      + RFC-1035 */
+  RR_MD         =   3,	/* Mail Destination		     O+ RFC-1035 */
+  RR_MF         =   4,	/* Mail Forwarder		     O+ RFC-1035 */
+  RR_CNAME      =   5,	/* Canonical name		      + RFC-1035 */
+  RR_SOA        =   6,	/* Start of Authority		      + RFC-1035 */
+  RR_MB         =   7,	/* Mailbox			     E+ RFC-1035 */
+  RR_MG         =   8,	/* Mailgroup			     E+ RFC-1035 */
+  RR_MR         =   9,	/* Mailrename			     E+ RFC-1035 */
+  RR_NULL       =  10,	/* NULL resource		     E+ RFC-1035 */
+  RR_WKS        =  11,	/* Well Known Service		      + RFC-1035 */
+  RR_PTR        =  12,	/* Pointer			      + RFC-1035 */
+  RR_HINFO      =  13,	/* Host Info			      + RFC-1035 */
+  RR_MINFO      =  14,	/* Mailbox/mail list info	      + RFC-1035 */
+  RR_MX         =  15,	/* Mail Exchange		      + RFC-1035 */
+  RR_TXT        =  16,	/* Text				      + RFC-1035 */
+  RR_RP         =  17,	/* Responsible Person		      + RFC-1183 */
+  RR_AFSDB      =  18,	/* Andrew File System DB	      + RFC-1183 RFC-5864 */
+  RR_X25        =  19,	/* X.25 address, route binding        + RFC-1183 */
+  RR_ISDN       =  20,	/* ISDN address, route binding	      + RFC-1183 */
+  RR_RT         =  21,	/* Route Through		      + RFC-1183 */
+  RR_NSAP       =  22,	/* Network Service Access Proto	      + RFC-1348 RFC-1706 */
+  RR_NSAP_PTR   =  23,	/* NSAP Pointer			      + RFC-1348 */
+  RR_SIG        =  24,	/* Signature				RFC-2065 RFC-2535 RFC-3755 RFC-4034 */
+  RR_KEY        =  25,	/* Key					RFC-2065 RFC-2535 RFC-3755 RFC-4034 */
+  RR_PX         =  26,	/* X.400 mail mapping		      + RFC-2163 */
+  RR_GPOS       =  27,	/* Geographical position	     O+ RFC-1712 */
+  RR_AAAA       =  28,	/* IPv6 Address			      + RFC-1886 RFC-3596 */
+  RR_LOC        =  29,	/* Location			      + RFC-1876 */
+  RR_NXT        =  30,	/* Next RR				RFC-2065 RFC-2535 RFC-3755 */
+  RR_EID        =  31,	/* Endpoint Identifier			???      */
+  RR_NIMLOC     =  32,	/* Nimrod Locator			???      */
+  RR_SRV        =  33,	/* Service			      + RFC-2782 */
+  RR_ATM        =  34,	/* ATM Address				???      */
+  RR_NAPTR      =  35,	/* Naming Authority Pointer	      + RFC-2168 RFC-2915 RFC-3403 */
+  RR_KX         =  36,	/* Key Exchange				RFC-2230 */
+  RR_CERT       =  37,	/* Certification			RFC-4398 */
+  RR_A6         =  38,	/* IPv6 Address				RFC-2874 RFC-3658 */
+  RR_DNAME      =  39,	/* Non-terminal DNAME (IPv6)		RFC-2672 */
+  RR_SINK       =  40,	/* Kitchen sink			     E  ???      */
+  RR_OPT        =  41,	/* EDNS0 option (meta-RR)	      + RFC-2671 */
+  RR_APL        =  42,	/* Address Prefix List			RFC-3123 */
+  RR_DS         =  43,	/* Delegation Signer			RFC-3658 RFC-4034 */
+  RR_SSHFP      =  44,	/* SSH Key Fingerprint			RFC-4255 */
+  RR_ISECKEY    =  45,	/* IP Security Key			RFC-4025 */
+  RR_RRSIG      =  46,	/* Resource Record Signature		RFC-3755 RFC-4034 */
+  RR_NSEC       =  47,	/* Next Security Record		        RFC-3755 RFC-4034 */
+  RR_DNSKEY     =  48,	/* DNS Security Key			RFC-3755 RFC-4034 */
+  RR_DHCID      =  49,	/* DHCID				RFC-4701 */
+  RR_NSEC3      =  50,	/* NSEC3				RFC-5155 */
+  RR_NSEC3PARAM =  51,	/* NSEC3PARAM				RFC-5155 */
+  RR_HIP        =  55,	/* Host Identity Protocol		RFC-5205 */
+  RR_NINFO      =  56,	/* NINFO				???      */
+  RR_RKEY       =  57,	/* RKEY					???      */
+  RR_TALINK     =  58,	/* Trust Anchor Link			???      */
+  RR_SPF        =  99,	/* Sender Policy Framework	      + RFC-4408 */
+  RR_UINFO      = 100,	/* IANA Reserved			???      */
+  RR_UID        = 101,	/* IANA Reserved			???      */
+  RR_GID        = 102,	/* IANA Reserved			???      */
+  RR_UNSPEC     = 103,	/* IANA Reserved			???      */
+
+	/* Query types, >= 128 */
+  
+  RR_TKEY       = 249,	/* Transaction Key			RFC-2930 */
+  RR_TSIG       = 250,	/* Transaction Signature		RFC-2845 */
+  RR_IXFR       = 251,	/* Incremental zone transfer		RFC-1995 */
+  RR_AXFR       = 252,	/* Transfer of zone			RFC-1035 RFC-5936 */
+  RR_MAILB      = 253,	/* Mailbox related records		RFC-1035 */
+  RR_MAILA      = 254,	/* Mail agent RRs (obsolete)	     O  RFC-1035 */
+  RR_ANY        = 255,	/* All records				RFC-1035 */
+
+  RR_UNKNOWN    = 65280	/* Unknown record type			RFC-2929 */
+} dns_type_t;
+
+typedef enum edns0_type
+{
+  EDNS0RR_NSID = 3	/* Name Server ID		      + RFC-5001 */
+} edns0_type_t;
+
+typedef enum dns_class
+{
+  CLASS_IN      =     1,	/* Internet	    	RFC-1035 */
+  CLASS_CS      =     2,	/* CSNET (obsolete)    	RFC-1035 */
+  CLASS_CH      =     3,	/* CHAOS		RFC-1035 */
+  CLASS_HS      =     4,	/* Hesiod		RFC-1035 */
+  CLASS_NONE    =   254,	/* 			RFC-2136 */
+  CLASS_ANY     =   255,	/* All classes		RFC-1035 */
+  CLASS_UNKNOWN = 65280		/* Unknown class 	RFC-2929 */
+} dns_class_t;
+
+typedef enum dns_op
+{
+  OP_QUERY   = 0,	/* RFC-1035 */
+  OP_IQUERY  = 1,	/* RFC-1035 RFC-3425 */ /* Obsolete */
+  OP_STATUS  = 2,	/* RFC-1035 */  
+  OP_NOTIFY  = 4,	/* RFC-1996 */
+  OP_UPDATE  = 5,	/* RFC-2136 */  
+  OP_UNKNOWN = 1	/* Since OP_IQUERY is obsolete */
+} dns_op_t;
+
+typedef enum dns_rcode
+{
+  RCODE_OKAY            =    0,	/* RFC-1035 */
+  RCODE_FORMAT_ERROR    =    1,	/* RFC-1035 */
+  RCODE_SERVER_FAILURE  =    2,	/* RFC-1035 */
+  RCODE_NAME_ERROR      =    3,	/* RFC-1035 */
+  RCODE_NOT_IMPLEMENTED =    4,	/* RFC-1035 */
+  RCODE_REFUSED         =    5,	/* RFC-1035 */
+  RCODE_YXDOMAIN        =    6,	/* RFC-2136 */
+  RCODE_YXRRSET         =    7,	/* RFC-2136 */
+  RCODE_NXRRSET         =    8,	/* RFC-2136 */
+  RCODE_NOTAUTH         =    9,	/* RFC-2136 */
+  RCODE_NOTZONE         =   10,	/* RFC-2136 */
+  RCODE_BADVERS         =   16,	/* RFC-2671 */
+  RCODE_BADSIG          =   16,	/* RFC-2845 */
+  RCODE_BADKEY          =   17,	/* RFC-2845 */
+  RCODE_BADTIME         =   18,	/* RFC-2845 */
+  RCODE_BADMODE         =   19,	/* RFC-2845 */
+  RCODE_BADNAME         =   20,	/* RFC-2930 */
+  RCODE_BADALG          =   21,	/* RFC-2930 */
+  RCODE_BADTRUC         =   22,	/* RFC-4635 */  
+  RCODE_PRIVATE         = 3841,	/* RFC-2929 */
+  
+  RCODE_NO_MEMORY
+} dns_rcode_t;
+
+typedef enum edns0_label
+{
+  EDNS0_ELT  = 0x01,		/* RFC-2673 (experimental RFC-3363) */
+  EDNS0_RSVP = 0x3F		/* RFC-2671 */
+} edns0_label_t;
+
+typedef uint32_t TTL;
+
+typedef struct dns_question_t	/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+} dns_question_t;
+
+typedef struct dns_generic_t	/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+} dns_generic_t;
+
+typedef struct dns_a_t		/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  in_addr_t    address;
+} dns_a_t;
+
+typedef struct dns_ns_t		/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *nsdname;
+} dns_ns_t;
+
+typedef struct dns_md_t		/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *madname;
+} dns_md_t;
+
+typedef struct dns_mf_t		/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *madname;
+} dns_mf_t;
+
+typedef struct dns_cname_t	/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *cname;
+} dns_cname_t;
+
+typedef struct dns_soa_t	/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *mname;
+  const char  *rname;
+  uint32_t     serial;
+  uint32_t     refresh;
+  uint32_t     retry;
+  uint32_t     expire;
+  uint32_t     minimum;
+} dns_soa_t;
+
+typedef struct dns_mb_t		/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *madname;
+} dns_mb_t;
+
+typedef struct dns_mg_t		/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *mgmname;
+} dns_mg_t;
+
+typedef struct dns_mr_t		/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *newname;
+} dns_mr_t;
+
+typedef struct dns_null_t	/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       size;
+  uint8_t     *data;
+} dns_null_t;
+
+typedef struct dns_wks_t	/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  in_addr_t    address;
+  int          protocol;
+  size_t       numbits;
+  uint8_t     *bits;
+} dns_wks_t;
+
+typedef struct dns_ptr_t	/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *ptr;
+} dns_ptr_t;
+
+typedef struct dns_hinfo_t	/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *cpu;
+  const char  *os;
+} dns_hinfo_t;
+
+typedef struct dns_minfo_t	/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *rmailbx;
+  const char  *emailbx;
+} dns_minfo_t;
+
+typedef struct dns_mx_t		/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  int          preference;
+  const char  *exchange;
+} dns_mx_t;
+
+typedef struct dns_txt_t	/* RFC-1035 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       len;
+  const char  *text;
+} dns_txt_t;
+
+typedef struct dns_rp_t		/* RFC-1183 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *mbox;
+  const char  *domain;
+} dns_rp_t;
+
+typedef struct dns_afsdb_t	/* RFC-1183 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  int          subtype;
+  const char  *hostname;
+} dns_afsdb_t;
+
+typedef struct dns_x25_t	/* RFC-1183 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       size;
+  const char  *psdnaddress;
+} dns_x25_t;
+
+typedef struct dns_isdn_t	/* RFC-1183 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *isdnaddress;
+  const char  *sa;
+} dns_isdn_t;
+
+typedef struct dns_rt_t		/* RFC-1183 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  int          preference;
+  const char  *host;
+} dns_rt_t;
+
+typedef struct dns_nsap_t	/* RFC-1348 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *length;
+  const char  *nsapaddress;
+} dns_nsap_t;
+
+typedef struct dns_nsap_ptr_t	/* RFC-1348 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *owner;
+} dns_nsap_ptr_t;
+
+typedef enum dnskey_algorithm	/* RFC-2065 */
+{
+  DNSKEYA_RSAMD5     =   1,
+  DNSKEYA_DH         =   2,	/* RFC-2535 */
+  DNSKEYA_DSA        =   3,	/* RFC-2535 */
+  DNSKEYA_ECC        =   4,	/* RFC-2535 */
+  DNSKEYA_RSASHA1    =   5,	/* RFC-3110 */
+  DNSKEYA_INDIRECT   = 252,	/* RFC-2535 */
+  DNSKEYA_PRIVATEDNS = 253,
+  DNSKEYA_PRIVATEOID = 254,
+  DNSKEYA_RSVP       = 255
+} dnskey_algorithm;
+
+typedef struct dns_sig_t	/* RFC-2065 */
+{
+  const char       *name;
+  dns_type_t        type;
+  dns_class_t       class;
+  TTL               ttl;
+  dns_type_t        covered;
+  dnskey_algorithm  algorithm;
+  int               labels;
+  TTL               originttl;
+  unsigned long     sigexpire;
+  unsigned long     timesigned;
+  uint16_t          keyfootprint;
+  const char       *signer;
+  size_t            sigsize;
+  uint8_t          *signature;
+} dns_sig_t;
+
+typedef enum dnskey_protocol	/* RFC-2535 */
+{
+  DNSKEYP_NONE   =   0,
+  DNSKEYP_TLS    =   1,
+  DNSKEYP_EMAIL  =   2,
+  DNSKEYP_DNSSEC =   3,
+  DNSKEYP_IPSEC  =   4,
+  DNSKEYP_ALL    = 255
+} dnskey_protocol;
+
+typedef union dnskey_key	/* RFC-2065 */
+{
+  struct
+  {
+    size_t   expsize;
+    uint8_t *exponent;
+    size_t   modsize;
+    uint8_t *modulus;
+  } md5;
+
+  struct
+  {
+    size_t   size;
+    uint8_t *data;
+  } unknown;
+} dnskey_key;
+
+typedef struct dns_key_t	/* RFC-2065 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  struct
+  {
+    bool authentication;
+    bool confidential;
+    bool experimental;
+    bool user;
+    bool zone;
+    bool host;
+    bool ipsec;
+    bool email;		/* not in RFC-2535 */
+  }                 flags;
+  int               signatory;
+  dnskey_protocol   protocol;
+  dnskey_algorithm  algorithm;
+  dnskey_key        key;
+} dns_key_t;
+
+typedef struct dns_px_t		/* RFC-2163 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *map822;
+  const char  *mapx400;
+} dns_px_t;
+
+typedef struct dnsgpos_angle	/* RFC-1712 , RFC1876 */
+{
+  int deg;
+  int min;
+  int sec;
+  int frac;
+  bool nw;	/* Northern or Western Hemisphere */
+} dnsgpos_angle;
+
+typedef struct dns_gpos_t	/* RFC-1712 */
+{
+  const char   *name;
+  dns_type_t    type;
+  dns_class_t   class;
+  TTL           ttl;
+  dnsgpos_angle longitude;
+  dnsgpos_angle latitude;
+  double        altitude;
+} dns_gpos_t;
+
+typedef struct dns_aaaa_t	/* RFC-1886 */
+{
+  const char      *name;
+  dns_type_t       type;
+  dns_class_t      class;
+  TTL              ttl;
+  struct in6_addr  address;
+} dns_aaaa_t;
+
+typedef struct dns_loc_t	/* RFC-1876 */
+{
+  const char     *name;
+  dns_type_t      type;
+  dns_class_t     class;
+  TTL             ttl;
+  int             version;
+  unsigned long   size;		/* plese see RFC-1876 for a discussion 	*/
+  unsigned long   horiz_pre;	/* of these fields			*/
+  unsigned long   vert_pre;
+  dnsgpos_angle   latitude;
+  dnsgpos_angle   longitude;
+  long            altitude;
+} dns_loc_t;  
+
+typedef struct dns_nxt_t	/* RFC-2065 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *next;
+  size_t       numbits;
+  uint8_t     *bitmap;
+} dns_nxt_t;
+
+typedef struct dns_eid_t	/* (unknown) */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       size;
+  uint8_t     *rawdata;
+} dns_eid_t;
+
+typedef struct dns_nimloc_t	/* (unknown) */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       size;
+  uint8_t     *rawdata;
+} dns_nimloc_t;
+
+typedef struct dns_srv_t	/* RFC-2782 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  int          priority;
+  int          weight;
+  int          port;
+  const char  *target;
+} dns_srv_t;
+
+typedef struct dns_atm_t	/* (unknown) */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       size;
+  uint8_t     *rawdata;
+} dns_atm_t;
+
+typedef struct dns_naptr_t	/* RFC-2915 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  int          order;
+  int          preference;
+  const char  *flags;
+  const char  *services;
+  const char  *regexp;
+  const char  *replacement;
+} dns_naptr_t;
+
+typedef struct dns_kx_t		/* (unknown) */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       size;
+  uint8_t     *rawdata;
+} dns_kx_t;
+
+typedef struct dns_cert_t	/* (unknown) */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       size;
+  uint8_t     *rawdata;
+} dns_cert_t;
+
+typedef struct dns_a6_t		/* RFC-2874 */
+{
+  const char      *name;
+  dns_type_t       type;
+  dns_class_t      class;
+  TTL              ttl;
+  size_t           mask;
+  struct in6_addr  address;
+  const char      *prefixname;
+} dns_a6_t;
+
+typedef struct dns_dname_t	/* RFC-2672 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       size;
+  uint8_t     *rawdata;
+} dns_dname_t;
+
+typedef struct dns_sink_t	/* (unknown) */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       size;
+  uint8_t     *rawdata;
+} dns_sink_t;
+
+typedef struct edns0_opt_t	/* RFC-2671 */
+{
+  edns0_type_t code;		/* 0 <= code <= UINT16_MAX */
+  size_t       len;		/* 0 <= len  <= UINT16_MAX */
+  uint8_t     *data;		/* encoded per RFC specification */
+} edns0_opt_t;
+
+typedef struct dns_edns0opt_t	/* RFC-2671 */
+{
+  const char    *name;
+  dns_type_t     type;		
+  dns_class_t    class;		/* not applicable --- set to CLASS_UNKNOWN */
+  TTL            ttl;		/* not applicable --- set to 0 */
+  size_t         udp_payload;
+  int            version;
+  bool           fdo;		/* RFC-3225 */
+  size_t         numopts;
+  edns0_opt_t   *opts;
+} dns_edns0opt_t;
+
+typedef struct dnsapl_record	/* RFC-3123 */
+{
+  int      addressfamily;
+  int      prefix;
+  size_t   afdlength;
+  uint8_t *afdpart;
+  bool     negate;
+} dnsapl_record;
+
+typedef struct dns_apl_t	/* RFC-3123 */
+{
+  const char     *name;
+  dns_type_t      type;
+  dns_class_t     class;
+  TTL             ttl;
+  size_t          numrecs;
+  dnsapl_record  *recs;
+} dns_apl_t;
+
+
+typedef enum dnsds_digest	/* RFC-3658 */
+{
+  DNSDS_SHA1 = 1
+} dnsds_digest;
+
+typedef struct dns_ds_t		/* RFC-3658 */
+{
+  const char       *name;
+  dns_type_t        type;
+  dns_class_t       class;
+  TTL               ttl;
+  dnskey_protocol   keytag;
+  dnskey_algorithm  algorithm;
+  dnsds_digest      digest;
+  size_t            digestlen;
+  uint8_t          *digestdata;
+} dns_ds_t;
+
+typedef struct dns_rrsig_t	/* RFC-4034 */
+{
+  const char       *name;
+  dns_type_t        type;
+  dns_class_t       class;
+  TTL               ttl;
+  dns_type_t        covered;
+  dnskey_algorithm  algorithm;
+  int               labels;
+  TTL               originttl;
+  unsigned long     sigexpire;
+  unsigned long     timesigned;
+  uint16_t          keyfootprint;
+  const char       *signer;
+  size_t            sigsize;
+  uint8_t          *signature;
+} dns_rrsig_t;
+
+typedef struct dns_nsec_t	/* RFC-4034 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  const char  *next;
+  size_t       numbits;
+  uint8_t     *bitmap;
+} dns_nsec_t;
+
+typedef struct dns_dnskey_t	/* RFC-4034 */
+{
+  const char       *name;
+  dns_type_t        type;
+  dns_class_t       class;
+  TTL               ttl;
+  bool              zonekey;
+  bool              sep;
+  dnskey_protocol   protocol;	/* must be DNSKEYP_DNSSEC */
+  dnskey_algorithm  algorithm;
+  size_t            keysize;
+  uint8_t          *key;
+} dns_dnskey_t;  
+
+typedef struct dns_sshfp_t	/* RFC-4255 */
+{
+  const char       *name;
+  dns_type_t        type;
+  dns_class_t       class;
+  TTL               ttl;
+  dnskey_algorithm  algorithm;
+  dnsds_digest      fptype;
+  size_t            fpsize;
+  uint8_t          *fingerprint;
+} dns_sshfp_t;
+
+typedef struct dns_spf_t	/* RFC-4408 */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       len;
+  const char  *text;
+} dns_spf_t;
+
+typedef struct dns_tsig_t	/* RFC-2845 */
+{
+  const char   *name;
+  dns_type_t    type;
+  dns_class_t   class;
+  TTL           ttl;	/* must be 0 */
+  const char   *algorithm;
+  uint64_t      timesigned;
+  unsigned int  fudge;
+  size_t        MACsize;
+  uint8_t      *MAC;
+  int           id;
+  int           error;
+  size_t        lenother;
+  uint8_t      *other;
+} dns_tsig_t;
+
+typedef struct dns_x_t		/* CATCH-ALL */
+{
+  const char  *name;
+  dns_type_t   type;
+  dns_class_t  class;
+  TTL          ttl;
+  size_t       size;
+  uint8_t     *rawdata;
+} dns_x_t;
+
+typedef union dns_answer_t
+{
+  dns_generic_t  generic;
+  dns_x_t        x;
+  dns_a_t        a;
+  dns_ns_t       ns;
+  dns_md_t       md;
+  dns_mf_t       mf;
+  dns_cname_t    cname;
+  dns_soa_t      soa;
+  dns_mb_t       mb;
+  dns_mg_t       mg;
+  dns_mr_t       mr;
+  dns_null_t     null;
+  dns_wks_t      wks;
+  dns_ptr_t      ptr;
+  dns_hinfo_t    hinfo;
+  dns_minfo_t    minfo;
+  dns_mx_t       mx;
+  dns_txt_t      txt;
+  dns_rp_t       rp;
+  dns_afsdb_t    afsdb;
+  dns_x25_t      x25;
+  dns_isdn_t     isdn;
+  dns_rt_t       rt;
+  dns_nsap_t     nsap;
+  dns_nsap_ptr_t nsap_ptr;
+  dns_sig_t      sig;
+  dns_key_t      key;
+  dns_px_t       px;
+  dns_gpos_t     gpos;
+  dns_aaaa_t     aaaa;
+  dns_loc_t      loc;
+  dns_nxt_t      nxt;
+  dns_eid_t      eid;
+  dns_nimloc_t   nimloc;
+  dns_srv_t      srv;
+  dns_atm_t      atm;
+  dns_naptr_t    naptr;
+  dns_kx_t       kx;
+  dns_cert_t     cert;
+  dns_a6_t       a6;
+  dns_dname_t    dname;
+  dns_sink_t     sink;
+  dns_edns0opt_t opt;
+  dns_apl_t      apl;
+  dns_ds_t       ds;
+  dns_rrsig_t    rrsig;
+  dns_nsec_t     nsec;
+  dns_dnskey_t   dnskey;
+  dns_spf_t      spf;
+  dns_tsig_t     tsig;
+} dns_answer_t;
+
+typedef struct dns_query_t	/* RFC-1035 */
+{
+  int             id;
+  bool            query;
+  dns_op_t        opcode;
+  bool            aa;
+  bool            tc;
+  bool            rd;
+  bool            ra;
+  bool            ad;		/* RFC-2065 */
+  bool            cd;		/* RFC-2065 */
+  dns_rcode_t     rcode;
+  size_t          qdcount;
+  size_t          ancount;
+  size_t          nscount;
+  size_t          arcount;
+  dns_question_t *questions;
+  dns_answer_t   *answers;
+  dns_answer_t   *nameservers;
+  dns_answer_t   *additional;
+} dns_query_t;
+
+/**********************************************************************/
+
+dns_rcode_t	dns_encode(
+			  dns_packet_t      *const restrict,
+			  size_t            *const restrict,
+			  const dns_query_t *const restrict
+			 ) __attribute__ ((nothrow,nonnull));
+
+dns_rcode_t	dns_decode(
+                          dns_decoded_t      *const restrict,
+                          size_t             *const restrict,
+			  const dns_packet_t *const restrict,
+			  const size_t
+			 ) __attribute__ ((nothrow,nonnull(1,2,3)));
+
+#ifdef __cplusplus
+   }
+#  undef class
+#  undef restrict
+#endif
+#endif
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/src/luadns.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/src/luadns.c	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,768 @@
+/*************************************************************************
+*
+* Copyright 2010 by Sean Conner.  All Rights Reserved.
+*
+* This library is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or (at your
+* option) any later version.
+*
+* This library is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+* License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this library; if not, see <http://www.gnu.org/licenses/>.
+*
+**************************************************************************/
+
+/**********************************************************************
+*
+* Implements Lua bindings for my DNS library.  This exports four functions
+* in the org.conman.dns object:
+*
+*	encode(t)
+*
+*		Accepts a table in the form:
+*
+*			{
+*			  id       = some_number,
+*			  query    = true,	-- making a query
+*			  rd       = true,	-- for recursive queries
+*			  opcode   = 'query',
+*			  question = {
+*			  		name = 'www.example.com',
+*			  		type = 'loc',
+*			  		class = 'in'
+*			  	}, -- and optionally
+*			  additional = {
+*				name = '.',
+*				type = 'opt',
+*				udp_payload = 1464,
+*				version     = 0,
+*				fdo         = false,
+*				opts        = {
+*					{
+*					  type = 'nsid', -- or a number
+*					  data = "..."
+*					} -- and more, if required 
+*				}
+*			  }
+*			}
+*
+*		And returns a binary string that is the wire format of the
+*		query.  This binary string can then be sent over a UDP or
+*		TCP packet to a DNS server.
+*
+*		This returns a binary string on success, nil,rcode on
+*		failre.
+*
+*		See lua/test.lua for an example of using this function.
+*
+*	decode(bs)
+*
+*		Decodes a binary string into a table (similar to the table
+*		above) for easy use of the DNS response.
+*
+*		This returns a table on success, or nil,rcode on failure.
+*
+*		See lua/test.lua for an example of using this function.
+*
+*	query(server,bs)
+*
+*		Sends the encoded binary string to the given server.  The
+*		server variable is a string of the IP address (IPv4 or
+*		IPv6)---hostnames will fail.
+*
+*		This function is very stupid simple; it sends the request,
+*		and if it doesn't see a reply in 15 seconds, it returns a
+*		failure.  No restransmission of the packet is done.  This is
+*		probably fine for simple applications but not for anything
+*		heavy duty or rubust.
+*
+*		This returns a binary string of the reponse, or nil,rcode on
+*		failure.
+*
+*	strerror(rcode)
+*
+*		Returns a string representation of the server response, or 
+*		the return value from a failed query() call.  This function
+*		does not fail (if it does, there's more to worry about).
+*
+*
+* This code is written to C99.
+*
+***************************************************************************/
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <arpa/inet.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "dns.h"
+#include "mappings.h"
+#include "netsimple.h"
+
+/********************************************************************/
+
+static bool parse_edns0_opt(lua_State *L,edns0_opt_t *opt)
+{
+  const char *type;
+  size_t      size;
+  bool        rc;
+  
+  rc = true;
+  
+  lua_getfield(L,-1,"data");
+  opt->len = 0;
+  opt->data = (uint8_t *)lua_tolstring(L,-1,&opt->len);
+  lua_pop(L,1);
+  
+  lua_getfield(L,-1,"code");
+  if (lua_isnumber(L,-1))
+    opt->code = lua_tointeger(L,-1);
+  else if (lua_isstring(L,-1))
+  {
+    type = lua_tolstring(L,-1,&size);
+    
+    if ((memcmp(type,"nsid",size) == 0) || (memcmp(type,"NSID",size) == 0))
+      opt->code = EDNS0RR_NSID;
+    else
+      rc = false;
+  }
+  else
+    rc = false;
+  lua_pop(L,1);
+  
+  return rc;
+}
+
+/********************************************************************/  
+
+static int dnslua_encode(lua_State *L)
+{
+  dns_question_t domain;
+  dns_query_t    query;
+  dns_packet_t   buffer[DNS_BUFFER_UDP];
+  size_t         len;
+  int            qidx;
+  int            rc;
+  
+  if (!lua_istable(L,1))
+    luaL_typerror(L,1,lua_typename(L,LUA_TTABLE));
+  
+  memset(&domain,0,sizeof(domain));
+  memset(&query, 0,sizeof(query));
+
+  lua_getfield(L,1,"question");
+  
+  /*----------------------------------------------------------------------
+  ; the user could have passed in multiple parameters; this way, we know
+  ; where the table we just referenced got stashed on the stack.
+  ;---------------------------------------------------------------------*/
+  
+  qidx = lua_gettop(L);
+  
+  /*-----------------------------------------------------------------
+  ; process the question
+  ;----------------------------------------------------------------*/
+  
+  if (!lua_istable(L,qidx))
+    luaL_typerror(L,qidx,lua_typename(L,LUA_TTABLE));
+  
+  lua_getfield(L,qidx,"name");
+  lua_getfield(L,qidx,"type");
+  lua_getfield(L,qidx,"class");
+  
+  domain.name  = luaL_checkstring(L,-3);
+  domain.type  = dns_type_value (luaL_optstring(L,-2,"A"));
+  domain.class = dns_class_value(luaL_optstring(L,-1,"IN"));
+
+  lua_pop(L,4);
+  
+  lua_getfield(L,1,"id");
+  lua_getfield(L,1,"query");
+  lua_getfield(L,1,"rd");
+  lua_getfield(L,1,"opcode");
+    
+  query.id        = luaL_optint(L,-4,1234);
+  query.query     = lua_toboolean(L,-3);
+  query.rd        = lua_toboolean(L,-2);
+  query.opcode    = dns_op_value(luaL_optstring(L,-1,"QUERY"));  
+  query.qdcount   = 1;
+  query.questions = &domain;
+  
+  lua_pop(L,4);
+  
+  /*----------------------------------------------------------------
+  ; OPT RR support---gring grind grind
+  ;-----------------------------------------------------------------*/
+  
+  lua_getfield(L,1,"additional");
+  if (lua_isnil(L,-1))
+  {
+    len = sizeof(buffer);
+    rc  = dns_encode(buffer,&len,&query);
+  }
+  else
+  {
+    dns_answer_t edns;
+    
+    qidx = lua_gettop(L);
+    if (!lua_istable(L,qidx))
+      luaL_typerror(L,qidx,lua_typename(L,LUA_TTABLE));
+    
+    query.arcount    = 1;
+    query.additional = &edns;
+    
+    memset(&edns,0,sizeof(edns));
+    
+    lua_getfield(L,qidx,"name");
+    lua_getfield(L,qidx,"type");
+    lua_getfield(L,qidx,"udp_payload");
+    lua_getfield(L,qidx,"version");
+    lua_getfield(L,qidx,"fdo");
+    
+    edns.opt.name        = luaL_optstring(L,-5,".");
+    edns.opt.type        = dns_type_value(luaL_optstring(L,-4,"OPT"));
+    edns.opt.udp_payload = luaL_optint   (L,-3,1464);
+    edns.opt.version     = luaL_optint   (L,-2,0);
+    edns.opt.fdo         = lua_toboolean (L,-1);
+    
+    lua_pop(L,5);
+    lua_getfield(L,qidx,"opts");
+    
+    if (lua_isnil(L,-1))
+    {
+      edns.opt.numopts = 0;
+      edns.opt.opts    = NULL;
+      len              = sizeof(buffer);
+      rc               = dns_encode(buffer,&len,&query);
+    }
+    else
+    {
+      if (!lua_istable(L,-1))
+        luaL_typerror(L,-1,lua_typename(L,LUA_TTABLE));
+      
+      edns.opt.numopts = lua_objlen(L,-1);
+      
+      /*----------------------------------------------------------------
+      ; the opts table can either be one record with named fields, or an
+      ; array of records, each with named fields.
+      ;----------------------------------------------------------------*/
+      
+      if (edns.opt.numopts == 0)
+      {
+        edns0_opt_t opt;
+        
+        if (!parse_edns0_opt(L,&opt))
+          return luaL_error(L,"EDNS0 option not supported");
+          
+        edns.opt.opts = &opt;
+        len           = sizeof(buffer);
+        rc            = dns_encode(buffer,&len,&query);
+      }
+      else
+      {
+        edns0_opt_t opt[edns.opt.numopts];
+        
+        for (size_t i = 1 ; i <= edns.opt.numopts ; i++)
+        {
+          lua_pushinteger(L,i);
+          lua_gettable(L,-2);
+          
+          if (!lua_istable(L,-1))
+            return luaL_typerror(L,-1,lua_typename(L,LUA_TTABLE));
+          
+          if (!parse_edns0_opt(L,&opt[i - 1]))
+            return luaL_error(L,"EDNS0 option no supported");
+          
+          lua_pop(L,1);
+        }
+        edns.opt.opts = opt;
+        len           = sizeof(buffer);
+        rc            = dns_encode(buffer,&len,&query);
+      }
+    }
+  }
+  
+  if (rc != RCODE_OKAY)
+  {
+    lua_pushnil(L);
+    lua_pushstring(L,dns_rcode_text(rc));
+    return 2;
+  }
+  
+  lua_pushlstring(L,(char *)buffer,len);
+  return 1;
+}
+
+/********************************************************************/
+
+static void push_dnsgpos_angle(lua_State *L,dnsgpos_angle *pa,bool lat)
+{
+  lua_createtable(L,0,4);
+  lua_pushinteger(L,pa->deg);
+  lua_setfield(L,-2,"deg");
+  lua_pushinteger(L,pa->min);
+  lua_setfield(L,-2,"min");
+  lua_pushnumber(L,(double)pa->sec + ((double)pa->frac / 1000.0));
+  lua_setfield(L,-2,"sec");
+  lua_pushboolean(L,pa->nw);
+  lua_setfield(L,-2,"nw");
+  if (lat)
+    lua_pushlstring(L,(pa->nw) ? "N" : "S" , 1);
+  else
+    lua_pushlstring(L,(pa->nw) ? "W" : "E" , 1);
+  lua_setfield(L,-2,"hemisphere");
+}
+
+/********************************************************************/
+
+static void decode_answer(
+	lua_State    *L,
+	int           tab,
+	const char   *name,
+	dns_answer_t *pans,
+	size_t        cnt,
+	bool          dup
+)
+{
+  char ipaddr[INET6_ADDRSTRLEN];
+  
+  lua_createtable(L,cnt,0);
+  
+  for (size_t i = 0 ; i < cnt ; i++)
+  {
+    lua_pushinteger(L,i + 1);
+    lua_createtable(L,0,0);
+    
+    lua_pushstring(L,pans[i].generic.name);
+    lua_setfield(L,-2,"name");
+    lua_pushinteger(L,pans[i].generic.ttl);
+    lua_setfield(L,-2,"ttl");
+    lua_pushstring(L,dns_class_text(pans[i].generic.class));
+    lua_setfield(L,-2,"class");
+    lua_pushstring(L,dns_type_text(pans[i].generic.type));
+    lua_setfield(L,-2,"type");
+    
+    switch(pans[i].generic.type)
+    {
+      case RR_A:
+           inet_ntop(AF_INET,&pans[i].a.address,ipaddr,sizeof(ipaddr));
+           lua_pushstring(L,ipaddr);
+           lua_setfield(L,-2,"address");
+           lua_pushlstring(L,(char *)&pans[i].a.address,4);
+           lua_setfield(L,-2,"raw_address");
+           break;
+
+      case RR_SOA:
+           lua_pushstring(L,pans[i].soa.mname);
+           lua_setfield(L,-2,"mname");
+           lua_pushstring(L,pans[i].soa.rname);
+           lua_setfield(L,-2,"rname");
+           lua_pushnumber(L,pans[i].soa.serial);
+           lua_setfield(L,-2,"serial");
+           lua_pushnumber(L,pans[i].soa.refresh);
+           lua_setfield(L,-2,"refresh");
+           lua_pushnumber(L,pans[i].soa.retry);
+           lua_setfield(L,-2,"retry");
+           lua_pushnumber(L,pans[i].soa.expire);
+           lua_setfield(L,-2,"expire");
+           lua_pushnumber(L,pans[i].soa.minimum);
+           lua_setfield(L,-2,"minimum");
+           break;
+
+      case RR_NAPTR:
+           lua_pushinteger(L,pans[i].naptr.order);
+           lua_setfield(L,-2,"order");
+           lua_pushinteger(L,pans[i].naptr.preference);
+           lua_setfield(L,-2,"preference");
+           lua_pushstring(L,pans[i].naptr.flags);
+           lua_setfield(L,-2,"flags");
+           lua_pushstring(L,pans[i].naptr.services);
+           lua_setfield(L,-2,"services");
+           lua_pushstring(L,pans[i].naptr.regexp);
+           lua_setfield(L,-2,"regexp");
+           lua_pushstring(L,pans[i].naptr.replacement);
+           lua_setfield(L,-2,"replacement");
+           break;
+
+      case RR_AAAA:
+           inet_ntop(AF_INET6,&pans[i].aaaa.address,ipaddr,sizeof(ipaddr));
+           lua_pushstring(L,ipaddr);
+           lua_setfield(L,-2,"address");
+           lua_pushlstring(L,(char *)&pans[i].aaaa.address,16);
+           lua_setfield(L,-2,"raw_address");
+           break;
+
+      case RR_SRV:
+           lua_pushinteger(L,pans[i].srv.priority);
+           lua_setfield(L,-2,"priority");
+           lua_pushinteger(L,pans[i].srv.weight);
+           lua_setfield(L,-2,"weight");
+           lua_pushinteger(L,pans[i].srv.port);
+           lua_setfield(L,-2,"port");
+           lua_pushstring(L,pans[i].srv.target);
+           lua_setfield(L,-2,"target");
+           break;
+           
+      case RR_WKS:
+           inet_ntop(AF_INET,&pans[i].wks.address,ipaddr,sizeof(ipaddr));
+           lua_pushstring(L,ipaddr);
+           lua_setfield(L,-2,"address");
+           lua_pushlstring(L,(char *)&pans[i].wks.address,4);
+           lua_setfield(L,-2,"raw_address");
+           lua_pushinteger(L,pans[i].wks.protocol);
+           lua_setfield(L,-2,"protocol");
+           lua_pushlstring(L,(char *)pans[i].wks.bits,pans[i].wks.numbits);
+           lua_setfield(L,-2,"bits");
+           break;
+           
+      case RR_GPOS:
+           push_dnsgpos_angle(L,&pans[i].gpos.latitude,true);
+           lua_setfield(L,-2,"longitude");
+           push_dnsgpos_angle(L,&pans[i].gpos.longitude,false);
+           lua_setfield(L,-2,"latitude");
+           lua_pushnumber(L,pans[i].gpos.altitude);
+           lua_setfield(L,-2,"altitude");
+           break;
+           
+      case RR_LOC:
+           lua_pushnumber(L,pans[i].loc.size);
+           lua_setfield(L,-2,"size");
+           lua_pushnumber(L,pans[i].loc.horiz_pre);
+           lua_setfield(L,-2,"horiz_pre");
+           lua_pushnumber(L,pans[i].loc.vert_pre);
+           lua_setfield(L,-2,"vert_pre");
+           push_dnsgpos_angle(L,&pans[i].loc.latitude,true);
+           lua_setfield(L,-2,"latitude");
+           push_dnsgpos_angle(L,&pans[i].loc.longitude,false);
+           lua_setfield(L,-2,"longitude");
+           lua_pushnumber(L,pans[i].loc.altitude);
+           lua_setfield(L,-2,"altitude");
+           break;
+           
+      case RR_PX:
+           lua_pushstring(L,pans[i].px.map822);
+           lua_setfield(L,-2,"map822");
+           lua_pushstring(L,pans[i].px.mapx400);
+           lua_setfield(L,-2,"mapx400");
+           break;
+      
+      case RR_RP:
+           lua_pushstring(L,pans[i].rp.mbox);
+           lua_setfield(L,-2,"mbox");
+           lua_pushstring(L,pans[i].rp.domain);
+           lua_setfield(L,-2,"domain");
+           break;
+      
+      case RR_MINFO:
+           lua_pushstring(L,pans[i].minfo.rmailbx);
+           lua_setfield(L,-2,"rmailbx");
+           lua_pushstring(L,pans[i].minfo.emailbx);
+           lua_setfield(L,-2,"emailbx");
+           break;
+           
+      case RR_AFSDB:
+           lua_pushinteger(L,pans[i].afsdb.subtype);
+           lua_setfield(L,-2,"subtype");
+           lua_pushstring(L,pans[i].afsdb.hostname);
+           lua_setfield(L,-2,"hostname");
+           break;
+      
+      case RR_RT:
+           lua_pushinteger(L,pans[i].rt.preference);
+           lua_setfield(L,-2,"preference");
+           lua_pushstring(L,pans[i].rt.host);
+           lua_setfield(L,-2,"host");
+           break;
+           
+      case RR_MX:
+           lua_pushinteger(L,pans[i].mx.preference);
+           lua_setfield(L,-2,"preference");
+           lua_pushstring(L,pans[i].mx.exchange);
+           lua_setfield(L,-2,"exchange");
+           break;
+           
+      case RR_NSAP:
+           lua_pushstring(L,pans[i].nsap.length);
+           lua_setfield(L,-2,"length");
+           lua_pushstring(L,pans[i].nsap.nsapaddress);
+           lua_setfield(L,-2,"address");
+           break;
+           
+      case RR_ISDN:
+           lua_pushstring(L,pans[i].isdn.isdnaddress);
+           lua_setfield(L,-2,"address");
+           lua_pushstring(L,pans[i].isdn.sa);
+           lua_setfield(L,-2,"sa");
+           break;
+      
+      case RR_HINFO:
+           lua_pushstring(L,pans[i].hinfo.cpu);
+           lua_setfield(L,-2,"cpu");
+           lua_pushstring(L,pans[i].hinfo.os);
+           lua_setfield(L,-2,"os");
+           break;
+           
+      case RR_X25:
+           lua_pushlstring(L,pans[i].x25.psdnaddress,pans[i].x25.size);
+           lua_setfield(L,-2,"address");
+           break;
+           
+      case RR_SPF:
+           lua_pushlstring(L,pans[i].spf.text,pans[i].spf.len);
+           lua_setfield(L,-2,"text");
+           break;
+           
+      case RR_TXT:
+           lua_pushlstring(L,pans[i].txt.text,pans[i].txt.len);
+           lua_setfield(L,-2,"text");
+           break;
+      
+      case RR_NSAP_PTR:
+           lua_pushstring(L,pans[i].nsap_ptr.owner);
+           lua_setfield(L,-2,"owner");
+           break;
+      
+      case RR_MD:
+           lua_pushstring(L,pans[i].md.madname);
+           lua_setfield(L,-2,"madname");
+           break;
+      
+      case RR_MF:
+           lua_pushstring(L,pans[i].mf.madname);
+           lua_setfield(L,-2,"madname");
+           break;
+      
+      case RR_MB:
+           lua_pushstring(L,pans[i].mb.madname);
+           lua_setfield(L,-2,"madname");
+           break;
+      
+      case RR_MG:
+           lua_pushstring(L,pans[i].mg.mgmname);
+           lua_setfield(L,-2,"mgmname");
+           break;
+      
+      case RR_MR:
+           lua_pushstring(L,pans[i].mr.newname);
+           lua_setfield(L,-2,"newname");
+           break;
+      
+      case RR_NS:
+           lua_pushstring(L,pans[i].ns.nsdname);
+           lua_setfield(L,-2,"nsdname");
+           break;
+
+      case RR_PTR:
+           lua_pushstring(L,pans[i].ptr.ptr);
+           lua_setfield(L,-2,"ptr");
+           break;
+
+      case RR_CNAME:
+           lua_pushstring(L,pans[i].cname.cname);
+           lua_setfield(L,-2,"cname");
+           break;
+           
+      case RR_NULL:
+           lua_pushlstring(L,(char *)pans[i].null.data,pans[i].null.size);
+           lua_setfield(L,-2,"data");
+           break;
+           
+      case RR_OPT:
+           lua_pushinteger(L,pans[i].opt.udp_payload);
+           lua_setfield(L,-2,"udp_payload");
+           lua_pushinteger(L,pans[i].opt.version);
+           lua_setfield(L,-2,"version");
+           lua_pushboolean(L,pans[i].opt.fdo);
+           lua_setfield(L,-2,"fdo");
+           lua_createtable(L,pans[i].opt.numopts,0);
+           for (size_t j = 0 ; j < pans[i].opt.numopts ; j++)
+           {
+             lua_pushinteger(L,i + 1);
+             lua_createtable(L,0,2);
+             lua_pushlstring(L,(char *)pans[i].opt.opts[j].data,pans[i].opt.opts[j].len);
+             lua_setfield(L,-2,"data");
+             if (pans[i].opt.opts[j].code == EDNS0RR_NSID)
+               lua_pushstring(L,"NSID");
+             else
+               lua_pushinteger(L,pans[i].opt.opts[i].code);
+             lua_setfield(L,-2,"code");
+             lua_settable(L,-3);
+           }
+           lua_setfield(L,-2,"opts");
+           break;
+
+      default:
+           lua_pushlstring(L,(char *)pans[i].x.rawdata,pans[i].x.size);
+           lua_setfield(L,-2,"rawdata");
+           break;
+    }
+    
+    if (dup)
+    {
+      lua_getfield(L,-1,"name");
+      lua_pushvalue(L,-2);
+      lua_settable(L,-5);
+    }
+    
+    lua_settable(L,-3);
+  }
+  
+  lua_setfield(L,tab,name);         
+}
+
+/**********************************************************************/
+
+static int dnslua_decode(lua_State *L)
+{
+  dns_decoded_t      bufresult[DNS_DECODEBUF_8K];
+  dns_packet_t       data     [DNS_BUFFER_UDP];
+  const char        *luadata;
+  dns_query_t       *result;
+  size_t             size;
+  int                tab;
+  int                rc;
+  
+  /*---------------------------------------------------------------------
+  ; We need to make sure our data is properly aligned.  And hey, this is
+  ; Lua---a scripting lanague.  We can afford a bit of waste 8-)
+  ;----------------------------------------------------------------------*/
+  
+  luadata = luaL_checklstring(L,1,&size);
+  if (size > MAX_DNS_QUERY_SIZE) size = MAX_DNS_QUERY_SIZE;
+  memcpy(data,luadata,size);
+  
+  rc = dns_decode(bufresult,&(size_t){sizeof(bufresult)},data,size);
+  
+  if (rc != RCODE_OKAY)
+  {
+    lua_pushnil(L);
+    lua_pushstring(L,dns_rcode_text(rc));
+    return 2;
+  }
+  
+  result = (dns_query_t *)bufresult;
+  
+  lua_createtable(L,0,0);
+  tab = lua_gettop(L);
+  
+  lua_pushboolean(L,result->aa);
+  lua_setfield(L,tab,"aa");
+  lua_pushboolean(L,result->tc);
+  lua_setfield(L,tab,"tc");
+  lua_pushboolean(L,result->rd);
+  lua_setfield(L,tab,"rd");
+  lua_pushboolean(L,result->ra);
+  lua_setfield(L,tab,"ra");
+  lua_pushboolean(L,result->ad);
+  lua_setfield(L,tab,"ad");
+  lua_pushboolean(L,result->cd);
+  lua_setfield(L,tab,"cd");
+  lua_pushinteger(L,result->rcode);
+  lua_setfield(L,tab,"rcode");
+  
+  if (result->qdcount)
+  {
+    lua_createtable(L,0,3);
+    lua_pushstring(L,result->questions[0].name);
+    lua_setfield(L,-2,"name");
+    lua_pushstring(L,dns_class_text(result->questions[0].class));
+    lua_setfield(L,-2,"class");
+    lua_pushstring(L,dns_type_text(result->questions[0].type));
+    lua_setfield(L,-2,"type");
+    lua_setfield(L,tab,"question");
+  }
+
+  decode_answer(L,tab,"answers"     , result->answers    , result->ancount,false);
+  decode_answer(L,tab,"nameservers" , result->nameservers, result->nscount,false);
+  decode_answer(L,tab,"additional"  , result->additional , result->arcount,true);
+
+  assert(tab == lua_gettop(L));
+  
+  return 1;
+}
+
+/*********************************************************************/
+
+static int dnslua_strerror(lua_State *L)
+{
+  lua_pushstring(L,dns_rcode_text(luaL_checkint(L,1)));
+  return 1;
+}
+
+/*********************************************************************/
+  
+static int dnslua_query(lua_State *L)
+{
+  sockaddr_all  srvaddr;
+  const char   *server;
+  const char   *luaquery;
+  size_t        querysize;
+  dns_packet_t  query[DNS_BUFFER_UDP];
+  dns_packet_t  reply[DNS_BUFFER_UDP];
+  size_t        replysize;
+  int           rc;
+  
+  server   = luaL_checkstring(L,1);  
+  luaquery = luaL_checklstring(L,2,&querysize);
+  
+  if (net_server(&srvaddr,server) < 0)
+    luaL_error(L,"%s is not an IPv4/IPv6 address",server);
+  
+  if (querysize > MAX_DNS_QUERY_SIZE) querysize = MAX_DNS_QUERY_SIZE;
+  memcpy(query,luaquery,querysize);
+  replysize = sizeof(reply);
+  rc = net_request(&srvaddr,reply,&replysize,query,querysize);
+
+  if (rc != 0)
+  {
+    lua_pushnil(L);
+    lua_pushinteger(L,rc);
+    return 2;
+  }
+  
+  lua_pushlstring(L,(char *)reply,replysize);
+  return 1;
+}
+
+/**********************************************************************/
+
+static const struct luaL_reg reg_dns[] =
+{
+  { "encode"	, dnslua_encode		} ,
+  { "decode"	, dnslua_decode		} ,
+  { "strerror"	, dnslua_strerror	} ,
+  { "query"	, dnslua_query		} ,
+  { NULL	, NULL			} 
+};
+
+int luaopen_org_conman_dns(lua_State *L)
+{
+  luaL_register(L,"org.conman.dns",reg_dns);
+  
+  lua_pushliteral(L,"Copyright 2010 by Sean Conner.  All Rights Reserved.");
+  lua_setfield(L,-2,"COPYRIGHT");
+  
+  lua_pushliteral(L,"Encode/Decode and send queries via DNS");
+  lua_setfield(L,-2,"DESCRIPTION");
+  
+  lua_pushliteral(L,"0.0.1");
+  lua_setfield(L,-2,"VERSION");
+  
+  return 1;
+}
+
+/**********************************************************************/
+
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/src/mappings.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/src/mappings.c	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,373 @@
+/*************************************************************************
+*
+* Copyright 2010 by Sean Conner.  All Rights Reserved.
+*
+* This library is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or (at your
+* option) any later version.
+*
+* This library is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+* License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this library; if not, see <http://www.gnu.org/licenses/>.
+*
+**************************************************************************/
+
+/***********************************************************************
+*
+* Implementation of mapping values to strings, or strings to values.
+*
+* This code is written to C99.
+*
+************************************************************************/
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "dns.h"
+#include "mappings.h"
+
+/************************************************************************
+*
+* The following structure is used to map values to strings.  The arrays
+* defined by this structure *MUST* be sorted by value in ascending order.
+*
+*************************************************************************/
+
+struct int_string_map
+{
+  const int         value;
+  const char *const text;
+};
+
+/*******************************************************************
+*
+* The following structure is used to map strings to values.  The arrays
+* defined by this structure *MUST* be sorted by the strings in ascending
+* order.
+*
+*********************************************************************/
+
+struct string_int_map
+{
+  const char *const text;
+  const int         value;
+};
+
+/************************************************************************/
+
+static const struct int_string_map cm_dns_rcode[] =
+{
+  { RCODE_OKAY 			, "No error"	 			} ,
+  { RCODE_FORMAT_ERROR		, "Format error"			} ,
+  { RCODE_SERVER_FAILURE 	, "Server failure"			} ,
+  { RCODE_NAME_ERROR		, "Non-existant domain"			} ,
+  { RCODE_NOT_IMPLEMENTED	, "Not implemented"			} ,
+  { RCODE_REFUSED		, "Query refused"			} ,
+  { RCODE_YXDOMAIN		, "Name exists when it should not"	} ,
+  { RCODE_YXRRSET		, "RRset exists when it should not"	} ,
+  { RCODE_NXRRSET		, "RRset does not exist"		} ,
+  { RCODE_NOTAUTH		, "Server not authoritative"		} ,
+  { RCODE_NOTZONE		, "Zone not in zone section"		} ,
+  { RCODE_BADVERS		, "Bad OPT version/TSIG failed"		} ,
+  { RCODE_BADKEY		, "Key not recognized"			} ,
+  { RCODE_BADTIME		, "Signature out of time window"	} ,
+  { RCODE_BADMODE		, "Bad TKEY mode"			} ,
+  { RCODE_BADNAME		, "Duplicate key name"			} ,
+  { RCODE_BADALG		, "Algorithm not supported"		} ,
+  { RCODE_BADTRUC		, "Bad truncation"			} ,
+  { RCODE_NO_MEMORY		, "No memory"				} ,
+};
+
+#define RCODE_COUNT	(sizeof(cm_dns_rcode) / sizeof(struct int_string_map))
+
+static const struct int_string_map cm_dns_type[] =
+{
+  { RR_A	, "A"		} ,
+  { RR_NS	, "NS"		} ,
+  { RR_MD	, "MD"		} ,
+  { RR_MF	, "MF"		} ,
+  { RR_CNAME	, "CNAME"	} ,
+  { RR_SOA	, "SOA"		} ,
+  { RR_MB	, "MB"		} ,
+  { RR_MG	, "MG"		} ,
+  { RR_MR	, "MR"		} ,
+  { RR_NULL	, "NULL"	} ,
+  { RR_WKS	, "WKS"		} ,
+  { RR_PTR	, "PTR"		} ,
+  { RR_HINFO	, "HINFO"	} ,
+  { RR_MINFO	, "MINFO"	} ,
+  { RR_MX	, "MX"		} ,
+  { RR_TXT	, "TXT"		} ,
+  { RR_RP	, "RP"		} ,
+  { RR_AFSDB	, "AFSDB"	} ,
+  { RR_X25	, "X25"		} ,
+  { RR_ISDN	, "ISDN"	} ,
+  { RR_RT	, "RT"		} ,
+  { RR_NSAP	, "NSAP"	} ,
+  { RR_NSAP_PTR	, "NSAP-PTR"	} ,
+  { RR_SIG	, "SIG"		} ,
+  { RR_KEY	, "KEY"		} ,
+  { RR_PX	, "PX"		} ,
+  { RR_GPOS	, "GPOS"	} ,
+  { RR_AAAA	, "AAAA"	} ,
+  { RR_LOC	, "LOC"		} ,
+  { RR_NXT	, "NXT"		} ,
+  { RR_EID	, "EID"		} ,
+  { RR_NIMLOC	, "NIMLOC"	} ,
+  { RR_SRV	, "SRV"		} ,
+  { RR_ATM	, "ATM"		} ,
+  { RR_NAPTR	, "NAPTR"	} ,
+  { RR_KX	, "KX"		} ,
+  { RR_CERT	, "CERT"	} ,
+  { RR_A6	, "A6"		} ,
+  { RR_DNAME	, "DNAME"	} ,
+  { RR_SINK	, "SINK"	} ,
+  { RR_OPT	, "OPT"		} ,
+  { RR_APL	, "APL"		} ,
+  { RR_DS	, "DS"		} ,
+  { RR_RRSIG	, "RRSIG"	} ,
+  { RR_NSEC	, "NSEC"	} ,
+  { RR_DNSKEY	, "DNSKEY"	} ,
+  { RR_SPF	, "SPF"		} ,
+  { RR_TSIG	, "TSIG"	} ,
+  { RR_IXFR	, "IXFR"	} ,
+  { RR_AXFR	, "AXFR"	} ,
+  { RR_MAILB	, "MAILB"	} ,
+  { RR_MAILA	, "MAILA"	} ,
+  { RR_ANY	, "ANY"		}
+};
+
+#define TYPE_COUNT	(sizeof(cm_dns_type) / sizeof(struct int_string_map))
+
+static const struct string_int_map cm_dns_type_is[] =
+{
+  { "A"		, RR_A		} ,
+  { "A6"	, RR_A6		} ,
+  { "AAAA"	, RR_AAAA	} ,
+  { "AFSDB"	, RR_AFSDB	} ,
+  { "ANY"	, RR_ANY	} ,
+  { "APL"	, RR_APL	} ,
+  { "ATM"	, RR_ATM	} ,
+  { "AXFR"	, RR_AXFR	} ,
+  { "CERT"	, RR_CERT	} ,
+  { "CNAME"	, RR_CNAME	} ,
+  { "DNAME"	, RR_DNAME	} ,
+  { "DNSKEY"	, RR_DNSKEY	} ,
+  { "DS"	, RR_DS		} ,
+  { "EID"	, RR_EID	} ,
+  { "GPOS"	, RR_GPOS	} ,
+  { "HINFO"	, RR_HINFO	} ,
+  { "ISDN"	, RR_ISDN	} ,
+  { "IXFR"	, RR_IXFR	} ,
+  { "KEY"	, RR_KEY	} ,
+  { "KX"	, RR_KX		} ,
+  { "LOC"	, RR_LOC	} ,
+  { "MAILA"	, RR_MAILA	} ,
+  { "MAILB"	, RR_MAILB	} ,
+  { "MB"	, RR_MB		} ,
+  { "MD"	, RR_MD		} ,
+  { "MF"	, RR_MF		} ,
+  { "MG"	, RR_MG		} ,
+  { "MINFO"	, RR_MINFO	} ,
+  { "MR"	, RR_MR		} ,
+  { "MX"	, RR_MX		} ,
+  { "NAPTR"	, RR_NAPTR	} ,
+  { "NIMLOC"	, RR_NIMLOC	} ,
+  { "NS"	, RR_NS		} ,
+  { "NSAP"	, RR_NSAP	} ,
+  { "NSAP-PTR"	, RR_NSAP_PTR	} ,
+  { "NSEC"	, RR_NSEC	} ,
+  { "NULL"	, RR_NULL	} ,
+  { "NXT"	, RR_NXT	} ,
+  { "OPT"	, RR_OPT	} ,
+  { "PTR"	, RR_PTR	} ,
+  { "PX"	, RR_PX		} ,
+  { "RP"	, RR_RP		} ,
+  { "RRSIG"	, RR_RRSIG	} ,
+  { "RT"	, RR_RT		} ,
+  { "SIG"	, RR_SIG	} ,
+  { "SINK"	, RR_SINK	} ,
+  { "SOA"	, RR_SOA	} ,
+  { "SPF"	, RR_SPF	} ,
+  { "SRV"	, RR_SRV	} ,
+  { "TSIG"	, RR_TSIG	} ,
+  { "TXT"	, RR_TXT	} ,
+  { "WKS"	, RR_WKS	} ,
+  { "X25"	, RR_X25	} ,
+};
+
+static const struct int_string_map cm_dns_class[] =
+{
+  { CLASS_IN	, "IN"		} ,
+  { CLASS_CS	, "CS"		} ,
+  { CLASS_CH	, "CH"		} ,
+  { CLASS_HS	, "HS"		} ,
+  { CLASS_NONE	, "NONE"	} 
+};
+
+#define CLASS_COUNT	(sizeof(cm_dns_class) / sizeof(struct int_string_map))
+
+static const struct string_int_map cm_dns_class_is[] =
+{
+  { "CH"	, CLASS_CH	} ,
+  { "CS"	, CLASS_CS	} ,
+  { "HS"	, CLASS_HS	} ,
+  { "IN"	, CLASS_IN	} ,
+  { "NONE"	, CLASS_NONE	} ,
+};
+
+static const struct int_string_map cm_dns_op[] = 
+{
+  { OP_QUERY	, "QUERY"	} ,
+  { OP_IQUERY	, "IQUERY"	} ,
+  { OP_STATUS	, "STATUS"	} ,
+  { OP_NOTIFY	, "NOTIFY"	} ,
+  { OP_UPDATE	, "UPDATE"	}
+};
+
+#define OP_COUNT	(sizeof(cm_dns_op) / sizeof(struct int_string_map))
+
+static const struct string_int_map cm_dns_op_is[] = 
+{
+  { "IQUERY"	, OP_IQUERY	} ,
+  { "NOTIFY"	, OP_NOTIFY	} ,
+  { "QUERY"	, OP_QUERY	} ,
+  { "STATUS"	, OP_STATUS	} ,
+  { "UPDATE"	, OP_UPDATE	} 
+};
+ 
+/*************************************************************************/
+
+static int intstr_cmp(const void *needle,const void *haystack)
+{
+  const struct int_string_map *pism = haystack;
+  const int                   *pi   = needle;
+
+  assert(needle   != NULL);
+  assert(haystack != NULL);
+  
+  return *pi - pism->value;
+}
+
+/*********************************************************************/
+
+static int strint_cmp(const void *needle,const void *haystack)
+{
+  const struct string_int_map *psim = haystack;
+  const char                  *key  = needle;
+  
+  assert(needle   != NULL);
+  assert(haystack != NULL);
+  
+  return strcmp(key,psim->text);
+}
+
+/**********************************************************************/
+
+static const char *itosdef(
+	int                                         v,
+	const struct int_string_map *const restrict pitab,
+	const size_t                                itabcnt,
+	const char                  *const restrict def
+)
+{
+  struct int_string_map *pism;
+  
+  assert(v       >= 0);
+  assert(pitab   != NULL);
+  assert(itabcnt >  0);
+  assert(def     != NULL);
+  
+  pism = bsearch(&v,pitab,itabcnt,sizeof(struct int_string_map),intstr_cmp);
+  if (pism)
+    return pism->text;
+  else
+    return def;
+}
+
+/********************************************************************/
+
+static int stoidef(
+	const char *const restrict                  tag,
+	const struct string_int_map *const restrict pstab,
+	const size_t                                stabcnt,
+	const int                                   def
+)
+{
+  struct string_int_map *psim;
+  size_t                 len = strlen(tag) + 1;
+  char                   buffer[len];
+  
+  for (size_t i = 0 ; i < len ; i++)
+    buffer[i] = toupper(tag[i]);
+  
+  psim = bsearch(buffer,pstab,stabcnt,sizeof(struct string_int_map),strint_cmp);
+  if (psim)
+    return psim->value;
+  else
+    return def;
+}
+
+/*******************************************************************/
+
+const char *dns_rcode_text(const dns_rcode_t r)
+{
+  return itosdef(r,cm_dns_rcode,RCODE_COUNT,"Unknown error");
+}
+
+/*********************************************************************/
+
+const char *dns_type_text(const dns_type_t t)
+{
+  return itosdef(t,cm_dns_type,TYPE_COUNT,"X-UNKN");
+}
+
+/**********************************************************************/
+
+const char *dns_class_text(const dns_class_t c)
+{
+  return itosdef(c,cm_dns_class,CLASS_COUNT,"X-UNKN");
+}
+
+/*******************************************************************/
+
+const char *dns_op_text(const dns_op_t o)
+{
+  return itosdef(o,cm_dns_op,OP_COUNT,"X-UNKNOWN");
+}
+
+/********************************************************************/
+
+dns_type_t dns_type_value(const char *tag)
+{
+  return stoidef(tag,cm_dns_type_is,TYPE_COUNT,RR_UNKNOWN);
+}
+
+/*********************************************************************/
+
+dns_class_t dns_class_value(const char *tag)
+{
+  return stoidef(tag,cm_dns_class_is,CLASS_COUNT,CLASS_UNKNOWN);
+}
+
+/**********************************************************************/
+
+dns_op_t dns_op_value(const char *tag)
+{
+  return stoidef(tag,cm_dns_op_is,OP_COUNT,OP_UNKNOWN);
+}
+
+/**********************************************************************/
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/src/mappings.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/src/mappings.h	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,55 @@
+/*************************************************************************
+*
+* Copyright 2010 by Sean Conner.  All Rights Reserved.
+*
+* This library is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or (at your
+* option) any later version.
+*
+* This library is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+* License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this library; if not, see <http://www.gnu.org/licenses/>.
+*
+**************************************************************************/
+
+/**************************************************************************
+*
+* Useful routines to convert error codes, RR, Class and Opcode values into
+* strings, and strings into their equivilent RR, Class or Opcode values.
+*
+* This file assumes C99.  You must include the following files before
+* including this one:
+*
+* #include "dns.h"
+*
+**************************************************************************/
+
+#ifndef DNS_MAPPINGS_H
+#define DNS_MAPPINGS_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+#ifndef __GNUC__
+#  define __attribute__(x)
+#endif
+
+const char 	*dns_rcode_text		(const dns_rcode_t)	__attribute__ ((pure,nothrow));
+const char 	*dns_type_text 		(const dns_type_t)	__attribute__ ((pure,nothrow));
+const char 	*dns_class_text		(const dns_class_t)	__attribute__ ((pure,nothrow));
+const char 	*dns_op_text		(const dns_op_t)	__attribute__ ((pure,nothrow));
+
+dns_type_t	 dns_type_value		(const char *const)	__attribute__ ((pure,nothrow,nonnull));
+dns_class_t	 dns_class_value	(const char *const)	__attribute__ ((pure,nothrow,nonnull));
+dns_op_t	 dns_op_value		(const char *const)	__attribute__ ((pure,nothrow,nonnull));
+
+#ifdef __cplusplus
+  }
+#endif
+#endif
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/src/netsimple.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/src/netsimple.c	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,156 @@
+/*************************************************************************
+*
+* Copyright 2010 by Sean Conner.  All Rights Reserved.
+*
+* This library is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or (at your
+* option) any later version.
+*
+* This library is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+* License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this library; if not, see <http://www.gnu.org/licenses/>.
+*
+**************************************************************************/
+
+/*********************************************************************
+*
+* Implementation of the simple network interface for DNS queries.  Two
+* functions are exported:
+*
+*	net_server()
+*
+*		decode the IP address (IPv4/IPv6) from a text representation
+*		to a network format.
+*
+*	net_request()
+*
+*		Send a request to the given server and wait a reponse.  This
+*		function is stupid simple---it opens a socket, sends the
+*		request via sendto(), waits for up to 15 seconds for a
+*		reply.  If no reply is seen in 15 seconds, close the socket
+*		and return an error---otherwise, call recvfrom(), close the
+*		socket and return the data.
+*
+*		Like I said, stupid simple.  It's enough for testing and
+*		very simple programs.
+*
+* This code is written for C99.
+*
+**************************************************************************/
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "dns.h"
+#include "netsimple.h"
+
+/************************************************************************/
+
+int net_server(
+	sockaddr_all *const restrict addr,
+	const char   *const restrict host
+)
+{
+  assert(addr != NULL);
+  assert(host != NULL);
+  
+  memset(addr,0,sizeof(sockaddr_all));
+  
+  if (inet_pton(AF_INET,host,&addr->sin.sin_addr.s_addr) < 0)
+  {
+    if (inet_pton(AF_INET6,host,&addr->sin6.sin6_addr.s6_addr) < 0)
+      return errno;
+    addr->sin6.sin6_family = AF_INET6;
+    addr->sin6.sin6_port   = htons(53);
+  }
+  else
+  {
+    addr->sin.sin_family = AF_INET;
+    addr->sin.sin_port   = htons(53);
+  }
+  
+  return 0;
+}
+
+/************************************************************************/
+
+int net_request(
+	sockaddr_all       *const restrict srvaddr,
+	dns_packet_t       *const restrict dest,
+	size_t             *const restrict dsize,
+	const dns_packet_t *const restrict src,
+	const size_t                      ssize
+)
+{
+  struct pollfd polldat;
+  socklen_t     asize;
+  ssize_t       bytes;
+  int           sock;
+  int           rc;
+  int           err;
+  
+  switch(srvaddr->sa.sa_family)
+  {
+    case AF_INET:  asize = sizeof(struct sockaddr_in);  break;
+    case AF_INET6: asize = sizeof(struct sockaddr_in6); break;
+    default:       assert(0); return EPROTOTYPE;
+  }
+
+  sock = socket(srvaddr->sa.sa_family,SOCK_DGRAM,0);
+  if (sock < 0)
+    return errno;
+
+  bytes = sendto(sock,src,ssize,0,&srvaddr->sa,asize);
+  if (bytes < 0)
+  {
+    err = errno;
+    close(sock);
+    return err;
+  }
+
+  polldat.fd     = sock;
+  polldat.events = POLLIN;
+  
+  rc = poll(&polldat,1,15000);
+  if (rc < 0)
+  {
+    err = errno;
+    close(sock);
+    return errno;
+  }
+  
+  if (rc == 0)
+  {
+    close(sock);
+    return ETIME;
+  }
+
+  bytes = recvfrom(sock,dest,*dsize,0,NULL,NULL);
+  if (bytes < 0)
+  {
+    int err = errno;
+    close(sock);
+    return err;
+  }
+
+  *dsize = bytes;
+  close(sock);
+  return 0;
+}
+
+/**************************************************************************/
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/src/netsimple.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/src/netsimple.h	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,75 @@
+/*************************************************************************
+*
+* Copyright 2010 by Sean Conner.  All Rights Reserved.
+*
+* This library is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or (at your
+* option) any later version.
+*
+* This library is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+* License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this library; if not, see <http://www.gnu.org/licenses/>.
+*
+**************************************************************************/
+
+/************************************************************************
+*
+* Definitions for a simple network interface to send and receive DNS
+* queries.
+* 
+* This only suffices for simple applications; for anything that does a lot
+* of DNS queries, you probably want to use something else.
+*
+* This file assumes C99.  You must include the following files before 
+* including this one:
+*
+* #include <stdint.h>
+* #include <stddef.h>
+* #include <arpa/inet.h>
+*
+* And if you want to decode the return values (beyond success/failure):
+*
+* #include <errno.h>
+*
+*************************************************************************/
+
+#ifndef NETSIMPLE_H
+#define NETSIMPLE_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+#ifndef __GNUC__
+#  define __attribute__(x)
+#endif
+
+typedef union sockaddr_all
+{
+  struct sockaddr     sa;
+  struct sockaddr_in  sin;
+  struct sockaddr_in6 sin6;
+} sockaddr_all;
+
+int 	net_server	(
+			  sockaddr_all *const restrict,
+			  const char   *const restrict
+			) __attribute__ ((nonnull));
+			
+int 	net_request	(
+			  sockaddr_all       *const restrict,
+			  dns_packet_t       *const restrict,
+			  size_t             *const restrict,
+			  const dns_packet_t *const restrict,
+			  const size_t
+			) __attribute__ ((nonnull(1,2,3,4)));
+
+#ifdef __cplusplus
+  }
+#endif
+#endif
diff -r 3bd5c2e9e8ac -r fee6b517c6d4 contrib/spcdns/src/test.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/spcdns/src/test.c	Wed Sep 19 15:25:04 2012 +0300
@@ -0,0 +1,563 @@
+/*************************************************************************
+*
+* Copyright 2010 by Sean Conner.  All Rights Reserved.
+*
+* This library is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or (at your
+* option) any later version.
+*
+* This library is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+* License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this library; if not, see <http://www.gnu.org/licenses/>.
+*
+**************************************************************************/
+
+/**********************************************************************
+*
+* Sample application using my DNS library.  It's somewhat similar to dig,
+* but lacking features found in dig.  Still useful though, and gives an
+* example of how to use the DNS library.
+*
+* This code is C99.
+*
+***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <getopt.h>
+#include <arpa/inet.h>
+
+#include "dns.h"
+#include "mappings.h"
+#include "netsimple.h"
+
+enum
+{
+  OPT_NONE	= '\0',
+  OPT_HELP	= 'h',
+  OPT_EDNS      = 'e',
+  OPT_SERVER	= 's',
+  OPT_DUMP	= 'd',
+  OPT_ERR	= '?'
+};
+
+/************************************************************************/
+
+static void print_question	(const char *,dns_question_t *,size_t);
+static void print_answer	(const char *,dns_answer_t   *,size_t);
+static void usage		(const char *);
+static void dump_memory		(FILE *,const void *,size_t,size_t);
+
+/************************************************************************/
+
+const struct option c_options[] =
+{
+  { "server"	, required_argument	, NULL	, OPT_SERVER 	} ,
+  { "edns"	, no_argument		, NULL  , OPT_EDNS	} ,
+  { "dump"	, no_argument		, NULL	, OPT_DUMP	} ,
+  { "help"	, no_argument		, NULL	, OPT_HELP	} ,
+  { NULL	, 0			, NULL	, 0		}
+};
+
+/***********************************************************************/
+
+int main(int argc,char *argv[])
+{
+  const char *serverhost;
+  const char *host;
+  const char *type;
+  bool        fdump;
+  bool        fedns;
+  int         option;
+  int         rc;
+  
+  /*-------------------------------------------------------------------------
+  ; verbiage to parse the command line and set some sane defaults, yada yada
+  ; blah blah blah
+  ;------------------------------------------------------------------*/
+  
+  serverhost = "127.0.0.1";
+  host       = "examle.net";
+  type       = "A";
+  fdump      = false;
+  fedns      = false;
+  option     = 0;
+  opterr     = 0; /* prevent getopt_long() from printing error messages */
+  
+  while(true)
+  {
+    rc = getopt_long(argc,argv,"hdes:",c_options,&option);
+    if (rc == EOF) break;
+    
+    switch(rc)
+    {
+      default:
+      case OPT_ERR:
+           fprintf(stderr,"unknown option '%c'\n",optopt);
+      case OPT_HELP:
+           usage(argv[0]);
+           return EXIT_FAILURE;
+      case OPT_SERVER:
+           serverhost = optarg;
+           break;
+      case OPT_EDNS:
+           fedns = true;
+           break;
+      case OPT_DUMP:
+           fdump = true;
+           break;
+      case OPT_NONE:
+           break;
+    }
+  }
+  
+  if (optind == argc)
+  {
+    usage(argv[0]);
+    return EXIT_FAILURE;
+  }
+  
+  host = argv[optind++];
+  
+  if (optind < argc)
+    type = argv[optind];
+  
+  /*------------------------------------------------------------------------
+  ; Encoding of a DNS query.  I'm finding that many (if not all) DNS servers
+  ; only accept a single question, even though the protocol seems to allow
+  ; more than one question.  If there *is* a DNS server that can handle
+  ; multiple questions, then we can handle them, even if they don't exist
+  ; yet.
+  ;--------------------------------------------------------------------*/
+  
+  dns_question_t domain;
+  dns_query_t    query;
+  dns_packet_t   request[DNS_BUFFER_UDP];
+  size_t         reqsize;
+  edns0_opt_t    opt;
+  dns_answer_t   edns;
+  
+  domain.name  = host;
+  domain.type  = dns_type_value(type);
+  domain.class = CLASS_IN;
+
+  query.id          = 1234;	/* should be a random value */
+  query.query       = true;
+  query.opcode      = OP_QUERY;
+  query.aa          = false;
+  query.tc          = false;
+  query.rd          = true;
+  query.ra          = false;
+  query.ad          = false;
+  query.cd          = false;
+  query.rcode       = RCODE_OKAY;
+  query.qdcount     = 1;
+  query.questions   = &domain;
+  query.ancount     = 0;
+  query.answers     = NULL;
+  query.nscount     = 0;
+  query.nameservers = NULL;
+  query.arcount     = 0;
+  query.additional  = NULL;
+  
+  if (fedns)
+  {
+    /*----------------------------------------------------------------------
+    ; Test EDNS0 by sending an NSID OPT RR type query.  
+    ;
+    ; The udp_payload is the largest UDP packet we can reasonably expect to
+    ; receive.  I'm using the value 1464 since that's about the largest UDP
+    ; packet that can fit into an Ethernet frame (20 bytes IP header, 8
+    ; bytes UDP header; RFC-1042 based Ethernet frame).
+    ;
+    ; Additionally, OPT RRs *MUST* be in the additional section of a DNS
+    ; packet, and there can be only one (Highlander 2 & 3?  Never happened;
+    ; neither did the TV series) OPT RR.
+    ;----------------------------------------------------------------------*/
+    
+    opt.code = EDNS0RR_NSID;
+    opt.data = (uint8_t *)"MY-DNS-SERVER-ID";
+    opt.len  = strlen((char *)opt.data);
+    
+    edns.opt.name        = ".";
+    edns.opt.type        = RR_OPT;
+    edns.opt.class       = CLASS_UNKNOWN;
+    edns.opt.ttl         = 0;
+    edns.opt.udp_payload = 1464;
+    edns.opt.version     = 0;
+    edns.opt.fdo         = false;
+    edns.opt.numopts     = 1;
+    edns.opt.opts        = &opt;
+    
+    query.arcount    = 1;
+    query.additional = &edns;
+  }
+ 
+  reqsize = sizeof(request);
+  rc      = dns_encode(request,&reqsize,&query);
+  if (rc != RCODE_OKAY)
+  {
+    fprintf(stderr,"dns_encode() = (%d) %s\n",rc,dns_rcode_text(rc));
+    return EXIT_FAILURE;
+  }
+  
+  if (fdump)
+  {  
+    printf("OUTGOING:\n\n");
+    dump_memory(stdout,request,reqsize,0);
+  }
+
+  /*-----------------------------------------------------------------------
+  ; Sending a DNS query.  This uses the simple interface provided and is
+  ; not good for much *except* as an example.  If you have any complex
+  ; requirements, do not look to this code.
+  ;-----------------------------------------------------------------------*/
+  
+  sockaddr_all server;
+  dns_packet_t reply[DNS_BUFFER_UDP];
+  size_t       replysize;
+
+  rc = net_server(&server,serverhost);
+  if (rc != 0)
+  {
+    fprintf(stderr,"net_server() = %s",strerror(rc)); 
+    return EXIT_FAILURE;
+  }
+  
+  replysize = sizeof(reply);
+  if (net_request(&server,reply,&replysize,request,reqsize) < 0)
+  {
+    fprintf(stderr,"failure\n");
+    return EXIT_FAILURE;
+  }
+
+  if (fdump)
+  {
+    printf("\nINCOMING:\n\n");
+    dump_memory(stdout,reply,replysize,0);
+  }
+
+  /*----------------------------------------------------------------------
+  ; Decode a DNS packet into something we can use.  dns_decoded_t is a type
+  ; to ensure proper alignment for stack based results---this must be big
+  ; enough to handle not only the dns_query_t but additional information as
+  ; well.  The 4K size so far seems good enough for decoding UDP packets,
+  ; although I'm using the 8K size just in case.
+  ;-----------------------------------------------------------------------*/
+  
+  dns_decoded_t  bufresult[DNS_DECODEBUF_8K];
+  size_t         bufsize;
+  dns_query_t   *result;
+  
+  bufsize = sizeof(bufresult);
+  rc = dns_decode(bufresult,&bufsize,reply,replysize);
+  if (rc != RCODE_OKAY)
+  {
+    fprintf(stderr,"dns_decode() = (%d) %s\n",rc,dns_rcode_text(rc));
+    return EXIT_FAILURE;
+  }
+  
+  if (fdump)
+    printf("\nBytes used: %lu\n\n",(unsigned long)bufsize);
+  
+  result = (dns_query_t *)bufresult;
+
+  /*-------------------------------------------
+  ; Print the results out, ala dig
+  ;-------------------------------------------*/
+
+  printf(
+  	"; Questions            = %lu\n"
+  	"; Answers              = %lu\n"
+  	"; Name Servers         = %lu\n"
+  	"; Additional Records   = %lu\n"
+  	"; Authoritative Result = %s\n"
+  	"; Truncated Result     = %s\n"
+  	"; Recursion Desired    = %s\n"
+  	"; Recursion Available  = %s\n"
+  	"; Result               = %s\n",
+  	(unsigned long)result->qdcount,
+  	(unsigned long)result->ancount,
+  	(unsigned long)result->nscount,
+  	(unsigned long)result->arcount,
+  	result->aa ? "true" : "false",
+  	result->tc ? "true" : "false",
+  	result->rd ? "true" : "false",
+  	result->ra ? "true" : "false",
+  	dns_rcode_text(result->rcode)
+  );
+  	
+  print_question("QUESTIONS"   ,result->questions   ,result->qdcount);
+  print_answer  ("ANSWERS"     ,result->answers     ,result->ancount);
+  print_answer  ("NAMESERVERS" ,result->nameservers ,result->nscount);
+  print_answer  ("ADDITIONAL"  ,result->additional  ,result->arcount);
+
+  return EXIT_SUCCESS;
+}
+
+/************************************************************************/
+
+static void print_question(const char *tag,dns_question_t *pquest,size_t cnt)
+{
+  assert(tag    != NULL);
+  assert(pquest != NULL);
+  
+  printf("\n;;; %s\n\n",tag);
+  for (size_t i = 0 ; i < cnt ; i++)
+  {
+    printf(
+    	";%s %s %s\n",
+    	pquest[i].name,
+    	dns_class_text(pquest[i].class),
+    	dns_type_text (pquest[i].type)
+    );
+  }
+}
+
+/***********************************************************************/
+
+static void print_answer(const char *tag,dns_answer_t *pans,size_t cnt)
+{
+  char ipaddr[INET6_ADDRSTRLEN];
+  
+  assert(tag  != NULL);
+  assert(pans != NULL);
+  
+  printf("\n;;; %s\n\n",tag);
+  
+  for (size_t i = 0 ; i < cnt ; i++)
+  {
+    if (pans[i].generic.type != RR_OPT)
+    {
+      printf(
+    	"%-16s\t%5lu\t%s\t%s\t",
+    	pans[i].generic.name,
+    	(unsigned long)pans[i].generic.ttl,
+    	dns_class_text(pans[i].generic.class),
+    	dns_type_text (pans[i].generic.type)
+      );
+    }
+    else
+      printf("; OPT RR");
+    
+    switch(pans[i].generic.type)
+    {
+      case RR_NS: 
+           printf("%s",pans[i].ns.nsdname);
+           break;
+      case RR_A:
+           inet_ntop(AF_INET,&pans[i].a.address,ipaddr,sizeof(ipaddr));
+           printf("%s",ipaddr);
+           break;
+      case RR_AAAA:
+           inet_ntop(AF_INET6,&pans[i].aaaa.address,ipaddr,sizeof(ipaddr));
+           printf("%s",ipaddr);
+           break;
+      case RR_CNAME:
+           printf("%s",pans[i].cname.cname);
+           break;
+      case RR_MX:
+           printf("%5d %s",pans[i].mx.preference,pans[i].mx.exchange);
+           break;
+      case RR_PTR:
+           printf("%s",pans[i].ptr.ptr);
+           break;
+      case RR_HINFO:
+           printf("\"%s\" \"%s\"",pans[i].hinfo.cpu,pans[i].hinfo.os);
+           break;
+      case RR_MINFO:
+           printf("(\n\t\t\"%s\"\n\t\t\"%s\" )",pans[i].minfo.rmailbx,pans[i].minfo.emailbx);
+           break;
+      case RR_SPF:
+      case RR_TXT:
+           if (pans[i].txt.len < 30)
+             printf("\"%s\"",pans[i].txt.text);
+           else
+           {
+             size_t len;
+             int    max;
+             size_t off;
+             
+             printf("(");
+             len = pans[i].txt.len;
+             off = 0;
+             
+             while(len)
+             {
+               max = (len > 64) ? 64 : (int)len;
+               printf("\n\t\"%*.*s\"",max,max,&pans[i].txt.text[off]);
+               off += max;
+               len -= max;
+             }
+             
+             printf("\n\t\t)\n");
+           }
+           break;
+      case RR_SOA:
+           printf(
+           	"%s %s (\n"
+           	"\t\t%10lu   ; Serial\n"
+           	"\t\t%10lu   ; Refresh\n"
+           	"\t\t%10lu   ; Retry\n"
+           	"\t\t%10lu   ; Expire\n"
+           	"\t\t%10lu ) ; Miminum\n",
+           	pans[i].soa.mname,
+           	pans[i].soa.rname,
+           	(unsigned long)pans[i].soa.serial,
+           	(unsigned long)pans[i].soa.refresh,
+           	(unsigned long)pans[i].soa.retry,
+           	(unsigned long)pans[i].soa.expire,
+           	(unsigned long)pans[i].soa.minimum
+           );
+           break;
+      case RR_NAPTR:
+           printf(
+           	"%5d %5d (\n"
+           	"\t\t\"%s\"\n"
+           	"\t\t\"%s\"\n"
+           	"\t\t\"%s\"\n"
+           	"\t\t%s )\n",
+           	pans[i].naptr.order,
+           	pans[i].naptr.preference,
+           	pans[i].naptr.flags,
+           	pans[i].naptr.services,
+           	pans[i].naptr.regexp,
+           	pans[i].naptr.replacement
+           );
+           break;
+      case RR_LOC:
+           printf(
+           	"(\n"
+           	"\t\t%3d %2d %2d %s ; Latitude\n"
+           	"\t\t%3d %2d %2d %s ; Longitude\n"
+           	"\t\t%11ld ; Altitude\n"
+           	"\t\t%11lu ; Size\n"
+           	"\t\t%11lu ; Horizontal Precision\n"
+           	"\t\t%11lu ; Vertical Precision\n"
+           	"\t\t)\n",
+           	pans[i].loc.latitude.deg,
+           	pans[i].loc.latitude.min,
+           	pans[i].loc.latitude.sec,
+           	pans[i].loc.latitude.nw ? "N" : "S",
+           	pans[i].loc.longitude.deg,
+           	pans[i].loc.longitude.min,
+           	pans[i].loc.longitude.sec,
+           	pans[i].loc.longitude.nw ? "W" : "E",
+           	pans[i].loc.altitude,
+           	pans[i].loc.size,
+           	pans[i].loc.horiz_pre,
+           	pans[i].loc.vert_pre
+           );
+           break;
+      case RR_SRV:
+           printf(
+           	"%5d %5d %5d %s",
+           	pans[i].srv.priority,
+           	pans[i].srv.weight,
+           	pans[i].srv.port,
+           	pans[i].srv.target
+           );
+           break;
+      case RR_OPT:
+           printf(
+           	"\n"
+           	";\tpayload = %lu\n"
+           	";\tDO      = %s\n"
+           	";\t#opts   = %lu\n",
+           	(unsigned long)pans[i].opt.udp_payload,
+           	pans[i].opt.fdo ? "true" : "false",
+           	(unsigned long)pans[i].opt.numopts
+           );
+           break;
+           
+      default:
+           break;
+    }
+    printf("\n");
+  }
+}
+
+/*********************************************************************/
+
+static void usage(const char *prog)
+{
+  assert(prog != NULL);
+  
+  fprintf(
+  	stderr,
+  	"usage: %s [-h] [-d] [-e] [-s server] host [type]\n"
+  	"\t-h\t\tusage text (this text)\n"
+  	"\t-d\t\tdump raw DNS queries\n"
+  	"\t-e\t\tInclude EDNS0 RR with query\n"
+  	"\t-s server\tIP address of server\n"
+  	"\n"
+  	"\ttype\t\tRR DNS type\n",
+  	prog
+  );
+}
+
+/**********************************************************************/
+
+#define LINESIZE	16
+
+static void dump_memory(FILE *out,const void *data,size_t size,size_t offset)
+{
+  const unsigned char *block = data;
+  char                 ascii[LINESIZE + 1];
+  int                  skip;
+  int                  j;
+  
+  assert(out   != NULL);
+  assert(block != NULL);
+  assert(size  >  0);
+  
+  while(size > 0)
+  {
+    fprintf(out,"%08lX: ",(unsigned long)offset);
+    
+    for (skip = offset % LINESIZE , j = 0 ; skip ; j++ , skip--)
+    {
+      fputs("   ",out);
+      ascii[j] = ' ';
+    }
+    
+    do
+    {
+      fprintf(out,"%02x ",*block);
+      if (isprint(*block))
+        ascii[j] = *block;
+      else
+        ascii[j] = '.';
+      
+      block++;
+      offset++;
+      j++;
+      size--;
+    } while((j < LINESIZE) && (size > 0));
+    
+    ascii[j] = '\0';
+
+    if (j < LINESIZE)
+    {
+      int i;
+      
+      for (i = j ; i < LINESIZE ; i++)
+        fputs("   ",out);
+    }
+    fprintf(out,"%s\n",ascii);
+  }
+}
+
+/**********************************************************************/
+


More information about the Zrouter-src mailing list