1 /* host.c - DNS lookup utility
2  *
3  * Copyright 2014 Rich Felker <dalias@aerifal.cx>
4  *
5  * No standard, but there's a version in bind9
6 
7 USE_HOST(NEWTOY(host, "<1>2avt:", TOYFLAG_USR|TOYFLAG_BIN))
8 
9 config HOST
10   bool "host"
11   default n
12   help
13     usage: host [-av] [-t TYPE] NAME [SERVER]
14 
15     Perform DNS lookup on NAME, which can be a domain name to lookup,
16     or an IPv4 dotted or IPv6 colon-separated address to reverse lookup.
17     SERVER (if present) is the DNS server to use.
18 
19     -a	-v -t ANY
20     -t TYPE	query records of type TYPE
21     -v	verbose
22 */
23 
24 #define FOR_host
25 #include "toys.h"
26 
27 GLOBALS(
28   char *type_str;
29 )
30 
31 #include <resolv.h>
32 
33 #define PL_IP 1
34 #define PL_NAME 2
35 #define PL_DATA 3
36 #define PL_TEXT 4
37 #define PL_SOA 5
38 #define PL_MX 6
39 #define PL_SRV 7
40 
41 static const struct rrt {
42   const char *name;
43   const char *msg;
44   int pl;
45   int af;
46 } rrt[] = {
47   [1] = { "A", "has address", PL_IP, AF_INET },
48   [28] = { "AAAA", "has address", PL_IP, AF_INET6 },
49   [2] = { "NS", "name server", PL_NAME },
50   [5] = { "CNAME", "is a nickname for", PL_NAME },
51   [16] = { "TXT", "descriptive text", PL_TEXT },
52   [6] = { "SOA", "start of authority", PL_SOA },
53   [12] = { "PTR", "domain name pointer", PL_NAME },
54   [15] = { "MX", "mail is handled", PL_MX },
55   [33] = { "SRV", "mail is handled", PL_SRV },
56   [255] = { "*", 0, 0 },
57 };
58 
59 static const char rct[16][32] = {
60   "Success",
61   "Format error",
62   "Server failure",
63   "Non-existant domain",
64   "Not implemented",
65   "Refused",
66 };
67 
host_main(void)68 void host_main(void)
69 {
70   int verbose=(toys.optflags & (FLAG_a|FLAG_v)), type,
71       i, j, ret, sec, count, rcode, qlen, alen, pllen = 0,
72       abuf_len = 65536; // Largest TCP response.
73   unsigned ttl, pri, v[5];
74   unsigned char *abuf = xmalloc(abuf_len);
75   char *rrname = xmalloc(MAXDNAME);
76   unsigned char qbuf[280], *p;
77   char *name, *nsname, plname[640], ptrbuf[128];
78   struct addrinfo *ai, iplit_hints = { .ai_flags = AI_NUMERICHOST };
79 
80   name = *toys.optargs;
81   nsname = toys.optargs[1];
82 
83   if (!TT.type_str && (toys.optflags & FLAG_a)) TT.type_str = "255";
84   if (!getaddrinfo(name, 0, &iplit_hints, &ai)) {
85     unsigned char *a;
86     static const char xdigits[] = "0123456789abcdef";
87 
88     if (ai->ai_family == AF_INET) {
89       a = (void *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr;
90       snprintf(ptrbuf, sizeof(ptrbuf), "%d.%d.%d.%d.in-addr.arpa",
91         a[3], a[2], a[1], a[0]);
92     } else if (ai->ai_family == AF_INET6) {
93       a = (void *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
94       for (j=0, i=15; i>=0; i--) {
95         ptrbuf[j++] = xdigits[a[i]&15];
96         ptrbuf[j++] = '.';
97         ptrbuf[j++] = xdigits[a[i]>>4];
98         ptrbuf[j++] = '.';
99       }
100       strcpy(ptrbuf+j, "ip6.arpa");
101     }
102     name = ptrbuf;
103     if (!TT.type_str) TT.type_str="12";
104   } else if (!TT.type_str) TT.type_str="1";
105 
106   if (TT.type_str[0]-'0' < 10u) type = atoi(TT.type_str);
107   else {
108     type = -1;
109     for (i=0; i<ARRAY_LEN(rrt); i++) {
110       if (rrt[i].name && !strcasecmp(TT.type_str, rrt[i].name)) {
111         type = i;
112         break;
113       }
114     }
115     if (!strcasecmp(TT.type_str, "any")) type = 255;
116     if (type < 0) error_exit("Invalid query type: %s", TT.type_str);
117   }
118 
119   qlen = res_mkquery(0, name, 1, type, 0, 0, 0, qbuf, sizeof(qbuf));
120   if (qlen < 0) error_exit("Invalid query parameters: %s", name);
121 
122   if (nsname) {
123     struct addrinfo ns_hints = { .ai_socktype = SOCK_DGRAM };
124 
125     if ((ret = getaddrinfo(nsname, "53", &ns_hints, &ai)) < 0)
126       error_exit("Error looking up server name: %s", gai_strerror(ret));
127     int s = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
128     xconnect(s, ai->ai_addr, ai->ai_addrlen);
129     setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &(struct timeval){ .tv_sec = 5 },
130       sizeof(struct timeval));
131     printf("Using domain server %s:\n", nsname);
132     send(s, qbuf, qlen, 0);
133     alen = recv(s, abuf, abuf_len, 0);
134   } else alen = res_send(qbuf, qlen, abuf, abuf_len);
135 
136   if (alen < 12) error_exit("Host not found.");
137 
138   rcode = abuf[3] & 15;
139 
140   if (verbose) {
141     printf("rcode = %d (%s), ancount = %d\n",
142       rcode, rct[rcode], 256*abuf[6] + abuf[7]);
143     if (!(abuf[2] & 4)) printf("The following answer is not authoritative:\n");
144   }
145 
146   if (rcode) error_exit("Host not found.");
147 
148   p = abuf + 12;
149   for (sec=0; sec<4; sec++) {
150     count = 256*abuf[4+2*sec] + abuf[5+2*sec];
151     if (verbose && count>0 && sec>1)
152       puts(sec==2 ? "For authoritative answers, see:"
153         : "Additional information:");
154 
155     for (; count--; p += pllen) {
156       p += dn_expand(abuf, abuf+alen, p, rrname, MAXDNAME);
157       type = (p[0]<<8) + p[1];
158       p += 4;
159       if (!sec) continue;
160       ttl = (p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3];
161       p += 4;
162       pllen = (p[0]<<8) + p[1];
163       p += 2;
164 
165       switch (type<ARRAY_LEN(rrt) ? rrt[type].pl : 0) {
166       case PL_IP:
167         inet_ntop(rrt[type].af, p, plname, sizeof(plname));
168         break;
169       case PL_NAME:
170         dn_expand(abuf, abuf+alen, p, plname, sizeof(plname));
171         break;
172       case PL_TEXT:
173         snprintf(plname, sizeof(plname), "\"%.*s\"", pllen, p);
174         break;
175       case PL_SOA:
176         i = dn_expand(abuf, abuf+alen, p, plname, sizeof(plname) - 1);
177         strcat(plname, " ");
178         i += dn_expand(abuf, abuf+alen, p+i, plname+strlen(plname),
179           sizeof(plname)-strlen(plname));
180         for (j=0; j<5; j++)
181           v[j] = (p[i+4*j]<<24)+(p[1+i+4*j]<<16)+(p[2+i+4*j]<<8)+p[3+i+4*j];
182         snprintf(plname+strlen(plname), sizeof(plname)-strlen(plname),
183           "(\n\t\t%u\t;serial (version)\n"
184           "\t\t%u\t;refresh period\n"
185           "\t\t%u\t;retry interval\n"
186           "\t\t%u\t;expire time\n"
187           "\t\t%u\t;default ttl\n"
188           "\t\t)", v[0], v[1], v[2], v[3], v[4]);
189         break;
190       case PL_MX:
191         pri = (p[0]<<8)+p[1];
192         snprintf(plname, sizeof(plname), verbose ? "%d " : "(pri=%d) by ", pri);
193         dn_expand(abuf, abuf+alen, p+2, plname+strlen(plname),
194           sizeof(plname) - strlen(plname));
195         break;
196       case PL_SRV:
197         for (j=0; j<3; j++) v[j] = (p[2*j]<<8) + p[1+2*j];
198         snprintf(plname, sizeof(plname), "%u %u %u ", v[0], v[1], v[2]);
199         dn_expand(abuf, abuf+alen, p+6, plname+strlen(plname),
200           sizeof(plname) - strlen(plname));
201         break;
202       default:
203         printf("%s unsupported RR type %u\n", rrname, type);
204         continue;
205       }
206 
207       if (verbose)
208         printf("%s\t%u\tIN %s\t%s\n", rrname, ttl, rrt[type].name, plname);
209       else if (rrt[type].msg)
210         printf("%s %s %s\n", rrname, rrt[type].msg, plname);
211     }
212     if (!verbose && sec==1) break;
213   }
214 
215   if (CFG_TOYBOX_FREE) {
216     free(abuf);
217     free(rrname);
218   }
219   toys.exitval = rcode;
220 }
221