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