/* host.c - DNS lookup utility * * Copyright 2014 Rich Felker <dalias@aerifal.cx> * * No standard, but there's a version in bind9 USE_HOST(NEWTOY(host, "<1>2avt:", TOYFLAG_USR|TOYFLAG_BIN)) config HOST bool "host" default n help usage: host [-av] [-t TYPE] NAME [SERVER] Perform DNS lookup on NAME, which can be a domain name to lookup, or an IPv4 dotted or IPv6 colon-separated address to reverse lookup. SERVER (if present) is the DNS server to use. -a -v -t ANY -t TYPE query records of type TYPE -v verbose */ #define FOR_host #include "toys.h" GLOBALS( char *type_str; ) #include <resolv.h> #define PL_IP 1 #define PL_NAME 2 #define PL_DATA 3 #define PL_TEXT 4 #define PL_SOA 5 #define PL_MX 6 #define PL_SRV 7 static const struct rrt { const char *name; const char *msg; int pl; int af; } rrt[] = { [1] = { "A", "has address", PL_IP, AF_INET }, [28] = { "AAAA", "has address", PL_IP, AF_INET6 }, [2] = { "NS", "name server", PL_NAME }, [5] = { "CNAME", "is a nickname for", PL_NAME }, [16] = { "TXT", "descriptive text", PL_TEXT }, [6] = { "SOA", "start of authority", PL_SOA }, [12] = { "PTR", "domain name pointer", PL_NAME }, [15] = { "MX", "mail is handled", PL_MX }, [33] = { "SRV", "mail is handled", PL_SRV }, [255] = { "*", 0, 0 }, }; static const char rct[16][32] = { "Success", "Format error", "Server failure", "Non-existant domain", "Not implemented", "Refused", }; void host_main(void) { int verbose=(toys.optflags & (FLAG_a|FLAG_v)), type, i, j, ret, sec, count, rcode, qlen, alen, pllen = 0, abuf_len = 65536; // Largest TCP response. unsigned ttl, pri, v[5]; unsigned char *abuf = xmalloc(abuf_len); char *rrname = xmalloc(MAXDNAME); unsigned char qbuf[280], *p; char *name, *nsname, plname[640], ptrbuf[128]; struct addrinfo *ai, iplit_hints = { .ai_flags = AI_NUMERICHOST }; name = *toys.optargs; nsname = toys.optargs[1]; if (!TT.type_str && (toys.optflags & FLAG_a)) TT.type_str = "255"; if (!getaddrinfo(name, 0, &iplit_hints, &ai)) { unsigned char *a; static const char xdigits[] = "0123456789abcdef"; if (ai->ai_family == AF_INET) { a = (void *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr; snprintf(ptrbuf, sizeof(ptrbuf), "%d.%d.%d.%d.in-addr.arpa", a[3], a[2], a[1], a[0]); } else if (ai->ai_family == AF_INET6) { a = (void *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; for (j=0, i=15; i>=0; i--) { ptrbuf[j++] = xdigits[a[i]&15]; ptrbuf[j++] = '.'; ptrbuf[j++] = xdigits[a[i]>>4]; ptrbuf[j++] = '.'; } strcpy(ptrbuf+j, "ip6.arpa"); } name = ptrbuf; if (!TT.type_str) TT.type_str="12"; } else if (!TT.type_str) TT.type_str="1"; if (TT.type_str[0]-'0' < 10u) type = atoi(TT.type_str); else { type = -1; for (i=0; i<ARRAY_LEN(rrt); i++) { if (rrt[i].name && !strcasecmp(TT.type_str, rrt[i].name)) { type = i; break; } } if (!strcasecmp(TT.type_str, "any")) type = 255; if (type < 0) error_exit("Invalid query type: %s", TT.type_str); } qlen = res_mkquery(0, name, 1, type, 0, 0, 0, qbuf, sizeof(qbuf)); if (qlen < 0) error_exit("Invalid query parameters: %s", name); if (nsname) { struct addrinfo ns_hints = { .ai_socktype = SOCK_DGRAM }; if ((ret = getaddrinfo(nsname, "53", &ns_hints, &ai)) < 0) error_exit("Error looking up server name: %s", gai_strerror(ret)); int s = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); xconnect(s, ai->ai_addr, ai->ai_addrlen); setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &(struct timeval){ .tv_sec = 5 }, sizeof(struct timeval)); printf("Using domain server %s:\n", nsname); send(s, qbuf, qlen, 0); alen = recv(s, abuf, abuf_len, 0); } else alen = res_send(qbuf, qlen, abuf, abuf_len); if (alen < 12) error_exit("Host not found."); rcode = abuf[3] & 15; if (verbose) { printf("rcode = %d (%s), ancount = %d\n", rcode, rct[rcode], 256*abuf[6] + abuf[7]); if (!(abuf[2] & 4)) printf("The following answer is not authoritative:\n"); } if (rcode) error_exit("Host not found."); p = abuf + 12; for (sec=0; sec<4; sec++) { count = 256*abuf[4+2*sec] + abuf[5+2*sec]; if (verbose && count>0 && sec>1) puts(sec==2 ? "For authoritative answers, see:" : "Additional information:"); for (; count--; p += pllen) { p += dn_expand(abuf, abuf+alen, p, rrname, MAXDNAME); type = (p[0]<<8) + p[1]; p += 4; if (!sec) continue; ttl = (p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; p += 4; pllen = (p[0]<<8) + p[1]; p += 2; switch (type<ARRAY_LEN(rrt) ? rrt[type].pl : 0) { case PL_IP: inet_ntop(rrt[type].af, p, plname, sizeof(plname)); break; case PL_NAME: dn_expand(abuf, abuf+alen, p, plname, sizeof(plname)); break; case PL_TEXT: snprintf(plname, sizeof(plname), "\"%.*s\"", pllen, p); break; case PL_SOA: i = dn_expand(abuf, abuf+alen, p, plname, sizeof(plname) - 1); strcat(plname, " "); i += dn_expand(abuf, abuf+alen, p+i, plname+strlen(plname), sizeof(plname)-strlen(plname)); for (j=0; j<5; j++) v[j] = (p[i+4*j]<<24)+(p[1+i+4*j]<<16)+(p[2+i+4*j]<<8)+p[3+i+4*j]; snprintf(plname+strlen(plname), sizeof(plname)-strlen(plname), "(\n\t\t%u\t;serial (version)\n" "\t\t%u\t;refresh period\n" "\t\t%u\t;retry interval\n" "\t\t%u\t;expire time\n" "\t\t%u\t;default ttl\n" "\t\t)", v[0], v[1], v[2], v[3], v[4]); break; case PL_MX: pri = (p[0]<<8)+p[1]; snprintf(plname, sizeof(plname), verbose ? "%d " : "(pri=%d) by ", pri); dn_expand(abuf, abuf+alen, p+2, plname+strlen(plname), sizeof(plname) - strlen(plname)); break; case PL_SRV: for (j=0; j<3; j++) v[j] = (p[2*j]<<8) + p[1+2*j]; snprintf(plname, sizeof(plname), "%u %u %u ", v[0], v[1], v[2]); dn_expand(abuf, abuf+alen, p+6, plname+strlen(plname), sizeof(plname) - strlen(plname)); break; default: printf("%s unsupported RR type %u\n", rrname, type); continue; } if (verbose) printf("%s\t%u\tIN %s\t%s\n", rrname, ttl, rrt[type].name, plname); else if (rrt[type].msg) printf("%s %s %s\n", rrname, rrt[type].msg, plname); } if (!verbose && sec==1) break; } if (CFG_TOYBOX_FREE) { free(abuf); free(rrname); } toys.exitval = rcode; }