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