/*
 *	Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *	This will be free software, but only when it is finished.
 */

#undef	RFC974		/* If BIND, check that TCP SMTP service is enabled */

#define	TIMEOUT		(10*60)		/* timeout in seconds, per exchange */

#include <stdio.h>
#include "hostenv.h"
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <sysexits.h>
#include <string.h>
#include <varargs.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/stat.h>

#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <mail.h>
#include "ta.h"

#if	defined(TRY_AGAIN) && defined(USE_RESOLV)
#define	BIND		/* Want BIND (named) nameserver support enabled */
#endif	/* TRY_AGAIN */
#ifdef	BIND
#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) */


#ifndef	L_SET
#define	L_SET	0
#endif	/* L_SET */

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

#define	PROGNAME	"smtpclient"	/* for logging */
#define	CHANNEL		"smtp"	/* the default channel name we deliver for */

/* SV_INTERRUPT is defined in <sys/signal.h> on SunOS */
#ifdef	SV_INTERRUPT
#define	SIGNAL_SYSCALLINTERRUPT(X,Y)	\
	{	struct sigvec sv; \
		(void) sigvec(X, (struct sigvec *)NULL, &sv); \
		sv.sv_handler = Y; \
		sv.sv_flags |= SV_INTERRUPT; \
		(void) sigvec(X, &sv, (struct sigvec *)NULL); \
	}
#else	/* !SV_INTERRUPT */
#define	SIGNAL_SYSCALLINTERRUPT(X,Y)	(void) signal(X,Y)
#endif	/* SV_INTERRUPT */

#ifndef	MAXHOSTNAMELEN
#define	MAXHOSTNAMELEN 64
#endif	/* MAXHOSTNAMELEN */

#define MAXFORWARDERS	128	/* Max number of MX rr's that can be listed */

char myhostname[MAXHOSTNAMELEN+1];
char remotehost[MAXHOSTNAMELEN+1];
char remotemsg[BUFSIZ];
char ipaddress[30];
char *progname;
char *cmdline, *eocmdline, *logfile, *msgfile;
char *myaddrs = NULL;
int nmyaddrs = 0;
int pid;
int debug = 0;
int conndebug = 0;
int timeout = 0;		/* how long do we wait for response? (sec.) */
int readalready = 0;		/* does buffer contain valid message data? */
int wantreserved = 0;		/* open connection on secure (reserved) port */
int statusreport = 0;		/* put status reports on the command line */
int noMX = 0;			/* do not use MX records to deliver mail */
FILE *logfp;

extern int errno;
extern void warning(), report();
extern char *emalloc();

#ifdef	lint
#undef	putc
#define	putc	fputc
#endif	/* lint */

int
main(argc, argv)
	int argc;
	char *argv[];
{
	char file[MAXPATHLEN+1];
	char *channel, *host;
	int i, fd, errflg, c, smtpstatus;
	FILE *smtpfp[2];
	struct ctldesc *dp;
	extern char *optarg;
	extern int optind;
	extern int smtpconn();
	extern int emptyline(), smtpopen(), process(), smtpwrite();
	extern void prversion();
#ifdef	BIND
	int checkmx = 0;	/* check all destination hosts for MXness */
	extern int rightmx();
#endif	/* BIND */
	extern SIGNAL_TYPE sig_pipe();
#ifdef	TIMEOUT
	extern SIGNAL_TYPE sig_alarm();
#endif	/* TIMEOUT */

	pid = getpid();
	ipaddress[0] = '\0';
	msgfile = "?";
	getout = 0;
	cmdline = &argv[0][0];
	eocmdline = cmdline;
	for (i = 0; i < argc; ++i)
		eocmdline += strlen(argv[i]) + 1;
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGINT, wantout);
	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
		(void) signal(SIGHUP, wantout);
	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
		(void) signal(SIGTERM, wantout);
#if 0
	SIGNAL_SYSCALLINTERRUPT(SIGPIPE, sig_pipe);
#else
	(void) signal(SIGPIPE, SIG_IGN);
#endif
#ifdef	TIMEOUT
	SIGNAL_SYSCALLINTERRUPT(SIGALRM, sig_alarm);
	timeout = TIMEOUT;
#endif	/* TIMEOUT */

	progname = PROGNAME;
	errflg = 0;
	channel = CHANNEL;
	wantreserved = debug = statusreport = 0;
	logfile = NULL;
	myhostname[0] = '\0';
	remotemsg[0] = '\0';
	remotehost[0] = '\0';
	while ((c = getopt(argc, argv, "c:deh:l:rsxDT:V")) != EOF) {
		switch (c) {
		case 'c':	/* specify channel scanned for */
			channel = emalloc(strlen(optarg)+1);
			(void) strcpy(channel, optarg);
			break;
		case 'd':	/* turn on debugging output */
			debug = 1;
			break;
		case 'e':	/* expensive MX checking for *all* addresses */
#ifdef	BIND
			checkmx = 1;
#else	/* !BIND */
			++errflg;
			fprintf(stderr,
				"%s: -e unavailable, no nameserver support!\n",
				progname);
#endif	/* BIND */
			break;
		case 'h':	/* my hostname */
			(void) strcpy(myhostname, optarg);
			break;
		case 'l':	/* log file */
			logfile = emalloc(strlen(optarg)+1);
			(void) strcpy(logfile, optarg);
			break;
		case 'r':	/* use reserved port for SMTP connection */
			wantreserved = 1;
			break;
		case 's':	/* report status to command line */
			statusreport = 1;
			break;
		case 'x':       /* don't use MX records lookups */
			noMX = 1;
			break;
		case 'D':	/* only try connecting to remote host */
			conndebug = 1;
			break;
		case 'T':	/* specify Timeout in seconds */
			if ((timeout = atoi(optarg)) < 5) {
				(void) fprintf(stderr,
					       "%s: illegal timeout: %d\n",
						argv[0], timeout);
				++errflg;
			}
			break;
		case 'V':
			prversion("smtp");
			exit(0);
			break;
		default:
			++errflg;
			break;
		}
	}
	if (errflg || optind != argc - 1) {
		(void) fprintf(stderr,
			       "Usage: %s [-c channel] host\n", argv[0]);
		exit(EX_USAGE);
	}
	host = emalloc(strlen(argv[optind]) + 1);
	(void) strcpy(host, argv[optind]);

	if (conndebug && !debug) {
		(void) smtpconn(host, smtpfp);
		exit(0);
	}

	if (logfile != NULL) {
		if ((fd = open(logfile, O_CREAT|O_APPEND|O_WRONLY, 0644)) < 0)
			(void) fprintf(stderr,
				       "%s: cannot open logfile \"%s\"!\n",
				       argv[0], logfile);
		else
			logfp = fdopen(fd, "a");
	} else
		logfp = NULL;

	/* defer opening a connection until we know there is work */
	smtpstatus = EX_DATAERR;
	while (!getout && fgets(file, sizeof file, stdin)) {
		if (emptyline(file, sizeof file))
			break;
#ifdef	BIND
		if (checkmx)
			dp = ctlopen(file, channel, host, &getout, rightmx);
		else
#endif	/* BIND */
			dp = ctlopen(file, channel, host, &getout, (int(*)())0);
		if (dp == NULL)
			continue;
		if (smtpstatus == EX_DATAERR)
			smtpstatus = smtpopen(host, smtpfp);
		smtpstatus = process(dp, smtpstatus, smtpfp);
		ctlclose(dp);
	}
	if (smtpstatus == EX_OK) {
		smtpstatus = smtpwrite(smtpfp, "QUIT", (char *)NULL);
		(void) fclose(smtpfp[0]);
		(void) fclose(smtpfp[1]);
	}
	if (logfp != NULL)
		(void) fclose(logfp);
	exit(smtpstatus);
	/* NOTREACHED */
	return smtpstatus;
}

int
process(dp, smtpstatus, smtpfp)
	struct ctldesc *dp;
	int smtpstatus;
	FILE *smtpfp[];
{
	struct rcpt *rp, *rphead;
	int loggedid;
	extern void deliver();

	readalready = 0; /* ignore any previous message data cache */
	loggedid = 0;

	for (rp = rphead = dp->recipients; rp != NULL; rp = rp->next) {
		if (rp->next == NULL
		    || rp->addr->link != rp->next->addr->link
		    || rp->newmsgheader != rp->next->newmsgheader) {
			if (smtpstatus == EX_OK) {
				if (logfp != NULL && !loggedid) {
					loggedid = 1;
					(void) fprintf(logfp,
						       "%d#\t%s: %s\n",
						       pid, dp->msgfile,
						       dp->logident);
					(void) fflush(logfp);
				}
				deliver(smtpfp, rphead, rp->next,
						dp->msgfd, dp->msgbodyoffset);
				rphead = rp->next;
			} else {
				while (rphead != rp->next) {
					diagnostic(rphead, smtpstatus,
							   remotemsg);
					rphead = rphead->next;
				}
			}
		}
	}
	return smtpstatus;
}

/*
 * deliver - deliver the letter in to user's mail box.  Return
 *	     errors and requests for further processing in the structure
 */

void
deliver(smtpfp, startrp, endrp, messagefd, msgoffset)
	FILE *smtpfp[2];
	struct rcpt *startrp, *endrp;
	int messagefd;
	long msgoffset;
{
	struct rcpt *rp;
	int r, nrcpt, hsize, size, tout;
	struct stat stbuf;
	extern int writebuf(), appendlet(), smtpwrite();

	if (lseek(messagefd, msgoffset, L_SET) < 0L)
		warning("Cannot seek to message body! (%m)", (char *)NULL);
	r = smtpwrite(smtpfp, "MAIL From:<%s>", startrp->addr->link->user);
	if (r != EX_OK) {
		for (rp = startrp; rp != endrp; rp = rp->next)
			diagnostic(rp, r, "%s", remotemsg);
		(void) smtpwrite(smtpfp, "RSET", (char *)NULL);
		return;
	}
	nrcpt = 0;
	for (rp = startrp; rp != endrp; rp = rp->next) {
		r = smtpwrite(smtpfp, "RCPT To:<%s>", rp->addr->user);
		if (r != EX_OK)
			diagnostic(rp, r, "%s", remotemsg);
		else {
			++nrcpt;
			rp->status = EX_OK;
		}
	}
	if (nrcpt == 0) {
		/* all the RCPT To addresses were rejected, so reset server */
		(void) smtpwrite(smtpfp, "RSET", (char *)NULL);
		return;
	}
	if ((r = smtpwrite(smtpfp, "DATA", (char *)NULL)) != EX_OK) {
		for (rp = startrp; rp != endrp; rp = rp->next)
			if (rp->status == EX_OK)
				diagnostic(rp, EX_TEMPFAIL, "%s", remotemsg);
		(void) smtpwrite(smtpfp, "RSET", (char *)NULL);
		return;
	}
	hsize = strlen(startrp->newmsgheader);
	(void) writebuf(smtpfp[1], (char *)NULL, 0);	/* magic init. */
	if (writebuf(smtpfp[1], startrp->newmsgheader, hsize) != hsize
	    || writebuf(smtpfp[1], "\n", 1) != 1) {
		for (rp = startrp; rp != endrp; rp = rp->next)
			if (rp->status == EX_OK)
				diagnostic(rp, EX_TEMPFAIL, "%s",
					      "header write error");
		(void) smtpwrite(smtpfp, "RSET", (char *)NULL);
		return;
	}
	++hsize;
	/* append message body itself */
	if (fstat(messagefd, &stbuf) >= 0)
		size = hsize + stbuf.st_size - msgoffset;
	else
		size = -hsize;
	if ((r = appendlet(smtpfp[1], messagefd, hsize, size)) != EX_OK) {
	   fail:
		for (rp = startrp; rp != endrp; rp = rp->next)
			if (rp->status == EX_OK)
				diagnostic(rp, r, "%s", remotemsg);
		/* The failure occured during processing and was due to an I/O
		 * error. The safe thing to do is to just abort processing.
		 * Don't send the dot! 2/June/94 edwin@cs.toronto.edu
		 */
		return;
	}
	/*
	 * This is the one place where we *have* to wait forever because
	 * there is no reliable way of aborting the transaction.
	 * Note that a good and useful approximation to "forever" is one day.
	 * Murphy's Law you know: Connections will hang even when they can't.
	 */
	tout = timeout, timeout = 60*60*24;
	r = smtpwrite(smtpfp, ".", (char *) NULL);
	timeout = tout;
	if (r != EX_OK)
		goto fail;
	if (logfp != NULL) {
		if (r != EX_OK)
			(void) fprintf(logfp, "%d#\t%s\n", pid, remotemsg);
		else
			(void) fprintf(logfp, "%d#\t%d bytes, %d in header\n",
				      pid, size, hsize);
		(void) fflush(logfp);
	}
	for (rp = startrp; rp != endrp; rp = rp->next)
		if (rp->status == EX_OK)
			diagnostic(rp, r, "%s", remotemsg);
	return;
}

/*
 * appendlet - append letter to file pointed at by fd
 */

int
appendlet(fp, mfd, hsize, msize)
	FILE *fp;
	int mfd, hsize, msize;
{
	register int i;
	register int bufferfull;
	int lastwasnl;
	static char buffer[BUFSIZ];
	extern int writebuf();

	/* can we use cache of message body data */
	if (readalready != 0) {
		if (writebuf(fp, buffer, readalready) != readalready)
			return EX_IOERR;
		if (buffer[readalready-1] != '\n')
			(void) writebuf(fp, "\n", 1);
		if (statusreport)
			report("DATA %d (100%%)", readalready+hsize);
		if (fflush(fp) != 0)
			return EX_IOERR;
		return EX_OK;
	}

	/* we are assumed to be positioned properly at start of message body */
	bufferfull = 0;
	lastwasnl = 1;	/* we are guaranteed to have a \n after the header */
	(void) writebuf(fp, (char *)NULL, 0);	/* magic initialization */
	while ((i = read(mfd, buffer, sizeof buffer)) != 0) {
		if (i < 0) {
			strcpy(remotemsg, "read error from message file");
			return EX_IOERR;
		}
		lastwasnl = (buffer[i-1] == '\n');
		if (writebuf(fp, buffer, i) != i)
			return EX_IOERR;
		if (statusreport) {
			hsize += i;
			report("DATA %d/%d (%d%%)",
				     hsize, msize, (hsize*100+msize/2)/msize);
		}
		readalready = i;
		bufferfull++;
	}
	/* we must make sure the last thing we transmit is a CRLF sequence */
	if (!lastwasnl)
		if (writebuf(fp, "\n", 1) != 1)
			return EX_IOERR;

	if (bufferfull > 1)	/* not all in memory, need to reread */
		readalready = 0;
	if (fflush(fp) != 0)
		return EX_IOERR;
	return EX_OK;
}

/*
 * Writebuf() is like write(), except all '\n' are converted to "\r\n"
 * (CRLF), and the sequence "\n." is converted to "\r\n..".
 */

int
writebuf(fp, buf, len)
	FILE *fp;
	char *buf;
	int len;
{
	register char *cp;
	register int n;
	int tlen, wlen;
	register int state;
	static int save = 0;

	if (buf == NULL) {		/* magic initialization */
		save = 1;
		return 0;
	}
	state = save;
	for (cp = buf, n = len, tlen = 0; n > 0; --n, ++cp) {
		if (state && *cp != '\n') {
			state = 0;
			if (*cp == '.' && putc(*cp, fp) == EOF) {
				strcpy(remotemsg, "write error 1");
				return EOF;
			}
		} else if (*cp == '\n') {
			if (cp > buf) {
				wlen = fwrite(buf, sizeof (char), cp-buf, fp);
				if (wlen <= 0) {
					strcpy(remotemsg, "write error 2");
					return EOF;
				}
				tlen += wlen;
			}
			if (putc('\r', fp) == EOF || putc(*cp, fp) == EOF) {
				strcpy(remotemsg, "write error 3");
				return EOF;
			}
			++tlen;
			buf = cp+1;
			state = 1;
		}
	}
	save = state;
	if (cp > buf) {
		wlen = fwrite(buf, sizeof (char), cp-buf, fp);
		if (wlen <= 0) {
			strcpy(remotemsg, "write error 4");
			return EOF;
		}
		tlen += wlen;
	}
	return tlen;
}

int
smtpopen(host, fparr)
	char *host;
	FILE *fparr[];
{
	int i;
	extern int getmyhostname(), smtpconn();
	extern int smtpwrite();
	extern void stashmyaddresses();

	if (myhostname[0] == '\0') {
		(void) getmyhostname(myhostname, sizeof myhostname);
		stashmyaddresses(myhostname);
	}
	if (debug) {
		(void) fprintf(logfp, "%d#\tsmtpopen: connecting to %s\n", pid, host);
		(void) fflush(logfp);
	}
	if ((i = smtpconn(host, fparr)) != EX_OK)
		return i;
	i = smtpwrite(fparr, "HELO %s", myhostname);
	if (debug) {
		(void) fprintf(logfp, "%d#\tsmtpopen: status = %d\n", pid, i);
		(void) fflush(logfp);
	}
	return i;
}

struct mxdata {
	msgdata	*host;
	u_short	pref;
};

int
smtpconn(host, fparr)
	char *host;
	FILE *fparr[];
{
	int	i, r, retval, smtpfd;
	u_long	ipaddr;
	struct hostent *hp, hent;
	char	hbuf[MAXHOSTNAMELEN+1], *addrs[2];
	struct mxdata mxh[MAXFORWARDERS];
	extern int makeconn(), getrr(), getmxrr();
	extern void hp_setalist();
#ifdef	BIND
	extern int h_errno;

	h_errno = 0;
#endif	/* BIND */

	if (debug) {
		(void) fprintf(logfp, "%d#\tsmtpconn: host = %s\n", pid, host);
		(void) fflush(logfp);
	}
	if (*host == '[') {	/* hostname is IP address domain literal */
		char *cp, *hcp, buf[BUFSIZ];

		for (cp = buf, hcp = host + 1;
		     isascii(*hcp) && (isdigit(*hcp) || *hcp == '.');
		     ++cp, ++hcp)
			*cp = *hcp;
		*cp = '\0';
		ipaddr = inet_addr(buf);
		if (((int)ipaddr) == -1) {
			(void) sprintf(remotemsg, "bad IP address: %s", host);
			return EX_NOHOST;
		}
		hp = &hent;
		/* don't really care about gethostbyaddr() here */
		hp->h_name = host;
		hp->h_addrtype = AF_INET;
		hp->h_aliases = NULL;
		hp->h_length = sizeof ipaddr;
		addrs[0] = (char *)&ipaddr;
		addrs[1] = NULL;
		hp_setalist(hp, addrs);
		retval = makeconn(hp, &smtpfd, fparr);
	} else {
		hbuf[0] = '\0';
		errno = 0;
		if ((hp = gethostbyname(host)) == NULL) {
			if (errno == ETIMEDOUT
#ifdef	XBIND
			    || h_errno == TRY_AGAIN
#endif	/* BIND */
			    ) {
				(void) sprintf(remotemsg,"gethostbyname/a: try later");
				return EX_TEMPFAIL;
			}
#ifdef	BIND
			/*
			 * Find the canonical name
			 * (needed when target of a CNAME has no other RR's)
			 */
			(void) strcpy(hbuf, host);
			if ((i = getrr(hbuf, sizeof hbuf, (u_short)T_CNAME)) == 1) {
				host = hbuf;
			} else if (h_errno == TRY_AGAIN) {
				(void) strcpy(remotemsg,
					       "canonicalize: try later");
				return EX_TEMPFAIL;
			} /* else [Matti Aarnio says:]
				it is possible that MXs or As are available
				even though CNAME lookups fail (see .UK) */
#else	/* !BIND */
			(void) sprintf(remotemsg, "canonicalize lookup failed on %s", host);
			/* temporary failure since the router knew it */
			return EX_TEMPFAIL;	/* was EX_NOHOST */
#endif	/* BIND */
		} else
			host = hp->h_name;

		mxh[0].host = NULL;	/* use the hostent we got */
#ifdef	BIND
		/*
		 * Look for MX RR's. If none found, use the hostentry in hp.
		 * Otherwise loop through all the mxhosts doing gethostbyname's.
		 */
		if (!noMX) {
			switch (getmxrr(host, mxh, sizeof mxh/sizeof mxh[0],0)){
				/* remotemsg is printed within getmxrr */
			case EX_SOFTWARE:
			case EX_TEMPFAIL:
			case EX_UNAVAILABLE:
				return EX_TEMPFAIL;
			case EX_NOHOST:
				return EX_NOHOST;
			case EX_NOPERM:
				return EX_NOPERM;
			}
		}
#endif	/* BIND */
		if (noMX || mxh[0].host == NULL) {
			errno = 0;
			if (hp == NULL && (hp = gethostbyname(host)) == NULL) {
				if (errno == ETIMEDOUT
#ifdef	BIND
				    || h_errno == TRY_AGAIN
#endif	/* BIND */
				    ) {
					(void) strcpy(remotemsg,
					    "gethostbyname/b: try later");
					return EX_TEMPFAIL;
				}
				(void) strcpy(remotemsg,
				       "nameserver data inconsistency");
				return EX_UNAVAILABLE;
			}
			retval = makeconn(hp, &smtpfd, fparr);
		} else {
			retval = EX_UNAVAILABLE;
			for (i = 0; mxh[i].host != NULL; ++i) {
				if ((hp = gethostbyname(mxh[i].host)) == NULL) {
					free(mxh[i].host);
					continue;
				}
				r = makeconn(hp, &smtpfd, fparr);
				if (r == EX_OK) {
					while (mxh[i].host != NULL)
						(void) free(mxh[i++].host);
					retval = EX_OK;
					break;
				} else if (r == EX_TEMPFAIL)
					retval = EX_TEMPFAIL;
				(void) free(mxh[i].host);
			}
		}
	}
	if (retval == EX_OK) {
		(void) strcpy(remotehost, hp->h_name);
	} else
		(void) strcpy(remotehost, host);
	if (debug) {
		(void) fprintf(logfp,
			       "%d#\tsmtpconn: retval = %d\n", pid, retval);
		(void) fflush(logfp);
	}
	return retval;
}

int
makeconn(hp, fdp, fparr)
	struct hostent *hp;
	int *fdp;
	FILE *fparr[];
{
	int retval, i;
	extern int vcsetup();
	extern void hp_init();
	extern char **hp_getaddr(), **hp_nextaddr();

#ifdef	BIND
#ifdef	RFC974
	char hostbuf[MAXHOSTNAMELEN+1];
	extern int getrr();

	(void) strcpy(hostbuf, hp->h_name);
	if (getrr(hostbuf, sizeof hostbuf, (u_short)T_WKS) != 1) {
		(void) sprintf(remotemsg, "no SMTP capability: %s", hostbuf);
		return EX_UNAVAILABLE;
	}
#endif	/* RFC974 */
#endif	/* BIND */
	
	retval = EX_UNAVAILABLE;
	for (hp_init(hp); *hp_getaddr() != NULL; (void) hp_nextaddr()) {
		for (i = 0; i < nmyaddrs; ++i) {
			/* if this is myself, skip to next MX host */
			if (memcmp(*hp_getaddr(), &myaddrs[i*hp->h_length],
					      hp->h_length) == 0)
				return EX_TEMPFAIL;
		}
		switch (vcsetup(*hp_getaddr(), hp->h_length, fdp)) {
		case EX_OK:
			fparr[0] = fdopen(*fdp, "r");
			fparr[1] = fdopen(*fdp, "w");
			retval = smtpwrite(fparr, (char *)NULL, (char *)NULL);
			if (retval != EX_OK)
				/*
				 * If you want to continue with the next host,
				 * the below should be 'return EX_TEMPFAIL'.
				 */
				break;	/* try another host address */
			return EX_OK;
		case EX_TEMPFAIL:
			retval = EX_TEMPFAIL;
			break;
		}
	}
	return retval;
}

int
vcsetup(ipaddrcp, ipaddrlen, fdp)
	char *ipaddrcp;
	int ipaddrlen, *fdp;
{
	register int s;
	struct sockaddr_in sad;
	u_short p;
	extern char *strerror();
	extern char *dottedquad();

	(void) strcpy(ipaddress, dottedquad((struct in_addr *)ipaddrcp));
	if (conndebug)
		(void) fprintf(stderr, "Trying %s ... ", ipaddress);
	/* try grabbing a port */
	sad.sin_family = AF_INET;
	sad.sin_addr.s_addr = 0;
	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		(void) sprintf(remotemsg, "socket: %s", strerror(errno));
		if (conndebug)
			(void) fprintf(stderr, "%s\n", remotemsg);
		return EX_TEMPFAIL;
	}
	if (wantreserved && getuid() == 0) {
		for (p = IPPORT_RESERVED-1; p >= IPPORT_RESERVED/2; --p) {
			sad.sin_port = htons(p);
			if (bind(s, (struct sockaddr *)&sad, sizeof sad) >= 0)
				break;
			if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
				(void) sprintf(remotemsg, "bind: %s", strerror(errno));
				if (conndebug)
					fprintf(stderr, "%s\n", remotemsg);
				return EX_UNAVAILABLE;
			}
		}
		if (p < IPPORT_RESERVED/2) {
			(void) sprintf(remotemsg, "too many busy ports");
			if (conndebug)
				fprintf(stderr, "%s\n", remotemsg);
			return EX_TEMPFAIL;
		}
	}
	bcopy(ipaddrcp, (char *)&sad.sin_addr, ipaddrlen);
	sad.sin_port = htons((u_short)IPPORT_SMTP);
	if (connect(s, (struct sockaddr *)&sad, sizeof sad) >= 0) {
		*fdp = s;
#if	BSD >= 43
		(void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 1, sizeof (int));
#else	/* BSD < 43 */
		(void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0);
#endif	/* BSD >= 43 */
		if (conndebug)
			fprintf(stderr, "connected!\n");
		return EX_OK;
	}
	(void) sprintf(remotemsg, "connect: %s", strerror(errno));
	if (conndebug)
		fprintf(stderr, "%s\n", remotemsg);
	switch (errno) {	/* from sendmail... */
	case EISCONN:
	case ETIMEDOUT:
	case EINPROGRESS:
	case EALREADY:
	case EADDRINUSE:
	case EHOSTDOWN:
	case ENETDOWN:
	case ENETRESET:
	case ENOBUFS:
	case ECONNREFUSED:
	case ECONNRESET:
	case EHOSTUNREACH:
	case ENETUNREACH:
	case EPERM:
	/* wonder how Sendmail missed this one... */
	case EINTR:
		(void) close(s);
		return EX_TEMPFAIL;
	}
	(void) close(s);
	return EX_UNAVAILABLE;
}

SIGNAL_TYPE
sig_pipe()
{
	if (logfp != NULL) {
		(void) fprintf(logfp, "%d#\t*** Received SIGPIPE!\n", pid);
		(void) fflush(logfp);
abort();
	}
}

#ifdef	TIMEOUT
int	gotalarm;

SIGNAL_TYPE
sig_alarm()
{
	gotalarm = 1;
}
#endif	/* TIMEOUT */

void
rmsgappend(s)
	char *s;
{
	char *cp = remotemsg + strlen(remotemsg);

	if (cp + strlen(s) >= remotemsg + sizeof remotemsg - 1)
		(void) strncpy(cp, s, remotemsg + sizeof remotemsg - 1 - cp);
	else
		(void) strcpy(cp, s);
}

int dflag = 0;

int
smtpwrite(smtpfp, fmt, arg)
	FILE *smtpfp[2];
	char *fmt, *arg;
{
	register char *s, *cp;
	int i, response, r, infd, outfd, rmsgsize;
	char ch, buf[8096];	/* XX: growl */
	extern char *strerror();

#ifdef	TIMEOUT
	(void) alarm(timeout);	/* This much total to write and get answer */
	gotalarm = 0;
	/* we don't need to turn alarms off again (yay!) */
#endif	/* TIMEOUT */
	(void) fflush(smtpfp[0]);
	(void) fflush(smtpfp[1]);
	infd = fileno(smtpfp[0]);
	outfd = fileno(smtpfp[1]);
	if (fmt != NULL) {
		/* we use raw I/O so we have control over what happens on
		   interrupts, etc. -- I don't like setjmp/longjmp */
		for (s = fmt, cp = buf; *s != '\0'; ++s) {
			if (*s == '%' && *++s == 's') {
				(void) strcpy(cp, arg);
				cp += strlen(cp);
			} else
				*cp++ = *s;
		}
		*cp++ = '\r';
		*cp++ = '\n';
		if ((r = write(outfd, buf, cp - buf)) == -1) {
#ifdef	TIMEOUT
			if (gotalarm) {
				(void) strcpy(remotemsg, "Timeout on write!");
			} else
#endif	/* TIMEOUT */
				(void) sprintf(remotemsg,
					       "write to server: %s",
					       strerror(errno));
			return EX_TEMPFAIL;
		} else if (r != cp - buf) {
			(void) sprintf(remotemsg, "Only wrote %d of %d bytes!",
					   r, cp - buf);
			return EX_TEMPFAIL;
		}
		if (logfp != NULL) {
			if (dflag) abort();
			(void) fprintf(logfp, "%dw\t", pid);
			(void) fprintf(logfp, fmt, arg);
			(void) fprintf(logfp, "\n");
			(void) fflush(logfp);
			dflag = 1;
		}
	}
	i = 2;	/* state variable, beginning of new line */
	remotemsg[0] = '\0';
	rmsgsize = 0;
	if (debug) {
		(void) fprintf(logfp, "%d#\tAttempting to read reply\n");
		(void) fflush(logfp);
	}
	if (statusreport && fmt != NULL)
		report(fmt, arg);
	cp = buf;
	do {
		r = read(infd, cp, sizeof buf - (cp - buf));
		if (r > 0) {
			for (s = cp, cp += r; s < cp; ++s) {
				switch (i) {
				/* i == 0 means we're on last line */
				case 1:	/* looking for \n */
					if (*s != '\n')
						break;
					*s = '\0';
					rmsgappend(&buf[4]);
					rmsgappend(" ");
					if (logfp != NULL) {
						if (debug)
							(void) putc('\n',logfp);
						(void) fprintf(logfp,
						      "%dr\t%s\n", pid, buf);
						(void) fflush(logfp);
					}
					if (s + 1 < cp)
						bcopy(s+1, buf, cp-s-1);
					cp = buf + (cp-s-1);
					s = buf;
					--s;	/* incremented in for() stmt */
					/* fall through */
				case 2:	/* saw \n, 1st char on line */
				case 3: /* 2nd char on line */
				case 4: /* 3rd char on line */
					if (i == 1
					    || (isascii(*s) && isdigit(*s)))
						++i;
					else
					/* silently look for num. code lines */
						i = 1;
					break;
				case 5: /* 4th char on line */
					i = (*s == '-');
					break;
				}
				if (debug) {
					if (i%8 == 0) {
						if (i > 0)
							(void) putc('\n',logfp);
						(void) fprintf(logfp,
							       "%d#\t", pid);
					}
					if (isprint(*s)) 
						(void) putc(*s, logfp);
					else
						(void) fprintf(logfp,
							       "\\%03o", *s);
				}
			}
			if (debug) {
				(void) fprintf(logfp, "\n");
				(void) fflush(logfp);
			}
		} else if (r == -1) {
#ifdef	TIMEOUT
			if (gotalarm)
				(void) strcpy(remotemsg,
					      "Timeout on read!");
			else
#endif	/* TIMEOUT */
				(void) sprintf(remotemsg,
					       "read from server: %s",
					       strerror(errno));
			dflag = 0;
			return EX_TEMPFAIL;
		} else {
			(void) strcpy(remotemsg, "read 0 bytes from server!");
			dflag = 0;
			return EX_TEMPFAIL;
		}
		/* Exit if the last thing we read was a LF and we're on the
		   last line (in case of multiline response).  This
		   also takes care of the required CRLF termination */
	} while (cp < buf+sizeof buf && !(i == 0 && *(cp-1) == '\n'));
	if (cp >= buf+sizeof buf) {
		(void) strcpy(remotemsg,
			      "response overran input buffer!");
		dflag = 0;
		return EX_TEMPFAIL;
	}
	*--cp = '\0';	/* kill the LF */
	if (cp - buf < 5) {
		(void) sprintf(remotemsg, "SMTP response '%s' unexpected!", buf);
		dflag = 0;
		return EX_TEMPFAIL;
	}
	--cp;
	/* trim trailing whitespace */
	while (isascii(*cp) && isspace(*cp))
		--cp;
	*++cp = '\0';
	for (i = 0; i < 4; ++i)		/* can't happen, right? wrong... */
		if (buf[i] == ' ')
			break;
	if (i == 4) --i;
	ch = buf[i];
	buf[i] = '\0';
	response = atoi(buf);
	if (logfp != NULL) {
		(void) fprintf(logfp, "%dr\t%s%c%s\n", pid, buf, ch, &buf[i+1]);
		(void) fflush(logfp);
	}
	buf[i] = ch;
	rmsgappend(buf);
	dflag = 0;
	switch (response) {
	case 211: /* System status, or system help reply */
	case 214: /* Help message */
	case 220: /* <domain> Service ready */
	case 221: /* <domain> Service closing transmission channel */
	case 250: /* Requested mail action okay, completed */
	case 251: /* User not local; will forward to <forward-path> */
	case 354: /* Start mail input; end with <CRLF>.<CRLF> */
		return EX_OK;
	case 421: /* <domain> Service not available, closing transmission channel */
	case 450: /* Requested mail action not taken: mailbox unavailable */
	case 451: /* Requested action aborted: local error in processing */
	case 452: /* Requested action not taken: insufficient system storage */
		return EX_TEMPFAIL;
	case 500: /* Syntax error, command unrecognized */
	case 501: /* Syntax error in parameters or arguments */
		return EX_USAGE;
	case 502: /* Command not implemented */
		return EX_PROTOCOL;
	case 503: /* Bad sequence of commands */
		return EX_TEMPFAIL;
	case 504: /* Command parameter not implemented */
		return EX_PROTOCOL;
	case 550: /* Requested action not taken: mailbox unavailable */
	case 551: /* User not local; please try <forward-path> */
		return EX_NOUSER;
	case 552: /* Requested mail action aborted: exceeded storage allocation */
		return EX_TEMPFAIL;
	case 553: /* Requested action not taken: mailbox name not allowed */
		return EX_NOUSER;
	case 554: /* Transaction failed */
		return EX_UNAVAILABLE;
	}
	switch (response/100) {
	case 2:
	case 3:
		return EX_OK;
	case 4:
		return EX_TEMPFAIL;
	case 5:
		return EX_UNAVAILABLE;
	}
	return EX_TEMPFAIL;
}

/*
 * In theory, this should modify the command that ps shows for this process.
 * This is known to not be portable, hopefully it will break badly on systems
 * where it doesn't work.
 */

/* VARARGS */
void
report(va_alist)
	va_dcl
{
	va_list	ap;
	char *cp, buf[8192];
	
	va_start(ap);
	(void) sprintf(buf, "-%s <%ld ", remotehost, atol(msgfile));
#ifdef	notdef
	if (logfp)
		(void) sprintf(buf+strlen(buf), ">>%s ", logfile);
	(void) strcat(buf, "# ");
#endif
	cp = va_arg(ap, char *);
#ifdef	USE_VFPRINTF
	(void) vsprintf(buf+strlen(buf), cp, ap);
#else	/* !USE_VFPRINTF */
	(void) sprintf(buf+strlen(buf), cp, va_arg(ap, char *));
#endif	/* USE_VFPRINTF */
	for (cp = buf+strlen(buf); cp < buf + (eocmdline - cmdline); ++cp)
		*cp = ' ';
	buf[eocmdline-cmdline] = '\0';
	(void) strcpy(cmdline, buf);
	va_end(ap);
}

void
stashmyaddresses(host)
	char *host;
{
	int naddrs;
	struct hostent *hp;
	extern void hp_init();
	extern char **hp_getaddr(), **hp_nextaddr();

	if ((hp = gethostbyname(host)) == NULL)
		return;

	naddrs = 0;
	for (hp_init(hp); *hp_getaddr() != NULL; (void) hp_nextaddr())
		++naddrs;
	nmyaddrs = naddrs;
	if ((myaddrs = (char *)malloc(nmyaddrs * hp->h_length)) == NULL)
		return;
	for (hp_init(hp); *hp_getaddr() != NULL; (void) hp_nextaddr())
		bcopy(*hp_getaddr(), &myaddrs[(--naddrs)*hp->h_length], hp->h_length);
}

#ifdef	BIND

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

extern int h_errno;

int
getmxrr(host, mx, maxmx, depth)
	char *host;
	struct mxdata mx[];
	int maxmx, depth;
{
	HEADER *hp;
	msgdata *eom, *cp;
	querybuf qbuf, answer;
	struct mxdata mxtemp;
	msgdata buf[8192], realname[8192];
	int n, i, j, nmx, ancount, qdcount, maxpref;
	u_short type;
	extern int res_mkquery(), res_send(), dn_skipname(), dn_expand();
	extern int cistrcmp();

	n = res_mkquery(QUERY, host, C_IN, T_MX, (char *)NULL, 0, NULL,
		(char *)&qbuf, sizeof qbuf);
	if (n < 0) {
		(void) fprintf(stderr, "res_mkquery failed\n");
		(void) sprintf(remotemsg, "res_mkquery failed");
		return EX_SOFTWARE;
	}
	n = res_send((char *)&qbuf, n, (char *)&answer, sizeof answer);
	if (n < 0) {
		(void) sprintf(remotemsg, "res_send failed");
		return EX_TEMPFAIL;
	}
	eom = (msgdata *)&answer + n;
	/*
	 * find first satisfactory answer
	 */
	hp = (HEADER *) &answer;
	ancount = ntohs(hp->ancount);
	qdcount = ntohs(hp->qdcount);
	if (hp->rcode != NOERROR || ancount == 0) {
		switch (hp->rcode) {
			case NXDOMAIN:
				/* Non-authoritative iff response from cache.
				 * Old BINDs used to return non-auth NXDOMAINs
				 * due to a bug; if that is the case by you,
				 * change to return EX_TEMPFAIL iff hp->aa == 0.
				 */
				(void) sprintf(remotemsg, "no such domain");
				return EX_NOHOST;
			case SERVFAIL:
				(void) sprintf(remotemsg, "server failure");
				return EX_TEMPFAIL;
#ifdef OLDJEEVES
			/*
			 * Jeeves (TOPS-20 server) still does not
			 * support MX records.  For the time being,
			 * we must accept FORMERRs as the same as
			 * NOERROR.
			 */
			case FORMERR:
#endif
			case NOERROR:
				mx[0].host = NULL;
				return EX_OK;
#ifndef OLDJEEVES
			case FORMERR:
#endif
			case NOTIMP:
			case REFUSED:
				(void) sprintf(remotemsg, "unsupported query");
				return EX_NOPERM;
		}
		(void) sprintf(remotemsg, "unknown error, MX info unavailable");
		return EX_UNAVAILABLE;
	}
	nmx = 0;
	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) */
	realname[0] = '\0';
	maxpref = -1;
	while (--ancount >= 0 && cp < eom && nmx < maxmx-1) {
		n = dn_expand((msgdata *)&answer, eom, cp, buf, sizeof buf);
		if (n < 0)
			break;
		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);
		if (type == T_CNAME) {
			cp += dn_expand((msgdata *)&answer, eom, cp,
					realname, sizeof realname);
			continue;
		} else if (type != T_MX)  {
			cp += n;
			continue;
		}
		mx[nmx].pref = _getshort(cp);
		cp += sizeof(u_short);		/* MX preference value */
		n = dn_expand((msgdata *)&answer, eom, cp, buf, sizeof buf);
		if (n < 0)
			break;
		cp += n;
		mx[nmx].host = (msgdata *)emalloc((u_int)(strlen(buf)+1));
		if (mx[nmx].host == NULL) {
			(void) fprintf(stderr, "Out of virtual memory!\n");
			exit(EX_OSERR);
		}
		(void) strcpy(mx[nmx].host, buf);
		if (cistrcmp(buf, myhostname) == 0)
			maxpref = mx[nmx].pref;
		++nmx;
	}
	if (nmx == 0 && realname[0] != '\0' && cistrcmp(host,(char*)realname)) {
		/* do it recursively for the real name */
		return getmxrr((char *)realname, mx, maxmx, depth+1);
	} else if (nmx == 0) {
		/* "give it benefit of doubt" */
		mx[0].host = NULL;
		return EX_OK;
	}
	/* discard MX RRs with a value >= that of localdomain */
	if (maxpref >= 0) {
		for (n = i = 0; n < nmx; ++n) {
			if (mx[n].pref >= maxpref) {
				(void) free(mx[n].host);
				mx[n].host = NULL;
				++i;
			}
		}
		if (i == nmx) {	/* we are the best MX, do it another way */
			mx[0].host = NULL;
			return EX_OK;
		}
	}
#ifdef	RFC974
	/* discard MX's that do not support SMTP service */
	for (n = 0; n < nmx; ++n) {
		if (mx[n].host == NULL)
			continue;
		(void) strcpy(buf, mx[n].host);
		if (!getrrtype(buf, sizeof buf, T_WKS)) {
			(void) free(mx[n].host);
			mx[n].host = NULL;
		}
	}
#endif	/* RFC974 */
	/* determine how many are left */
	for (i = 0, n = 0; i < nmx; ++i) {
		if (mx[i].host == NULL)
			continue;
		if (n < i)
			mx[n] = mx[i];
		++n; /* found one! */
	}
	if (n == 0) {/* MX's exist, but their WKS's show no TCP smtp service */
		(void) sprintf(remotemsg, "MX host does not support SMTP");
		return EX_UNAVAILABLE;
	}
	nmx = n;
	/* sort the records */
	for (i = 0; i < nmx; i++) {
		for (j = i + 1; j < nmx; j++) {
			if (mx[i].pref > mx[j].pref) {
				mxtemp = mx[i];
				mx[i] = mx[j];
				mx[j] = mxtemp;
			}
		}
	}
	mx[nmx].host = NULL;
	return EX_OK;
}

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

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

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

	HEADER *hp;
	msgdata *eom, *cp;
	querybuf buf, answer;
	int n, ancount, qdcount, ok;
	u_short type;
	msgdata nbuf[BUFSIZ];
	int first;
	extern int res_mkquery(), res_send(), dn_expand(), dn_skipname();

	n = res_mkquery(QUERY, host, C_IN, rrtype, (char *)NULL, 0, NULL,
		(char *)&buf, sizeof(buf));
	if (n < 0) {
		(void) fprintf(stderr, "res_mkquery failed\n");
		h_errno = NO_RECOVERY;
		return -2;
	}
	n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer));
	if (n < 0) {
		h_errno = 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 0;
	}
	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, nbuf,
		    sizeof(nbuf))) < 0)
			break;
		if (first) {
			(void)strncpy(host, 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 < sizeof (u_long) + 1) {
				cp = nextcp;	/* ? */
				continue;
			}
			ok = 0;
			cp += sizeof(u_long);		/* skip IP address */
			if (*cp++ == IPPROTO_TCP) {	/* check protocol */
				if (cp + (IPPORT_SMTP/8) < nextcp
				    && (*(cp+(IPPORT_SMTP/8))
					 & (0x80>>IPPORT_SMTP%8)))
					return 1;
			}
			cp = nextcp;
			continue;
		} else {
			if ((n = dn_expand((msgdata *)&answer, eom, cp, nbuf,
			    sizeof(nbuf))) < 0)
				break;
			(void)strncpy(host, nbuf, hbsize);
			host[hbsize - 1] = '\0';
		}
		return ok;
	}
	return 0;
}

/*
 * This is the callback function for ctlopen.  It should return 0 to reject
 * an address, and 1 to accept it.  This routine will only be used if we've
 * been asked to check MX RR's for all hosts for applicability. Therefore we
 * check whether the addr_host has an MX RR pointing at the host that we have
 * an SMTP connection open with.  Return 1 if it is so.
 */

int
rightmx(spec_host, addr_host)
	char	*spec_host, *addr_host;
{
	struct mxdata mxh[MAXFORWARDERS];
	int	i;
	extern int cistrcmp();

	if (cistrcmp(spec_host, addr_host) == 0)
		return 1;
	if (remotehost[0] == '\0')
		return 0;

	mxh[0].host = NULL;
	switch (getmxrr(addr_host, mxh, sizeof mxh/sizeof mxh[0], 0)) {
	case EX_OK:
		if (mxh[0].host == NULL)
			return cistrcmp(addr_host, remotehost) == 0;
		break;
	default:
		return 0;
	}
	for (i = 0; mxh[i].host != NULL; ++i) {
		if (cistrcmp(mxh[i].host, remotehost) == 0) {
			while (mxh[i].host != NULL)
				free(mxh[i++].host);
			return 1;
		}
		(void) free(mxh[i].host);
	}
	return 0;
}
#endif	/* BIND */
