blob: 50d91abbcd11bda85a82a4128e4b98255aefc548 [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 Vlasenko55bc8e82018-04-14 17:54:24 +0200560 write(pfd.fd, queries[qn].query, queries[qn].qlen);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200561 }
562
563 t1 = t2;
564 servfail_retry = 2 * n_queries;
565 }
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200566 poll_more:
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200567 /* Wait for a response, or until time to retry */
568 if (poll(&pfd, 1, t1 + retry_interval - t2) <= 0)
569 continue;
570
571 while (1) {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200572 int qn;
573 int recvlen;
574
575 recvlen = read(pfd.fd,
576 queries[next_query].reply,
577 sizeof(queries[next_query].reply)
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200578 );
579
580 /* read error */
581 if (recvlen < 0)
582 break;
583
584 /* Ignore non-identifiable packets */
585 if (recvlen < 4)
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200586 goto poll_more;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200587
588 /* Find which query this answer goes with, if any */
589 for (qn = next_query; qn < n_queries; qn++)
590 if (memcmp(queries[next_query].reply, queries[qn].query, 2) == 0)
591 break;
592
593 if (qn >= n_queries || queries[qn].rlen)
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200594 goto poll_more;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200595
596 queries[qn].rcode = queries[next_query].reply[3] & 15;
597 queries[qn].latency = monotonic_ms() - t0;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200598
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200599 ns->replies++;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200600
601 /* Only accept positive or negative responses;
602 * retry immediately on server failure, and ignore
603 * all other codes such as refusal. */
604 switch (queries[qn].rcode) {
605 case 0:
606 case 3:
607 break;
608
609 case 2:
610 if (servfail_retry && servfail_retry--) {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200611 ns->failures++;
612 write(pfd.fd, queries[qn].query, queries[qn].qlen);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200613 }
614 /* fall through */
615
616 default:
617 continue;
618 }
619
620 /* Store answer */
621 n_replies++;
622
623 queries[qn].rlen = recvlen;
624
625 if (qn == next_query) {
626 while (next_query < n_queries) {
627 if (!queries[next_query].rlen)
628 break;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200629 next_query++;
630 }
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200631 } else {
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200632 memcpy(queries[qn].reply, queries[next_query].reply, recvlen);
633 }
634
635 if (next_query >= n_queries)
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200636 goto ret;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200637 }
638 }
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200639 ret:
640 if (pfd.fd >= 0)
641 close(pfd.fd);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200642
643 return n_replies;
644}
645
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200646static void add_ns(const char *addr)
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200647{
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200648 struct ns *ns;
649 unsigned count;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200650
651 dbg("%s: addr:'%s'\n", __func__, addr);
652
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200653 count = G.serv_count++;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200654
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200655 G.server = xrealloc_vector(G.server, /*8=2^3:*/ 3, count);
656 ns = &G.server[count];
657 ns->name = addr;
658 ns->lsa = xhost2sockaddr(addr, 53);
659 /*ns->replies = 0; - already is */
660 /*ns->failures = 0; - already is */
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200661}
662
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200663static void parse_resolvconf(void)
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200664{
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200665 FILE *resolv;
666
667 resolv = fopen("/etc/resolv.conf", "r");
668 if (resolv) {
669 char line[128], *p;
670
671 while (fgets(line, sizeof(line), resolv)) {
672 p = strtok(line, " \t\n");
673
674 if (!p || strcmp(p, "nameserver") != 0)
675 continue;
676
677 p = strtok(NULL, " \t\n");
678
679 if (!p)
680 continue;
681
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200682 add_ns(xstrdup(p));
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200683 }
684
685 fclose(resolv);
686 }
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200687}
688
689static struct query *add_query(struct query **queries, int *n_queries,
690 int type, const char *dname)
691{
692 struct query *new_q;
693 int pos;
694 ssize_t qlen;
695
696 pos = *n_queries;
697 *n_queries = pos + 1;
698 *queries = new_q = xrealloc(*queries, sizeof(**queries) * (pos + 1));
699
700 dbg("new query#%u type %u for '%s'\n", pos, type, dname);
701 new_q += pos;
702 memset(new_q, 0, sizeof(*new_q));
703
704 qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL,
705 new_q->query, sizeof(new_q->query));
706
707 new_q->qlen = qlen;
708 new_q->name = dname;
709
710 return new_q;
711}
712
713//FIXME: use xmalloc_sockaddr2dotted[_noport]() instead of sal2str()
714
715#define SIZEOF_SAL2STR_BUF (INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1 + 5 + 1)
716static char *sal2str(char buf[SIZEOF_SAL2STR_BUF], len_and_sockaddr *a)
717{
718 char *p = buf;
719
720#if ENABLE_FEATURE_IPV6
721 if (a->u.sa.sa_family == AF_INET6) {
722 inet_ntop(AF_INET6, &a->u.sin6.sin6_addr, buf, SIZEOF_SAL2STR_BUF);
723 p += strlen(p);
724
725 if (a->u.sin6.sin6_scope_id) {
726 if (if_indextoname(a->u.sin6.sin6_scope_id, p + 1)) {
727 *p++ = '%';
728 p += strlen(p);
729 }
730 }
731 } else
732#endif
733 {
734 inet_ntop(AF_INET, &a->u.sin.sin_addr, buf, SIZEOF_SAL2STR_BUF);
735 p += strlen(p);
736 }
737
738 sprintf(p, "#%hu", ntohs(a->u.sin.sin_port));
739
740 return buf;
741}
742
743int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
744int nslookup_main(int argc UNUSED_PARAM, char **argv)
745{
746 struct ns *ns;
747 struct query *queries;
748 llist_t *type_strings;
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200749 int n_queries;
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200750 int bb_style_counter = 0;
751 unsigned types;
752 int rc;
753 int opts;
754 enum {
755 OPT_stats = (1 << 4),
756 };
757
758 INIT_G();
759
760 type_strings = NULL;
761#if ENABLE_FEATURE_NSLOOKUP_LONG_OPTIONS
762 opts = getopt32long(argv, "^"
763 "+" /* '+': stop at first non-option (why?) */
764 "q:*p:+r:+t:+s"
765 "\0"
766 "-1:q::", /* minimum 1 arg, -q is a list */
767 "type\0" Required_argument "q"
768 "querytype\0" Required_argument "q"
769 "port\0" Required_argument "p"
770 "retry\0" Required_argument "r"
771 "timeout\0" Required_argument "t"
772 "stats\0" No_argument "s",
773 &type_strings, &G.default_port,
774 &G.default_retry, &G.default_timeout
775 );
776#else
777 opts = getopt32(argv, "^"
778 "+" /* '+': stop at first non-option (why?) */
779 "q:*p:+r:+t:+s"
780 "\0"
781 "-1:q::", /* minimum 1 arg, -q is a list */
782 &type_strings, &G.default_port,
783 &G.default_retry, &G.default_timeout
784 );
785#endif
786 if (G.default_port > 65535)
787 bb_error_msg_and_die("invalid server port");
788 if (G.default_retry == 0)
789 bb_error_msg_and_die("invalid retry value");
790 if (G.default_timeout == 0)
791 bb_error_msg_and_die("invalid timeout value");
792
793 types = 0;
794 while (type_strings) {
795 int c;
796 char *ptr, *chr;
797
798 ptr = llist_pop(&type_strings);
799
800 /* skip leading text, e.g. when invoked with -querytype=AAAA */
801 chr = strchr(ptr, '=');
802 if (chr)
803 ptr = chr + 1;
804
805 for (c = 0;; c++) {
806 if (!qtypes[c].name)
807 bb_error_msg_and_die("invalid query type \"%s\"", ptr);
808 if (strcmp(qtypes[c].name, ptr) == 0)
809 break;
810 }
811
812 types |= (1 << c);
813 }
814
815 argv += optind;
816
817 n_queries = 0;
818 queries = NULL;
819 do {
820 /* No explicit type given, guess query type.
821 * If we can convert the domain argument into a ptr (means that
822 * inet_pton() could read it) we assume a PTR request, else
823 * we issue A+AAAA queries and switch to an output format
824 * mimicking the one of the traditional nslookup applet. */
825 if (types == 0) {
826 char *ptr;
827 char buf80[80];
828
829 ptr = make_ptr(buf80, *argv);
830
831 if (ptr) {
832 add_query(&queries, &n_queries, T_PTR, ptr);
833 }
834 else {
835 bb_style_counter = 1;
836 add_query(&queries, &n_queries, T_A, *argv);
837#if ENABLE_FEATURE_IPV6
838 add_query(&queries, &n_queries, T_AAAA, *argv);
839#endif
840 }
841 }
842 else {
843 int c;
844 for (c = 0; qtypes[c].name; c++) {
845 if (types & (1 << c))
846 add_query(&queries, &n_queries, qtypes[c].type,
847 *argv);
848 }
849 }
850 argv++;
851 } while (argv[0] && argv[1]);
852
853 /* Use given DNS server if present */
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200854 if (argv[0]) {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200855 add_ns(argv[0]);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200856 } else {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200857 parse_resolvconf();
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200858 /* Fall back to localhost if we could not find NS in resolv.conf */
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200859 if (G.serv_count == 0)
860 add_ns("127.0.0.1");
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200861 }
862
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200863 for (rc = 0; rc < G.serv_count; rc++) {
864 int c = send_queries(&G.server[rc], queries, n_queries);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200865 if (c < 0)
866 bb_perror_msg_and_die("can't send queries");
867 if (c > 0)
868 break;
869 rc++;
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200870 if (rc >= G.serv_count) {
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200871 fprintf(stderr,
872 ";; connection timed out; no servers could be reached\n\n");
873 return EXIT_FAILURE;
874 }
875 }
876
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200877 printf("Server:\t\t%s\n", G.server[rc].name);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200878 {
879 char buf[SIZEOF_SAL2STR_BUF];
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200880 printf("Address:\t%s\n", sal2str(buf, G.server[rc].lsa));
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200881 }
882 if (opts & OPT_stats) {
Denys Vlasenko55bc8e82018-04-14 17:54:24 +0200883 printf("Replies:\t%d\n", G.server[rc].replies);
884 printf("Failures:\t%d\n", G.server[rc].failures);
Denys Vlasenko0dd3be82018-04-14 14:05:45 +0200885 }
886 printf("\n");
887
888 for (rc = 0; rc < n_queries; rc++) {
889 int c;
890
891 if (opts & OPT_stats) {
892 printf("Query #%d completed in %lums:\n", rc, queries[rc].latency);
893 }
894
895 if (queries[rc].rcode != 0) {
896 printf("** server can't find %s: %s\n", queries[rc].name,
897 rcodes[queries[rc].rcode]);
898 continue;
899 }
900
901 c = 0;
902
903 if (queries[rc].rlen) {
904 HEADER *header;
905
906 if (!bb_style_counter) {
907 header = (HEADER *)queries[rc].reply;
908 if (!header->aa)
909 printf("Non-authoritative answer:\n");
910 c = parse_reply(queries[rc].reply, queries[rc].rlen, NULL);
911 }
912 else {
913 c = parse_reply(queries[rc].reply, queries[rc].rlen,
914 &bb_style_counter);
915 }
916 }
917
918 if (c == 0)
919 printf("*** Can't find %s: No answer\n", queries[rc].name);
920 else if (c < 0)
921 printf("*** Can't find %s: Parse error\n", queries[rc].name);
922
923 if (!bb_style_counter)
924 printf("\n");
925 }
926
927 if (ENABLE_FEATURE_CLEAN_UP) {
928 free(ns);
929 if (n_queries)
930 free(queries);
931 }
932
933 return EXIT_SUCCESS;
934}
935
936#endif