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