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