summaryrefslogtreecommitdiff
path: root/programs/lwdnsq
diff options
context:
space:
mode:
authorRene Mayrhofer <rene@mayrhofer.eu.org>2006-05-22 05:12:18 +0000
committerRene Mayrhofer <rene@mayrhofer.eu.org>2006-05-22 05:12:18 +0000
commitaa0f5b38aec14428b4b80e06f90ff781f8bca5f1 (patch)
tree95f3d0c8cb0d59d88900dbbd72110d7ab6e15b2a /programs/lwdnsq
parent7c383bc22113b23718be89fe18eeb251942d7356 (diff)
downloadvyos-strongswan-aa0f5b38aec14428b4b80e06f90ff781f8bca5f1.tar.gz
vyos-strongswan-aa0f5b38aec14428b4b80e06f90ff781f8bca5f1.zip
Import initial strongswan 2.7.0 version into SVN.
Diffstat (limited to 'programs/lwdnsq')
-rw-r--r--programs/lwdnsq/.cvsignore4
-rw-r--r--programs/lwdnsq/CONTRACT.txt106
-rw-r--r--programs/lwdnsq/Makefile96
-rw-r--r--programs/lwdnsq/cmds.c351
-rw-r--r--programs/lwdnsq/lookup.c632
-rw-r--r--programs/lwdnsq/lwdnsq.8250
-rw-r--r--programs/lwdnsq/lwdnsq.c506
-rw-r--r--programs/lwdnsq/lwdnsq.h121
-rw-r--r--programs/lwdnsq/lwdnsq.xml.in446
-rw-r--r--programs/lwdnsq/states.fig66
-rw-r--r--programs/lwdnsq/states.pngbin0 -> 6756 bytes
11 files changed, 2578 insertions, 0 deletions
diff --git a/programs/lwdnsq/.cvsignore b/programs/lwdnsq/.cvsignore
new file mode 100644
index 000000000..b1ff942bf
--- /dev/null
+++ b/programs/lwdnsq/.cvsignore
@@ -0,0 +1,4 @@
+dnskey
+dnskey.cat8
+lwdnsq
+lwdnsq.xml
diff --git a/programs/lwdnsq/CONTRACT.txt b/programs/lwdnsq/CONTRACT.txt
new file mode 100644
index 000000000..77335e8cf
--- /dev/null
+++ b/programs/lwdnsq/CONTRACT.txt
@@ -0,0 +1,106 @@
+The only delays are after START, and after CNAME.
+
+add the time to each line.
+
+put DNSSEC status on each line.
+
+The format of the replies is:
+
+ <ID> <TIME> <TTL> <TYPE> <TYPE-SPECIFIC> \n
+ ^- whitespace.
+
+ID is a unique number that identifies the transaction. It is determined
+by the caller. lwdnsq treats this ID as a string, and does nothing
+with it other than repeat it on each line.
+There is no predetermined bound on the length, but the total line
+length of input to lwdnsq must not exceed LWDNSQ_CMDBUF_LEN (1024).
+LWDNSQ_CMDBUF_LEN is defined in <freeswan.h>
+
+The output of lwdnsq is currently limited to LWDNSQ_RESULT_LEN_MAX (4096) byte
+lines. LWDNSQ_RESULT_LEN_MAX is defined in <freeswan.h>
+
+Time is a decimal encoded integer, currently 32-bit time (time_t) since Unix
+epoch. On systems with a 64-bit time_t, it would be 64-bit in range.
+
+The TTL field gives the number of seconds that the result is valid for.
+(starting at the time given). If there is no useful TTL value for the
+record, it will be either "0".
+
+Type is a case-insensitive, one of:
+structure
+ START (optional comments) acknowledges start of transaction
+ DONE (optional comments) signals the end of data for a transaction
+
+errors
+ RETRY same as for FATAL, but this implies that the data
+ was not found, but could be found later.
+
+ FATAL Following this, is text detailing the fault,
+ in a human readable form.
+ "FATAL" results likely mean that the lwdnsq should
+ be restarted.
+
+ WARNING Log this result, but do not cancel transaction.
+
+ Errors are still followed by "DONE".
+
+
+
+data answers
+ DNSSEC followed by "OKAY" or "not present"
+ NAME followed by canonical name for requested RR,
+ i.e. the result of any CNAMEs/DNAMEs that were chased
+ by the recursive resolving server.
+ CNAME followed by the name which has been followed.
+ CNAMEFROM the thing that was mapped
+ TXT followed by RR-specific Presentation Format
+ SIG "
+ A "
+ AAAA "
+ PTR "
+ KEY "
+ AD-TXT followed by RR-specific Presentation Format - DNSSEC
+ TXT followed by RR-specific Presentation Format - DNSSEC
+ AD-KEY followed by RR-specific Presentation Format - DNSSEC
+ KEY followed by RR-specific Presentation Format - DNSSEC
+
+If there is no data of the type requested, even after lwdnsq
+has attempted to follow CNAMEs, then there will be no resource
+records returned. This is the formal indication of the lack of
+the records, however, in addition, an error will be returned, of the type:
+ RETRY the record "foobar" does not have a RR resource record.
+
+The -ldns library from bind9 will deal with the presentation format,
+producing a structure breakout from it. The functions are:
+
+dns_rdata_fromtext(3)
+ Presentation Format -> Wire Format
+dns_rdata_tostruct(3)
+ Wire Format -> C-structure
+
+dns_rdata_totext(3)
+ Wire Format -> Presentation Format
+
+(Above from .../src/bind-9.3.0s20020722/lib/dns/include/dns/rdata.h)
+
+The lwdnsq program uses dns_rdata_totext(3) to format the resource record
+(received from lwres in wire format) into its presentation format.
+
+The documentation is in the bind-9.3 source tree, in the header files.
+(They are likely all installed into the include directories).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/programs/lwdnsq/Makefile b/programs/lwdnsq/Makefile
new file mode 100644
index 000000000..2fca5e249
--- /dev/null
+++ b/programs/lwdnsq/Makefile
@@ -0,0 +1,96 @@
+# Makefile for the KLIPS interface utilities
+# Copyright (C) 1998, 1999 Henry Spencer.
+# Copyright (C) 1999, 2000, 2001 Richard Guy Briggs
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program 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 General Public License
+# for more details.
+#
+# RCSID $Id: Makefile,v 1.1 2004/03/15 20:35:28 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM:=lwdnsq
+
+OBJS:=cmds.o lookup.o
+
+LWRESINCL=${LWRESDIR}/include
+
+LIBS:=${FREESWANLIB} ${LWRESLIB} ${BIND9STATICLIBDIR}/libdns.a ${BIND9STATICLIBDIR}/libisc.a
+CFLAGS+=-I${LWRESINCL}
+#USERCOMPILE=-g
+
+
+include ../Makefile.program
+
+lwdnsq.8: lwdnsq.xml
+ xmlto man lwdnsq.xml
+
+lwdnsq.xml: lwdnsq.xml.in
+
+TAGS:
+ etags *.[ch] ../../lib/liblwres/*.[ch] ../../lib/liblwres/include/lwres/*.h
+
+# manually maintained dependancies
+lwdnsq.o: lwdnsq.c lwdnsq.h
+cmds.o: cmds.c lwdnsq.h
+lookup.o: lookup.c lwdnsq.h
+
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:28 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.9 2003/09/03 01:13:24 mcr
+# first attempt at async capable lwdnsq.
+#
+# Revision 1.8 2003/02/27 09:29:02 mcr
+# moved targets to after include file so that XML-conversion
+# does not occur by default.
+#
+# Revision 1.7 2003/02/01 01:36:53 mcr
+# updates to lwdnsq man page to reflect CONTRACT
+#
+# Revision 1.6 2003/01/14 03:01:14 dhr
+#
+# improve diagnostics; tidy
+#
+# Revision 1.5 2003/01/10 23:20:40 dhr
+#
+# remove reference to /sandel
+#
+# Revision 1.4 2002/12/19 05:45:47 mcr
+# use BIND9STATICLIBDIR to find -lisc/-ldns.
+#
+# Revision 1.3 2002/12/12 06:03:41 mcr
+# added --regress option to force times to be regular
+#
+# Revision 1.2 2002/12/04 03:21:06 mcr
+# DNS zone files (with signed versions) for DNSSEC enabled testing root.
+#
+# Revision 1.1 2002/10/30 02:25:31 mcr
+# renamed version of files from dnskey/
+#
+# Revision 1.4 2002/10/18 04:08:02 mcr
+# added -ldns and -lisc to libraries, but it isn't clear
+# where we will find these only-slightly standard libraries yet.
+#
+# Revision 1.3 2002/10/09 20:13:10 mcr
+# get appropriate LWRES include directory.
+#
+# Revision 1.2 2002/09/30 18:55:54 mcr
+# skeleton for dnskey helper program.
+#
+# Revision 1.1 2002/09/30 16:50:23 mcr
+# documentation for "dnskey" helper
+#
+#
+#
diff --git a/programs/lwdnsq/cmds.c b/programs/lwdnsq/cmds.c
new file mode 100644
index 000000000..1b15202ff
--- /dev/null
+++ b/programs/lwdnsq/cmds.c
@@ -0,0 +1,351 @@
+/*
+ * DNS KEY lookup helper - command implementation
+ * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <freeswan.h>
+
+#include <errno.h>
+#include <arpa/nameser.h>
+#include <lwres/netdb.h>
+#include <time.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/types.h>
+#include <isc/result.h>
+#include <isc/mem.h>
+#include <isc/buffer.h>
+#include <isc/region.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <lwres/netdb.h>
+#include <lwres/async.h>
+
+
+#include "lwdnsq.h"
+
+static void cmd_not_implemented(dnskey_glob *gs, const char *what)
+{
+ fprintf(gs->cmdproto_out, "0 FATAL unimplemented command \"%s\"\n", what);
+}
+
+void output_transaction_line(dnskey_glob *gs,
+ char *id,
+ int ttl,
+ char *cmd,
+ char *data)
+{
+ time_t t;
+
+ t=time(NULL);
+
+ /* regularlize time for regression testing */
+ if(gs->regress) {
+ t=3145915;
+ }
+
+ if(data) {
+ fprintf(gs->cmdproto_out,
+ "%s %ld %d %s %s\n",
+ id, t, ttl, cmd, data);
+ } else {
+ fprintf(gs->cmdproto_out,
+ "%s %ld %d %s\n",
+ id, t, ttl, cmd);
+ }
+
+}
+
+void output_transaction_line_limited(dnskey_glob *gs,
+ char *id,
+ int ttl,
+ char *cmd,
+ int max,
+ char *data)
+{
+ time_t t;
+
+ t=time(NULL);
+
+ /* regularlize time for regression testing */
+ if(gs->regress) {
+ t=3145915;
+ }
+
+ fprintf(gs->cmdproto_out,
+ "%s %ld %d %s %.*s\n",
+ id, t, ttl, cmd, max, data);
+}
+
+
+#if 0
+again:
+
+ lwres_getrrsetbyname_xmit(ctx, &las);
+ timeout.tv_sec = lwres_async_timeout(ctx);
+ sock = lwres_async_fd(ctx);
+
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+ ret2 = select(sock + 1, &readfds, NULL, NULL, &timeout);
+
+ /*
+ * What happened with select?
+ */
+ if (ret2 < 0) {
+ success = LWRES_R_IOERROR;
+ goto out3;
+ }
+ if (ret2 == 0) {
+ success = LWRES_R_TIMEOUT;
+ goto out3;
+ }
+
+ out:
+ if (ctx != NULL)
+ lwres_context_destroy(&ctx);
+
+ out2:
+
+#endif
+
+
+void lookup_key(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ char *id;
+ char *fqdn;
+ char simplebuf[80];
+
+ /* process arguments */
+ /* KEY 31459 east.uml.freeswan.org */
+ if(argc!=3) {
+ snprintf(simplebuf, sizeof(simplebuf), "wrong number of arguments %d", argc);
+ output_transaction_line(gs, "0", 0, "FATAL", simplebuf);
+ return;
+ }
+
+ id=argv[1];
+ fqdn=argv[2];
+
+ lookup_thing(gs, dns_rdatatype_key, "KEY", id, fqdn);
+}
+
+void lookup_key4(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "key4");
+}
+
+void lookup_key6(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "key6");
+}
+
+
+void lookup_txt(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ char *id;
+ char *fqdn;
+ char simplebuf[80];
+
+ /* process arguments */
+ /* KEY 31459 east.uml.freeswan.org */
+ if(argc != 3) {
+ snprintf(simplebuf, sizeof(simplebuf), "wrong number of arguments to TXT: %d", argc);
+ output_transaction_line(gs, "0", 0, "FATAL", simplebuf);
+ return;
+ }
+
+ id=argv[1];
+ fqdn=argv[2];
+
+ lookup_thing(gs, dns_rdatatype_txt, "TXT", id, fqdn);
+}
+
+void lookup_txt4(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ char *id;
+ char *ipv4;
+ struct in_addr in4;
+ char simplebuf[80];
+
+ /* process arguments */
+ /* KEY 31459 east.uml.freeswan.org */
+ if(argc != 3) {
+ snprintf(simplebuf, sizeof(simplebuf), "wrong number of arguments to TXT: %d", argc);
+ output_transaction_line(gs, "0", 0, "FATAL", simplebuf);
+ return;
+ }
+
+ id=argv[1];
+ ipv4=argv[2];
+
+ if(inet_pton(AF_INET, ipv4, &in4) <= 0) {
+ snprintf(simplebuf, sizeof(simplebuf), "invalid IPv4 address: %s", ipv4);
+ output_transaction_line(gs, "0", 0, "FATAL", simplebuf);
+ return;
+ }
+
+ snprintf(simplebuf, 80, "%d.%d.%d.%d.in-addr.arpa",
+ in4.s_addr & 0xff,
+ (in4.s_addr & 0xff00) >> 8,
+ (in4.s_addr & 0xff0000) >> 16,
+ (in4.s_addr & 0xff000000) >> 24);
+
+ lookup_thing(gs, dns_rdatatype_txt, "TXT4", id, simplebuf);
+}
+
+void lookup_txt6(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "txt6");
+}
+
+void lookup_ipseckey(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "ipseckey");
+}
+
+void lookup_ipseckey4(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "ipseckey4");
+}
+
+void lookup_ipseckey6(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "ipseckey6");
+}
+
+void lookup_oe4(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "oe4");
+}
+
+void lookup_oe6(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "oe6");
+}
+
+void lookup_a(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "a");
+}
+
+void lookup_aaaa(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "aaaa");
+}
+
+
+
+
+
+
+/*
+ * $Log: cmds.c,v $
+ * Revision 1.1 2004/03/15 20:35:28 as
+ * added files from freeswan-2.04-x509-1.5.3
+ *
+ * Revision 1.11 2003/09/03 01:13:24 mcr
+ * first attempt at async capable lwdnsq.
+ *
+ * Revision 1.10 2003/05/22 16:33:51 mcr
+ * added trailing . to CNAME return and cleaned up "CNAMEFROM" output.
+ *
+ * Revision 1.9 2003/05/14 15:47:39 mcr
+ * processing of IP address into pieces was not done with
+ * the right order of operations.
+ *
+ * Revision 1.8 2003/02/27 09:27:17 mcr
+ * adjusted lwdnsq so that it adheres to contract - TXT records
+ * are returned in a single piece. Requires custom decoding.
+ * implemented "txt4" lookup type.
+ *
+ * Revision 1.7 2003/01/14 07:53:29 dhr
+ *
+ * - attempt to diagnose lack of lwdnsq
+ * - increase too-small buffer size
+ *
+ * Revision 1.6 2003/01/14 03:01:14 dhr
+ *
+ * improve diagnostics; tidy
+ *
+ * Revision 1.5 2002/12/12 06:03:41 mcr
+ * added --regress option to force times to be regular
+ *
+ * Revision 1.4 2002/11/25 18:37:28 mcr
+ * added AD- marking of each record that was DNSSEC verified.
+ *
+ * Revision 1.3 2002/11/16 02:53:53 mcr
+ * lwdnsq - with new contract added.
+ *
+ * Revision 1.2 2002/11/12 04:33:44 mcr
+ * print DNSSEC status as we process CNAMEs.
+ *
+ * Revision 1.1 2002/10/30 02:25:31 mcr
+ * renamed version of files from dnskey/
+ *
+ * Revision 1.4 2002/10/18 23:11:02 mcr
+ * if we get ENOENT, then see if we can get a CNAME. If so, then
+ * follow it.
+ * Be careful when following them to avoid recursion.
+ *
+ * Revision 1.3 2002/10/18 04:08:47 mcr
+ * use -ldns routines to decode lwres results and format them nicely.
+ *
+ * Revision 1.2 2002/10/09 20:13:34 mcr
+ * first set of real code - lookup KEY records in forward.
+ *
+ * Revision 1.1 2002/09/30 18:55:54 mcr
+ * skeleton for dnskey helper program.
+ *
+ * Revision 1.1 2002/09/30 16:50:23 mcr
+ * documentation for "dnskey" helper
+ *
+ * Local variables:
+ * c-file-style: "linux"
+ * c-basic-offset: 2
+ * End:
+ *
+ */
diff --git a/programs/lwdnsq/lookup.c b/programs/lwdnsq/lookup.c
new file mode 100644
index 000000000..700c4adbe
--- /dev/null
+++ b/programs/lwdnsq/lookup.c
@@ -0,0 +1,632 @@
+/*
+ * DNS KEY lookup helper
+ * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+char lookup_c_version[] = "@(#) RCSID $Id: lookup.c,v 1.1 2004/03/15 20:35:28 as Exp $";
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <freeswan.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <setjmp.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <isc/mem.h>
+#include <isc/buffer.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <dns/name.h>
+#include <lwres/netdb.h>
+#include <lwres/async.h>
+#include "lwdnsq.h"
+
+static int lwresd_has_spoken = 0;
+
+char *xstrdup(const char *s)
+{
+ char *n;
+
+ n = strdup(s);
+ if(n == NULL) {
+ abort();
+ }
+ return n;
+}
+
+void free_dl(dnskey_glob *gs, dnskey_lookup *dl)
+{
+ dnskey_lookup **walk;
+
+ walk = &gs->dns_outstanding;
+ while(*walk!=NULL && *walk != dl)
+ {
+ walk = &((*walk)->next);
+ }
+ if(*walk != NULL)
+ {
+ /* if we exit with it non-null, then we
+ * found a matching location, remove
+ * it.
+ */
+ *walk = dl->next;
+ dl->next = NULL;
+ }
+ gs->dns_inflight--;
+
+ if(dl->tracking_id) {
+ free(dl->tracking_id);
+ dl->tracking_id = NULL;
+ }
+ if(dl->wantedtype_name) {
+ free(dl->wantedtype_name);
+ dl->wantedtype_name = NULL;
+ }
+ if(dl->fqdn) {
+ free(dl->fqdn);
+ dl->fqdn = NULL;
+ }
+#if 0
+ if(dl->last_cname_used) {
+ dns_name_free(&dl->last_cname, gs->iscmem);
+ }
+#endif
+
+ free(dl);
+}
+
+void lookup_thing(dnskey_glob *gs,
+ dns_rdatatype_t wantedtype,
+ char *wantedtype_name,
+ char *id,
+ char *fqdn)
+{
+ isc_mem_t *iscmem;
+ isc_buffer_t *iscbuf;
+ int success;
+ dnskey_lookup *dl;
+
+ iscmem=NULL;
+ iscbuf=NULL;
+ dl = malloc(sizeof(*dl));
+ memset(dl, 0, sizeof(*dl));
+
+ dl->tracking_id = strdup(id);
+ dl->step = dkl_start;
+
+ output_transaction_line(gs, id, 0, "START", NULL);
+
+ success = lwres_getrrsetbyname_init(fqdn, dns_rdataclass_in,
+ wantedtype, 0 /*flags*/,
+ gs->lwctx, &dl->las);
+
+ if(success != ERRSET_SUCCESS) {
+ /* screwed: */
+ output_transaction_line(gs, id, 0, "FATAL", "isc buffer error");
+ return;
+ }
+
+ lwres_getrrsetbyname_xmit(gs->lwctx, &dl->las);
+
+ dl->step = dkl_first;
+ dl->wantedtype = wantedtype;
+ dl->wantedtype_name = xstrdup(wantedtype_name);
+ dl->fqdn = xstrdup(fqdn);
+ dl->tracking_id = xstrdup(id);
+
+ /* link it in */
+ dl->next = gs->dns_outstanding;
+ gs->dns_outstanding = dl;
+
+ gs->dns_inflight++;
+
+ return;
+}
+
+
+int setup_follow_possible_cname(dnskey_glob *gs,
+ dnskey_lookup *dl)
+{
+ int ret;
+
+ dl->cname_count++;
+
+ /*
+ * If we are on an odd cycle (starting with 1),
+ * then convert to dns_name_t so that we can compare later.
+ *
+ * This detects loops in the CNAME processing, while still
+ * allowing an arbitrary number of CNAMEs to be followed.
+ */
+ if(dl->cname_count & 1)
+ {
+ isc_buffer_t fqdn_src;
+ isc_buffer_t *fqdn_dst;
+
+ if(dl->cname_count == 1)
+ {
+ memset(&dl->last_cname, 0, sizeof(dl->last_cname));
+ dns_name_init(&dl->last_cname, NULL);
+ }
+ else
+ {
+ dns_name_reset(&dl->last_cname);
+ }
+
+ fqdn_dst=NULL;
+
+ isc_buffer_init(&fqdn_src, dl->fqdn, strlen(dl->fqdn));
+ isc_buffer_add(&fqdn_src, strlen(dl->fqdn));
+
+ isc_buffer_allocate(gs->iscmem, &fqdn_dst, strlen(dl->fqdn)+1);
+
+#if 0
+ if(dl->last_cname_used) {
+ dns_name_free(&dl->last_cname, gs->iscmem);
+ }
+#endif
+ dl->last_cname_used = 1;
+ if(dns_name_fromtext(&dl->last_cname,
+ &fqdn_src,
+ NULL,
+ 1,
+ fqdn_dst) != ISC_R_SUCCESS) {
+ return 0;
+ }
+
+ /* something else here ? */
+ }
+
+ ret = lwres_getrrsetbyname_init(dl->fqdn, dns_rdataclass_in,
+ dns_rdatatype_cname, 0 /*flags*/,
+ gs->lwctx,
+ &dl->las);
+
+ if(ret != ERRSET_SUCCESS) {
+ return 0;
+ }
+
+ lwres_getrrsetbyname_xmit(gs->lwctx, &dl->las);
+
+ return 1;
+}
+
+
+/*
+ * we asked for, and got a CNAME of some kind.
+ */
+void process_step_cname(dnskey_glob *gs,
+ dnskey_lookup *dl,
+ struct rrsetinfo *ans,
+ int success)
+{
+ struct rdatainfo *ri;
+ isc_region_t region;
+ dns_rdata_t rd;
+ dns_rdata_cname_t cn;
+ char simplebuf[80];
+ isc_buffer_t *cname_text;
+ char cname_buf[DNS_NAME_MAXTEXT];
+ /* char cname_buf2[DNS_NAME_MAXTEXT]; */
+
+ switch(success) {
+ case ERRSET_NONAME:
+ case ERRSET_NODATA:
+ /* no, no CNAME found, thing isn't there */
+ snprintf(simplebuf, sizeof(simplebuf),
+ "RR of type %s for %s was not found (tried CNAMEs)",
+ dl->wantedtype_name,
+ dl->fqdn);
+ output_transaction_line(gs, dl->tracking_id, 0, "RETRY",
+ simplebuf);
+ dl->step = dkl_done;
+ return;
+
+ case 0:
+ /* aha! found a CNAME */
+ break;
+
+ default:
+ fatal:
+ /* some other error */
+ snprintf(simplebuf, sizeof(simplebuf), "err=%d", success);
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", simplebuf);
+ dl->step = dkl_done;
+ return;
+ }
+
+ /*
+ * now process out the CNAMEs, and look them up, one by one...
+ * there should be only one... We just use the first one that works.
+ */
+
+ if(ans->rri_flags & RRSET_VALIDATED) {
+ output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "OKAY");
+ } else {
+ output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "not present");
+ }
+
+ if(ans->rri_nrdatas != 1) {
+ /* we got a number of CNAMEs different from 1! */
+ success=0;
+ snprintf(simplebuf, sizeof(simplebuf), "illegal number of CNAMES: %d", ans->rri_nrdatas);
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", simplebuf);
+ dl->step = dkl_done;
+ return;
+ }
+
+ /* process first CNAME record */
+ ri= &ans->rri_rdatas[0];
+
+ memset(&region, 0, sizeof(region));
+ memset(&rd, 0, sizeof(rd));
+
+ region.base = ri->rdi_data;
+ region.length = ri->rdi_length;
+
+ dns_rdata_fromregion(&rd, dns_rdataclass_in,
+ dns_rdatatype_cname, &region);
+
+ /* we set mctx to NULL, which means that the tenure for
+ * the stuff pointed to by cn will persist only as long
+ * as rd persists.
+ */
+ if(dns_rdata_tostruct(&rd, &cn, NULL) != ISC_R_SUCCESS) {
+ /* failed, try next return error */
+ success=0;
+ goto fatal;
+ }
+
+ cname_text=NULL;
+ if(isc_buffer_allocate(gs->iscmem, &cname_text, DNS_NAME_MAXTEXT)) {
+ success=0;
+ goto fatal;
+ }
+
+ if(dns_name_totext(&cn.cname, ISC_TRUE, cname_text) !=
+ ISC_R_SUCCESS) {
+ success=0;
+ goto fatal;
+ }
+
+ cname_buf[0]='\0';
+ strncat(cname_buf,
+ isc_buffer_base(cname_text),
+ isc_buffer_usedlength(cname_text));
+
+ /* free up buffer */
+ isc_buffer_free(&cname_text);
+
+ {
+ /* add a trailing . */
+ char *end;
+ end = &cname_buf[strlen(cname_buf)];
+ if(*end != '.') {
+ strncat(cname_buf, ".", sizeof(cname_buf));
+ }
+ }
+
+ /* format out a text version */
+ output_transaction_line(gs, dl->tracking_id, 0, "CNAME", cname_buf);
+ output_transaction_line(gs, dl->tracking_id, 0, "CNAMEFROM", dl->fqdn);
+
+ /* check for loops in the CNAMEs! */
+ if(dns_name_equal(&dl->last_cname, &cn.cname) == ISC_TRUE) {
+ /* damn, we found a loop! */
+ dl->step = dkl_done;
+ return;
+ }
+
+ /* send new request. */
+ /* okay, so look this new thing up */
+ success = lwres_getrrsetbyname_init(cname_buf, dns_rdataclass_in,
+ dl->wantedtype, 0 /*flags*/,
+ gs->lwctx, &dl->las);
+
+ if(success != ERRSET_SUCCESS) {
+ return;
+ }
+
+ lwres_getrrsetbyname_xmit(gs->lwctx, &dl->las);
+
+ dl->step = dkl_second;
+}
+
+void process_step_first(dnskey_glob *gs,
+ dnskey_lookup *dl,
+ struct rrsetinfo *ans,
+ int success,
+ int attempt) /* attempt = 0 first time, 1 after cname */
+{
+ char simplebuf[132], typebuf[16];
+ char txtbuf[1024];
+ int i;
+
+ switch(success) {
+ case ERRSET_NODATA:
+ if(attempt == 0) {
+ lwresd_has_spoken = 1;
+ setup_follow_possible_cname(gs, dl);
+ dl->step = dkl_cname;
+ return;
+ }
+ /* FALLTHROUGH */
+ case ERRSET_NONAME:
+ lwresd_has_spoken = 1;
+ snprintf(simplebuf, sizeof(simplebuf),
+ "RR of type %s for %s was not found",
+ dl->wantedtype_name,
+ dl->fqdn);
+ output_transaction_line(gs, dl->tracking_id, 0, "RETRY",
+ simplebuf);
+ dl->step = dkl_done;
+ goto done;
+
+ case ERRSET_NOMEMORY:
+ snprintf(simplebuf, sizeof(simplebuf),
+ "ran out of memory while looking up RR of type %s for %s",
+ dl->wantedtype_name, dl->fqdn);
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", simplebuf);
+ dl->step = dkl_done;
+ goto done;
+
+ case ERRSET_FAIL:
+ snprintf(simplebuf, sizeof(simplebuf),
+ "unspecified failure while looking up RR of type %s for %s%s",
+ dl->wantedtype_name, dl->fqdn,
+ lwresd_has_spoken ? "" : " (is lwresd running?)");
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", simplebuf);
+ dl->step = dkl_done;
+ goto done;
+
+ case ERRSET_INVAL:
+ snprintf(simplebuf, sizeof(simplebuf),
+ "invalid input while looking up RR of type %s for %s",
+ dl->wantedtype_name, dl->fqdn);
+ output_transaction_line(gs, dl->tracking_id, 0, "RETRY", simplebuf);
+ dl->step = dkl_done;
+ goto done;
+
+ default:
+ snprintf(simplebuf, sizeof(simplebuf), " unknown error %d", success);
+ output_transaction_line(gs, dl->tracking_id, 0, "RETRY", simplebuf);
+ dl->step = dkl_done;
+ done:
+ return;
+
+ case 0:
+ /* everything okay */
+ lwresd_has_spoken = 1;
+ dl->step = dkl_done;
+ break;
+ }
+
+ /* output the rest of the data */
+
+ if(ans->rri_flags & RRSET_VALIDATED) {
+ output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "OKAY");
+ snprintf(typebuf, sizeof(typebuf), "AD-%s", dl->wantedtype_name);
+ if(dl->wantedtype_name) free(dl->wantedtype_name);
+ dl->wantedtype_name=xstrdup(typebuf);
+ } else {
+ output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "not present");
+ }
+
+ output_transaction_line(gs, dl->tracking_id, 0, "NAME", ans->rri_name);
+
+ for(i=0; i<ans->rri_nrdatas; i++) {
+ struct rdatainfo *ri = &ans->rri_rdatas[i];
+ isc_region_t region;
+ dns_rdata_t rd;
+
+ isc_buffer_clear(gs->iscbuf);
+ memset(&region, 0, sizeof(region));
+ memset(&rd, 0, sizeof(rd));
+
+ region.base = ri->rdi_data;
+ region.length = ri->rdi_length;
+
+ if(dl->wantedtype == dns_rdatatype_txt) {
+ /* special treatment for TXT records */
+ unsigned int len, rdatalen, totlen;
+ unsigned char *txtp, *rdata;
+
+ txtp = txtbuf;
+ totlen = 0;
+ rdatalen = ri->rdi_length;
+ rdata = ri->rdi_data;
+
+ while(rdatalen > 0) {
+ len= (unsigned)rdata[0];
+ memcpy(txtp, rdata+1, len);
+ totlen += len;
+ txtp += len;
+ rdata += len+1;
+ rdatalen -= len+1;
+ }
+ *txtp = '\0';
+
+ output_transaction_line_limited(gs, dl->tracking_id, 0,
+ dl->wantedtype_name,
+ totlen, txtbuf);
+
+ } else {
+ dns_rdata_fromregion(&rd, dns_rdataclass_in,
+ dl->wantedtype, &region);
+
+ if(dns_rdata_totext(&rd, NULL, gs->iscbuf) != ISC_R_SUCCESS) {
+
+ }
+
+ output_transaction_line_limited(gs, dl->tracking_id, 0,
+ dl->wantedtype_name,
+ (int)isc_buffer_usedlength(gs->iscbuf),
+ (char *)isc_buffer_base(gs->iscbuf));
+ }
+ }
+
+ for(i=0; i<ans->rri_nsigs; i++) {
+ struct rdatainfo *ri = &ans->rri_sigs[i];
+ isc_region_t region;
+ dns_rdata_t rd;
+
+ isc_buffer_clear(gs->iscbuf);
+ memset(&region, 0, sizeof(region));
+ memset(&rd, 0, sizeof(rd));
+
+ region.base = ri->rdi_data;
+ region.length = ri->rdi_length;
+
+ dns_rdata_fromregion(&rd, dns_rdataclass_in,
+ dns_rdatatype_sig, &region);
+ if(dns_rdata_totext(&rd, NULL, gs->iscbuf) != ISC_R_SUCCESS) {
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", "isc totext error");
+ return;
+ }
+
+ output_transaction_line_limited(gs, dl->tracking_id, 0, "SIG",
+ (int)isc_buffer_usedlength(gs->iscbuf),
+ (char *)isc_buffer_base(gs->iscbuf));
+ }
+}
+
+
+
+void lookup_step(dnskey_glob *gs,
+ dnskey_lookup *dl,
+ struct rrsetinfo *ans,
+ int success)
+{
+ /* char simplebuf[80]; */
+ int nextstate;
+
+ nextstate = dkl_done;
+
+ if(dl == NULL)
+ {
+ return;
+ }
+
+ switch(dl->step)
+ {
+ case dkl_start:
+ /* first request done, why are still in this state? */
+ break;
+
+ case dkl_first:
+ /* okay, got the reply from the first step! */
+ process_step_first(gs, dl, ans, success, 0);
+ nextstate = dl->step;
+ break;
+
+ case dkl_cname:
+ /*
+ * we asked for a cname, and we have some result to deal
+ * with here.
+ */
+ process_step_cname(gs, dl, ans, success);
+ nextstate = dl->step;
+ break;
+
+ case dkl_second:
+ /*
+ * we had asked for something, for a cname, and we followed
+ * it, and we'll see what we got back.
+ */
+ process_step_first(gs, dl, ans, success, 1);
+ nextstate = dl->step;
+ break;
+
+ case dkl_done:
+ /* this should not happen, really, just book keeping, so,
+ * just free up the structure, and return.
+ */
+ nextstate = dl->step;
+ return;
+ }
+
+
+ /* we have been through, made a state transition, if we are
+ * done, then do that.
+ */
+ if(nextstate == dkl_done)
+ {
+ output_transaction_line(gs, dl->tracking_id, 0, "DONE", NULL);
+ free_dl(gs, dl);
+ dl=NULL;
+ }
+ return;
+}
+
+void process_dns_reply(dnskey_glob *gs)
+{
+ dnskey_lookup *dl;
+ struct lwres_async_state *plas;
+ struct rrsetinfo *res;
+ int success;
+
+ plas = NULL;
+
+ success = lwres_getrrsetbyname_read(&plas, gs->lwctx, &res);
+
+ /* cast answer back to dnskey_lookup structure */
+ dl = (dnskey_lookup *)plas;
+
+ if(success == LWRES_R_RETRY) {
+ /* XXX we got something from some other weird place!
+ * transmit again, in the hope of getting the right answer
+ */
+ dl->retry_count--;
+ if(dl->retry_count > 0) {
+ lwres_getrrsetbyname_xmit(gs->lwctx, plas);
+ } else {
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", "too many retries");
+ free_dl(gs, dl);
+ }
+ return;
+ }
+
+ /* perform next step for this one */
+ lookup_step(gs, dl, res, success);
+}
+
+/*
+ * $Log: lookup.c,v $
+ * Revision 1.1 2004/03/15 20:35:28 as
+ * added files from freeswan-2.04-x509-1.5.3
+ *
+ * Revision 1.3 2003/09/18 02:17:39 mcr
+ * if we have tried a CNAME lookup, then take a NODATA
+ * reply as a no-name.
+ *
+ * Revision 1.2 2003/09/10 17:55:14 mcr
+ * the CNAME message had the s removed, which changes test
+ * results gratuitously.
+ *
+ * Revision 1.1 2003/09/03 01:13:24 mcr
+ * first attempt at async capable lwdnsq.
+ *
+ *
+ * Local variables:
+ * c-file-style: "linux"
+ * c-basic-offset: 2
+ * End:
+ *
+ */
diff --git a/programs/lwdnsq/lwdnsq.8 b/programs/lwdnsq/lwdnsq.8
new file mode 100644
index 000000000..bb07985f2
--- /dev/null
+++ b/programs/lwdnsq/lwdnsq.8
@@ -0,0 +1,250 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "IPSEC LWDNSQ" 8 "" "" ""
+.SH NAME
+lwdnsq \- lookup items in DNS to help pluto (and others)
+.SH "SYNOPSIS"
+
+.nf
+\fBipsec lwdnsq\fR lwdnsq\fR [\fB\-\-prompt\fR] [\fB\-\-serial\fR]
+.fi
+
+.nf
+\fBipsec lwdnsq\fR lwdnsq\fR [\fB\-\-help\fR]
+.fi
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBipsec lwdnsq\fR is a helper program that does DNS lookups for other programs. It implements an asynchronous interface on stdin/stdout, with an ASCII driven command language.
+
+.PP
+If stdin is a tty or if the \fB\-\-prompt\fR option is given, then it issues a prompt to the user. Otherwise, it is silent, except for results.
+
+.PP
+The program will accept multiple queries concurrently, with each result being marked with the ID provided on the output. The IDs are strings.
+
+.PP
+If the \fB\-\-serial\fR option is given, then the program will not attempt to execute concurrent queries, but will serialize all input and output.
+
+.SH "QUERY LANGUAGE"
+
+.PP
+There are eleven command that the program understands. This is to lookup different types of records in both the forward and reverse maps. Every query includes a queryid, which is returned in the output, on every single line to identify the transaction.
+
+.SS "KEY queryid FQDN"
+
+.PP
+This request looks up the KEY resource record for the given \fBFQDN.\fR.
+
+.SS "KEY4 queryid A.B.C.D"
+
+.PP
+This request looks up the KEY resource record found in the reverse map for the IP version 4 address \fBA.B.C.D\fR, i.e. it looks up D.C.B.A.in\-addr.arpa.
+
+.SS "KEY6 queryid A:B::C:D"
+
+.PP
+This request looks up the KEY resource record found in the reverse map for the IPv6 address \fBA:B::C:D\fR, i.e. it looks the 32\-nibble long entry in ip6.arpa (and ip6.int).
+
+.SS "TXT4 queryid A.B.C.D"
+
+.PP
+This request looks up the TXT resource record found in the reverse map for the IP version 4 address \fBA.B.C.D\fR, i.e. it looks up D.C.B.A.in\-addr.arpa.
+
+.SS "TXT6 queryid A:B::C:D"
+
+.PP
+This request looks up the TXT resource record found in the reverse map for the IPv6 address \fBA:B::C:D\fR, i.e. it looks the 32\-nibble long entry in ip6.arpa (and ip6.int).
+
+.SS "KEY queryid FQDN"
+
+.PP
+This request looks up the IPSECKEY resource record for the given \fBFQDN.\fR. See note about IPSECKEY processing, below.
+
+.SS "IPSECKEY4 queryid A.B.C.D"
+
+.PP
+This request looks up the IPSECKEY resource record found in the reverse map for the IP version 4 address \fBA.B.C.D\fR, i.e. it looks up D.C.B.A.in\-addr.arpa. See special note about IPSECKEY processing, below.
+
+.SS "IPSECKEY6 queryid A:B::C:D"
+
+.PP
+This request looks up the IPSECKEY resource record found in the reverse map for the IPv6 address \fBA:B::C:D\fR, i.e. it looks the 32\-nibble long entry in ip6.arpa (and ip6.int). See special note about IPSECKEY processing, below.
+
+.SS "OE4 queryid A.B.C.D"
+
+.PP
+This request looks an appropriate record for Opportunistic Encryption for the given IP address. This attempts to look for the delegation record. This may be one of IPSECKEY, KEY, or TXT record. Unless configured otherwise, (see OE4 Directives, below), then a query type of ANY will be used to retrieve all relevant records, and all will be returned.
+
+.SS "OE6 queryid A:B::C:D"
+
+.PP
+This request looks an appropriate record for Opportunistic Encryption for the given IPv6 address. This attempts to look for the delegation record. This may be one of IPSECKEY, KEY, or TXT record. Unless configured otherwise, (see OE Directives, below), then a query type of ALL will be used to retrieve all relevant records, and all will be returned. i.e. it looks the 32\-nibble long entry in ip6.arpa (and ip6.int).
+
+.SS "A queryid FQDN"
+
+.PP
+This request looks up the A (IPv4) resource record for the given \fBFQDN.\fR.
+
+.SS "AAAA queryid FQDN"
+
+.PP
+This request looks up the AAAA (IPv6) resource record for the given \fBFQDN.\fR.
+
+.SH "REPLIES TO QUERIES"
+
+.PP
+All replies from the queries are in the following format:
+
+.nf
+
+<ID> <TIME> <TTL> <TYPE> <TYPE\-SPECIFIC> \\n
+
+.fi
+
+
+.TP
+\fIID\fR
+this is the \fBqueryid\fR value that was provided in the query. It is repeated on every line to permit the replies to be properly associated with the query. When the response is not ascribable to particular query (such as for a mis\-formed query), then the query ID "0" will be used.
+
+.TP
+\fITIME\fR
+this is the current time in seconds since epoch.
+
+.TP
+\fITTL\fR
+for answers which have a time to live, this is the current value. The answer is valid for this number of seconds. If there is no useful value here, then the number 0 is used.
+
+.TP
+\fITYPE\fR
+This is the type of the record that is being returned. The types are described in the next section. The TYPE specific data that follows is specific to the type.
+
+
+.PP
+The replies are limited to 4096 bytes, a value defined as \fBLWDNSQ_RESULT_LEN_MAX\fR. This is defined in \fIfreeswan.h\fR.
+
+.PP
+All of the replies which include resource records use the standard presentation format (with no line feeds or carriage returns) in their answer.
+
+.SS "START"
+
+.PP
+This reply indicates that a query has been received and has been started. It serves as an anchor point for timing, as well as an acknowledgement.
+
+.SS "DONE"
+
+.PP
+This reply indicates that a query is entirely over, and no further information from this query will be sent.
+
+.SS "RETRY"
+
+.PP
+This reply indicates that a query is entirely over, but that no data was found. The records may exist, but appropriate servers could not be reached.
+
+.SS "FATAL"
+
+.PP
+This reply indicates that a query is entirely over, and that no data of the type requested could be found. There were no timeouts, and all servers were available and confirmed non\-existances. There may be NXT records returned prior to this.
+
+.SS "CNAME"
+
+.PP
+This is an interim reply, and indicates that a CNAME was found (and followed) while performing the query. The value of the CNAME is present in the type specific section.
+
+.SS "CNAMEFROM"
+
+.PP
+This is an interim reply, and indicates that a CNAME was found. The original name that was queries for was not the canonical name, and this reply indicates the name that was actually followed.
+
+.SS "NAME"
+
+.PP
+This is an interim reply. The original name that was queries for was not the canonical name. This reply indicates the canonical name.
+
+.SS "DNSSEC"
+
+.PP
+This is an interim reply. It is followed either by "OKAY" or "not present. It indicates if DNSSEC was available on the reply.
+
+.SS "TXT and AD-TXT"
+
+.PP
+This is an interim reply. If there are TXT resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SS "A and AD-A"
+
+.PP
+This is an interim reply. If there are A resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SS "AAAA and AD-AAAA"
+
+.PP
+This is an interim reply. If there are AAAA resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SS "PTR and AD-PTR"
+
+.PP
+This is an interim reply. If there are PTR resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SS "KEY and AD-KEY"
+
+.PP
+This is an interim reply. If there are KEY resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SS "IPSECKEY and AD-IPSECKEY"
+
+.PP
+This is an interim reply. If there are IPSEC resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SH "SPECIAL IPSECKEY PROCESSING"
+
+.PP
+At the time of this writing, the IPSECKEY resource record is not entirely specified. In particular no resource record number has been assigned. This program assumes that it is resource record number 45. If the file /etc/ipsec.d/lwdnsq.conf exists, and contains a line like
+
+.nf
+
+ipseckey_rr=\fBnumber\fR
+
+.fi
+ then this number will be used instead. The file is read only once at startup.
+
+.SH "OE DIRECTIVES"
+
+.PP
+If the file /etc/ipsec.d/lwdnsq.conf exists, and contains a line like
+
+.nf
+
+queryany=false
+
+.fi
+ then instead of doing an ALL query when looking for OE delegation records, lwdnsq will do a series of queries. It will first look for IPSECKEY, and then TXT record. If it finds neither, it will then look for KEY records of all kinds, although they do not contain delegation information.
+
+.SH "SPECIAL IPSECKEY PROCESSING"
+
+.nf
+
+/etc/ipsec.d/lwdnsq.conf
+
+.fi
+
+.SH AUTHOR
+Michael Richardson <mcr@sandelman.ottawa.on.ca>.
diff --git a/programs/lwdnsq/lwdnsq.c b/programs/lwdnsq/lwdnsq.c
new file mode 100644
index 000000000..2684a7d45
--- /dev/null
+++ b/programs/lwdnsq/lwdnsq.c
@@ -0,0 +1,506 @@
+/*
+ * DNS KEY lookup helper
+ * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+char tncfg_c_version[] = "RCSID $Id: lwdnsq.c,v 1.1 2004/03/15 20:35:28 as Exp $";
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <freeswan.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <setjmp.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/types.h>
+#include <isc/result.h>
+#include <isc/mem.h>
+#include <isc/buffer.h>
+#include <isc/region.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <lwres/netdb.h>
+#include <lwres/async.h>
+
+#include "lwdnsq.h"
+
+static void
+usage(char *name)
+{
+ fprintf(stdout,"%s --attach --virtual <virtual-device> --physical <physical-device>\n",
+ name);
+ exit(1);
+}
+
+static struct option const longopts[] =
+{
+ {"prompt", 0, 0, 'i'},
+ {"serial", 0, 0, 's'},
+ {"debug", 0, 0, 'g'},
+ {"regress",0, 0, 'X'},
+ {"ignoreeof",0, 0, 'Z'},
+ {0, 0, 0, 0}
+};
+
+/* globals */
+jmp_buf getMeOut;
+
+void sig_handler(int sig)
+{
+ fprintf(stderr, "Caught signal %d, cleaning up and exiting\n", sig);
+ longjmp(getMeOut, 1);
+}
+
+void cmdprompt(dnskey_glob *gs)
+{
+ if(gs->prompt) {
+ printf("lwdnsq> ");
+ }
+ fflush(gs->cmdproto_out);
+}
+
+void quitprog(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ gs->done=1;
+}
+
+void setdebug(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ if(argc > 1) {
+ gs->debug=strtoul(argv[1],NULL,0);
+ }
+ printf("0 DEBUG is %d\n",gs->debug);
+}
+
+
+int cmdparse(dnskey_glob *gs,
+ char *cmdline)
+{
+ char *argv[256];
+ int argc;
+ char *arg;
+ static const struct cmd_entry {
+ const char *cmdname;
+ void (*cmdfunc)(dnskey_glob *, int, char **);
+ } cmds[]={
+ {"key", lookup_key},
+ {"key4", lookup_key4},
+ {"key6", lookup_key6},
+ {"txt", lookup_txt},
+ {"txt4", lookup_txt4},
+ {"txt6", lookup_txt6},
+ {"ipseckey", lookup_ipseckey},
+ {"ipseckey4", lookup_ipseckey4},
+ {"ipseckey6", lookup_ipseckey6},
+ {"oe4", lookup_oe4},
+ {"oe6", lookup_oe6},
+ {"vpn4", lookup_key4},
+ {"vpn6", lookup_key6},
+ {"quit", quitprog},
+ {"a", lookup_a},
+ {"aaaa", lookup_aaaa},
+ {"debug", setdebug},
+ {NULL, NULL}};
+ const struct cmd_entry *ce = cmds;
+
+ argc=0;
+
+ /* skip initial spaces */
+ while(cmdline && isspace(*cmdline)) {
+ cmdline++;
+ }
+
+ while(cmdline && *cmdline!='\0' &&
+ (arg=strsep(&cmdline, " \t\n"))!=NULL) {
+ if (argc < sizeof(argv)/sizeof(*argv - 1)) {
+ /* ignore arguments that would overflow.
+ * XXX should generate a diagnostic.
+ */
+ argv[argc++]=arg;
+ }
+ while(cmdline && isspace(*cmdline)) {
+ cmdline++;
+ }
+ }
+ argv[argc]=NULL;
+
+ if(argc==0 || argv[0][0]=='\0') {
+ /* ignore empty line */
+ } else if(strcasecmp("help", argv[0]) == 0) {
+ fprintf(gs->cmdproto_out, "0 HELP\n");
+ for (; ce->cmdname != NULL; ce++)
+ fprintf(gs->cmdproto_out, "0 HELP %s\n", ce->cmdname);
+ } else {
+ for (;; ce++) {
+ if (ce->cmdname == NULL) {
+ fprintf(gs->cmdproto_out, "0 FATAL unknown command \"%s\"\n", argv[0]);
+ break;
+ }
+ if(strcasecmp(ce->cmdname, argv[0])==0) {
+ (*ce->cmdfunc)(gs, argc, argv);
+ break;
+ }
+ }
+ }
+
+ if (!gs->done)
+ cmdprompt(gs);
+ return 0;
+}
+
+int cmdread(dnskey_glob *gs,
+ char *buf,
+ int len)
+{
+ unsigned char *nl;
+ int cmdlen;
+
+ cmdlen=0;
+
+ /*
+ * have to handle partial reads and multiple commands
+ * per read, since this may in fact be a file or a pipe.
+ */
+ if((gs->cmdloc + len + 1) > sizeof(gs->cmdbuf)) {
+ fprintf(stderr, "command '%.*s...' is too long, discarding!\n",
+ 40, buf);
+ fflush(stdout);
+
+ gs->cmdloc=0;
+ return 0;
+ }
+ memcpy(gs->cmdbuf+gs->cmdloc, buf, len);
+ gs->cmdloc+=len;
+ gs->cmdbuf[gs->cmdloc]='\0';
+
+ while((nl = strchr(gs->cmdbuf, '\n')) != NULL) {
+ /* found a newline, so turn it into a \0, and process the
+ * command, and then we will pull the rest of the buffer
+ * up.
+ */
+ *nl='\0';
+ cmdlen= nl - gs->cmdbuf +1;
+
+ cmdparse(gs, gs->cmdbuf);
+
+ gs->cmdloc -= cmdlen;
+ memmove(gs->cmdbuf, gs->cmdbuf+cmdlen, gs->cmdloc);
+ }
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *program_name;
+ dnskey_glob gs;
+ int c;
+ static int ignoreeof=0; /* static to avoid longjmp clobber */
+ int ineof;
+
+ memset(&gs, 0, sizeof(dnskey_glob));
+
+#if 0
+ printf("PID: %d\n", getpid());
+ sleep(60);
+#endif
+
+ program_name = argv[0];
+ gs.concurrent = 1;
+
+ if(lwres_async_init(&gs.lwctx) != ERRSET_SUCCESS) {
+ fprintf(stderr, "Can not initialize async context\n");
+ exit(3);
+ }
+
+ if(isc_mem_create(0,0,&gs.iscmem) != ISC_R_SUCCESS) {
+ fprintf(stderr, "Can not initialize isc memory allocator\n");
+ exit(4);
+ }
+
+ if(isc_buffer_allocate(gs.iscmem, &gs.iscbuf, LWDNSQ_RESULT_LEN_MAX)) {
+ fprintf(stderr, "Can not allocate a result buffer\n");
+ exit(5);
+ }
+
+ while((c = getopt_long_only(argc, argv, "dgsiXZ", longopts, 0)) != EOF) {
+ switch(c) {
+ case 'd':
+ gs.debug+=2;
+ break;
+
+ case 'g':
+ gs.debug++;
+ break;
+ case 's':
+ gs.concurrent=0;
+ break;
+ case 'i':
+ gs.prompt=1;
+ break;
+ case 'X':
+ gs.regress++;
+ break;
+
+ case 'Z':
+ ignoreeof=1;
+ break;
+
+ default:
+ usage(program_name);
+ break;
+ }
+ }
+
+ if(gs.debug && ignoreeof) {
+ fprintf(stderr, "Ignoring end of file\n");
+ }
+
+ if(isatty(0)) {
+ gs.prompt=1;
+ }
+
+ /* do various bits of setup */
+ if(setjmp(getMeOut)!=0) {
+ signal(SIGINT, SIG_DFL);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* cleanup_crap(); */
+
+ exit(1);
+ }
+
+ if(signal(SIGINT, sig_handler) < 0)
+ perror("Setting handler for SIGINT");
+
+ if(signal(SIGPIPE, sig_handler) < 0)
+ perror("Setting handler for SIGINT");
+
+ cmdprompt(&gs);
+
+ ineof = 0;
+ gs.done = 0;
+ gs.cmdproto_out = stdout;
+ gs.l_fds[0].events = POLLIN|POLLHUP;
+ gs.l_fds[0].fd=0;
+
+ gs.l_fds[1].events = POLLIN|POLLHUP|POLLERR;
+ gs.l_fds[1].fd = lwres_async_fd(gs.lwctx);
+
+ gs.l_nfds= 2;
+
+ while(!gs.done)
+ {
+ int timeout;
+ char buf[128];
+ int n;
+ int rlen;
+
+ timeout=-1;
+
+ gs.l_fds[0].revents = 0;
+
+ gs.l_fds[1].events = POLLIN|POLLHUP|POLLERR;
+ gs.l_fds[1].revents = 0;
+ gs.l_fds[1].fd = lwres_async_fd(gs.lwctx);
+
+ if(gs.debug > 1) {
+ fprintf(stderr, "=== invoking poll(,%d,) with %s\n",
+ gs.l_nfds,
+ timeout>0 ? "waittime" : "no wait");
+ for(n = 0; n < gs.l_nfds; n++) {
+ fprintf(stderr, "=== waiting on fd#%d\n",
+ gs.l_fds[n].fd);
+ }
+ fprintf(stderr, "=== inflight: %d\n", gs.dns_inflight);
+ }
+
+ n = poll(gs.l_fds, gs.l_nfds, timeout);
+
+ if(n == 0) {
+ /* timeout! */
+ }
+
+ if(n < 0) {
+ perror("poll");
+ }
+
+ if(gs.debug > 1) {
+ fprintf(stderr, "=== poll returned with %d\n", n);
+ }
+
+ while(n>0) {
+ if((gs.l_fds[0].revents & POLLERR) == POLLERR ||
+ (gs.l_fds[1].revents & POLLERR) == POLLERR)
+ {
+ break;
+ }
+
+ /* see if there are DNS events coming back */
+ if((gs.l_fds[1].revents & POLLIN) == POLLIN) {
+ if(gs.debug > 1) {
+ fprintf(stderr,
+ "=== new responses from lwdnsd\n");
+ }
+
+ process_dns_reply(&gs);
+ fflush(stdout);
+ n--;
+ }
+
+ if(!ignoreeof &&
+ (gs.l_fds[0].revents & POLLHUP) == POLLHUP)
+ {
+ break;
+ }
+
+ if((gs.l_fds[0].revents & POLLIN) == POLLIN) {
+
+ rlen=read(0, buf, sizeof(buf));
+
+ if(gs.debug > 1) {
+ if(rlen > 0) {
+ buf[rlen]='\0';
+ }
+ fprintf(stderr,
+ "=== new commands on fd 0: %d: %s\n",
+ rlen, buf);
+ }
+
+ if(rlen > 0) {
+ cmdread(&gs, buf, rlen);
+ } else if(rlen == 0) {
+ ineof = 1;
+ if(!ignoreeof) {
+ /* EOF, die */
+ gs.done=1;
+ }
+ }
+ n--;
+ }
+
+ }
+
+ if((gs.l_fds[0].revents & POLLHUP) == POLLHUP)
+ {
+ ineof = 1;
+ if(!ignoreeof)
+ {
+ gs.done=1;
+ }
+ }
+
+ if(ignoreeof) {
+ /* if we have exhausted the input,
+ * and there are none in flight,
+ * then exit, finally.
+ */
+ if(ineof) {
+ if(gs.dns_inflight == 0) {
+ gs.done=1;
+ }
+ }
+ }
+
+ if(gs.debug) {
+ fprintf(stderr, "=== ineof: %d inflight: %d\n",
+ ineof, gs.dns_inflight);
+ }
+
+ }
+
+ signal(SIGINT, SIG_DFL);
+ signal(SIGPIPE, SIG_IGN);
+
+ exit(0);
+}
+
+/*
+ * $Log: lwdnsq.c,v $
+ * Revision 1.1 2004/03/15 20:35:28 as
+ * added files from freeswan-2.04-x509-1.5.3
+ *
+ * Revision 1.12 2003/09/16 05:01:14 mcr
+ * prefix all debugging with === so that it can be easily removed.
+ *
+ * Revision 1.11 2003/09/10 04:43:52 mcr
+ * final fixes to lwdnsq to exit only when all requests are done,
+ * and we have been told to wait, *OR* if there is an EOF in stdin.
+ *
+ * Revision 1.10 2003/09/03 01:13:24 mcr
+ * first attempt at async capable lwdnsq.
+ *
+ * Revision 1.9 2003/04/02 07:37:57 dhr
+ *
+ * lwdnsq: fix non-deterministic bug in handling batched input
+ *
+ * Revision 1.8 2003/02/08 04:03:06 mcr
+ * renamed --single to --serial.
+ *
+ * Revision 1.7 2003/01/14 03:01:14 dhr
+ *
+ * improve diagnostics; tidy
+ *
+ * Revision 1.6 2002/12/19 07:29:47 dhr
+ *
+ * - avoid (improbable) buffer overflow
+ * - suppress prompt after "quit" command
+ * - add space to prompt to match aesthetics and man page
+ * - elminate a magic number
+ *
+ * Revision 1.5 2002/12/19 07:08:42 dhr
+ *
+ * continue renaming dnskey => lwdnsq
+ *
+ * Revision 1.4 2002/12/12 06:03:41 mcr
+ * added --regress option to force times to be regular
+ *
+ * Revision 1.3 2002/11/25 18:37:48 mcr
+ * make sure that we exit cleanly upon EOF.
+ *
+ * Revision 1.2 2002/11/16 02:53:53 mcr
+ * lwdnsq - with new contract added.
+ *
+ * Revision 1.1 2002/10/30 02:25:31 mcr
+ * renamed version of files from dnskey/
+ *
+ * Revision 1.3 2002/10/09 20:14:16 mcr
+ * make sure to flush stdout at the right time - do it regardless
+ * of whether or not we are printing prompts.
+ *
+ * Revision 1.2 2002/09/30 18:55:54 mcr
+ * skeleton for dnskey helper program.
+ *
+ * Revision 1.1 2002/09/30 16:50:23 mcr
+ * documentation for "dnskey" helper
+ *
+ * Local variables:
+ * c-file-style: "linux"
+ * c-basic-offset: 2
+ * End:
+ *
+ */
diff --git a/programs/lwdnsq/lwdnsq.h b/programs/lwdnsq/lwdnsq.h
new file mode 100644
index 000000000..109b39507
--- /dev/null
+++ b/programs/lwdnsq/lwdnsq.h
@@ -0,0 +1,121 @@
+/*
+ * DNS KEY lookup global definitions
+ * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#ifndef POLLIN
+#include <poll.h>
+#endif
+
+#include "freeswan.h"
+
+/*
+ * a base-64 encoded 2192 bit key takes:
+ * 2192/8 * 4/3 = 365 bytes.
+ *
+ * a base-64 encoded 16384 bit key takes:
+ * 16384/8*4/3 = 2730 bytes.
+ *
+ * so, we pick 4096 bytes as the maximum.
+ *
+ * Note that TXT records may have an introducer (X-IPsec) and an ID which
+ * is either an IP address or @FQDN that preceeds the base64 encoded key.
+ *
+ */
+
+enum dkl_state {
+ dkl_start, /* no work yet none - initial state */
+ dkl_first, /* sent first DNS request. */
+ dkl_cname, /* sent request for CNAME record */
+ dkl_second, /* sent request for thing CNAME pointed to */
+ dkl_done /* done */
+};
+
+typedef struct dnskey_lookup dnskey_lookup;
+
+struct dnskey_lookup {
+ struct lwres_async_state las;
+ dnskey_lookup *next;
+ char *tracking_id;
+ enum dkl_state step;
+ /* lwres_context_t *ctx; */
+ char *wantedtype_name;
+ dns_rdatatype_t wantedtype;
+ char *fqdn;
+ int cname_count;
+ int last_cname_used;
+ dns_name_t last_cname;
+ int retry_count;
+};
+
+typedef struct dnskey_glob {
+ int debug;
+ int prompt;
+ int concurrent;
+ int done;
+ int regress; /* if 1, then we are doing regression testing */
+ struct pollfd l_fds[5]; /* array of input sources */
+ int l_nfds; /* number of relevant entries */
+ int cmdloc;
+ unsigned char cmdbuf[LWDNSQ_CMDBUF_LEN];
+ FILE *cmdproto_out;
+ dnskey_lookup *dns_outstanding;
+ int dns_inflight;
+ lwres_context_t *lwctx;
+ isc_mem_t *iscmem;
+ isc_buffer_t *iscbuf;
+} dnskey_glob;
+
+/* in cmds.c */
+extern void lookup_key(dnskey_glob *gs,int, char **);
+extern void lookup_key4(dnskey_glob *gs,int, char **);
+extern void lookup_key6(dnskey_glob *gs,int, char **);
+extern void lookup_txt(dnskey_glob *gs,int, char **);
+extern void lookup_txt4(dnskey_glob *gs,int, char **);
+extern void lookup_txt6(dnskey_glob *gs,int, char **);
+extern void lookup_ipseckey(dnskey_glob *gs,int, char **);
+extern void lookup_ipseckey4(dnskey_glob *gs,int, char **);
+extern void lookup_ipseckey6(dnskey_glob *gs,int, char **);
+extern void lookup_oe4(dnskey_glob *gs,int, char **);
+extern void lookup_oe6(dnskey_glob *gs,int, char **);
+extern void lookup_a(dnskey_glob *gs,int, char **);
+extern void lookup_aaaa(dnskey_glob *gs,int, char **);
+extern void output_transaction_line(dnskey_glob *gs,
+ char *id,
+ int ttl,
+ char *cmd,
+ char *data);
+extern void output_transaction_line_limited(dnskey_glob *gs,
+ char *id,
+ int ttl,
+ char *cmd,
+ int max,
+ char *data);
+
+
+/* lookup code */
+extern void process_dns_reply(dnskey_glob *gs);
+extern void lookup_thing(dnskey_glob *gs,
+ dns_rdatatype_t wantedtype,
+ char *wantedtype_name,
+ char *id,
+ char *fqdn);
+
+/*
+ *
+ * Local variables:
+ * c-file-style: "linux"
+ * c-basic-offset: 2
+ * End:
+ *
+ */
diff --git a/programs/lwdnsq/lwdnsq.xml.in b/programs/lwdnsq/lwdnsq.xml.in
new file mode 100644
index 000000000..4c4039120
--- /dev/null
+++ b/programs/lwdnsq/lwdnsq.xml.in
@@ -0,0 +1,446 @@
+<?xml version='1.0'?> <!-- -*- docbook -*- -->
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<article>
+ <articleinfo>
+ <title>lwdnsq</title>
+
+ <author>
+ <firstname>Michael</firstname>
+ <surname>Richardson</surname>
+ <affiliation>
+ <address><email>mcr@sandelman.ottawa.on.ca</email></address>
+ </affiliation>
+ </author>
+
+ <copyright>
+ <year>2003</year>
+ <holder>Michael Richardson</holder>
+ </copyright>
+ </articleinfo>
+
+ <section>
+ <title>Reference</title>
+
+<refentry id="ipsec_lwdnsq">
+
+<refmeta>
+<refentrytitle>ipsec lwdnsq</refentrytitle>
+<manvolnum>8</manvolnum>
+</refmeta>
+
+<refnamediv>
+<refname>lwdnsq</refname>
+<refpurpose>lookup items in DNS to help pluto (and others)</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+
+<cmdsynopsis>
+ <command>ipsec lwdnsq</command>
+ <arg choice="opt"><option>--prompt</option></arg>
+ <arg choice="opt"><option>--serial</option></arg>
+</cmdsynopsis>
+
+<cmdsynopsis>
+ <command>ipsec lwdnsq</command>
+ <arg choice="opt"><option>--help</option></arg>
+</cmdsynopsis>
+
+</refsynopsisdiv>
+
+<refsect1><title>Description</title>
+<para>
+The
+<command>ipsec lwdnsq</command>
+is a helper program that does DNS lookups for other programs. It implements
+an asynchronous interface on stdin/stdout, with an ASCII driven command
+language.
+</para>
+
+<para>
+If stdin is a tty or if the
+<option>--prompt</option>
+option is given, then it issues a prompt to the user. Otherwise, it is
+silent, except for results.
+</para>
+
+<para>
+The program will accept multiple queries concurrently, with each result
+being marked with the ID provided on the output. The IDs are strings.
+</para>
+
+<para>
+If the
+<option>--serial</option>
+option is given, then the program will not attempt to execute concurrent
+queries, but will serialize all input and output.
+</para>
+
+</refsect1>
+
+<refsect1><title>QUERY LANGUAGE</title>
+
+<para>
+There are eleven command that the program understands. This is to lookup
+different types of records in both the forward and reverse maps. Every query
+includes a queryid, which is returned in the output, on every single line to
+identify the transaction.
+</para>
+
+<refsect2><title>KEY <option>queryid</option> <option>FQDN</option></title>
+<para>
+This request looks up the KEY resource record for the given <option>FQDN.</option>.
+</para>
+</refsect2>
+
+<refsect2>
+<title>KEY4 <option>queryid</option> <option>A.B.C.D</option></title>
+<para>
+This request looks up the KEY resource record found in the reverse map for
+the IP version 4 address <option>A.B.C.D</option>, i.e. it looks
+up D.C.B.A.in-addr.arpa.
+</para>
+</refsect2>
+
+<refsect2>
+<title>KEY6 <option>queryid</option> <option>A:B::C:D</option></title>
+<para>
+This request looks up the KEY resource record found in the reverse map
+for the IPv6 address <option>A:B::C:D</option>, i.e.
+it looks the 32-nibble long entry in ip6.arpa (and ip6.int).
+</para>
+</refsect2>
+
+<refsect2>
+<title>TXT4 <option>queryid</option> <option>A.B.C.D</option></title>
+<para>
+This request looks up the TXT resource record found in the reverse map for
+the IP version 4 address <option>A.B.C.D</option>, i.e. it looks
+up D.C.B.A.in-addr.arpa.
+</para>
+</refsect2>
+
+<refsect2>
+<title>TXT6 <option>queryid</option> <option>A:B::C:D</option></title>
+<para>
+This request looks up the TXT resource record found in the reverse map
+for the IPv6 address <option>A:B::C:D</option>, i.e.
+it looks the 32-nibble long entry in ip6.arpa (and ip6.int).
+</para>
+</refsect2>
+
+<refsect2>
+<title>KEY <option>queryid</option> <option>FQDN</option></title>
+<para>
+This request looks up the IPSECKEY resource record for the given
+<option>FQDN.</option>. See note about IPSECKEY processing, below.
+</para>
+</refsect2>
+
+<refsect2>
+<title>IPSECKEY4 <option>queryid</option> <option>A.B.C.D</option></title>
+<para>
+This request looks up the IPSECKEY resource record found in the reverse map for
+the IP version 4 address <option>A.B.C.D</option>, i.e. it looks
+up D.C.B.A.in-addr.arpa. See special note about IPSECKEY processing, below.
+</para>
+</refsect2>
+
+<refsect2>
+<title>IPSECKEY6 <option>queryid</option> <option>A:B::C:D</option></title>
+<para>
+This request looks up the IPSECKEY resource record found in the reverse map
+for the IPv6 address <option>A:B::C:D</option>, i.e.
+it looks the 32-nibble long entry in ip6.arpa (and ip6.int). See
+special note about IPSECKEY processing, below.
+</para>
+</refsect2>
+
+<refsect2>
+<title>OE4 <option>queryid</option> <option>A.B.C.D</option></title>
+<para>
+This request looks an appropriate record for Opportunistic
+Encryption for the given IP address. This attempts to look for the
+delegation record. This may be one of IPSECKEY, KEY, or TXT
+record. Unless configured otherwise, (see OE4 Directives, below), then
+a query type of ANY will be used to retrieve all relevant records, and
+all will be returned.
+</para>
+</refsect2>
+
+<refsect2>
+<title>OE6 <option>queryid</option> <option>A:B::C:D</option></title>
+<para>
+This request looks an appropriate record for Opportunistic
+Encryption for the given IPv6 address. This attempts to look for the
+delegation record. This may be one of IPSECKEY, KEY, or TXT
+record. Unless configured otherwise, (see OE Directives, below), then
+a query type of ALL will be used to retrieve all relevant records, and
+all will be returned.
+i.e. it looks the 32-nibble long entry in ip6.arpa (and ip6.int).
+</para>
+</refsect2>
+
+<refsect2>
+<title>A <option>queryid</option> <option>FQDN</option></title>
+<para>
+This request looks up the A (IPv4) resource record for the given
+<option>FQDN.</option>.
+</para>
+</refsect2>
+
+<refsect2>
+<title>AAAA <option>queryid</option> <option>FQDN</option></title>
+<para>
+This request looks up the AAAA (IPv6) resource record for the given
+<option>FQDN.</option>.
+</para>
+</refsect2>
+
+</refsect1>
+
+<refsect1><title>Replies to queries</title>
+
+<para>
+All replies from the queries are in the following format:
+<programlisting>
+&lt;ID&gt; &lt;TIME&gt; &lt;TTL&gt; &lt;TYPE&gt; &lt;TYPE-SPECIFIC&gt; \n
+</programlisting>
+
+<variablelist>
+
+<varlistentry><term><parameter>ID</parameter></term>
+<listitem>
+<para>
+this is the <option>queryid</option> value that was provided in
+the query. It is repeated on every line to permit the replies to be
+properly associated with the query. When the response is not ascribable to
+particular query (such as for a mis-formed query), then the query ID "0" will
+be used.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><parameter>TIME</parameter></term>
+<listitem>
+<para>
+this is the current time in seconds since epoch.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><parameter>TTL</parameter></term>
+<listitem>
+<para>
+for answers which have a time to live, this is the current value. The
+answer is valid for this number of seconds. If there is no useful
+value here, then the number 0 is used.
+</para>
+</listitem>
+</varlistentry>
+
+
+<varlistentry><term><parameter>TYPE</parameter></term>
+<listitem>
+<para>
+This is the type of the record that is being returned. The types are
+described in the next section. The TYPE specific data that follows is
+specific to the type.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</para>
+
+<para>
+The replies are limited to 4096 bytes, a value defined as
+<constant>LWDNSQ_RESULT_LEN_MAX</constant>. This is defined in
+<filename>freeswan.h</filename>.
+</para>
+
+<para>All of the replies which include resource records use the
+standard presentation format (with no line feeds or carriage returns)
+in their answer.</para>
+
+<refsect2>
+<title>START</title>
+<para>
+This reply indicates that a query has been received and has been
+started. It serves as an anchor point for timing, as well as an acknowledgement.
+</para>
+</refsect2>
+
+<refsect2>
+<title>DONE</title>
+<para>
+This reply indicates that a query is entirely over, and no further
+information from this query will be sent.
+</para>
+</refsect2>
+
+<refsect2>
+<title>RETRY</title>
+<para>
+This reply indicates that a query is entirely over, but that no
+data was found. The records may exist, but appropriate servers could
+not be reached.
+</para>
+</refsect2>
+
+<refsect2>
+<title>FATAL</title>
+<para>
+This reply indicates that a query is entirely over, and that no
+data of the type requested could be found. There were no timeouts, and
+all servers were available and confirmed non-existances. There may be
+NXT records returned prior to this.
+</para>
+</refsect2>
+
+<refsect2>
+<title>CNAME</title>
+<para>
+This is an interim reply, and indicates that a CNAME was found (and
+followed) while performing the query. The value of the CNAME is
+present in the type specific section.
+</para>
+</refsect2>
+
+<refsect2>
+<title>CNAMEFROM</title>
+<para>
+This is an interim reply, and indicates that a CNAME was found. The
+original name that was queries for was not the canonical name, and
+this reply indicates the name that was actually followed.
+</para>
+</refsect2>
+
+<refsect2>
+<title>NAME</title>
+<para>
+This is an interim reply. The original name that was queries for was
+not the canonical name. This reply indicates the canonical name.
+</para>
+</refsect2>
+
+<refsect2>
+<title>DNSSEC</title>
+<para>
+This is an interim reply. It is followed either by "OKAY" or "not
+present.
+It indicates if DNSSEC was available on the reply.
+</para>
+</refsect2>
+
+<refsect2>
+<title>TXT and AD-TXT</title>
+<para>
+This is an interim reply. If there are TXT resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+<refsect2>
+<title>A and AD-A</title>
+<para>
+This is an interim reply. If there are A resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+<refsect2>
+<title>AAAA and AD-AAAA</title>
+<para>
+This is an interim reply. If there are AAAA resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+<refsect2>
+<title>PTR and AD-PTR</title>
+<para>
+This is an interim reply. If there are PTR resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+<refsect2>
+<title>KEY and AD-KEY</title>
+<para>
+This is an interim reply. If there are KEY resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+
+<refsect2>
+<title>IPSECKEY and AD-IPSECKEY</title>
+<para>
+This is an interim reply. If there are IPSEC resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+
+</refsect1>
+
+<refsect1><title>Special IPSECKEY processing</title>
+
+<para>
+At the time of this writing, the IPSECKEY resource record is not
+entirely specified. In particular no resource record number has been
+assigned. This program assumes that it is resource record number
+45. If the file
+@IPSEC_CONFDDIR@/lwdnsq.conf
+exists, and contains a line like
+<programlisting>
+ipseckey_rr=<option>number</option>
+</programlisting>
+then this number will be used instead. The file is read only once at
+startup.
+</para>
+</refsect1>
+
+<refsect1><title>OE Directives</title>
+
+<para>
+If the file
+@IPSEC_CONFDDIR@/lwdnsq.conf
+exists, and contains a line like
+<programlisting>
+queryany=false
+</programlisting>
+then instead of doing an ALL query when looking for OE delegation
+records, lwdnsq will do a series of queries. It will first look for
+IPSECKEY, and then TXT record. If it finds neither, it will then look
+for KEY records of all kinds, although they do not contain delegation
+information.
+</para>
+</refsect1>
+
+<refsect1><title>Special IPSECKEY processing</title>
+
+<programlisting>
+/etc/ipsec.d/lwdnsq.conf
+</programlisting>
+
+</refsect1>
+
+</refentry>
+</section>
+</article>
+
+
+
+
+
+
diff --git a/programs/lwdnsq/states.fig b/programs/lwdnsq/states.fig
new file mode 100644
index 000000000..6a28249ee
--- /dev/null
+++ b/programs/lwdnsq/states.fig
@@ -0,0 +1,66 @@
+#FIG 3.2
+Landscape
+Center
+Metric
+A4
+100.00
+Single
+-2
+1200 2
+6 1305 1530 3330 2205
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 3330 2205 3330 1530 1305 1530 1305 2205 3330 2205
+4 1 0 50 0 0 20 0.0000 4 255 1575 2385 1935 initial request\001
+-6
+6 1350 5850 3375 6525
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 3375 6525 3375 5850 1350 5850 1350 6525 3375 6525
+4 1 0 50 0 0 20 0.0000 4 135 825 2430 6255 success\001
+-6
+6 4275 2700 6525 3375
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 6525 3375 6525 2700 4275 2700 4275 3375 6525 3375
+4 1 0 50 0 0 20 0.0000 4 195 2115 5400 3150 ASK for CNAME\001
+-6
+6 225 3825 2250 4500
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 2250 4500 2250 3825 225 3825 225 4500 2250 4500
+4 1 0 50 0 0 20 0.0000 4 195 750 1305 4230 failure\001
+-6
+6 5625 4545 7875 5220
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 7875 5220 7875 4545 5625 4545 5625 5220 7875 5220
+4 1 0 50 0 0 20 0.0000 4 255 1740 6750 4995 ASK for target\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 3330 1935 4275 2745
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 2250 2205 1305 3825
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 4275 3330 2250 4050
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 2880 2250 2880 5850
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 5895 5220 3375 6120
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 5625 4950 2250 4275
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 6030 3375 6570 4545
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 7695 4545 6525 3150
+4 0 0 50 0 0 14 0.0000 4 195 1830 3825 2295 ERRSET_NODATA\001
+4 2 0 50 0 0 14 0.0000 4 195 1875 1575 3150 ERRSET_NONAME\001
+4 0 0 50 0 0 14 0.0000 4 150 300 2970 5175 OK\001
+4 0 0 50 0 0 14 0.0000 4 150 300 4500 5895 OK\001
+4 0 0 50 0 0 14 0.0000 4 195 1875 3420 3825 ERRSET_NONAME\001
+4 0 0 50 0 0 14 0.0000 4 195 1875 3420 4500 ERRSET_NONAME\001
+4 0 0 50 0 0 14 0.0000 4 150 300 6390 3960 OK\001
+4 0 0 50 0 0 14 0.0000 4 195 1830 7110 3825 ERRSET_NODATA\001
diff --git a/programs/lwdnsq/states.png b/programs/lwdnsq/states.png
new file mode 100644
index 000000000..ceb5b3c45
--- /dev/null
+++ b/programs/lwdnsq/states.png
Binary files differ