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/* 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 76static 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 */ 126static 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 */ 146static 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 178int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 179int 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} 210