/* 
 *
 * Expn is a program that opens a tcp connection to a mail hub and 
 * queries it for recipients of mail to a certain address.
 * Original version: December 15, 1988
 */

/*
 * Copyright (c) 1985, 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)gethostnamadr.c	6.36 (Berkeley) 10/7/88";
#endif /* LIBC_SCCS and not lint */

#ifdef RCS
/* I wanted gcc -Wall -pedantic to stop telling me that rcsid_expn_c is
 * defined but not used
 */
#ifndef lint
static char *rcsid_expn_c = "$Header: /afs/.athena.mit.edu/contrib/consult/src/expn/RCS/expn.c,v 1.2 1998/05/22 21:30:20 bdrosen Exp $";
#endif /*lint*/
#endif

#include "expn.h"

int main(int argc, char **argv)
{

  struct hostent *hp;		/* Host info */
  char request[BUFSIZ];         /* Request buffer */ 
  char buf[BUFSIZ];             /* Buffer */
  char recipient[BUFSIZ];   	/* Recipient to expn */
  char host[BUFSIZ];		/* Host in mail address */
  int s;                        /* Socket */
  char *atsign;                 /* '@' in recipient */
  int pmdf = 0;			/* 1 Means don't send hostname */

  if (argc <2)
    usage_exit();
  /* initialize */
  whoami = ((whoami = strrchr(*argv, '/')) ? whoami+1 : *argv);
  recipient[0]='\0';
  strcpy(host,"");
  /* Parse commandline arguments. */
  expn_do_args(argv,host,recipient);
  if (bobo) 
    hp = expn_do_lookup(host,recipient);
  else 
    {
      if (recipient[0] == '\0' ) usage_exit();
      hp = expn_do_lookup(host,recipient);
    }
  if (print) 
    printf("Using host: %s\n",hp->h_name);
  s = expn_do_socket(hp);
  gethostname(buf, BUFSIZ);
  if ((strlen(buf)+strlen(DEFAULT_DOMAIN)+1) > BUFSIZ)
    {
      fprintf(stderr,"Host name too long\n");
      exit(1);
    }
  if (strchr(buf,'.') && 
      (strstr(buf,DEFAULT_DOMAIN) || strstr(buf,DEFAULT_DOMAINL)))
    {
      /* do nothing */ 
    }
  else
    strcat(buf,DEFAULT_DOMAIN);
  (void) sprintf(request, "HELO %s\r\n", buf);
  pmdf = expn_do_send(s,request,0);
  if (bobo) 
    expn_do_send(s,"bobo\nQUIT\r\n",1);
  else 
    {
      if (verb) 
	expn_do_send(s,"verb\r\n",0);
      if (strlen(recipient)>(BUFSIZ - 15))
	{
	  fprintf(stderr,"Recipient too large\n");
	  exit(1);
	}
      if ((atsign = strchr(recipient, '@')) && (pmdf == 1)) 
	{
	  *atsign = '\0';
	  strcpy(request, vrfy?"VRFY":"EXPN");
	  strcat(request, " ");
	  strcat(request, recipient);
	  strcat(request, "\r\nQUIT\r\n");
	}
      else
	(void) sprintf(request, "%s %s\r\nQUIT\r\n", vrfy?"VRFY":"EXPN", recipient);
      expn_do_send(s,request,1);
    }
  close(s);
  exit(0);
}

int expn_do_send(int s,char *request,int rec)
{
  int len;
  char buf[BUFSIZ];
  int pmdf = 0;
 
  /* Send a request */
  if (write(s, request, strlen(request)) == -1) 
    {
      fprintf(stderr, "%s: write: %s\n", whoami, sys_errlist[errno]);
      exit(ERR_WRITE);
    }

/* revc will block if there is no data to read. Some operations
 * need only one line of text and some need more. The first block
 * is for those that need only one line.
 */
  if (!rec) 
    {
      len = recv(s, buf, BUFSIZ, MSG_PEEK);
      (void) read(s, buf, len);
      write(1, buf, len);
      if (strstr(buf,"PMDF") || strstr(buf,"pmdf"))
	pmdf =1;
    } 
  else 
    {
      while ((len = recv(s, buf, BUFSIZ, MSG_PEEK)) > 0) 
	{
	  (void) read(s, buf, len);
	  write(1, buf, len);
	}
      if (len <0) 
	{
	  fprintf(stderr, "%s: recv: %s\n", whoami, sys_errlist[errno]);
	  exit(ERR_RECV);}
    }
  return pmdf;
}

int expn_do_socket (struct hostent *hp)
{
  int s;
  struct servent *sp;    	/* Service info from /etc/services */
  struct sockaddr_in sin;       /* Internet style socket address */

  
  /* Get service information from /etc/services */
  if ((sp = getservbyname("smtp", "tcp")) == NULL) 
    {
      fprintf(stderr, "%s: getservbyname: %s\n", whoami, sys_errlist[errno]);
      exit(ERR_SERVICE);
    }
  
  /* Get an internet style stream socket */
  if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0) 
    {
      fprintf(stderr, "%s: socket: %s\n", whoami, sys_errlist[errno]);
      exit(ERR_SOCKET);
    }

  memset((char *)&sin, 0, (int)sizeof(sin));
  
  sin.sin_port = sp->s_port;
  memcpy((char *)&sin.sin_addr, hp->h_addr, sizeof(struct in_addr));
  sin.sin_family = AF_INET;

  if (connect(s, (struct sockaddr*)&sin, sizeof(sin))) 
    {
      fprintf(stderr, "%s: connect: %s\n", whoami, sys_errlist[errno]);
      exit(ERR_CONNECT);
    }
  return s;
}

struct hostent * expn_do_lookup (char *host, char *recipient) 
{
  struct hostent *hp;
  char *p;
  char *realhost = NULL;        /* Actual host to query */
  
  if (!host || !strcmp(host,""))
    {
      p = strrchr(recipient, '@');
      if (!p)
	host = DEFAULT_HOST;
      else 
	{
	p++;
	if (p && *p)
	  host = p;
	else
	  host = DEFAULT_HOST;}
    }
  if (!nomx) 
    {
      if ((realhost=expn_getmxbyname(host)) == NULL) 
	realhost = host;
    }
  else 
    realhost = host;
  if (!(hp = gethostbyname(realhost))) 
    {
      fprintf(stderr, "%s: gethostbyname: ", whoami);
      switch (h_errno)
	{
	case HOST_NOT_FOUND:
	  fprintf(stderr, "host not found: %s\n",realhost);
	  break;
	case NO_ADDRESS:
	  fprintf(stderr, "no address found for host\n");
	  break;
	case NO_RECOVERY:
	  fprintf(stderr, "nameserver lookup failed miserably\n");
	  break;
	case TRY_AGAIN:
	  fprintf(stderr, "nameserver lookup failed -- try again in a few\n");
	  break;
	}
      exit(-1);
    }
  return hp;
}

void usage_exit(void)
{
  fprintf(stderr, "\nUsage: expn [-vrfy] [-verb] [-bobo] [-nomx] [-prnt]\n\t[-last] [-host host] recipient\n");
  fprintf(stderr,"\t-vrfy: user VRFY instead of EXPN\n");
  fprintf(stderr,"\t-verb: use VERB\n");
  fprintf(stderr,"\t-bobo: get a BOBO count instead of an EXPN\n");
  fprintf(stderr,"\t-nomx: don't lookup the MX record for host\n");
  fprintf(stderr,"\t-prnt: more verbose output of host lookup\n");
  fprintf(stderr,"\t-last: use the last MX record instead of the first\n\n");
  fprintf(stderr, "\tThe recipient can be placed anywhere on the ");
  fprintf(stderr, "commandline.\n\tIf more than one ");
  fprintf(stderr, "recipient appears, the last one found\n\twill be used. ");
  fprintf(stderr, "If the host is not specified with -host\n\tand an @ ");
  fprintf(stderr, "symbol is in the recipient, the text ");
  fprintf(stderr, "following \n\tthe last @ is the host.  If no host is ");
  fprintf(stderr, "specified,\n\t%s is used.\n\n", DEFAULT_HOST);
  exit(ERR_USAGE);
}

void expn_do_args(char **args,char thost[],char recip[])
{

  while (*++args != 0) 
    {
      if (strcmp(*args, "-host") == 0) 
	{
	  if (*(++args) == NULL)
	    usage_exit();
	  else 
	    {
	      if (strlen(*args)>BUFSIZ)
		{
		  fprintf(stderr,"Host name too long\n");
		  exit(1);
		}
	      strcpy(thost, *(args));
	    }
	} 
      else 
	if(strcmp(*args,"-vrfy") == 0) 
	  {
	    vrfy=1;
	  } 
	else 
	  if(strcmp(*args,"-bobo") == 0) 
	    {
	      bobo=1;
	    } 
	  else 
	    if (strcmp(*args,"-verb") == 0) 
	      {
		verb=1;
	      } 
	    else 
	      if(strcmp(*args,"-prnt") == 0) 
		{
		  print=1;
		} 
	      else 
		if(strcmp(*args,"-last") == 0) 
		  {
		    last=1;
		  } else if(strcmp(*args,"-nomx") ==0) 
		    {
		      nomx=1;
		    } 
		else 
		  if (strcmp(*args,"-h") == 0) 
		    {
		      usage_exit();
		    } 
		  else 
		    if (strcmp(*args,"-help") ==0) 
		      {
			usage_exit();
		      } 
		    else 
		      {
			if (*args[0] == '-')
			  usage_exit();
			if (strlen(*args) > BUFSIZ)
			  {
			    fprintf(stderr,"Recipient too large\n");
			    exit(1);
			  }
			strcpy(recip, *args);
		      }
    }
}


char * expn_getans(querybuf *ans, int anslen,char **info)
{
  register HEADER *hp;
  register u_char *cp;
  register int n;
  u_char *eom;
  char *bp, **ap;
  int type, class, buflen, ancount, qdcount;
  int haveanswer, getclass = C_ANY;
  char **hap;
  u_short len;
  int once=1;
  char *host1=NULL;
  

  eom = ans->buf + anslen;
  /*
   * find first satisfactory answer
   */
  hp = &ans->hdr;
  ancount = ntohs(hp->ancount);
  qdcount = ntohs(hp->qdcount);
  bp = hostbuf;
  buflen = sizeof(hostbuf);
  cp = ans->buf + sizeof(HEADER);
  
  /* zero qdcount and skip the query record in the nameserver answer buffer */
  if (qdcount) 
    {
      cp += dn_skipname(cp, eom) + QFIXEDSZ;
      while (--qdcount > 0)
	cp += dn_skipname(cp, eom) + QFIXEDSZ;
    }
  /* Process the actual answer section */
  
  ap = host_aliases;
  host.h_aliases = host_aliases;
  hap = h_addr_ptrs;
  host.h_addr_list = h_addr_ptrs;
  haveanswer = 0;
  
  /* check each answer until we get one */
  while (--ancount >= 0 && cp < eom && !haveanswer) 
    {
      if ((n = dn_expand((char *)ans->buf, eom, cp, bp, buflen)) < 0)
	break;
      /* get type and class of the answer */
      cp += n;
      type = _getshort(cp);
      cp += sizeof(u_short);
      class = _getshort(cp);
      cp += sizeof(u_short) + sizeof(u_long);
      n = _getshort(cp);
      cp += sizeof(u_short);
      /* Its a cname so continue until we get correct name */
      if (type == T_CNAME) 
	{
	  cp += n;
	  if (ap >= &host_aliases[MAXALIASES-1])
	    continue;
	  *ap++ = bp;
	  printf("found cname: %s continuing\n",bp);
	  n = strlen(bp) + 1;
	  bp += n;
	  buflen -= n;
	  continue;
	}
    
      if (type == T_HINFO) 
	{
	  *info = (char *) cp;
	  cp += n;
	  *cp = '\0';
	  buflen -= n;
	  haveanswer++;
	  continue;
	}
      if (type == T_MX) 
	{
	  cp=cp+2;
	  if (print) printf("Found MX record: %s expands to:\n",bp);
	  while ((n = dn_expand((char *)ans->buf, eom,
				cp, bp, buflen)) > 0)
	    {
	      *info = (char *) bp;
	      if (last) 
		host1=bp;
	      else
		if (once) 
		  {
		    host1 = (char *) malloc(sizeof(char) * (strlen(bp) +1));
		    strcpy(host1,bp);
		    once=0;
		  }
	      if (print) printf("\t%s\n",bp);
	      cp += n+  4 * sizeof(u_short) + sizeof(u_long)  ;
	      len = _getshort(cp);
	      cp += sizeof(u_short);
	      buflen -= n;
	      haveanswer++;
	      if ((int)len > 255) 
		break;
	    }
	  cp += n;
	  continue;
	}
      if (type != T_A)  
	{
	  cp += n;
	  continue;
	}
      if (haveanswer) 
	{
	  if (n != host.h_length) 
	    {
	      cp += n;
	      continue;
	    }
	  if (class != getclass) 
	    {
	      cp += n;
	      continue;
	    }
	} 
      else 
	{
	  host.h_length = n;
	  getclass = class;
	  host.h_addrtype = (class == C_IN) ? AF_INET : AF_UNSPEC;
	  host.h_name = host1;
	  bp += strlen(bp) + 1;
	}
      
      bp += sizeof(align) - ((u_long)bp % sizeof(align));
      
      if (bp + n >= &hostbuf[sizeof(hostbuf)]) 
	{
	  break;
	}
      memmove(*hap++ = bp,cp, n);
      bp +=n;
      cp += n;
      haveanswer++;
    }
  if (haveanswer) 
    {
      return host1;
    } 
  else 
    {
      h_errno = TRY_AGAIN;
      return (char *) NULL;
    }
}

char * expn_getmxbyname(char * name)
{
	static querybuf buf;
	register char *cp;
	int n;
	char *info;
	char *host;
	/*
	 * disallow names consisting only of digits/dots, unless
	 * they end in a dot.
	 */
	if (isdigit(name[0]))
	  for (cp = name;; ++cp) 
	    {
	      if (!*cp) 
		{
		  if (*--cp == '.')
		    break;
		  h_errno = HOST_NOT_FOUND;
		  return (char  *) NULL;
		}
	      if (!isdigit(*cp) && *cp != '.') 
		break;
	    }
	n = res_search(name, C_IN, T_MX, buf.buf, sizeof(buf));
	if (n < 0) 
	  return (char *) NULL;
	if((host = expn_getans(&buf, n, &info)))
	  return host;
	else
	  return NULL;
}






