1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37#include <resolv.h>
38#include <net/if.h>
39
40
41#include "libbb.h"
42#include "common_bufsiz.h"
43
44
45#if !ENABLE_FEATURE_NSLOOKUP_BIG
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95static int print_host(const char *hostname, const char *header)
96{
97
98
99 struct addrinfo *result = NULL;
100 int rc;
101 struct addrinfo hint;
102
103 memset(&hint, 0 , sizeof(hint));
104
105
106
107 hint.ai_socktype = SOCK_STREAM;
108
109 rc = getaddrinfo(hostname, NULL , &hint, &result);
110
111 if (rc == 0) {
112 struct addrinfo *cur = result;
113 unsigned cnt = 0;
114
115 printf("%-10s %s\n", header, hostname);
116
117 while (cur) {
118 char *dotted, *revhost;
119 dotted = xmalloc_sockaddr2dotted_noport(cur->ai_addr);
120 revhost = xmalloc_sockaddr2hostonly_noport(cur->ai_addr);
121
122 printf("Address %u: %s%c", ++cnt, dotted, revhost ? ' ' : '\n');
123 if (revhost) {
124 puts(revhost);
125 if (ENABLE_FEATURE_CLEAN_UP)
126 free(revhost);
127 }
128 if (ENABLE_FEATURE_CLEAN_UP)
129 free(dotted);
130 cur = cur->ai_next;
131 }
132 } else {
133#if ENABLE_VERBOSE_RESOLUTION_ERRORS
134 bb_error_msg("can't resolve '%s': %s", hostname, gai_strerror(rc));
135#else
136 bb_error_msg("can't resolve '%s'", hostname);
137#endif
138 }
139 if (ENABLE_FEATURE_CLEAN_UP && result)
140 freeaddrinfo(result);
141 return (rc != 0);
142}
143
144
145static void server_print(void)
146{
147 char *server;
148 struct sockaddr *sa;
149
150#if ENABLE_FEATURE_IPV6
151 sa = (struct sockaddr*)_res._u._ext.nsaddrs[0];
152 if (!sa)
153#endif
154 sa = (struct sockaddr*)&_res.nsaddr_list[0];
155 server = xmalloc_sockaddr2dotted_noport(sa);
156
157 print_host(server, "Server:");
158 if (ENABLE_FEATURE_CLEAN_UP)
159 free(server);
160 bb_putchar('\n');
161}
162
163
164
165static void set_default_dns(const char *server)
166{
167 len_and_sockaddr *lsa;
168
169 if (!server)
170 return;
171
172
173 lsa = xhost2sockaddr(server, 53);
174
175 if (lsa->u.sa.sa_family == AF_INET) {
176 _res.nscount = 1;
177
178 _res.nsaddr_list[0] = lsa->u.sin;
179 }
180#if ENABLE_FEATURE_IPV6
181
182
183
184
185 if (lsa->u.sa.sa_family == AF_INET6) {
186
187
188
189 _res._u._ext.nscount = 1;
190
191 _res._u._ext.nsaddrs[0] = &lsa->u.sin6;
192
193 }
194#endif
195}
196
197int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
198int nslookup_main(int argc, char **argv)
199{
200
201
202
203
204
205 if (!argv[1] || argv[1][0] == '-' || argc > 3)
206 bb_show_usage();
207
208
209
210 res_init();
211
212
213
214
215 set_default_dns(argv[2]);
216
217 server_print();
218
219
220
221
222
223
224
225 set_default_dns(argv[2]);
226
227 return print_host(argv[1], "Name:");
228}
229
230
231#else
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251#if 0
252# define dbg(...) fprintf(stderr, __VA_ARGS__)
253#else
254# define dbg(...) ((void)0)
255#endif
256
257struct ns {
258 const char *name;
259 len_and_sockaddr *lsa;
260
261 int replies;
262};
263
264struct query {
265 const char *name;
266 unsigned qlen;
267
268
269 unsigned char query[512];
270
271};
272
273static const struct {
274 unsigned char type;
275 char name[7];
276} qtypes[] = {
277 { ns_t_soa, "SOA" },
278 { ns_t_ns, "NS" },
279 { ns_t_a, "A" },
280#if ENABLE_FEATURE_IPV6
281 { ns_t_aaaa, "AAAA" },
282#endif
283 { ns_t_cname, "CNAME" },
284 { ns_t_mx, "MX" },
285 { ns_t_txt, "TXT" },
286 { ns_t_ptr, "PTR" },
287 { ns_t_any, "ANY" },
288};
289
290static 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 "11",
303 "12",
304 "13",
305 "14",
306 "15",
307};
308
309#if ENABLE_FEATURE_IPV6
310static const char v4_mapped[12] = { 0,0,0,0, 0,0,0,0, 0,0,0xff,0xff };
311#endif
312
313struct globals {
314 unsigned default_port;
315 unsigned default_retry;
316 unsigned default_timeout;
317 unsigned query_count;
318 unsigned serv_count;
319 struct ns *server;
320 struct query *query;
321 char *search;
322 smalluint have_search_directive;
323 smalluint exitcode;
324} FIX_ALIASING;
325#define G (*(struct globals*)bb_common_bufsiz1)
326#define INIT_G() do { \
327 setup_common_bufsiz(); \
328 G.default_port = 53; \
329 G.default_retry = 2; \
330 G.default_timeout = 5; \
331} while (0)
332
333enum {
334 OPT_debug = (1 << 0),
335};
336
337static int parse_reply(const unsigned char *msg, size_t len)
338{
339 HEADER *header;
340
341 ns_msg handle;
342 ns_rr rr;
343 int i, n, rdlen;
344 const char *format = NULL;
345 char astr[INET6_ADDRSTRLEN], dname[MAXDNAME];
346 const unsigned char *cp;
347
348 header = (HEADER *)msg;
349 if (!header->aa)
350 printf("Non-authoritative answer:\n");
351
352 if (ns_initparse(msg, len, &handle) != 0) {
353
354 return -1;
355 }
356
357 for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
358 if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) {
359
360 return -1;
361 }
362
363 rdlen = ns_rr_rdlen(rr);
364
365 switch (ns_rr_type(rr))
366 {
367 case ns_t_a:
368 if (rdlen != 4) {
369 dbg("unexpected A record length %d\n", rdlen);
370 return -1;
371 }
372 inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr));
373 printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr);
374 break;
375
376#if ENABLE_FEATURE_IPV6
377 case ns_t_aaaa:
378 if (rdlen != 16) {
379 dbg("unexpected AAAA record length %d\n", rdlen);
380 return -1;
381 }
382 inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr));
383
384 printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr);
385 break;
386#endif
387
388 case ns_t_ns:
389 if (!format)
390 format = "%s\tnameserver = %s\n";
391
392
393 case ns_t_cname:
394 if (!format)
395 format = "%s\tcanonical name = %s\n";
396
397
398 case ns_t_ptr:
399 if (!format)
400 format = "%s\tname = %s\n";
401 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
402 ns_rr_rdata(rr), dname, sizeof(dname)) < 0
403 ) {
404
405 return -1;
406 }
407 printf(format, ns_rr_name(rr), dname);
408 break;
409
410 case ns_t_mx:
411 if (rdlen < 2) {
412 printf("MX record too short\n");
413 return -1;
414 }
415 n = ns_get16(ns_rr_rdata(rr));
416 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
417 ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0
418 ) {
419
420 return -1;
421 }
422 printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname);
423 break;
424
425 case ns_t_txt:
426 if (rdlen < 1) {
427
428 return -1;
429 }
430 n = *(unsigned char *)ns_rr_rdata(rr);
431 if (n > 0) {
432 memset(dname, 0, sizeof(dname));
433 memcpy(dname, ns_rr_rdata(rr) + 1, n);
434 printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname);
435 }
436 break;
437
438 case ns_t_soa:
439 if (rdlen < 20) {
440 dbg("SOA record too short:%d\n", rdlen);
441 return -1;
442 }
443
444 printf("%s\n", ns_rr_name(rr));
445
446 cp = ns_rr_rdata(rr);
447 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
448 cp, dname, sizeof(dname));
449 if (n < 0) {
450
451 return -1;
452 }
453
454 printf("\torigin = %s\n", dname);
455 cp += n;
456
457 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
458 cp, dname, sizeof(dname));
459 if (n < 0) {
460
461 return -1;
462 }
463
464 printf("\tmail addr = %s\n", dname);
465 cp += n;
466
467 printf("\tserial = %lu\n", ns_get32(cp));
468 cp += 4;
469
470 printf("\trefresh = %lu\n", ns_get32(cp));
471 cp += 4;
472
473 printf("\tretry = %lu\n", ns_get32(cp));
474 cp += 4;
475
476 printf("\texpire = %lu\n", ns_get32(cp));
477 cp += 4;
478
479 printf("\tminimum = %lu\n", ns_get32(cp));
480 break;
481
482 default:
483 break;
484 }
485 }
486
487 return i;
488}
489
490
491
492
493
494static int send_queries(struct ns *ns)
495{
496 unsigned char reply[512];
497 uint8_t rcode;
498 len_and_sockaddr *local_lsa;
499 struct pollfd pfd;
500 int servfail_retry = 0;
501 int n_replies = 0;
502
503 unsigned retry_interval;
504 unsigned timeout = G.default_timeout * 1000;
505 unsigned tstart, tsent, tcur;
506
507 pfd.events = POLLIN;
508 pfd.fd = xsocket_type(&local_lsa, ns->lsa->u.sa.sa_family, SOCK_DGRAM);
509
510
511
512
513
514
515 xbind(pfd.fd, &local_lsa->u.sa, local_lsa->len);
516 free(local_lsa);
517
518 xconnect(pfd.fd, &ns->lsa->u.sa, ns->lsa->len);
519 ndelay_on(pfd.fd);
520
521 retry_interval = timeout / G.default_retry;
522 tstart = tcur = monotonic_ms();
523 goto send;
524
525 while (tcur - tstart < timeout) {
526 int qn;
527 int recvlen;
528
529 if (tcur - tsent >= retry_interval) {
530 send:
531 for (qn = 0; qn < G.query_count; qn++) {
532 if (G.query[qn].qlen == 0)
533 continue;
534
535 if (write(pfd.fd, G.query[qn].query, G.query[qn].qlen) < 0) {
536 bb_perror_msg("write to '%s'", ns->name);
537 n_replies = -1;
538 goto ret;
539 }
540 dbg("query %u sent\n", qn);
541 }
542 tsent = tcur;
543 servfail_retry = 2 * G.query_count;
544 }
545
546
547 if (poll(&pfd, 1, retry_interval - (tcur - tsent)) <= 0)
548 goto next;
549
550 recvlen = read(pfd.fd, reply, sizeof(reply));
551 if (recvlen < 0) {
552 bb_perror_msg("read");
553 next:
554 tcur = monotonic_ms();
555 continue;
556 }
557
558 if (ns->replies++ == 0) {
559 printf("Server:\t\t%s\n", ns->name);
560 printf("Address:\t%s\n\n",
561 auto_string(xmalloc_sockaddr2dotted(&ns->lsa->u.sa))
562 );
563
564
565 }
566
567
568 if (recvlen < 4) {
569 dbg("read is too short:%d\n", recvlen);
570 goto next;
571 }
572
573
574
575 qn = 0;
576 for (;;) {
577 if (memcmp(reply, G.query[qn].query, 2) == 0) {
578 dbg("response matches query %u\n", qn);
579 break;
580 }
581 if (++qn >= G.query_count) {
582 dbg("response does not match any query\n");
583 goto next;
584 }
585 }
586
587 if (G.query[qn].qlen == 0) {
588 dbg("dropped duplicate response to query %u\n", qn);
589 goto next;
590 }
591
592 rcode = reply[3] & 0x0f;
593 dbg("query %u rcode:%s\n", qn, rcodes[rcode]);
594
595
596 if (rcode == 2) {
597
598 if (servfail_retry) {
599 servfail_retry--;
600 write(pfd.fd, G.query[qn].query, G.query[qn].qlen);
601 dbg("query %u resent\n", qn);
602 goto next;
603 }
604 }
605
606
607 G.query[qn].qlen = 0;
608 tcur = monotonic_ms();
609#if 1
610 if (option_mask32 & OPT_debug) {
611 printf("Query #%d completed in %ums:\n", qn, tcur - tstart);
612 }
613 if (rcode != 0) {
614 printf("** server can't find %s: %s\n",
615 G.query[qn].name, rcodes[rcode]);
616 G.exitcode = EXIT_FAILURE;
617 } else {
618 if (parse_reply(reply, recvlen) < 0) {
619 printf("*** Can't find %s: Parse error\n", G.query[qn].name);
620 G.exitcode = EXIT_FAILURE;
621 }
622 }
623 bb_putchar('\n');
624 n_replies++;
625 if (n_replies >= G.query_count)
626 goto ret;
627#else
628
629 G.query[qn].latency = tcur - tstart;
630 n_replies++;
631 if (qn != save_idx) {
632
633 memcpy(G.query[qn].reply, G.query[save_idx].reply, recvlen);
634 continue;
635 }
636
637 for (;;) {
638 save_idx++;
639 if (save_idx >= G.query_count)
640 goto ret;
641 if (!G.query[save_idx].rlen)
642 break;
643 }
644#endif
645 }
646
647 ret:
648 close(pfd.fd);
649
650 return n_replies;
651}
652
653static void add_ns(const char *addr)
654{
655 struct ns *ns;
656 unsigned count;
657
658 dbg("%s: addr:'%s'\n", __func__, addr);
659
660 count = G.serv_count++;
661
662 G.server = xrealloc_vector(G.server, 3, count);
663 ns = &G.server[count];
664 ns->name = addr;
665 ns->lsa = xhost2sockaddr(addr, G.default_port);
666
667
668}
669
670static void parse_resolvconf(void)
671{
672 FILE *resolv;
673
674 resolv = fopen("/etc/resolv.conf", "r");
675 if (resolv) {
676 char line[512];
677
678 while (fgets(line, sizeof(line), resolv)) {
679 char *p, *arg;
680
681 p = strtok(line, " \t\n");
682 if (!p)
683 continue;
684 dbg("resolv_key:'%s'\n", p);
685 arg = strtok(NULL, "\n");
686 dbg("resolv_arg:'%s'\n", arg);
687 if (!arg)
688 continue;
689
690 if (strcmp(p, "domain") == 0) {
691
692 if (!G.have_search_directive)
693 goto set_search;
694 continue;
695 }
696 if (strcmp(p, "search") == 0) {
697
698 G.have_search_directive = 1;
699 set_search:
700 free(G.search);
701 G.search = xstrdup(arg);
702 dbg("search='%s'\n", G.search);
703 continue;
704 }
705
706 if (strcmp(p, "nameserver") != 0)
707 continue;
708
709
710 add_ns(xstrdup(arg));
711 }
712
713 fclose(resolv);
714 }
715
716 if (!G.search) {
717
718 char *h = safe_gethostname();
719 char *d = strchr(h, '.');
720 if (d) {
721 G.search = d + 1;
722 dbg("search='%s' (from hostname)\n", G.search);
723 }
724
725 }
726
727
728 if (G.search && LONE_CHAR(G.search, '.'))
729 G.search = NULL;
730}
731
732static void add_query(int type, const char *dname)
733{
734 struct query *new_q;
735 unsigned count;
736 ssize_t qlen;
737
738 count = G.query_count++;
739
740 G.query = xrealloc_vector(G.query, 2, count);
741 new_q = &G.query[count];
742
743 dbg("new query#%u type %u for '%s'\n", count, type, dname);
744 new_q->name = dname;
745
746 qlen = res_mkquery(QUERY, dname, C_IN, type,
747 NULL, 0,
748 NULL,
749 new_q->query, sizeof(new_q->query)
750 );
751 new_q->qlen = qlen;
752}
753
754static void add_query_with_search(int type, const char *dname)
755{
756 char *s;
757
758 if (type == T_PTR || !G.search || strchr(dname, '.')) {
759 add_query(type, dname);
760 return;
761 }
762
763 s = G.search;
764 for (;;) {
765 char *fullname, *e;
766
767 e = skip_non_whitespace(s);
768 fullname = xasprintf("%s.%.*s", dname, (int)(e - s), s);
769 add_query(type, fullname);
770 s = skip_whitespace(e);
771 if (!*s)
772 break;
773 }
774}
775
776static char *make_ptr(const char *addrstr)
777{
778 unsigned char addr[16];
779
780#if ENABLE_FEATURE_IPV6
781 if (inet_pton(AF_INET6, addrstr, addr)) {
782 if (memcmp(addr, v4_mapped, 12) != 0) {
783 int i;
784 char resbuf[80];
785 char *ptr = resbuf;
786 for (i = 0; i < 16; i++) {
787 *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] & 0xf];
788 *ptr++ = '.';
789 *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] >> 4];
790 *ptr++ = '.';
791 }
792 strcpy(ptr, "ip6.arpa");
793 return xstrdup(resbuf);
794 }
795 return xasprintf("%u.%u.%u.%u.in-addr.arpa",
796 addr[15], addr[14], addr[13], addr[12]);
797 }
798#endif
799
800 if (inet_pton(AF_INET, addrstr, addr)) {
801 return xasprintf("%u.%u.%u.%u.in-addr.arpa",
802 addr[3], addr[2], addr[1], addr[0]);
803 }
804
805 return NULL;
806}
807
808int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
809int nslookup_main(int argc UNUSED_PARAM, char **argv)
810{
811 unsigned types;
812 int rc;
813 int err;
814
815 INIT_G();
816
817
818
819
820 types = 0;
821 argv++;
822 for (;;) {
823 const char *options =
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848 "type\0"
849 "querytype\0"
850 "port\0"
851 "retry\0"
852 "debug\0"
853 "t\0"
854 "timeout\0"
855 "";
856 int i;
857 char *arg;
858 char *val;
859
860 if (!*argv)
861 bb_show_usage();
862 if (argv[0][0] != '-')
863 break;
864
865
866 arg = (*argv++) + 1;
867 val = strchrnul(arg, '=');
868 if (*val)
869 *val++ = '\0';
870
871 i = index_in_substrings(options, arg);
872
873 if (i < 0)
874 bb_show_usage();
875
876 if (i <= 1) {
877 for (i = 0;; i++) {
878 if (i == ARRAY_SIZE(qtypes))
879 bb_error_msg_and_die("invalid query type \"%s\"", val);
880 if (strcasecmp(qtypes[i].name, val) == 0)
881 break;
882 }
883 types |= (1 << i);
884 continue;
885 }
886 if (i == 2) {
887 G.default_port = xatou_range(val, 1, 0xffff);
888 }
889 if (i == 3) {
890 G.default_retry = xatou_range(val, 1, INT_MAX);
891 }
892 if (i == 4) {
893 option_mask32 |= OPT_debug;
894 }
895 if (i > 4) {
896 G.default_timeout = xatou_range(val, 1, INT_MAX / 1000);
897 }
898 }
899
900
901 if (argv[1]) {
902 if (argv[2])
903 bb_show_usage();
904 add_ns(argv[1]);
905 } else {
906 parse_resolvconf();
907
908 if (G.serv_count == 0)
909 add_ns("127.0.0.1");
910 }
911
912 if (types == 0) {
913
914
915
916
917
918
919 char *ptr;
920
921 ptr = make_ptr(argv[0]);
922 if (ptr) {
923 add_query(T_PTR, ptr);
924 } else {
925 add_query_with_search(T_A, argv[0]);
926#if ENABLE_FEATURE_IPV6
927 add_query_with_search(T_AAAA, argv[0]);
928#endif
929 }
930 } else {
931 int c;
932 for (c = 0; c < ARRAY_SIZE(qtypes); c++) {
933 if (types & (1 << c))
934 add_query_with_search(qtypes[c].type, argv[0]);
935 }
936 }
937
938 for (rc = 0; rc < G.serv_count;) {
939 int c;
940
941 c = send_queries(&G.server[rc]);
942 if (c > 0) {
943
944#if 0
945 if (option_mask32 & OPT_debug) {
946 printf("Replies:\t%d\n", G.server[rc].replies);
947 printf("Failures:\t%d\n\n", G.server[rc].failures);
948 }
949#endif
950 break;
951
952 }
953
954
955 rc++;
956 if (rc >= G.serv_count) {
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975 printf(";; connection timed out; no servers could be reached\n\n");
976 return EXIT_FAILURE;
977 }
978 }
979
980 err = 0;
981 for (rc = 0; rc < G.query_count; rc++) {
982 if (G.query[rc].qlen) {
983 printf("*** Can't find %s: No answer\n", G.query[rc].name);
984 err = 1;
985 }
986 }
987 if (err)
988 bb_putchar('\n');
989
990 if (ENABLE_FEATURE_CLEAN_UP) {
991 free(G.server);
992 free(G.query);
993 }
994
995 return G.exitcode;
996}
997
998#endif
999