add netbsd nl(1)
[rofl0r-hardcore-utils.git] / host.c
blob21d3c7058aac06a979d9c1e0c8e42fa6c50b1b4d
1 /*
2 * host.c - Light implementation of the classic host utility
4 * Copyright 2014 Rich Felker
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
25 #include <resolv.h>
26 #include <netdb.h>
27 #include <arpa/inet.h>
28 #include <netinet/in.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <sys/time.h>
35 #include <sys/socket.h>
37 #define PL_IP 1
38 #define PL_NAME 2
39 #define PL_DATA 3
40 #define PL_TEXT 4
41 #define PL_SOA 5
42 #define PL_MX 6
43 #define PL_SRV 7
45 static const struct rrt {
46 const char *name;
47 const char *msg;
48 int pl;
49 int af;
50 } rrt[] = {
51 [1] = { "A", "has address", PL_IP, AF_INET },
52 [28] = { "AAAA", "has address", PL_IP, AF_INET6 },
53 [2] = { "NS", "name server", PL_NAME },
54 [5] = { "CNAME", "is a nickname for", PL_NAME },
55 [16] = { "TXT", "descriptive text", PL_TEXT },
56 [6] = { "SOA", "start of authority", PL_SOA },
57 [12] = { "PTR", "domain name pointer", PL_NAME },
58 [15] = { "MX", "mail is handled", PL_MX },
59 [33] = { "SRV", "mail is handled", PL_SRV },
60 [255] = { "*", 0, 0 },
63 static const char rct[16][32] = {
64 "Success",
65 "Format error",
66 "Server failure",
67 "Non-existant domain",
68 "Not implemented",
69 "Refused",
72 int main(int argc, char **argv)
74 int verbose=0, type;
75 char *type_str=0, *name, *nsname;
76 int c, i, j, ret, sec, count, rcode, qlen, alen, pllen = 0;
77 unsigned ttl, pri, v[5];
78 unsigned char qbuf[280], abuf[512], *p;
79 char rrname[256], plname[640], ptrbuf[80];
80 struct addrinfo *ai, iplit_hints = { .ai_flags = AI_NUMERICHOST };
82 while ((c = getopt(argc, argv, "avt:")) != EOF) switch(c) {
83 case 'a': type_str="255"; verbose=1; break;
84 case 'v': verbose=1; break;
85 case 't': type_str=optarg; break;
86 default: return 1;
88 if (!argv[optind]) {
89 fprintf(stderr, "Usage: %s [-a] [-v] [-t type] "
90 "hostname [nameserver]\n", argv[0]);
91 return 1;
93 name = argv[optind];
94 nsname = argv[optind+1];
96 if (!getaddrinfo(name, 0, &iplit_hints, &ai)) {
97 switch (ai->ai_family) {
98 unsigned char *a;
99 static const char xdigits[] = "0123456789abcdef";
100 case AF_INET:
101 a = (void *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr;
102 snprintf(ptrbuf, sizeof ptrbuf,
103 "%d.%d.%d.%d.in-addr.arpa",
104 a[3], a[2], a[1], a[0]);
105 break;
106 case AF_INET6:
107 a = (void *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
108 for (j=0, i=15; i>=0; i--) {
109 ptrbuf[j++] = xdigits[a[i]&15];
110 ptrbuf[j++] = '.';
111 ptrbuf[j++] = xdigits[a[i]>>4];
112 ptrbuf[j++] = '.';
114 strcpy(ptrbuf+j, "ip6.arpa");
115 break;
117 name = ptrbuf;
118 if (!type_str) type_str="12";
119 } else {
120 if (!type_str) type_str="1";
123 if (type_str[0]-'0' < 10u) {
124 type = atoi(type_str);
125 } else {
126 type = -1;
127 for (i=0; i < sizeof rrt / sizeof *rrt; i++) {
128 if (rrt[i].name && !strcasecmp(type_str, rrt[i].name)) {
129 type = i;
130 break;
133 if (!strcasecmp(type_str, "any")) type = 255;
134 if (type < 0) {
135 fprintf(stderr, "Invalid query type: %s\n", type_str);
136 return 1;
140 qlen = res_mkquery(0, name, 1, type, 0, 0, 0, qbuf, sizeof qbuf);
141 if (qlen < 0) {
142 fprintf(stderr, "Invalid query parameters: %s", name);
143 return 1;
146 if (nsname) {
147 struct addrinfo ns_hints = { .ai_socktype = SOCK_DGRAM };
148 if ((ret = getaddrinfo(nsname, "53", &ns_hints, &ai)) < 0) {
149 fprintf(stderr, "Error looking up server name: %s\n",
150 gai_strerror(ret));
151 return 1;
153 int s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
154 if (s < 0 || connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
155 fprintf(stderr, "Socket error: %s\n", strerror(errno));
156 return 1;
158 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO,
159 &(struct timeval){ .tv_sec = 5 },
160 sizeof (struct timeval));
161 printf("Using domain server %s:\n", nsname);
162 send(s, qbuf, qlen, 0);
163 alen = recv(s, abuf, sizeof abuf, 0);
164 } else {
165 alen = res_send(qbuf, qlen, abuf, sizeof abuf);
168 if (alen < 12) {
169 fprintf(stderr, "Host not found, try again.\n");
170 return 1;
173 rcode = abuf[3] & 15;
175 if (verbose) {
176 printf("rcode = %d (%s), ancount = %d\n",
177 rcode, rct[rcode], 256*abuf[6] + abuf[7]);
178 if (!(abuf[2] & 4))
179 printf("The following answer is not authoritative:\n");
182 if (rcode) {
183 fprintf(stderr, "Host not found.\n");
184 if (!verbose) return 1;
187 p = abuf + 12;
188 for (sec=0; sec<4; sec++) {
189 count = 256*abuf[4+2*sec] + abuf[5+2*sec];
190 if (verbose && count>0 && sec>1) {
191 puts(sec==2 ? "For authoritative answers, see:"
192 : "Additional information:");
194 for (; count--; p += pllen) {
195 p += dn_expand(abuf, abuf+alen, p, rrname, sizeof rrname);
196 type = 256*p[0] + p[1];
197 p += 4;
198 if (!sec) continue;
199 ttl = 16777216*p[0] + 65536*p[1] + 256*p[2] + p[3];
200 p += 4;
201 pllen = 256*p[0] + p[1];
202 p += 2;
203 switch (type<sizeof rrt/sizeof *rrt ? rrt[type].pl : 0) {
204 case PL_IP:
205 inet_ntop(rrt[type].af, p, plname, sizeof plname);
206 break;
207 case PL_NAME:
208 dn_expand(abuf, abuf+alen, p, plname, sizeof plname);
209 break;
210 case PL_TEXT:
211 snprintf(plname, sizeof plname, "\"%.*s\"", pllen, p);
212 break;
213 case PL_SOA:
214 i = dn_expand(abuf, abuf+alen, p, plname, sizeof plname - 1);
215 strcat(plname, " ");
216 i += dn_expand(abuf, abuf+alen, p+i, plname+strlen(plname),
217 sizeof plname-strlen(plname));
218 for (j=0; j<5; j++)
219 v[j] = 16777216u*p[i+4*j] + 65536*p[1+i+4*j]
220 + 256*p[2+i+4*j] + p[3+i+4*j];
221 snprintf(plname+strlen(plname),
222 sizeof plname-strlen(plname),
223 "(\n\t\t%u\t;serial (version)\n"
224 "\t\t%u\t;refresh period\n"
225 "\t\t%u\t;retry interval\n"
226 "\t\t%u\t;expire time\n"
227 "\t\t%u\t;default ttl\n"
228 "\t\t)", v[0], v[1], v[2], v[3], v[4]);
229 break;
230 case PL_MX:
231 pri = 256*p[0] + p[1];
232 snprintf(plname, sizeof plname,
233 verbose ? "%d " : "(pri=%d) by ", pri);
234 dn_expand(abuf, abuf+alen, p+2,
235 plname+strlen(plname),
236 sizeof plname - strlen(plname));
237 break;
238 case PL_SRV:
239 for (j=0; j<3; j++)
240 v[j] = 256*p[2*j] + p[1+2*j];
241 snprintf(plname, sizeof plname,
242 "%u %u %u ", v[0], v[1], v[2]);
243 dn_expand(abuf, abuf+alen, p+6,
244 plname+strlen(plname),
245 sizeof plname - strlen(plname));
246 break;
247 default:
248 printf("%s unsupported RR type %u\n", rrname, type);
249 continue;
251 if (verbose) {
252 printf("%s\t%u\t%s %s\t%s\n",
253 rrname, ttl, "IN", rrt[type].name, plname);
254 } else if (rrt[type].msg) {
255 printf("%s %s %s\n", rrname, rrt[type].msg, plname);
258 if (!verbose && sec==1) break;
260 return rcode;