xref: /aosp_15_r20/external/toybox/toys/net/host.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1*cf5a6c84SAndroid Build Coastguard Worker /* host.c - DNS lookup utility
2*cf5a6c84SAndroid Build Coastguard Worker  *
3*cf5a6c84SAndroid Build Coastguard Worker  * Copyright 2014 Rich Felker <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker  *
5*cf5a6c84SAndroid Build Coastguard Worker  * No standard, but there's a version in bind9
6*cf5a6c84SAndroid Build Coastguard Worker  * See https://www.ietf.org/rfc/rfc1035.txt
7*cf5a6c84SAndroid Build Coastguard Worker  * See https://www.ietf.org/rfc/rfc3596.txt
8*cf5a6c84SAndroid Build Coastguard Worker 
9*cf5a6c84SAndroid Build Coastguard Worker USE_HOST(NEWTOY(host, "<1>2avt:", TOYFLAG_USR|TOYFLAG_BIN))
10*cf5a6c84SAndroid Build Coastguard Worker 
11*cf5a6c84SAndroid Build Coastguard Worker config HOST
12*cf5a6c84SAndroid Build Coastguard Worker   bool "host"
13*cf5a6c84SAndroid Build Coastguard Worker   default y
14*cf5a6c84SAndroid Build Coastguard Worker   help
15*cf5a6c84SAndroid Build Coastguard Worker     usage: host [-v] [-t TYPE] NAME [SERVER]
16*cf5a6c84SAndroid Build Coastguard Worker 
17*cf5a6c84SAndroid Build Coastguard Worker     Look up DNS records for NAME, either domain name or IPv4/IPv6 address to
18*cf5a6c84SAndroid Build Coastguard Worker     reverse lookup, from SERVER or default DNS server(s).
19*cf5a6c84SAndroid Build Coastguard Worker 
20*cf5a6c84SAndroid Build Coastguard Worker     -a	All records
21*cf5a6c84SAndroid Build Coastguard Worker     -t TYPE	Record TYPE (number or ANY A AAAA CNAME MX NS PTR SOA SRV TXT)
22*cf5a6c84SAndroid Build Coastguard Worker     -v	Verbose
23*cf5a6c84SAndroid Build Coastguard Worker */
24*cf5a6c84SAndroid Build Coastguard Worker 
25*cf5a6c84SAndroid Build Coastguard Worker #define FOR_host
26*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
27*cf5a6c84SAndroid Build Coastguard Worker #include <resolv.h>
28*cf5a6c84SAndroid Build Coastguard Worker 
29*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
30*cf5a6c84SAndroid Build Coastguard Worker   char *t;
31*cf5a6c84SAndroid Build Coastguard Worker 
32*cf5a6c84SAndroid Build Coastguard Worker   char **nsname;
33*cf5a6c84SAndroid Build Coastguard Worker   unsigned nslen;
34*cf5a6c84SAndroid Build Coastguard Worker )
35*cf5a6c84SAndroid Build Coastguard Worker 
36*cf5a6c84SAndroid Build Coastguard Worker static const struct rrt {
37*cf5a6c84SAndroid Build Coastguard Worker   char *name, *msg;
38*cf5a6c84SAndroid Build Coastguard Worker   int type;
39*cf5a6c84SAndroid Build Coastguard Worker } rrt[] = { { "A", "has address", 1 }, { "NS", "name server", 2 },
40*cf5a6c84SAndroid Build Coastguard Worker   { "CNAME", "is a nickname for", 5 }, { "SOA", "start of authority", 6 },
41*cf5a6c84SAndroid Build Coastguard Worker   { "PTR", "domain name pointer", 12 }, { "HINFO", "host information", 13 },
42*cf5a6c84SAndroid Build Coastguard Worker   { "MX", "mail is handled", 15 }, { "TXT", "descriptive text", 16 },
43*cf5a6c84SAndroid Build Coastguard Worker   { "AAAA", "has IPv6 address", 28 }, { "SRV", "has SRV record", 33 }
44*cf5a6c84SAndroid Build Coastguard Worker };
45*cf5a6c84SAndroid Build Coastguard Worker 
xdn_expand(char * packet,char * endpkt,char * comp,char * expand,int elen)46*cf5a6c84SAndroid Build Coastguard Worker int xdn_expand(char *packet, char *endpkt, char *comp, char *expand, int elen)
47*cf5a6c84SAndroid Build Coastguard Worker {
48*cf5a6c84SAndroid Build Coastguard Worker   int i = dn_expand(packet, endpkt, comp, expand, elen);
49*cf5a6c84SAndroid Build Coastguard Worker 
50*cf5a6c84SAndroid Build Coastguard Worker   if (i<1) error_exit("bad dn_expand");
51*cf5a6c84SAndroid Build Coastguard Worker 
52*cf5a6c84SAndroid Build Coastguard Worker   return i;
53*cf5a6c84SAndroid Build Coastguard Worker }
54*cf5a6c84SAndroid Build Coastguard Worker 
55*cf5a6c84SAndroid Build Coastguard Worker // Fetch "nameserve" lines from /etc/resolv.conf. Ignores 'options' lines
get_nsname(char ** pline,long len)56*cf5a6c84SAndroid Build Coastguard Worker static void get_nsname(char **pline, long len)
57*cf5a6c84SAndroid Build Coastguard Worker {
58*cf5a6c84SAndroid Build Coastguard Worker   char *line, *p;
59*cf5a6c84SAndroid Build Coastguard Worker 
60*cf5a6c84SAndroid Build Coastguard Worker   if (!len) return;
61*cf5a6c84SAndroid Build Coastguard Worker   line = *pline;
62*cf5a6c84SAndroid Build Coastguard Worker   if (strstart(&line, "nameserver") && isspace(*line)) {
63*cf5a6c84SAndroid Build Coastguard Worker     while (isspace(*line)) line++;
64*cf5a6c84SAndroid Build Coastguard Worker     for (p = line; *p && !isspace(*p) && *p!='#'; p++);
65*cf5a6c84SAndroid Build Coastguard Worker     if (p == line) return;
66*cf5a6c84SAndroid Build Coastguard Worker     *p = 0;
67*cf5a6c84SAndroid Build Coastguard Worker     if (!(TT.nslen&8))
68*cf5a6c84SAndroid Build Coastguard Worker       TT.nsname = xrealloc(TT.nsname, (TT.nslen+8)*sizeof(void *));
69*cf5a6c84SAndroid Build Coastguard Worker     TT.nsname[TT.nslen++] = xstrdup(line);
70*cf5a6c84SAndroid Build Coastguard Worker   }
71*cf5a6c84SAndroid Build Coastguard Worker }
72*cf5a6c84SAndroid Build Coastguard Worker 
host_main(void)73*cf5a6c84SAndroid Build Coastguard Worker void host_main(void)
74*cf5a6c84SAndroid Build Coastguard Worker {
75*cf5a6c84SAndroid Build Coastguard Worker   int verbose = FLAG(a)||FLAG(v), type, abuf_len = 65536, //Largest TCP response
76*cf5a6c84SAndroid Build Coastguard Worker       i, j, sec, rcode, qlen, alen QUIET, pllen = 0, t2len = 2048;
77*cf5a6c84SAndroid Build Coastguard Worker   unsigned count, ttl;
78*cf5a6c84SAndroid Build Coastguard Worker   char *abuf = xmalloc(abuf_len), *name = *toys.optargs, *p, *ss,
79*cf5a6c84SAndroid Build Coastguard Worker        *t2 = toybuf+t2len;
80*cf5a6c84SAndroid Build Coastguard Worker   struct addrinfo *ai;
81*cf5a6c84SAndroid Build Coastguard Worker 
82*cf5a6c84SAndroid Build Coastguard Worker   // What kind of query are we doing?
83*cf5a6c84SAndroid Build Coastguard Worker   if (!TT.t && FLAG(a)) TT.t = "255";
84*cf5a6c84SAndroid Build Coastguard Worker   if (!getaddrinfo(name, 0,&(struct addrinfo){.ai_flags=AI_NUMERICHOST}, &ai)) {
85*cf5a6c84SAndroid Build Coastguard Worker     name = toybuf;
86*cf5a6c84SAndroid Build Coastguard Worker     if (ai->ai_family == AF_INET) {
87*cf5a6c84SAndroid Build Coastguard Worker       p = (void *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr;
88*cf5a6c84SAndroid Build Coastguard Worker       sprintf(name, "%d.%d.%d.%d.in-addr.arpa", p[3], p[2], p[1], p[0]);
89*cf5a6c84SAndroid Build Coastguard Worker     } else if (ai->ai_family == AF_INET6) {
90*cf5a6c84SAndroid Build Coastguard Worker       p = (void *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
91*cf5a6c84SAndroid Build Coastguard Worker       for (j = 0, i = 15; i>=0; i--)
92*cf5a6c84SAndroid Build Coastguard Worker         j += sprintf(name+j, "%x.%x.", p[i]&15, p[i]>>4);
93*cf5a6c84SAndroid Build Coastguard Worker       strcpy(name+j, "ip6.arpa");
94*cf5a6c84SAndroid Build Coastguard Worker     }
95*cf5a6c84SAndroid Build Coastguard Worker     if (!TT.t) TT.t = "12";
96*cf5a6c84SAndroid Build Coastguard Worker   } else if (!TT.t) TT.t = "1";
97*cf5a6c84SAndroid Build Coastguard Worker 
98*cf5a6c84SAndroid Build Coastguard Worker   // Prepare query packet of appropriate type
99*cf5a6c84SAndroid Build Coastguard Worker   if (TT.t[0]-'0'<10) type = atoi(TT.t); // TODO
100*cf5a6c84SAndroid Build Coastguard Worker   else if (!strcasecmp(TT.t, "any") || !strcmp(TT.t, "*")) type = 255;
101*cf5a6c84SAndroid Build Coastguard Worker   else {
102*cf5a6c84SAndroid Build Coastguard Worker     for (i = 0; i<ARRAY_LEN(rrt); i++) if (!strcasecmp(TT.t, rrt[i].name)) {
103*cf5a6c84SAndroid Build Coastguard Worker       type = rrt[i].type;
104*cf5a6c84SAndroid Build Coastguard Worker       break;
105*cf5a6c84SAndroid Build Coastguard Worker     }
106*cf5a6c84SAndroid Build Coastguard Worker     if (i == ARRAY_LEN(rrt)) error_exit("bad -t: %s", TT.t);
107*cf5a6c84SAndroid Build Coastguard Worker   }
108*cf5a6c84SAndroid Build Coastguard Worker   qlen = res_mkquery(0, name, 1, type, 0, 0, 0, t2, t2len);
109*cf5a6c84SAndroid Build Coastguard Worker   if (qlen<0) error_exit("bad NAME: %s", name);
110*cf5a6c84SAndroid Build Coastguard Worker 
111*cf5a6c84SAndroid Build Coastguard Worker   // Grab nameservers
112*cf5a6c84SAndroid Build Coastguard Worker   if (toys.optargs[1]) TT.nsname = toys.optargs+1;
113*cf5a6c84SAndroid Build Coastguard Worker   else do_lines(xopen("/etc/resolv.conf", O_RDONLY), '\n', get_nsname);
114*cf5a6c84SAndroid Build Coastguard Worker   if (!TT.nsname) error_exit("No nameservers");
115*cf5a6c84SAndroid Build Coastguard Worker 
116*cf5a6c84SAndroid Build Coastguard Worker   // Send one query packet to each server until we receive response
117*cf5a6c84SAndroid Build Coastguard Worker   while (*TT.nsname) {
118*cf5a6c84SAndroid Build Coastguard Worker     if (verbose) printf("Using domain server %s:\n", *TT.nsname);
119*cf5a6c84SAndroid Build Coastguard Worker     ai = xgetaddrinfo(*TT.nsname, "53", 0, SOCK_DGRAM, 0, 0);
120*cf5a6c84SAndroid Build Coastguard Worker     i = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
121*cf5a6c84SAndroid Build Coastguard Worker     xconnect(i, ai->ai_addr, ai->ai_addrlen);
122*cf5a6c84SAndroid Build Coastguard Worker     setsockopt(i, SOL_SOCKET, SO_RCVTIMEO, &(struct timeval){ .tv_sec = 5 },
123*cf5a6c84SAndroid Build Coastguard Worker       sizeof(struct timeval));
124*cf5a6c84SAndroid Build Coastguard Worker     send(i, t2, qlen, 0);
125*cf5a6c84SAndroid Build Coastguard Worker     alen = recv(i, abuf, abuf_len, 0);
126*cf5a6c84SAndroid Build Coastguard Worker     close(i);
127*cf5a6c84SAndroid Build Coastguard Worker     if (16<alen) break;
128*cf5a6c84SAndroid Build Coastguard Worker     if (!*++TT.nsname) error_exit("Host not found.");
129*cf5a6c84SAndroid Build Coastguard Worker   }
130*cf5a6c84SAndroid Build Coastguard Worker 
131*cf5a6c84SAndroid Build Coastguard Worker   // Did it error?
132*cf5a6c84SAndroid Build Coastguard Worker   rcode = abuf[3]&7;
133*cf5a6c84SAndroid Build Coastguard Worker   if (verbose) {
134*cf5a6c84SAndroid Build Coastguard Worker     printf("rcode = %d, ancount = %d\n", rcode, (int)peek_be(abuf+6, 2));
135*cf5a6c84SAndroid Build Coastguard Worker     if (!(abuf[2]&4)) puts("The following answer is not authoritative:");
136*cf5a6c84SAndroid Build Coastguard Worker   }
137*cf5a6c84SAndroid Build Coastguard Worker   if (rcode) error_exit("Host not found: %s",
138*cf5a6c84SAndroid Build Coastguard Worker     (char *[]){ "Format error", "Server failure",
139*cf5a6c84SAndroid Build Coastguard Worker     "Non-existant domain", "Not implemented", "Refused", ""}[rcode-1]);
140*cf5a6c84SAndroid Build Coastguard Worker   if (abuf[2]&2) puts("Truncated");
141*cf5a6c84SAndroid Build Coastguard Worker 
142*cf5a6c84SAndroid Build Coastguard Worker   // Print the result
143*cf5a6c84SAndroid Build Coastguard Worker   p = abuf + 12;
144*cf5a6c84SAndroid Build Coastguard Worker   qlen = 0;
145*cf5a6c84SAndroid Build Coastguard Worker   for (sec = 0; sec<(2<<verbose); sec++) {
146*cf5a6c84SAndroid Build Coastguard Worker     count = peek_be(abuf+4+2*sec, 2);
147*cf5a6c84SAndroid Build Coastguard Worker     if (verbose && count && sec>1)
148*cf5a6c84SAndroid Build Coastguard Worker       puts(sec==2 ? "For authoritative answers, see:"
149*cf5a6c84SAndroid Build Coastguard Worker         : "Additional information:");
150*cf5a6c84SAndroid Build Coastguard Worker 
151*cf5a6c84SAndroid Build Coastguard Worker     for (; count--; p += pllen) {
152*cf5a6c84SAndroid Build Coastguard Worker       p += xdn_expand(abuf, abuf+alen, p, toybuf, sizeof(toybuf)-t2len);
153*cf5a6c84SAndroid Build Coastguard Worker       if (alen-(p-abuf)<10) error_exit("bad header");
154*cf5a6c84SAndroid Build Coastguard Worker       type = peek_be(p, 2);
155*cf5a6c84SAndroid Build Coastguard Worker       p += 4;
156*cf5a6c84SAndroid Build Coastguard Worker       if (!sec) continue;
157*cf5a6c84SAndroid Build Coastguard Worker       ttl = peek_be(p, 4);
158*cf5a6c84SAndroid Build Coastguard Worker       p += 4;
159*cf5a6c84SAndroid Build Coastguard Worker       pllen = peek_be(p, 2);
160*cf5a6c84SAndroid Build Coastguard Worker       p += 2;
161*cf5a6c84SAndroid Build Coastguard Worker       if ((p-abuf)+pllen>alen) error_exit("bad header");
162*cf5a6c84SAndroid Build Coastguard Worker       if (type==1 || type == 28)
163*cf5a6c84SAndroid Build Coastguard Worker         inet_ntop(type==1 ? AF_INET : AF_INET6, p, t2, t2len);
164*cf5a6c84SAndroid Build Coastguard Worker       else if (type==2 || type==5) xdn_expand(abuf, abuf+alen, p, t2, t2len);
165*cf5a6c84SAndroid Build Coastguard Worker       else if (type==13 || type==16) {
166*cf5a6c84SAndroid Build Coastguard Worker         if (pllen && pllen-1==*p) p++, pllen--;
167*cf5a6c84SAndroid Build Coastguard Worker         sprintf(t2, "\"%.*s\"", minof(pllen, t2len), p);
168*cf5a6c84SAndroid Build Coastguard Worker       } else if (type==6) {
169*cf5a6c84SAndroid Build Coastguard Worker         ss = p+xdn_expand(abuf, abuf+alen, p, t2, t2len-1);
170*cf5a6c84SAndroid Build Coastguard Worker         j = strlen(t2);
171*cf5a6c84SAndroid Build Coastguard Worker         t2[j++] = ' ';
172*cf5a6c84SAndroid Build Coastguard Worker         ss += xdn_expand(abuf, abuf+alen, ss, t2+j, t2len-j);
173*cf5a6c84SAndroid Build Coastguard Worker         j += strlen(t2+j);
174*cf5a6c84SAndroid Build Coastguard Worker         snprintf(t2+j, t2len-j, "(\n\t\t%u\t;serial (version)\n\t\t%u\t"
175*cf5a6c84SAndroid Build Coastguard Worker           ";refresh period\n\t\t%u\t;retry interval\n\t\t%u\t;expire time\n"
176*cf5a6c84SAndroid Build Coastguard Worker           "\t\t%u\t;default ttl\n\t\t)", (unsigned)peek_be(ss, 4),
177*cf5a6c84SAndroid Build Coastguard Worker           (unsigned)peek_be(ss+4, 4), (unsigned)peek_be(ss+8, 4),
178*cf5a6c84SAndroid Build Coastguard Worker           (unsigned)peek_be(ss+12, 4), (unsigned)peek_be(ss+16, 4));
179*cf5a6c84SAndroid Build Coastguard Worker       } else if (type==15) {
180*cf5a6c84SAndroid Build Coastguard Worker         j = peek_be(p, 2);
181*cf5a6c84SAndroid Build Coastguard Worker         j = sprintf(t2, verbose ? "%d " : "(pri=%d) by ", j);
182*cf5a6c84SAndroid Build Coastguard Worker         xdn_expand(abuf, abuf+alen, p+2, t2+j, t2len-j);
183*cf5a6c84SAndroid Build Coastguard Worker       } else if (type==33) {
184*cf5a6c84SAndroid Build Coastguard Worker         j = sprintf(t2, "%u %u %u ", (int)peek_be(p, 2), (int)peek_be(p+2, 2),
185*cf5a6c84SAndroid Build Coastguard Worker           (int)peek_be(p+4, 2));
186*cf5a6c84SAndroid Build Coastguard Worker         xdn_expand(abuf, abuf+alen, p+6, t2+j, t2len-j);
187*cf5a6c84SAndroid Build Coastguard Worker       } else {
188*cf5a6c84SAndroid Build Coastguard Worker         printf("%s unsupported RR type %u\n", toybuf, type);
189*cf5a6c84SAndroid Build Coastguard Worker         continue;
190*cf5a6c84SAndroid Build Coastguard Worker       }
191*cf5a6c84SAndroid Build Coastguard Worker       for (i = 0; rrt[i].type != type; i++);
192*cf5a6c84SAndroid Build Coastguard Worker       if (verbose) printf("%s\t%u\tIN %s\t%s\n", toybuf, ttl, rrt[i].name, t2);
193*cf5a6c84SAndroid Build Coastguard Worker       else printf("%s %s %s\n", toybuf, rrt[i].msg, t2);
194*cf5a6c84SAndroid Build Coastguard Worker       qlen++;
195*cf5a6c84SAndroid Build Coastguard Worker     }
196*cf5a6c84SAndroid Build Coastguard Worker   }
197*cf5a6c84SAndroid Build Coastguard Worker   if (TT.t && !qlen) printf("%s has no %s record\n", *toys.optargs, TT.t);
198*cf5a6c84SAndroid Build Coastguard Worker 
199*cf5a6c84SAndroid Build Coastguard Worker   if (CFG_TOYBOX_FREE) free(abuf);
200*cf5a6c84SAndroid Build Coastguard Worker   toys.exitval = rcode;
201*cf5a6c84SAndroid Build Coastguard Worker }
202