diff options
Diffstat (limited to 'programs/lwdnsq/lookup.c')
-rw-r--r-- | programs/lwdnsq/lookup.c | 632 |
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(®ion, 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, ®ion); + + /* 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(®ion, 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, ®ion); + + 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(®ion, 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, ®ion); + 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: + * + */ |