/* ----------------------------------------------------------------------------]
 Retrieve MX-es by name
 
 developed for Eugene Crosser's ifmail by Ruslan Belkin <rus@ua.net>
 1994
   ----------------------------------------------------------------------------]
 */

# include  <stdio.h>
# include  <netdb.h>
# include  <ctype.h>

# include <stdlib.h>
# include <string.h>

# include <sys/types.h>
# include <sys/param.h>
# include <sys/socket.h>

# include <netinet/in.h>
# include <arpa/inet.h>
# include <arpa/nameser.h>

# include <resolv.h>

extern int h_errno;

# ifndef NO_DATA
# define NO_DATA NO_ADDRESS
# endif

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

char * store_rr   (char * cp, querybuf * msg, char ** mxp);
char * exp_cdname (char * cp, querybuf * msg, char * name);

static char *cname = NULL;

/*
 * Store records in user(s) buffer
 */

int storeinfo (answer, eom, mxp, mxplen)
	querybuf *answer;
	char *eom, **mxp;
	int mxplen;
{
	HEADER *hp;
	char   *cp;
	int ancount, nscount, arcount, qdcount, i;

	/*
	 * find first satisfactory answer
	 */
	hp = (HEADER *) answer;
	ancount = ntohs (hp->ancount);
	qdcount = ntohs (hp->qdcount);
	nscount = ntohs (hp->nscount);
	arcount = ntohs (hp->arcount);

	if (hp -> rcode != NOERROR || (ancount + nscount + arcount) == 0) {
		switch (hp -> rcode) {
			case NXDOMAIN:
				/* Check if it's an authoritive answer */
				if (hp -> aa)	{
					h_errno = HOST_NOT_FOUND;
					return 0;
				} 
				else 	{
					h_errno = TRY_AGAIN;
					return 0;
				}
			case SERVFAIL:
				h_errno = TRY_AGAIN;
				return 0;
			case NOERROR :
				h_errno = NO_DATA;
				return 0; 
			case NOTIMP :
			case REFUSED:
				h_errno = NO_RECOVERY;
				return 0;
		}
		return 0;
	}

	cp = (char *)answer + sizeof(HEADER);
	if (qdcount)	{
		cp += dn_skipname (cp, eom) + QFIXEDSZ;
		while (--qdcount > 0)
			cp += dn_skipname (cp, eom) + QFIXEDSZ;
	}

        for (i = 0; i < mxplen && --ancount >= 0 && cp && cp < eom; i++)
	    cp = store_rr (cp, answer, &mxp[i]);

	return 1;
}

int getinfo (name, domain, type, mxp, mxplen)
	char *name, *domain, **mxp;
	int type, mxplen;
{
	char host [2 * MAXDNAME + 2], *eom;
	int n;
	querybuf buf, answer;

	if (domain == NULL)
		sprintf (host, "%.*s", MAXDNAME, name);
	else
		sprintf (host, "%.*s.%.*s", MAXDNAME, name, MAXDNAME, domain);

	n = res_mkquery (QUERY, host, C_IN, type, (char *)NULL, 0, NULL,
				(char *)&buf, sizeof (buf)
	    		);
		
	if (n < 0)	{
		h_errno = NO_RECOVERY;
		return 0;
	}
	n = res_send ((char *)&buf, n, (char *)&answer, sizeof(answer));
	if (n < 0)	{
		h_errno = TRY_AGAIN;
		return 0;
	}
	eom = (char *)&answer + n;
	return storeinfo (&answer, eom, mxp, mxplen);
}

int lookup_node_mxes (node, domain, mxp, mxplen)
	char *node, *domain, ** mxp;
	int mxplen;
{    
	char  *oldcname  = NULL;
	unsigned   addr;
	int ncnames = 5, hp = 0;
	
	res_init ();

	_res.options &= ~RES_DEFNAMES;
	_res.retrans = 3;

	addr = inet_addr (node);
	h_errno = TRY_AGAIN;

	oldcname = NULL;
	ncnames  = 5;

	while (!hp && h_errno == TRY_AGAIN)
	if (addr == -1)	{
		cname = NULL;
	      	if (oldcname == NULL)
			hp = getinfo (node, domain, T_MX, mxp, mxplen);
		else
			hp = getinfo (node, domain, T_MX, mxp, mxplen);

		if (cname)	{
			/*
			 * too many CNAMEs ?
			 */
			if (ncnames-- == 0)	return -1;
			oldcname = cname;
			hp       = 0;
			h_errno  = TRY_AGAIN;
			continue;
	      	}
	}
	else	{
	    	hp = (int)gethostbyaddr ((char*)&addr, 4, AF_INET);
	   	break;
	}

	if (hp == 0)	return -1;
	return 0;
}

char * store_rr (cp, msg, mxp)
	char *cp, **mxp;
	querybuf *msg;
{
	int type, class, dlen, ttl;
	char *cp1;
	char name[MAXDNAME];

	if ((cp = exp_cdname (cp, msg, name)) == NULL)
		return NULL;			/* compression error */

	type = _getshort (cp);
	cp  += sizeof (u_short);

	class=  _getshort (cp);
	cp  += sizeof (u_short);

	ttl  = _getlong (cp);
	cp  += sizeof (u_long);

	dlen = _getshort (cp);
	cp  += sizeof (u_short);
	cp1  = cp;

	/*
	 * Print type specific data, if appropriate
	 */
	switch (type)	{
		case T_MX:
			cp = exp_cdname (cp + sizeof (u_short), msg, name);
			if (cp != NULL)	
			    strcpy (*mxp = malloc (strlen (name) + 1), name);
			break;
		default:
			cp += dlen;
	}

	if (cp != cp1 + dlen)	return NULL;
	return cp;
}

/*
 * expand correct host name
 */

char * exp_cdname (cp, msg, name)
	char *cp, *name;
	querybuf   *msg;
{
	int n = dn_expand (msg, msg + 512, cp, name, MAXDNAME - 2);

	if (n < 0)	return NULL;

	if (name[0] == '\0')	{
		name[0] = '.';
		name[1] = '\0';
	}
	return cp + n;
}
