2 * DNS KEY lookup helper
3 * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 char lookup_c_version
[] = "@(#) RCSID $Id: lookup.c,v 1.1 2004/03/15 20:35:28 as Exp $";
33 #include <isc/buffer.h>
34 #include <dns/rdata.h>
35 #include <dns/rdatastruct.h>
37 #include <lwres/netdb.h>
38 #include <lwres/async.h>
41 static int lwresd_has_spoken
= 0;
43 char *xstrdup(const char *s
)
54 void free_dl(dnskey_glob
*gs
, dnskey_lookup
*dl
)
58 walk
= &gs
->dns_outstanding
;
59 while(*walk
!=NULL
&& *walk
!= dl
)
61 walk
= &((*walk
)->next
);
65 /* if we exit with it non-null, then we
66 * found a matching location, remove
75 free(dl
->tracking_id
);
76 dl
->tracking_id
= NULL
;
78 if(dl
->wantedtype_name
) {
79 free(dl
->wantedtype_name
);
80 dl
->wantedtype_name
= NULL
;
87 if(dl
->last_cname_used
) {
88 dns_name_free(&dl
->last_cname
, gs
->iscmem
);
95 void lookup_thing(dnskey_glob
*gs
,
96 dns_rdatatype_t wantedtype
,
97 char *wantedtype_name
,
102 isc_buffer_t
*iscbuf
;
108 dl
= malloc(sizeof(*dl
));
109 memset(dl
, 0, sizeof(*dl
));
111 dl
->tracking_id
= strdup(id
);
112 dl
->step
= dkl_start
;
114 output_transaction_line(gs
, id
, 0, "START", NULL
);
116 success
= lwres_getrrsetbyname_init(fqdn
, dns_rdataclass_in
,
117 wantedtype
, 0 /*flags*/,
118 gs
->lwctx
, &dl
->las
);
120 if(success
!= ERRSET_SUCCESS
) {
122 output_transaction_line(gs
, id
, 0, "FATAL", "isc buffer error");
126 lwres_getrrsetbyname_xmit(gs
->lwctx
, &dl
->las
);
128 dl
->step
= dkl_first
;
129 dl
->wantedtype
= wantedtype
;
130 dl
->wantedtype_name
= xstrdup(wantedtype_name
);
131 dl
->fqdn
= xstrdup(fqdn
);
132 dl
->tracking_id
= xstrdup(id
);
135 dl
->next
= gs
->dns_outstanding
;
136 gs
->dns_outstanding
= dl
;
144 int setup_follow_possible_cname(dnskey_glob
*gs
,
152 * If we are on an odd cycle (starting with 1),
153 * then convert to dns_name_t so that we can compare later.
155 * This detects loops in the CNAME processing, while still
156 * allowing an arbitrary number of CNAMEs to be followed.
158 if(dl
->cname_count
& 1)
160 isc_buffer_t fqdn_src
;
161 isc_buffer_t
*fqdn_dst
;
163 if(dl
->cname_count
== 1)
165 memset(&dl
->last_cname
, 0, sizeof(dl
->last_cname
));
166 dns_name_init(&dl
->last_cname
, NULL
);
170 dns_name_reset(&dl
->last_cname
);
175 isc_buffer_init(&fqdn_src
, dl
->fqdn
, strlen(dl
->fqdn
));
176 isc_buffer_add(&fqdn_src
, strlen(dl
->fqdn
));
178 isc_buffer_allocate(gs
->iscmem
, &fqdn_dst
, strlen(dl
->fqdn
)+1);
181 if(dl
->last_cname_used
) {
182 dns_name_free(&dl
->last_cname
, gs
->iscmem
);
185 dl
->last_cname_used
= 1;
186 if(dns_name_fromtext(&dl
->last_cname
,
190 fqdn_dst
) != ISC_R_SUCCESS
) {
194 /* something else here ? */
197 ret
= lwres_getrrsetbyname_init(dl
->fqdn
, dns_rdataclass_in
,
198 dns_rdatatype_cname
, 0 /*flags*/,
202 if(ret
!= ERRSET_SUCCESS
) {
206 lwres_getrrsetbyname_xmit(gs
->lwctx
, &dl
->las
);
213 * we asked for, and got a CNAME of some kind.
215 void process_step_cname(dnskey_glob
*gs
,
217 struct rrsetinfo
*ans
,
220 struct rdatainfo
*ri
;
223 dns_rdata_cname_t cn
;
225 isc_buffer_t
*cname_text
;
226 char cname_buf
[DNS_NAME_MAXTEXT
];
227 /* char cname_buf2[DNS_NAME_MAXTEXT]; */
232 /* no, no CNAME found, thing isn't there */
233 snprintf(simplebuf
, sizeof(simplebuf
),
234 "RR of type %s for %s was not found (tried CNAMEs)",
237 output_transaction_line(gs
, dl
->tracking_id
, 0, "RETRY",
243 /* aha! found a CNAME */
248 /* some other error */
249 snprintf(simplebuf
, sizeof(simplebuf
), "err=%d", success
);
250 output_transaction_line(gs
, dl
->tracking_id
, 0, "FATAL", simplebuf
);
256 * now process out the CNAMEs, and look them up, one by one...
257 * there should be only one... We just use the first one that works.
260 if(ans
->rri_flags
& RRSET_VALIDATED
) {
261 output_transaction_line(gs
, dl
->tracking_id
, 0, "DNSSEC", "OKAY");
263 output_transaction_line(gs
, dl
->tracking_id
, 0, "DNSSEC", "not present");
266 if(ans
->rri_nrdatas
!= 1) {
267 /* we got a number of CNAMEs different from 1! */
269 snprintf(simplebuf
, sizeof(simplebuf
), "illegal number of CNAMES: %d", ans
->rri_nrdatas
);
270 output_transaction_line(gs
, dl
->tracking_id
, 0, "FATAL", simplebuf
);
275 /* process first CNAME record */
276 ri
= &ans
->rri_rdatas
[0];
278 memset(®ion
, 0, sizeof(region
));
279 memset(&rd
, 0, sizeof(rd
));
281 region
.base
= ri
->rdi_data
;
282 region
.length
= ri
->rdi_length
;
284 dns_rdata_fromregion(&rd
, dns_rdataclass_in
,
285 dns_rdatatype_cname
, ®ion
);
287 /* we set mctx to NULL, which means that the tenure for
288 * the stuff pointed to by cn will persist only as long
291 if(dns_rdata_tostruct(&rd
, &cn
, NULL
) != ISC_R_SUCCESS
) {
292 /* failed, try next return error */
298 if(isc_buffer_allocate(gs
->iscmem
, &cname_text
, DNS_NAME_MAXTEXT
)) {
303 if(dns_name_totext(&cn
.cname
, ISC_TRUE
, cname_text
) !=
311 isc_buffer_base(cname_text
),
312 isc_buffer_usedlength(cname_text
));
315 isc_buffer_free(&cname_text
);
318 /* add a trailing . */
320 end
= &cname_buf
[strlen(cname_buf
)];
322 strncat(cname_buf
, ".", sizeof(cname_buf
));
326 /* format out a text version */
327 output_transaction_line(gs
, dl
->tracking_id
, 0, "CNAME", cname_buf
);
328 output_transaction_line(gs
, dl
->tracking_id
, 0, "CNAMEFROM", dl
->fqdn
);
330 /* check for loops in the CNAMEs! */
331 if(dns_name_equal(&dl
->last_cname
, &cn
.cname
) == ISC_TRUE
) {
332 /* damn, we found a loop! */
337 /* send new request. */
338 /* okay, so look this new thing up */
339 success
= lwres_getrrsetbyname_init(cname_buf
, dns_rdataclass_in
,
340 dl
->wantedtype
, 0 /*flags*/,
341 gs
->lwctx
, &dl
->las
);
343 if(success
!= ERRSET_SUCCESS
) {
347 lwres_getrrsetbyname_xmit(gs
->lwctx
, &dl
->las
);
349 dl
->step
= dkl_second
;
352 void process_step_first(dnskey_glob
*gs
,
354 struct rrsetinfo
*ans
,
356 int attempt
) /* attempt = 0 first time, 1 after cname */
358 char simplebuf
[132], typebuf
[16];
365 lwresd_has_spoken
= 1;
366 setup_follow_possible_cname(gs
, dl
);
367 dl
->step
= dkl_cname
;
372 lwresd_has_spoken
= 1;
373 snprintf(simplebuf
, sizeof(simplebuf
),
374 "RR of type %s for %s was not found",
377 output_transaction_line(gs
, dl
->tracking_id
, 0, "RETRY",
382 case ERRSET_NOMEMORY
:
383 snprintf(simplebuf
, sizeof(simplebuf
),
384 "ran out of memory while looking up RR of type %s for %s",
385 dl
->wantedtype_name
, dl
->fqdn
);
386 output_transaction_line(gs
, dl
->tracking_id
, 0, "FATAL", simplebuf
);
391 snprintf(simplebuf
, sizeof(simplebuf
),
392 "unspecified failure while looking up RR of type %s for %s%s",
393 dl
->wantedtype_name
, dl
->fqdn
,
394 lwresd_has_spoken
? "" : " (is lwresd running?)");
395 output_transaction_line(gs
, dl
->tracking_id
, 0, "FATAL", simplebuf
);
400 snprintf(simplebuf
, sizeof(simplebuf
),
401 "invalid input while looking up RR of type %s for %s",
402 dl
->wantedtype_name
, dl
->fqdn
);
403 output_transaction_line(gs
, dl
->tracking_id
, 0, "RETRY", simplebuf
);
408 snprintf(simplebuf
, sizeof(simplebuf
), " unknown error %d", success
);
409 output_transaction_line(gs
, dl
->tracking_id
, 0, "RETRY", simplebuf
);
415 /* everything okay */
416 lwresd_has_spoken
= 1;
421 /* output the rest of the data */
423 if(ans
->rri_flags
& RRSET_VALIDATED
) {
424 output_transaction_line(gs
, dl
->tracking_id
, 0, "DNSSEC", "OKAY");
425 snprintf(typebuf
, sizeof(typebuf
), "AD-%s", dl
->wantedtype_name
);
426 if(dl
->wantedtype_name
) free(dl
->wantedtype_name
);
427 dl
->wantedtype_name
=xstrdup(typebuf
);
429 output_transaction_line(gs
, dl
->tracking_id
, 0, "DNSSEC", "not present");
432 output_transaction_line(gs
, dl
->tracking_id
, 0, "NAME", ans
->rri_name
);
434 for(i
=0; i
<ans
->rri_nrdatas
; i
++) {
435 struct rdatainfo
*ri
= &ans
->rri_rdatas
[i
];
439 isc_buffer_clear(gs
->iscbuf
);
440 memset(®ion
, 0, sizeof(region
));
441 memset(&rd
, 0, sizeof(rd
));
443 region
.base
= ri
->rdi_data
;
444 region
.length
= ri
->rdi_length
;
446 if(dl
->wantedtype
== dns_rdatatype_txt
) {
447 /* special treatment for TXT records */
448 unsigned int len
, rdatalen
, totlen
;
449 unsigned char *txtp
, *rdata
;
453 rdatalen
= ri
->rdi_length
;
454 rdata
= ri
->rdi_data
;
456 while(rdatalen
> 0) {
457 len
= (unsigned)rdata
[0];
458 memcpy(txtp
, rdata
+1, len
);
466 output_transaction_line_limited(gs
, dl
->tracking_id
, 0,
471 dns_rdata_fromregion(&rd
, dns_rdataclass_in
,
472 dl
->wantedtype
, ®ion
);
474 if(dns_rdata_totext(&rd
, NULL
, gs
->iscbuf
) != ISC_R_SUCCESS
) {
478 output_transaction_line_limited(gs
, dl
->tracking_id
, 0,
480 (int)isc_buffer_usedlength(gs
->iscbuf
),
481 (char *)isc_buffer_base(gs
->iscbuf
));
485 for(i
=0; i
<ans
->rri_nsigs
; i
++) {
486 struct rdatainfo
*ri
= &ans
->rri_sigs
[i
];
490 isc_buffer_clear(gs
->iscbuf
);
491 memset(®ion
, 0, sizeof(region
));
492 memset(&rd
, 0, sizeof(rd
));
494 region
.base
= ri
->rdi_data
;
495 region
.length
= ri
->rdi_length
;
497 dns_rdata_fromregion(&rd
, dns_rdataclass_in
,
498 dns_rdatatype_sig
, ®ion
);
499 if(dns_rdata_totext(&rd
, NULL
, gs
->iscbuf
) != ISC_R_SUCCESS
) {
500 output_transaction_line(gs
, dl
->tracking_id
, 0, "FATAL", "isc totext error");
504 output_transaction_line_limited(gs
, dl
->tracking_id
, 0, "SIG",
505 (int)isc_buffer_usedlength(gs
->iscbuf
),
506 (char *)isc_buffer_base(gs
->iscbuf
));
512 void lookup_step(dnskey_glob
*gs
,
514 struct rrsetinfo
*ans
,
517 /* char simplebuf[80]; */
520 nextstate
= dkl_done
;
530 /* first request done, why are still in this state? */
534 /* okay, got the reply from the first step! */
535 process_step_first(gs
, dl
, ans
, success
, 0);
536 nextstate
= dl
->step
;
541 * we asked for a cname, and we have some result to deal
544 process_step_cname(gs
, dl
, ans
, success
);
545 nextstate
= dl
->step
;
550 * we had asked for something, for a cname, and we followed
551 * it, and we'll see what we got back.
553 process_step_first(gs
, dl
, ans
, success
, 1);
554 nextstate
= dl
->step
;
558 /* this should not happen, really, just book keeping, so,
559 * just free up the structure, and return.
561 nextstate
= dl
->step
;
566 /* we have been through, made a state transition, if we are
567 * done, then do that.
569 if(nextstate
== dkl_done
)
571 output_transaction_line(gs
, dl
->tracking_id
, 0, "DONE", NULL
);
578 void process_dns_reply(dnskey_glob
*gs
)
581 struct lwres_async_state
*plas
;
582 struct rrsetinfo
*res
;
587 success
= lwres_getrrsetbyname_read(&plas
, gs
->lwctx
, &res
);
589 /* cast answer back to dnskey_lookup structure */
590 dl
= (dnskey_lookup
*)plas
;
592 if(success
== LWRES_R_RETRY
) {
593 /* XXX we got something from some other weird place!
594 * transmit again, in the hope of getting the right answer
597 if(dl
->retry_count
> 0) {
598 lwres_getrrsetbyname_xmit(gs
->lwctx
, plas
);
600 output_transaction_line(gs
, dl
->tracking_id
, 0, "FATAL", "too many retries");
606 /* perform next step for this one */
607 lookup_step(gs
, dl
, res
, success
);
612 * Revision 1.1 2004/03/15 20:35:28 as
613 * added files from freeswan-2.04-x509-1.5.3
615 * Revision 1.3 2003/09/18 02:17:39 mcr
616 * if we have tried a CNAME lookup, then take a NODATA
617 * reply as a no-name.
619 * Revision 1.2 2003/09/10 17:55:14 mcr
620 * the CNAME message had the s removed, which changes test
621 * results gratuitously.
623 * Revision 1.1 2003/09/03 01:13:24 mcr
624 * first attempt at async capable lwdnsq.
628 * c-file-style: "linux"