blob: 6f8f02fc162585d4591b7b73d65e1c87bf7feeff [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;
259 len_and_sockaddr addr;
260 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;
269 int rcode, n_ns;
270};
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;
318} FIX_ALIASING;
319#define G (*(struct globals*)bb_common_bufsiz1)
320#define INIT_G() do { \
321 setup_common_bufsiz(); \
322 G.default_port = 53; \
323 G.default_retry = 2; \
324 G.default_timeout = 5; \
325} while (0)
326
327static int
328parse_reply(const unsigned char *msg, size_t len, int *bb_style_counter)
329{
330 ns_msg handle;
331 ns_rr rr;
332 int i, n, rdlen;
333 const char *format = NULL;
334 char astr[INET6_ADDRSTRLEN], dname[MAXDNAME];
335 const unsigned char *cp;
336
337 if (ns_initparse(msg, len, &handle) != 0) {
338 //fprintf(stderr, "Unable to parse reply: %s\n", strerror(errno));
339 return -1;
340 }
341
342 for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
343 if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) {
344 //fprintf(stderr, "Unable to parse resource record: %s\n", strerror(errno));
345 return -1;
346 }
347
348 if (bb_style_counter && *bb_style_counter == 1)
349 printf("Name: %s\n", ns_rr_name(rr));
350
351 rdlen = ns_rr_rdlen(rr);
352
353 switch (ns_rr_type(rr))
354 {
355 case ns_t_a:
356 if (rdlen != 4) {
357 //fprintf(stderr, "Unexpected A record length\n");
358 return -1;
359 }
360 inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr));
361 if (bb_style_counter)
362 printf("Address %d: %s\n", (*bb_style_counter)++, astr);
363 else
364 printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr);
365 break;
366
367#if ENABLE_FEATURE_IPV6
368 case ns_t_aaaa:
369 if (rdlen != 16) {
370 //fprintf(stderr, "Unexpected AAAA record length\n");
371 return -1;
372 }
373 inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr));
374 if (bb_style_counter)
375 printf("Address %d: %s\n", (*bb_style_counter)++, astr);
376 else
377 printf("%s\thas AAAA address %s\n", ns_rr_name(rr), astr);
378 break;
379#endif
380
381 case ns_t_ns:
382 if (!format)
383 format = "%s\tnameserver = %s\n";
384 /* fall through */
385
386 case ns_t_cname:
387 if (!format)
388 format = "%s\tcanonical name = %s\n";
389 /* fall through */
390
391 case ns_t_ptr:
392 if (!format)
393 format = "%s\tname = %s\n";
394 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
395 ns_rr_rdata(rr), dname, sizeof(dname)) < 0) {
396 //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
397 return -1;
398 }
399 printf(format, ns_rr_name(rr), dname);
400 break;
401
402 case ns_t_mx:
403 if (rdlen < 2) {
404 fprintf(stderr, "MX record too short\n");
405 return -1;
406 }
407 n = ns_get16(ns_rr_rdata(rr));
408 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
409 ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0) {
410 //fprintf(stderr, "Cannot uncompress MX domain: %s\n", strerror(errno));
411 return -1;
412 }
413 printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname);
414 break;
415
416 case ns_t_txt:
417 if (rdlen < 1) {
418 //fprintf(stderr, "TXT record too short\n");
419 return -1;
420 }
421 n = *(unsigned char *)ns_rr_rdata(rr);
422 if (n > 0) {
423 memset(dname, 0, sizeof(dname));
424 memcpy(dname, ns_rr_rdata(rr) + 1, n);
425 printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname);
426 }
427 break;
428
429 case ns_t_soa:
430 if (rdlen < 20) {
431 //fprintf(stderr, "SOA record too short\n");
432 return -1;
433 }
434
435 printf("%s\n", ns_rr_name(rr));
436
437 cp = ns_rr_rdata(rr);
438 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
439 cp, dname, sizeof(dname));
440
441 if (n < 0) {
442 //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
443 return -1;
444 }
445
446 printf("\torigin = %s\n", dname);
447 cp += n;
448
449 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
450 cp, dname, sizeof(dname));
451
452 if (n < 0) {
453 //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
454 return -1;
455 }
456
457 printf("\tmail addr = %s\n", dname);
458 cp += n;
459
460 printf("\tserial = %lu\n", ns_get32(cp));
461 cp += 4;
462
463 printf("\trefresh = %lu\n", ns_get32(cp));
464 cp += 4;
465
466 printf("\tretry = %lu\n", ns_get32(cp));
467 cp += 4;
468
469 printf("\texpire = %lu\n", ns_get32(cp));
470 cp += 4;
471
472 printf("\tminimum = %lu\n", ns_get32(cp));
473 break;
474
475 default:
476 break;
477 }
478 }
479
480 return i;
481}
482
483static int parse_nsaddr(const char *addrstr, len_and_sockaddr *lsa)
484{
485 char *hash;
486 unsigned port = G.default_port;
487 IF_FEATURE_IPV6(unsigned scope;)
488
489 dbg("%s: addrstr:'%s'\n", __func__, addrstr);
490
491 hash = strchr(addrstr, '#');
492
493 if (hash) {
494 *hash++ = '\0';
495 port = bb_strtou(hash, NULL, 10);
496 if (errno || port > 65535) {
497 errno = EINVAL;
498 return -1;
499 }
500 }
501
502#if ENABLE_FEATURE_IPV6
503 scope = 0;
504 hash = strchr(addrstr, '%');
505 if (hash) {
506 char ifname[IFNAMSIZ];
507 int i;
508
509 hash++;
510 for (i = 0; hash[i] != '\0' && hash[i] != '#'; i++) {
511 if (i >= IFNAMSIZ) {
512 errno = ENODEV;
513 return -1;
514 }
515 ifname[i] = hash[i];
516 }
517 ifname[i] = '\0';
518
519 scope = if_nametoindex(ifname);
520 if (scope == 0) {
521 errno = ENODEV;
522 return -1;
523 }
524 }
525
526 if (inet_pton(AF_INET6, addrstr, &lsa->u.sin6.sin6_addr)) {
527 lsa->u.sin6.sin6_family = AF_INET6;
528 lsa->u.sin6.sin6_port = htons(port);
529 lsa->u.sin6.sin6_scope_id = scope;
530 lsa->len = sizeof(lsa->u.sin6);
531 return 0;
532 }
533#endif
534
535 if (inet_pton(AF_INET, addrstr, &lsa->u.sin.sin_addr)) {
536 lsa->u.sin.sin_family = AF_INET;
537 lsa->u.sin.sin_port = htons(port);
538 lsa->len = sizeof(lsa->u.sin);
539 return 0;
540 }
541
542 errno = EINVAL;
543 return -1;
544}
545
546static char *make_ptr(char resbuf[80], const char *addrstr)
547{
548 unsigned char addr[16];
549 int i;
550
551#if ENABLE_FEATURE_IPV6
552 if (inet_pton(AF_INET6, addrstr, addr)) {
553 if (memcmp(addr, v4_mapped, 12) != 0) {
554 char *ptr = resbuf;
555 for (i = 0; i < 16; i++) {
556 *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] & 0xf];
557 *ptr++ = '.';
558 *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] >> 4];
559 *ptr++ = '.';
560 }
561 strcpy(ptr, "ip6.arpa");
562 }
563 else {
564 sprintf(resbuf, "%u.%u.%u.%u.in-addr.arpa",
565 addr[15], addr[14], addr[13], addr[12]);
566 }
567 return resbuf;
568 }
569#endif
570
571 if (inet_pton(AF_INET, addrstr, addr)) {
572 sprintf(resbuf, "%u.%u.%u.%u.in-addr.arpa",
573 addr[3], addr[2], addr[1], addr[0]);
574 return resbuf;
575 }
576
577 return NULL;
578}
579
580#if ENABLE_FEATURE_IPV6
581static void to_v4_mapped(len_and_sockaddr *a)
582{
583 if (a->u.sa.sa_family != AF_INET)
584 return;
585
586 /* Order is important */
587//FIXME: port?
588 memcpy(a->u.sin6.sin6_addr.s6_addr + 12, &a->u.sin.sin_addr, 4);
589 memcpy(a->u.sin6.sin6_addr.s6_addr, v4_mapped, 12);
590
591 a->u.sin6.sin6_family = AF_INET6;
592 a->u.sin6.sin6_flowinfo = 0;
593 a->u.sin6.sin6_scope_id = 0;
594 a->len = sizeof(a->u.sin6);
595}
596#endif
597
598/*
599 * Function logic borrowed & modified from musl libc, res_msend.c
600 */
601static int send_queries(struct ns *ns, int n_ns, struct query *queries, int n_queries)
602{
603 int fd;
604 int timeout = G.default_timeout * 1000, retry_interval, servfail_retry = 0;
605 len_and_sockaddr from = { };
606 int recvlen = 0;
607 int n_replies = 0;
608 struct pollfd pfd;
609 unsigned long t0, t1, t2;
610 int nn, qn, next_query = 0;
611
612 from.u.sa.sa_family = AF_INET;
613 from.len = sizeof(from.u.sin);
614#if ENABLE_FEATURE_IPV6
615 for (nn = 0; nn < n_ns; nn++) {
616 if (ns[nn].addr.u.sa.sa_family == AF_INET6) {
617 from.u.sa.sa_family = AF_INET6;
618 from.len = sizeof(from.u.sin6);
619 break;
620 }
621 }
622#endif
623
624 /* Get local address and open/bind a socket */
625#if ENABLE_FEATURE_IPV6
626 fd = socket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
627 /* Handle case where system lacks IPv6 support */
628 if (fd < 0) {
629 if (from.u.sa.sa_family != AF_INET6 || errno != EAFNOSUPPORT)
630 bb_perror_msg_and_die("socket");
631 fd = xsocket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
632 from.u.sa.sa_family = AF_INET;
633 }
634#else
635 fd = xsocket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
636#endif
637 xbind(fd, &from.u.sa, from.len);
638
639#if ENABLE_FEATURE_IPV6
640 /* Convert any IPv4 addresses in a mixed environment to v4-mapped */
641 if (from.u.sa.sa_family == AF_INET6) {
642 setsockopt_1(fd, IPPROTO_IPV6, IPV6_V6ONLY);
643 for (nn = 0; nn < n_ns; nn++)
644 to_v4_mapped(&ns[nn].addr);
645 }
646#endif
647
648 pfd.fd = fd;
649 pfd.events = POLLIN;
650 retry_interval = timeout / G.default_retry;
651 t0 = t2 = monotonic_ms();
652 t1 = t2 - retry_interval;
653
654 for (; t2 - t0 < timeout; t2 = monotonic_ms()) {
655 if (t2 - t1 >= retry_interval) {
656 for (qn = 0; qn < n_queries; qn++) {
657 if (queries[qn].rlen)
658 continue;
659
660 for (nn = 0; nn < n_ns; nn++) {
661 sendto(fd, queries[qn].query, queries[qn].qlen,
662 MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len);
663 }
664 }
665
666 t1 = t2;
667 servfail_retry = 2 * n_queries;
668 }
669
670 /* Wait for a response, or until time to retry */
671 if (poll(&pfd, 1, t1 + retry_interval - t2) <= 0)
672 continue;
673
674 while (1) {
675 recvlen = recvfrom(fd, queries[next_query].reply,
676 sizeof(queries[next_query].reply), 0,
677 &from.u.sa, &from.len
678 );
679
680 /* read error */
681 if (recvlen < 0)
682 break;
683
684 /* Ignore non-identifiable packets */
685 if (recvlen < 4)
686 continue;
687
688 /* Ignore replies from addresses we didn't send to */
689 for (nn = 0; nn < n_ns; nn++)
690 if (memcmp(&from.u.sa, &ns[nn].addr.u.sa, from.len) == 0)
691 break;
692
693 if (nn >= n_ns)
694 continue;
695
696 /* Find which query this answer goes with, if any */
697 for (qn = next_query; qn < n_queries; qn++)
698 if (memcmp(queries[next_query].reply, queries[qn].query, 2) == 0)
699 break;
700
701 if (qn >= n_queries || queries[qn].rlen)
702 continue;
703
704 queries[qn].rcode = queries[next_query].reply[3] & 15;
705 queries[qn].latency = monotonic_ms() - t0;
706 queries[qn].n_ns = nn;
707
708 ns[nn].replies++;
709
710 /* Only accept positive or negative responses;
711 * retry immediately on server failure, and ignore
712 * all other codes such as refusal. */
713 switch (queries[qn].rcode) {
714 case 0:
715 case 3:
716 break;
717
718 case 2:
719 if (servfail_retry && servfail_retry--) {
720 ns[nn].failures++;
721 sendto(fd, queries[qn].query, queries[qn].qlen,
722 MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len);
723 }
724 /* fall through */
725
726 default:
727 continue;
728 }
729
730 /* Store answer */
731 n_replies++;
732
733 queries[qn].rlen = recvlen;
734
735 if (qn == next_query) {
736 while (next_query < n_queries) {
737 if (!queries[next_query].rlen)
738 break;
739
740 next_query++;
741 }
742 }
743 else {
744 memcpy(queries[qn].reply, queries[next_query].reply, recvlen);
745 }
746
747 if (next_query >= n_queries)
748 return n_replies;
749 }
750 }
751
752 return n_replies;
753}
754
755///FIXME: use standard lsa = xhost2sockaddr(host, port) instead
756
757static struct ns *add_ns(struct ns **ns, int *n_ns, const char *addr)
758{
759 char portstr[sizeof("65535")], *p;
760 len_and_sockaddr a = { };
761 struct ns *tmp;
762 struct addrinfo *ai, *aip, hints = {
763 .ai_flags = AI_NUMERICSERV,
764 .ai_socktype = SOCK_DGRAM
765 };
766
767 dbg("%s: addr:'%s'\n", __func__, addr);
768
769 if (parse_nsaddr(addr, &a)) {
770 /* Maybe we got a domain name, attempt to resolve it using the standard
771 * resolver routines */
772
773 p = strchr(addr, '#');
774 snprintf(portstr, sizeof(portstr), "%hu",
775 (unsigned short)(p ? strtoul(p, NULL, 10) : G.default_port));
776
777 if (!getaddrinfo(addr, portstr, &hints, &ai)) {
778 for (aip = ai; aip; aip = aip->ai_next) {
779 if (aip->ai_addr->sa_family != AF_INET &&
780 aip->ai_addr->sa_family != AF_INET6)
781 continue;
782
783#if ! ENABLE_FEATURE_IPV6
784 if (aip->ai_addr->sa_family != AF_INET)
785 continue;
786#endif
787
788 tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1));
789
790 if (!tmp)
791 return NULL;
792
793 *ns = tmp;
794
795 (*ns)[*n_ns].name = addr;
796 (*ns)[*n_ns].replies = 0;
797 (*ns)[*n_ns].failures = 0;
798 (*ns)[*n_ns].addr.len = aip->ai_addrlen;
799
800 memcpy(&(*ns)[*n_ns].addr.u.sa, aip->ai_addr, aip->ai_addrlen);
801
802 (*n_ns)++;
803 }
804
805 freeaddrinfo(ai);
806
807 return &(*ns)[*n_ns];
808 }
809
810 return NULL;
811 }
812
813 tmp = xrealloc(*ns, sizeof(**ns) * (*n_ns + 1));
814 *ns = tmp;
815
816 (*ns)[*n_ns].addr = a;
817 (*ns)[*n_ns].name = addr;
818 (*ns)[*n_ns].replies = 0;
819 (*ns)[*n_ns].failures = 0;
820
821 return &(*ns)[(*n_ns)++];
822}
823
824static int parse_resolvconf(struct ns **ns, int *n_ns)
825{
826 int prev_n_ns = *n_ns;
827 FILE *resolv;
828
829 resolv = fopen("/etc/resolv.conf", "r");
830 if (resolv) {
831 char line[128], *p;
832
833 while (fgets(line, sizeof(line), resolv)) {
834 p = strtok(line, " \t\n");
835
836 if (!p || strcmp(p, "nameserver") != 0)
837 continue;
838
839 p = strtok(NULL, " \t\n");
840
841 if (!p)
842 continue;
843
844 if (!add_ns(ns, n_ns, xstrdup(p))) {
845 free(p);
846 break;
847 }
848 }
849
850 fclose(resolv);
851 }
852
853 return *n_ns - prev_n_ns;
854}
855
856static struct query *add_query(struct query **queries, int *n_queries,
857 int type, const char *dname)
858{
859 struct query *new_q;
860 int pos;
861 ssize_t qlen;
862
863 pos = *n_queries;
864 *n_queries = pos + 1;
865 *queries = new_q = xrealloc(*queries, sizeof(**queries) * (pos + 1));
866
867 dbg("new query#%u type %u for '%s'\n", pos, type, dname);
868 new_q += pos;
869 memset(new_q, 0, sizeof(*new_q));
870
871 qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL,
872 new_q->query, sizeof(new_q->query));
873
874 new_q->qlen = qlen;
875 new_q->name = dname;
876
877 return new_q;
878}
879
880//FIXME: use xmalloc_sockaddr2dotted[_noport]() instead of sal2str()
881
882#define SIZEOF_SAL2STR_BUF (INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1 + 5 + 1)
883static char *sal2str(char buf[SIZEOF_SAL2STR_BUF], len_and_sockaddr *a)
884{
885 char *p = buf;
886
887#if ENABLE_FEATURE_IPV6
888 if (a->u.sa.sa_family == AF_INET6) {
889 inet_ntop(AF_INET6, &a->u.sin6.sin6_addr, buf, SIZEOF_SAL2STR_BUF);
890 p += strlen(p);
891
892 if (a->u.sin6.sin6_scope_id) {
893 if (if_indextoname(a->u.sin6.sin6_scope_id, p + 1)) {
894 *p++ = '%';
895 p += strlen(p);
896 }
897 }
898 } else
899#endif
900 {
901 inet_ntop(AF_INET, &a->u.sin.sin_addr, buf, SIZEOF_SAL2STR_BUF);
902 p += strlen(p);
903 }
904
905 sprintf(p, "#%hu", ntohs(a->u.sin.sin_port));
906
907 return buf;
908}
909
910int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
911int nslookup_main(int argc UNUSED_PARAM, char **argv)
912{
913 struct ns *ns;
914 struct query *queries;
915 llist_t *type_strings;
916 int n_ns, n_queries;
917 int bb_style_counter = 0;
918 unsigned types;
919 int rc;
920 int opts;
921 enum {
922 OPT_stats = (1 << 4),
923 };
924
925 INIT_G();
926
927 type_strings = NULL;
928#if ENABLE_FEATURE_NSLOOKUP_LONG_OPTIONS
929 opts = getopt32long(argv, "^"
930 "+" /* '+': stop at first non-option (why?) */
931 "q:*p:+r:+t:+s"
932 "\0"
933 "-1:q::", /* minimum 1 arg, -q is a list */
934 "type\0" Required_argument "q"
935 "querytype\0" Required_argument "q"
936 "port\0" Required_argument "p"
937 "retry\0" Required_argument "r"
938 "timeout\0" Required_argument "t"
939 "stats\0" No_argument "s",
940 &type_strings, &G.default_port,
941 &G.default_retry, &G.default_timeout
942 );
943#else
944 opts = getopt32(argv, "^"
945 "+" /* '+': stop at first non-option (why?) */
946 "q:*p:+r:+t:+s"
947 "\0"
948 "-1:q::", /* minimum 1 arg, -q is a list */
949 &type_strings, &G.default_port,
950 &G.default_retry, &G.default_timeout
951 );
952#endif
953 if (G.default_port > 65535)
954 bb_error_msg_and_die("invalid server port");
955 if (G.default_retry == 0)
956 bb_error_msg_and_die("invalid retry value");
957 if (G.default_timeout == 0)
958 bb_error_msg_and_die("invalid timeout value");
959
960 types = 0;
961 while (type_strings) {
962 int c;
963 char *ptr, *chr;
964
965 ptr = llist_pop(&type_strings);
966
967 /* skip leading text, e.g. when invoked with -querytype=AAAA */
968 chr = strchr(ptr, '=');
969 if (chr)
970 ptr = chr + 1;
971
972 for (c = 0;; c++) {
973 if (!qtypes[c].name)
974 bb_error_msg_and_die("invalid query type \"%s\"", ptr);
975 if (strcmp(qtypes[c].name, ptr) == 0)
976 break;
977 }
978
979 types |= (1 << c);
980 }
981
982 argv += optind;
983
984 n_queries = 0;
985 queries = NULL;
986 do {
987 /* No explicit type given, guess query type.
988 * If we can convert the domain argument into a ptr (means that
989 * inet_pton() could read it) we assume a PTR request, else
990 * we issue A+AAAA queries and switch to an output format
991 * mimicking the one of the traditional nslookup applet. */
992 if (types == 0) {
993 char *ptr;
994 char buf80[80];
995
996 ptr = make_ptr(buf80, *argv);
997
998 if (ptr) {
999 add_query(&queries, &n_queries, T_PTR, ptr);
1000 }
1001 else {
1002 bb_style_counter = 1;
1003 add_query(&queries, &n_queries, T_A, *argv);
1004#if ENABLE_FEATURE_IPV6
1005 add_query(&queries, &n_queries, T_AAAA, *argv);
1006#endif
1007 }
1008 }
1009 else {
1010 int c;
1011 for (c = 0; qtypes[c].name; c++) {
1012 if (types & (1 << c))
1013 add_query(&queries, &n_queries, qtypes[c].type,
1014 *argv);
1015 }
1016 }
1017 argv++;
1018 } while (argv[0] && argv[1]);
1019
1020 /* Use given DNS server if present */
1021 n_ns = 0;
1022 ns = NULL;
1023 if (argv[0]) {
1024 if (!add_ns(&ns, &n_ns, argv[0]))
1025 bb_error_msg_and_die("invalid NS server address \"%s\"", argv[0]);
1026 } else {
1027 parse_resolvconf(&ns, &n_ns);
1028 /* Fall back to localhost if we could not find NS in resolv.conf */
1029 if (n_ns == 0)
1030 add_ns(&ns, &n_ns, "127.0.0.1");
1031 }
1032
1033 for (rc = 0; rc < n_ns; rc++) {
1034 int c = send_queries(&ns[rc], 1, queries, n_queries);
1035 if (c < 0)
1036 bb_perror_msg_and_die("can't send queries");
1037 if (c > 0)
1038 break;
1039 rc++;
1040 if (rc >= n_ns) {
1041 fprintf(stderr,
1042 ";; connection timed out; no servers could be reached\n\n");
1043 return EXIT_FAILURE;
1044 }
1045 }
1046
1047 printf("Server:\t\t%s\n", ns[rc].name);
1048 {
1049 char buf[SIZEOF_SAL2STR_BUF];
1050 printf("Address:\t%s\n", sal2str(buf, &ns[rc].addr));
1051 }
1052 if (opts & OPT_stats) {
1053 printf("Replies:\t%d\n", ns[rc].replies);
1054 printf("Failures:\t%d\n", ns[rc].failures);
1055 }
1056 printf("\n");
1057
1058 for (rc = 0; rc < n_queries; rc++) {
1059 int c;
1060
1061 if (opts & OPT_stats) {
1062 printf("Query #%d completed in %lums:\n", rc, queries[rc].latency);
1063 }
1064
1065 if (queries[rc].rcode != 0) {
1066 printf("** server can't find %s: %s\n", queries[rc].name,
1067 rcodes[queries[rc].rcode]);
1068 continue;
1069 }
1070
1071 c = 0;
1072
1073 if (queries[rc].rlen) {
1074 HEADER *header;
1075
1076 if (!bb_style_counter) {
1077 header = (HEADER *)queries[rc].reply;
1078 if (!header->aa)
1079 printf("Non-authoritative answer:\n");
1080 c = parse_reply(queries[rc].reply, queries[rc].rlen, NULL);
1081 }
1082 else {
1083 c = parse_reply(queries[rc].reply, queries[rc].rlen,
1084 &bb_style_counter);
1085 }
1086 }
1087
1088 if (c == 0)
1089 printf("*** Can't find %s: No answer\n", queries[rc].name);
1090 else if (c < 0)
1091 printf("*** Can't find %s: Parse error\n", queries[rc].name);
1092
1093 if (!bb_style_counter)
1094 printf("\n");
1095 }
1096
1097 if (ENABLE_FEATURE_CLEAN_UP) {
1098 free(ns);
1099 if (n_queries)
1100 free(queries);
1101 }
1102
1103 return EXIT_SUCCESS;
1104}
1105
1106#endif