/*
**
** Copyright (C) 1994 Swedish University Network (SUNET)
**
**
** This program is developed by UDAC, Uppsala University by commission
** of the Swedish University Network (SUNET). 
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITTNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
** 
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**
**                                        Martin.Wendel@udac.uu.se
**                                        Torbjorn.Wictorin@udac.uu.se
**
**                                        UDAC	
**                                        P.O. Box 174
**                                        S-751 04 Uppsala
**                                        Sweden
**
*/


#include "emil.h"
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>

#ifdef PACKETSZ
#define MAXPACKET PACKETSZ
#else
#define MAXPACKET 1024
#endif
#ifdef NAMED_BIND
#ifdef HAVE_RES_SEARCH
typedef union {
  HEADER hdr;
  u_char buf[MAXPACKET];
} querybuf;

struct mxlist *
get_mx(char *dname)
{
  querybuf answer;
  register HEADER *hp;
  register u_char *cp;
  register int n;
  u_char *eom;
  char *bp;
  char hostbuf[BUFSIZ + 1];
  int type, class, buflen, ancount, qdcount;
  int lpreference, preference;
  int anslen;
  struct mxlist *list, *tlist, *wlist;

  type = T_MX;
  list = NULL;
  if ((_res.options & RES_INIT) == 0 && res_init() == -1)
    {
      fprintf(stderr, "Emil SMTP: res_init(): Check /etc/resolv.conf");
      return(NULL);
    }
  if ((anslen = res_search(dname, C_IN, type, answer.buf, sizeof(answer))) < 0) {
    switch (errno) {
    case TRY_AGAIN:
      exit(EX_TEMPFAIL);
      break;
    default:
      /* MX lookup failed */
      return(NULL);
      break;
    }
  }
  
  eom = answer.buf + anslen;
  lpreference = -1; /* Set lowest preference unset */

  /*
   * find satisfactory answer
   */

  hp = &answer.hdr;
  ancount = ntohs(hp->ancount);
  qdcount = ntohs(hp->qdcount);
  bp = hostbuf;
  buflen = sizeof(hostbuf);
  cp = answer.buf + sizeof(HEADER);

  /* 
   * Skipping over question entries 
   */


  if (qdcount) {
    cp += dn_skipname(cp, eom) + QFIXEDSZ;
    while ( --qdcount > 0)
      cp += dn_skipname(cp, eom) + QFIXEDSZ;
  }
  
  /* 
   * Extracting hostnames from answer entries 
   */

  while (--ancount >= 0 && cp < eom) {
    if ((n = dn_expand((char *)answer.buf, eom, cp, bp, buflen)) < 0) {
      break;
    }
    cp += n;
    type = _getshort(cp);
    if (type == T_MX) {
      cp += sizeof(u_short);
      class = _getshort(cp);
      cp += sizeof(u_short) + sizeof(u_long);
      cp += sizeof(u_short);
      /* MX RDATA */
      preference = _getshort(cp);
      cp += sizeof(u_short);
      if ((n = dn_expand((char *)answer.buf, eom, cp, bp, buflen)) < 0) {
	break;
      }
      tlist = (struct mxlist *)Yalloc(sizeof(struct mxlist));
      tlist->host = NEWSTR(bp);
      tlist->preference = preference;
      if (list == NULL || preference < lpreference) 
	{
	  lpreference = preference;
	  tlist->next = list;
	  list = tlist;
	}
      else
	{
	  /* Sort by preference */
	  for (wlist = list; wlist->next != NULL; wlist = wlist->next)
	      if (wlist->next->preference > preference)
		  break;
	  tlist->next = wlist->next;
	  wlist->next = tlist;
	}
      cp += n;
    }
  }
  if (list == NULL)
    return(NULL);
#ifdef DEBUG
  if (edebug)
    for (wlist = list; wlist != NULL; wlist = wlist->next)
      fprintf(stderr, "Emil MX: %s preference %d\n", wlist->host, wlist->preference);
#endif
  /* Removing localhost and lower preferences */
  if (cmatch(list->host, SMTPHOSTNAME))
    list = NULL;
  else
    for (wlist = list; wlist->next != NULL; wlist = wlist->next)
      if (cmatch(wlist->next->host, SMTPHOSTNAME))
	{
	  wlist->next = NULL;
	  break;
	}
  return(list);
}
#endif
#endif
      





