1 | /* Copyright 1998 by the Massachusetts Institute of Technology. |
---|
2 | * |
---|
3 | * Permission to use, copy, modify, and distribute this |
---|
4 | * software and its documentation for any purpose and without |
---|
5 | * fee is hereby granted, provided that the above copyright |
---|
6 | * notice appear in all copies and that both that copyright |
---|
7 | * notice and this permission notice appear in supporting |
---|
8 | * documentation, and that the name of M.I.T. not be used in |
---|
9 | * advertising or publicity pertaining to distribution of the |
---|
10 | * software without specific, written prior permission. |
---|
11 | * M.I.T. makes no representations about the suitability of |
---|
12 | * this software for any purpose. It is provided "as is" |
---|
13 | * without express or implied warranty. |
---|
14 | */ |
---|
15 | |
---|
16 | static const char rcsid[] = "$Id: adig.c,v 1.5 1998-11-02 19:37:47 ghudson Exp $"; |
---|
17 | |
---|
18 | #include <sys/types.h> |
---|
19 | #include <sys/time.h> |
---|
20 | #include <sys/socket.h> |
---|
21 | #include <netinet/in.h> |
---|
22 | #include <arpa/inet.h> |
---|
23 | #include <arpa/nameser.h> |
---|
24 | #include <stdio.h> |
---|
25 | #include <stdlib.h> |
---|
26 | #include <string.h> |
---|
27 | #include <ctype.h> |
---|
28 | #include <unistd.h> |
---|
29 | #include <errno.h> |
---|
30 | #include <netdb.h> |
---|
31 | #include "ares.h" |
---|
32 | #include "ares_dns.h" |
---|
33 | |
---|
34 | #ifndef INADDR_NONE |
---|
35 | #define INADDR_NONE 0xffffffff |
---|
36 | #endif |
---|
37 | |
---|
38 | extern int optind; |
---|
39 | extern char *optarg; |
---|
40 | |
---|
41 | struct nv { |
---|
42 | const char *name; |
---|
43 | int value; |
---|
44 | }; |
---|
45 | |
---|
46 | static const struct nv flags[] = { |
---|
47 | { "usevc", ARES_FLAG_USEVC }, |
---|
48 | { "primary", ARES_FLAG_PRIMARY }, |
---|
49 | { "igntc", ARES_FLAG_IGNTC }, |
---|
50 | { "norecurse", ARES_FLAG_NORECURSE }, |
---|
51 | { "stayopen", ARES_FLAG_STAYOPEN }, |
---|
52 | { "noaliases", ARES_FLAG_NOALIASES } |
---|
53 | }; |
---|
54 | static const int nflags = sizeof(flags) / sizeof(flags[0]); |
---|
55 | |
---|
56 | static const struct nv classes[] = { |
---|
57 | { "IN", C_IN }, |
---|
58 | { "CHAOS", C_CHAOS }, |
---|
59 | { "HS", C_HS }, |
---|
60 | { "ANY", C_ANY } |
---|
61 | }; |
---|
62 | static const int nclasses = sizeof(classes) / sizeof(classes[0]); |
---|
63 | |
---|
64 | static const struct nv types[] = { |
---|
65 | { "A", T_A }, |
---|
66 | { "NS", T_NS }, |
---|
67 | { "MD", T_MD }, |
---|
68 | { "MF", T_MF }, |
---|
69 | { "CNAME", T_CNAME }, |
---|
70 | { "SOA", T_SOA }, |
---|
71 | { "MB", T_MB }, |
---|
72 | { "MG", T_MG }, |
---|
73 | { "MR", T_MR }, |
---|
74 | { "NULL", T_NULL }, |
---|
75 | { "WKS", T_WKS }, |
---|
76 | { "PTR", T_PTR }, |
---|
77 | { "HINFO", T_HINFO }, |
---|
78 | { "MINFO", T_MINFO }, |
---|
79 | { "MX", T_MX }, |
---|
80 | { "TXT", T_TXT }, |
---|
81 | { "RP", T_RP }, |
---|
82 | { "AFSDB", T_AFSDB }, |
---|
83 | { "X25", T_X25 }, |
---|
84 | { "ISDN", T_ISDN }, |
---|
85 | { "RT", T_RT }, |
---|
86 | { "NSAP", T_NSAP }, |
---|
87 | { "NSAP_PTR", T_NSAP_PTR }, |
---|
88 | { "SIG", T_SIG }, |
---|
89 | { "KEY", T_KEY }, |
---|
90 | { "PX", T_PX }, |
---|
91 | { "GPOS", T_GPOS }, |
---|
92 | { "AAAA", T_AAAA }, |
---|
93 | { "LOC", T_LOC }, |
---|
94 | { "UINFO", T_UINFO }, |
---|
95 | { "UID", T_UID }, |
---|
96 | { "GID", T_GID }, |
---|
97 | { "UNSPEC", T_UNSPEC }, |
---|
98 | { "AXFR", T_AXFR }, |
---|
99 | { "MAILB", T_MAILB }, |
---|
100 | { "MAILA", T_MAILA }, |
---|
101 | { "ANY", T_ANY } |
---|
102 | }; |
---|
103 | static const int ntypes = sizeof(types) / sizeof(types[0]); |
---|
104 | |
---|
105 | static const char *opcodes[] = { |
---|
106 | "QUERY", "IQUERY", "STATUS", "(reserved)", "NOTIFY", |
---|
107 | "(unknown)", "(unknown)", "(unknown)", "(unknown)", |
---|
108 | "UPDATEA", "UPDATED", "UPDATEDA", "UPDATEM", "UPDATEMA", |
---|
109 | "ZONEINIT", "ZONEREF" |
---|
110 | }; |
---|
111 | |
---|
112 | static const char *rcodes[] = { |
---|
113 | "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED", |
---|
114 | "(unknown)", "(unknown)", "(unknown)", "(unknown)", "(unknown)", |
---|
115 | "(unknown)", "(unknown)", "(unknown)", "(unknown)", "NOCHANGE" |
---|
116 | }; |
---|
117 | |
---|
118 | static void callback(void *arg, int status, unsigned char *abuf, int alen); |
---|
119 | static const unsigned char *display_question(const unsigned char *aptr, |
---|
120 | const unsigned char *abuf, |
---|
121 | int alen); |
---|
122 | static const unsigned char *display_rr(const unsigned char *aptr, |
---|
123 | const unsigned char *abuf, int alen); |
---|
124 | static const char *type_name(int type); |
---|
125 | static const char *class_name(int class); |
---|
126 | static void usage(void); |
---|
127 | |
---|
128 | int main(int argc, char **argv) |
---|
129 | { |
---|
130 | ares_channel channel; |
---|
131 | int c, i, optmask = ARES_OPT_FLAGS, class = C_IN, type = T_A; |
---|
132 | int status, nfds, count; |
---|
133 | struct ares_options options; |
---|
134 | struct hostent *hostent; |
---|
135 | fd_set read_fds, write_fds; |
---|
136 | struct timeval *tvp, tv; |
---|
137 | char *errmem; |
---|
138 | |
---|
139 | options.flags = ARES_FLAG_NOCHECKRESP; |
---|
140 | options.servers = NULL; |
---|
141 | options.nservers = 0; |
---|
142 | while ((c = getopt(argc, argv, "f:s:c:t:T:U:")) != -1) |
---|
143 | { |
---|
144 | switch (c) |
---|
145 | { |
---|
146 | case 'f': |
---|
147 | /* Add a flag. */ |
---|
148 | for (i = 0; i < nflags; i++) |
---|
149 | { |
---|
150 | if (strcmp(flags[i].name, optarg) == 0) |
---|
151 | break; |
---|
152 | } |
---|
153 | if (i == nflags) |
---|
154 | usage(); |
---|
155 | options.flags |= flags[i].value; |
---|
156 | break; |
---|
157 | |
---|
158 | case 's': |
---|
159 | /* Add a server, and specify servers in the option mask. */ |
---|
160 | hostent = gethostbyname(optarg); |
---|
161 | if (!hostent || hostent->h_addrtype != AF_INET) |
---|
162 | { |
---|
163 | fprintf(stderr, "adig: server %s not found.\n", optarg); |
---|
164 | return 1; |
---|
165 | } |
---|
166 | options.servers = realloc(options.servers, (options.nservers + 1) |
---|
167 | * sizeof(struct in_addr)); |
---|
168 | if (!options.servers) |
---|
169 | { |
---|
170 | fprintf(stderr, "Out of memory!\n"); |
---|
171 | return 1; |
---|
172 | } |
---|
173 | memcpy(&options.servers[options.nservers], hostent->h_addr, |
---|
174 | sizeof(struct in_addr)); |
---|
175 | options.nservers++; |
---|
176 | optmask |= ARES_OPT_SERVERS; |
---|
177 | break; |
---|
178 | |
---|
179 | case 'c': |
---|
180 | /* Set the query class. */ |
---|
181 | for (i = 0; i < nclasses; i++) |
---|
182 | { |
---|
183 | if (strcasecmp(classes[i].name, optarg) == 0) |
---|
184 | break; |
---|
185 | } |
---|
186 | if (i == nclasses) |
---|
187 | usage(); |
---|
188 | class = classes[i].value; |
---|
189 | break; |
---|
190 | |
---|
191 | case 't': |
---|
192 | /* Set the query type. */ |
---|
193 | for (i = 0; i < ntypes; i++) |
---|
194 | { |
---|
195 | if (strcasecmp(types[i].name, optarg) == 0) |
---|
196 | break; |
---|
197 | } |
---|
198 | if (i == ntypes) |
---|
199 | usage(); |
---|
200 | type = types[i].value; |
---|
201 | break; |
---|
202 | |
---|
203 | case 'T': |
---|
204 | /* Set the TCP port number. */ |
---|
205 | if (!isdigit(*optarg)) |
---|
206 | usage(); |
---|
207 | options.tcp_port = strtol(optarg, NULL, 0); |
---|
208 | optmask |= ARES_OPT_TCP_PORT; |
---|
209 | break; |
---|
210 | |
---|
211 | case 'U': |
---|
212 | /* Set the UDP port number. */ |
---|
213 | if (!isdigit(*optarg)) |
---|
214 | usage(); |
---|
215 | options.udp_port = strtol(optarg, NULL, 0); |
---|
216 | optmask |= ARES_OPT_UDP_PORT; |
---|
217 | break; |
---|
218 | } |
---|
219 | } |
---|
220 | argc -= optind; |
---|
221 | argv += optind; |
---|
222 | if (argc == 0) |
---|
223 | usage(); |
---|
224 | |
---|
225 | status = ares_init_options(&channel, &options, optmask); |
---|
226 | if (status != ARES_SUCCESS) |
---|
227 | { |
---|
228 | fprintf(stderr, "ares_init_options: %s\n", |
---|
229 | ares_strerror(status, &errmem)); |
---|
230 | ares_free_errmem(errmem); |
---|
231 | return 1; |
---|
232 | } |
---|
233 | |
---|
234 | /* Initiate the queries, one per command-line argument. If there is |
---|
235 | * only one query to do, supply NULL as the callback argument; |
---|
236 | * otherwise, supply the query name as an argument so we can |
---|
237 | * distinguish responses for the user when printing them out. |
---|
238 | */ |
---|
239 | if (argc == 1) |
---|
240 | ares_query(channel, *argv, class, type, callback, (char *) NULL); |
---|
241 | else |
---|
242 | { |
---|
243 | for (; *argv; argv++) |
---|
244 | ares_query(channel, *argv, class, type, callback, *argv); |
---|
245 | } |
---|
246 | |
---|
247 | /* Wait for all queries to complete. */ |
---|
248 | while (1) |
---|
249 | { |
---|
250 | FD_ZERO(&read_fds); |
---|
251 | FD_ZERO(&write_fds); |
---|
252 | nfds = ares_fds(channel, &read_fds, &write_fds); |
---|
253 | if (nfds == 0) |
---|
254 | break; |
---|
255 | tvp = ares_timeout(channel, NULL, &tv); |
---|
256 | count = select(nfds, &read_fds, &write_fds, NULL, tvp); |
---|
257 | if (count < 0 && errno != EINVAL) |
---|
258 | { |
---|
259 | perror("select"); |
---|
260 | return 1; |
---|
261 | } |
---|
262 | ares_process(channel, &read_fds, &write_fds); |
---|
263 | } |
---|
264 | |
---|
265 | ares_destroy(channel); |
---|
266 | return 0; |
---|
267 | } |
---|
268 | |
---|
269 | static void callback(void *arg, int status, unsigned char *abuf, int alen) |
---|
270 | { |
---|
271 | char *name = (char *) arg, *errmem; |
---|
272 | int id, qr, opcode, aa, tc, rd, ra, rcode, i; |
---|
273 | unsigned int qdcount, ancount, nscount, arcount; |
---|
274 | const unsigned char *aptr; |
---|
275 | |
---|
276 | /* Display the query name if given. */ |
---|
277 | if (name) |
---|
278 | printf("Answer for query %s:\n", name); |
---|
279 | |
---|
280 | /* Display an error message if there was an error, but only stop if |
---|
281 | * we actually didn't get an answer buffer. |
---|
282 | */ |
---|
283 | if (status != ARES_SUCCESS) |
---|
284 | { |
---|
285 | printf("%s\n", ares_strerror(status, &errmem)); |
---|
286 | ares_free_errmem(errmem); |
---|
287 | if (!abuf) |
---|
288 | return; |
---|
289 | } |
---|
290 | |
---|
291 | /* Won't happen, but check anyway, for safety. */ |
---|
292 | if (alen < HFIXEDSZ) |
---|
293 | return; |
---|
294 | |
---|
295 | /* Parse the answer header. */ |
---|
296 | id = DNS_HEADER_QID(abuf); |
---|
297 | qr = DNS_HEADER_QR(abuf); |
---|
298 | opcode = DNS_HEADER_OPCODE(abuf); |
---|
299 | aa = DNS_HEADER_AA(abuf); |
---|
300 | tc = DNS_HEADER_TC(abuf); |
---|
301 | rd = DNS_HEADER_RD(abuf); |
---|
302 | ra = DNS_HEADER_RA(abuf); |
---|
303 | rcode = DNS_HEADER_RCODE(abuf); |
---|
304 | qdcount = DNS_HEADER_QDCOUNT(abuf); |
---|
305 | ancount = DNS_HEADER_ANCOUNT(abuf); |
---|
306 | nscount = DNS_HEADER_NSCOUNT(abuf); |
---|
307 | arcount = DNS_HEADER_ARCOUNT(abuf); |
---|
308 | |
---|
309 | /* Display the answer header. */ |
---|
310 | printf("id: %d\n", id); |
---|
311 | printf("flags: %s%s%s%s%s\n", |
---|
312 | qr ? "qr " : "", |
---|
313 | aa ? "aa " : "", |
---|
314 | tc ? "tc " : "", |
---|
315 | rd ? "rd " : "", |
---|
316 | ra ? "ra " : ""); |
---|
317 | printf("opcode: %s\n", opcodes[opcode]); |
---|
318 | printf("rcode: %s\n", rcodes[rcode]); |
---|
319 | |
---|
320 | /* Display the questions. */ |
---|
321 | printf("Questions:\n"); |
---|
322 | aptr = abuf + HFIXEDSZ; |
---|
323 | for (i = 0; i < qdcount; i++) |
---|
324 | { |
---|
325 | aptr = display_question(aptr, abuf, alen); |
---|
326 | if (aptr == NULL) |
---|
327 | return; |
---|
328 | } |
---|
329 | |
---|
330 | /* Display the answers. */ |
---|
331 | printf("Answers:\n"); |
---|
332 | for (i = 0; i < ancount; i++) |
---|
333 | { |
---|
334 | aptr = display_rr(aptr, abuf, alen); |
---|
335 | if (aptr == NULL) |
---|
336 | return; |
---|
337 | } |
---|
338 | |
---|
339 | /* Display the NS records. */ |
---|
340 | printf("NS records:\n"); |
---|
341 | for (i = 0; i < nscount; i++) |
---|
342 | { |
---|
343 | aptr = display_rr(aptr, abuf, alen); |
---|
344 | if (aptr == NULL) |
---|
345 | return; |
---|
346 | } |
---|
347 | |
---|
348 | /* Display the additional records. */ |
---|
349 | printf("Additional records:\n"); |
---|
350 | for (i = 0; i < arcount; i++) |
---|
351 | { |
---|
352 | aptr = display_rr(aptr, abuf, alen); |
---|
353 | if (aptr == NULL) |
---|
354 | return; |
---|
355 | } |
---|
356 | } |
---|
357 | |
---|
358 | static const unsigned char *display_question(const unsigned char *aptr, |
---|
359 | const unsigned char *abuf, |
---|
360 | int alen) |
---|
361 | { |
---|
362 | char *name; |
---|
363 | int type, class, status, len; |
---|
364 | |
---|
365 | /* Parse the question name. */ |
---|
366 | status = ares_expand_name(aptr, abuf, alen, &name, &len); |
---|
367 | if (status != ARES_SUCCESS) |
---|
368 | return NULL; |
---|
369 | aptr += len; |
---|
370 | |
---|
371 | /* Make sure there's enough data after the name for the fixed part |
---|
372 | * of the question. |
---|
373 | */ |
---|
374 | if (aptr + QFIXEDSZ > abuf + alen) |
---|
375 | { |
---|
376 | free(name); |
---|
377 | return NULL; |
---|
378 | } |
---|
379 | |
---|
380 | /* Parse the question type and class. */ |
---|
381 | type = DNS_QUESTION_TYPE(aptr); |
---|
382 | class = DNS_QUESTION_CLASS(aptr); |
---|
383 | aptr += QFIXEDSZ; |
---|
384 | |
---|
385 | /* Display the question, in a format sort of similar to how we will |
---|
386 | * display RRs. |
---|
387 | */ |
---|
388 | printf("\t%-15s.\t", name); |
---|
389 | if (class != C_IN) |
---|
390 | printf("\t%s", class_name(class)); |
---|
391 | printf("\t%s\n", type_name(type)); |
---|
392 | free(name); |
---|
393 | return aptr; |
---|
394 | } |
---|
395 | |
---|
396 | static const unsigned char *display_rr(const unsigned char *aptr, |
---|
397 | const unsigned char *abuf, int alen) |
---|
398 | { |
---|
399 | const unsigned char *p; |
---|
400 | char *name; |
---|
401 | int type, class, ttl, dlen, status, len; |
---|
402 | struct in_addr addr; |
---|
403 | |
---|
404 | /* Parse the RR name. */ |
---|
405 | status = ares_expand_name(aptr, abuf, alen, &name, &len); |
---|
406 | if (status != ARES_SUCCESS) |
---|
407 | return NULL; |
---|
408 | aptr += len; |
---|
409 | |
---|
410 | /* Make sure there is enough data after the RR name for the fixed |
---|
411 | * part of the RR. |
---|
412 | */ |
---|
413 | if (aptr + RRFIXEDSZ > abuf + alen) |
---|
414 | { |
---|
415 | free(name); |
---|
416 | return NULL; |
---|
417 | } |
---|
418 | |
---|
419 | /* Parse the fixed part of the RR, and advance to the RR data |
---|
420 | * field. */ |
---|
421 | type = DNS_RR_TYPE(aptr); |
---|
422 | class = DNS_RR_CLASS(aptr); |
---|
423 | ttl = DNS_RR_TTL(aptr); |
---|
424 | dlen = DNS_RR_LEN(aptr); |
---|
425 | aptr += RRFIXEDSZ; |
---|
426 | if (aptr + dlen > abuf + alen) |
---|
427 | { |
---|
428 | free(name); |
---|
429 | return NULL; |
---|
430 | } |
---|
431 | |
---|
432 | /* Display the RR name, class, and type. */ |
---|
433 | printf("\t%-15s.\t%d", name, ttl); |
---|
434 | if (class != C_IN) |
---|
435 | printf("\t%s", class_name(class)); |
---|
436 | printf("\t%s", type_name(type)); |
---|
437 | free(name); |
---|
438 | |
---|
439 | /* Display the RR data. Don't touch aptr. */ |
---|
440 | switch (type) |
---|
441 | { |
---|
442 | case T_CNAME: |
---|
443 | case T_MB: |
---|
444 | case T_MD: |
---|
445 | case T_MF: |
---|
446 | case T_MG: |
---|
447 | case T_MR: |
---|
448 | case T_NS: |
---|
449 | case T_PTR: |
---|
450 | /* For these types, the RR data is just a domain name. */ |
---|
451 | status = ares_expand_name(aptr, abuf, alen, &name, &len); |
---|
452 | if (status != ARES_SUCCESS) |
---|
453 | return NULL; |
---|
454 | printf("\t%s.", name); |
---|
455 | free(name); |
---|
456 | break; |
---|
457 | |
---|
458 | case T_HINFO: |
---|
459 | /* The RR data is two length-counted character strings. */ |
---|
460 | p = aptr; |
---|
461 | len = *p; |
---|
462 | if (p + len + 1 > aptr + dlen) |
---|
463 | return NULL; |
---|
464 | printf("\t%.*s", len, p + 1); |
---|
465 | p += len + 1; |
---|
466 | len = *p; |
---|
467 | if (p + len + 1 > aptr + dlen) |
---|
468 | return NULL; |
---|
469 | printf("\t%.*s", len, p + 1); |
---|
470 | break; |
---|
471 | |
---|
472 | case T_MINFO: |
---|
473 | /* The RR data is two domain names. */ |
---|
474 | p = aptr; |
---|
475 | status = ares_expand_name(p, abuf, alen, &name, &len); |
---|
476 | if (status != ARES_SUCCESS) |
---|
477 | return NULL; |
---|
478 | printf("\t%s.", name); |
---|
479 | free(name); |
---|
480 | p += len; |
---|
481 | status = ares_expand_name(p, abuf, alen, &name, &len); |
---|
482 | if (status != ARES_SUCCESS) |
---|
483 | return NULL; |
---|
484 | printf("\t%s.", name); |
---|
485 | free(name); |
---|
486 | break; |
---|
487 | |
---|
488 | case T_MX: |
---|
489 | /* The RR data is two bytes giving a preference ordering, and |
---|
490 | * then a domain name. |
---|
491 | */ |
---|
492 | if (dlen < 2) |
---|
493 | return NULL; |
---|
494 | printf("\t%d", (aptr[0] << 8) | aptr[1]); |
---|
495 | status = ares_expand_name(aptr + 2, abuf, alen, &name, &len); |
---|
496 | if (status != ARES_SUCCESS) |
---|
497 | return NULL; |
---|
498 | printf("\t%s.", name); |
---|
499 | free(name); |
---|
500 | break; |
---|
501 | |
---|
502 | case T_SOA: |
---|
503 | /* The RR data is two domain names and then five four-byte |
---|
504 | * numbers giving the serial number and some timeouts. |
---|
505 | */ |
---|
506 | p = aptr; |
---|
507 | status = ares_expand_name(p, abuf, alen, &name, &len); |
---|
508 | if (status != ARES_SUCCESS) |
---|
509 | return NULL; |
---|
510 | printf("\t%s.\n", name); |
---|
511 | free(name); |
---|
512 | p += len; |
---|
513 | status = ares_expand_name(p, abuf, alen, &name, &len); |
---|
514 | if (status != ARES_SUCCESS) |
---|
515 | return NULL; |
---|
516 | printf("\t\t\t\t\t\t%s.\n", name); |
---|
517 | free(name); |
---|
518 | p += len; |
---|
519 | if (p + 20 > aptr + dlen) |
---|
520 | return NULL; |
---|
521 | printf("\t\t\t\t\t\t( %d %d %d %d %d )", |
---|
522 | (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3], |
---|
523 | (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7], |
---|
524 | (p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11], |
---|
525 | (p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15], |
---|
526 | (p[16] << 24) | (p[17] << 16) | (p[18] << 8) | p[19]); |
---|
527 | break; |
---|
528 | |
---|
529 | case T_TXT: |
---|
530 | /* The RR data is one or more length-counted character |
---|
531 | * strings. */ |
---|
532 | p = aptr; |
---|
533 | while (p < aptr + dlen) |
---|
534 | { |
---|
535 | len = *p; |
---|
536 | if (p + len + 1 > aptr + dlen) |
---|
537 | return NULL; |
---|
538 | printf("\t%.*s", len, p + 1); |
---|
539 | p += len + 1; |
---|
540 | } |
---|
541 | break; |
---|
542 | |
---|
543 | case T_A: |
---|
544 | /* The RR data is a four-byte Internet address. */ |
---|
545 | if (dlen != 4) |
---|
546 | return NULL; |
---|
547 | memcpy(&addr, aptr, sizeof(struct in_addr)); |
---|
548 | printf("\t%s", inet_ntoa(addr)); |
---|
549 | break; |
---|
550 | |
---|
551 | case T_WKS: |
---|
552 | /* Not implemented yet */ |
---|
553 | break; |
---|
554 | } |
---|
555 | printf("\n"); |
---|
556 | |
---|
557 | return aptr + dlen; |
---|
558 | } |
---|
559 | |
---|
560 | static const char *type_name(int type) |
---|
561 | { |
---|
562 | int i; |
---|
563 | |
---|
564 | for (i = 0; i < ntypes; i++) |
---|
565 | { |
---|
566 | if (types[i].value == type) |
---|
567 | return types[i].name; |
---|
568 | } |
---|
569 | return "(unknown)"; |
---|
570 | } |
---|
571 | |
---|
572 | static const char *class_name(int class) |
---|
573 | { |
---|
574 | int i; |
---|
575 | |
---|
576 | for (i = 0; i < nclasses; i++) |
---|
577 | { |
---|
578 | if (classes[i].value == class) |
---|
579 | return classes[i].name; |
---|
580 | } |
---|
581 | return "(unknown)"; |
---|
582 | } |
---|
583 | |
---|
584 | static void usage(void) |
---|
585 | { |
---|
586 | fprintf(stderr, "usage: adig [-f flag] [-s server] [-c class] " |
---|
587 | "[-t type] [-p port] name ...\n"); |
---|
588 | exit(1); |
---|
589 | } |
---|