1 |
/*- |
2 |
* (c) Magerya Vitaly |
3 |
* |
4 |
* Copying and distribution of this file, with or without modification, |
5 |
* are permitted in any medium without royalty provided the copyright |
6 |
* notice and this notice are preserved. This file is offered as-is, |
7 |
* without any warranty. |
8 |
*/ |
9 |
|
10 |
#include <netinet/in.h> |
11 |
|
12 |
#include <limits.h> |
13 |
#include <netdb.h> |
14 |
#include <stdio.h> |
15 |
#include <stdlib.h> |
16 |
#include <unistd.h> |
17 |
|
18 |
#include <ldns/ldns.h> |
19 |
|
20 |
/* General utilities. |
21 |
*/ |
22 |
|
23 |
static char *progname; |
24 |
|
25 |
#define countof(array) (sizeof(array)/sizeof(*(array))) |
26 |
|
27 |
static void |
28 |
die(int code, const char *fmt, ...) { |
29 |
va_list args; |
30 |
|
31 |
va_start(args, fmt); |
32 |
fprintf(stderr, "%s: ", progname); |
33 |
vfprintf(stderr, fmt, args); |
34 |
fprintf(stderr, "\n"); |
35 |
va_end(args); |
36 |
exit(code); |
37 |
} |
38 |
|
39 |
static int |
40 |
ndots(const char *name) { |
41 |
int n; |
42 |
|
43 |
for (n = 0; (name = strchr(name, '.')); n++, name++); |
44 |
return n; |
45 |
} |
46 |
|
47 |
/* General LDNS-specific utilities. |
48 |
*/ |
49 |
|
50 |
static ldns_status |
51 |
ldns_resolver_new_default(ldns_resolver **res) { |
52 |
if (ldns_resolver_new_frm_file(res, NULL) == LDNS_STATUS_OK || |
53 |
(*res = ldns_resolver_new()) != NULL) |
54 |
return LDNS_STATUS_OK; |
55 |
return LDNS_STATUS_MEM_ERR; |
56 |
} |
57 |
|
58 |
static ldns_status |
59 |
ldns_resolver_push_default_servers(ldns_resolver *res) { |
60 |
ldns_status status; |
61 |
ldns_rdf *addr; |
62 |
|
63 |
if ((status = ldns_str2rdf_a(&addr, "127.0.0.1")) != LDNS_STATUS_OK || |
64 |
(status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK) |
65 |
return ldns_rdf_deep_free(addr), status; |
66 |
ldns_rdf_deep_free(addr); |
67 |
if ((status = ldns_str2rdf_aaaa(&addr, "::1")) != LDNS_STATUS_OK || |
68 |
(status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK) |
69 |
return ldns_rdf_deep_free(addr), status; |
70 |
ldns_rdf_deep_free(addr); |
71 |
return LDNS_STATUS_OK; |
72 |
} |
73 |
|
74 |
static ldns_rdf * |
75 |
ldns_rdf_new_addr_frm_str(const char *str) { |
76 |
ldns_rdf *addr; |
77 |
|
78 |
if ((addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, str)) == NULL) |
79 |
addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, str); |
80 |
return addr; |
81 |
} |
82 |
|
83 |
static void |
84 |
ldns_resolver_remove_nameservers(ldns_resolver *res) { |
85 |
while (ldns_resolver_nameserver_count(res) > 0) |
86 |
ldns_rdf_deep_free(ldns_resolver_pop_nameserver(res)); |
87 |
} |
88 |
|
89 |
static ldns_rdf * |
90 |
ldns_rdf_reverse_a(ldns_rdf *addr, const char *base) { |
91 |
char *buf; |
92 |
int i, len; |
93 |
|
94 |
len = strlen(base); |
95 |
buf = alloca(LDNS_IP4ADDRLEN*4 + len + 1); |
96 |
for (len = i = 0; i < LDNS_IP4ADDRLEN; i++) |
97 |
len += sprintf(&buf[len], "%d.", |
98 |
(int)ldns_rdf_data(addr)[LDNS_IP4ADDRLEN - i - 1]); |
99 |
sprintf(&buf[len], "%s", base); |
100 |
return ldns_dname_new_frm_str(buf); |
101 |
} |
102 |
|
103 |
static ldns_rdf * |
104 |
ldns_rdf_reverse_aaaa(ldns_rdf *addr, const char *base) { |
105 |
char *buf; |
106 |
int i, len; |
107 |
|
108 |
len = strlen(base); |
109 |
buf = alloca(LDNS_IP6ADDRLEN*4 + len + 1); |
110 |
for (i = 0; i < LDNS_IP6ADDRLEN; i++) { |
111 |
uint8_t byte = ldns_rdf_data(addr)[LDNS_IP6ADDRLEN - i - 1]; |
112 |
sprintf(&buf[i*4], "%x.%x.", byte & 0x0F, byte >> 4); |
113 |
} |
114 |
sprintf(&buf[LDNS_IP6ADDRLEN*4], "%s", base); |
115 |
return ldns_dname_new_frm_str(buf); |
116 |
} |
117 |
|
118 |
static ldns_status |
119 |
ldns_pkt_push_rr_soa(ldns_pkt *pkt, ldns_pkt_section sec, |
120 |
const ldns_rdf *name, ldns_rr_class c, uint32_t serial) { |
121 |
ldns_rdf *rdf; |
122 |
ldns_rr *rr; |
123 |
uint32_t n; |
124 |
|
125 |
if ((rr = ldns_rr_new_frm_type(LDNS_RR_TYPE_SOA)) == NULL) |
126 |
return LDNS_STATUS_MEM_ERR; |
127 |
ldns_rr_set_class(rr, c); |
128 |
ldns_rr_set_owner(rr, ldns_rdf_clone(name)); |
129 |
ldns_rr_set_ttl(rr, 0); |
130 |
|
131 |
n = 0; |
132 |
if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, 1, &n)) == NULL) |
133 |
goto memerr; |
134 |
ldns_rr_set_rdf(rr, rdf, 0); |
135 |
ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 1); |
136 |
|
137 |
n = htonl(serial); |
138 |
if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT32, 4, &n)) == NULL) |
139 |
goto memerr; |
140 |
ldns_rr_set_rdf(rr, rdf, 2); |
141 |
|
142 |
n = 0; |
143 |
if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_PERIOD, 4, &n)) == NULL) |
144 |
goto memerr; |
145 |
ldns_rr_set_rdf(rr, rdf, 3); |
146 |
ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 4); |
147 |
ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 5); |
148 |
ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 6); |
149 |
|
150 |
if (ldns_rr_rdf(rr, 1) == NULL || ldns_rr_rdf(rr, 4) == NULL || |
151 |
ldns_rr_rdf(rr, 5) == NULL || ldns_rr_rdf(rr, 6) == NULL || |
152 |
!ldns_pkt_push_rr(pkt, sec, rr)) |
153 |
goto memerr; |
154 |
return LDNS_STATUS_OK; |
155 |
|
156 |
memerr: |
157 |
ldns_rr_free(rr); |
158 |
return LDNS_STATUS_MEM_ERR; |
159 |
} |
160 |
|
161 |
static ldns_status |
162 |
ldns_resolver_send_to(ldns_pkt **answer, ldns_resolver *res, |
163 |
const ldns_rdf *name, ldns_rr_type t, ldns_rr_class c, |
164 |
uint16_t flags, uint32_t ixfr_serial, int nameserver) { |
165 |
ldns_status status; |
166 |
ldns_pkt *qpkt; |
167 |
|
168 |
int nscnt = ldns_resolver_nameserver_count(res); |
169 |
ldns_rdf **ns = ldns_resolver_nameservers(res); |
170 |
size_t *rtt = ldns_resolver_rtt(res); |
171 |
|
172 |
ldns_resolver_set_nameservers(res, &ns[nameserver]); |
173 |
ldns_resolver_set_rtt(res, &rtt[nameserver]); |
174 |
ldns_resolver_set_nameserver_count(res, 1); |
175 |
|
176 |
status = ldns_resolver_prepare_query_pkt(&qpkt, res, name, t, c, flags); |
177 |
if (status == LDNS_STATUS_OK && t == LDNS_RR_TYPE_IXFR) |
178 |
status = ldns_pkt_push_rr_soa(qpkt, |
179 |
LDNS_SECTION_AUTHORITY, name, c, ixfr_serial); |
180 |
if (status == LDNS_STATUS_OK) |
181 |
status = ldns_resolver_send_pkt(answer, res, qpkt); |
182 |
ldns_pkt_free(qpkt); |
183 |
|
184 |
ldns_resolver_set_nameservers(res, ns); |
185 |
ldns_resolver_set_rtt(res, rtt); |
186 |
ldns_resolver_set_nameserver_count(res, nscnt); |
187 |
return status; |
188 |
} |
189 |
|
190 |
static void |
191 |
ldns_pkt_filter_answer(ldns_pkt *pkt, ldns_rr_type type) { |
192 |
int i, j, cnt; |
193 |
ldns_rr_list *rrlist; |
194 |
ldns_rr *rr; |
195 |
ldns_rr_type rrtype; |
196 |
|
197 |
rrlist = ldns_pkt_answer(pkt); |
198 |
cnt = ldns_rr_list_rr_count(rrlist); |
199 |
for (i = j = 0; i < cnt; i++) { |
200 |
rr = ldns_rr_list_rr(rrlist, i); |
201 |
rrtype = ldns_rr_get_type(rr); |
202 |
if (type == LDNS_RR_TYPE_ANY || |
203 |
type == rrtype || |
204 |
(type == LDNS_RR_TYPE_AXFR && |
205 |
(rrtype == LDNS_RR_TYPE_A || |
206 |
rrtype == LDNS_RR_TYPE_AAAA || |
207 |
rrtype == LDNS_RR_TYPE_NS || |
208 |
rrtype == LDNS_RR_TYPE_PTR))) |
209 |
ldns_rr_list_set_rr(rrlist, rr, j++); |
210 |
} |
211 |
ldns_rr_list_set_rr_count(rrlist, j); |
212 |
} |
213 |
|
214 |
/* Packet content printing. |
215 |
*/ |
216 |
|
217 |
static struct { |
218 |
ldns_rr_type type; |
219 |
const char *text; |
220 |
} rr_types[] = { |
221 |
{LDNS_RR_TYPE_A, "has address"}, |
222 |
{LDNS_RR_TYPE_NS, "name server"}, |
223 |
{LDNS_RR_TYPE_CNAME, "is an alias for"}, |
224 |
{LDNS_RR_TYPE_WKS, "has well known services"}, |
225 |
{LDNS_RR_TYPE_PTR, "domain name pointer"}, |
226 |
{LDNS_RR_TYPE_HINFO, "host information"}, |
227 |
{LDNS_RR_TYPE_MX, "mail is handled by"}, |
228 |
{LDNS_RR_TYPE_TXT, "descriptive text"}, |
229 |
{LDNS_RR_TYPE_X25, "x25 address"}, |
230 |
{LDNS_RR_TYPE_ISDN, "ISDN address"}, |
231 |
{LDNS_RR_TYPE_SIG, "has signature"}, |
232 |
{LDNS_RR_TYPE_KEY, "has key"}, |
233 |
{LDNS_RR_TYPE_AAAA, "has IPv6 address"}, |
234 |
{LDNS_RR_TYPE_LOC, "location"}, |
235 |
}; |
236 |
|
237 |
static void |
238 |
print_opcode(ldns_pkt_opcode opcode) { |
239 |
ldns_lookup_table *lt = ldns_lookup_by_id(ldns_opcodes, opcode); |
240 |
|
241 |
if (lt && lt->name) |
242 |
printf("%s", lt->name); |
243 |
else |
244 |
printf("RESERVED%d", opcode); |
245 |
} |
246 |
|
247 |
static void |
248 |
print_rcode(uint8_t rcode) { |
249 |
ldns_lookup_table *lt = ldns_lookup_by_id(ldns_rcodes, rcode); |
250 |
|
251 |
if (lt && lt->name) |
252 |
printf("%s", lt->name); |
253 |
else |
254 |
printf("RESERVED%d", rcode); |
255 |
} |
256 |
|
257 |
static int |
258 |
print_rr_type(ldns_rr_type type) { |
259 |
char *str; |
260 |
int n; |
261 |
|
262 |
str = ldns_rr_type2str(type); |
263 |
n = printf("%s", str); |
264 |
free(str); |
265 |
return n; |
266 |
} |
267 |
|
268 |
static int |
269 |
print_rr_class(ldns_rr_class cls) { |
270 |
char *str; |
271 |
int n; |
272 |
|
273 |
str = ldns_rr_class2str(cls); |
274 |
n = printf("%s", str); |
275 |
free(str); |
276 |
return n; |
277 |
} |
278 |
|
279 |
static int |
280 |
print_rdf(ldns_rdf *rdf) { |
281 |
char *str; |
282 |
int n; |
283 |
|
284 |
str = ldns_rdf2str(rdf); |
285 |
n = printf("%s", str); |
286 |
free(str); |
287 |
return n; |
288 |
} |
289 |
|
290 |
static int |
291 |
print_rdf_nodot(ldns_rdf *rdf) { |
292 |
char *str; |
293 |
int len, n; |
294 |
|
295 |
str = ldns_rdf2str(rdf); |
296 |
len = strlen(str); |
297 |
n = printf("%.*s", str[len-1] == '.' ? len-1 : len, str); |
298 |
free(str); |
299 |
return n; |
300 |
} |
301 |
|
302 |
static int |
303 |
print_padding(int fromcol, int tocol) { |
304 |
int col = fromcol, nextcol = fromcol + 8 - fromcol%8; |
305 |
|
306 |
if (fromcol + 1 > tocol) tocol = fromcol + 1; |
307 |
for (; nextcol <= tocol; col = nextcol, nextcol += 8) |
308 |
printf("\t"); |
309 |
for (; col < tocol; col++) |
310 |
printf(" "); |
311 |
return col - fromcol; |
312 |
} |
313 |
|
314 |
static void |
315 |
print_rr_verbose(ldns_rr *rr) { |
316 |
bool isq = ldns_rr_is_question(rr); |
317 |
int rdcnt = ldns_rr_rd_count(rr); |
318 |
int i, n; |
319 |
|
320 |
/* bind9-host does not count the initial ';' here */ |
321 |
n = isq ? printf(";") : 0; |
322 |
n = print_rdf(ldns_rr_owner(rr)); |
323 |
if (!isq) { |
324 |
n += print_padding(n, 24); |
325 |
n += printf("%d", ldns_rr_ttl(rr)); |
326 |
} |
327 |
n += print_padding(n, 32); |
328 |
n += print_rr_class(ldns_rr_get_class(rr)); |
329 |
n += print_padding(n, 40); |
330 |
n += print_rr_type(ldns_rr_get_type(rr)); |
331 |
for (i = 0; i < rdcnt; i++) { |
332 |
if (i == 0) print_padding(n, 48); |
333 |
else printf(" "); |
334 |
print_rdf(ldns_rr_rdf(rr, i)); |
335 |
} |
336 |
printf("\n"); |
337 |
} |
338 |
|
339 |
static void |
340 |
print_pkt_section_verbose(const char *name, ldns_rr_list *rrlist) { |
341 |
int i, cnt = ldns_rr_list_rr_count(rrlist); |
342 |
|
343 |
if (cnt == 0) |
344 |
return; |
345 |
printf(";; %s SECTION:\n", name); |
346 |
for (i = 0; i < cnt; i++) |
347 |
print_rr_verbose(ldns_rr_list_rr(rrlist, i)); |
348 |
printf("\n"); |
349 |
} |
350 |
|
351 |
static void |
352 |
print_pkt_verbose(ldns_pkt *pkt) { |
353 |
int got_flags = 0; |
354 |
|
355 |
printf(";; ->>HEADER<<- opcode: "); |
356 |
print_opcode(ldns_pkt_get_opcode(pkt)); |
357 |
printf(", status: "); |
358 |
print_rcode(ldns_pkt_get_rcode(pkt)); |
359 |
printf(", id: %u\n", ldns_pkt_id(pkt)); |
360 |
printf(";; flags:"); |
361 |
if (ldns_pkt_qr(pkt)) printf(" qr"), got_flags = 1; |
362 |
if (ldns_pkt_aa(pkt)) printf(" aa"), got_flags = 1; |
363 |
if (ldns_pkt_tc(pkt)) printf(" tc"), got_flags = 1; |
364 |
if (ldns_pkt_rd(pkt)) printf(" rd"), got_flags = 1; |
365 |
if (ldns_pkt_ra(pkt)) printf(" ra"), got_flags = 1; |
366 |
if (ldns_pkt_ad(pkt)) printf(" ad"), got_flags = 1; |
367 |
if (ldns_pkt_cd(pkt)) printf(" cd"), got_flags = 1; |
368 |
if (!got_flags) printf(" "); |
369 |
printf("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n", |
370 |
ldns_pkt_qdcount(pkt), ldns_pkt_ancount(pkt), |
371 |
ldns_pkt_nscount(pkt), ldns_pkt_arcount(pkt)); |
372 |
if (ldns_pkt_edns(pkt)) |
373 |
printf(";; EDNS: version: %u, udp=%u\n", |
374 |
ldns_pkt_edns_version(pkt), ldns_pkt_edns_udp_size(pkt)); |
375 |
printf("\n"); |
376 |
print_pkt_section_verbose("QUESTION", ldns_pkt_question(pkt)); |
377 |
print_pkt_section_verbose("ANSWER", ldns_pkt_answer(pkt)); |
378 |
print_pkt_section_verbose("AUTHORITY", ldns_pkt_authority(pkt)); |
379 |
print_pkt_section_verbose("ADDITIONAL", ldns_pkt_additional(pkt)); |
380 |
} |
381 |
|
382 |
static void |
383 |
print_rr_short(ldns_rr *rr) { |
384 |
ldns_rr_type type = ldns_rr_get_type(rr); |
385 |
size_t i, rdcnt = ldns_rr_rd_count(rr); |
386 |
|
387 |
print_rdf_nodot(ldns_rr_owner(rr)); |
388 |
printf(" "); |
389 |
for (i = 0; i < countof(rr_types); i++) { |
390 |
if (rr_types[i].type == type) { |
391 |
printf("%s", rr_types[i].text); |
392 |
goto found; |
393 |
} |
394 |
} |
395 |
|
396 |
printf("has "); |
397 |
print_rr_type(type); |
398 |
printf(" record"); |
399 |
|
400 |
found: |
401 |
for (i = 0; i < rdcnt; i++) { |
402 |
printf(" "); |
403 |
print_rdf(ldns_rr_rdf(rr, i)); |
404 |
} |
405 |
printf("\n"); |
406 |
} |
407 |
|
408 |
static void |
409 |
print_pkt_short(ldns_pkt *pkt, bool print_rr_server) { |
410 |
ldns_rr_list *rrlist = ldns_pkt_answer(pkt); |
411 |
size_t i; |
412 |
|
413 |
for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) { |
414 |
if (print_rr_server) { |
415 |
printf("Nameserver "); |
416 |
print_rdf(ldns_pkt_answerfrom(pkt)); |
417 |
printf(":\n\t"); |
418 |
} |
419 |
print_rr_short(ldns_rr_list_rr(rrlist, i)); |
420 |
} |
421 |
} |
422 |
|
423 |
static void |
424 |
print_received_line(ldns_resolver *res, ldns_pkt *pkt) { |
425 |
char *from = ldns_rdf2str(ldns_pkt_answerfrom(pkt)); |
426 |
|
427 |
printf("Received %zu bytes from %s#%d in %d ms\n", |
428 |
ldns_pkt_size(pkt), from, ldns_resolver_port(res), |
429 |
ldns_pkt_querytime(pkt)); |
430 |
free(from); |
431 |
} |
432 |
|
433 |
/* Main program. |
434 |
* |
435 |
* Note that no memory is freed below this line by intention. |
436 |
*/ |
437 |
|
438 |
#define DEFAULT_TCP_TIMEOUT 10 |
439 |
#define DEFAULT_UDP_TIMEOUT 5 |
440 |
|
441 |
enum operation_mode { M_AXFR, M_DEFAULT_Q, M_SINGLE_Q, M_SOA }; |
442 |
|
443 |
static enum operation_mode o_mode = M_DEFAULT_Q; |
444 |
static bool o_ignore_servfail = true; |
445 |
static bool o_ip6_int = false; |
446 |
static bool o_print_pkt_server = false; |
447 |
static bool o_print_rr_server = false; |
448 |
static bool o_recursive = true; |
449 |
static bool o_tcp = false; |
450 |
static bool o_verbose = false; |
451 |
static char *o_name = NULL; |
452 |
static char *o_server = NULL; |
453 |
static int o_ipversion = LDNS_RESOLV_INETANY; |
454 |
static int o_ndots = 1; |
455 |
static int o_retries = 1; |
456 |
static ldns_rr_class o_rrclass = LDNS_RR_CLASS_IN; |
457 |
static ldns_rr_type o_rrtype = LDNS_RR_TYPE_A; |
458 |
static time_t o_timeout = 0; |
459 |
static uint32_t o_ixfr_serial = 0; |
460 |
|
461 |
static void |
462 |
usage(void) { |
463 |
fputs( |
464 |
"Usage: host [-aCdilrsTvw46] [-c class] [-N ndots] [-R number]\n" |
465 |
" [-t type] [-W wait] name [server]\n" |
466 |
"\t-a same as -v -t ANY\n" |
467 |
"\t-C query SOA records from all authoritative name servers\n" |
468 |
"\t-c use this query class (IN, CH, HS, etc)\n" |
469 |
"\t-d produce verbose output, same as -v\n" |
470 |
"\t-i use IP6.INT for IPv6 reverse lookups\n" |
471 |
"\t-l list records in a zone via AXFR\n" |
472 |
"\t-N consider names with at least this many dots as absolute\n" |
473 |
"\t-R retry UDP queries this many times\n" |
474 |
"\t-r disable recursive query\n" |
475 |
"\t-s do not ignore SERVFAIL responses\n" |
476 |
"\t-T send query via TCP\n" |
477 |
"\t-t use this query type (A, AAAA, MX, etc)\n" |
478 |
"\t-v produce verbose output\n" |
479 |
"\t-w wait forever for a server reply\n" |
480 |
"\t-W wait this many seconds for a reply\n" |
481 |
"\t-4 use IPv4 only\n" |
482 |
"\t-6 use IPv6 only\n", |
483 |
stderr); |
484 |
exit(1); |
485 |
} |
486 |
|
487 |
static void |
488 |
parse_args(int argc, char *argv[]) { |
489 |
int ch; |
490 |
|
491 |
progname = argv[0]; |
492 |
while ((ch = getopt(argc, argv, "aCdilrsTvw46c:N:R:t:W:")) != -1) { |
493 |
switch (ch) { |
494 |
case 'a': |
495 |
if (o_mode != M_AXFR) |
496 |
o_mode = M_SINGLE_Q; |
497 |
o_rrtype = LDNS_RR_TYPE_ANY; |
498 |
o_verbose = true; |
499 |
break; |
500 |
case 'C': |
501 |
o_mode = M_SOA; |
502 |
o_print_rr_server = true; |
503 |
o_rrclass = LDNS_RR_CLASS_IN; |
504 |
o_rrtype = LDNS_RR_TYPE_NS; |
505 |
break; |
506 |
case 'c': |
507 |
/* bind9-host sets o_mode to M_SINGLE_Q here */ |
508 |
o_rrclass = ldns_get_rr_class_by_name(optarg); |
509 |
if (o_rrclass <= 0) |
510 |
die(2, "invalid class: %s\n", optarg); |
511 |
break; |
512 |
case 'd': o_verbose = true; break; |
513 |
case 'i': o_ip6_int = true; break; |
514 |
case 'l': |
515 |
o_mode = M_AXFR; |
516 |
o_rrtype = LDNS_RR_TYPE_AXFR; |
517 |
o_tcp = true; |
518 |
break; |
519 |
case 'N': |
520 |
o_ndots = atoi(optarg); |
521 |
if (o_ndots < 0) o_ndots = 0; |
522 |
break; |
523 |
case 'n': |
524 |
/* bind9-host accepts and ignores this option */ |
525 |
break; |
526 |
case 'r': o_recursive = 0; break; |
527 |
case 'R': |
528 |
o_retries = atoi(optarg); |
529 |
if (o_retries <= 0) o_retries = 1; |
530 |
if (o_retries > 255) o_retries = 255; |
531 |
break; |
532 |
case 's': o_ignore_servfail = false; break; |
533 |
case 'T': o_tcp = true; break; |
534 |
case 't': |
535 |
if (o_mode != M_AXFR) |
536 |
o_mode = M_SINGLE_Q; |
537 |
if (strncasecmp(optarg, "ixfr=", 5) == 0) { |
538 |
o_rrtype = LDNS_RR_TYPE_IXFR; |
539 |
o_ixfr_serial = atol(optarg + 5); |
540 |
} else { |
541 |
o_rrtype = ldns_get_rr_type_by_name(optarg); |
542 |
if (o_rrtype <= 0) |
543 |
die(2, "invalid type: %s\n", optarg); |
544 |
} |
545 |
if (o_rrtype == LDNS_RR_TYPE_AXFR || o_rrtype == LDNS_RR_TYPE_IXFR) |
546 |
o_tcp = true; |
547 |
if (o_rrtype == LDNS_RR_TYPE_AXFR) { |
548 |
o_mode = M_AXFR; |
549 |
o_rrtype = LDNS_RR_TYPE_ANY; |
550 |
o_verbose = true; |
551 |
} |
552 |
break; |
553 |
case 'v': o_verbose = true; break; |
554 |
case 'w': |
555 |
o_timeout = (time_t)INT_MAX; |
556 |
break; |
557 |
case 'W': |
558 |
o_timeout = atol(optarg); |
559 |
if (o_timeout <= 0) o_timeout = 1; |
560 |
break; |
561 |
case '4': o_ipversion = LDNS_RESOLV_INET; break; |
562 |
case '6': o_ipversion = LDNS_RESOLV_INET6; break; |
563 |
default: |
564 |
usage(); |
565 |
} |
566 |
} |
567 |
argc -= optind; |
568 |
argv += optind; |
569 |
/* bind9-host ignores arguments after the 2-nd one */ |
570 |
if (argc < 1) |
571 |
usage(); |
572 |
o_name = argv[0]; |
573 |
if (argc > 1) { |
574 |
o_server = argv[1]; |
575 |
o_print_pkt_server = true; |
576 |
} |
577 |
} |
578 |
|
579 |
static ldns_rdf* |
580 |
safe_str2rdf_dname(const char *name) { |
581 |
ldns_rdf *dname; |
582 |
ldns_status status; |
583 |
|
584 |
if ((status = ldns_str2rdf_dname(&dname, name)) != LDNS_STATUS_OK) { |
585 |
die(1, "'%s' is not a legal name (%s)", |
586 |
name, ldns_get_errorstr_by_id(status)); |
587 |
} |
588 |
return dname; |
589 |
} |
590 |
|
591 |
static ldns_rdf* |
592 |
safe_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2) { |
593 |
ldns_rdf *result = ldns_dname_cat_clone(rd1, rd2); |
594 |
|
595 |
if (!result) |
596 |
die(1, "not enought memory for a domain name"); |
597 |
/* Why doesn't ldns_dname_cat_clone check this condition? */ |
598 |
if (ldns_rdf_size(result) > LDNS_MAX_DOMAINLEN) |
599 |
die(1, "'%s' is not a legal name (%s)\n", ldns_rdf2str(result), |
600 |
ldns_get_errorstr_by_id(LDNS_STATUS_DOMAINNAME_OVERFLOW)); |
601 |
return result; |
602 |
} |
603 |
|
604 |
static bool |
605 |
query(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt) { |
606 |
ldns_status status; |
607 |
ldns_pkt_rcode rcode; |
608 |
int i, cnt; |
609 |
|
610 |
if (o_verbose) { |
611 |
printf("Trying \""); |
612 |
print_rdf_nodot(domain); |
613 |
printf("\"\n"); |
614 |
} |
615 |
for (cnt = ldns_resolver_nameserver_count(res), i = 0; i < cnt; i++) { |
616 |
status = ldns_resolver_send_to(pkt, res, domain, o_rrtype, |
617 |
o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i); |
618 |
if (status != LDNS_STATUS_OK) { |
619 |
*pkt = NULL; |
620 |
continue; |
621 |
} |
622 |
if (ldns_pkt_tc(*pkt) && !ldns_resolver_usevc(res)) { |
623 |
if (o_verbose) |
624 |
printf(";; Truncated, retrying in TCP mode.\n"); |
625 |
ldns_resolver_set_usevc(res, true); |
626 |
status = ldns_resolver_send_to(pkt, res, domain, o_rrtype, |
627 |
o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i); |
628 |
ldns_resolver_set_usevc(res, false); |
629 |
if (status != LDNS_STATUS_OK) |
630 |
continue; |
631 |
} |
632 |
rcode = ldns_pkt_get_rcode(*pkt); |
633 |
if (o_ignore_servfail && rcode == LDNS_RCODE_SERVFAIL && cnt > 1) |
634 |
continue; |
635 |
return rcode == LDNS_RCODE_NOERROR; |
636 |
} |
637 |
if (*pkt == NULL) { |
638 |
printf(";; connection timed out; no servers could be reached\n"); |
639 |
exit(1); |
640 |
} |
641 |
return false; |
642 |
} |
643 |
|
644 |
static ldns_rdf * |
645 |
search(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt, bool absolute) { |
646 |
ldns_rdf *dname, **searchlist; |
647 |
int i, n; |
648 |
|
649 |
if (absolute && query(res, domain, pkt)) |
650 |
return domain; |
651 |
|
652 |
if ((dname = ldns_resolver_domain(res)) != NULL) { |
653 |
dname = safe_dname_cat_clone(domain, dname); |
654 |
if (query(res, dname, pkt)) |
655 |
return dname; |
656 |
} |
657 |
|
658 |
searchlist = ldns_resolver_searchlist(res); |
659 |
n = ldns_resolver_searchlist_count(res); |
660 |
for (i = 0; i < n; i++) { |
661 |
dname = safe_dname_cat_clone(domain, searchlist[i]); |
662 |
if (query(res, dname, pkt)) |
663 |
return dname; |
664 |
} |
665 |
|
666 |
if (!absolute && query(res, domain, pkt)) |
667 |
return domain; |
668 |
|
669 |
return NULL; |
670 |
} |
671 |
|
672 |
static void |
673 |
report(ldns_resolver *res, ldns_rdf *domain, ldns_pkt *pkt) { |
674 |
ldns_pkt_rcode rcode; |
675 |
|
676 |
if (o_print_pkt_server) { |
677 |
printf("Using domain server:\nName: %s\nAddress: ", o_server); |
678 |
print_rdf(ldns_pkt_answerfrom(pkt)); |
679 |
printf("#%d\nAliases: \n\n", ldns_resolver_port(res)); |
680 |
o_print_pkt_server = false; |
681 |
} |
682 |
rcode = ldns_pkt_get_rcode(pkt); |
683 |
if (rcode != LDNS_RCODE_NOERROR) { |
684 |
printf("Host "); |
685 |
print_rdf_nodot(domain); |
686 |
printf(" not found: %d(", rcode); |
687 |
print_rcode(rcode); |
688 |
printf(")\n"); |
689 |
} else { |
690 |
if (o_verbose) { |
691 |
print_pkt_verbose(pkt); |
692 |
} else { |
693 |
print_pkt_short(pkt, o_print_rr_server); |
694 |
if (o_mode != M_DEFAULT_Q && |
695 |
ldns_rr_list_rr_count(ldns_pkt_answer(pkt)) == 0) { |
696 |
print_rdf_nodot(domain); |
697 |
printf(" has no "); |
698 |
print_rr_type(o_rrtype); |
699 |
printf(" record\n"); |
700 |
} |
701 |
} |
702 |
} |
703 |
if (o_verbose) |
704 |
print_received_line(res, pkt); |
705 |
} |
706 |
|
707 |
static bool |
708 |
doquery(ldns_resolver *res, ldns_rdf *domain) { |
709 |
ldns_pkt *pkt; |
710 |
bool q; |
711 |
|
712 |
q = query(res, domain, &pkt); |
713 |
report(res, domain, pkt); |
714 |
return q; |
715 |
} |
716 |
|
717 |
static bool |
718 |
doquery_filtered(ldns_resolver *res, ldns_rdf *domain) { |
719 |
ldns_pkt *pkt; |
720 |
bool q; |
721 |
|
722 |
q = query(res, domain, &pkt); |
723 |
ldns_pkt_filter_answer(pkt, o_rrtype); |
724 |
report(res, domain, pkt); |
725 |
return q; |
726 |
} |
727 |
|
728 |
static bool |
729 |
dosearch(ldns_resolver *res, ldns_rdf *domain, bool absolute) { |
730 |
ldns_pkt *pkt; |
731 |
ldns_rdf *dname; |
732 |
|
733 |
dname = search(res, domain, &pkt, absolute); |
734 |
report(res, dname != NULL ? dname : domain, pkt); |
735 |
return o_mode != M_DEFAULT_Q ? (dname != NULL) : |
736 |
(dname != NULL) && |
737 |
(o_rrtype = LDNS_RR_TYPE_AAAA, doquery_filtered(res, dname)) && |
738 |
(o_rrtype = LDNS_RR_TYPE_MX, doquery_filtered(res, dname)); |
739 |
} |
740 |
|
741 |
static bool |
742 |
doaxfr(ldns_resolver *res, ldns_rdf *domain, bool absolute) { |
743 |
ldns_pkt *pkt; |
744 |
ldns_rdf *dname; |
745 |
ldns_rr_type rrtype; |
746 |
|
747 |
rrtype = o_rrtype; |
748 |
o_rrtype = LDNS_RR_TYPE_AXFR; |
749 |
dname = search(res, domain, &pkt, absolute); |
750 |
ldns_pkt_filter_answer(pkt, rrtype); |
751 |
report(res, dname != NULL ? dname : domain, pkt); |
752 |
return dname != NULL; |
753 |
} |
754 |
|
755 |
static bool |
756 |
dosoa(ldns_resolver *res, ldns_rdf *domain, bool absolute) { |
757 |
ldns_rr_list *answer, **nsaddrs; |
758 |
ldns_rdf *dname, *addr; |
759 |
ldns_pkt *pkt; |
760 |
ldns_rr *rr; |
761 |
size_t i, j, n, cnt; |
762 |
|
763 |
if ((dname = search(res, domain, &pkt, absolute)) == NULL) |
764 |
return false; |
765 |
|
766 |
answer = ldns_pkt_answer(pkt); |
767 |
cnt = ldns_rr_list_rr_count(answer); |
768 |
nsaddrs = alloca(cnt*sizeof(*nsaddrs)); |
769 |
for (n = 0, i = 0; i < cnt; i++) |
770 |
if ((addr = ldns_rr_ns_nsdname(ldns_rr_list_rr(answer, i))) != NULL) |
771 |
nsaddrs[n++] = ldns_get_rr_list_addr_by_name(res, |
772 |
addr, LDNS_RR_CLASS_IN, 0); |
773 |
|
774 |
o_print_pkt_server = false; |
775 |
o_recursive = false; |
776 |
o_rrtype = LDNS_RR_TYPE_SOA; |
777 |
for (i = 0; i < n; i++) { |
778 |
cnt = ldns_rr_list_rr_count(nsaddrs[i]); |
779 |
for (j = 0; j < cnt; j++) { |
780 |
ldns_resolver_remove_nameservers(res); |
781 |
rr = ldns_rr_list_rr(nsaddrs[i], j); |
782 |
if ((ldns_resolver_ip6(res) == LDNS_RESOLV_INET && |
783 |
ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) || |
784 |
(ldns_resolver_ip6(res) == LDNS_RESOLV_INET6 && |
785 |
ldns_rr_get_type(rr) == LDNS_RR_TYPE_A)) |
786 |
continue; |
787 |
if (ldns_resolver_push_nameserver_rr(res, rr) == LDNS_STATUS_OK) |
788 |
/* bind9-host queries for domain, not dname here */ |
789 |
doquery(res, dname); |
790 |
} |
791 |
} |
792 |
return 0; |
793 |
} |
794 |
|
795 |
static void |
796 |
resolver_set_nameserver_hostname(ldns_resolver *res, const char *server) { |
797 |
struct addrinfo hints, *ailist, *ai; |
798 |
ldns_status status; |
799 |
ldns_rdf *rdf; |
800 |
int err; |
801 |
|
802 |
memset(&hints, 0, sizeof hints); |
803 |
switch (ldns_resolver_ip6(res)) { |
804 |
case LDNS_RESOLV_INET: hints.ai_family = PF_INET; break; |
805 |
case LDNS_RESOLV_INET6: hints.ai_family = PF_INET6; break; |
806 |
default: hints.ai_family = PF_UNSPEC; break; |
807 |
} |
808 |
hints.ai_socktype = SOCK_STREAM; |
809 |
do err = getaddrinfo(server, NULL, &hints, &ailist); |
810 |
while (err == EAI_AGAIN); |
811 |
if (err != 0) |
812 |
die(1, "couldn't get address for '%s': %s", server, gai_strerror(err)); |
813 |
for (ai = ailist; ai != NULL; ai = ai->ai_next) { |
814 |
if ((rdf = ldns_sockaddr_storage2rdf((void*)ai->ai_addr, NULL)) == NULL) |
815 |
die(1, "couldn't allocate an rdf: %s", |
816 |
ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); |
817 |
status = ldns_resolver_push_nameserver(res, rdf); |
818 |
if (status != LDNS_STATUS_OK) |
819 |
die(1, "couldn't push a nameserver address: %s", |
820 |
ldns_get_errorstr_by_id(status)); |
821 |
} |
822 |
} |
823 |
|
824 |
static void |
825 |
resolver_set_nameserver_str(ldns_resolver *res, const char *server) { |
826 |
ldns_rdf *addr; |
827 |
|
828 |
ldns_resolver_remove_nameservers(res); |
829 |
addr = ldns_rdf_new_addr_frm_str(server); |
830 |
if (addr) { |
831 |
if (ldns_resolver_push_nameserver(res, addr) != LDNS_STATUS_OK) |
832 |
die(1, "couldn't push a nameserver address"); |
833 |
} else |
834 |
resolver_set_nameserver_hostname(res, server); |
835 |
} |
836 |
|
837 |
int |
838 |
main(int argc, char *argv[]) { |
839 |
ldns_rdf *addr, *dname; |
840 |
ldns_resolver *res; |
841 |
ldns_status status; |
842 |
struct timeval restimeout; |
843 |
|
844 |
parse_args(argc, argv); |
845 |
|
846 |
status = ldns_resolver_new_default(&res); |
847 |
if (status != LDNS_STATUS_OK) |
848 |
die(1, "error creating resolver: %s", ldns_get_errorstr_by_id(status)); |
849 |
if (ldns_resolver_nameserver_count(res) == 0) |
850 |
ldns_resolver_push_default_servers(res); |
851 |
|
852 |
ldns_resolver_set_usevc(res, o_tcp); |
853 |
restimeout.tv_sec = o_timeout > 0 ? o_timeout : |
854 |
o_tcp ? DEFAULT_TCP_TIMEOUT : DEFAULT_UDP_TIMEOUT; |
855 |
restimeout.tv_usec = 0; |
856 |
ldns_resolver_set_timeout(res, restimeout); |
857 |
ldns_resolver_set_retry(res, o_retries+1); |
858 |
ldns_resolver_set_ip6(res, o_ipversion); |
859 |
ldns_resolver_set_defnames(res, false); |
860 |
ldns_resolver_set_fallback(res, false); |
861 |
|
862 |
if (o_server) |
863 |
resolver_set_nameserver_str(res, o_server); |
864 |
|
865 |
if (ldns_str2rdf_a(&addr, o_name) == LDNS_STATUS_OK) { |
866 |
dname = ldns_rdf_reverse_a(addr, "in-addr.arpa"); |
867 |
if (dname == NULL) |
868 |
die(1, "can't reverse '%s': %s", o_name, |
869 |
ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); |
870 |
o_mode = M_SINGLE_Q; |
871 |
o_rrtype = LDNS_RR_TYPE_PTR; |
872 |
return !doquery(res, dname); |
873 |
} else if (ldns_str2rdf_aaaa(&addr, o_name) == LDNS_STATUS_OK) { |
874 |
dname = ldns_rdf_reverse_aaaa(addr, o_ip6_int ? "ip6.int" : "ip6.arpa"); |
875 |
if (dname == NULL) |
876 |
die(1, "can't reverse '%s': %s", o_name, |
877 |
ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); |
878 |
o_mode = M_SINGLE_Q; |
879 |
o_rrtype = LDNS_RR_TYPE_PTR; |
880 |
return !doquery(res, dname); |
881 |
} |
882 |
return !(o_mode == M_SOA ? dosoa : o_mode == M_AXFR ? doaxfr : dosearch) |
883 |
(res, safe_str2rdf_dname(o_name), ndots(o_name) >= o_ndots); |
884 |
} |