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