resetting manifest requested domain to floor
[platform/upstream/keyutils.git] / key.dns_resolver.c
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));
312         switch (mask & INET_ALL) {
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;
316         }
317
318         /* resolve name to ip */
319         ret = getaddrinfo(server_name, NULL, &hints, &addr);
320         if (ret) {
321                 info("unable to resolve hostname: %s [%s]",
322                      server_name, gai_strerror(ret));
323                 return -1;
324         }
325
326         debug("getaddrinfo = %d", ret);
327
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 */
335                 switch (ai->ai_family) {
336                 case AF_INET:
337                         if (!(mask & INET_IP4_ONLY))
338                                 continue;
339                         sa = &(((struct sockaddr_in *)ai->ai_addr)->sin_addr);
340                         len = INET_ADDRSTRLEN;
341                         break;
342                 case AF_INET6:
343                         if (!(mask & INET_IP6_ONLY))
344                                 continue;
345                         sa = &(((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr);
346                         len = INET6_ADDRSTRLEN;
347                         break;
348                 default:
349                         debug("Address of unknown family %u", addr->ai_family);
350                         continue;
351                 }
352
353                 if (!inet_ntop(ai->ai_family, sa, buf, len))
354                         error("%s: inet_ntop: %m", __func__);
355
356                 slen = strlen(buf);
357                 seg = malloc(slen);
358                 if (!seg)
359                         error("%s: inet_ntop: %m", __func__);
360                 memcpy(seg, buf, slen);
361                 append_address_to_payload(seg, slen);
362                 if (mask & ONE_ADDR_ONLY)
363                         break;
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\n", DNS_PARSE_VERSION);
653                         exit(0);
654                 case 'v':
655                         verbose++;
656                         continue;
657                 default:
658                         if (!isatty(2))
659                                 syslog(LOG_ERR, "unknown option: %c", ret);
660                         usage();
661                 }
662         }
663
664         argc -= optind;
665         argv += optind;
666
667         if (!debug_mode) {
668                 if (argc != 1)
669                         usage();
670
671                 /* get the key ID */
672                 errno = 0;
673                 key = strtol(*argv, NULL, 10);
674                 if (errno != 0)
675                         error("Invalid key ID format: %m");
676
677                 /* get the key description (of the form "x;x;x;x;<query_type>:<name>") */
678                 if (!buf) {
679                         ret = keyctl_describe_alloc(key, &buf);
680                         if (ret == -1)
681                                 error("keyctl_describe_alloc failed: %m");
682                 }
683
684                 /* get the callout_info (which can supply options) */
685                 if (!callout_info) {
686                         ret = keyctl_read_alloc(KEY_SPEC_REQKEY_AUTH_KEY,
687                                                 (void **)&callout_info);
688                         if (ret == -1)
689                                 error("Invalid key callout_info read: %m");
690                 }
691         } else {
692                 if (argc != 2)
693                         usage();
694
695                 ret = asprintf(&buf, "%s;-1;-1;0;%s", key_type, argv[0]);
696                 if (ret < 0)
697                         error("Error %m");
698                 callout_info = argv[1];
699         }
700
701         ret = 1;
702         info("Key description: '%s'", buf);
703         info("Callout info: '%s'", callout_info);
704
705         p = strchr(buf, ';');
706         if (!p)
707                 error("Badly formatted key description '%s'", buf);
708         ktlen = p - buf;
709
710         /* make sure it's the type we are expecting */
711         if (ktlen != sizeof(key_type) - 1 ||
712             memcmp(buf, key_type, ktlen) != 0)
713                 error("Key type is not supported: '%*.*s'", ktlen, ktlen, buf);
714
715         keyend = buf + ktlen + 1;
716
717         /* the actual key description follows the last semicolon */
718         keyend = rindex(keyend, ';');
719         if (!keyend)
720                 error("Invalid key description: %s", buf);
721         keyend++;
722
723         name = index(keyend, ':');
724         if (!name)
725                 dns_query_a_or_aaaa(key, keyend, callout_info);
726
727         qtlen = name - keyend;
728         name++;
729
730         if ((qtlen == sizeof(a_query_type) - 1 &&
731              memcmp(keyend, a_query_type, sizeof(a_query_type) - 1) == 0) ||
732             (qtlen == sizeof(aaaa_query_type) - 1 &&
733              memcmp(keyend, aaaa_query_type, sizeof(aaaa_query_type) - 1) == 0)
734             ) {
735                 info("Do DNS query of A/AAAA type for:'%s' mask:'%s'",
736                      name, callout_info);
737                 dns_query_a_or_aaaa(key, name, callout_info);
738         }
739
740         if (qtlen == sizeof(afsdb_query_type) - 1 &&
741             memcmp(keyend, afsdb_query_type, sizeof(afsdb_query_type) - 1) == 0
742             ) {
743                 info("Do DNS query of AFSDB type for:'%s' mask:'%s'",
744                      name, callout_info);
745                 dns_query_afsdb(key, name, callout_info);
746         }
747
748         error("Query type: \"%*.*s\" is not supported", qtlen, qtlen, keyend);
749 }