/*
Usage: host [-adlrwv] [-t querytype] [-c class] host [server]
        -a is equivalent to '-v -t *'
        -c class to look for non-Internet data
        -d to turn on debugging output
        -l to turn on 'list mode'
        -r to disable recursive processing
        -s recursively chase signature found in answers
        -t querytype to look for a specific type of information
        -v for verbose output
        -w to wait forever until reply
 */

/*
time.fu-berlin.de is a nickname for chronos.zedat.fu-berlin.de
chronos.zedat.fu-berlin.de has address 130.133.1.10
fu-berlin.de name server ns1.fu-berlin.de
fu-berlin.de mail is handled (pri=40) by mail.zedat.fu-berlin.de
knuth.fefe.de IPv6 address 2001:658:0:2:2e0:18ff:fe98:b03d
194.209.84.212.IN-ADDR.ARPA is a nickname for 194.192-255.209.84.212.IN-ADDR.ARPA
194.192-255.209.84.212.IN-ADDR.ARPA domain name pointer fefe.de
*/

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <netdb.h>
#include <buffer.h>
#include <fmt.h>
#include <ip4.h>
#include <ip6.h>
#include <uint32.h>

typedef struct { const char* name; unsigned short value; } rec;

rec classes[] =
  { { "in", C_IN }, { "chaos", C_CHAOS }, { "hs", C_HS }, 
    { "none", C_NONE }, { "any", C_ANY } };

rec types[] =
  { { "a", T_A }, { "aaaa", T_AAAA }, { "ns", T_NS }, 
    { "mx", T_MX }, { "soa", T_SOA }, { "ptr", T_PTR }, 
    { "txt", T_TXT }, { "any", C_ANY } };

static unsigned short chase(rec* r) {
  for (;;) {
    if (!strcasecmp(r->name,optarg)) return r->value;
    if (!strcmp(r->name,"any")) {
      buffer_puts(buffer_2,"invalid identifier: ");
      buffer_puts(buffer_2,optarg);
      buffer_putsflush(buffer_2,"\n");
      exit(0);
    }
    ++r;
  }
}

static unsigned char tohex(unsigned char c) {
  return c<10?c+'0':c-10+'a';
}

int main(int argc,char* argv[]) {
  unsigned char inpkg[1500];
  int size,j,c;
  unsigned char *tmp;
  char Name[257];
  int class=C_IN;
  int type=T_A;
  char* query;
  unsigned short q;
  int _r=0;

  while ((c=getopt(argc,argv,"arc:t:v")) != -1) {
    switch (c) {
    case 'a': type=T_ANY; break;
    case 'r': _r=1; break;
    case 'c': class=chase(classes); break;
    case 't': type=chase(types); break;
    }
  }

  if (!argv[optind]) {
    buffer_putsflush(buffer_2,"usage: host [-a] [-t type] [-c class] host [server]\n");
    exit(0);
  }
  if (argv[optind][scan_ip4(argv[optind],inpkg)]==0) {
    int i;
    i=fmt_ulong(Name,inpkg[3]);
    Name[i]='.'; ++i;
    i+=fmt_ulong(Name+i,inpkg[2]);
    Name[i]='.'; ++i;
    i+=fmt_ulong(Name+i,inpkg[1]);
    Name[i]='.'; ++i;
    i+=fmt_ulong(Name+i,inpkg[0]);
    strcpy(Name+i,".in-addr.arpa");
    argv[optind]=Name;
    type=T_PTR;
  } else if (argv[optind][scan_ip6(argv[optind],inpkg)]==0) {
    int i;
    for (i=0; i<16; ++i) {
      Name[i*4]='.';
      Name[i*4+1]=tohex(inpkg[15-i]&15);
      Name[i*4+2]='.';
      Name[i*4+3]=tohex(inpkg[15-i]>>4);
    }
    strcpy(Name+4*16,".ip6.int");
    argv[optind]=Name;
    type=T_PTR;
  }
  res_init();
  if (argv[optind+1]) {	/* server given */
    const char* server=argv[optind+1];
    _res.nsaddr_list[0].sin_family=PF_INET;
    _res.nsaddr_list[0].sin_port=htons(53);
    if (server[scan_ip4(server,inpkg)]==0) {
      _res.nsaddr_list[0].sin_addr.s_addr=*(unsigned long*)inpkg;
    } else if (server[scan_ip6(server,inpkg)]==0) {
      struct sockaddr_in6* x=(struct sockaddr_in6*)(&_res.nsaddr_list[0]);
      memmove(&x->sin6_addr,inpkg,16);
      x->sin6_family=AF_INET6;
      x->sin6_port=htons(53);
      x->sin6_flowinfo=x->sin6_scope_id=0;
    } else {
      struct hostent host,*res;
      int fnord;
      if (gethostbyname_r(server,&host,inpkg,2048,&res,&fnord)) {
	buffer_puts(buffer_2,"unknown host: ");
	buffer_puts(buffer_2,server);
	buffer_putsflush(buffer_2,"\n");
	return 2;
      }
      _res.nsaddr_list[0].sin_addr=*(struct in_addr*)(host.h_addr_list[0]);
    }
    _res.nscount=1;
  }
  if (_r) _res.options&=~RES_RECURSE; 
  size=res_query(argv[optind],class,type,inpkg,1500);
  if (size<0) {
    buffer_puts(buffer_2,hstrerror(h_errno));
    buffer_putsflush(buffer_2,"\n");
    exit(1);
  }
  tmp=inpkg+12;
  q=((unsigned short)inpkg[4]<<8)+inpkg[5];
  while (q>0) {
    while (*tmp) tmp+=*tmp+1;
    tmp+=5;
    --q;
  }
  q=((unsigned short)inpkg[6]<<8)+inpkg[7];
  if (q<1) {
    buffer_putsflush(buffer_2,"no data\n");
    return 1;
  }
  while (q>0) {
    int decofs;
    uint32 l;
    /* dn_expand(msg,eomorig,comp_dn,exp_dn,length) */
    decofs=dn_expand(inpkg,inpkg+size,tmp,Name,256);
    buffer_puts(buffer_1,Name);
    tmp+=decofs;
    --q;
    if (tmp[2]!=0 || tmp[3]!=class) exit(1);
    c=tmp[1]; tmp+=10;
    switch (c) {
    case T_SOA:
      buffer_puts(buffer_1," start of authority\t");
      decofs=dn_expand(inpkg,inpkg+size,tmp,Name,256);
      tmp+=decofs;
      buffer_puts(buffer_1,Name);
      buffer_putspace(buffer_1);
      decofs=dn_expand(inpkg,inpkg+size,tmp,Name,256);
      tmp+=decofs;
      buffer_puts(buffer_1,Name);
      buffer_puts(buffer_1,"(\n\t\t\t");

      uint32_unpack_big(tmp,&l);
      buffer_putulong(buffer_1,l);
      buffer_puts(buffer_1,"\t;serial (version)\n\t\t\t");
      uint32_unpack_big(tmp+4,&l);
      buffer_putulong(buffer_1,l);
      buffer_puts(buffer_1,"\t;refresh period\n\t\t\t");
      uint32_unpack_big(tmp+8,&l);
      buffer_putulong(buffer_1,l);
      buffer_puts(buffer_1,"\t;retry refresh this often\n\t\t\t");
      uint32_unpack_big(tmp+12,&l);
      buffer_putulong(buffer_1,l);
      buffer_puts(buffer_1,"\t;expiration period\n\t\t\t");
      uint32_unpack_big(tmp+16,&l);
      buffer_putulong(buffer_1,l);
      buffer_putsflush(buffer_1,"\t;minimum TTL\n\t\t\t)\n");
      break;
    case T_A:
      buffer_puts(buffer_1," has address ");
      j=fmt_ip4(Name,tmp); tmp+=4;
      goto print;
      write(1,Name,fmt_ip4(Name,tmp));
      break;
    case T_AAAA:
      buffer_puts(buffer_1," IPv6 address ");
      j=fmt_ip6(Name,tmp); tmp+=16;
print:
      Name[j]='\n';
      buffer_put(buffer_1,Name,j+1);
      break;
    case T_NS:
      buffer_puts(buffer_1," name server ");
      goto printname;
    case T_MX:
      buffer_puts(buffer_1," mail is handled (pri=");
      buffer_putulong(buffer_1,((unsigned long)tmp[0])*256+tmp[1]);
      tmp+=2;
      buffer_puts(buffer_1,") by ");
printname:
      decofs=dn_expand(inpkg,inpkg+size,tmp,Name,256);
      tmp+=decofs;
      buffer_puts(buffer_1,Name);
      buffer_puts(buffer_1,"\n");
      break;
    case T_PTR:
      buffer_puts(buffer_1," domain name pointer ");
      goto printname;
    case T_CNAME:
      buffer_puts(buffer_1," is a nickname for ");
      decofs=dn_expand(inpkg,inpkg+size,tmp,Name,256);
      j=strlen(Name); tmp+=decofs;
      goto print;
    }
  }
  buffer_flush(buffer_1);
}
