dnsdomainname,hostname: make NOEXEC
[oweals/busybox.git] / networking / nslookup.c
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 (4.5 kb)"
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 /*
41  * I'm only implementing non-interactive mode;
42  * I totally forgot nslookup even had an interactive mode.
43  *
44  * This applet is the only user of res_init(). Without it,
45  * you may avoid pulling in _res global from libc.
46  */
47
48 /* Examples of 'standard' nslookup output
49  * $ nslookup yahoo.com
50  * Server:         128.193.0.10
51  * Address:        128.193.0.10#53
52  *
53  * Non-authoritative answer:
54  * Name:   yahoo.com
55  * Address: 216.109.112.135
56  * Name:   yahoo.com
57  * Address: 66.94.234.13
58  *
59  * $ nslookup 204.152.191.37
60  * Server:         128.193.4.20
61  * Address:        128.193.4.20#53
62  *
63  * Non-authoritative answer:
64  * 37.191.152.204.in-addr.arpa     canonical name = 37.32-27.191.152.204.in-addr.arpa.
65  * 37.32-27.191.152.204.in-addr.arpa       name = zeus-pub2.kernel.org.
66  *
67  * Authoritative answers can be found from:
68  * 32-27.191.152.204.in-addr.arpa  nameserver = ns1.kernel.org.
69  * 32-27.191.152.204.in-addr.arpa  nameserver = ns2.kernel.org.
70  * 32-27.191.152.204.in-addr.arpa  nameserver = ns3.kernel.org.
71  * ns1.kernel.org  internet address = 140.211.167.34
72  * ns2.kernel.org  internet address = 204.152.191.4
73  * ns3.kernel.org  internet address = 204.152.191.36
74  */
75
76 static int print_host(const char *hostname, const char *header)
77 {
78         /* We can't use xhost2sockaddr() - we want to get ALL addresses,
79          * not just one */
80         struct addrinfo *result = NULL;
81         int rc;
82         struct addrinfo hint;
83
84         memset(&hint, 0 , sizeof(hint));
85         /* hint.ai_family = AF_UNSPEC; - zero anyway */
86         /* Needed. Or else we will get each address thrice (or more)
87          * for each possible socket type (tcp,udp,raw...): */
88         hint.ai_socktype = SOCK_STREAM;
89         // hint.ai_flags = AI_CANONNAME;
90         rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result);
91
92         if (rc == 0) {
93                 struct addrinfo *cur = result;
94                 unsigned cnt = 0;
95
96                 printf("%-10s %s\n", header, hostname);
97                 // puts(cur->ai_canonname); ?
98                 while (cur) {
99                         char *dotted, *revhost;
100                         dotted = xmalloc_sockaddr2dotted_noport(cur->ai_addr);
101                         revhost = xmalloc_sockaddr2hostonly_noport(cur->ai_addr);
102
103                         printf("Address %u: %s%c", ++cnt, dotted, revhost ? ' ' : '\n');
104                         if (revhost) {
105                                 puts(revhost);
106                                 if (ENABLE_FEATURE_CLEAN_UP)
107                                         free(revhost);
108                         }
109                         if (ENABLE_FEATURE_CLEAN_UP)
110                                 free(dotted);
111                         cur = cur->ai_next;
112                 }
113         } else {
114 #if ENABLE_VERBOSE_RESOLUTION_ERRORS
115                 bb_error_msg("can't resolve '%s': %s", hostname, gai_strerror(rc));
116 #else
117                 bb_error_msg("can't resolve '%s'", hostname);
118 #endif
119         }
120         if (ENABLE_FEATURE_CLEAN_UP && result)
121                 freeaddrinfo(result);
122         return (rc != 0);
123 }
124
125 /* lookup the default nameserver and display it */
126 static void server_print(void)
127 {
128         char *server;
129         struct sockaddr *sa;
130
131 #if ENABLE_FEATURE_IPV6
132         sa = (struct sockaddr*)_res._u._ext.nsaddrs[0];
133         if (!sa)
134 #endif
135                 sa = (struct sockaddr*)&_res.nsaddr_list[0];
136         server = xmalloc_sockaddr2dotted_noport(sa);
137
138         print_host(server, "Server:");
139         if (ENABLE_FEATURE_CLEAN_UP)
140                 free(server);
141         bb_putchar('\n');
142 }
143
144 /* alter the global _res nameserver structure to use
145    an explicit dns server instead of what is in /etc/resolv.conf */
146 static void set_default_dns(const char *server)
147 {
148         len_and_sockaddr *lsa;
149
150         if (!server)
151                 return;
152
153         /* NB: this works even with, say, "[::1]:5353"! :) */
154         lsa = xhost2sockaddr(server, 53);
155
156         if (lsa->u.sa.sa_family == AF_INET) {
157                 _res.nscount = 1;
158                 /* struct copy */
159                 _res.nsaddr_list[0] = lsa->u.sin;
160         }
161 #if ENABLE_FEATURE_IPV6
162         /* Hoped libc can cope with IPv4 address there too.
163          * No such luck, glibc 2.4 segfaults even with IPv6,
164          * maybe I misunderstand how to make glibc use IPv6 addr?
165          * (uclibc 0.9.31+ should work) */
166         if (lsa->u.sa.sa_family == AF_INET6) {
167                 // glibc neither SEGVs nor sends any dgrams with this
168                 // (strace shows no socket ops):
169                 //_res.nscount = 0;
170                 _res._u._ext.nscount = 1;
171                 /* store a pointer to part of malloc'ed lsa */
172                 _res._u._ext.nsaddrs[0] = &lsa->u.sin6;
173                 /* must not free(lsa)! */
174         }
175 #endif
176 }
177
178 int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
179 int nslookup_main(int argc, char **argv)
180 {
181         /* We allow 1 or 2 arguments.
182          * The first is the name to be looked up and the second is an
183          * optional DNS server with which to do the lookup.
184          * More than 3 arguments is an error to follow the pattern of the
185          * standard nslookup */
186         if (!argv[1] || argv[1][0] == '-' || argc > 3)
187                 bb_show_usage();
188
189         /* initialize DNS structure _res used in printing the default
190          * name server and in the explicit name server option feature. */
191         res_init();
192         /* rfc2133 says this enables IPv6 lookups */
193         /* (but it also says "may be enabled in /etc/resolv.conf") */
194         /*_res.options |= RES_USE_INET6;*/
195
196         set_default_dns(argv[2]);
197
198         server_print();
199
200         /* getaddrinfo and friends are free to request a resolver
201          * reinitialization. Just in case, set_default_dns() again
202          * after getaddrinfo (in server_print). This reportedly helps
203          * with bug 675 "nslookup does not properly use second argument"
204          * at least on Debian Wheezy and Openwrt AA (eglibc based).
205          */
206         set_default_dns(argv[2]);
207
208         return print_host(argv[1], "Name:");
209 }