diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2018-04-14 14:05:45 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2018-04-14 14:05:45 +0200 |
commit | 0dd3be8c0933d5649152b6019b975876e804e22a (patch) | |
tree | 04a196f434fc9bf1b0a56243c4ffb02d8711868d | |
parent | 43dd0062229170747dcbee0a2a87b8e5ee2f09d6 (diff) | |
download | busybox-w32-0dd3be8c0933d5649152b6019b975876e804e22a.tar.gz busybox-w32-0dd3be8c0933d5649152b6019b975876e804e22a.tar.bz2 busybox-w32-0dd3be8c0933d5649152b6019b975876e804e22a.zip |
nslookup: add openwrt / lede version
Needs work on size reduction
function old new delta
nslookup_main 114 2363 +2249
parse_reply - 1022 +1022
add_ns - 663 +663
ns_parserr - 486 +486
ns_initparse - 184 +184
ns_skiprr - 117 +117
add_query - 95 +95
qtypes - 80 +80
rcodes - 68 +68
dn_skipname - 58 +58
ns_name_uncompress - 56 +56
ns_get16 - 13 +13
v4_mapped - 12 +12
ns_get32 - 9 +9
res_init 3 - -3
__res_state 6 - -6
xmalloc_sockaddr2hostonly_noport 10 - -10
gai_strerror 47 - -47
set_default_dns 95 - -95
print_host 199 - -199
static.res 512 - -512
------------------------------------------------------------------------------
(add/remove: 15/10 grow/shrink: 1/0 up/down: 5112/-872) Total: 4240 bytes
text data bss dec hex filename
921944 555 6252 928751 e2bef busybox_old
927375 555 5740 933670 e3f26 busybox_unstripped
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | networking/nslookup.c | 919 |
1 files changed, 908 insertions, 11 deletions
diff --git a/networking/nslookup.c b/networking/nslookup.c index 45c218e6c..6f8f02fc1 100644 --- a/networking/nslookup.c +++ b/networking/nslookup.c | |||
@@ -1,20 +1,20 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | |
3 | * Mini nslookup implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu | ||
6 | * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> | ||
7 | * | ||
8 | * Correct default name server display and explicit name server option | ||
9 | * added by Ben Zeckel <bzeckel@hmc.edu> June 2001 | ||
10 | * | ||
11 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
12 | */ | ||
13 | //config:config NSLOOKUP | 3 | //config:config NSLOOKUP |
14 | //config: bool "nslookup (4.5 kb)" | 4 | //config: bool "nslookup (4.5 kb)" |
15 | //config: default y | 5 | //config: default y |
16 | //config: help | 6 | //config: help |
17 | //config: nslookup is a tool to query Internet name servers. | 7 | //config: nslookup is a tool to query Internet name servers. |
8 | //config: | ||
9 | //config:config NSLOOKUP_BIG | ||
10 | //config: bool "Use internal resolver code instead of libc" | ||
11 | //config: depends on NSLOOKUP | ||
12 | //config: default y | ||
13 | //config: | ||
14 | //config:config FEATURE_NSLOOKUP_LONG_OPTIONS | ||
15 | //config: bool "Enable long options" | ||
16 | //config: default y | ||
17 | //config: depends on NSLOOKUP_BIG && LONG_OPTS | ||
18 | 18 | ||
19 | //applet:IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP)) | 19 | //applet:IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP)) |
20 | 20 | ||
@@ -35,7 +35,26 @@ | |||
35 | //usage: "Address: 127.0.0.1\n" | 35 | //usage: "Address: 127.0.0.1\n" |
36 | 36 | ||
37 | #include <resolv.h> | 37 | #include <resolv.h> |
38 | #include <net/if.h> /* for IFNAMSIZ */ | ||
39 | //#include <arpa/inet.h> | ||
40 | //#include <netdb.h> | ||
38 | #include "libbb.h" | 41 | #include "libbb.h" |
42 | #include "common_bufsiz.h" | ||
43 | |||
44 | |||
45 | #if !ENABLE_NSLOOKUP_BIG | ||
46 | |||
47 | /* | ||
48 | * Mini nslookup implementation for busybox | ||
49 | * | ||
50 | * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu | ||
51 | * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> | ||
52 | * | ||
53 | * Correct default name server display and explicit name server option | ||
54 | * added by Ben Zeckel <bzeckel@hmc.edu> June 2001 | ||
55 | * | ||
56 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
57 | */ | ||
39 | 58 | ||
40 | /* | 59 | /* |
41 | * I'm only implementing non-interactive mode; | 60 | * I'm only implementing non-interactive mode; |
@@ -207,3 +226,881 @@ int nslookup_main(int argc, char **argv) | |||
207 | 226 | ||
208 | return print_host(argv[1], "Name:"); | 227 | return print_host(argv[1], "Name:"); |
209 | } | 228 | } |
229 | |||
230 | |||
231 | #else /****** A version from LEDE / OpenWRT ******/ | ||
232 | |||
233 | /* | ||
234 | * musl compatible nslookup | ||
235 | * | ||
236 | * Copyright (C) 2017 Jo-Philipp Wich <jo@mein.io> | ||
237 | * | ||
238 | * Permission to use, copy, modify, and/or distribute this software for any | ||
239 | * purpose with or without fee is hereby granted, provided that the above | ||
240 | * copyright notice and this permission notice appear in all copies. | ||
241 | * | ||
242 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
243 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
244 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
245 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
246 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
247 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
248 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
249 | */ | ||
250 | |||
251 | #if 0 | ||
252 | # define dbg(...) fprintf(stderr, __VA_ARGS__) | ||
253 | #else | ||
254 | # define dbg(...) ((void)0) | ||
255 | #endif | ||
256 | |||
257 | struct ns { | ||
258 | const char *name; | ||
259 | len_and_sockaddr addr; | ||
260 | int failures; | ||
261 | int replies; | ||
262 | }; | ||
263 | |||
264 | struct query { | ||
265 | const char *name; | ||
266 | size_t qlen, rlen; | ||
267 | unsigned char query[512], reply[512]; | ||
268 | unsigned long latency; | ||
269 | int rcode, n_ns; | ||
270 | }; | ||
271 | |||
272 | static const struct { | ||
273 | int type; | ||
274 | const char *name; | ||
275 | } qtypes[] = { | ||
276 | { ns_t_soa, "SOA" }, | ||
277 | { ns_t_ns, "NS" }, | ||
278 | { ns_t_a, "A" }, | ||
279 | #if ENABLE_FEATURE_IPV6 | ||
280 | { ns_t_aaaa, "AAAA" }, | ||
281 | #endif | ||
282 | { ns_t_cname, "CNAME" }, | ||
283 | { ns_t_mx, "MX" }, | ||
284 | { ns_t_txt, "TXT" }, | ||
285 | { ns_t_ptr, "PTR" }, | ||
286 | { ns_t_any, "ANY" }, | ||
287 | { } | ||
288 | }; | ||
289 | |||
290 | static const char *const rcodes[] = { | ||
291 | "NOERROR", | ||
292 | "FORMERR", | ||
293 | "SERVFAIL", | ||
294 | "NXDOMAIN", | ||
295 | "NOTIMP", | ||
296 | "REFUSED", | ||
297 | "YXDOMAIN", | ||
298 | "YXRRSET", | ||
299 | "NXRRSET", | ||
300 | "NOTAUTH", | ||
301 | "NOTZONE", | ||
302 | "RESERVED11", | ||
303 | "RESERVED12", | ||
304 | "RESERVED13", | ||
305 | "RESERVED14", | ||
306 | "RESERVED15", | ||
307 | "BADVERS" | ||
308 | }; | ||
309 | |||
310 | #if ENABLE_FEATURE_IPV6 | ||
311 | static const char v4_mapped[] = { 0,0,0,0,0,0,0,0,0,0,0xff,0xff }; | ||
312 | #endif | ||
313 | |||
314 | struct globals { | ||
315 | unsigned default_port; | ||
316 | unsigned default_retry; | ||
317 | unsigned default_timeout; | ||
318 | } FIX_ALIASING; | ||
319 | #define G (*(struct globals*)bb_common_bufsiz1) | ||
320 | #define INIT_G() do { \ | ||
321 | setup_common_bufsiz(); \ | ||
322 | G.default_port = 53; \ | ||
323 | G.default_retry = 2; \ | ||
324 | G.default_timeout = 5; \ | ||
325 | } while (0) | ||
326 | |||
327 | static int | ||
328 | parse_reply(const unsigned char *msg, size_t len, int *bb_style_counter) | ||
329 | { | ||
330 | ns_msg handle; | ||
331 | ns_rr rr; | ||
332 | int i, n, rdlen; | ||
333 | const char *format = NULL; | ||
334 | char astr[INET6_ADDRSTRLEN], dname[MAXDNAME]; | ||
335 | const unsigned char *cp; | ||
336 | |||
337 | if (ns_initparse(msg, len, &handle) != 0) { | ||
338 | //fprintf(stderr, "Unable to parse reply: %s\n", strerror(errno)); | ||
339 | return -1; | ||
340 | } | ||
341 | |||
342 | for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) { | ||
343 | if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) { | ||
344 | //fprintf(stderr, "Unable to parse resource record: %s\n", strerror(errno)); | ||
345 | return -1; | ||
346 | } | ||
347 | |||
348 | if (bb_style_counter && *bb_style_counter == 1) | ||
349 | printf("Name: %s\n", ns_rr_name(rr)); | ||
350 | |||
351 | rdlen = ns_rr_rdlen(rr); | ||
352 | |||
353 | switch (ns_rr_type(rr)) | ||
354 | { | ||
355 | case ns_t_a: | ||
356 | if (rdlen != 4) { | ||
357 | //fprintf(stderr, "Unexpected A record length\n"); | ||
358 | return -1; | ||
359 | } | ||
360 | inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr)); | ||
361 | if (bb_style_counter) | ||
362 | printf("Address %d: %s\n", (*bb_style_counter)++, astr); | ||
363 | else | ||
364 | printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr); | ||
365 | break; | ||
366 | |||
367 | #if ENABLE_FEATURE_IPV6 | ||
368 | case ns_t_aaaa: | ||
369 | if (rdlen != 16) { | ||
370 | //fprintf(stderr, "Unexpected AAAA record length\n"); | ||
371 | return -1; | ||
372 | } | ||
373 | inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr)); | ||
374 | if (bb_style_counter) | ||
375 | printf("Address %d: %s\n", (*bb_style_counter)++, astr); | ||
376 | else | ||
377 | printf("%s\thas AAAA address %s\n", ns_rr_name(rr), astr); | ||
378 | break; | ||
379 | #endif | ||
380 | |||
381 | case ns_t_ns: | ||
382 | if (!format) | ||
383 | format = "%s\tnameserver = %s\n"; | ||
384 | /* fall through */ | ||
385 | |||
386 | case ns_t_cname: | ||
387 | if (!format) | ||
388 | format = "%s\tcanonical name = %s\n"; | ||
389 | /* fall through */ | ||
390 | |||
391 | case ns_t_ptr: | ||
392 | if (!format) | ||
393 | format = "%s\tname = %s\n"; | ||
394 | if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||
395 | ns_rr_rdata(rr), dname, sizeof(dname)) < 0) { | ||
396 | //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); | ||
397 | return -1; | ||
398 | } | ||
399 | printf(format, ns_rr_name(rr), dname); | ||
400 | break; | ||
401 | |||
402 | case ns_t_mx: | ||
403 | if (rdlen < 2) { | ||
404 | fprintf(stderr, "MX record too short\n"); | ||
405 | return -1; | ||
406 | } | ||
407 | n = ns_get16(ns_rr_rdata(rr)); | ||
408 | if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||
409 | ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0) { | ||
410 | //fprintf(stderr, "Cannot uncompress MX domain: %s\n", strerror(errno)); | ||
411 | return -1; | ||
412 | } | ||
413 | printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname); | ||
414 | break; | ||
415 | |||
416 | case ns_t_txt: | ||
417 | if (rdlen < 1) { | ||
418 | //fprintf(stderr, "TXT record too short\n"); | ||
419 | return -1; | ||
420 | } | ||
421 | n = *(unsigned char *)ns_rr_rdata(rr); | ||
422 | if (n > 0) { | ||
423 | memset(dname, 0, sizeof(dname)); | ||
424 | memcpy(dname, ns_rr_rdata(rr) + 1, n); | ||
425 | printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname); | ||
426 | } | ||
427 | break; | ||
428 | |||
429 | case ns_t_soa: | ||
430 | if (rdlen < 20) { | ||
431 | //fprintf(stderr, "SOA record too short\n"); | ||
432 | return -1; | ||
433 | } | ||
434 | |||
435 | printf("%s\n", ns_rr_name(rr)); | ||
436 | |||
437 | cp = ns_rr_rdata(rr); | ||
438 | n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||
439 | cp, dname, sizeof(dname)); | ||
440 | |||
441 | if (n < 0) { | ||
442 | //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); | ||
443 | return -1; | ||
444 | } | ||
445 | |||
446 | printf("\torigin = %s\n", dname); | ||
447 | cp += n; | ||
448 | |||
449 | n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||
450 | cp, dname, sizeof(dname)); | ||
451 | |||
452 | if (n < 0) { | ||
453 | //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); | ||
454 | return -1; | ||
455 | } | ||
456 | |||
457 | printf("\tmail addr = %s\n", dname); | ||
458 | cp += n; | ||
459 | |||
460 | printf("\tserial = %lu\n", ns_get32(cp)); | ||
461 | cp += 4; | ||
462 | |||
463 | printf("\trefresh = %lu\n", ns_get32(cp)); | ||
464 | cp += 4; | ||
465 | |||
466 | printf("\tretry = %lu\n", ns_get32(cp)); | ||
467 | cp += 4; | ||
468 | |||
469 | printf("\texpire = %lu\n", ns_get32(cp)); | ||
470 | cp += 4; | ||
471 | |||
472 | printf("\tminimum = %lu\n", ns_get32(cp)); | ||
473 | break; | ||
474 | |||
475 | default: | ||
476 | break; | ||
477 | } | ||
478 | } | ||
479 | |||
480 | return i; | ||
481 | } | ||
482 | |||
483 | static int parse_nsaddr(const char *addrstr, len_and_sockaddr *lsa) | ||
484 | { | ||
485 | char *hash; | ||
486 | unsigned port = G.default_port; | ||
487 | IF_FEATURE_IPV6(unsigned scope;) | ||
488 | |||
489 | dbg("%s: addrstr:'%s'\n", __func__, addrstr); | ||
490 | |||
491 | hash = strchr(addrstr, '#'); | ||
492 | |||
493 | if (hash) { | ||
494 | *hash++ = '\0'; | ||
495 | port = bb_strtou(hash, NULL, 10); | ||
496 | if (errno || port > 65535) { | ||
497 | errno = EINVAL; | ||
498 | return -1; | ||
499 | } | ||
500 | } | ||
501 | |||
502 | #if ENABLE_FEATURE_IPV6 | ||
503 | scope = 0; | ||
504 | hash = strchr(addrstr, '%'); | ||
505 | if (hash) { | ||
506 | char ifname[IFNAMSIZ]; | ||
507 | int i; | ||
508 | |||
509 | hash++; | ||
510 | for (i = 0; hash[i] != '\0' && hash[i] != '#'; i++) { | ||
511 | if (i >= IFNAMSIZ) { | ||
512 | errno = ENODEV; | ||
513 | return -1; | ||
514 | } | ||
515 | ifname[i] = hash[i]; | ||
516 | } | ||
517 | ifname[i] = '\0'; | ||
518 | |||
519 | scope = if_nametoindex(ifname); | ||
520 | if (scope == 0) { | ||
521 | errno = ENODEV; | ||
522 | return -1; | ||
523 | } | ||
524 | } | ||
525 | |||
526 | if (inet_pton(AF_INET6, addrstr, &lsa->u.sin6.sin6_addr)) { | ||
527 | lsa->u.sin6.sin6_family = AF_INET6; | ||
528 | lsa->u.sin6.sin6_port = htons(port); | ||
529 | lsa->u.sin6.sin6_scope_id = scope; | ||
530 | lsa->len = sizeof(lsa->u.sin6); | ||
531 | return 0; | ||
532 | } | ||
533 | #endif | ||
534 | |||
535 | if (inet_pton(AF_INET, addrstr, &lsa->u.sin.sin_addr)) { | ||
536 | lsa->u.sin.sin_family = AF_INET; | ||
537 | lsa->u.sin.sin_port = htons(port); | ||
538 | lsa->len = sizeof(lsa->u.sin); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | errno = EINVAL; | ||
543 | return -1; | ||
544 | } | ||
545 | |||
546 | static char *make_ptr(char resbuf[80], const char *addrstr) | ||
547 | { | ||
548 | unsigned char addr[16]; | ||
549 | int i; | ||
550 | |||
551 | #if ENABLE_FEATURE_IPV6 | ||
552 | if (inet_pton(AF_INET6, addrstr, addr)) { | ||
553 | if (memcmp(addr, v4_mapped, 12) != 0) { | ||
554 | char *ptr = resbuf; | ||
555 | for (i = 0; i < 16; i++) { | ||
556 | *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] & 0xf]; | ||
557 | *ptr++ = '.'; | ||
558 | *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] >> 4]; | ||
559 | *ptr++ = '.'; | ||
560 | } | ||
561 | strcpy(ptr, "ip6.arpa"); | ||
562 | } | ||
563 | else { | ||
564 | sprintf(resbuf, "%u.%u.%u.%u.in-addr.arpa", | ||
565 | addr[15], addr[14], addr[13], addr[12]); | ||
566 | } | ||
567 | return resbuf; | ||
568 | } | ||
569 | #endif | ||
570 | |||
571 | if (inet_pton(AF_INET, addrstr, addr)) { | ||
572 | sprintf(resbuf, "%u.%u.%u.%u.in-addr.arpa", | ||
573 | addr[3], addr[2], addr[1], addr[0]); | ||
574 | return resbuf; | ||
575 | } | ||
576 | |||
577 | return NULL; | ||
578 | } | ||
579 | |||
580 | #if ENABLE_FEATURE_IPV6 | ||
581 | static void to_v4_mapped(len_and_sockaddr *a) | ||
582 | { | ||
583 | if (a->u.sa.sa_family != AF_INET) | ||
584 | return; | ||
585 | |||
586 | /* Order is important */ | ||
587 | //FIXME: port? | ||
588 | memcpy(a->u.sin6.sin6_addr.s6_addr + 12, &a->u.sin.sin_addr, 4); | ||
589 | memcpy(a->u.sin6.sin6_addr.s6_addr, v4_mapped, 12); | ||
590 | |||
591 | a->u.sin6.sin6_family = AF_INET6; | ||
592 | a->u.sin6.sin6_flowinfo = 0; | ||
593 | a->u.sin6.sin6_scope_id = 0; | ||
594 | a->len = sizeof(a->u.sin6); | ||
595 | } | ||
596 | #endif | ||
597 | |||
598 | /* | ||
599 | * Function logic borrowed & modified from musl libc, res_msend.c | ||
600 | */ | ||
601 | static int send_queries(struct ns *ns, int n_ns, struct query *queries, int n_queries) | ||
602 | { | ||
603 | int fd; | ||
604 | int timeout = G.default_timeout * 1000, retry_interval, servfail_retry = 0; | ||
605 | len_and_sockaddr from = { }; | ||
606 | int recvlen = 0; | ||
607 | int n_replies = 0; | ||
608 | struct pollfd pfd; | ||
609 | unsigned long t0, t1, t2; | ||
610 | int nn, qn, next_query = 0; | ||
611 | |||
612 | from.u.sa.sa_family = AF_INET; | ||
613 | from.len = sizeof(from.u.sin); | ||
614 | #if ENABLE_FEATURE_IPV6 | ||
615 | for (nn = 0; nn < n_ns; nn++) { | ||
616 | if (ns[nn].addr.u.sa.sa_family == AF_INET6) { | ||
617 | from.u.sa.sa_family = AF_INET6; | ||
618 | from.len = sizeof(from.u.sin6); | ||
619 | break; | ||
620 | } | ||
621 | } | ||
622 | #endif | ||
623 | |||
624 | /* Get local address and open/bind a socket */ | ||
625 | #if ENABLE_FEATURE_IPV6 | ||
626 | fd = socket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); | ||
627 | /* Handle case where system lacks IPv6 support */ | ||
628 | if (fd < 0) { | ||
629 | if (from.u.sa.sa_family != AF_INET6 || errno != EAFNOSUPPORT) | ||
630 | bb_perror_msg_and_die("socket"); | ||
631 | fd = xsocket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); | ||
632 | from.u.sa.sa_family = AF_INET; | ||
633 | } | ||
634 | #else | ||
635 | fd = xsocket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); | ||
636 | #endif | ||
637 | xbind(fd, &from.u.sa, from.len); | ||
638 | |||
639 | #if ENABLE_FEATURE_IPV6 | ||
640 | /* Convert any IPv4 addresses in a mixed environment to v4-mapped */ | ||
641 | if (from.u.sa.sa_family == AF_INET6) { | ||
642 | setsockopt_1(fd, IPPROTO_IPV6, IPV6_V6ONLY); | ||
643 | for (nn = 0; nn < n_ns; nn++) | ||
644 | to_v4_mapped(&ns[nn].addr); | ||
645 | } | ||
646 | #endif | ||
647 | |||
648 | pfd.fd = fd; | ||
649 | pfd.events = POLLIN; | ||
650 | retry_interval = timeout / G.default_retry; | ||
651 | t0 = t2 = monotonic_ms(); | ||
652 | t1 = t2 - retry_interval; | ||
653 | |||
654 | for (; t2 - t0 < timeout; t2 = monotonic_ms()) { | ||
655 | if (t2 - t1 >= retry_interval) { | ||
656 | for (qn = 0; qn < n_queries; qn++) { | ||
657 | if (queries[qn].rlen) | ||
658 | continue; | ||
659 | |||
660 | for (nn = 0; nn < n_ns; nn++) { | ||
661 | sendto(fd, queries[qn].query, queries[qn].qlen, | ||
662 | MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len); | ||
663 | } | ||
664 | } | ||
665 | |||
666 | t1 = t2; | ||
667 | servfail_retry = 2 * n_queries; | ||
668 | } | ||
669 | |||
670 | /* Wait for a response, or until time to retry */ | ||
671 | if (poll(&pfd, 1, t1 + retry_interval - t2) <= 0) | ||
672 | continue; | ||
673 | |||
674 | while (1) { | ||
675 | recvlen = recvfrom(fd, queries[next_query].reply, | ||
676 | sizeof(queries[next_query].reply), 0, | ||
677 | &from.u.sa, &from.len | ||
678 | ); | ||
679 | |||
680 | /* read error */ | ||
681 | if (recvlen < 0) | ||
682 | break; | ||
683 | |||
684 | /* Ignore non-identifiable packets */ | ||
685 | if (recvlen < 4) | ||
686 | continue; | ||
687 | |||
688 | /* Ignore replies from addresses we didn't send to */ | ||
689 | for (nn = 0; nn < n_ns; nn++) | ||
690 | if (memcmp(&from.u.sa, &ns[nn].addr.u.sa, from.len) == 0) | ||
691 | break; | ||
692 | |||
693 | if (nn >= n_ns) | ||
694 | continue; | ||
695 | |||
696 | /* Find which query this answer goes with, if any */ | ||
697 | for (qn = next_query; qn < n_queries; qn++) | ||
698 | if (memcmp(queries[next_query].reply, queries[qn].query, 2) == 0) | ||
699 | break; | ||
700 | |||
701 | if (qn >= n_queries || queries[qn].rlen) | ||
702 | continue; | ||
703 | |||
704 | queries[qn].rcode = queries[next_query].reply[3] & 15; | ||
705 | queries[qn].latency = monotonic_ms() - t0; | ||
706 | queries[qn].n_ns = nn; | ||
707 | |||
708 | ns[nn].replies++; | ||
709 | |||
710 | /* Only accept positive or negative responses; | ||
711 | * retry immediately on server failure, and ignore | ||
712 | * all other codes such as refusal. */ | ||
713 | switch (queries[qn].rcode) { | ||
714 | case 0: | ||
715 | case 3: | ||
716 | break; | ||
717 | |||
718 | case 2: | ||
719 | if (servfail_retry && servfail_retry--) { | ||
720 | ns[nn].failures++; | ||
721 | sendto(fd, queries[qn].query, queries[qn].qlen, | ||
722 | MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len); | ||
723 | } | ||
724 | /* fall through */ | ||
725 | |||
726 | default: | ||
727 | continue; | ||
728 | } | ||
729 | |||
730 | /* Store answer */ | ||
731 | n_replies++; | ||
732 | |||
733 | queries[qn].rlen = recvlen; | ||
734 | |||
735 | if (qn == next_query) { | ||
736 | while (next_query < n_queries) { | ||
737 | if (!queries[next_query].rlen) | ||
738 | break; | ||
739 | |||
740 | next_query++; | ||
741 | } | ||
742 | } | ||
743 | else { | ||
744 | memcpy(queries[qn].reply, queries[next_query].reply, recvlen); | ||
745 | } | ||
746 | |||
747 | if (next_query >= n_queries) | ||
748 | return n_replies; | ||
749 | } | ||
750 | } | ||
751 | |||
752 | return n_replies; | ||
753 | } | ||
754 | |||
755 | ///FIXME: use standard lsa = xhost2sockaddr(host, port) instead | ||
756 | |||
757 | static struct ns *add_ns(struct ns **ns, int *n_ns, const char *addr) | ||
758 | { | ||
759 | char portstr[sizeof("65535")], *p; | ||
760 | len_and_sockaddr a = { }; | ||
761 | struct ns *tmp; | ||
762 | struct addrinfo *ai, *aip, hints = { | ||
763 | .ai_flags = AI_NUMERICSERV, | ||
764 | .ai_socktype = SOCK_DGRAM | ||
765 | }; | ||
766 | |||
767 | dbg("%s: addr:'%s'\n", __func__, addr); | ||
768 | |||
769 | if (parse_nsaddr(addr, &a)) { | ||
770 | /* Maybe we got a domain name, attempt to resolve it using the standard | ||
771 | * resolver routines */ | ||
772 | |||
773 | p = strchr(addr, '#'); | ||
774 | snprintf(portstr, sizeof(portstr), "%hu", | ||
775 | (unsigned short)(p ? strtoul(p, NULL, 10) : G.default_port)); | ||
776 | |||
777 | if (!getaddrinfo(addr, portstr, &hints, &ai)) { | ||
778 | for (aip = ai; aip; aip = aip->ai_next) { | ||
779 | if (aip->ai_addr->sa_family != AF_INET && | ||
780 | aip->ai_addr->sa_family != AF_INET6) | ||
781 | continue; | ||
782 | |||
783 | #if ! ENABLE_FEATURE_IPV6 | ||
784 | if (aip->ai_addr->sa_family != AF_INET) | ||
785 | continue; | ||
786 | #endif | ||
787 | |||
788 | tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1)); | ||
789 | |||
790 | if (!tmp) | ||
791 | return NULL; | ||
792 | |||
793 | *ns = tmp; | ||
794 | |||
795 | (*ns)[*n_ns].name = addr; | ||
796 | (*ns)[*n_ns].replies = 0; | ||
797 | (*ns)[*n_ns].failures = 0; | ||
798 | (*ns)[*n_ns].addr.len = aip->ai_addrlen; | ||
799 | |||
800 | memcpy(&(*ns)[*n_ns].addr.u.sa, aip->ai_addr, aip->ai_addrlen); | ||
801 | |||
802 | (*n_ns)++; | ||
803 | } | ||
804 | |||
805 | freeaddrinfo(ai); | ||
806 | |||
807 | return &(*ns)[*n_ns]; | ||
808 | } | ||
809 | |||
810 | return NULL; | ||
811 | } | ||
812 | |||
813 | tmp = xrealloc(*ns, sizeof(**ns) * (*n_ns + 1)); | ||
814 | *ns = tmp; | ||
815 | |||
816 | (*ns)[*n_ns].addr = a; | ||
817 | (*ns)[*n_ns].name = addr; | ||
818 | (*ns)[*n_ns].replies = 0; | ||
819 | (*ns)[*n_ns].failures = 0; | ||
820 | |||
821 | return &(*ns)[(*n_ns)++]; | ||
822 | } | ||
823 | |||
824 | static int parse_resolvconf(struct ns **ns, int *n_ns) | ||
825 | { | ||
826 | int prev_n_ns = *n_ns; | ||
827 | FILE *resolv; | ||
828 | |||
829 | resolv = fopen("/etc/resolv.conf", "r"); | ||
830 | if (resolv) { | ||
831 | char line[128], *p; | ||
832 | |||
833 | while (fgets(line, sizeof(line), resolv)) { | ||
834 | p = strtok(line, " \t\n"); | ||
835 | |||
836 | if (!p || strcmp(p, "nameserver") != 0) | ||
837 | continue; | ||
838 | |||
839 | p = strtok(NULL, " \t\n"); | ||
840 | |||
841 | if (!p) | ||
842 | continue; | ||
843 | |||
844 | if (!add_ns(ns, n_ns, xstrdup(p))) { | ||
845 | free(p); | ||
846 | break; | ||
847 | } | ||
848 | } | ||
849 | |||
850 | fclose(resolv); | ||
851 | } | ||
852 | |||
853 | return *n_ns - prev_n_ns; | ||
854 | } | ||
855 | |||
856 | static struct query *add_query(struct query **queries, int *n_queries, | ||
857 | int type, const char *dname) | ||
858 | { | ||
859 | struct query *new_q; | ||
860 | int pos; | ||
861 | ssize_t qlen; | ||
862 | |||
863 | pos = *n_queries; | ||
864 | *n_queries = pos + 1; | ||
865 | *queries = new_q = xrealloc(*queries, sizeof(**queries) * (pos + 1)); | ||
866 | |||
867 | dbg("new query#%u type %u for '%s'\n", pos, type, dname); | ||
868 | new_q += pos; | ||
869 | memset(new_q, 0, sizeof(*new_q)); | ||
870 | |||
871 | qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL, | ||
872 | new_q->query, sizeof(new_q->query)); | ||
873 | |||
874 | new_q->qlen = qlen; | ||
875 | new_q->name = dname; | ||
876 | |||
877 | return new_q; | ||
878 | } | ||
879 | |||
880 | //FIXME: use xmalloc_sockaddr2dotted[_noport]() instead of sal2str() | ||
881 | |||
882 | #define SIZEOF_SAL2STR_BUF (INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1 + 5 + 1) | ||
883 | static char *sal2str(char buf[SIZEOF_SAL2STR_BUF], len_and_sockaddr *a) | ||
884 | { | ||
885 | char *p = buf; | ||
886 | |||
887 | #if ENABLE_FEATURE_IPV6 | ||
888 | if (a->u.sa.sa_family == AF_INET6) { | ||
889 | inet_ntop(AF_INET6, &a->u.sin6.sin6_addr, buf, SIZEOF_SAL2STR_BUF); | ||
890 | p += strlen(p); | ||
891 | |||
892 | if (a->u.sin6.sin6_scope_id) { | ||
893 | if (if_indextoname(a->u.sin6.sin6_scope_id, p + 1)) { | ||
894 | *p++ = '%'; | ||
895 | p += strlen(p); | ||
896 | } | ||
897 | } | ||
898 | } else | ||
899 | #endif | ||
900 | { | ||
901 | inet_ntop(AF_INET, &a->u.sin.sin_addr, buf, SIZEOF_SAL2STR_BUF); | ||
902 | p += strlen(p); | ||
903 | } | ||
904 | |||
905 | sprintf(p, "#%hu", ntohs(a->u.sin.sin_port)); | ||
906 | |||
907 | return buf; | ||
908 | } | ||
909 | |||
910 | int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
911 | int nslookup_main(int argc UNUSED_PARAM, char **argv) | ||
912 | { | ||
913 | struct ns *ns; | ||
914 | struct query *queries; | ||
915 | llist_t *type_strings; | ||
916 | int n_ns, n_queries; | ||
917 | int bb_style_counter = 0; | ||
918 | unsigned types; | ||
919 | int rc; | ||
920 | int opts; | ||
921 | enum { | ||
922 | OPT_stats = (1 << 4), | ||
923 | }; | ||
924 | |||
925 | INIT_G(); | ||
926 | |||
927 | type_strings = NULL; | ||
928 | #if ENABLE_FEATURE_NSLOOKUP_LONG_OPTIONS | ||
929 | opts = getopt32long(argv, "^" | ||
930 | "+" /* '+': stop at first non-option (why?) */ | ||
931 | "q:*p:+r:+t:+s" | ||
932 | "\0" | ||
933 | "-1:q::", /* minimum 1 arg, -q is a list */ | ||
934 | "type\0" Required_argument "q" | ||
935 | "querytype\0" Required_argument "q" | ||
936 | "port\0" Required_argument "p" | ||
937 | "retry\0" Required_argument "r" | ||
938 | "timeout\0" Required_argument "t" | ||
939 | "stats\0" No_argument "s", | ||
940 | &type_strings, &G.default_port, | ||
941 | &G.default_retry, &G.default_timeout | ||
942 | ); | ||
943 | #else | ||
944 | opts = getopt32(argv, "^" | ||
945 | "+" /* '+': stop at first non-option (why?) */ | ||
946 | "q:*p:+r:+t:+s" | ||
947 | "\0" | ||
948 | "-1:q::", /* minimum 1 arg, -q is a list */ | ||
949 | &type_strings, &G.default_port, | ||
950 | &G.default_retry, &G.default_timeout | ||
951 | ); | ||
952 | #endif | ||
953 | if (G.default_port > 65535) | ||
954 | bb_error_msg_and_die("invalid server port"); | ||
955 | if (G.default_retry == 0) | ||
956 | bb_error_msg_and_die("invalid retry value"); | ||
957 | if (G.default_timeout == 0) | ||
958 | bb_error_msg_and_die("invalid timeout value"); | ||
959 | |||
960 | types = 0; | ||
961 | while (type_strings) { | ||
962 | int c; | ||
963 | char *ptr, *chr; | ||
964 | |||
965 | ptr = llist_pop(&type_strings); | ||
966 | |||
967 | /* skip leading text, e.g. when invoked with -querytype=AAAA */ | ||
968 | chr = strchr(ptr, '='); | ||
969 | if (chr) | ||
970 | ptr = chr + 1; | ||
971 | |||
972 | for (c = 0;; c++) { | ||
973 | if (!qtypes[c].name) | ||
974 | bb_error_msg_and_die("invalid query type \"%s\"", ptr); | ||
975 | if (strcmp(qtypes[c].name, ptr) == 0) | ||
976 | break; | ||
977 | } | ||
978 | |||
979 | types |= (1 << c); | ||
980 | } | ||
981 | |||
982 | argv += optind; | ||
983 | |||
984 | n_queries = 0; | ||
985 | queries = NULL; | ||
986 | do { | ||
987 | /* No explicit type given, guess query type. | ||
988 | * If we can convert the domain argument into a ptr (means that | ||
989 | * inet_pton() could read it) we assume a PTR request, else | ||
990 | * we issue A+AAAA queries and switch to an output format | ||
991 | * mimicking the one of the traditional nslookup applet. */ | ||
992 | if (types == 0) { | ||
993 | char *ptr; | ||
994 | char buf80[80]; | ||
995 | |||
996 | ptr = make_ptr(buf80, *argv); | ||
997 | |||
998 | if (ptr) { | ||
999 | add_query(&queries, &n_queries, T_PTR, ptr); | ||
1000 | } | ||
1001 | else { | ||
1002 | bb_style_counter = 1; | ||
1003 | add_query(&queries, &n_queries, T_A, *argv); | ||
1004 | #if ENABLE_FEATURE_IPV6 | ||
1005 | add_query(&queries, &n_queries, T_AAAA, *argv); | ||
1006 | #endif | ||
1007 | } | ||
1008 | } | ||
1009 | else { | ||
1010 | int c; | ||
1011 | for (c = 0; qtypes[c].name; c++) { | ||
1012 | if (types & (1 << c)) | ||
1013 | add_query(&queries, &n_queries, qtypes[c].type, | ||
1014 | *argv); | ||
1015 | } | ||
1016 | } | ||
1017 | argv++; | ||
1018 | } while (argv[0] && argv[1]); | ||
1019 | |||
1020 | /* Use given DNS server if present */ | ||
1021 | n_ns = 0; | ||
1022 | ns = NULL; | ||
1023 | if (argv[0]) { | ||
1024 | if (!add_ns(&ns, &n_ns, argv[0])) | ||
1025 | bb_error_msg_and_die("invalid NS server address \"%s\"", argv[0]); | ||
1026 | } else { | ||
1027 | parse_resolvconf(&ns, &n_ns); | ||
1028 | /* Fall back to localhost if we could not find NS in resolv.conf */ | ||
1029 | if (n_ns == 0) | ||
1030 | add_ns(&ns, &n_ns, "127.0.0.1"); | ||
1031 | } | ||
1032 | |||
1033 | for (rc = 0; rc < n_ns; rc++) { | ||
1034 | int c = send_queries(&ns[rc], 1, queries, n_queries); | ||
1035 | if (c < 0) | ||
1036 | bb_perror_msg_and_die("can't send queries"); | ||
1037 | if (c > 0) | ||
1038 | break; | ||
1039 | rc++; | ||
1040 | if (rc >= n_ns) { | ||
1041 | fprintf(stderr, | ||
1042 | ";; connection timed out; no servers could be reached\n\n"); | ||
1043 | return EXIT_FAILURE; | ||
1044 | } | ||
1045 | } | ||
1046 | |||
1047 | printf("Server:\t\t%s\n", ns[rc].name); | ||
1048 | { | ||
1049 | char buf[SIZEOF_SAL2STR_BUF]; | ||
1050 | printf("Address:\t%s\n", sal2str(buf, &ns[rc].addr)); | ||
1051 | } | ||
1052 | if (opts & OPT_stats) { | ||
1053 | printf("Replies:\t%d\n", ns[rc].replies); | ||
1054 | printf("Failures:\t%d\n", ns[rc].failures); | ||
1055 | } | ||
1056 | printf("\n"); | ||
1057 | |||
1058 | for (rc = 0; rc < n_queries; rc++) { | ||
1059 | int c; | ||
1060 | |||
1061 | if (opts & OPT_stats) { | ||
1062 | printf("Query #%d completed in %lums:\n", rc, queries[rc].latency); | ||
1063 | } | ||
1064 | |||
1065 | if (queries[rc].rcode != 0) { | ||
1066 | printf("** server can't find %s: %s\n", queries[rc].name, | ||
1067 | rcodes[queries[rc].rcode]); | ||
1068 | continue; | ||
1069 | } | ||
1070 | |||
1071 | c = 0; | ||
1072 | |||
1073 | if (queries[rc].rlen) { | ||
1074 | HEADER *header; | ||
1075 | |||
1076 | if (!bb_style_counter) { | ||
1077 | header = (HEADER *)queries[rc].reply; | ||
1078 | if (!header->aa) | ||
1079 | printf("Non-authoritative answer:\n"); | ||
1080 | c = parse_reply(queries[rc].reply, queries[rc].rlen, NULL); | ||
1081 | } | ||
1082 | else { | ||
1083 | c = parse_reply(queries[rc].reply, queries[rc].rlen, | ||
1084 | &bb_style_counter); | ||
1085 | } | ||
1086 | } | ||
1087 | |||
1088 | if (c == 0) | ||
1089 | printf("*** Can't find %s: No answer\n", queries[rc].name); | ||
1090 | else if (c < 0) | ||
1091 | printf("*** Can't find %s: Parse error\n", queries[rc].name); | ||
1092 | |||
1093 | if (!bb_style_counter) | ||
1094 | printf("\n"); | ||
1095 | } | ||
1096 | |||
1097 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
1098 | free(ns); | ||
1099 | if (n_queries) | ||
1100 | free(queries); | ||
1101 | } | ||
1102 | |||
1103 | return EXIT_SUCCESS; | ||
1104 | } | ||
1105 | |||
1106 | #endif | ||