/*
 * Copyright (c) 1993, Trusted Information Systems, Incorporated
 * All rights reserved.
 *
 * Redistribution and use are governed by the terms detailed in the
 * license document ("LICENSE") included with the toolkit.
 */

/*
 *	Author: Marcus J. Ranum, Trusted Information Systems, Inc.
 */
static	char	RcsId[] = "$Header: /usr/home/rick/fwtk2.0/fwtk/smap/RCS/smap.c,v 1.9 1996/09/06 04:15:06 rick Exp $";

/*
 *      Modified: 09 October 1996, Bruce R. Ellis
 *      prevent remote machines from sending mail to other
 *      remote machines
 *
 *      Modified: 10 October 1996, Craig I. Hagan (hagan@cih.com)
 *      changed how local host determination works, moved
 *      configuration from code to netperm-table option.
 *
 *      Provides Sender Host/RCPT verification to insure that origin 
 *      or destination is within local domain. If Sender Host is non-
 *	local, connection is dropped if and when a non-local RCPT is
 *	entered. 
 *
 *	Unimplemented commands now return appropriate message instead
 *	on an incorrect answer - particularly EXPN and VRFY no longer
 *	return "User unknown". 
 *
 *	HELO uses the resolver info rather than using the string sent 
 *	with the HELO command.
 */



#include	<sys/types.h>
#include	<sys/file.h>
#include	<sys/stat.h>
#include	<sys/wait.h>
#include	<stdio.h>
#include	<time.h>
#include	<ctype.h>
#include	<syslog.h>
#include	<netdb.h>
#include	<sys/socket.h>
#include	<sys/signal.h>
#include	<netinet/in.h>
#include	<errno.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>

#include	"firewall.h"

#define		TRUE	1
#define		FALSE	0

/*
	smap - sendmail wrapper.

	the object of this program is to allow us to present an SMTP service
	for people to talk to, which is unprivileged, and runs in a chrooted
	directory. a secondary requirement is that the code be as simple as
	possible, to permit manual review. this code, therefore, contains
	no comments other than this one - comments being an indication that
	code is too complex to be trusted.

	files created in spool directory are locked and given mode 644. when
	smap is done gathering them, they are unlocked and given mode 755.
	smapd checks the mode and locking status to ensure that it's not
	dealing with a partial file. unlocked files with mode 644 are by
	definition partial.

	mjr. 1993
*/

extern	char	*strtok();

extern	char	*optarg;

struct	towho	{
	char		*who;
	struct	towho	*nxt;
};


static	struct	towho	*recip = (struct towho *)0;
static	char		*ruser = (char *)0;
static	char		*rhost = (char *)0;
static	char		rladdr[512];
static	char		riaddr[512];
static	char		*rootdir = (char *)0;
static	int		runuid = -1;
static	int		rungid = -1;
static	char		*tempfile = (char *)0;
static	int		maxbytes = -1;
static	int		maxrecip = -1;
static	int		curbytes = 0;
static	int		currecip = 0;
static	int		timeout = PROXY_TIMEOUT;
static	int		livetim = 0;
static	int		msgsize = 0;

static	FILE	*smapopen();
static	char	*crlfgets();
static  void	add_file_list();
static	void	smap_exit();
static	void	waitwaitwait();

struct file_list {
	struct file_list *next;
	char *tmpfile;
} *filelist = (struct file_list *) 0;

/* bre 96/10/09 */
 
static  int     get_mail_ok 	  = FALSE;	/* Boolean */
static  int     from_host_local   = FALSE;	/* Boolean */

static  int     scrub_spam        = FALSE;      /* Boolean */
   
/* bre 96/10/09 */

/* cih 96/10/25 */

static int      check_from_address = FALSE; /* Boolean */
static int      require_full_email = FALSE; /* Boolean */

#define UNACCEPTABLE_ERRNO 410
#define LYING_ERRNO 550
#define NOTLOCAL_ERRNO 550


static	void
ringring()
{
	if(livetim == 0) {
		syslog(LLEV,"Network timeout signal after %d seconds",timeout);
		if(tempfile != (char *)0)
			unlink(tempfile);
		smap_exit(1);
	}
	livetim = 0;
	alarm(timeout);
}


main(ac,av)
int	ac;
char	*av[];
{
	Cfg		*cf;
	Cfg		*cfp;
	char		buf[BUFSIZ * 4];
	char		myhostname[512];
	char		*p;
	int		x;
	char		opt;
	FILE		*vout = (FILE *)0;
	int		gotdata = 0;
	struct hostent	*hp;
	int             goodaddr;
	char            *domain;
	char            *site;
	char            *tmpstr;
	char            answer[512];

	if((cfp = cfg_read("smap")) == (Cfg *)-1)
		exit(1);


#ifndef	LOG_DAEMON
	openlog("smap",LOG_PID);
#else
	openlog("smap",LOG_PID|LOG_NDELAY,LFAC);
#endif

	if (ac > 1 && !strcmp(av[1], "-daemon")) {
		int sock, sockl;
		struct sockaddr_in sa;
		int pid;
	
		sa.sin_family = AF_INET;
		bzero((char *)&sa.sin_addr, sizeof(sa.sin_addr));
		sa.sin_port = htons(25);
		sock = socket(AF_INET, SOCK_STREAM, 0);
		if (sock < 0) {
			syslog(LLEV, "fwtksyserr: Failed to create socket: %m");
			exit(1);
		}
		if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))) {
			syslog(LLEV,"fwtksyserr: Failed to bind port 25: %m");
			exit(1);
		}
		if (listen(sock, 5) < 0) {
			syslog(LLEV,"fwtksyserr: Failed to listen: %m");
			exit(1);
		}
		while (1) {
			int cstat;
			signal (SIGCHLD, waitwaitwait);
			sockl = accept (sock, (struct sockaddr *)0, (int *)0);
			if (sockl < 0) {
				if (errno = EINTR)
					continue;
				syslog(LLEV,"fwtksyserr: Accept failed: %m");
				exit(1);
			}

			pid = fork();
			if (pid < 0) {
				syslog(LLEV,"fwtksyserr: Fork failed: %m");
				exit(1);
			}
			if (pid == 0)
				break;	/* We are the child */
			close(sockl);
		}
		close(0);
		close(1);
		close(2);
		dup(sockl);
		dup(sockl);
		dup(sockl);
		close(sockl);
		close(sock);
	}

	if(peername(0,rladdr,riaddr,sizeof(riaddr))) 
	{
		syslog(LLEV,"cannot get remote host %s",riaddr);
		if((cf=cfg_get("unknown-host",cfp)) !=(Cfg *)0)
		  {
		    if(cf->argc !=1) {
      			syslog(LLEV,"fwtkcfgerr: unknown-host must have one parameter, line %d",cf->ln);
			exit(1);
		    }
		    else if(atoi(cf->argv[0]) == 1) {
		      strcpy(rladdr,"unknown");
		    }
		    else {
		      exit(1);
		    }
		  }
		else {
		  exit(1);
		}
	} 
	else
		syslog(LLEV,"connect host=%s/%s",rladdr,riaddr);



	if((cf = cfg_get("check-from-address",cfp)) != (Cfg *)0) 
	{
		if(cf->argc != 1) 
		{
			syslog(LLEV,"fwtkcfgerr: check-from-address must have one parameter, line %d",cf->ln);
			exit(1);
		}
		check_from_address=(atoi(cf->argv[0]) == 1);
	}

	if((cf = cfg_get("require-full-email",cfp)) != (Cfg *)0) 
	{
		if(cf->argc != 1) 
		{
			syslog(LLEV,"fwtkcfgerr: require-full-email must have one parameter, line %d",cf->ln);
			exit(1);
		}
		require_full_email=(atoi(cf->argv[0]) == 1);
	}


	if((cfp = cfg_read("smap")) == (Cfg *)-1)
		exit(1);

	if((cf = cfg_get("groupid",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: groupid must have one parameter, line %d",cf->ln);
			exit(1);
		}
		if((rungid = mapgid(cf->argv[0])) == -1) {
			syslog(LLEV,"fwtkcfgerr: cannot map %s to gid",cf->argv[0]);
			exit(1);
		}
	}

	if((cf = cfg_get("userid",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: userid must have one parameter, line %d",cf->ln);
			exit(1);
		}
		if((runuid = mapuid(cf->argv[0])) == -1) {
			syslog(LLEV,"fwtkcfgerr: cannot map %s to uid",cf->argv[0]);
			exit(1);
		}
	}


	if((cf = cfg_get("directory",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: chroot must have one parameter, line %d",cf->ln);
			exit(1);
		}
		rootdir = cf->argv[0];
	}


	if((cf = cfg_get("maxbytes",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: maxbytes must have one parameter, line %d",cf->ln);
			exit(1);
		}
		maxbytes = atoi(cf->argv[0]);
	}


	if((cf = cfg_get("maxrecip",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: maxrecip must have one parameter, line %d",cf->ln);
			exit(1);
		}
		maxrecip = atoi(cf->argv[0]);
	}


	if((cf = cfg_get("timeout",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: timeout must have one parameter, line %d",cf->ln);
			exit(1);
		}
		timeout = atoi(cf->argv[0]);
		signal(SIGALRM,ringring);
		alarm(timeout);
	}

	if(rootdir != (char *)0) {
		chdir("/");
		if((chdir(rootdir) || chroot(rootdir))) {
			syslog(LLEV,"fwtksyserr: cannot chroot to %s: %m",rootdir);
			exit(1);
		}
		chdir("/");
	}


	if(rungid != -1 && setgid(rungid)) {
		syslog(LLEV,"fwtksyserr: cannot set gid to %d: %m",rungid);
		exit(1);
	}

	if(runuid != -1 && setuid(runuid)) {
		syslog(LLEV,"fwtksyserr: cannot set uid to %d: %m",runuid);
		exit(1);
	}

	if(gethostname(myhostname,sizeof(myhostname)))
		strcpy(myhostname,"amnesiac");
	else if ((hp = gethostbyname(myhostname)) != 0)
		strcpy(myhostname,hp->h_name);
	printf("220 %s SMTP/smap Ready.\r\n",myhostname);
	fflush(stdout);


#ifdef	SO_KEEPALIVE
	opt = 1;
	(void)setsockopt(fileno(stdin),SOL_SOCKET,SO_KEEPALIVE,&opt,sizeof(opt));
#endif

/* bre 96/10/09 */
 
/* Check to see if mail is originating from within the local domain by 
 * examining the IP name that is returned by the resolver when 
 * smap is first started by inetd.
 */
 	rhost = malloc(strlen(rladdr) + 1);
 	strcpy(rhost,rladdr);

	if((cf=cfg_get("scrub-spam",cfp)) !=(Cfg *)0) {
	  scrub_spam = TRUE;
	}

        if(scrub_spam && check_spamhost(cfp)) {
	  printf("451 your site has been depermitted due to heavy spammage\n");
	  syslog(LLEV,"rejected spam from %s [%s] from host %s/%s",ruser,domain,rladdr,riaddr);
	  exit(1);
	}
 	from_host_local = check_hostname(cfp);
 
/* bre 96/10/09 */

	while(1) {
		if(crlfgets(buf,sizeof(buf),stdin) == NULL) {
			syslog(LLEV,"peer dropped connection: %m");
			break;
		}

		if((p = strtok(buf," \r\t\n")) == (char *)0) {
			printf("500 Command unrecognized\r\n");
			fflush(stdout);
			continue;
		}


		if(!strcasecmp(p,"MAIL")) {
			char	*q;


			if((q = strtok((char *)0,"\r\n")) == (char *)0) {
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}

			if(strncasecmp(q,"From:",5)) {
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}
			/* block escape in names */
			if(strchr(q, '`')) {
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}

			q += 5;
			while(isspace(*q))
				q++;
			if(ruser != (char *)0)
				free(ruser);
			ruser = malloc(strlen(q) + 1);
			if(ruser == (char *)0) {
				printf("410 out of memory\r\n");
				fflush(stdout);
				syslog(LLEV,"fwtksyserr: out of memory: %m");
				unlink(tempfile);
				smap_exit(1);
			}
			strcpy(ruser,q);
			if(check_from_address || require_full_email) {
			  site=1+strchr(ruser,'@');
			  if(require_full_email && (site == 1) && (!from_host_local)) {
			    printf("%d Liar\r\n",LYING_ERRNO);
			    syslog(LLEV,"bad from address, %s, from %s/%s",ruser,
				   rladdr,riaddr);
			    exit(1);
			  }
			  
			  if(check_from_address) {

			    if((domain=malloc((x=strlen(site))+1)) == (char *)0)
			    {
			      unlink(tempfile);
			      syslog(LLEV,"fwtksyserr: of memory: %m");
			      exit(1);
			    }
			  strcpy(domain,site);
			  tmpstr=strchr(domain,'<');
			  if(tmpstr)
			    domain=1+tmpstr;
			  tmpstr=strchr(domain,'>');
			  if(tmpstr)
			    tmpstr[0]='\0';

			    if(scrub_spam && check_spamdomain(domain, cfp)) {
			      printf("451 your site has been depermitted due to heavy spammage\n");
			      syslog(LLEV,"rejected spam from %s [%s] from host %s/%s",ruser,domain,rladdr,riaddr);
			      exit(1);
			    }			      
			      
			  goodaddr=0;
			  while(domain && *domain && !goodaddr)
			  {
			    goodaddr=(from_host_local || !check_domain(domain,cfp)) && ((res_query(domain,C_IN,T_A,(u_char *)&answer,512) >0) ||
			      (res_query(domain,C_IN,T_MX,(u_char *)&answer,512)>0));
			    if(!goodaddr) {
				domain=strchr(domain,'.');
				if(domain && *domain) 
					domain++;
		            }
			  }
			  
			    if(!goodaddr) {
			      printf("%d Unacceptable address/domain: %s [%s]/\r\n",UNACCEPTABLE_ERRNO,ruser,domain);
			      syslog(LLEV,"rejected mail purporting to be from %s [%s] from host %s/%s",ruser,domain,rladdr,riaddr);
			      exit(1);
			    }
			  }
			}

			printf("250 %s... Sender Ok\r\n",ruser);
			fflush(stdout);
			continue;
		}


		if(!strcasecmp(p,"RCPT")) {
			struct	towho	*nr;
			char	*q;


			if(ruser == (char *)0) {
				printf("503 need MAIL From: first.\r\n");
				fflush(stdout);
				continue;
			}

			if((q = strtok((char *)0,"\r\n")) == (char *)0) {
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}

			if(strncasecmp(q,"To:",3)) {
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}

			q += 3;
			while(isspace(*q))
				q++;
#ifdef	SPECIALDOMAIN
			if(!checkvalid(q,cfp)) {
				syslog(LLEV,"securityalert: rejecting recip %s",q);
				fflush(stdout);
				continue;
			}
#endif

			if(maxrecip > 0 && currecip++ > maxrecip) {
				printf("550 too many recipients\r\n");
				fflush(stdout);
				syslog(LLEV,"exiting - too many recipients");
				unlink(tempfile);
				smap_exit(1);
			}

			nr = (struct towho *)malloc(sizeof(struct towho));
			if(nr == (struct towho *)0) {
				printf("410 out of memory\r\n");
				fflush(stdout);
				syslog(LLEV,"fwtksyserr: out of memory: %m");
				unlink(tempfile);
				smap_exit(1);
			}
			nr->who = malloc(strlen(q) + 1);
			if(nr->who == (char *)0) {
				printf("410 out of memory\r\n");
				fflush(stdout);
				syslog(LLEV,"fwtksyserr: out of memory: %m");
				unlink(tempfile);
				smap_exit(1);
			}
			strcpy(nr->who,q);
			nr->nxt = recip;
			recip = nr;
 
/* bre 96/10/09 */
                        if (from_host_local) 
			{
				get_mail_ok = TRUE;
			}
			else
			{
				get_mail_ok = checkvalid(q,cfp);
			}

                        if (! get_mail_ok) 
			{
				printf("%d Mail not local. Closing connection.\r\n",NOTLOCAL_ERRNO);
				fflush(stdout);
				break;
			}
			printf("250 %s OK\r\n",q);
			fflush(stdout);
			continue;
		}
		

/* bre 96/10/09 */

		if(!strcasecmp(p,"DATA")) {
			struct	towho	*tp;
			long		now;

			curbytes = 0;
			currecip = 0;
			if(recip == (struct towho *)0) {
				printf("503 need RCPT first.\r\n");
				fflush(stdout);
				continue;
			}
			if((vout = smapopen()) == (FILE *)0) {
				syslog(LLEV,"fwtksyserr: cannot open temp file %m");
				smap_exit(1);
			}

			printf("354 Enter mail, end with \".\" on a line by itself\r\n");
			fflush(stdout);

			fprintf(vout,"FROM %s\n",ruser);
			for(tp = recip; tp != (struct towho *)0; tp = tp->nxt)
				fprintf(vout,"RCPT %s\n",tp->who);
			fprintf(vout,"BODY\n");
			time(&now);
			fprintf(vout,"Received: from %s(%s) by %s via smap (%s)\n\tid %s; %s\n",
				rladdr,riaddr,myhostname,
				FWTK_VERSION_MINOR,tempfile,
				arpadate((char *)0));

			msgsize = 0;
			switch(crunchbody(stdin,vout))  {
				case 1:
					printf("550 you could say goodbye\r\n");
					fflush(stdout);
					break;
				case 2:
					printf("550 Too much data\r\n");
					fflush(stdout);
					syslog(LLEV,"exiting too much data");
					unlink(tempfile);
					smap_exit(1);
				case 3:
					printf("450 System problem\r\n");
					fflush(stdout);
					continue;
				default:
					gotdata++;
					/* .... fall through... */
			}

			for(tp = recip; tp != (struct towho *)0; tp = tp->nxt)
				syslog(LLEV,"host=%s/%s bytes=%d from=%s to=%s",rladdr,riaddr,msgsize,ruser,tp->who);
			if(recip != (struct towho *)0) {
				currecip = 0;
				/* should really free them */
				recip = (struct towho *)0;
			}
			if(!gotdata) {
				unlink(tempfile);
				syslog(LLEV,"SMTP QUIT with no message %s/%s",rladdr,riaddr);
				smap_exit(1);
			}
			add_file_list(tempfile);
			chmod(tempfile,0700);
			lockun_fd(fileno(vout));
			printf("250 Mail accepted\r\n");
			fflush(stdout);
			syslog(LLEV,"exiting host=%s/%s bytes=%d",rladdr,riaddr,msgsize);
			vout = (FILE *)0;
			tempfile = (char *)0;
			continue;
		}


		if(!strcasecmp(p,"VRFY") || !strcasecmp(p,"EXPN")) {
			char	*q;

			if((q = strtok((char *)0," \t\r\n")) == (char *)0)
				printf("550 User Unknown\r\n");
			else
				printf("250 <%s>\r\n",q);
			syslog(LLEV,"%s %s (%s/%s)",p,q,rladdr,riaddr);
			fflush(stdout);
			continue;
		}


		if(!strcasecmp(p,"HELP")) {
			printf("214-Commands\r\n");
			printf("214-HELO    MAIL    RCPT    DATA    RSET\r\n");
			printf("214 NOOP    QUIT    HELP    VRFY    EXPN\r\n");
			fflush(stdout);
			continue;
		}


		if(!strcasecmp(p,"NOOP")) {
			printf("220 OK\r\n");
			fflush(stdout);
			continue;
		}


		if(!strcasecmp(p,"QUIT")) {
			printf("221 Closing connection\r\n");
			fflush(stdout);
			break;
		}


		if(!strcasecmp(p,"RSET")) {
			recip = (struct towho *)0;
			ruser = (char *)0;
			if(vout != (FILE *)0) {
				unlink(tempfile);
				fclose(vout);
				if((vout = smapopen()) == (FILE *)0) {
					syslog(LLEV,"fwtksyserr: cannot open temp file %m");
					smap_exit(1);
				}
			}
			printf("250 Reset state\r\n");
			fflush(stdout);
			continue;
		}


		if(!strcasecmp(p,"HELO")) {
			char	*q;

			if((q = strtok((char *)0,"\r\n")) == (char *)0) {
				printf("250 Charmed, Im sure.\r\n");
				rhost = "??";
			} else {
				printf("250 (%s) pleased to meet you.\r\n",q);
				rhost = malloc(strlen(q) + 1);
				strcpy(rhost,q);
			}
			fflush(stdout);
			continue;
		}


		if(!strcasecmp(p,"VERB")) {
			printf("200 Verbose mode\r\n");
			fflush(stdout);
			continue;
		}


		if(!strcasecmp(p,"ONEX")) {
			printf("200 Only one transaction\r\n");
			fflush(stdout);
			continue;
		}


		if(!strcasecmp(p,"DEBUG")) {
			printf("200 Debug set -NOT!\r\n");
			fflush(stdout);
			continue;
		}

		printf("500 Command unrecognized\r\n");
		fflush(stdout);
	}
	smap_exit(0);
}



static	FILE	*
smapopen()
{
	FILE	*ret;
	int	fd;
	char	xuf[512];

	strcpy(xuf,"xmaXXXXXX");
	/* system V locking will have problems if mkstemp does
	not open for random access as well as write */
	if((fd = mkstemp(xuf)) < 0)
		return((FILE *)0);
	chmod(xuf,0600);
	if((ret = fdopen(fd,"w")) == (FILE *)0)
		return((FILE *)0);
	lock_fd(fd);
	if((tempfile = malloc(strlen(xuf) + 1)) == (char *)0) {
		syslog(LLEV,"fwtksyserr: out of memory: %m");
		unlink(xuf);
		smap_exit(1);
	}
	strcpy(tempfile,xuf);
	return(ret);
}



crunchbody(in,out)
FILE	*in;
FILE	*out;
{
	char	buf[BUFSIZ];
	int	x;

	while(1) {
		if(crlfgets(buf,sizeof(buf),in) == NULL)
			return(1);
		if(buf[0] == '.' && buf[1] == '\0')
			break;
		if(buf[0] == '.' && buf[1] == '.')
			x = fprintf(out,"%s\n",&buf[1]);
		else
			x = fprintf(out,"%s\n",buf);
		if(x == -1 || ferror(out)) {
			fclose(out);
			syslog(LLEV,"fwtksyserr: cannot fprintf: %m");
			return(3);
		}
		if(maxbytes > 0) {
			if((curbytes += strlen(buf)) > maxbytes)
				return(2);
		}

	}
	if(fflush(out) || ferror(out)) {
		syslog(LLEV,"fwtksyserr: cannot fprintf: %m");
		return(3);
	}
	msgsize = filesize(fileno(out));
	if (fclose(out)) {
		syslog(LLEV,"fwtksyserr: cannot fclose: %m");
		return (1);
	}
	return(0);
}



static	char	*
crlfgets(buf,siz,fp)
char	*buf;
int	siz;
FILE	*fp;
{
	int	x;
	char	*s = buf;

	if(ferror(fp) || feof(fp))
		return((char *)0);

	while(--siz) {
		if((x = fgetc(fp)) == EOF) {
			if(s == buf)
				return((char *)0);
			break;
		}
		if(x == '\n') {
			/* back up over carriage returns */
			if(s > buf && *(s - 1) == '\r')
				s--;
			break;
		}
		*s++ = x;
	}
	*s = '\0';
	livetim = 1;
	return(buf);
}


filesize(fd)
int	fd;
{
	struct	stat	sbuf;

	if(fstat(fd,&sbuf))
		return(-1);
	return(sbuf.st_size);
}


usage()
{
	printf("usage:\n");
	return(1);
}


static void
add_file_list (tmpf)
char *tmpf;
{
	struct file_list *fl = (struct file_list *) malloc(sizeof(struct file_list));
	char *p = (char *)malloc(strlen(tmpf) + 1);

	if (p == (char *)0 || fl == (struct file_list *)0)
		smap_exit(1);

	strcpy(p, tmpf);
	fl->next = filelist;
	fl->tmpfile = p;
	filelist = fl;

	/* use link to make it visible to smapd keeping original as well */
	p[0] = 's';
	if (link(tmpf, p) && (errno != EEXIST)) {
		syslog(LLEV, "link from %s to %s failed, %m", tmpf, p);
		p[0] = 'x';
		smap_exit(1);
	}
	p[0] = 'x';
}

static void
smap_exit (code)
int code;
{
	struct file_list *fl;

	while (filelist != (struct file_list *)0) {
		fl = filelist->next;
		unlink(filelist->tmpfile);

		free(filelist->tmpfile);
		filelist = fl;
	}
	exit(code);
}

static void
waitwaitwait()
{
	int cstat;

	while (wait3(&cstat, WNOHANG, (struct rusage *)0) > 0)
		;
}

/* kludge for whitehouse.gov anti-spoofing bogosity */

#ifndef SYSV
extern	char	*index();
extern	char	*rindex();
#endif
extern	char	*strpbrk();

char	*bad = "550 Recipient must be in form of: user, user@eop.gov, or user@whitehouse.gov\r\n";

checkvalid(r,cfp)
char	*r;
Cfg     *cfp;
{
	char	*atp;
	char	*jxp;
	char	*chop;
	char	*chsavp;
	int	x;

	if((chop = malloc((x = strlen(r)) + 1)) == (char *)0) {
		unlink(tempfile);
		syslog(LLEV,"fwtksyserr: of memory: %m");
		exit(1);
	}
	chsavp = chop;
	strcpy(chop,r);

	if(r[0] == '<') {
		if(chop[x - 1] == '>')
			chop[x - 1] = '\0';
		chop++;
	}

	if((atp = rindex(chop,'@')) != (char *)0) {
		atp++;

		/* check if it ends in @freemark.com or @corp.freemark.com*/
		if(!check_domain(atp,cfp))
			goto bomb;

		/* now make sure there are no other routing chars */
		atp--;
		*atp = '\0';
		if((jxp = strpbrk(chop,"%@:[]!")) != (char *)0) {
			goto bomb;
		}
	}
	if((jxp = strpbrk(chop,"%@:[]!")) != (char *)0)
		goto bomb;

	free(chsavp);
	return(1);
bomb:
	printf(bad);
	free(chsavp);
	return(0);
}



/* bre 96/10/09 */
 

/*
 * check to see if the connector is in the list of spam hosts
 *
 */	

check_spamhost(Cfg *cfp)
{
	Cfg	*cf;
	int	x;

	cf = cfg_get("spam",cfp);
	while(cf != (Cfg *)0) {
		if(cf->argc < 1)
			goto skip;

		for(x = 0; x < cf->argc; x++) {
			if(cf->argv[x][0] == '-')
				break;
			if(hostmatch(cf->argv[x],riaddr)) {
				if(cf->flags & PERM_DENY) {
					return(FALSE);
				}
				return(TRUE);
			}
		}

skip:
		cf = cfg_get("spam",(Cfg*)0);
	}
	return(FALSE);
}

/*
 * check to see if the domain is in the list of spam domains
 * (same config as spam-hosts)
 *
 */	

check_spamdomain(const char *domain, Cfg *cfp)
{
	Cfg	*cf;
	int	x;

	cf = cfg_get("spam",cfp);
	while(cf != (Cfg *)0) {
		if(cf->argc < 1)
			goto skip;

		for(x = 0; x < cf->argc; x++) {
			if(cf->argv[x][0] == '-')
				break;
			if(hostmatch(cf->argv[x],domain)) {
				if(cf->flags & PERM_DENY) {
					return(FALSE);
				}
				return(TRUE);
			}
		}

skip:
		cf = cfg_get("spam",(Cfg*)0);
	}
	return(FALSE);
}

/*
 * if the ip address/hostname is specified as local
 * in the config file, then it may send to non-local addresses.
 * cih 96/10/10
 *
 */	

check_hostname(Cfg *cfp)
{
	Cfg	*cf;
	int	x;

	cf = cfg_get("hosts",cfp);
	while(cf != (Cfg *)0) {
		if(cf->argc < 1)
			goto skip;

		for(x = 0; x < cf->argc; x++) {
			if(cf->argv[x][0] == '-')
				break;
			if(hostmatch(cf->argv[x],riaddr)) {
				if(cf->flags & PERM_DENY) {
					return(FALSE);
				}
				return(TRUE);
			}
		}

skip:
		cf = cfg_get("hosts",(Cfg*)0);
	}
	return(FALSE);
}


/*
 * if the domain name is specified 
 * in the config file, then mail will be accepted to it from the outside
 * cih 96/10/10
 *
 */	

check_domain(char *atp,Cfg *cfp)
{
	Cfg	*cf;
	int	x;

	cf = cfg_get("domains",cfp);
	while(cf != (Cfg *)0) {
		if(cf->argc < 1)
			goto skip;

		for(x = 0; x < cf->argc; x++) {
			if(cf->argv[x][0] == '-')
				break;
			if(hostmatch(cf->argv[x],atp)) {
				if(cf->flags & PERM_DENY) {
					syslog(LLEV,"deny host=%s/%s use of gateway",rladdr,riaddr);
					return(FALSE);
				}
				return(TRUE);
				syslog(LLEV,"permit host=%s/%s use of gateway",rladdr,riaddr);
			}
		}

skip:
		cf = cfg_get("domains",(Cfg*)0);
	}
	return(FALSE);
}




