blob: 2e6569497c044a699dcd086a139283947f172a21 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Denys Vlasenko0dd3be82018-04-14 14:05:45 +02002
Denys Vlasenko47367e12016-11-23 09:05:14 +01003//config:config NSLOOKUP
Denys Vlasenkoae178ce2017-07-19 14:32:54 +02004//config: bool "nslookup (4.5 kb)"
Denys Vlasenko47367e12016-11-23 09:05:14 +01005//config: default y
6//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +02007//config: nslookup is a tool to query Internet name servers.
Denys Vlasenko0dd3be82018-04-14 14:05:45 +02008//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
Denys Vlasenko47367e12016-11-23 09:05:14 +010018
19//applet:IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP))
20
21//kbuild:lib-$(CONFIG_NSLOOKUP) += nslookup.o
John Beppub332e772000-01-29 12:59:01 +000022
Pere Orga5bc8c002011-04-11 03:29:49 +020023//usage:#define nslookup_trivial_usage
24//usage: "[HOST] [SERVER]"
25//usage:#define nslookup_full_usage "\n\n"
26//usage: "Query the nameserver for the IP address of the given HOST\n"
27//usage: "optionally using a specified DNS server"
28//usage:
29//usage:#define nslookup_example_usage
30//usage: "$ nslookup localhost\n"
31//usage: "Server: default\n"
32//usage: "Address: default\n"
33//usage: "\n"
34//usage: "Name: debian\n"
35//usage: "Address: 127.0.0.1\n"
36
Eric Andersendab3d462001-06-12 22:21:24 +000037#include <resolv.h>
Denys Vlasenko0dd3be82018-04-14 14:05:45 +020038#include <net/if.h> /* for IFNAMSIZ */
39//#include <arpa/inet.h>
40//#include <netdb.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000041#include "libbb.h"
Denys Vlasenko0dd3be82018-04-14 14:05:45 +020042#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 */
John Beppub332e772000-01-29 12:59:01 +000058
John Beppu50bc1012000-01-30 09:47:16 +000059/*
Denis Vlasenko391ffa12008-11-04 21:44:28 +000060 * I'm only implementing non-interactive mode;
61 * I totally forgot nslookup even had an interactive mode.
62 *
63 * This applet is the only user of res_init(). Without it,
64 * you may avoid pulling in _res global from libc.
John Beppu50bc1012000-01-30 09:47:16 +000065 */
John Beppub332e772000-01-29 12:59:01 +000066
Denis Vlasenkoebe578a2006-10-26 17:17:59 +000067/* Examples of 'standard' nslookup output
68 * $ nslookup yahoo.com
69 * Server: 128.193.0.10
70 * Address: 128.193.0.10#53
Denis Vlasenkof7996f32007-01-11 17:20:00 +000071 *
Denis Vlasenkoebe578a2006-10-26 17:17:59 +000072 * Non-authoritative answer:
73 * Name: yahoo.com
74 * Address: 216.109.112.135
75 * Name: yahoo.com
76 * Address: 66.94.234.13
77 *
78 * $ nslookup 204.152.191.37
79 * Server: 128.193.4.20
80 * Address: 128.193.4.20#53
Denis Vlasenkof7996f32007-01-11 17:20:00 +000081 *
Denis Vlasenkoebe578a2006-10-26 17:17:59 +000082 * Non-authoritative answer:
83 * 37.191.152.204.in-addr.arpa canonical name = 37.32-27.191.152.204.in-addr.arpa.
84 * 37.32-27.191.152.204.in-addr.arpa name = zeus-pub2.kernel.org.
Denis Vlasenkof7996f32007-01-11 17:20:00 +000085 *
Denis Vlasenkoebe578a2006-10-26 17:17:59 +000086 * Authoritative answers can be found from:
87 * 32-27.191.152.204.in-addr.arpa nameserver = ns1.kernel.org.
88 * 32-27.191.152.204.in-addr.arpa nameserver = ns2.kernel.org.
89 * 32-27.191.152.204.in-addr.arpa nameserver = ns3.kernel.org.
90 * ns1.kernel.org internet address = 140.211.167.34
91 * ns2.kernel.org internet address = 204.152.191.4
92 * ns3.kernel.org internet address = 204.152.191.36
93 */
John Beppub332e772000-01-29 12:59:01 +000094
Denis Vlasenkoebe578a2006-10-26 17:17:59 +000095static int print_host(const char *hostname, const char *header)
John Beppub332e772000-01-29 12:59:01 +000096{
Denis Vlasenko42823d52007-02-04 02:39:08 +000097 /* We can't use xhost2sockaddr() - we want to get ALL addresses,
Denis Vlasenko448f0242007-01-22 22:43:05 +000098 * not just one */
Denis Vlasenko448f0242007-01-22 22:43:05 +000099 struct addrinfo *result = NULL;
100 int rc;
101 struct addrinfo hint;
102
103 memset(&hint, 0 , sizeof(hint));
104 /* hint.ai_family = AF_UNSPEC; - zero anyway */
105 /* Needed. Or else we will get each address thrice (or more)
106 * for each possible socket type (tcp,udp,raw...): */
107 hint.ai_socktype = SOCK_STREAM;
108 // hint.ai_flags = AI_CANONNAME;
109 rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result);
110
Vitaly Magerya7f4b7692011-03-22 20:14:26 +0100111 if (rc == 0) {
Denis Vlasenko448f0242007-01-22 22:43:05 +0000112 struct addrinfo *cur = result;
113 unsigned cnt = 0;
114
115 printf("%-10s %s\n", header, hostname);
Denis Vlasenkofeb7ae72007-10-01 12:05:12 +0000116 // puts(cur->ai_canonname); ?
Denis Vlasenko448f0242007-01-22 22:43:05 +0000117 while (cur) {
118 char *dotted, *revhost;
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000119 dotted = xmalloc_sockaddr2dotted_noport(cur->ai_addr);
120 revhost = xmalloc_sockaddr2hostonly_noport(cur->ai_addr);
Denis Vlasenko448f0242007-01-22 22:43:05 +0000121
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
Denis Vlasenko5de9e9c2007-01-22 22:46:04 +0000134 bb_error_msg("can't resolve '%s': %s", hostname, gai_strerror(rc));
Denis Vlasenko448f0242007-01-22 22:43:05 +0000135#else
136 bb_error_msg("can't resolve '%s'", hostname);
137#endif
138 }
Vitaly Magerya7f4b7692011-03-22 20:14:26 +0100139 if (ENABLE_FEATURE_CLEAN_UP && result)
Denis Vlasenko448f0242007-01-22 22:43:05 +0000140 freeaddrinfo(result);
141 return (rc != 0);
Denis Vlasenko448f0242007-01-22 22:43:05 +0000142}
143
Denis Vlasenko448f0242007-01-22 22:43:05 +0000144/* lookup the default nameserver and display it */
145static void server_print(void)
146{
147 char *server;
Denis Vlasenko3f5f2462008-11-16 19:02:26 +0000148 struct sockaddr *sa;
Denis Vlasenko448f0242007-01-22 22:43:05 +0000149
Denis Vlasenko3f5f2462008-11-16 19:02:26 +0000150#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
Denis Vlasenko448f0242007-01-22 22:43:05 +0000157 print_host(server, "Server:");
158 if (ENABLE_FEATURE_CLEAN_UP)
159 free(server);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000160 bb_putchar('\n');
John Beppub332e772000-01-29 12:59:01 +0000161}
162
Robert Griebl31a2e202002-07-24 00:56:56 +0000163/* alter the global _res nameserver structure to use
Denis Vlasenko391ffa12008-11-04 21:44:28 +0000164 an explicit dns server instead of what is in /etc/resolv.conf */
Denis Vlasenko3f5f2462008-11-16 19:02:26 +0000165static void set_default_dns(const char *server)
Robert Griebl31a2e202002-07-24 00:56:56 +0000166{
Denis Vlasenko3f5f2462008-11-16 19:02:26 +0000167 len_and_sockaddr *lsa;
Robert Griebl31a2e202002-07-24 00:56:56 +0000168
Denys Vlasenkod66eb902013-06-27 01:09:51 +0200169 if (!server)
170 return;
171
Denis Vlasenko3f5f2462008-11-16 19:02:26 +0000172 /* NB: this works even with, say, "[::1]:5353"! :) */
173 lsa = xhost2sockaddr(server, 53);
174
175 if (lsa->u.sa.sa_family == AF_INET) {
Robert Griebl31a2e202002-07-24 00:56:56 +0000176 _res.nscount = 1;
Denis Vlasenko3f5f2462008-11-16 19:02:26 +0000177 /* struct copy */
178 _res.nsaddr_list[0] = lsa->u.sin;
Robert Griebl31a2e202002-07-24 00:56:56 +0000179 }
Denis Vlasenko3f5f2462008-11-16 19:02:26 +0000180#if ENABLE_FEATURE_IPV6
Denis Vlasenko249d9482008-11-17 15:36:36 +0000181 /* Hoped libc can cope with IPv4 address there too.
182 * No such luck, glibc 2.4 segfaults even with IPv6,
183 * maybe I misunderstand how to make glibc use IPv6 addr?
184 * (uclibc 0.9.31+ should work) */
185 if (lsa->u.sa.sa_family == AF_INET6) {
186 // glibc neither SEGVs nor sends any dgrams with this
187 // (strace shows no socket ops):
188 //_res.nscount = 0;
Denis Vlasenko3f5f2462008-11-16 19:02:26 +0000189 _res._u._ext.nscount = 1;
190 /* store a pointer to part of malloc'ed lsa */
191 _res._u._ext.nsaddrs[0] = &lsa->u.sin6;
192 /* must not free(lsa)! */
Denis Vlasenko249d9482008-11-17 15:36:36 +0000193 }
Denis Vlasenko3f5f2462008-11-16 19:02:26 +0000194#endif
Eric Andersen39cdf4e2004-01-30 22:40:05 +0000195}
Robert Griebl31a2e202002-07-24 00:56:56 +0000196
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000197int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000198int nslookup_main(int argc, char **argv)
John Beppub332e772000-01-29 12:59:01 +0000199{
Denis Vlasenko448f0242007-01-22 22:43:05 +0000200 /* We allow 1 or 2 arguments.
201 * The first is the name to be looked up and the second is an
202 * optional DNS server with which to do the lookup.
203 * More than 3 arguments is an error to follow the pattern of the
204 * standard nslookup */
Denis Vlasenko391ffa12008-11-04 21:44:28 +0000205 if (!argv[1] || argv[1][0] == '-' || argc > 3)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000206 bb_show_usage();
Denis Vlasenko448f0242007-01-22 22:43:05 +0000207
208 /* initialize DNS structure _res used in printing the default
209 * name server and in the explicit name server option feature. */
210 res_init();
211 /* rfc2133 says this enables IPv6 lookups */
Denis Vlasenko391ffa12008-11-04 21:44:28 +0000212 /* (but it also says "may be enabled in /etc/resolv.conf") */
Denis Vlasenko448f0242007-01-22 22:43:05 +0000213 /*_res.options |= RES_USE_INET6;*/
214
Denys Vlasenkod66eb902013-06-27 01:09:51 +0200215 set_default_dns(argv[2]);
Robert Griebl31a2e202002-07-24 00:56:56 +0000216
Eric Andersenfe9888a2001-01-20 21:51:21 +0000217 server_print();
Denys Vlasenkod66eb902013-06-27 01:09:51 +0200218
219 /* getaddrinfo and friends are free to request a resolver
220 * reinitialization. Just in case, set_default_dns() again
221 * after getaddrinfo (in server_print). This reportedly helps
222 * with bug 675 "nslookup does not properly use second argument"
223 * at least on Debian Wheezy and Openwrt AA (eglibc based).
224 */
225 set_default_dns(argv[2]);
226
Denis Vlasenko448f0242007-01-22 22:43:05 +0000227 return print_host(argv[1], "Name:");
John Beppub332e772000-01-29 12:59:01 +0000228}
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200229
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
257struct ns {
258 const char *name;
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200259 len_and_sockaddr *lsa;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200260 int failures;
261 int replies;
262};
263
264struct query {
265 const char *name;
266 size_t qlen, rlen;
267 unsigned char query[512], reply[512];
268 unsigned long latency;
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200269 int rcode;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200270};
271
272static 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
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 "RESERVED11",
303 "RESERVED12",
304 "RESERVED13",
305 "RESERVED14",
306 "RESERVED15",
307 "BADVERS"
308};
309
310#if ENABLE_FEATURE_IPV6
311static const char v4_mapped[] = { 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;
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200318 unsigned serv_count;
319 struct ns *server;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200320} FIX_ALIASING;
321#define G (*(struct globals*)bb_common_bufsiz1)
322#define INIT_G() do { \
323 setup_common_bufsiz(); \
324 G.default_port = 53; \
325 G.default_retry = 2; \
326 G.default_timeout = 5; \
327} while (0)
328
329static int
330parse_reply(const unsigned char *msg, size_t len, int *bb_style_counter)
331{
332 ns_msg handle;
333 ns_rr rr;
334 int i, n, rdlen;
335 const char *format = NULL;
336 char astr[INET6_ADDRSTRLEN], dname[MAXDNAME];
337 const unsigned char *cp;
338
339 if (ns_initparse(msg, len, &handle) != 0) {
340 //fprintf(stderr, "Unable to parse reply: %s\n", strerror(errno));
341 return -1;
342 }
343
344 for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
345 if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) {
346 //fprintf(stderr, "Unable to parse resource record: %s\n", strerror(errno));
347 return -1;
348 }
349
350 if (bb_style_counter && *bb_style_counter == 1)
351 printf("Name: %s\n", ns_rr_name(rr));
352
353 rdlen = ns_rr_rdlen(rr);
354
355 switch (ns_rr_type(rr))
356 {
357 case ns_t_a:
358 if (rdlen != 4) {
359 //fprintf(stderr, "Unexpected A record length\n");
360 return -1;
361 }
362 inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr));
363 if (bb_style_counter)
364 printf("Address %d: %s\n", (*bb_style_counter)++, astr);
365 else
366 printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr);
367 break;
368
369#if ENABLE_FEATURE_IPV6
370 case ns_t_aaaa:
371 if (rdlen != 16) {
372 //fprintf(stderr, "Unexpected AAAA record length\n");
373 return -1;
374 }
375 inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr));
376 if (bb_style_counter)
377 printf("Address %d: %s\n", (*bb_style_counter)++, astr);
378 else
379 printf("%s\thas AAAA address %s\n", ns_rr_name(rr), astr);
380 break;
381#endif
382
383 case ns_t_ns:
384 if (!format)
385 format = "%s\tnameserver = %s\n";
386 /* fall through */
387
388 case ns_t_cname:
389 if (!format)
390 format = "%s\tcanonical name = %s\n";
391 /* fall through */
392
393 case ns_t_ptr:
394 if (!format)
395 format = "%s\tname = %s\n";
396 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
397 ns_rr_rdata(rr), dname, sizeof(dname)) < 0) {
398 //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
399 return -1;
400 }
401 printf(format, ns_rr_name(rr), dname);
402 break;
403
404 case ns_t_mx:
405 if (rdlen < 2) {
406 fprintf(stderr, "MX record too short\n");
407 return -1;
408 }
409 n = ns_get16(ns_rr_rdata(rr));
410 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
411 ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0) {
412 //fprintf(stderr, "Cannot uncompress MX domain: %s\n", strerror(errno));
413 return -1;
414 }
415 printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname);
416 break;
417
418 case ns_t_txt:
419 if (rdlen < 1) {
420 //fprintf(stderr, "TXT record too short\n");
421 return -1;
422 }
423 n = *(unsigned char *)ns_rr_rdata(rr);
424 if (n > 0) {
425 memset(dname, 0, sizeof(dname));
426 memcpy(dname, ns_rr_rdata(rr) + 1, n);
427 printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname);
428 }
429 break;
430
431 case ns_t_soa:
432 if (rdlen < 20) {
433 //fprintf(stderr, "SOA record too short\n");
434 return -1;
435 }
436
437 printf("%s\n", ns_rr_name(rr));
438
439 cp = ns_rr_rdata(rr);
440 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
441 cp, dname, sizeof(dname));
442
443 if (n < 0) {
444 //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
445 return -1;
446 }
447
448 printf("\torigin = %s\n", dname);
449 cp += n;
450
451 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
452 cp, dname, sizeof(dname));
453
454 if (n < 0) {
455 //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
456 return -1;
457 }
458
459 printf("\tmail addr = %s\n", dname);
460 cp += n;
461
462 printf("\tserial = %lu\n", ns_get32(cp));
463 cp += 4;
464
465 printf("\trefresh = %lu\n", ns_get32(cp));
466 cp += 4;
467
468 printf("\tretry = %lu\n", ns_get32(cp));
469 cp += 4;
470
471 printf("\texpire = %lu\n", ns_get32(cp));
472 cp += 4;
473
474 printf("\tminimum = %lu\n", ns_get32(cp));
475 break;
476
477 default:
478 break;
479 }
480 }
481
482 return i;
483}
484
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200485static char *make_ptr(char resbuf[80], const char *addrstr)
486{
487 unsigned char addr[16];
488 int i;
489
490#if ENABLE_FEATURE_IPV6
491 if (inet_pton(AF_INET6, addrstr, addr)) {
492 if (memcmp(addr, v4_mapped, 12) != 0) {
493 char *ptr = resbuf;
494 for (i = 0; i < 16; i++) {
495 *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] & 0xf];
496 *ptr++ = '.';
497 *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] >> 4];
498 *ptr++ = '.';
499 }
500 strcpy(ptr, "ip6.arpa");
501 }
502 else {
503 sprintf(resbuf, "%u.%u.%u.%u.in-addr.arpa",
504 addr[15], addr[14], addr[13], addr[12]);
505 }
506 return resbuf;
507 }
508#endif
509
510 if (inet_pton(AF_INET, addrstr, addr)) {
511 sprintf(resbuf, "%u.%u.%u.%u.in-addr.arpa",
512 addr[3], addr[2], addr[1], addr[0]);
513 return resbuf;
514 }
515
516 return NULL;
517}
518
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200519/*
520 * Function logic borrowed & modified from musl libc, res_msend.c
521 */
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200522static int send_queries(struct ns *ns, struct query *queries, int n_queries)
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200523{
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200524 struct pollfd pfd;
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200525 int servfail_retry = 0;
526 int n_replies = 0;
527 int next_query = 0;
528 unsigned retry_interval;
529 unsigned timeout = G.default_timeout * 1000;
530 unsigned t0, t1, t2;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200531
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200532 pfd.fd = -1;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200533 pfd.events = POLLIN;
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200534
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200535 retry_interval = timeout / G.default_retry;
536 t0 = t2 = monotonic_ms();
537 t1 = t2 - retry_interval;
538
539 for (; t2 - t0 < timeout; t2 = monotonic_ms()) {
540 if (t2 - t1 >= retry_interval) {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200541 int qn;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200542 for (qn = 0; qn < n_queries; qn++) {
543 if (queries[qn].rlen)
544 continue;
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200545 if (pfd.fd < 0) {
546 len_and_sockaddr *local_lsa;
547 pfd.fd = xsocket_type(&local_lsa, ns->lsa->u.sa.sa_family, SOCK_DGRAM);
548 /*
549 * local_lsa has "null" address and port 0 now.
550 * bind() ensures we have a *particular port* selected by kernel
551 * and remembered in fd, thus later recv(fd)
552 * receives only packets sent to this port.
553 */
554 xbind(pfd.fd, &local_lsa->u.sa, local_lsa->len);
555 free(local_lsa);
556 /* Make read/writes know the destination */
557 xconnect(pfd.fd, &ns->lsa->u.sa, ns->lsa->len);
558 ndelay_on(pfd.fd);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200559 }
Denys Vlasenkodb93b212018-04-14 18:11:35 +0200560 if (write(pfd.fd, queries[qn].query, queries[qn].qlen) < 0) {
561 bb_perror_msg("write to '%s'", ns->name);
562 close(pfd.fd);
563 return -1; /* "no go, try next server" */
564 }
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200565 }
566
567 t1 = t2;
568 servfail_retry = 2 * n_queries;
569 }
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200570 poll_more:
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200571 /* Wait for a response, or until time to retry */
572 if (poll(&pfd, 1, t1 + retry_interval - t2) <= 0)
573 continue;
574
575 while (1) {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200576 int qn;
577 int recvlen;
578
579 recvlen = read(pfd.fd,
580 queries[next_query].reply,
581 sizeof(queries[next_query].reply)
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200582 );
583
584 /* read error */
585 if (recvlen < 0)
586 break;
587
588 /* Ignore non-identifiable packets */
589 if (recvlen < 4)
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200590 goto poll_more;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200591
592 /* Find which query this answer goes with, if any */
593 for (qn = next_query; qn < n_queries; qn++)
594 if (memcmp(queries[next_query].reply, queries[qn].query, 2) == 0)
595 break;
596
597 if (qn >= n_queries || queries[qn].rlen)
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200598 goto poll_more;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200599
600 queries[qn].rcode = queries[next_query].reply[3] & 15;
601 queries[qn].latency = monotonic_ms() - t0;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200602
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200603 ns->replies++;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200604
605 /* Only accept positive or negative responses;
606 * retry immediately on server failure, and ignore
607 * all other codes such as refusal. */
608 switch (queries[qn].rcode) {
609 case 0:
610 case 3:
611 break;
612
613 case 2:
614 if (servfail_retry && servfail_retry--) {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200615 ns->failures++;
616 write(pfd.fd, queries[qn].query, queries[qn].qlen);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200617 }
618 /* fall through */
619
620 default:
621 continue;
622 }
623
624 /* Store answer */
625 n_replies++;
626
627 queries[qn].rlen = recvlen;
628
629 if (qn == next_query) {
630 while (next_query < n_queries) {
631 if (!queries[next_query].rlen)
632 break;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200633 next_query++;
634 }
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200635 } else {
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200636 memcpy(queries[qn].reply, queries[next_query].reply, recvlen);
637 }
638
639 if (next_query >= n_queries)
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200640 goto ret;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200641 }
642 }
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200643 ret:
644 if (pfd.fd >= 0)
645 close(pfd.fd);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200646
647 return n_replies;
648}
649
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200650static void add_ns(const char *addr)
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200651{
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200652 struct ns *ns;
653 unsigned count;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200654
655 dbg("%s: addr:'%s'\n", __func__, addr);
656
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200657 count = G.serv_count++;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200658
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200659 G.server = xrealloc_vector(G.server, /*8=2^3:*/ 3, count);
660 ns = &G.server[count];
661 ns->name = addr;
662 ns->lsa = xhost2sockaddr(addr, 53);
663 /*ns->replies = 0; - already is */
664 /*ns->failures = 0; - already is */
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200665}
666
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200667static void parse_resolvconf(void)
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200668{
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200669 FILE *resolv;
670
671 resolv = fopen("/etc/resolv.conf", "r");
672 if (resolv) {
673 char line[128], *p;
674
675 while (fgets(line, sizeof(line), resolv)) {
676 p = strtok(line, " \t\n");
677
678 if (!p || strcmp(p, "nameserver") != 0)
679 continue;
680
681 p = strtok(NULL, " \t\n");
682
683 if (!p)
684 continue;
685
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200686 add_ns(xstrdup(p));
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200687 }
688
689 fclose(resolv);
690 }
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200691}
692
693static struct query *add_query(struct query **queries, int *n_queries,
694 int type, const char *dname)
695{
696 struct query *new_q;
697 int pos;
698 ssize_t qlen;
699
700 pos = *n_queries;
701 *n_queries = pos + 1;
702 *queries = new_q = xrealloc(*queries, sizeof(**queries) * (pos + 1));
703
704 dbg("new query#%u type %u for '%s'\n", pos, type, dname);
705 new_q += pos;
706 memset(new_q, 0, sizeof(*new_q));
707
708 qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL,
709 new_q->query, sizeof(new_q->query));
710
711 new_q->qlen = qlen;
712 new_q->name = dname;
713
714 return new_q;
715}
716
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200717int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
718int nslookup_main(int argc UNUSED_PARAM, char **argv)
719{
720 struct ns *ns;
721 struct query *queries;
722 llist_t *type_strings;
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200723 int n_queries;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200724 int bb_style_counter = 0;
725 unsigned types;
726 int rc;
727 int opts;
728 enum {
729 OPT_stats = (1 << 4),
730 };
731
732 INIT_G();
733
734 type_strings = NULL;
735#if ENABLE_FEATURE_NSLOOKUP_LONG_OPTIONS
736 opts = getopt32long(argv, "^"
737 "+" /* '+': stop at first non-option (why?) */
738 "q:*p:+r:+t:+s"
739 "\0"
740 "-1:q::", /* minimum 1 arg, -q is a list */
741 "type\0" Required_argument "q"
742 "querytype\0" Required_argument "q"
743 "port\0" Required_argument "p"
744 "retry\0" Required_argument "r"
745 "timeout\0" Required_argument "t"
746 "stats\0" No_argument "s",
747 &type_strings, &G.default_port,
748 &G.default_retry, &G.default_timeout
749 );
750#else
751 opts = getopt32(argv, "^"
752 "+" /* '+': stop at first non-option (why?) */
753 "q:*p:+r:+t:+s"
754 "\0"
755 "-1:q::", /* minimum 1 arg, -q is a list */
756 &type_strings, &G.default_port,
757 &G.default_retry, &G.default_timeout
758 );
759#endif
760 if (G.default_port > 65535)
761 bb_error_msg_and_die("invalid server port");
762 if (G.default_retry == 0)
763 bb_error_msg_and_die("invalid retry value");
764 if (G.default_timeout == 0)
765 bb_error_msg_and_die("invalid timeout value");
766
767 types = 0;
768 while (type_strings) {
769 int c;
770 char *ptr, *chr;
771
772 ptr = llist_pop(&type_strings);
773
774 /* skip leading text, e.g. when invoked with -querytype=AAAA */
775 chr = strchr(ptr, '=');
776 if (chr)
777 ptr = chr + 1;
778
779 for (c = 0;; c++) {
780 if (!qtypes[c].name)
781 bb_error_msg_and_die("invalid query type \"%s\"", ptr);
782 if (strcmp(qtypes[c].name, ptr) == 0)
783 break;
784 }
785
786 types |= (1 << c);
787 }
788
789 argv += optind;
790
791 n_queries = 0;
792 queries = NULL;
793 do {
794 /* No explicit type given, guess query type.
795 * If we can convert the domain argument into a ptr (means that
796 * inet_pton() could read it) we assume a PTR request, else
797 * we issue A+AAAA queries and switch to an output format
798 * mimicking the one of the traditional nslookup applet. */
799 if (types == 0) {
800 char *ptr;
801 char buf80[80];
802
803 ptr = make_ptr(buf80, *argv);
804
805 if (ptr) {
806 add_query(&queries, &n_queries, T_PTR, ptr);
807 }
808 else {
809 bb_style_counter = 1;
810 add_query(&queries, &n_queries, T_A, *argv);
811#if ENABLE_FEATURE_IPV6
812 add_query(&queries, &n_queries, T_AAAA, *argv);
813#endif
814 }
815 }
816 else {
817 int c;
818 for (c = 0; qtypes[c].name; c++) {
819 if (types & (1 << c))
820 add_query(&queries, &n_queries, qtypes[c].type,
821 *argv);
822 }
823 }
824 argv++;
825 } while (argv[0] && argv[1]);
826
827 /* Use given DNS server if present */
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200828 if (argv[0]) {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200829 add_ns(argv[0]);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200830 } else {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200831 parse_resolvconf();
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200832 /* Fall back to localhost if we could not find NS in resolv.conf */
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200833 if (G.serv_count == 0)
834 add_ns("127.0.0.1");
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200835 }
836
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200837 for (rc = 0; rc < G.serv_count; rc++) {
838 int c = send_queries(&G.server[rc], queries, n_queries);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200839 if (c > 0)
840 break;
Denys Vlasenkodb93b212018-04-14 18:11:35 +0200841 /* c = 0: timed out waiting for replies */
842 /* c < 0: error (message already printed) */
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200843 rc++;
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200844 if (rc >= G.serv_count) {
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200845 fprintf(stderr,
846 ";; connection timed out; no servers could be reached\n\n");
847 return EXIT_FAILURE;
848 }
849 }
850
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200851 printf("Server:\t\t%s\n", G.server[rc].name);
Denys Vlasenkodb93b212018-04-14 18:11:35 +0200852 printf("Address:\t%s\n", xmalloc_sockaddr2dotted(&G.server[rc].lsa->u.sa));
853 /* In "Address", bind-utils-9.11.3 show port after a hash: "1.2.3.4#53" */
854 /* Should we do the same? */
855
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200856 if (opts & OPT_stats) {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200857 printf("Replies:\t%d\n", G.server[rc].replies);
858 printf("Failures:\t%d\n", G.server[rc].failures);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200859 }
860 printf("\n");
861
862 for (rc = 0; rc < n_queries; rc++) {
863 int c;
864
865 if (opts & OPT_stats) {
866 printf("Query #%d completed in %lums:\n", rc, queries[rc].latency);
867 }
868
869 if (queries[rc].rcode != 0) {
870 printf("** server can't find %s: %s\n", queries[rc].name,
871 rcodes[queries[rc].rcode]);
872 continue;
873 }
874
875 c = 0;
876
877 if (queries[rc].rlen) {
878 HEADER *header;
879
880 if (!bb_style_counter) {
881 header = (HEADER *)queries[rc].reply;
882 if (!header->aa)
883 printf("Non-authoritative answer:\n");
884 c = parse_reply(queries[rc].reply, queries[rc].rlen, NULL);
885 }
886 else {
887 c = parse_reply(queries[rc].reply, queries[rc].rlen,
888 &bb_style_counter);
889 }
890 }
891
892 if (c == 0)
893 printf("*** Can't find %s: No answer\n", queries[rc].name);
894 else if (c < 0)
895 printf("*** Can't find %s: Parse error\n", queries[rc].name);
896
897 if (!bb_style_counter)
898 printf("\n");
899 }
900
901 if (ENABLE_FEATURE_CLEAN_UP) {
902 free(ns);
903 if (n_queries)
904 free(queries);
905 }
906
907 return EXIT_SUCCESS;
908}
909
910#endif