#include "hostenv.h"
#include <stdio.h>
#ifdef __linux__
#define __USE_BSD 1
#endif
#include <ctype.h>
#include <pwd.h>
#include <sysexits.h>
#include <sys/param.h>
#include <sys/stat.h>
#include "zmsignal.h"
#include "malloc.h"

#if	defined(HAVE_RESOLVER)
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif	/* HAVE_RESOLVER */

#include "ta.h"

#if	defined(TRY_AGAIN) && defined(HAVE_RESOLVER)
#define	BIND		/* Want BIND (named) nameserver support enabled */
#endif	/* TRY_AGAIN */
#ifdef	BIND
#undef NOERROR /* Solaris  <sys/socket.h>  has  NOERROR too.. */
#include <arpa/nameser.h>
#include <resolv.h>

#ifndef	BIND_VER
#ifdef	GETLONG
/* 4.7.3 introduced the {GET,PUT}{LONG,SHORT} macros in nameser.h */
#define	BIND_VER	473
#else	/* !GETLONG */
#define	BIND_VER	472
#endif	/* GETLONG */
#endif	/* !BIND_VER */
#endif	/* BIND */

#if	defined(BIND_VER) && (BIND_VER >= 473)
typedef u_char msgdata;
#else	/* !defined(BIND_VER) || (BIND_VER < 473) */
typedef char msgdata;
#endif	/* defined(BIND_VER) && (BIND_VER >= 473) */


/* Define all those things which exist on newer BINDs, and which may
   get returned to us, when we make a query with  T_ANY ... */

#ifndef	T_TXT
# define T_TXT 16
#endif
#ifndef T_RP
# define T_RP 17
#endif
#ifndef T_AFSDB
# define T_AFSDB 18
#endif
#ifndef T_NSAP
# define T_NSAP 22
#endif
#ifndef T_NSAP_PTR
# define T_NSAP_PTR 23
#endif
#ifndef	T_UINFO
# define T_UINFO 100
#endif
#ifndef T_UID
# define T_UID 101
#endif
#ifndef T_GID
# define T_GID 102
#endif
#ifndef T_UNSPEC
# define T_UNSPEC 103
#endif
#ifndef T_SA
# define T_SA 200
#endif

extern int res_mkquery(), res_send(), dn_expand(), dn_skipname();

#ifndef	IPPORT_SMTP
#define	IPPORT_SMTP	25
#endif	/* IPPORT_SMTP */

extern char errormsg[];
extern char *strchr();

#ifdef	BIND

typedef union {
	HEADER qb1;
	char qb2[PACKETSZ];
} querybuf;

extern int h_errno;
extern FILE *verboselog;

int
getrr(host, hbsize, rrtype, cnamelevel)	/* getrrtype with completion */
	char *host;
	int hbsize;
	int rrtype;
	int cnamelevel;
{
	int rval;
	char buf[BUFSIZ], **domain;

	if ((rval = getrrtype(host, hbsize, rrtype, cnamelevel)) > 0)
	  return rval;
	for (domain = _res.dnsrch; *domain != NULL; domain++) {
	  sprintf(buf, "%s.%s", host, *domain);
	  if ((rval = getrrtype(buf, BUFSIZ, rrtype, cnamelevel)) > 0) {
	    strncpy(host, buf, hbsize<BUFSIZ?hbsize:BUFSIZ);
	    host[hbsize - 1] = '\0';
	    return rval;
	  }
	}
	return 0;
}


int
getrrtype(host, hbsize, rrtype, cnamelevel)
	char *host;
	int hbsize;
	int rrtype;
	int cnamelevel;
{

	HEADER *hp;
	msgdata *eom, *cp;
	querybuf buf, answer;
	int qlen, n, ancount, qdcount, ok;
	u_short type;
	msgdata nbuf[BUFSIZ];
	int first;

	qlen = res_mkquery(QUERY, host, C_IN, rrtype, NULL, 0, NULL,
			   (void*)&buf, sizeof(buf));
	if (qlen < 0) {
	  fprintf(stderr, "res_mkquery failed\n");
	  h_errno = NO_RECOVERY;
	  strcpy(errormsg, "no recovery");
	  return -2;
	}
	n = res_send((void*)&buf, qlen, (void*)&answer, sizeof(answer));
	if (n < 0) {
	  h_errno = TRY_AGAIN;
	  strcpy(errormsg, "try again");
	  return -1;
	}
	eom = (msgdata *)&answer + n;
	/*
	 * find first satisfactory answer
	 */
	hp = (HEADER *) &answer;
	ancount = ntohs(hp->ancount);
	qdcount = ntohs(hp->qdcount);
	h_errno = 0;
	/*
	 * We don't care about errors here, only if we got an answer
	 */
	if (ancount == 0) {
	  if (rrtype == T_CNAME && hp->rcode == NOERROR) {
	    if (qdcount > 0 && strchr(host, '.') == NULL) {
	      cp = (msgdata *)&answer + sizeof(HEADER);
	      if (dn_expand((msgdata *)&answer,
			    eom, cp, host, hbsize) >= 0) {
		if (host[0] == '\0') {
		  host[0] = '.';
		  host[1] = '\0';
		}
	      }
	    }
	    return 1;
	  }
	  return (hp->rcode == NOERROR || hp->rcode == NXDOMAIN) ? 0 : -3;
	}
	cp = (msgdata *)&answer + sizeof(HEADER);
	for (; qdcount > 0; --qdcount) {
#if	defined(BIND_VER) && (BIND_VER >= 473)
	  cp += dn_skipname(cp, eom) + QFIXEDSZ;
#else	/* !defined(BIND_VER) || (BIND_VER < 473) */
	  cp += dn_skip(cp) + QFIXEDSZ;
#endif	/* defined(BIND_VER) && (BIND_VER >= 473) */
	}
	first = 1;
	ok = (rrtype != T_WKS);
	while (--ancount >= 0 && cp < eom) {
	  if ((n = dn_expand((msgdata *)&answer, eom, cp, (void*)nbuf,
			     sizeof(nbuf))) < 0)
	    break;
	  if (first) {
	    strncpy(host, (char *)nbuf, hbsize);
	    host[hbsize - 1] = '\0';
	    first = 0;
	  }
	  cp += n;
	  type = _getshort(cp);
	  cp += sizeof(u_short); /* type */
	  cp += sizeof(u_short); /* class */
	  cp += sizeof(u_long);	/* ttl */
	  n = _getshort(cp);
	  cp += sizeof(u_short); /* dlen */
	  if (type != rrtype) {
	    cp += n;
	    continue;
	  }
	  /*
	   * Assume that only one rrtype will be found.  More
	   * than one is undefined.
	   */
	  if (type == T_WKS) {
	    msgdata *nextcp = cp + n;
	    /* If we have seen a WKS, it had better have SMTP,
	     * however in absence of a WKS, assume SMTP.
	     */
	    if (n <= 4) {	/* IP address! */
	      cp = nextcp;	/* bad data.. */
	      continue;
	    }
	    ok = 0;
	    cp += 4;		/* skip the IP address */
	    if (*cp++ == IPPROTO_TCP) {	/* check protocol */
	      if (cp + (IPPORT_SMTP/8) < nextcp
		  && (*(cp+(IPPORT_SMTP/8))
		      & (0x80>>IPPORT_SMTP%8))) {
		if (verboselog)
		  fprintf(verboselog,"WKS: Found TCP/SMTP\n");
		return 1;
	      }
	      if (verboselog)
		fprintf(verboselog,"WKS: Found WKS of TCP, but not SMTP\n");
	    } else
	      if (verboselog)
		fprintf(verboselog,"WKS: Found WKS other protocol, than TCP\n");
	    cp = nextcp;
	    continue;
	  } else {
	    /* Special processing on T_CNAME ??? */
	    if ((n = dn_expand((msgdata *)&answer, eom, cp, (void*)nbuf,
			       sizeof(nbuf))) < 0)
	      break;
	    strncpy(host, (char *)nbuf, hbsize);
	    host[hbsize - 1] = '\0';
	  }
	  return ok;
	}
	return 0;
}
#endif	/* BIND */
