/*

dns.c
This source code use libadns code by Ian Jackson and Tony Finch
Homepage: http://www.chiark.greenend.org.uk/~ian/adns

*/


int rcode;
const char *config_text;

static char *buf;

int ov_env=1, ov_pipe=0, ov_asynch=0;
int ov_verbose= 0;
adns_rrtype ov_type= adns_r_none;
int ov_search=0, ov_qc_query=0, ov_qc_anshost=0, ov_qc_cname=1;
int ov_tcp=0, ov_cname=0, ov_format=fmt_default;
char *ov_id= 0;
struct perqueryflags_remember ov_pqfr = { 1,1,1, tm_none };
adns_state ads;
struct outstanding_list outstanding;
static unsigned long idcounter;

void quitnow(int rc) {
  if (ads) adns_finish(ads);
  free(buf);
  free(ov_id);
  exit(rc);
}

void sysfail(const char *what, int errnoval) {
  fprintf(stderr,"adnshost failed: %s: %s\n",what,strerror(errnoval));
  quitnow(10);
}

void outerr(void) {
  sysfail("write to stdout",errno);
}

void *xmalloc(size_t sz) {
  void *p;

  p= malloc(sz); if (!p) sysfail("malloc",sz);
  return p;
}

char *xstrsave(const char *str) {
  char *p;
  
  p= xmalloc(strlen(str)+1);
  strcpy(p,str);
  return p;
}

void ensure_adns_init(void) {
  adns_initflags initflags;
  int r;
  
  if (ads) return;

  if (signal(SIGPIPE,SIG_IGN) == SIG_ERR) sysfail("ignore SIGPIPE",errno);

  initflags= adns_if_noautosys|adns_if_nosigpipe|ov_verbose;
  if (!ov_env) initflags |= adns_if_noenv;

  if (config_text) {
    r= adns_init_strcfg(&ads, initflags, stderr, config_text);
  } else {
    r= adns_init(&ads, initflags, 0);
  }
  if (r) sysfail("adns_init",r);

  if (ov_format == fmt_default)
    ov_format= ov_asynch ? fmt_asynch : fmt_simple;
}

static void prep_query(struct query_node **qun_r, int *quflags_r) {
  struct query_node *qun;
  char idbuf[20];
  
  ensure_adns_init();
  
  qun= malloc(sizeof(*qun));
  qun->pqfr= ov_pqfr;
  if (ov_id) {
    qun->id= xstrsave(ov_id);
  } else {
    sprintf(idbuf,"%lu",idcounter++);
    idcounter &= 0x0fffffffflu;
    qun->id= xstrsave(idbuf);
  }

  *quflags_r=
    (ov_search ? adns_qf_search : 0) |
    (ov_tcp ? adns_qf_usevc : 0) |
    ((ov_pqfr.show_owner || ov_format == fmt_simple) ? adns_qf_owner : 0) |
    (ov_qc_query ? adns_qf_quoteok_query : 0) |
    (ov_qc_anshost ? adns_qf_quoteok_anshost : 0) |
    (ov_qc_cname ? 0 : adns_qf_quoteok_cname) |
    ov_cname,
    
  *qun_r= qun;
}
  
int query_do(const char *domain) {
  struct query_node *qun;
  int quflags, r;

  prep_query(&qun,&quflags);
  qun->owner= xstrsave(domain);
  r= adns_submit(ads, domain,
		 ov_type == adns_r_none ? adns_r_addr : ov_type,
		 quflags,
		 qun,
		 &qun->qu);
  if (r) sysfail("adns_submit",r);
  LIST_LINK_TAIL(outstanding,qun);
  return(atoi(qun->id));
}

static void dequeue_query(struct query_node *qun) {
  LIST_UNLINK(outstanding,qun);
  free(qun->id);
  free(qun->owner);
  free(qun);
}

int get_dns_status(adns_status st, struct query_node *qun, adns_answer *answer) {
  const char *statusstring;

  statusstring= adns_strerror(st);
  if (!strncasecmp(statusstring, "OK", 2))
	return 0;
  return -1;
}


static const char *owner_show(struct query_node *qun, adns_answer *answer) {
  return answer->owner ? answer->owner : qun->owner;
}


static void check_status(adns_status st) {
  static const adns_status statuspoints[]= {
    adns_s_ok,
    adns_s_max_localfail, adns_s_max_remotefail, adns_s_max_tempfail,
    adns_s_max_misconfig, adns_s_max_misquery
  };

  const adns_status *spp;
  int minrcode;

  for (minrcode=0, spp=statuspoints;
       spp < statuspoints + (sizeof(statuspoints)/sizeof(statuspoints[0]));
       spp++)
    if (st > *spp) minrcode++;
  if (rcode < minrcode) rcode= minrcode;
}

int query_done(struct query_node *qun, adns_answer *answer, char *result) {
  adns_status st, ist;
  int rrn, nrrs;
  const char *rrp, *realowner, *typename;
  char *datastr;
  int ret;

  st= answer->status;
  nrrs= answer->nrrs;
  ret = atoi(qun->id);
  check_status(st);
  if (get_dns_status(st, qun, answer) < 0) {
	result[0] = 0;
  }
  if (qun->pqfr.show_owner) {
    realowner= answer->cname ? answer->cname : owner_show(qun,answer);
  } else {
    realowner= 0;
  }
  if (nrrs) {
    for (rrn=0, rrp = answer->rrs.untyped;
	 rrn < nrrs;
	 rrn++, rrp += answer->rrsz) {
      ist= adns_rr_info(answer->type, &typename, 0, 0, rrp, &datastr);
      if (ist == adns_s_nomemory) sysfail("adns_rr_info failed",ENOMEM);
      assert(!ist);
	  strncpy(result, datastr, 120);
	  result[120] = 0;
      free(datastr);
    }
  }
  free(answer);
  dequeue_query(qun);
  return ret;
}

