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 no idea
20 -t not a clue
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 unsigned ttl, pri, v[5];
73 unsigned char qbuf[280], abuf[512], *p;
74 char *name, *nsname, rrname[256], plname[640], ptrbuf[128];
75 struct addrinfo *ai, iplit_hints = { .ai_flags = AI_NUMERICHOST };
76
77 name = *toys.optargs;
78 nsname = toys.optargs[1];
79
80 if (!TT.type_str && (toys.optflags & FLAG_a)) TT.type_str = "255";
81 if (!getaddrinfo(name, 0, &iplit_hints, &ai)) {
82 unsigned char *a;
83 static const char xdigits[] = "0123456789abcdef";
84
85 if (ai->ai_family == AF_INET) {
86 a = (void *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr;
87 snprintf(ptrbuf, sizeof(ptrbuf), "%d.%d.%d.%d.in-addr.arpa",
88 a[3], a[2], a[1], a[0]);
89 } else if (ai->ai_family == AF_INET6) {
90 a = (void *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
91 for (j=0, i=15; i>=0; i--) {
92 ptrbuf[j++] = xdigits[a[i]&15];
93 ptrbuf[j++] = '.';
94 ptrbuf[j++] = xdigits[a[i]>>4];
95 ptrbuf[j++] = '.';
96 }
97 strcpy(ptrbuf+j, "ip6.arpa");
98 }
99 name = ptrbuf;
100 if (!TT.type_str) TT.type_str="12";
101 } else if (!TT.type_str) TT.type_str="1";
102
103 if (TT.type_str[0]-'0' < 10u) type = atoi(TT.type_str);
104 else {
105 type = -1;
106 for (i=0; i<ARRAY_LEN(rrt); i++) {
107 if (rrt[i].name && !strcasecmp(TT.type_str, rrt[i].name)) {
108 type = i;
109 break;
110 }
111 }
112 if (!strcasecmp(TT.type_str, "any")) type = 255;
113 if (type < 0) error_exit("Invalid query type: %s", TT.type_str);
114 }
115
116 qlen = res_mkquery(0, name, 1, type, 0, 0, 0, qbuf, sizeof qbuf);
117 if (qlen < 0) error_exit("Invalid query parameters: %s", name);
118
119 if (nsname) {
120 struct addrinfo ns_hints = { .ai_socktype = SOCK_DGRAM };
121
122 if ((ret = getaddrinfo(nsname, "53", &ns_hints, &ai)) < 0)
123 error_exit("Error looking up server name: %s", gai_strerror(ret));
124 int s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
125 if (s < 0 || connect(s, ai->ai_addr, ai->ai_addrlen) < 0)
126 perror_exit("Socket error");
127 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &(struct timeval){ .tv_sec = 5 },
128 sizeof(struct timeval));
129 printf("Using domain server %s:\n", nsname);
130 send(s, qbuf, qlen, 0);
131 alen = recv(s, abuf, sizeof abuf, 0);
132 } else alen = res_send(qbuf, qlen, abuf, sizeof abuf);
133
134 if (alen < 12) error_exit("Host not found.");
135
136 rcode = abuf[3] & 15;
137
138 if (verbose) {
139 printf("rcode = %d (%s), ancount = %d\n",
140 rcode, rct[rcode], 256*abuf[6] + abuf[7]);
141 if (!(abuf[2] & 4)) printf("The following answer is not authoritative:\n");
142 }
143
144 if (rcode) error_exit("Host not found.");
145
146 p = abuf + 12;
147 for (sec=0; sec<4; sec++) {
148 count = 256*abuf[4+2*sec] + abuf[5+2*sec];
149 if (verbose && count>0 && sec>1)
150 puts(sec==2 ? "For authoritative answers, see:"
151 : "Additional information:");
152
153 for (; count--; p += pllen) {
154 p += dn_expand(abuf, abuf+alen, p, rrname, sizeof(rrname));
155 type = (p[0]<<8) + p[1];
156 p += 4;
157 if (!sec) continue;
158 ttl = (p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3];
159 p += 4;
160 pllen = (p[0]<<8) + p[1];
161 p += 2;
162
163 switch (type<sizeof(rrt)/sizeof(*rrt) ? rrt[type].pl : 0) {
164 case PL_IP:
165 inet_ntop(rrt[type].af, p, plname, sizeof plname);
166 break;
167 case PL_NAME:
168 dn_expand(abuf, abuf+alen, p, plname, sizeof plname);
169 break;
170 case PL_TEXT:
171 snprintf(plname, sizeof plname, "\"%.*s\"", pllen, p);
172 break;
173 case PL_SOA:
174 i = dn_expand(abuf, abuf+alen, p, plname, sizeof plname - 1);
175 strcat(plname, " ");
176 i += dn_expand(abuf, abuf+alen, p+i, plname+strlen(plname),
177 sizeof(plname)-strlen(plname));
178 for (j=0; j<5; j++)
179 v[j] = (p[i+4*j]<<24)+(p[1+i+4*j]<<16)+(p[2+i+4*j]<<8)+p[3+i+4*j];
180 snprintf(plname+strlen(plname), sizeof(plname)-strlen(plname),
181 "(\n\t\t%u\t;serial (version)\n"
182 "\t\t%u\t;refresh period\n"
183 "\t\t%u\t;retry interval\n"
184 "\t\t%u\t;expire time\n"
185 "\t\t%u\t;default ttl\n"
186 "\t\t)", v[0], v[1], v[2], v[3], v[4]);
187 break;
188 case PL_MX:
189 pri = (p[0]<<8)+p[1];
190 snprintf(plname, sizeof(plname), verbose ? "%d " : "(pri=%d) by ", pri);
191 dn_expand(abuf, abuf+alen, p+2, plname+strlen(plname),
192 sizeof plname - strlen(plname));
193 break;
194 case PL_SRV:
195 for (j=0; j<3; j++) v[j] = (p[2*j]<<8) + p[1+2*j];
196 snprintf(plname, sizeof(plname), "%u %u %u ", v[0], v[1], v[2]);
197 dn_expand(abuf, abuf+alen, p+6, plname+strlen(plname),
198 sizeof plname - strlen(plname));
199 break;
200 default:
201 printf("%s unsupported RR type %u\n", rrname, type);
202 continue;
203 }
204
205 if (verbose)
206 printf("%s\t%u\tIN %s\t%s\n", rrname, ttl, rrt[type].name, plname);
207 else if (rrt[type].msg)
208 printf("%s %s %s\n", rrname, rrt[type].msg, plname);
209 }
210 if (!verbose && sec==1) break;
211 }
212
213 toys.exitval = rcode;
214 }
215