1 /*
2 * DNS Resolver Module User-space Helper for AFSDB records
3 *
4 * Copyright (C) Wang Lei (wang840925@gmail.com) 2010
5 * Authors: Wang Lei (wang840925@gmail.com)
6 * David Howells (dhowells@redhat.com)
7 *
8 * This is a userspace tool for querying AFSDB RR records in the DNS on behalf
9 * of the kernel, and converting the VL server addresses to IPv4 format so that
10 * they can be used by the kAFS filesystem.
11 *
12 * Compile with:
13 *
14 * cc -o key.dns_resolver key.dns_resolver.c -lresolv -lkeyutils
15 *
16 * As some function like res_init() should use the static liberary, which is a
17 * bug of libresolv, that is the reason for cifs.upcall to reimplement.
18 *
19 * To use this program, you must tell /sbin/request-key how to invoke it. You
20 * need to have the keyutils package installed and something like the following
21 * lines added to your /etc/request-key.conf file:
22 *
23 * #OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ...
24 * ====== ============ =========== ============ ==========================
25 * create dns_resolver afsdb:* * /sbin/key.dns_resolver %k
26 *
27 *
28 * This program is free software; you can redistribute it and/or modify
29 * it under the terms of the GNU General Public License as published by
30 * the Free Software Foundation; either version 2 of the License, or
31 * (at your option) any later version.
32 *
33 * This program is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 * GNU General Public License for more details.
37 * You should have received a copy of the GNU General Public License
38 * along with this program; if not, write to the Free Software
39 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
40 */
41 #define _GNU_SOURCE
42 #include <netinet/in.h>
43 #include <arpa/nameser.h>
44 #include <arpa/inet.h>
45 #include <resolv.h>
46 #include <getopt.h>
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netdb.h>
50 #include <syslog.h>
51 #include <errno.h>
52 #include <string.h>
53 #include <stdio.h>
54 #include <stdarg.h>
55 #include <keyutils.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <time.h>
59
60 static const char *DNS_PARSE_VERSION = "1.0";
61 static const char prog[] = "key.dns_resolver";
62 static const char key_type[] = "dns_resolver";
63 static const char a_query_type[] = "a";
64 static const char aaaa_query_type[] = "aaaa";
65 static const char afsdb_query_type[] = "afsdb";
66 static key_serial_t key;
67 static int verbose;
68 static int debug_mode;
69
70
71 #define MAX_VLS 15 /* Max Volume Location Servers Per-Cell */
72 #define DNS_EXPIRY_PREFIX "expiry_time="
73 #define DNS_EXPIRY_TIME_LEN 10 /* 2^32 - 1 = 4294967295 */
74 #define AFSDB_MAX_DATA_LEN \
75 ((MAX_VLS * (INET6_ADDRSTRLEN + 1)) + sizeof(DNS_EXPIRY_PREFIX) + \
76 DNS_EXPIRY_TIME_LEN + 1 /* '#'*/ + 1 /* end 0 */)
77
78 #define INET_IP4_ONLY 0x1
79 #define INET_IP6_ONLY 0x2
80 #define INET_ALL 0xFF
81 #define ONE_ADDR_ONLY 0x100
82 #define LIST_MULTIPLE_ADDRS 0x200
83
84 /*
85 * segmental payload
86 */
87 #define N_PAYLOAD 256
88 struct iovec payload[N_PAYLOAD];
89 int payload_index;
90
91 /*
92 * Print an error to stderr or the syslog, negate the key being created and
93 * exit
94 */
95 static __attribute__((format(printf, 1, 2), noreturn))
96 void error(const char *fmt, ...)
97 {
98 va_list va;
99
100 va_start(va, fmt);
101 if (isatty(2)) {
102 vfprintf(stderr, fmt, va);
103 fputc('\n', stderr);
104 } else {
105 vsyslog(LOG_ERR, fmt, va);
106 }
107 va_end(va);
108
109 /*
110 * on error, negatively instantiate the key ourselves so that we can
111 * make sure the kernel doesn't hang it off of a searchable keyring
112 * and interfere with the next attempt to instantiate the key.
113 */
114 if (!debug_mode)
115 keyctl_negate(key, 1, KEY_REQKEY_DEFL_DEFAULT);
116
117 exit(1);
118 }
119
120 #define error(FMT, ...) error("Error: " FMT, ##__VA_ARGS__);
121
122 /*
123 * Just print an error to stderr or the syslog
124 */
125 static __attribute__((format(printf, 1, 2)))
126 void _error(const char *fmt, ...)
127 {
128 va_list va;
129
130 va_start(va, fmt);
131 if (isatty(2)) {
132 vfprintf(stderr, fmt, va);
133 fputc('\n', stderr);
134 } else {
135 vsyslog(LOG_ERR, fmt, va);
136 }
137 va_end(va);
138 }
139
140 /*
141 * Print status information
142 */
143 static __attribute__((format(printf, 1, 2)))
144 void info(const char *fmt, ...)
145 {
146 va_list va;
147
148 if (verbose < 1)
149 return;
150
151 va_start(va, fmt);
152 if (isatty(1)) {
153 fputs("I: ", stdout);
154 vfprintf(stdout, fmt, va);
155 fputc('\n', stdout);
156 } else {
157 vsyslog(LOG_INFO, fmt, va);
158 }
159 va_end(va);
160 }
161
162 /*
163 * Print a nameserver error and exit
164 */
165 static const int ns_errno_map[] = {
166 [0] = ECONNREFUSED,
167 [HOST_NOT_FOUND] = ENODATA,
168 [TRY_AGAIN] = EAGAIN,
169 [NO_RECOVERY] = ECONNREFUSED,
170 [NO_DATA] = ENODATA,
171 };
172
173 static __attribute__((noreturn))
174 void nsError(int err, const char *domain)
175 {
176 unsigned timeout = 1 * 60;
177 int ret;
178
179 if (isatty(2))
180 fprintf(stderr, "%s: %s.\n", domain, hstrerror(err));
181 else
182 syslog(LOG_INFO, "%s: %s", domain, hstrerror(err));
183
184 if (err >= sizeof(ns_errno_map) / sizeof(ns_errno_map[0]))
185 err = ECONNREFUSED;
186 else
187 err = ns_errno_map[err];
188
189 info("Reject the key with error %d", err);
190
191 if (err == EAGAIN)
192 timeout = 1;
193 else if (err == ECONNREFUSED)
194 timeout = 10;
195
196 if (!debug_mode) {
197 ret = keyctl_reject(key, timeout, err, KEY_REQKEY_DEFL_DEFAULT);
198 if (ret == -1)
199 error("%s: keyctl_reject: %m", __func__);
200 }
201 exit(0);
202 }
203
204 /*
205 * Print debugging information
206 */
207 static __attribute__((format(printf, 1, 2)))
208 void debug(const char *fmt, ...)
209 {
210 va_list va;
211
212 if (verbose < 2)
213 return;
214
215 va_start(va, fmt);
216 if (isatty(1)) {
217 fputs("D: ", stdout);
218 vfprintf(stdout, fmt, va);
219 fputc('\n', stdout);
220 } else {
221 vsyslog(LOG_DEBUG, fmt, va);
222 }
223 va_end(va);
224 }
225
226 /*
227 * Append an address to the payload segment list
228 */
229 static void append_address_to_payload(char *p, size_t sz)
230 {
231 int loop;
232
233 debug("append '%*.*s'", (int)sz, (int)sz, p);
234
235 /* discard duplicates */
236 for (loop = 0; loop < payload_index; loop++)
237 if (payload[loop].iov_len == sz &&
238 memcmp(payload[loop].iov_base, p, sz) == 0)
239 return;
240
241 if (payload_index != 0) {
242 if (payload_index + 2 > N_PAYLOAD - 1)
243 return;
244 payload[payload_index ].iov_base = ",";
245 payload[payload_index++].iov_len = 1;
246 } else {
247 if (payload_index + 1 > N_PAYLOAD - 1)
248 return;
249 }
250
251 payload[payload_index ].iov_base = p;
252 payload[payload_index++].iov_len = sz;
253 }
254
255 /*
256 * Dump the payload when debugging
257 */
258 static void dump_payload(void)
259 {
260 size_t plen, n;
261 char *buf, *p;
262 int loop;
263
264 if (debug_mode)
265 verbose = 1;
266 if (verbose < 1)
267 return;
268
269 plen = 0;
270 for (loop = 0; loop < payload_index; loop++) {
271 n = payload[loop].iov_len;
272 debug("seg[%d]: %zu", loop, n);
273 plen += n;
274 }
275 if (plen == 0) {
276 info("The key instantiation data is empty");
277 return;
278 }
279
280 debug("total: %zu", plen);
281 buf = malloc(plen + 1);
282 if (!buf)
283 return;
284
285 p = buf;
286 for (loop = 0; loop < payload_index; loop++) {
287 n = payload[loop].iov_len;
288 memcpy(p, payload[loop].iov_base, n);
289 p += n;
290 }
291
292 info("The key instantiation data is '%s'", buf);
293 free(buf);
294 }
295
296 /*
297 * Perform address resolution on a hostname and add the resulting address as a
298 * string to the list of payload segments.
299 */
300 static int
301 dns_resolver(const char *server_name, unsigned mask)
302 {
303 struct addrinfo hints, *addr, *ai;
304 size_t slen;
305 char buf[INET6_ADDRSTRLEN + 1], *seg;
306 int ret, len;
307 void *sa;
308
309 debug("Resolve '%s' with %x", server_name, mask);
310
311 memset(&hints, 0, sizeof(hints));
(1) Event switch: |
Switch case value "1U" |
312 switch (mask & INET_ALL) {
(2) Event switch_case: |
Reached case "1U" |
(3) Event break: |
Breaking from switch |
313 case INET_IP4_ONLY: hints.ai_family = AF_INET; debug("IPv4"); break;
314 case INET_IP6_ONLY: hints.ai_family = AF_INET6; debug("IPv6"); break;
315 default: break;
(4) Event switch_end: |
Reached end of switch |
316 }
317
318 /* resolve name to ip */
319 ret = getaddrinfo(server_name, NULL, &hints, &addr);
(5) Event cond_false: |
Condition "ret", taking false branch |
320 if (ret) {
321 info("unable to resolve hostname: %s [%s]",
322 server_name, gai_strerror(ret));
323 return -1;
(6) Event if_end: |
End of if statement |
324 }
325
326 debug("getaddrinfo = %d", ret);
327
(7) Event cond_true: |
Condition "ai", taking true branch |
(13) Event cond_true: |
Condition "ai", taking true branch |
(19) Event cond_true: |
Condition "ai", taking true branch |
328 for (ai = addr; ai; ai = ai->ai_next) {
329 debug("RR: %x,%x,%x,%x,%x,%s",
330 ai->ai_flags, ai->ai_family,
331 ai->ai_socktype, ai->ai_protocol,
332 ai->ai_addrlen, ai->ai_canonname);
333
334 /* convert address to string */
(8) Event switch: |
Switch case value "2" |
(14) Event switch: |
Switch case value "10" |
(20) Event switch: |
Switch case value "10" |
335 switch (ai->ai_family) {
(9) Event switch_case: |
Reached case "2" |
336 case AF_INET:
(10) Event cond_true: |
Condition "!(mask & 1)", taking true branch |
337 if (!(mask & INET_IP4_ONLY))
(11) Event continue: |
Continuing loop |
338 continue;
339 sa = &(((struct sockaddr_in *)ai->ai_addr)->sin_addr);
340 len = INET_ADDRSTRLEN;
341 break;
(15) Event switch_case: |
Reached case "10" |
(21) Event switch_case: |
Reached case "10" |
342 case AF_INET6:
(16) Event cond_true: |
Condition "!(mask & 2)", taking true branch |
(22) Event cond_false: |
Condition "!(mask & 2)", taking false branch |
343 if (!(mask & INET_IP6_ONLY))
(17) Event continue: |
Continuing loop |
(23) Event if_end: |
End of if statement |
344 continue;
345 sa = &(((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr);
346 len = INET6_ADDRSTRLEN;
(24) Event break: |
Breaking from switch |
347 break;
348 default:
349 debug("Address of unknown family %u", addr->ai_family);
350 continue;
(25) Event switch_end: |
Reached end of switch |
351 }
352
(26) Event cond_false: |
Condition "!inet_ntop(ai->ai_family, sa, buf, len)", taking false branch |
353 if (!inet_ntop(ai->ai_family, sa, buf, len))
(27) Event if_end: |
End of if statement |
354 error("%s: inet_ntop: %m", __func__);
355
(28) Event strlen_assign: |
Setting variable "slen" to a value computed using function strlen. |
Also see events: |
[alloc_strlen] |
356 slen = strlen(buf);
(29) Event alloc_strlen: |
Allocating insufficient memory for the terminating null of the string. |
Also see events: |
[strlen_assign] |
357 seg = malloc(slen);
(30) Event cond_false: |
Condition "!seg", taking false branch |
358 if (!seg)
(31) Event if_end: |
End of if statement |
359 error("%s: inet_ntop: %m", __func__);
360 memcpy(seg, buf, slen);
361 append_address_to_payload(seg, slen);
(32) Event cond_true: |
Condition "mask & 256", taking true branch |
362 if (mask & ONE_ADDR_ONLY)
(33) Event break: |
Breaking from loop |
363 break;
(12) Event loop: |
Looping back |
(18) Event loop: |
Looping back |
(34) Event loop_end: |
Reached end of loop |
364 }
365
366 freeaddrinfo(addr);
367 return 0;
368 }
369
370 /*
371 *
372 */
373 static void afsdb_hosts_to_addrs(char *vllist[],
374 int *vlsnum,
375 ns_msg handle,
376 ns_sect section,
377 unsigned mask,
378 unsigned long *_ttl)
379 {
380 int rrnum;
381 ns_rr rr;
382 int subtype, i, ret;
383 unsigned int ttl = UINT_MAX, rr_ttl;
384
385 debug("AFSDB RR count is %d", ns_msg_count(handle, section));
386
387 /* Look at all the resource records in this section. */
388 for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) {
389 /* Expand the resource record number rrnum into rr. */
390 if (ns_parserr(&handle, section, rrnum, &rr)) {
391 _error("ns_parserr failed : %m");
392 continue;
393 }
394
395 /* We're only interested in AFSDB records */
396 if (ns_rr_type(rr) == ns_t_afsdb) {
397 vllist[*vlsnum] = malloc(MAXDNAME);
398 if (!vllist[*vlsnum])
399 error("Out of memory");
400
401 subtype = ns_get16(ns_rr_rdata(rr));
402
403 /* Expand the name server's domain name */
404 if (ns_name_uncompress(ns_msg_base(handle),
405 ns_msg_end(handle),
406 ns_rr_rdata(rr) + 2,
407 vllist[*vlsnum],
408 MAXDNAME) < 0)
409 error("ns_name_uncompress failed");
410
411 rr_ttl = ns_rr_ttl(rr);
412 if (ttl > rr_ttl)
413 ttl = rr_ttl;
414
415 /* Check the domain name we've just unpacked and add it to
416 * the list of VL servers if it is not a duplicate.
417 * If it is a duplicate, just ignore it.
418 */
419 for (i = 0; i < *vlsnum; i++)
420 if (strcasecmp(vllist[i], vllist[*vlsnum]) == 0)
421 goto next_one;
422
423 /* Turn the hostname into IP addresses */
424 ret = dns_resolver(vllist[*vlsnum], mask);
425 if (ret) {
426 debug("AFSDB RR can't resolve."
427 "subtype:%d, server name:%s, netmask:%u",
428 subtype, vllist[*vlsnum], mask);
429 goto next_one;
430 }
431
432 info("AFSDB RR subtype:%d, server name:%s, ip:%*.*s, ttl:%u",
433 subtype, vllist[*vlsnum],
434 (int)payload[payload_index - 1].iov_len,
435 (int)payload[payload_index - 1].iov_len,
436 (char *)payload[payload_index - 1].iov_base,
437 ttl);
438
439 /* prepare for the next record */
440 *vlsnum += 1;
441 continue;
442
443 next_one:
444 free(vllist[*vlsnum]);
445 }
446 }
447
448 *_ttl = ttl;
449 info("ttl: %u", ttl);
450 }
451
452 /*
453 * Look up an AFSDB record to get the VL server addresses.
454 *
455 * The callout_info is parsed for request options. For instance, "ipv4" to
456 * request only IPv4 addresses and "ipv6" to request only IPv6 addresses.
457 */
458 static __attribute__((noreturn))
459 int dns_query_afsdb(key_serial_t key, const char *cell, char *options)
460 {
461 int ret;
462 char *vllist[MAX_VLS]; /* list of name servers */
463 int vlsnum = 0; /* number of name servers in list */
464 unsigned mask = INET_ALL;
465 int response_len; /* buffer length */
466 ns_msg handle; /* handle for response message */
467 unsigned long ttl = ULONG_MAX;
468 union {
469 HEADER hdr;
470 u_char buf[NS_PACKETSZ];
471 } response; /* response buffers */
472
473 debug("Get AFSDB RR for cell name:'%s', options:'%s'", cell, options);
474
475 /* query the dns for an AFSDB resource record */
476 response_len = res_query(cell,
477 ns_c_in,
478 ns_t_afsdb,
479 response.buf,
480 sizeof(response));
481
482 if (response_len < 0)
483 /* negative result */
484 nsError(h_errno, cell);
485
486 if (ns_initparse(response.buf, response_len, &handle) < 0)
487 error("ns_initparse: %m");
488
489 /* Is the IP address family limited? */
490 if (strcmp(options, "ipv4") == 0)
491 mask = INET_IP4_ONLY;
492 else if (strcmp(options, "ipv6") == 0)
493 mask = INET_IP6_ONLY;
494
495 /* look up the hostnames we've obtained to get the actual addresses */
496 afsdb_hosts_to_addrs(vllist, &vlsnum, handle, ns_s_an, mask, &ttl);
497
498 info("DNS query AFSDB RR results:%u ttl:%lu", payload_index, ttl);
499
500 /* set the key's expiry time from the minimum TTL encountered */
501 if (!debug_mode) {
502 ret = keyctl_set_timeout(key, ttl);
503 if (ret == -1)
504 error("%s: keyctl_set_timeout: %m", __func__);
505 }
506
507 /* handle a lack of results */
508 if (payload_index == 0)
509 nsError(NO_DATA, cell);
510
511 /* must include a NUL char at the end of the payload */
512 payload[payload_index].iov_base = "";
513 payload[payload_index++].iov_len = 1;
514 dump_payload();
515
516 /* load the key with data key */
517 if (!debug_mode) {
518 ret = keyctl_instantiate_iov(key, payload, payload_index, 0);
519 if (ret == -1)
520 error("%s: keyctl_instantiate: %m", __func__);
521 }
522
523 exit(0);
524 }
525
526 /*
527 * Look up a A and/or AAAA records to get host addresses
528 *
529 * The callout_info is parsed for request options. For instance, "ipv4" to
530 * request only IPv4 addresses, "ipv6" to request only IPv6 addresses and
531 * "list" to get multiple addresses.
532 */
533 static __attribute__((noreturn))
534 int dns_query_a_or_aaaa(key_serial_t key, const char *hostname, char *options)
535 {
536 unsigned mask;
537 int ret;
538
539 debug("Get A/AAAA RR for hostname:'%s', options:'%s'",
540 hostname, options);
541
542 if (!options[0]) {
543 /* legacy mode */
544 mask = INET_IP4_ONLY | ONE_ADDR_ONLY;
545 } else {
546 char *key, *val;
547
548 mask = INET_ALL | ONE_ADDR_ONLY;
549
550 do {
551 key = options;
552 options = strchr(options, ' ');
553 if (!options)
554 options = key + strlen(key);
555 else
556 *options++ = '\0';
557 if (!*key)
558 continue;
559 if (strchr(key, ','))
560 error("Option name '%s' contains a comma", key);
561
562 val = strchr(key, '=');
563 if (val)
564 *val++ = '\0';
565
566 debug("Opt %s", key);
567
568 if (strcmp(key, "ipv4") == 0) {
569 mask &= ~INET_ALL;
570 mask |= INET_IP4_ONLY;
571 } else if (strcmp(key, "ipv6") == 0) {
572 mask &= ~INET_ALL;
573 mask |= INET_IP6_ONLY;
574 } else if (strcmp(key, "list") == 0) {
575 mask &= ~ONE_ADDR_ONLY;
576 mask |= LIST_MULTIPLE_ADDRS;
577 }
578
579 } while (*options);
580 }
581
582 /* Turn the hostname into IP addresses */
583 ret = dns_resolver(hostname, mask);
584 if (ret)
585 nsError(NO_DATA, hostname);
586
587 /* handle a lack of results */
588 if (payload_index == 0)
589 nsError(NO_DATA, hostname);
590
591 /* must include a NUL char at the end of the payload */
592 payload[payload_index].iov_base = "";
593 payload[payload_index++].iov_len = 1;
594 dump_payload();
595
596 /* load the key with data key */
597 if (!debug_mode) {
598 ret = keyctl_instantiate_iov(key, payload, payload_index, 0);
599 if (ret == -1)
600 error("%s: keyctl_instantiate: %m", __func__);
601 }
602
603 exit(0);
604 }
605
606 /*
607 * Print usage details,
608 */
609 static __attribute__((noreturn))
610 void usage(void)
611 {
612 if (isatty(2)) {
613 fprintf(stderr,
614 "Usage: %s [-vv] key_serial\n",
615 prog);
616 fprintf(stderr,
617 "Usage: %s -D [-vv] <desc> <calloutinfo>\n",
618 prog);
619 } else {
620 info("Usage: %s [-vv] key_serial", prog);
621 }
622 if (!debug_mode)
623 keyctl_negate(key, 1, KEY_REQKEY_DEFL_DEFAULT);
624 exit(2);
625 }
626
627 const struct option long_options[] = {
628 { "debug", 0, NULL, 'D' },
629 { "verbose", 0, NULL, 'v' },
630 { "version", 0, NULL, 'V' },
631 { NULL, 0, NULL, 0 }
632 };
633
634 /*
635 *
636 */
637 int main(int argc, char *argv[])
638 {
639 int ktlen, qtlen, ret;
640 char *keyend, *p;
641 char *callout_info = NULL;
642 char *buf = NULL, *name;
643
644 openlog(prog, 0, LOG_DAEMON);
645
646 while ((ret = getopt_long(argc, argv, "vD", long_options, NULL)) != -1) {
647 switch (ret) {
648 case 'D':
649 debug_mode = 1;
650 continue;
651 case 'V':
652 printf("version: %s from %s (%s)\n",
653 DNS_PARSE_VERSION,
654 keyutils_version_string,
655 keyutils_build_string);
656 exit(0);
657 case 'v':
658 verbose++;
659 continue;
660 default:
661 if (!isatty(2))
662 syslog(LOG_ERR, "unknown option: %c", ret);
663 usage();
664 }
665 }
666
667 argc -= optind;
668 argv += optind;
669
670 if (!debug_mode) {
671 if (argc != 1)
672 usage();
673
674 /* get the key ID */
675 errno = 0;
676 key = strtol(*argv, NULL, 10);
677 if (errno != 0)
678 error("Invalid key ID format: %m");
679
680 /* get the key description (of the form "x;x;x;x;<query_type>:<name>") */
681 if (!buf) {
682 ret = keyctl_describe_alloc(key, &buf);
683 if (ret == -1)
684 error("keyctl_describe_alloc failed: %m");
685 }
686
687 /* get the callout_info (which can supply options) */
688 if (!callout_info) {
689 ret = keyctl_read_alloc(KEY_SPEC_REQKEY_AUTH_KEY,
690 (void **)&callout_info);
691 if (ret == -1)
692 error("Invalid key callout_info read: %m");
693 }
694 } else {
695 if (argc != 2)
696 usage();
697
698 ret = asprintf(&buf, "%s;-1;-1;0;%s", key_type, argv[0]);
699 if (ret < 0)
700 error("Error %m");
701 callout_info = argv[1];
702 }
703
704 ret = 1;
705 info("Key description: '%s'", buf);
706 info("Callout info: '%s'", callout_info);
707
708 p = strchr(buf, ';');
709 if (!p)
710 error("Badly formatted key description '%s'", buf);
711 ktlen = p - buf;
712
713 /* make sure it's the type we are expecting */
714 if (ktlen != sizeof(key_type) - 1 ||
715 memcmp(buf, key_type, ktlen) != 0)
716 error("Key type is not supported: '%*.*s'", ktlen, ktlen, buf);
717
718 keyend = buf + ktlen + 1;
719
720 /* the actual key description follows the last semicolon */
721 keyend = rindex(keyend, ';');
722 if (!keyend)
723 error("Invalid key description: %s", buf);
724 keyend++;
725
726 name = index(keyend, ':');
727 if (!name)
728 dns_query_a_or_aaaa(key, keyend, callout_info);
729
730 qtlen = name - keyend;
731 name++;
732
733 if ((qtlen == sizeof(a_query_type) - 1 &&
734 memcmp(keyend, a_query_type, sizeof(a_query_type) - 1) == 0) ||
735 (qtlen == sizeof(aaaa_query_type) - 1 &&
736 memcmp(keyend, aaaa_query_type, sizeof(aaaa_query_type) - 1) == 0)
737 ) {
738 info("Do DNS query of A/AAAA type for:'%s' mask:'%s'",
739 name, callout_info);
740 dns_query_a_or_aaaa(key, name, callout_info);
741 }
742
743 if (qtlen == sizeof(afsdb_query_type) - 1 &&
744 memcmp(keyend, afsdb_query_type, sizeof(afsdb_query_type) - 1) == 0
745 ) {
746 info("Do DNS query of AFSDB type for:'%s' mask:'%s'",
747 name, callout_info);
748 dns_query_afsdb(key, name, callout_info);
749 }
750
751 error("Query type: \"%*.*s\" is not supported", qtlen, qtlen, keyend);
752 }
753