10f66f451Sopenharmony_ci/* host.c - DNS lookup utility
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2014 Rich Felker <dalias@aerifal.cx>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * No standard, but there's a version in bind9
60f66f451Sopenharmony_ci
70f66f451Sopenharmony_ciUSE_HOST(NEWTOY(host, "<1>2avt:", TOYFLAG_USR|TOYFLAG_BIN))
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciconfig HOST
100f66f451Sopenharmony_ci  bool "host"
110f66f451Sopenharmony_ci  default n
120f66f451Sopenharmony_ci  help
130f66f451Sopenharmony_ci    usage: host [-av] [-t TYPE] NAME [SERVER]
140f66f451Sopenharmony_ci
150f66f451Sopenharmony_ci    Perform DNS lookup on NAME, which can be a domain name to lookup,
160f66f451Sopenharmony_ci    or an IPv4 dotted or IPv6 colon-separated address to reverse lookup.
170f66f451Sopenharmony_ci    SERVER (if present) is the DNS server to use.
180f66f451Sopenharmony_ci
190f66f451Sopenharmony_ci    -a	-v -t ANY
200f66f451Sopenharmony_ci    -t TYPE	query records of type TYPE
210f66f451Sopenharmony_ci    -v	verbose
220f66f451Sopenharmony_ci*/
230f66f451Sopenharmony_ci
240f66f451Sopenharmony_ci#define FOR_host
250f66f451Sopenharmony_ci#include "toys.h"
260f66f451Sopenharmony_ci
270f66f451Sopenharmony_ciGLOBALS(
280f66f451Sopenharmony_ci  char *type_str;
290f66f451Sopenharmony_ci)
300f66f451Sopenharmony_ci
310f66f451Sopenharmony_ci#include <resolv.h>
320f66f451Sopenharmony_ci
330f66f451Sopenharmony_ci#define PL_IP 1
340f66f451Sopenharmony_ci#define PL_NAME 2
350f66f451Sopenharmony_ci#define PL_DATA 3
360f66f451Sopenharmony_ci#define PL_TEXT 4
370f66f451Sopenharmony_ci#define PL_SOA 5
380f66f451Sopenharmony_ci#define PL_MX 6
390f66f451Sopenharmony_ci#define PL_SRV 7
400f66f451Sopenharmony_ci
410f66f451Sopenharmony_cistatic const struct rrt {
420f66f451Sopenharmony_ci  const char *name;
430f66f451Sopenharmony_ci  const char *msg;
440f66f451Sopenharmony_ci  int pl;
450f66f451Sopenharmony_ci  int af;
460f66f451Sopenharmony_ci} rrt[] = {
470f66f451Sopenharmony_ci  [1] = { "A", "has address", PL_IP, AF_INET },
480f66f451Sopenharmony_ci  [28] = { "AAAA", "has address", PL_IP, AF_INET6 },
490f66f451Sopenharmony_ci  [2] = { "NS", "name server", PL_NAME },
500f66f451Sopenharmony_ci  [5] = { "CNAME", "is a nickname for", PL_NAME },
510f66f451Sopenharmony_ci  [16] = { "TXT", "descriptive text", PL_TEXT },
520f66f451Sopenharmony_ci  [6] = { "SOA", "start of authority", PL_SOA },
530f66f451Sopenharmony_ci  [12] = { "PTR", "domain name pointer", PL_NAME },
540f66f451Sopenharmony_ci  [15] = { "MX", "mail is handled", PL_MX },
550f66f451Sopenharmony_ci  [33] = { "SRV", "mail is handled", PL_SRV },
560f66f451Sopenharmony_ci  [255] = { "*", 0, 0 },
570f66f451Sopenharmony_ci};
580f66f451Sopenharmony_ci
590f66f451Sopenharmony_cistatic const char rct[16][32] = {
600f66f451Sopenharmony_ci  "Success",
610f66f451Sopenharmony_ci  "Format error",
620f66f451Sopenharmony_ci  "Server failure",
630f66f451Sopenharmony_ci  "Non-existant domain",
640f66f451Sopenharmony_ci  "Not implemented",
650f66f451Sopenharmony_ci  "Refused",
660f66f451Sopenharmony_ci};
670f66f451Sopenharmony_ci
680f66f451Sopenharmony_civoid host_main(void)
690f66f451Sopenharmony_ci{
700f66f451Sopenharmony_ci  int verbose=(toys.optflags & (FLAG_a|FLAG_v)), type,
710f66f451Sopenharmony_ci      i, j, ret, sec, count, rcode, qlen, alen, pllen = 0,
720f66f451Sopenharmony_ci      abuf_len = 65536; // Largest TCP response.
730f66f451Sopenharmony_ci  unsigned ttl, pri, v[5];
740f66f451Sopenharmony_ci  unsigned char *abuf = xmalloc(abuf_len);
750f66f451Sopenharmony_ci  char *rrname = xmalloc(MAXDNAME);
760f66f451Sopenharmony_ci  unsigned char qbuf[280], *p;
770f66f451Sopenharmony_ci  char *name, *nsname, plname[640], ptrbuf[128];
780f66f451Sopenharmony_ci  struct addrinfo *ai, iplit_hints = { .ai_flags = AI_NUMERICHOST };
790f66f451Sopenharmony_ci
800f66f451Sopenharmony_ci  name = *toys.optargs;
810f66f451Sopenharmony_ci  nsname = toys.optargs[1];
820f66f451Sopenharmony_ci
830f66f451Sopenharmony_ci  if (!TT.type_str && (toys.optflags & FLAG_a)) TT.type_str = "255";
840f66f451Sopenharmony_ci  if (!getaddrinfo(name, 0, &iplit_hints, &ai)) {
850f66f451Sopenharmony_ci    unsigned char *a;
860f66f451Sopenharmony_ci    static const char xdigits[] = "0123456789abcdef";
870f66f451Sopenharmony_ci
880f66f451Sopenharmony_ci    if (ai->ai_family == AF_INET) {
890f66f451Sopenharmony_ci      a = (void *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr;
900f66f451Sopenharmony_ci      snprintf(ptrbuf, sizeof(ptrbuf), "%d.%d.%d.%d.in-addr.arpa",
910f66f451Sopenharmony_ci        a[3], a[2], a[1], a[0]);
920f66f451Sopenharmony_ci    } else if (ai->ai_family == AF_INET6) {
930f66f451Sopenharmony_ci      a = (void *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
940f66f451Sopenharmony_ci      for (j=0, i=15; i>=0; i--) {
950f66f451Sopenharmony_ci        ptrbuf[j++] = xdigits[a[i]&15];
960f66f451Sopenharmony_ci        ptrbuf[j++] = '.';
970f66f451Sopenharmony_ci        ptrbuf[j++] = xdigits[a[i]>>4];
980f66f451Sopenharmony_ci        ptrbuf[j++] = '.';
990f66f451Sopenharmony_ci      }
1000f66f451Sopenharmony_ci      strcpy(ptrbuf+j, "ip6.arpa");
1010f66f451Sopenharmony_ci    }
1020f66f451Sopenharmony_ci    name = ptrbuf;
1030f66f451Sopenharmony_ci    if (!TT.type_str) TT.type_str="12";
1040f66f451Sopenharmony_ci  } else if (!TT.type_str) TT.type_str="1";
1050f66f451Sopenharmony_ci
1060f66f451Sopenharmony_ci  if (TT.type_str[0]-'0' < 10u) type = atoi(TT.type_str);
1070f66f451Sopenharmony_ci  else {
1080f66f451Sopenharmony_ci    type = -1;
1090f66f451Sopenharmony_ci    for (i=0; i<ARRAY_LEN(rrt); i++) {
1100f66f451Sopenharmony_ci      if (rrt[i].name && !strcasecmp(TT.type_str, rrt[i].name)) {
1110f66f451Sopenharmony_ci        type = i;
1120f66f451Sopenharmony_ci        break;
1130f66f451Sopenharmony_ci      }
1140f66f451Sopenharmony_ci    }
1150f66f451Sopenharmony_ci    if (!strcasecmp(TT.type_str, "any")) type = 255;
1160f66f451Sopenharmony_ci    if (type < 0) error_exit("Invalid query type: %s", TT.type_str);
1170f66f451Sopenharmony_ci  }
1180f66f451Sopenharmony_ci
1190f66f451Sopenharmony_ci  qlen = res_mkquery(0, name, 1, type, 0, 0, 0, qbuf, sizeof(qbuf));
1200f66f451Sopenharmony_ci  if (qlen < 0) error_exit("Invalid query parameters: %s", name);
1210f66f451Sopenharmony_ci
1220f66f451Sopenharmony_ci  if (nsname) {
1230f66f451Sopenharmony_ci    struct addrinfo ns_hints = { .ai_socktype = SOCK_DGRAM };
1240f66f451Sopenharmony_ci
1250f66f451Sopenharmony_ci    if ((ret = getaddrinfo(nsname, "53", &ns_hints, &ai)) < 0)
1260f66f451Sopenharmony_ci      error_exit("Error looking up server name: %s", gai_strerror(ret));
1270f66f451Sopenharmony_ci    int s = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1280f66f451Sopenharmony_ci    xconnect(s, ai->ai_addr, ai->ai_addrlen);
1290f66f451Sopenharmony_ci    setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &(struct timeval){ .tv_sec = 5 },
1300f66f451Sopenharmony_ci      sizeof(struct timeval));
1310f66f451Sopenharmony_ci    printf("Using domain server %s:\n", nsname);
1320f66f451Sopenharmony_ci    send(s, qbuf, qlen, 0);
1330f66f451Sopenharmony_ci    alen = recv(s, abuf, abuf_len, 0);
1340f66f451Sopenharmony_ci  } else alen = res_send(qbuf, qlen, abuf, abuf_len);
1350f66f451Sopenharmony_ci
1360f66f451Sopenharmony_ci  if (alen < 12) error_exit("Host not found.");
1370f66f451Sopenharmony_ci
1380f66f451Sopenharmony_ci  rcode = abuf[3] & 15;
1390f66f451Sopenharmony_ci
1400f66f451Sopenharmony_ci  if (verbose) {
1410f66f451Sopenharmony_ci    printf("rcode = %d (%s), ancount = %d\n",
1420f66f451Sopenharmony_ci      rcode, rct[rcode], 256*abuf[6] + abuf[7]);
1430f66f451Sopenharmony_ci    if (!(abuf[2] & 4)) printf("The following answer is not authoritative:\n");
1440f66f451Sopenharmony_ci  }
1450f66f451Sopenharmony_ci
1460f66f451Sopenharmony_ci  if (rcode) error_exit("Host not found.");
1470f66f451Sopenharmony_ci
1480f66f451Sopenharmony_ci  p = abuf + 12;
1490f66f451Sopenharmony_ci  for (sec=0; sec<4; sec++) {
1500f66f451Sopenharmony_ci    count = 256*abuf[4+2*sec] + abuf[5+2*sec];
1510f66f451Sopenharmony_ci    if (verbose && count>0 && sec>1)
1520f66f451Sopenharmony_ci      puts(sec==2 ? "For authoritative answers, see:"
1530f66f451Sopenharmony_ci        : "Additional information:");
1540f66f451Sopenharmony_ci
1550f66f451Sopenharmony_ci    for (; count--; p += pllen) {
1560f66f451Sopenharmony_ci      p += dn_expand(abuf, abuf+alen, p, rrname, MAXDNAME);
1570f66f451Sopenharmony_ci      type = (p[0]<<8) + p[1];
1580f66f451Sopenharmony_ci      p += 4;
1590f66f451Sopenharmony_ci      if (!sec) continue;
1600f66f451Sopenharmony_ci      ttl = (p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3];
1610f66f451Sopenharmony_ci      p += 4;
1620f66f451Sopenharmony_ci      pllen = (p[0]<<8) + p[1];
1630f66f451Sopenharmony_ci      p += 2;
1640f66f451Sopenharmony_ci
1650f66f451Sopenharmony_ci      switch (type<ARRAY_LEN(rrt) ? rrt[type].pl : 0) {
1660f66f451Sopenharmony_ci      case PL_IP:
1670f66f451Sopenharmony_ci        inet_ntop(rrt[type].af, p, plname, sizeof(plname));
1680f66f451Sopenharmony_ci        break;
1690f66f451Sopenharmony_ci      case PL_NAME:
1700f66f451Sopenharmony_ci        dn_expand(abuf, abuf+alen, p, plname, sizeof(plname));
1710f66f451Sopenharmony_ci        break;
1720f66f451Sopenharmony_ci      case PL_TEXT:
1730f66f451Sopenharmony_ci        snprintf(plname, sizeof(plname), "\"%.*s\"", pllen, p);
1740f66f451Sopenharmony_ci        break;
1750f66f451Sopenharmony_ci      case PL_SOA:
1760f66f451Sopenharmony_ci        i = dn_expand(abuf, abuf+alen, p, plname, sizeof(plname) - 1);
1770f66f451Sopenharmony_ci        strcat(plname, " ");
1780f66f451Sopenharmony_ci        i += dn_expand(abuf, abuf+alen, p+i, plname+strlen(plname),
1790f66f451Sopenharmony_ci          sizeof(plname)-strlen(plname));
1800f66f451Sopenharmony_ci        for (j=0; j<5; j++)
1810f66f451Sopenharmony_ci          v[j] = (p[i+4*j]<<24)+(p[1+i+4*j]<<16)+(p[2+i+4*j]<<8)+p[3+i+4*j];
1820f66f451Sopenharmony_ci        snprintf(plname+strlen(plname), sizeof(plname)-strlen(plname),
1830f66f451Sopenharmony_ci          "(\n\t\t%u\t;serial (version)\n"
1840f66f451Sopenharmony_ci          "\t\t%u\t;refresh period\n"
1850f66f451Sopenharmony_ci          "\t\t%u\t;retry interval\n"
1860f66f451Sopenharmony_ci          "\t\t%u\t;expire time\n"
1870f66f451Sopenharmony_ci          "\t\t%u\t;default ttl\n"
1880f66f451Sopenharmony_ci          "\t\t)", v[0], v[1], v[2], v[3], v[4]);
1890f66f451Sopenharmony_ci        break;
1900f66f451Sopenharmony_ci      case PL_MX:
1910f66f451Sopenharmony_ci        pri = (p[0]<<8)+p[1];
1920f66f451Sopenharmony_ci        snprintf(plname, sizeof(plname), verbose ? "%d " : "(pri=%d) by ", pri);
1930f66f451Sopenharmony_ci        dn_expand(abuf, abuf+alen, p+2, plname+strlen(plname),
1940f66f451Sopenharmony_ci          sizeof(plname) - strlen(plname));
1950f66f451Sopenharmony_ci        break;
1960f66f451Sopenharmony_ci      case PL_SRV:
1970f66f451Sopenharmony_ci        for (j=0; j<3; j++) v[j] = (p[2*j]<<8) + p[1+2*j];
1980f66f451Sopenharmony_ci        snprintf(plname, sizeof(plname), "%u %u %u ", v[0], v[1], v[2]);
1990f66f451Sopenharmony_ci        dn_expand(abuf, abuf+alen, p+6, plname+strlen(plname),
2000f66f451Sopenharmony_ci          sizeof(plname) - strlen(plname));
2010f66f451Sopenharmony_ci        break;
2020f66f451Sopenharmony_ci      default:
2030f66f451Sopenharmony_ci        printf("%s unsupported RR type %u\n", rrname, type);
2040f66f451Sopenharmony_ci        continue;
2050f66f451Sopenharmony_ci      }
2060f66f451Sopenharmony_ci
2070f66f451Sopenharmony_ci      if (verbose)
2080f66f451Sopenharmony_ci        printf("%s\t%u\tIN %s\t%s\n", rrname, ttl, rrt[type].name, plname);
2090f66f451Sopenharmony_ci      else if (rrt[type].msg)
2100f66f451Sopenharmony_ci        printf("%s %s %s\n", rrname, rrt[type].msg, plname);
2110f66f451Sopenharmony_ci    }
2120f66f451Sopenharmony_ci    if (!verbose && sec==1) break;
2130f66f451Sopenharmony_ci  }
2140f66f451Sopenharmony_ci
2150f66f451Sopenharmony_ci  if (CFG_TOYBOX_FREE) {
2160f66f451Sopenharmony_ci    free(abuf);
2170f66f451Sopenharmony_ci    free(rrname);
2180f66f451Sopenharmony_ci  }
2190f66f451Sopenharmony_ci  toys.exitval = rcode;
2200f66f451Sopenharmony_ci}
221