nslookup: make it more IPv6 friendly
[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 tarball for details.
12  */
13
14 #include <resolv.h>
15 #include "busybox.h"
16
17 /*
18  *  I'm only implementing non-interactive mode;
19  *  I totally forgot nslookup even had an interactive mode.
20  */
21
22 /* Examples of 'standard' nslookup output
23  * $ nslookup yahoo.com
24  * Server:         128.193.0.10
25  * Address:        128.193.0.10#53
26  * 
27  * Non-authoritative answer:
28  * Name:   yahoo.com
29  * Address: 216.109.112.135
30  * Name:   yahoo.com
31  * Address: 66.94.234.13
32  *
33  * $ nslookup 204.152.191.37
34  * Server:         128.193.4.20
35  * Address:        128.193.4.20#53
36  * 
37  * Non-authoritative answer:
38  * 37.191.152.204.in-addr.arpa     canonical name = 37.32-27.191.152.204.in-addr.arpa.
39  * 37.32-27.191.152.204.in-addr.arpa       name = zeus-pub2.kernel.org.
40  * 
41  * Authoritative answers can be found from:
42  * 32-27.191.152.204.in-addr.arpa  nameserver = ns1.kernel.org.
43  * 32-27.191.152.204.in-addr.arpa  nameserver = ns2.kernel.org.
44  * 32-27.191.152.204.in-addr.arpa  nameserver = ns3.kernel.org.
45  * ns1.kernel.org  internet address = 140.211.167.34
46  * ns2.kernel.org  internet address = 204.152.191.4
47  * ns3.kernel.org  internet address = 204.152.191.36
48  */
49
50 static int sockaddr_to_dotted(struct sockaddr *saddr, char *buf, int buflen)
51 {
52         if (buflen <= 0) return -1;
53         buf[0] = '\0';
54         if (saddr->sa_family == AF_INET) {
55                 inet_ntop(AF_INET, &((struct sockaddr_in*)saddr)->sin_addr, buf, buflen);
56                 return 0;
57         }
58         if (saddr->sa_family == AF_INET6) {
59                 inet_ntop(AF_INET6, &((struct sockaddr_in6*)saddr)->sin6_addr, buf, buflen);
60                 return 0;
61         }
62         return -1;
63 }
64
65 static int print_host(const char *hostname, const char *header)
66 {
67         char str[128];  /* IPv6 address will fit, hostnames hopefully too */
68         struct addrinfo *result = NULL;
69         int rc;
70         struct addrinfo hint;
71
72         memset(&hint, 0 , sizeof(hint));
73         /* hint.ai_family = AF_UNSPEC; - zero anyway */
74         /* Needed. Or else we will get each address thrice (or more)
75          * for each possible socket type (tcp,udp,raw...): */
76         hint.ai_socktype = SOCK_STREAM;
77         // hint.ai_flags = AI_CANONNAME;
78         rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result);
79         if (!rc) {
80                 struct addrinfo *cur = result;
81                 // printf("%s\n", cur->ai_canonname); ?
82                 while (cur) {
83                         sockaddr_to_dotted(cur->ai_addr, str, sizeof(str));
84                         printf("%s  %s\nAddress: %s", header, hostname, str);
85                         if (getnameinfo(cur->ai_addr, cur->ai_addrlen, str, sizeof(str), NULL, 0, NI_NAMEREQD))
86                                 str[0] = '\0';
87                         printf(" %s\n", str);
88                         cur = cur->ai_next;
89                 }
90         } else {
91                 bb_error_msg("getaddrinfo('%s') failed: %s", hostname, gai_strerror(rc));
92         }
93         freeaddrinfo(result);
94         return (rc != 0);
95 }
96
97
98 /* alter the global _res nameserver structure to use
99    an explicit dns server instead of what is in /etc/resolv.h */
100 static void set_default_dns(char *server)
101 {
102         struct in_addr server_in_addr;
103
104         if (inet_pton(AF_INET, server, &server_in_addr) > 0) {
105                 _res.nscount = 1;
106                 _res.nsaddr_list[0].sin_addr = server_in_addr;
107         }
108 }
109
110
111 /* lookup the default nameserver and display it */
112 static void server_print(void)
113 {
114         char str[INET6_ADDRSTRLEN];
115
116         sockaddr_to_dotted((struct sockaddr*)&_res.nsaddr_list[0], str, sizeof(str));
117         print_host(str, "Server:");
118         puts("");
119 }
120
121
122 int nslookup_main(int argc, char **argv)
123 {
124         /*
125         * initialize DNS structure _res used in printing the default
126         * name server and in the explicit name server option feature.
127         */
128
129         res_init();
130
131         /*
132         * We allow 1 or 2 arguments.
133         * The first is the name to be looked up and the second is an
134         * optional DNS server with which to do the lookup.
135         * More than 3 arguments is an error to follow the pattern of the
136         * standard nslookup
137         */
138
139         if (argc < 2 || *argv[1] == '-' || argc > 3)
140                 bb_show_usage();
141         else if(argc == 3)
142                 set_default_dns(argv[2]);
143
144         server_print();
145         return print_host(argv[1], "Name:  ");
146 }
147
148 /* $Id: nslookup.c,v 1.33 2004/10/13 07:25:01 andersen Exp $ */