2
0

230-add_nslookup_lede.patch 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  1. From ab0f8bb80527928f513297ab93e3ec8c8b48dd50 Mon Sep 17 00:00:00 2001
  2. From: Jo-Philipp Wich <jo@mein.io>
  3. Date: Tue, 14 Mar 2017 22:21:34 +0100
  4. Subject: [PATCH] networking: add LEDE nslookup applet
  5. Add a new LEDE nslookup applet which is compatible with musl libc
  6. and providing more features like ability to specify query type.
  7. In contrast to busybox' builtin nslookup applet, this variant does
  8. not rely on libc resolver internals but uses explicit send logic
  9. and the libresolv primitives to parse received DNS responses.
  10. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
  11. ---
  12. Makefile.flags | 6 +
  13. networking/nslookup_lede.c | 915 +++++++++++++++++++++++++++++++++++++++++++++
  14. 2 files changed, 921 insertions(+)
  15. create mode 100644 networking/nslookup_lede.c
  16. diff --git a/Makefile.flags b/Makefile.flags
  17. index 65021de25..096ab7756 100644
  18. --- a/Makefile.flags
  19. +++ b/Makefile.flags
  20. @@ -134,6 +134,12 @@ else
  21. LDLIBS += m
  22. endif
  23. +# nslookup_lede might need the resolv library
  24. +RESOLV_AVAILABLE := $(shell echo 'int main(void){res_init();return 0;}' >resolvtest.c; $(CC) $(CFLAGS) -include resolv.h -lresolv -o /dev/null resolvtest.c >/dev/null 2>&1 && echo "y"; rm resolvtest.c)
  25. +ifeq ($(RESOLV_AVAILABLE),y)
  26. +LDLIBS += resolv
  27. +endif
  28. +
  29. # libpam may use libpthread, libdl and/or libaudit.
  30. # On some platforms that requires an explicit -lpthread, -ldl, -laudit.
  31. # However, on *other platforms* it fails when some of those flags
  32. diff --git a/networking/nslookup_lede.c b/networking/nslookup_lede.c
  33. new file mode 100644
  34. index 000000000..c6c90ddf3
  35. --- /dev/null
  36. +++ b/networking/nslookup_lede.c
  37. @@ -0,0 +1,915 @@
  38. +/*
  39. + * nslookup_lede - musl compatible replacement for busybox nslookup
  40. + *
  41. + * Copyright (C) 2017 Jo-Philipp Wich <jo@mein.io>
  42. + *
  43. + * Permission to use, copy, modify, and/or distribute this software for any
  44. + * purpose with or without fee is hereby granted, provided that the above
  45. + * copyright notice and this permission notice appear in all copies.
  46. + *
  47. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  48. + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  49. + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  50. + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  51. + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  52. + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  53. + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  54. + */
  55. +
  56. +//config:config NSLOOKUP_LEDE
  57. +//config: bool "nslookup_lede"
  58. +//config: depends on !NSLOOKUP
  59. +//config: default y
  60. +//config: help
  61. +//config: nslookup is a tool to query Internet name servers (LEDE flavor).
  62. +//config:
  63. +//config:config FEATURE_NSLOOKUP_LEDE_LONG_OPTIONS
  64. +//config: bool "Enable long options"
  65. +//config: default y
  66. +//config: depends on NSLOOKUP_LEDE && LONG_OPTS
  67. +//config: help
  68. +//config: Support long options for the nslookup applet.
  69. +
  70. +//applet:IF_NSLOOKUP_LEDE(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP))
  71. +
  72. +//kbuild:lib-$(CONFIG_NSLOOKUP_LEDE) += nslookup_lede.o
  73. +
  74. +//usage:#define nslookup_lede_trivial_usage
  75. +//usage: "[HOST] [SERVER]"
  76. +//usage:#define nslookup_lede_full_usage "\n\n"
  77. +//usage: "Query the nameserver for the IP address of the given HOST\n"
  78. +//usage: "optionally using a specified DNS server"
  79. +//usage:
  80. +//usage:#define nslookup_lede_example_usage
  81. +//usage: "$ nslookup localhost\n"
  82. +//usage: "Server: default\n"
  83. +//usage: "Address: default\n"
  84. +//usage: "\n"
  85. +//usage: "Name: debian\n"
  86. +//usage: "Address: 127.0.0.1\n"
  87. +
  88. +#include <stdio.h>
  89. +#include <resolv.h>
  90. +#include <string.h>
  91. +#include <errno.h>
  92. +#include <time.h>
  93. +#include <poll.h>
  94. +#include <unistd.h>
  95. +#include <stdlib.h>
  96. +#include <sys/socket.h>
  97. +#include <arpa/inet.h>
  98. +#include <net/if.h>
  99. +#include <netdb.h>
  100. +
  101. +#include "libbb.h"
  102. +
  103. +struct ns {
  104. + const char *name;
  105. + len_and_sockaddr addr;
  106. + int failures;
  107. + int replies;
  108. +};
  109. +
  110. +struct query {
  111. + const char *name;
  112. + size_t qlen, rlen;
  113. + unsigned char query[512], reply[512];
  114. + unsigned long latency;
  115. + int rcode, n_ns;
  116. +};
  117. +
  118. +static struct {
  119. + int type;
  120. + const char *name;
  121. +} qtypes[] = {
  122. + { ns_t_soa, "SOA" },
  123. + { ns_t_ns, "NS" },
  124. + { ns_t_a, "A" },
  125. +#if ENABLE_FEATURE_IPV6
  126. + { ns_t_aaaa, "AAAA" },
  127. +#endif
  128. + { ns_t_cname, "CNAME" },
  129. + { ns_t_mx, "MX" },
  130. + { ns_t_txt, "TXT" },
  131. + { ns_t_ptr, "PTR" },
  132. + { ns_t_any, "ANY" },
  133. + { }
  134. +};
  135. +
  136. +static const char *rcodes[] = {
  137. + "NOERROR",
  138. + "FORMERR",
  139. + "SERVFAIL",
  140. + "NXDOMAIN",
  141. + "NOTIMP",
  142. + "REFUSED",
  143. + "YXDOMAIN",
  144. + "YXRRSET",
  145. + "NXRRSET",
  146. + "NOTAUTH",
  147. + "NOTZONE",
  148. + "RESERVED11",
  149. + "RESERVED12",
  150. + "RESERVED13",
  151. + "RESERVED14",
  152. + "RESERVED15",
  153. + "BADVERS"
  154. +};
  155. +
  156. +static unsigned int default_port = 53;
  157. +static unsigned int default_retry = 2;
  158. +static unsigned int default_timeout = 5;
  159. +
  160. +
  161. +static int parse_reply(const unsigned char *msg, size_t len, int *bb_style_counter)
  162. +{
  163. + ns_msg handle;
  164. + ns_rr rr;
  165. + int i, n, rdlen;
  166. + const char *format = NULL;
  167. + char astr[INET6_ADDRSTRLEN], dname[MAXDNAME];
  168. + const unsigned char *cp;
  169. +
  170. + if (ns_initparse(msg, len, &handle) != 0) {
  171. + //fprintf(stderr, "Unable to parse reply: %s\n", strerror(errno));
  172. + return -1;
  173. + }
  174. +
  175. + for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
  176. + if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) {
  177. + //fprintf(stderr, "Unable to parse resource record: %s\n", strerror(errno));
  178. + return -1;
  179. + }
  180. +
  181. + if (bb_style_counter && *bb_style_counter == 1)
  182. + printf("Name: %s\n", ns_rr_name(rr));
  183. +
  184. + rdlen = ns_rr_rdlen(rr);
  185. +
  186. + switch (ns_rr_type(rr))
  187. + {
  188. + case ns_t_a:
  189. + if (rdlen != 4) {
  190. + //fprintf(stderr, "Unexpected A record length\n");
  191. + return -1;
  192. + }
  193. + inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr));
  194. + if (bb_style_counter)
  195. + printf("Address %d: %s\n", (*bb_style_counter)++, astr);
  196. + else
  197. + printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr);
  198. + break;
  199. +
  200. +#if ENABLE_FEATURE_IPV6
  201. + case ns_t_aaaa:
  202. + if (rdlen != 16) {
  203. + //fprintf(stderr, "Unexpected AAAA record length\n");
  204. + return -1;
  205. + }
  206. + inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr));
  207. + if (bb_style_counter)
  208. + printf("Address %d: %s\n", (*bb_style_counter)++, astr);
  209. + else
  210. + printf("%s\thas AAAA address %s\n", ns_rr_name(rr), astr);
  211. + break;
  212. +#endif
  213. +
  214. + case ns_t_ns:
  215. + if (!format)
  216. + format = "%s\tnameserver = %s\n";
  217. + /* fall through */
  218. +
  219. + case ns_t_cname:
  220. + if (!format)
  221. + format = "%s\tcanonical name = %s\n";
  222. + /* fall through */
  223. +
  224. + case ns_t_ptr:
  225. + if (!format)
  226. + format = "%s\tname = %s\n";
  227. + if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
  228. + ns_rr_rdata(rr), dname, sizeof(dname)) < 0) {
  229. + //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
  230. + return -1;
  231. + }
  232. + printf(format, ns_rr_name(rr), dname);
  233. + break;
  234. +
  235. + case ns_t_mx:
  236. + if (rdlen < 2) {
  237. + fprintf(stderr, "MX record too short\n");
  238. + return -1;
  239. + }
  240. + n = ns_get16(ns_rr_rdata(rr));
  241. + if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
  242. + ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0) {
  243. + //fprintf(stderr, "Cannot uncompress MX domain: %s\n", strerror(errno));
  244. + return -1;
  245. + }
  246. + printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname);
  247. + break;
  248. +
  249. + case ns_t_txt:
  250. + if (rdlen < 1) {
  251. + //fprintf(stderr, "TXT record too short\n");
  252. + return -1;
  253. + }
  254. + n = *(unsigned char *)ns_rr_rdata(rr);
  255. + if (n > 0) {
  256. + memset(dname, 0, sizeof(dname));
  257. + memcpy(dname, ns_rr_rdata(rr) + 1, n);
  258. + printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname);
  259. + }
  260. + break;
  261. +
  262. + case ns_t_soa:
  263. + if (rdlen < 20) {
  264. + //fprintf(stderr, "SOA record too short\n");
  265. + return -1;
  266. + }
  267. +
  268. + printf("%s\n", ns_rr_name(rr));
  269. +
  270. + cp = ns_rr_rdata(rr);
  271. + n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
  272. + cp, dname, sizeof(dname));
  273. +
  274. + if (n < 0) {
  275. + //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
  276. + return -1;
  277. + }
  278. +
  279. + printf("\torigin = %s\n", dname);
  280. + cp += n;
  281. +
  282. + n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
  283. + cp, dname, sizeof(dname));
  284. +
  285. + if (n < 0) {
  286. + //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
  287. + return -1;
  288. + }
  289. +
  290. + printf("\tmail addr = %s\n", dname);
  291. + cp += n;
  292. +
  293. + printf("\tserial = %lu\n", ns_get32(cp));
  294. + cp += 4;
  295. +
  296. + printf("\trefresh = %lu\n", ns_get32(cp));
  297. + cp += 4;
  298. +
  299. + printf("\tretry = %lu\n", ns_get32(cp));
  300. + cp += 4;
  301. +
  302. + printf("\texpire = %lu\n", ns_get32(cp));
  303. + cp += 4;
  304. +
  305. + printf("\tminimum = %lu\n", ns_get32(cp));
  306. + break;
  307. +
  308. + default:
  309. + break;
  310. + }
  311. + }
  312. +
  313. + return i;
  314. +}
  315. +
  316. +static int parse_nsaddr(const char *addrstr, len_and_sockaddr *lsa)
  317. +{
  318. + char *eptr, *hash, ifname[IFNAMSIZ];
  319. + unsigned int port = default_port;
  320. + unsigned int scope = 0;
  321. +
  322. + hash = strchr(addrstr, '#');
  323. +
  324. + if (hash) {
  325. + *hash++ = '\0';
  326. + port = strtoul(hash, &eptr, 10);
  327. +
  328. + if (eptr == hash || *eptr != '\0' || port > 65535) {
  329. + errno = EINVAL;
  330. + return -1;
  331. + }
  332. + }
  333. +
  334. + hash = strchr(addrstr, '%');
  335. +
  336. + if (hash) {
  337. + for (eptr = ++hash; *eptr != '\0' && *eptr != '#'; eptr++) {
  338. + if ((eptr - hash) >= IFNAMSIZ) {
  339. + errno = ENODEV;
  340. + return -1;
  341. + }
  342. +
  343. + ifname[eptr - hash] = *eptr;
  344. + }
  345. +
  346. + ifname[eptr - hash] = '\0';
  347. + scope = if_nametoindex(ifname);
  348. +
  349. + if (scope == 0) {
  350. + errno = ENODEV;
  351. + return -1;
  352. + }
  353. + }
  354. +
  355. +#if ENABLE_FEATURE_IPV6
  356. + if (inet_pton(AF_INET6, addrstr, &lsa->u.sin6.sin6_addr)) {
  357. + lsa->u.sin6.sin6_family = AF_INET6;
  358. + lsa->u.sin6.sin6_port = htons(port);
  359. + lsa->u.sin6.sin6_scope_id = scope;
  360. + lsa->len = sizeof(lsa->u.sin6);
  361. + return 0;
  362. + }
  363. +#endif
  364. +
  365. + if (!scope && inet_pton(AF_INET, addrstr, &lsa->u.sin.sin_addr)) {
  366. + lsa->u.sin.sin_family = AF_INET;
  367. + lsa->u.sin.sin_port = htons(port);
  368. + lsa->len = sizeof(lsa->u.sin);
  369. + return 0;
  370. + }
  371. +
  372. + errno = EINVAL;
  373. + return -1;
  374. +}
  375. +
  376. +static char *make_ptr(const char *addrstr)
  377. +{
  378. + const char *hexdigit = "0123456789abcdef";
  379. + static char ptrstr[73];
  380. + unsigned char addr[16];
  381. + char *ptr = ptrstr;
  382. + int i;
  383. +
  384. + if (inet_pton(AF_INET6, addrstr, addr)) {
  385. + if (memcmp(addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12) != 0) {
  386. + for (i = 0; i < 16; i++) {
  387. + *ptr++ = hexdigit[(unsigned char)addr[15 - i] & 0xf];
  388. + *ptr++ = '.';
  389. + *ptr++ = hexdigit[(unsigned char)addr[15 - i] >> 4];
  390. + *ptr++ = '.';
  391. + }
  392. + strcpy(ptr, "ip6.arpa");
  393. + }
  394. + else {
  395. + sprintf(ptr, "%u.%u.%u.%u.in-addr.arpa",
  396. + addr[15], addr[14], addr[13], addr[12]);
  397. + }
  398. +
  399. + return ptrstr;
  400. + }
  401. +
  402. + if (inet_pton(AF_INET, addrstr, addr)) {
  403. + sprintf(ptr, "%u.%u.%u.%u.in-addr.arpa",
  404. + addr[3], addr[2], addr[1], addr[0]);
  405. + return ptrstr;
  406. + }
  407. +
  408. + return NULL;
  409. +}
  410. +
  411. +static unsigned long mtime(void)
  412. +{
  413. + struct timespec ts;
  414. + clock_gettime(CLOCK_REALTIME, &ts);
  415. + return (unsigned long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
  416. +}
  417. +
  418. +#if ENABLE_FEATURE_IPV6
  419. +static void to_v4_mapped(len_and_sockaddr *a)
  420. +{
  421. + if (a->u.sa.sa_family != AF_INET)
  422. + return;
  423. +
  424. + memcpy(a->u.sin6.sin6_addr.s6_addr + 12,
  425. + &a->u.sin.sin_addr, 4);
  426. +
  427. + memcpy(a->u.sin6.sin6_addr.s6_addr,
  428. + "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
  429. +
  430. + a->u.sin6.sin6_family = AF_INET6;
  431. + a->u.sin6.sin6_flowinfo = 0;
  432. + a->u.sin6.sin6_scope_id = 0;
  433. + a->len = sizeof(a->u.sin6);
  434. +}
  435. +#endif
  436. +
  437. +
  438. +/*
  439. + * Function logic borrowed & modified from musl libc, res_msend.c
  440. + */
  441. +
  442. +static int send_queries(struct ns *ns, int n_ns, struct query *queries, int n_queries)
  443. +{
  444. + int fd;
  445. + int timeout = default_timeout * 1000, retry_interval, servfail_retry = 0;
  446. + len_and_sockaddr from = { };
  447. +#if ENABLE_FEATURE_IPV6
  448. + int one = 1;
  449. +#endif
  450. + int recvlen = 0;
  451. + int n_replies = 0;
  452. + struct pollfd pfd;
  453. + unsigned long t0, t1, t2;
  454. + int nn, qn, next_query = 0;
  455. +
  456. + from.u.sa.sa_family = AF_INET;
  457. + from.len = sizeof(from.u.sin);
  458. +
  459. +#if ENABLE_FEATURE_IPV6
  460. + for (nn = 0; nn < n_ns; nn++) {
  461. + if (ns[nn].addr.u.sa.sa_family == AF_INET6) {
  462. + from.u.sa.sa_family = AF_INET6;
  463. + from.len = sizeof(from.u.sin6);
  464. + break;
  465. + }
  466. + }
  467. +#endif
  468. +
  469. + /* Get local address and open/bind a socket */
  470. + fd = socket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
  471. +
  472. +#if ENABLE_FEATURE_IPV6
  473. + /* Handle case where system lacks IPv6 support */
  474. + if (fd < 0 && from.u.sa.sa_family == AF_INET6 && errno == EAFNOSUPPORT) {
  475. + fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
  476. + from.u.sa.sa_family = AF_INET;
  477. + }
  478. +#endif
  479. +
  480. + if (fd < 0)
  481. + return -1;
  482. +
  483. + if (bind(fd, &from.u.sa, from.len) < 0) {
  484. + close(fd);
  485. + return -1;
  486. + }
  487. +
  488. +#if ENABLE_FEATURE_IPV6
  489. + /* Convert any IPv4 addresses in a mixed environment to v4-mapped */
  490. + if (from.u.sa.sa_family == AF_INET6) {
  491. + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
  492. +
  493. + for (nn = 0; nn < n_ns; nn++)
  494. + to_v4_mapped(&ns[nn].addr);
  495. + }
  496. +#endif
  497. +
  498. + pfd.fd = fd;
  499. + pfd.events = POLLIN;
  500. + retry_interval = timeout / default_retry;
  501. + t0 = t2 = mtime();
  502. + t1 = t2 - retry_interval;
  503. +
  504. + for (; t2 - t0 < timeout; t2 = mtime()) {
  505. + if (t2 - t1 >= retry_interval) {
  506. + for (qn = 0; qn < n_queries; qn++) {
  507. + if (queries[qn].rlen)
  508. + continue;
  509. +
  510. + for (nn = 0; nn < n_ns; nn++) {
  511. + sendto(fd, queries[qn].query, queries[qn].qlen,
  512. + MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len);
  513. + }
  514. + }
  515. +
  516. + t1 = t2;
  517. + servfail_retry = 2 * n_queries;
  518. + }
  519. +
  520. + /* Wait for a response, or until time to retry */
  521. + if (poll(&pfd, 1, t1+retry_interval-t2) <= 0)
  522. + continue;
  523. +
  524. + while (1) {
  525. + recvlen = recvfrom(fd, queries[next_query].reply,
  526. + sizeof(queries[next_query].reply), 0,
  527. + &from.u.sa, &from.len);
  528. +
  529. + /* read error */
  530. + if (recvlen < 0)
  531. + break;
  532. +
  533. + /* Ignore non-identifiable packets */
  534. + if (recvlen < 4)
  535. + continue;
  536. +
  537. + /* Ignore replies from addresses we didn't send to */
  538. + for (nn = 0; nn < n_ns; nn++)
  539. + if (memcmp(&from.u.sa, &ns[nn].addr.u.sa, from.len) == 0)
  540. + break;
  541. +
  542. + if (nn >= n_ns)
  543. + continue;
  544. +
  545. + /* Find which query this answer goes with, if any */
  546. + for (qn = next_query; qn < n_queries; qn++)
  547. + if (!memcmp(queries[next_query].reply, queries[qn].query, 2))
  548. + break;
  549. +
  550. + if (qn >= n_queries || queries[qn].rlen)
  551. + continue;
  552. +
  553. + queries[qn].rcode = queries[next_query].reply[3] & 15;
  554. + queries[qn].latency = mtime() - t0;
  555. + queries[qn].n_ns = nn;
  556. +
  557. + ns[nn].replies++;
  558. +
  559. + /* Only accept positive or negative responses;
  560. + * retry immediately on server failure, and ignore
  561. + * all other codes such as refusal. */
  562. + switch (queries[qn].rcode) {
  563. + case 0:
  564. + case 3:
  565. + break;
  566. +
  567. + case 2:
  568. + if (servfail_retry && servfail_retry--) {
  569. + ns[nn].failures++;
  570. + sendto(fd, queries[qn].query, queries[qn].qlen,
  571. + MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len);
  572. + }
  573. + /* fall through */
  574. +
  575. + default:
  576. + continue;
  577. + }
  578. +
  579. + /* Store answer */
  580. + n_replies++;
  581. +
  582. + queries[qn].rlen = recvlen;
  583. +
  584. + if (qn == next_query) {
  585. + while (next_query < n_queries) {
  586. + if (!queries[next_query].rlen)
  587. + break;
  588. +
  589. + next_query++;
  590. + }
  591. + }
  592. + else {
  593. + memcpy(queries[qn].reply, queries[next_query].reply, recvlen);
  594. + }
  595. +
  596. + if (next_query >= n_queries)
  597. + return n_replies;
  598. + }
  599. + }
  600. +
  601. + return n_replies;
  602. +}
  603. +
  604. +static struct ns *add_ns(struct ns **ns, int *n_ns, const char *addr)
  605. +{
  606. + char portstr[sizeof("65535")], *p;
  607. + len_and_sockaddr a = { };
  608. + struct ns *tmp;
  609. + struct addrinfo *ai, *aip, hints = {
  610. + .ai_flags = AI_NUMERICSERV,
  611. + .ai_socktype = SOCK_DGRAM
  612. + };
  613. +
  614. + if (parse_nsaddr(addr, &a)) {
  615. + /* Maybe we got a domain name, attempt to resolve it using the standard
  616. + * resolver routines */
  617. +
  618. + p = strchr(addr, '#');
  619. + snprintf(portstr, sizeof(portstr), "%hu",
  620. + (unsigned short)(p ? strtoul(p, NULL, 10) : default_port));
  621. +
  622. + if (!getaddrinfo(addr, portstr, &hints, &ai)) {
  623. + for (aip = ai; aip; aip = aip->ai_next) {
  624. + if (aip->ai_addr->sa_family != AF_INET &&
  625. + aip->ai_addr->sa_family != AF_INET6)
  626. + continue;
  627. +
  628. +#if ! ENABLE_FEATURE_IPV6
  629. + if (aip->ai_addr->sa_family != AF_INET)
  630. + continue;
  631. +#endif
  632. +
  633. + tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1));
  634. +
  635. + if (!tmp)
  636. + return NULL;
  637. +
  638. + *ns = tmp;
  639. +
  640. + (*ns)[*n_ns].name = addr;
  641. + (*ns)[*n_ns].replies = 0;
  642. + (*ns)[*n_ns].failures = 0;
  643. + (*ns)[*n_ns].addr.len = aip->ai_addrlen;
  644. +
  645. + memcpy(&(*ns)[*n_ns].addr.u.sa, aip->ai_addr, aip->ai_addrlen);
  646. +
  647. + (*n_ns)++;
  648. + }
  649. +
  650. + freeaddrinfo(ai);
  651. +
  652. + return &(*ns)[*n_ns];
  653. + }
  654. +
  655. + return NULL;
  656. + }
  657. +
  658. + tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1));
  659. +
  660. + if (!tmp)
  661. + return NULL;
  662. +
  663. + *ns = tmp;
  664. +
  665. + (*ns)[*n_ns].addr = a;
  666. + (*ns)[*n_ns].name = addr;
  667. + (*ns)[*n_ns].replies = 0;
  668. + (*ns)[*n_ns].failures = 0;
  669. +
  670. + return &(*ns)[(*n_ns)++];
  671. +}
  672. +
  673. +static int parse_resolvconf(struct ns **ns, int *n_ns)
  674. +{
  675. + int prev_n_ns = *n_ns;
  676. + char line[128], *p;
  677. + FILE *resolv;
  678. +
  679. + if ((resolv = fopen("/etc/resolv.conf", "r")) != NULL) {
  680. + while (fgets(line, sizeof(line), resolv)) {
  681. + p = strtok(line, " \t\n");
  682. +
  683. + if (!p || strcmp(p, "nameserver"))
  684. + continue;
  685. +
  686. + p = strtok(NULL, " \t\n");
  687. +
  688. + if (!p)
  689. + continue;
  690. +
  691. + if (!add_ns(ns, n_ns, strdup(p))) {
  692. + free(p);
  693. + break;
  694. + }
  695. + }
  696. +
  697. + fclose(resolv);
  698. + }
  699. +
  700. + return *n_ns - prev_n_ns;
  701. +}
  702. +
  703. +static struct query *add_query(struct query **queries, int *n_queries,
  704. + int type, const char *dname)
  705. +{
  706. + struct query *tmp;
  707. + ssize_t qlen;
  708. +
  709. + tmp = realloc(*queries, sizeof(**queries) * (*n_queries + 1));
  710. +
  711. + if (!tmp)
  712. + return NULL;
  713. +
  714. + memset(&tmp[*n_queries], 0, sizeof(*tmp));
  715. +
  716. + qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL,
  717. + tmp[*n_queries].query, sizeof(tmp[*n_queries].query));
  718. +
  719. + tmp[*n_queries].qlen = qlen;
  720. + tmp[*n_queries].name = dname;
  721. + *queries = tmp;
  722. +
  723. + return &tmp[(*n_queries)++];
  724. +}
  725. +
  726. +static char *sal2str(len_and_sockaddr *a)
  727. +{
  728. + static char buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1 + 5 + 1];
  729. + char *p = buf;
  730. +
  731. +#if ENABLE_FEATURE_IPV6
  732. + if (a->u.sa.sa_family == AF_INET6) {
  733. + inet_ntop(AF_INET6, &a->u.sin6.sin6_addr, buf, sizeof(buf));
  734. + p += strlen(p);
  735. +
  736. + if (a->u.sin6.sin6_scope_id) {
  737. + if (if_indextoname(a->u.sin6.sin6_scope_id, p + 1)) {
  738. + *p++ = '%';
  739. + p += strlen(p);
  740. + }
  741. + }
  742. + }
  743. + else
  744. +#endif
  745. + {
  746. + inet_ntop(AF_INET, &a->u.sin.sin_addr, buf, sizeof(buf));
  747. + p += strlen(p);
  748. + }
  749. +
  750. + sprintf(p, "#%hu", ntohs(a->u.sin.sin_port));
  751. +
  752. + return buf;
  753. +}
  754. +
  755. +
  756. +#if ENABLE_FEATURE_NSLOOKUP_LEDE_LONG_OPTIONS
  757. +static const char nslookup_longopts[] ALIGN1 =
  758. + "type\0" Required_argument "q"
  759. + "querytype\0" Required_argument "q"
  760. + "port\0" Required_argument "p"
  761. + "retry\0" Required_argument "r"
  762. + "timeout\0" Required_argument "t"
  763. + "stats\0" Required_argument "s"
  764. + ;
  765. +#endif
  766. +
  767. +int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  768. +int nslookup_main(int argc, char **argv)
  769. +{
  770. + int rc = 1;
  771. + char *ptr, *chr;
  772. + struct ns *ns = NULL;
  773. + struct query *queries = NULL;
  774. + llist_t *type_strings = NULL;
  775. + int n_ns = 0, n_queries = 0;
  776. + int c, opts, option_index = 0;
  777. + int stats = 0, bb_style_counter = 0;
  778. + unsigned int types = 0;
  779. + HEADER *header;
  780. +
  781. +#if ENABLE_FEATURE_NSLOOKUP_LEDE_LONG_OPTIONS
  782. + applet_long_options = nslookup_longopts;
  783. +#endif
  784. +
  785. + opt_complementary = "q::";
  786. + opts = getopt32(argv, "+q:*p:+r:+t:+s",
  787. + &type_strings, &default_port,
  788. + &default_retry, &default_timeout);
  789. +
  790. + while (type_strings) {
  791. + ptr = llist_pop(&type_strings);
  792. +
  793. + /* skip leading text, e.g. when invoked with -querytype=AAAA */
  794. + if ((chr = strchr(ptr, '=')) != NULL)
  795. + ptr = chr + 1;
  796. +
  797. + for (c = 0; qtypes[c].name; c++)
  798. + if (!strcmp(qtypes[c].name, ptr))
  799. + break;
  800. +
  801. + if (!qtypes[c].name) {
  802. + fprintf(stderr, "Invalid query type \"%s\"\n", ptr);
  803. + goto out;
  804. + }
  805. +
  806. + types |= (1 << c);
  807. + }
  808. +
  809. + if (default_port > 65535) {
  810. + fprintf(stderr, "Invalid server port\n");
  811. + goto out;
  812. + }
  813. +
  814. + if (!default_retry) {
  815. + fprintf(stderr, "Invalid retry value\n");
  816. + goto out;
  817. + }
  818. +
  819. + if (!default_timeout) {
  820. + fprintf(stderr, "Invalid timeout value\n");
  821. + goto out;
  822. + }
  823. +
  824. + stats = (opts & 16);
  825. +
  826. + if (optind >= argc)
  827. + bb_show_usage();
  828. +
  829. + for (option_index = optind;
  830. + option_index < ((argc - optind) > 1 ? argc - 1 : argc);
  831. + option_index++) {
  832. +
  833. + /* No explicit type given, guess query type.
  834. + * If we can convert the domain argument into a ptr (means that
  835. + * inet_pton() could read it) we assume a PTR request, else
  836. + * we issue A+AAAA queries and switch to an output format
  837. + * mimicking the one of the traditional nslookup applet. */
  838. + if (types == 0) {
  839. + ptr = make_ptr(argv[option_index]);
  840. +
  841. + if (ptr) {
  842. + add_query(&queries, &n_queries, T_PTR, ptr);
  843. + }
  844. + else {
  845. + bb_style_counter = 1;
  846. + add_query(&queries, &n_queries, T_A, argv[option_index]);
  847. +#if ENABLE_FEATURE_IPV6
  848. + add_query(&queries, &n_queries, T_AAAA, argv[option_index]);
  849. +#endif
  850. + }
  851. + }
  852. + else {
  853. + for (c = 0; qtypes[c].name; c++)
  854. + if (types & (1 << c))
  855. + add_query(&queries, &n_queries, qtypes[c].type,
  856. + argv[option_index]);
  857. + }
  858. + }
  859. +
  860. + /* Use given DNS server if present */
  861. + if (option_index < argc) {
  862. + if (!add_ns(&ns, &n_ns, argv[option_index])) {
  863. + fprintf(stderr, "Invalid NS server address \"%s\": %s\n",
  864. + argv[option_index], strerror(errno));
  865. + goto out;
  866. + }
  867. + }
  868. + else {
  869. + parse_resolvconf(&ns, &n_ns);
  870. + }
  871. +
  872. + /* Fall back to localhost if we could not find NS in resolv.conf */
  873. + if (n_ns == 0) {
  874. + add_ns(&ns, &n_ns, "127.0.0.1");
  875. + }
  876. +
  877. + for (c = 0; c < n_ns; c++) {
  878. + rc = send_queries(&ns[c], 1, queries, n_queries);
  879. +
  880. + if (rc < 0) {
  881. + fprintf(stderr, "Failed to send queries: %s\n", strerror(errno));
  882. + goto out;
  883. + } else if (rc > 0) {
  884. + break;
  885. + }
  886. + }
  887. +
  888. + if (c >= n_ns) {
  889. + fprintf(stderr,
  890. + ";; connection timed out; no servers could be reached\n\n");
  891. +
  892. + return 1;
  893. + }
  894. +
  895. + printf("Server:\t\t%s\n", ns[c].name);
  896. + printf("Address:\t%s\n", sal2str(&ns[c].addr));
  897. +
  898. + if (stats) {
  899. + printf("Replies:\t%d\n", ns[c].replies);
  900. + printf("Failures:\t%d\n", ns[c].failures);
  901. + }
  902. +
  903. + printf("\n");
  904. +
  905. + for (rc = 0; rc < n_queries; rc++) {
  906. + if (stats) {
  907. + printf("Query #%d completed in %lums:\n", rc, queries[rc].latency);
  908. + }
  909. +
  910. + if (queries[rc].rcode != 0) {
  911. + printf("** server can't find %s: %s\n", queries[rc].name,
  912. + rcodes[queries[rc].rcode]);
  913. + continue;
  914. + }
  915. +
  916. + c = 0;
  917. +
  918. + if (queries[rc].rlen) {
  919. + if (!bb_style_counter) {
  920. + header = (HEADER *)queries[rc].reply;
  921. +
  922. + if (!header->aa)
  923. + printf("Non-authoritative answer:\n");
  924. +
  925. + c = parse_reply(queries[rc].reply, queries[rc].rlen, NULL);
  926. + }
  927. + else {
  928. + c = parse_reply(queries[rc].reply, queries[rc].rlen,
  929. + &bb_style_counter);
  930. + }
  931. + }
  932. +
  933. + if (c == 0)
  934. + printf("*** Can't find %s: No answer\n", queries[rc].name);
  935. + else if (c < 0)
  936. + printf("*** Can't find %s: Parse error\n", queries[rc].name);
  937. +
  938. + if (!bb_style_counter)
  939. + printf("\n");
  940. + }
  941. +
  942. + rc = 0;
  943. +
  944. +out:
  945. + if (n_ns)
  946. + free(ns);
  947. +
  948. + if (n_queries)
  949. + free(queries);
  950. +
  951. + return rc;
  952. +}
  953. --
  954. 2.11.0