summaryrefslogtreecommitdiff
path: root/programs/lwdnsq/lookup.c
diff options
context:
space:
mode:
Diffstat (limited to 'programs/lwdnsq/lookup.c')
-rw-r--r--programs/lwdnsq/lookup.c632
1 files changed, 632 insertions, 0 deletions
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:
+ *
+ */