blob: 8be5f348c36eaa87c5e5092283879f58175ec9bb
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Mini nslookup implementation for busybox |
4 | * |
5 | * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu |
6 | * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> |
7 | * |
8 | * Correct default name server display and explicit name server option |
9 | * added by Ben Zeckel <bzeckel@hmc.edu> June 2001 |
10 | * |
11 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
12 | */ |
13 | //config:config NSLOOKUP |
14 | //config: bool "nslookup" |
15 | //config: default y |
16 | //config: help |
17 | //config: nslookup is a tool to query Internet name servers. |
18 | |
19 | //applet:IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP)) |
20 | |
21 | //kbuild:lib-$(CONFIG_NSLOOKUP) += nslookup.o |
22 | |
23 | //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 | |
37 | #include <resolv.h> |
38 | #include "libbb.h" |
39 | |
40 | #ifdef ANDROID |
41 | # include <netinet/in.h> |
42 | # if ENABLE_FEATURE_IPV6 |
43 | # include <netinet/in6.h> |
44 | # endif |
45 | # define ANDROID_CHANGES |
46 | # ifdef BIONIC_L |
47 | # include <arpa/nameser.h> |
48 | # include <dns/include/resolv_private.h> |
49 | # include <dns/resolv/res_private.h> |
50 | # else |
51 | # include <arpa_nameser.h> |
52 | # include <private/resolv_private.h> |
53 | # include <netbsd/resolv/res_private.h> |
54 | # endif |
55 | |
56 | static struct __res_state res_st; |
57 | struct __res_state * __res_state(void) |
58 | { |
59 | return &res_st; |
60 | } |
61 | #endif |
62 | |
63 | #define EXT(res) ((&res)->_u._ext) |
64 | |
65 | /* |
66 | * I'm only implementing non-interactive mode; |
67 | * I totally forgot nslookup even had an interactive mode. |
68 | * |
69 | * This applet is the only user of res_init(). Without it, |
70 | * you may avoid pulling in _res global from libc. |
71 | */ |
72 | |
73 | /* Examples of 'standard' nslookup output |
74 | * $ nslookup yahoo.com |
75 | * Server: 128.193.0.10 |
76 | * Address: 128.193.0.10#53 |
77 | * |
78 | * Non-authoritative answer: |
79 | * Name: yahoo.com |
80 | * Address: 216.109.112.135 |
81 | * Name: yahoo.com |
82 | * Address: 66.94.234.13 |
83 | * |
84 | * $ nslookup 204.152.191.37 |
85 | * Server: 128.193.4.20 |
86 | * Address: 128.193.4.20#53 |
87 | * |
88 | * Non-authoritative answer: |
89 | * 37.191.152.204.in-addr.arpa canonical name = 37.32-27.191.152.204.in-addr.arpa. |
90 | * 37.32-27.191.152.204.in-addr.arpa name = zeus-pub2.kernel.org. |
91 | * |
92 | * Authoritative answers can be found from: |
93 | * 32-27.191.152.204.in-addr.arpa nameserver = ns1.kernel.org. |
94 | * 32-27.191.152.204.in-addr.arpa nameserver = ns2.kernel.org. |
95 | * 32-27.191.152.204.in-addr.arpa nameserver = ns3.kernel.org. |
96 | * ns1.kernel.org internet address = 140.211.167.34 |
97 | * ns2.kernel.org internet address = 204.152.191.4 |
98 | * ns3.kernel.org internet address = 204.152.191.36 |
99 | */ |
100 | |
101 | static int print_host(const char *hostname, const char *header) |
102 | { |
103 | /* We can't use xhost2sockaddr() - we want to get ALL addresses, |
104 | * not just one */ |
105 | struct addrinfo *result = NULL; |
106 | int rc; |
107 | struct addrinfo hint; |
108 | |
109 | memset(&hint, 0 , sizeof(hint)); |
110 | /* hint.ai_family = AF_UNSPEC; - zero anyway */ |
111 | /* Needed. Or else we will get each address thrice (or more) |
112 | * for each possible socket type (tcp,udp,raw...): */ |
113 | hint.ai_socktype = SOCK_STREAM; |
114 | // hint.ai_flags = AI_CANONNAME; |
115 | rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result); |
116 | |
117 | if (rc == 0) { |
118 | struct addrinfo *cur = result; |
119 | unsigned cnt = 0; |
120 | |
121 | printf("%-10s %s\n", header, hostname); |
122 | // puts(cur->ai_canonname); ? |
123 | while (cur) { |
124 | char *dotted, *revhost; |
125 | dotted = xmalloc_sockaddr2dotted_noport(cur->ai_addr); |
126 | revhost = xmalloc_sockaddr2hostonly_noport(cur->ai_addr); |
127 | |
128 | printf("Address %u: %s%c", ++cnt, dotted, revhost ? ' ' : '\n'); |
129 | if (revhost) { |
130 | puts(revhost); |
131 | if (ENABLE_FEATURE_CLEAN_UP) |
132 | free(revhost); |
133 | } |
134 | if (ENABLE_FEATURE_CLEAN_UP) |
135 | free(dotted); |
136 | cur = cur->ai_next; |
137 | } |
138 | } else { |
139 | #if ENABLE_VERBOSE_RESOLUTION_ERRORS |
140 | bb_error_msg("can't resolve '%s': %s", hostname, gai_strerror(rc)); |
141 | #else |
142 | bb_error_msg("can't resolve '%s'", hostname); |
143 | #endif |
144 | } |
145 | if (ENABLE_FEATURE_CLEAN_UP && result) |
146 | freeaddrinfo(result); |
147 | return (rc != 0); |
148 | } |
149 | |
150 | /* lookup the default nameserver and display it */ |
151 | static void server_print(void) |
152 | { |
153 | char *server; |
154 | struct sockaddr *sa = NULL; |
155 | |
156 | #if ENABLE_FEATURE_IPV6 |
157 | # ifdef ANDROID |
158 | if (EXT(_res).ext) |
159 | sa = (struct sockaddr*) &EXT(_res).ext->nsaddrs[0]; |
160 | # else |
161 | sa = (struct sockaddr*)_res._u._ext.nsaddrs[0]; |
162 | # endif |
163 | |
164 | if (!sa) |
165 | #endif |
166 | sa = (struct sockaddr*) &_res.nsaddr_list[0]; |
167 | server = xmalloc_sockaddr2dotted_noport(sa); |
168 | |
169 | print_host(server, "Server:"); |
170 | if (ENABLE_FEATURE_CLEAN_UP) |
171 | free(server); |
172 | bb_putchar('\n'); |
173 | } |
174 | |
175 | /* alter the global _res nameserver structure to use |
176 | an explicit dns server instead of what is in /etc/resolv.conf */ |
177 | static void set_default_dns(const char *server) |
178 | { |
179 | len_and_sockaddr *lsa; |
180 | |
181 | if (!server) |
182 | return; |
183 | |
184 | /* NB: this works even with, say, "[::1]:53"! :) */ |
185 | lsa = xhost2sockaddr(server, 53); |
186 | |
187 | if (lsa->u.sa.sa_family == AF_INET) { |
188 | _res.nscount = 1; |
189 | /* struct copy */ |
190 | _res.nsaddr_list[0] = lsa->u.sin; |
191 | } |
192 | |
193 | #if ENABLE_FEATURE_IPV6 |
194 | /* Hoped libc can cope with IPv4 address there too. |
195 | * No such luck, glibc 2.4 segfaults even with IPv6, |
196 | * maybe I misunderstand how to make glibc use IPv6 addr? |
197 | * (uclibc 0.9.31+ should work) */ |
198 | if (lsa->u.sa.sa_family == AF_INET6) { |
199 | // glibc neither SEGVs nor sends any dgrams with this |
200 | // (strace shows no socket ops): |
201 | //_res.nscount = 0; |
202 | #ifdef ANDROID |
203 | if (EXT(_res).ext) { |
204 | EXT(_res).nscount = 1; |
205 | memcpy(&EXT(_res).ext->nsaddrs[0].sin6, &lsa->u.sin6, |
206 | sizeof(struct sockaddr_in6)); |
207 | } |
208 | #else |
209 | /* store a pointer to part of malloc'ed lsa */ |
210 | _res._u._ext.nscount = 1; |
211 | _res._u._ext.nsaddrs[0] = &lsa->u.sin6; |
212 | /* must not free(lsa)! */ |
213 | #endif |
214 | } |
215 | #endif |
216 | } |
217 | |
218 | int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
219 | int nslookup_main(int argc, char **argv) |
220 | { |
221 | /* We allow 1 or 2 arguments. |
222 | * The first is the name to be looked up and the second is an |
223 | * optional DNS server with which to do the lookup. |
224 | * More than 3 arguments is an error to follow the pattern of the |
225 | * standard nslookup */ |
226 | if (!argv[1] || argv[1][0] == '-' || argc > 3) |
227 | bb_show_usage(); |
228 | |
229 | /* initialize DNS structure _res used in printing the default |
230 | * name server and in the explicit name server option feature. */ |
231 | res_init(); |
232 | |
233 | #ifdef ANDROID |
234 | res_ninit(&_res); |
235 | #endif |
236 | |
237 | /* rfc2133 says this enables IPv6 lookups */ |
238 | /* (but it also says "may be enabled in /etc/resolv.conf") */ |
239 | /*_res.options |= RES_USE_INET6;*/ |
240 | |
241 | set_default_dns(argv[2]); |
242 | |
243 | server_print(); |
244 | |
245 | /* getaddrinfo and friends are free to request a resolver |
246 | * reinitialization. Just in case, set_default_dns() again |
247 | * after getaddrinfo (in server_print). This reportedly helps |
248 | * with bug 675 "nslookup does not properly use second argument" |
249 | * at least on Debian Wheezy and Openwrt AA (eglibc based). |
250 | */ |
251 | set_default_dns(argv[2]); |
252 | |
253 | return print_host(argv[1], "Name:"); |
254 | } |
255 |