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 int failures;
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} FIX_ALIASING;
324#define G (*(struct globals*)bb_common_bufsiz1)
325#define INIT_G() do { \
326 setup_common_bufsiz(); \
327 G.default_port = 53; \
328 G.default_retry = 2; \
329 G.default_timeout = 5; \
330} while (0)
331
332enum {
333 OPT_debug = (1 << 0),
334};
335
336static int parse_reply(const unsigned char *msg, size_t len)
337{
338 HEADER *header;
339
340 ns_msg handle;
341 ns_rr rr;
342 int i, n, rdlen;
343 const char *format = NULL;
344 char astr[INET6_ADDRSTRLEN], dname[MAXDNAME];
345 const unsigned char *cp;
346
347 header = (HEADER *)msg;
348 if (!header->aa)
349 printf("Non-authoritative answer:\n");
350
351 if (ns_initparse(msg, len, &handle) != 0) {
352
353 return -1;
354 }
355
356 for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
357 if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) {
358
359 return -1;
360 }
361
362 rdlen = ns_rr_rdlen(rr);
363
364 switch (ns_rr_type(rr))
365 {
366 case ns_t_a:
367 if (rdlen != 4) {
368 dbg("unexpected A record length %d\n", rdlen);
369 return -1;
370 }
371 inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr));
372 printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr);
373 break;
374
375#if ENABLE_FEATURE_IPV6
376 case ns_t_aaaa:
377 if (rdlen != 16) {
378 dbg("unexpected AAAA record length %d\n", rdlen);
379 return -1;
380 }
381 inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr));
382
383 printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr);
384 break;
385#endif
386
387 case ns_t_ns:
388 if (!format)
389 format = "%s\tnameserver = %s\n";
390
391
392 case ns_t_cname:
393 if (!format)
394 format = "%s\tcanonical name = %s\n";
395
396
397 case ns_t_ptr:
398 if (!format)
399 format = "%s\tname = %s\n";
400 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
401 ns_rr_rdata(rr), dname, sizeof(dname)) < 0
402 ) {
403
404 return -1;
405 }
406 printf(format, ns_rr_name(rr), dname);
407 break;
408
409 case ns_t_mx:
410 if (rdlen < 2) {
411 printf("MX record too short\n");
412 return -1;
413 }
414 n = ns_get16(ns_rr_rdata(rr));
415 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
416 ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0
417 ) {
418
419 return -1;
420 }
421 printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname);
422 break;
423
424 case ns_t_txt:
425 if (rdlen < 1) {
426
427 return -1;
428 }
429 n = *(unsigned char *)ns_rr_rdata(rr);
430 if (n > 0) {
431 memset(dname, 0, sizeof(dname));
432 memcpy(dname, ns_rr_rdata(rr) + 1, n);
433 printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname);
434 }
435 break;
436
437 case ns_t_soa:
438 if (rdlen < 20) {
439 dbg("SOA record too short:%d\n", rdlen);
440 return -1;
441 }
442
443 printf("%s\n", ns_rr_name(rr));
444
445 cp = ns_rr_rdata(rr);
446 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
447 cp, dname, sizeof(dname));
448 if (n < 0) {
449
450 return -1;
451 }
452
453 printf("\torigin = %s\n", dname);
454 cp += n;
455
456 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
457 cp, dname, sizeof(dname));
458 if (n < 0) {
459
460 return -1;
461 }
462
463 printf("\tmail addr = %s\n", dname);
464 cp += n;
465
466 printf("\tserial = %lu\n", ns_get32(cp));
467 cp += 4;
468
469 printf("\trefresh = %lu\n", ns_get32(cp));
470 cp += 4;
471
472 printf("\tretry = %lu\n", ns_get32(cp));
473 cp += 4;
474
475 printf("\texpire = %lu\n", ns_get32(cp));
476 cp += 4;
477
478 printf("\tminimum = %lu\n", ns_get32(cp));
479 break;
480
481 default:
482 break;
483 }
484 }
485
486 return i;
487}
488
489
490
491
492
493static int send_queries(struct ns *ns)
494{
495 unsigned char reply[512];
496 uint8_t rcode;
497 len_and_sockaddr *local_lsa;
498 struct pollfd pfd;
499 int servfail_retry = 0;
500 int n_replies = 0;
501
502 unsigned retry_interval;
503 unsigned timeout = G.default_timeout * 1000;
504 unsigned tstart, tsent, tcur;
505
506 pfd.events = POLLIN;
507 pfd.fd = xsocket_type(&local_lsa, ns->lsa->u.sa.sa_family, SOCK_DGRAM);
508
509
510
511
512
513
514 xbind(pfd.fd, &local_lsa->u.sa, local_lsa->len);
515 free(local_lsa);
516
517 xconnect(pfd.fd, &ns->lsa->u.sa, ns->lsa->len);
518 ndelay_on(pfd.fd);
519
520 retry_interval = timeout / G.default_retry;
521 tstart = tcur = monotonic_ms();
522 goto send;
523
524 while (tcur - tstart < timeout) {
525 int qn;
526 int recvlen;
527
528 if (tcur - tsent >= retry_interval) {
529 send:
530 for (qn = 0; qn < G.query_count; qn++) {
531 if (G.query[qn].qlen == 0)
532 continue;
533
534 if (write(pfd.fd, G.query[qn].query, G.query[qn].qlen) < 0) {
535 bb_perror_msg("write to '%s'", ns->name);
536 n_replies = -1;
537 goto ret;
538 }
539 dbg("query %u sent\n", qn);
540 }
541 tsent = tcur;
542 servfail_retry = 2 * G.query_count;
543 }
544
545
546 if (poll(&pfd, 1, retry_interval - (tcur - tsent)) <= 0)
547 goto next;
548
549 recvlen = read(pfd.fd, reply, sizeof(reply));
550 if (recvlen < 0) {
551 bb_perror_msg("read");
552 next:
553 tcur = monotonic_ms();
554 continue;
555 }
556
557 if (ns->replies++ == 0) {
558 printf("Server:\t\t%s\n", ns->name);
559 printf("Address:\t%s\n\n",
560 auto_string(xmalloc_sockaddr2dotted(&ns->lsa->u.sa))
561 );
562
563
564 }
565
566
567 if (recvlen < 4) {
568 dbg("read is too short:%d\n", recvlen);
569 goto next;
570 }
571
572
573
574 qn = 0;
575 for (;;) {
576 if (memcmp(reply, G.query[qn].query, 2) == 0) {
577 dbg("response matches query %u\n", qn);
578 break;
579 }
580 if (++qn >= G.query_count) {
581 dbg("response does not match any query\n");
582 goto next;
583 }
584 }
585
586 if (G.query[qn].qlen == 0) {
587 dbg("dropped duplicate response to query %u\n", qn);
588 goto next;
589 }
590
591 rcode = reply[3] & 0x0f;
592 dbg("query %u rcode:%s\n", qn, rcodes[rcode]);
593
594
595 if (rcode == 2) {
596 ns->failures++;
597 if (servfail_retry) {
598 servfail_retry--;
599 write(pfd.fd, G.query[qn].query, G.query[qn].qlen);
600 dbg("query %u resent\n", qn);
601 goto next;
602 }
603 }
604
605
606 G.query[qn].qlen = 0;
607 tcur = monotonic_ms();
608#if 1
609 if (option_mask32 & OPT_debug) {
610 printf("Query #%d completed in %ums:\n", qn, tcur - tstart);
611 }
612 if (rcode != 0) {
613 printf("** server can't find %s: %s\n",
614 G.query[qn].name, rcodes[rcode]);
615 } else {
616 if (parse_reply(reply, recvlen) < 0)
617 printf("*** Can't find %s: Parse error\n", G.query[qn].name);
618 }
619 bb_putchar('\n');
620 n_replies++;
621 if (n_replies >= G.query_count)
622 goto ret;
623#else
624
625 G.query[qn].latency = tcur - tstart;
626 n_replies++;
627 if (qn != save_idx) {
628
629 memcpy(G.query[qn].reply, G.query[save_idx].reply, recvlen);
630 continue;
631 }
632
633 for (;;) {
634 save_idx++;
635 if (save_idx >= G.query_count)
636 goto ret;
637 if (!G.query[save_idx].rlen)
638 break;
639 }
640#endif
641 }
642
643 ret:
644 close(pfd.fd);
645
646 return n_replies;
647}
648
649static void add_ns(const char *addr)
650{
651 struct ns *ns;
652 unsigned count;
653
654 dbg("%s: addr:'%s'\n", __func__, addr);
655
656 count = G.serv_count++;
657
658 G.server = xrealloc_vector(G.server, 3, count);
659 ns = &G.server[count];
660 ns->name = addr;
661 ns->lsa = xhost2sockaddr(addr, G.default_port);
662
663
664}
665
666static void parse_resolvconf(void)
667{
668 FILE *resolv;
669
670 resolv = fopen("/etc/resolv.conf", "r");
671 if (resolv) {
672 char line[512];
673
674 while (fgets(line, sizeof(line), resolv)) {
675 char *p, *arg;
676
677 p = strtok(line, " \t\n");
678 if (!p)
679 continue;
680 dbg("resolv_key:'%s'\n", p);
681 arg = strtok(NULL, "\n");
682 dbg("resolv_arg:'%s'\n", arg);
683 if (!arg)
684 continue;
685
686 if (strcmp(p, "domain") == 0) {
687
688 if (!G.have_search_directive)
689 goto set_search;
690 continue;
691 }
692 if (strcmp(p, "search") == 0) {
693
694 G.have_search_directive = 1;
695 set_search:
696 free(G.search);
697 G.search = xstrdup(arg);
698 dbg("search='%s'\n", G.search);
699 continue;
700 }
701
702 if (strcmp(p, "nameserver") != 0)
703 continue;
704
705
706 add_ns(xstrdup(arg));
707 }
708
709 fclose(resolv);
710 }
711
712 if (!G.search) {
713
714 char *h = safe_gethostname();
715 char *d = strchr(h, '.');
716 if (d) {
717 G.search = d + 1;
718 dbg("search='%s' (from hostname)\n", G.search);
719 }
720
721 }
722
723
724 if (G.search && LONE_CHAR(G.search, '.'))
725 G.search = NULL;
726}
727
728static void add_query(int type, const char *dname)
729{
730 struct query *new_q;
731 unsigned count;
732 ssize_t qlen;
733
734 count = G.query_count++;
735
736 G.query = xrealloc_vector(G.query, 2, count);
737 new_q = &G.query[count];
738
739 dbg("new query#%u type %u for '%s'\n", count, type, dname);
740 new_q->name = dname;
741
742 qlen = res_mkquery(QUERY, dname, C_IN, type,
743 NULL, 0,
744 NULL,
745 new_q->query, sizeof(new_q->query)
746 );
747 new_q->qlen = qlen;
748}
749
750static void add_query_with_search(int type, const char *dname)
751{
752 char *s;
753
754 if (type == T_PTR || !G.search || strchr(dname, '.')) {
755 add_query(type, dname);
756 return;
757 }
758
759 s = G.search;
760 for (;;) {
761 char *fullname, *e;
762
763 e = skip_non_whitespace(s);
764 fullname = xasprintf("%s.%.*s", dname, (int)(e - s), s);
765 add_query(type, fullname);
766 s = skip_whitespace(e);
767 if (!*s)
768 break;
769 }
770}
771
772static char *make_ptr(const char *addrstr)
773{
774 unsigned char addr[16];
775
776#if ENABLE_FEATURE_IPV6
777 if (inet_pton(AF_INET6, addrstr, addr)) {
778 if (memcmp(addr, v4_mapped, 12) != 0) {
779 int i;
780 char resbuf[80];
781 char *ptr = resbuf;
782 for (i = 0; i < 16; i++) {
783 *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] & 0xf];
784 *ptr++ = '.';
785 *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] >> 4];
786 *ptr++ = '.';
787 }
788 strcpy(ptr, "ip6.arpa");
789 return xstrdup(resbuf);
790 }
791 return xasprintf("%u.%u.%u.%u.in-addr.arpa",
792 addr[15], addr[14], addr[13], addr[12]);
793 }
794#endif
795
796 if (inet_pton(AF_INET, addrstr, addr)) {
797 return xasprintf("%u.%u.%u.%u.in-addr.arpa",
798 addr[3], addr[2], addr[1], addr[0]);
799 }
800
801 return NULL;
802}
803
804int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
805int nslookup_main(int argc UNUSED_PARAM, char **argv)
806{
807 unsigned types;
808 int rc;
809 int err;
810
811 INIT_G();
812
813
814
815
816 types = 0;
817 argv++;
818 for (;;) {
819 const char *options =
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844 "type\0"
845 "querytype\0"
846 "port\0"
847 "retry\0"
848 "debug\0"
849 "t\0"
850 "timeout\0"
851 "";
852 int i;
853 char *arg;
854 char *val;
855
856 if (!*argv)
857 bb_show_usage();
858 if (argv[0][0] != '-')
859 break;
860
861
862 arg = (*argv++) + 1;
863 val = strchrnul(arg, '=');
864 if (*val)
865 *val++ = '\0';
866
867 i = index_in_substrings(options, arg);
868
869 if (i < 0)
870 bb_show_usage();
871
872 if (i <= 1) {
873 for (i = 0;; i++) {
874 if (i == ARRAY_SIZE(qtypes))
875 bb_error_msg_and_die("invalid query type \"%s\"", val);
876 if (strcasecmp(qtypes[i].name, val) == 0)
877 break;
878 }
879 types |= (1 << i);
880 continue;
881 }
882 if (i == 2) {
883 G.default_port = xatou_range(val, 1, 0xffff);
884 }
885 if (i == 3) {
886 G.default_retry = xatou_range(val, 1, INT_MAX);
887 }
888 if (i == 4) {
889 option_mask32 |= OPT_debug;
890 }
891 if (i > 4) {
892 G.default_timeout = xatou_range(val, 1, INT_MAX / 1000);
893 }
894 }
895
896
897 if (argv[1]) {
898 if (argv[2])
899 bb_show_usage();
900 add_ns(argv[1]);
901 } else {
902 parse_resolvconf();
903
904 if (G.serv_count == 0)
905 add_ns("127.0.0.1");
906 }
907
908 if (types == 0) {
909
910
911
912
913
914
915 char *ptr;
916
917 ptr = make_ptr(argv[0]);
918 if (ptr) {
919 add_query(T_PTR, ptr);
920 } else {
921 add_query_with_search(T_A, argv[0]);
922#if ENABLE_FEATURE_IPV6
923 add_query_with_search(T_AAAA, argv[0]);
924#endif
925 }
926 } else {
927 int c;
928 for (c = 0; c < ARRAY_SIZE(qtypes); c++) {
929 if (types & (1 << c))
930 add_query_with_search(qtypes[c].type, argv[0]);
931 }
932 }
933
934 for (rc = 0; rc < G.serv_count;) {
935 int c;
936
937 c = send_queries(&G.server[rc]);
938 if (c > 0) {
939
940#if 0
941 if (option_mask32 & OPT_debug) {
942 printf("Replies:\t%d\n", G.server[rc].replies);
943 printf("Failures:\t%d\n\n", G.server[rc].failures);
944 }
945#endif
946 break;
947
948 }
949
950
951 rc++;
952 if (rc >= G.serv_count) {
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971 printf(";; connection timed out; no servers could be reached\n\n");
972 return EXIT_FAILURE;
973 }
974 }
975
976 err = 0;
977 for (rc = 0; rc < G.query_count; rc++) {
978 if (G.query[rc].qlen) {
979 printf("*** Can't find %s: No answer\n", G.query[rc].name);
980 err = 1;
981 }
982 }
983 if (err)
984 bb_putchar('\n');
985
986 if (ENABLE_FEATURE_CLEAN_UP) {
987 free(G.server);
988 free(G.query);
989 }
990
991 return EXIT_SUCCESS;
992}
993
994#endif
995