]> git.ipfire.org Git - people/ms/strongswan.git/blob - programs/lwdnsq/lookup.c
- import of strongswan-2.7.0
[people/ms/strongswan.git] / programs / lwdnsq / lookup.c
1 /*
2 * DNS KEY lookup helper
3 * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
4 *
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>.
9 *
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
13 * for more details.
14 */
15
16 char lookup_c_version[] = "@(#) RCSID $Id: lookup.c,v 1.1 2004/03/15 20:35:28 as Exp $";
17
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23
24 #include <freeswan.h>
25
26 #include <errno.h>
27 #include <getopt.h>
28 #include <setjmp.h>
29 #include <ctype.h>
30 #include <signal.h>
31
32 #include <isc/mem.h>
33 #include <isc/buffer.h>
34 #include <dns/rdata.h>
35 #include <dns/rdatastruct.h>
36 #include <dns/name.h>
37 #include <lwres/netdb.h>
38 #include <lwres/async.h>
39 #include "lwdnsq.h"
40
41 static int lwresd_has_spoken = 0;
42
43 char *xstrdup(const char *s)
44 {
45 char *n;
46
47 n = strdup(s);
48 if(n == NULL) {
49 abort();
50 }
51 return n;
52 }
53
54 void free_dl(dnskey_glob *gs, dnskey_lookup *dl)
55 {
56 dnskey_lookup **walk;
57
58 walk = &gs->dns_outstanding;
59 while(*walk!=NULL && *walk != dl)
60 {
61 walk = &((*walk)->next);
62 }
63 if(*walk != NULL)
64 {
65 /* if we exit with it non-null, then we
66 * found a matching location, remove
67 * it.
68 */
69 *walk = dl->next;
70 dl->next = NULL;
71 }
72 gs->dns_inflight--;
73
74 if(dl->tracking_id) {
75 free(dl->tracking_id);
76 dl->tracking_id = NULL;
77 }
78 if(dl->wantedtype_name) {
79 free(dl->wantedtype_name);
80 dl->wantedtype_name = NULL;
81 }
82 if(dl->fqdn) {
83 free(dl->fqdn);
84 dl->fqdn = NULL;
85 }
86 #if 0
87 if(dl->last_cname_used) {
88 dns_name_free(&dl->last_cname, gs->iscmem);
89 }
90 #endif
91
92 free(dl);
93 }
94
95 void lookup_thing(dnskey_glob *gs,
96 dns_rdatatype_t wantedtype,
97 char *wantedtype_name,
98 char *id,
99 char *fqdn)
100 {
101 isc_mem_t *iscmem;
102 isc_buffer_t *iscbuf;
103 int success;
104 dnskey_lookup *dl;
105
106 iscmem=NULL;
107 iscbuf=NULL;
108 dl = malloc(sizeof(*dl));
109 memset(dl, 0, sizeof(*dl));
110
111 dl->tracking_id = strdup(id);
112 dl->step = dkl_start;
113
114 output_transaction_line(gs, id, 0, "START", NULL);
115
116 success = lwres_getrrsetbyname_init(fqdn, dns_rdataclass_in,
117 wantedtype, 0 /*flags*/,
118 gs->lwctx, &dl->las);
119
120 if(success != ERRSET_SUCCESS) {
121 /* screwed: */
122 output_transaction_line(gs, id, 0, "FATAL", "isc buffer error");
123 return;
124 }
125
126 lwres_getrrsetbyname_xmit(gs->lwctx, &dl->las);
127
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);
133
134 /* link it in */
135 dl->next = gs->dns_outstanding;
136 gs->dns_outstanding = dl;
137
138 gs->dns_inflight++;
139
140 return;
141 }
142
143
144 int setup_follow_possible_cname(dnskey_glob *gs,
145 dnskey_lookup *dl)
146 {
147 int ret;
148
149 dl->cname_count++;
150
151 /*
152 * If we are on an odd cycle (starting with 1),
153 * then convert to dns_name_t so that we can compare later.
154 *
155 * This detects loops in the CNAME processing, while still
156 * allowing an arbitrary number of CNAMEs to be followed.
157 */
158 if(dl->cname_count & 1)
159 {
160 isc_buffer_t fqdn_src;
161 isc_buffer_t *fqdn_dst;
162
163 if(dl->cname_count == 1)
164 {
165 memset(&dl->last_cname, 0, sizeof(dl->last_cname));
166 dns_name_init(&dl->last_cname, NULL);
167 }
168 else
169 {
170 dns_name_reset(&dl->last_cname);
171 }
172
173 fqdn_dst=NULL;
174
175 isc_buffer_init(&fqdn_src, dl->fqdn, strlen(dl->fqdn));
176 isc_buffer_add(&fqdn_src, strlen(dl->fqdn));
177
178 isc_buffer_allocate(gs->iscmem, &fqdn_dst, strlen(dl->fqdn)+1);
179
180 #if 0
181 if(dl->last_cname_used) {
182 dns_name_free(&dl->last_cname, gs->iscmem);
183 }
184 #endif
185 dl->last_cname_used = 1;
186 if(dns_name_fromtext(&dl->last_cname,
187 &fqdn_src,
188 NULL,
189 1,
190 fqdn_dst) != ISC_R_SUCCESS) {
191 return 0;
192 }
193
194 /* something else here ? */
195 }
196
197 ret = lwres_getrrsetbyname_init(dl->fqdn, dns_rdataclass_in,
198 dns_rdatatype_cname, 0 /*flags*/,
199 gs->lwctx,
200 &dl->las);
201
202 if(ret != ERRSET_SUCCESS) {
203 return 0;
204 }
205
206 lwres_getrrsetbyname_xmit(gs->lwctx, &dl->las);
207
208 return 1;
209 }
210
211
212 /*
213 * we asked for, and got a CNAME of some kind.
214 */
215 void process_step_cname(dnskey_glob *gs,
216 dnskey_lookup *dl,
217 struct rrsetinfo *ans,
218 int success)
219 {
220 struct rdatainfo *ri;
221 isc_region_t region;
222 dns_rdata_t rd;
223 dns_rdata_cname_t cn;
224 char simplebuf[80];
225 isc_buffer_t *cname_text;
226 char cname_buf[DNS_NAME_MAXTEXT];
227 /* char cname_buf2[DNS_NAME_MAXTEXT]; */
228
229 switch(success) {
230 case ERRSET_NONAME:
231 case ERRSET_NODATA:
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)",
235 dl->wantedtype_name,
236 dl->fqdn);
237 output_transaction_line(gs, dl->tracking_id, 0, "RETRY",
238 simplebuf);
239 dl->step = dkl_done;
240 return;
241
242 case 0:
243 /* aha! found a CNAME */
244 break;
245
246 default:
247 fatal:
248 /* some other error */
249 snprintf(simplebuf, sizeof(simplebuf), "err=%d", success);
250 output_transaction_line(gs, dl->tracking_id, 0, "FATAL", simplebuf);
251 dl->step = dkl_done;
252 return;
253 }
254
255 /*
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.
258 */
259
260 if(ans->rri_flags & RRSET_VALIDATED) {
261 output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "OKAY");
262 } else {
263 output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "not present");
264 }
265
266 if(ans->rri_nrdatas != 1) {
267 /* we got a number of CNAMEs different from 1! */
268 success=0;
269 snprintf(simplebuf, sizeof(simplebuf), "illegal number of CNAMES: %d", ans->rri_nrdatas);
270 output_transaction_line(gs, dl->tracking_id, 0, "FATAL", simplebuf);
271 dl->step = dkl_done;
272 return;
273 }
274
275 /* process first CNAME record */
276 ri= &ans->rri_rdatas[0];
277
278 memset(&region, 0, sizeof(region));
279 memset(&rd, 0, sizeof(rd));
280
281 region.base = ri->rdi_data;
282 region.length = ri->rdi_length;
283
284 dns_rdata_fromregion(&rd, dns_rdataclass_in,
285 dns_rdatatype_cname, &region);
286
287 /* we set mctx to NULL, which means that the tenure for
288 * the stuff pointed to by cn will persist only as long
289 * as rd persists.
290 */
291 if(dns_rdata_tostruct(&rd, &cn, NULL) != ISC_R_SUCCESS) {
292 /* failed, try next return error */
293 success=0;
294 goto fatal;
295 }
296
297 cname_text=NULL;
298 if(isc_buffer_allocate(gs->iscmem, &cname_text, DNS_NAME_MAXTEXT)) {
299 success=0;
300 goto fatal;
301 }
302
303 if(dns_name_totext(&cn.cname, ISC_TRUE, cname_text) !=
304 ISC_R_SUCCESS) {
305 success=0;
306 goto fatal;
307 }
308
309 cname_buf[0]='\0';
310 strncat(cname_buf,
311 isc_buffer_base(cname_text),
312 isc_buffer_usedlength(cname_text));
313
314 /* free up buffer */
315 isc_buffer_free(&cname_text);
316
317 {
318 /* add a trailing . */
319 char *end;
320 end = &cname_buf[strlen(cname_buf)];
321 if(*end != '.') {
322 strncat(cname_buf, ".", sizeof(cname_buf));
323 }
324 }
325
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);
329
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! */
333 dl->step = dkl_done;
334 return;
335 }
336
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);
342
343 if(success != ERRSET_SUCCESS) {
344 return;
345 }
346
347 lwres_getrrsetbyname_xmit(gs->lwctx, &dl->las);
348
349 dl->step = dkl_second;
350 }
351
352 void process_step_first(dnskey_glob *gs,
353 dnskey_lookup *dl,
354 struct rrsetinfo *ans,
355 int success,
356 int attempt) /* attempt = 0 first time, 1 after cname */
357 {
358 char simplebuf[132], typebuf[16];
359 char txtbuf[1024];
360 int i;
361
362 switch(success) {
363 case ERRSET_NODATA:
364 if(attempt == 0) {
365 lwresd_has_spoken = 1;
366 setup_follow_possible_cname(gs, dl);
367 dl->step = dkl_cname;
368 return;
369 }
370 /* FALLTHROUGH */
371 case ERRSET_NONAME:
372 lwresd_has_spoken = 1;
373 snprintf(simplebuf, sizeof(simplebuf),
374 "RR of type %s for %s was not found",
375 dl->wantedtype_name,
376 dl->fqdn);
377 output_transaction_line(gs, dl->tracking_id, 0, "RETRY",
378 simplebuf);
379 dl->step = dkl_done;
380 goto done;
381
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);
387 dl->step = dkl_done;
388 goto done;
389
390 case ERRSET_FAIL:
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);
396 dl->step = dkl_done;
397 goto done;
398
399 case ERRSET_INVAL:
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);
404 dl->step = dkl_done;
405 goto done;
406
407 default:
408 snprintf(simplebuf, sizeof(simplebuf), " unknown error %d", success);
409 output_transaction_line(gs, dl->tracking_id, 0, "RETRY", simplebuf);
410 dl->step = dkl_done;
411 done:
412 return;
413
414 case 0:
415 /* everything okay */
416 lwresd_has_spoken = 1;
417 dl->step = dkl_done;
418 break;
419 }
420
421 /* output the rest of the data */
422
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);
428 } else {
429 output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "not present");
430 }
431
432 output_transaction_line(gs, dl->tracking_id, 0, "NAME", ans->rri_name);
433
434 for(i=0; i<ans->rri_nrdatas; i++) {
435 struct rdatainfo *ri = &ans->rri_rdatas[i];
436 isc_region_t region;
437 dns_rdata_t rd;
438
439 isc_buffer_clear(gs->iscbuf);
440 memset(&region, 0, sizeof(region));
441 memset(&rd, 0, sizeof(rd));
442
443 region.base = ri->rdi_data;
444 region.length = ri->rdi_length;
445
446 if(dl->wantedtype == dns_rdatatype_txt) {
447 /* special treatment for TXT records */
448 unsigned int len, rdatalen, totlen;
449 unsigned char *txtp, *rdata;
450
451 txtp = txtbuf;
452 totlen = 0;
453 rdatalen = ri->rdi_length;
454 rdata = ri->rdi_data;
455
456 while(rdatalen > 0) {
457 len= (unsigned)rdata[0];
458 memcpy(txtp, rdata+1, len);
459 totlen += len;
460 txtp += len;
461 rdata += len+1;
462 rdatalen -= len+1;
463 }
464 *txtp = '\0';
465
466 output_transaction_line_limited(gs, dl->tracking_id, 0,
467 dl->wantedtype_name,
468 totlen, txtbuf);
469
470 } else {
471 dns_rdata_fromregion(&rd, dns_rdataclass_in,
472 dl->wantedtype, &region);
473
474 if(dns_rdata_totext(&rd, NULL, gs->iscbuf) != ISC_R_SUCCESS) {
475
476 }
477
478 output_transaction_line_limited(gs, dl->tracking_id, 0,
479 dl->wantedtype_name,
480 (int)isc_buffer_usedlength(gs->iscbuf),
481 (char *)isc_buffer_base(gs->iscbuf));
482 }
483 }
484
485 for(i=0; i<ans->rri_nsigs; i++) {
486 struct rdatainfo *ri = &ans->rri_sigs[i];
487 isc_region_t region;
488 dns_rdata_t rd;
489
490 isc_buffer_clear(gs->iscbuf);
491 memset(&region, 0, sizeof(region));
492 memset(&rd, 0, sizeof(rd));
493
494 region.base = ri->rdi_data;
495 region.length = ri->rdi_length;
496
497 dns_rdata_fromregion(&rd, dns_rdataclass_in,
498 dns_rdatatype_sig, &region);
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");
501 return;
502 }
503
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));
507 }
508 }
509
510
511
512 void lookup_step(dnskey_glob *gs,
513 dnskey_lookup *dl,
514 struct rrsetinfo *ans,
515 int success)
516 {
517 /* char simplebuf[80]; */
518 int nextstate;
519
520 nextstate = dkl_done;
521
522 if(dl == NULL)
523 {
524 return;
525 }
526
527 switch(dl->step)
528 {
529 case dkl_start:
530 /* first request done, why are still in this state? */
531 break;
532
533 case dkl_first:
534 /* okay, got the reply from the first step! */
535 process_step_first(gs, dl, ans, success, 0);
536 nextstate = dl->step;
537 break;
538
539 case dkl_cname:
540 /*
541 * we asked for a cname, and we have some result to deal
542 * with here.
543 */
544 process_step_cname(gs, dl, ans, success);
545 nextstate = dl->step;
546 break;
547
548 case dkl_second:
549 /*
550 * we had asked for something, for a cname, and we followed
551 * it, and we'll see what we got back.
552 */
553 process_step_first(gs, dl, ans, success, 1);
554 nextstate = dl->step;
555 break;
556
557 case dkl_done:
558 /* this should not happen, really, just book keeping, so,
559 * just free up the structure, and return.
560 */
561 nextstate = dl->step;
562 return;
563 }
564
565
566 /* we have been through, made a state transition, if we are
567 * done, then do that.
568 */
569 if(nextstate == dkl_done)
570 {
571 output_transaction_line(gs, dl->tracking_id, 0, "DONE", NULL);
572 free_dl(gs, dl);
573 dl=NULL;
574 }
575 return;
576 }
577
578 void process_dns_reply(dnskey_glob *gs)
579 {
580 dnskey_lookup *dl;
581 struct lwres_async_state *plas;
582 struct rrsetinfo *res;
583 int success;
584
585 plas = NULL;
586
587 success = lwres_getrrsetbyname_read(&plas, gs->lwctx, &res);
588
589 /* cast answer back to dnskey_lookup structure */
590 dl = (dnskey_lookup *)plas;
591
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
595 */
596 dl->retry_count--;
597 if(dl->retry_count > 0) {
598 lwres_getrrsetbyname_xmit(gs->lwctx, plas);
599 } else {
600 output_transaction_line(gs, dl->tracking_id, 0, "FATAL", "too many retries");
601 free_dl(gs, dl);
602 }
603 return;
604 }
605
606 /* perform next step for this one */
607 lookup_step(gs, dl, res, success);
608 }
609
610 /*
611 * $Log: lookup.c,v $
612 * Revision 1.1 2004/03/15 20:35:28 as
613 * added files from freeswan-2.04-x509-1.5.3
614 *
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.
618 *
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.
622 *
623 * Revision 1.1 2003/09/03 01:13:24 mcr
624 * first attempt at async capable lwdnsq.
625 *
626 *
627 * Local variables:
628 * c-file-style: "linux"
629 * c-basic-offset: 2
630 * End:
631 *
632 */