xref: /onnv-gate/usr/src/cmd/sendmail/aux/mail.local.c (revision 4509:f5161bc5bbf2)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Copyright (c) 1990, 1993, 1994
40Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
50Sstevel@tonic-gate  *
60Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
70Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level
80Sstevel@tonic-gate  * of the sendmail distribution.
90Sstevel@tonic-gate  */
100Sstevel@tonic-gate 
110Sstevel@tonic-gate /*
12*4509Sjbeck  * Copyright 1994-2007 Sun Microsystems, Inc.  All rights reserved.
130Sstevel@tonic-gate  * Use is subject to license terms.
140Sstevel@tonic-gate  */
150Sstevel@tonic-gate 
160Sstevel@tonic-gate #ifndef lint
170Sstevel@tonic-gate static char copyright[] =
180Sstevel@tonic-gate "@(#) Copyright (c) 1990, 1993, 1994\n\
190Sstevel@tonic-gate 	The Regents of the University of California.  All rights reserved.\n";
200Sstevel@tonic-gate #endif /* not lint */
210Sstevel@tonic-gate 
220Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
230Sstevel@tonic-gate 
240Sstevel@tonic-gate #ifndef lint
250Sstevel@tonic-gate static char sccsid[] = "@(#)mail.local.c	8.83 (Berkeley) 12/17/98";
260Sstevel@tonic-gate static char sccsi2[] = "%W% (Sun) %G%";
270Sstevel@tonic-gate #endif /* not lint */
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/param.h>
300Sstevel@tonic-gate #include <sys/stat.h>
310Sstevel@tonic-gate #include <sys/socket.h>
320Sstevel@tonic-gate #include <sys/file.h>
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #include <netinet/in.h>
350Sstevel@tonic-gate 
360Sstevel@tonic-gate #include <errno.h>
370Sstevel@tonic-gate #include <fcntl.h>
380Sstevel@tonic-gate #include <netdb.h>
390Sstevel@tonic-gate #include <pwd.h>
400Sstevel@tonic-gate #include <stdio.h>
410Sstevel@tonic-gate #include <stdlib.h>
420Sstevel@tonic-gate #include <signal.h>
430Sstevel@tonic-gate #include <ctype.h>
440Sstevel@tonic-gate #include <string.h>
450Sstevel@tonic-gate #include <sysexits.h>
460Sstevel@tonic-gate #include <time.h>
470Sstevel@tonic-gate #include <unistd.h>
480Sstevel@tonic-gate #include <maillock.h>
490Sstevel@tonic-gate #include <grp.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate #ifdef __STDC__
520Sstevel@tonic-gate #include <stdarg.h>
530Sstevel@tonic-gate #else
540Sstevel@tonic-gate #include <varargs.h>
550Sstevel@tonic-gate #endif
560Sstevel@tonic-gate 
570Sstevel@tonic-gate #include <syslog.h>
580Sstevel@tonic-gate 
590Sstevel@tonic-gate #include <sysexits.h>
600Sstevel@tonic-gate #include <ctype.h>
610Sstevel@tonic-gate 
620Sstevel@tonic-gate #include <sm/conf.h>
630Sstevel@tonic-gate #include <sendmail/pathnames.h>
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /*
660Sstevel@tonic-gate **  If you don't have flock, you could try using lockf instead.
670Sstevel@tonic-gate */
680Sstevel@tonic-gate 
690Sstevel@tonic-gate #ifdef LDA_USE_LOCKF
700Sstevel@tonic-gate # define flock(a, b)	lockf(a, b, 0)
710Sstevel@tonic-gate # ifdef LOCK_EX
720Sstevel@tonic-gate #  undef LOCK_EX
730Sstevel@tonic-gate # endif /* LOCK_EX */
740Sstevel@tonic-gate # define LOCK_EX        F_LOCK
750Sstevel@tonic-gate #endif /* LDA_USE_LOCKF */
760Sstevel@tonic-gate 
770Sstevel@tonic-gate #ifndef LOCK_EX
780Sstevel@tonic-gate # include <sys/file.h>
790Sstevel@tonic-gate #endif /* ! LOCK_EX */
800Sstevel@tonic-gate 
810Sstevel@tonic-gate #ifndef MAILER_DAEMON
820Sstevel@tonic-gate # define MAILER_DAEMON	"MAILER-DAEMON"
830Sstevel@tonic-gate #endif
840Sstevel@tonic-gate 
850Sstevel@tonic-gate typedef int bool;
860Sstevel@tonic-gate 
870Sstevel@tonic-gate #define	FALSE	0
880Sstevel@tonic-gate #define	TRUE	1
890Sstevel@tonic-gate 
900Sstevel@tonic-gate bool	EightBitMime = TRUE;		/* advertise 8BITMIME in LMTP */
910Sstevel@tonic-gate static int eval = EX_OK;			/* sysexits.h error value. */
920Sstevel@tonic-gate static int lmtpmode = 0;
930Sstevel@tonic-gate bool	bouncequota = FALSE;		/* permanent error when over quota */
940Sstevel@tonic-gate 
950Sstevel@tonic-gate #define	_PATH_MAILDIR	"/var/mail"
960Sstevel@tonic-gate #define	_PATH_LOCTMP	"/tmp/local.XXXXXX"
970Sstevel@tonic-gate #define	_PATH_LOCHTMP	"/tmp/lochd.XXXXXX"
980Sstevel@tonic-gate #define	FALSE 0
990Sstevel@tonic-gate #define	TRUE  1
1000Sstevel@tonic-gate #define	MAXLINE 2048
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate static void	deliver(int, int, char *, bool);
1030Sstevel@tonic-gate static void	e_to_sys(int);
1040Sstevel@tonic-gate static void	err(const char *fmt, ...);
1050Sstevel@tonic-gate static void	notifybiff(char *);
1060Sstevel@tonic-gate static void	store(char *, int);
1070Sstevel@tonic-gate static void	usage(void);
1080Sstevel@tonic-gate static void	vwarn();
1090Sstevel@tonic-gate static void	warn(const char *fmt, ...);
1100Sstevel@tonic-gate static void	mailerr(const char *, const char *, ...);
1110Sstevel@tonic-gate static void	sigterm_handler();
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate static char	unix_from_line[MAXLINE];
1140Sstevel@tonic-gate static int	ulen;
1150Sstevel@tonic-gate static int	content_length;
1160Sstevel@tonic-gate static int	bfd, hfd; /* temp file */
1170Sstevel@tonic-gate static uid_t	src_uid, targ_uid, saved_uid;
1180Sstevel@tonic-gate static int	sigterm_caught;
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate int
main(argc,argv)1210Sstevel@tonic-gate main(argc, argv)
1220Sstevel@tonic-gate 	int argc;
1230Sstevel@tonic-gate 	char *argv[];
1240Sstevel@tonic-gate {
1250Sstevel@tonic-gate 	struct passwd *pw;
1260Sstevel@tonic-gate 	int ch;
1270Sstevel@tonic-gate 	uid_t uid;
1280Sstevel@tonic-gate 	char *from;
1290Sstevel@tonic-gate 	struct  group *grpptr;
1300Sstevel@tonic-gate 	void dolmtp();
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate 	openlog("mail.local", 0, LOG_MAIL);
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate 	from = NULL;
1350Sstevel@tonic-gate 	pw = NULL;
1360Sstevel@tonic-gate 	sigterm_caught = FALSE;
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 	(void) sigset(SIGTERM, sigterm_handler);
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 	while ((ch = getopt(argc, argv, "7bdf:r:l")) != EOF)
1410Sstevel@tonic-gate 		switch (ch) {
1420Sstevel@tonic-gate 		case '7':		/* Do not advertise 8BITMIME */
1430Sstevel@tonic-gate 			EightBitMime = FALSE;
1440Sstevel@tonic-gate 			break;
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 		case 'b':		/* bounce mail when over quota. */
1470Sstevel@tonic-gate 			bouncequota = TRUE;
1480Sstevel@tonic-gate 			break;
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 		case 'd':		/* Backward compatible. */
1510Sstevel@tonic-gate 			break;
1520Sstevel@tonic-gate 		case 'f':
1530Sstevel@tonic-gate 		case 'r':		/* Backward compatible. */
1540Sstevel@tonic-gate 			if (from != NULL) {
1550Sstevel@tonic-gate 				warn("multiple -f options");
1560Sstevel@tonic-gate 				usage();
1570Sstevel@tonic-gate 			}
1580Sstevel@tonic-gate 			from = optarg;
1590Sstevel@tonic-gate 			break;
1600Sstevel@tonic-gate 		case 'l':
1610Sstevel@tonic-gate 			lmtpmode++;
1620Sstevel@tonic-gate 			break;
1630Sstevel@tonic-gate 		case '?':
1640Sstevel@tonic-gate 		default:
1650Sstevel@tonic-gate 			usage();
1660Sstevel@tonic-gate 		}
1670Sstevel@tonic-gate 	argc -= optind;
1680Sstevel@tonic-gate 	argv += optind;
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	notifybiff(NULL); /* initialize biff structures */
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 	/*
1730Sstevel@tonic-gate 	 * We expect sendmail will invoke us with saved id 0
1740Sstevel@tonic-gate 	 * We then do setgid and setuid defore delivery
1750Sstevel@tonic-gate 	 * setgid to mail group
1760Sstevel@tonic-gate 	 */
1770Sstevel@tonic-gate 	if ((grpptr = getgrnam("mail")) != NULL)
1780Sstevel@tonic-gate 		(void) setgid(grpptr->gr_gid);
1790Sstevel@tonic-gate 	saved_uid = geteuid();
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 	if (lmtpmode) {
1820Sstevel@tonic-gate 		if (saved_uid != 0) {
1830Sstevel@tonic-gate 			warn("only super-user can use -l option");
1840Sstevel@tonic-gate 			exit(EX_CANTCREAT);
1850Sstevel@tonic-gate 		}
1860Sstevel@tonic-gate 		dolmtp(bouncequota);
1870Sstevel@tonic-gate 	}
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	if (!*argv)
1900Sstevel@tonic-gate 		usage();
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	/*
1930Sstevel@tonic-gate 	 * If from not specified, use the name from getlogin() if the
1940Sstevel@tonic-gate 	 * uid matches, otherwise, use the name from the password file
1950Sstevel@tonic-gate 	 * corresponding to the uid.
1960Sstevel@tonic-gate 	 */
1970Sstevel@tonic-gate 	uid = getuid();
1980Sstevel@tonic-gate 	if (!from && (!(from = getlogin()) ||
1990Sstevel@tonic-gate 	    !(pw = getpwnam(from)) || pw->pw_uid != uid))
2000Sstevel@tonic-gate 		from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
2010Sstevel@tonic-gate 	src_uid = pw ? pw->pw_uid : uid;
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	/*
2040Sstevel@tonic-gate 	 * There is no way to distinguish the error status of one delivery
2050Sstevel@tonic-gate 	 * from the rest of the deliveries.  So, if we failed hard on one
2060Sstevel@tonic-gate 	 * or more deliveries, but had no failures on any of the others, we
2070Sstevel@tonic-gate 	 * return a hard failure.  If we failed temporarily on one or more
2080Sstevel@tonic-gate 	 * deliveries, we return a temporary failure regardless of the other
2090Sstevel@tonic-gate 	 * failures.  This results in the delivery being reattempted later
2100Sstevel@tonic-gate 	 * at the expense of repeated failures and multiple deliveries.
2110Sstevel@tonic-gate 	 */
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	for (store(from, 0); *argv; ++argv)
2140Sstevel@tonic-gate 		deliver(hfd, bfd, *argv, bouncequota);
2150Sstevel@tonic-gate 	return (eval);
2160Sstevel@tonic-gate }
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate void
sigterm_handler()2190Sstevel@tonic-gate sigterm_handler()
2200Sstevel@tonic-gate {
2210Sstevel@tonic-gate 	sigterm_caught = TRUE;
2220Sstevel@tonic-gate 	(void) sigignore(SIGTERM);
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate char *
parseaddr(s)2260Sstevel@tonic-gate parseaddr(s)
2270Sstevel@tonic-gate 	char *s;
2280Sstevel@tonic-gate {
2290Sstevel@tonic-gate 	char *p;
2300Sstevel@tonic-gate 	int len;
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 	if (*s++ != '<')
2330Sstevel@tonic-gate 		return NULL;
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	p = s;
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	/* at-domain-list */
2380Sstevel@tonic-gate 	while (*p == '@') {
2390Sstevel@tonic-gate 		p++;
2400Sstevel@tonic-gate 		if (*p == '[') {
2410Sstevel@tonic-gate 			p++;
2420Sstevel@tonic-gate 			while (isascii(*p) &&
2430Sstevel@tonic-gate 			       (isalnum(*p) || *p == '.' ||
2440Sstevel@tonic-gate 				*p == '-' || *p == ':'))
2450Sstevel@tonic-gate 				p++;
2460Sstevel@tonic-gate 			if (*p++ != ']')
2470Sstevel@tonic-gate 				return NULL;
2480Sstevel@tonic-gate 		} else {
2490Sstevel@tonic-gate 			while ((isascii(*p) && isalnum(*p)) ||
2500Sstevel@tonic-gate 			       strchr(".-_", *p))
2510Sstevel@tonic-gate 				p++;
2520Sstevel@tonic-gate 		}
2530Sstevel@tonic-gate 		if (*p == ',' && p[1] == '@')
2540Sstevel@tonic-gate 			p++;
2550Sstevel@tonic-gate 		else if (*p == ':' && p[1] != '@')
2560Sstevel@tonic-gate 			p++;
2570Sstevel@tonic-gate 		else
2580Sstevel@tonic-gate 			return NULL;
2590Sstevel@tonic-gate 	}
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 	s = p;
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	/* local-part */
2640Sstevel@tonic-gate 	if (*p == '\"') {
2650Sstevel@tonic-gate 		p++;
2660Sstevel@tonic-gate 		while (*p && *p != '\"') {
2670Sstevel@tonic-gate 			if (*p == '\\') {
2680Sstevel@tonic-gate 				if (!*++p)
2690Sstevel@tonic-gate 					return NULL;
2700Sstevel@tonic-gate 			}
2710Sstevel@tonic-gate 			p++;
2720Sstevel@tonic-gate 		}
2730Sstevel@tonic-gate 		if (!*p++)
2740Sstevel@tonic-gate 			return NULL;
2750Sstevel@tonic-gate 	} else {
2760Sstevel@tonic-gate 		while (*p && *p != '@' && *p != '>') {
2770Sstevel@tonic-gate 			if (*p == '\\') {
2780Sstevel@tonic-gate 				if (!*++p)
2790Sstevel@tonic-gate 					return NULL;
2800Sstevel@tonic-gate 			} else {
2810Sstevel@tonic-gate 			if (*p <= ' ' || (*p & 128) ||
2820Sstevel@tonic-gate 			    strchr("<>()[]\\,;:\"", *p))
2830Sstevel@tonic-gate 				return NULL;
2840Sstevel@tonic-gate 			}
2850Sstevel@tonic-gate 			p++;
2860Sstevel@tonic-gate 		}
2870Sstevel@tonic-gate 	}
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	/* @domain */
2900Sstevel@tonic-gate 	if (*p == '@') {
2910Sstevel@tonic-gate 		p++;
2920Sstevel@tonic-gate 		if (*p == '[') {
2930Sstevel@tonic-gate 			p++;
2940Sstevel@tonic-gate 			while (isascii(*p) &&
2950Sstevel@tonic-gate 			       (isalnum(*p) || *p == '.' ||
2960Sstevel@tonic-gate 				*p == '-' || *p == ':'))
2970Sstevel@tonic-gate 				p++;
2980Sstevel@tonic-gate 			if (*p++ != ']')
2990Sstevel@tonic-gate 				return NULL;
3000Sstevel@tonic-gate 		} else {
3010Sstevel@tonic-gate 			while ((isascii(*p) && isalnum(*p)) ||
3020Sstevel@tonic-gate 			       strchr(".-_", *p))
3030Sstevel@tonic-gate 				p++;
3040Sstevel@tonic-gate 		}
3050Sstevel@tonic-gate 	}
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 	if (*p++ != '>')
3080Sstevel@tonic-gate 		return NULL;
3090Sstevel@tonic-gate 	if (*p && *p != ' ')
3100Sstevel@tonic-gate 		return NULL;
3110Sstevel@tonic-gate 	len = p - s - 1;
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	if (*s == '\0' || len <= 0)
3140Sstevel@tonic-gate 	{
3150Sstevel@tonic-gate 		s = MAILER_DAEMON;
3160Sstevel@tonic-gate 		len = strlen(s);
3170Sstevel@tonic-gate 	}
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate 	p = malloc(len + 1);
3200Sstevel@tonic-gate 	if (p == NULL) {
3210Sstevel@tonic-gate 		printf("421 4.3.0 memory exhausted\r\n");
3220Sstevel@tonic-gate 		exit(EX_TEMPFAIL);
3230Sstevel@tonic-gate 	}
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	strncpy(p, s, len);
3260Sstevel@tonic-gate 	p[len] = '\0';
3270Sstevel@tonic-gate 	return p;
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate char *
process_recipient(addr)3310Sstevel@tonic-gate process_recipient(addr)
3320Sstevel@tonic-gate 	char *addr;
3330Sstevel@tonic-gate {
3340Sstevel@tonic-gate 	if (getpwnam(addr) == NULL) {
3350Sstevel@tonic-gate 		return "550 5.1.1 user unknown";
3360Sstevel@tonic-gate 	}
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	return NULL;
3390Sstevel@tonic-gate }
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate #define RCPT_GROW	30
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate void
dolmtp(bouncequota)3440Sstevel@tonic-gate dolmtp(bouncequota)
3450Sstevel@tonic-gate 	bool bouncequota;
3460Sstevel@tonic-gate {
3470Sstevel@tonic-gate 	char *return_path = NULL;
3480Sstevel@tonic-gate 	char **rcpt_addr = NULL;
3490Sstevel@tonic-gate 	int rcpt_num = 0;
3500Sstevel@tonic-gate 	int rcpt_alloc = 0;
3510Sstevel@tonic-gate 	bool gotlhlo = FALSE;
3520Sstevel@tonic-gate 	char myhostname[MAXHOSTNAMELEN];
3530Sstevel@tonic-gate 	char buf[4096];
3540Sstevel@tonic-gate 	char *err;
3550Sstevel@tonic-gate 	char *p;
3560Sstevel@tonic-gate 	int i;
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	gethostname(myhostname, sizeof myhostname - 1);
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 	printf("220 %s LMTP ready\r\n", myhostname);
3610Sstevel@tonic-gate 	for (;;) {
3620Sstevel@tonic-gate 		if (sigterm_caught) {
3630Sstevel@tonic-gate 			for (; rcpt_num > 0; rcpt_num--)
3640Sstevel@tonic-gate 				printf("451 4.3.0 shutting down\r\n");
3650Sstevel@tonic-gate 			exit(EX_OK);
3660Sstevel@tonic-gate 		}
3670Sstevel@tonic-gate 		fflush(stdout);
3680Sstevel@tonic-gate 		if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
3690Sstevel@tonic-gate 			exit(EX_OK);
3700Sstevel@tonic-gate 		}
3710Sstevel@tonic-gate 		p = buf + strlen(buf) - 1;
3720Sstevel@tonic-gate 		if (p >= buf && *p == '\n')
3730Sstevel@tonic-gate 			*p-- = '\0';
3740Sstevel@tonic-gate 		if (p >= buf && *p == '\r')
3750Sstevel@tonic-gate 			*p-- = '\0';
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 		switch (buf[0]) {
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 		case 'd':
3800Sstevel@tonic-gate 		case 'D':
3810Sstevel@tonic-gate 			if (strcasecmp(buf, "data") == 0) {
3820Sstevel@tonic-gate 				if (rcpt_num == 0) {
3830Sstevel@tonic-gate 					printf("503 5.5.1 No recipients\r\n");
3840Sstevel@tonic-gate 					continue;
3850Sstevel@tonic-gate 				}
3860Sstevel@tonic-gate 				store(return_path, rcpt_num);
3870Sstevel@tonic-gate 				if (bfd == -1 || hfd == -1)
3880Sstevel@tonic-gate 					continue;
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 				for (i = 0; i < rcpt_num; i++) {
3910Sstevel@tonic-gate 					p = strchr(rcpt_addr[i], '+');
3920Sstevel@tonic-gate 					if (p != NULL)
3930Sstevel@tonic-gate 						*p++ = '\0';
3940Sstevel@tonic-gate 					deliver(hfd, bfd, rcpt_addr[i],
3950Sstevel@tonic-gate 						bouncequota);
3960Sstevel@tonic-gate 				}
3970Sstevel@tonic-gate 				close(bfd);
3980Sstevel@tonic-gate 				close(hfd);
3990Sstevel@tonic-gate 				goto rset;
4000Sstevel@tonic-gate 			}
4010Sstevel@tonic-gate 			goto syntaxerr;
4020Sstevel@tonic-gate 			/* NOTREACHED */
4030Sstevel@tonic-gate 			break;
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 		case 'l':
4060Sstevel@tonic-gate 		case 'L':
4070Sstevel@tonic-gate 			if (strncasecmp(buf, "lhlo ", 5) == 0)
4080Sstevel@tonic-gate 			{
4090Sstevel@tonic-gate 				/* check for duplicate per RFC 1651 4.2 */
4100Sstevel@tonic-gate 				if (gotlhlo)
4110Sstevel@tonic-gate 				{
4120Sstevel@tonic-gate 					printf("503 %s Duplicate LHLO\r\n",
4130Sstevel@tonic-gate 					       myhostname);
4140Sstevel@tonic-gate 					continue;
4150Sstevel@tonic-gate 				}
4160Sstevel@tonic-gate 				gotlhlo = TRUE;
4170Sstevel@tonic-gate 				printf("250-%s\r\n", myhostname);
4180Sstevel@tonic-gate 				if (EightBitMime)
4190Sstevel@tonic-gate 					printf("250-8BITMIME\r\n");
4200Sstevel@tonic-gate 				printf("250-ENHANCEDSTATUSCODES\r\n");
4210Sstevel@tonic-gate 				printf("250 PIPELINING\r\n");
4220Sstevel@tonic-gate 				continue;
4230Sstevel@tonic-gate 			}
4240Sstevel@tonic-gate 			goto syntaxerr;
4250Sstevel@tonic-gate 			/* NOTREACHED */
4260Sstevel@tonic-gate 			break;
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 		case 'm':
4290Sstevel@tonic-gate 		case 'M':
4300Sstevel@tonic-gate 			if (strncasecmp(buf, "mail ", 5) == 0) {
4310Sstevel@tonic-gate 				if (return_path != NULL) {
4320Sstevel@tonic-gate 					printf("503 5.5.1 Nested MAIL command\r\n");
4330Sstevel@tonic-gate 					continue;
4340Sstevel@tonic-gate 				}
4350Sstevel@tonic-gate 				if (strncasecmp(buf+5, "from:", 5) != 0 ||
4360Sstevel@tonic-gate 				    ((return_path = parseaddr(buf+10)) == NULL)) {
4370Sstevel@tonic-gate 					printf("501 5.5.4 Syntax error in parameters\r\n");
4380Sstevel@tonic-gate 					continue;
4390Sstevel@tonic-gate 				}
4400Sstevel@tonic-gate 				printf("250 2.5.0 ok\r\n");
4410Sstevel@tonic-gate 				continue;
4420Sstevel@tonic-gate 			}
4430Sstevel@tonic-gate 			goto syntaxerr;
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 		case 'n':
4460Sstevel@tonic-gate 		case 'N':
4470Sstevel@tonic-gate 			if (strcasecmp(buf, "noop") == 0) {
4480Sstevel@tonic-gate 				printf("250 2.0.0 ok\r\n");
4490Sstevel@tonic-gate 				continue;
4500Sstevel@tonic-gate 			}
4510Sstevel@tonic-gate 			goto syntaxerr;
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 		case 'q':
4540Sstevel@tonic-gate 		case 'Q':
4550Sstevel@tonic-gate 			if (strcasecmp(buf, "quit") == 0) {
4560Sstevel@tonic-gate 				printf("221 2.0.0 bye\r\n");
4570Sstevel@tonic-gate 				exit(EX_OK);
4580Sstevel@tonic-gate 			}
4590Sstevel@tonic-gate 			goto syntaxerr;
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 		case 'r':
4620Sstevel@tonic-gate 		case 'R':
4630Sstevel@tonic-gate 			if (strncasecmp(buf, "rcpt ", 5) == 0) {
4640Sstevel@tonic-gate 				if (return_path == NULL) {
4650Sstevel@tonic-gate 					printf("503 5.5.1 Need MAIL command\r\n");
4660Sstevel@tonic-gate 					continue;
4670Sstevel@tonic-gate 				}
4680Sstevel@tonic-gate 				if (rcpt_num >= rcpt_alloc) {
4690Sstevel@tonic-gate 					rcpt_alloc += RCPT_GROW;
4700Sstevel@tonic-gate 					rcpt_addr = (char **)
4710Sstevel@tonic-gate 						realloc((char *)rcpt_addr,
4720Sstevel@tonic-gate 							rcpt_alloc * sizeof(char **));
4730Sstevel@tonic-gate 					if (rcpt_addr == NULL) {
4740Sstevel@tonic-gate 						printf("421 4.3.0 memory exhausted\r\n");
4750Sstevel@tonic-gate 						exit(EX_TEMPFAIL);
4760Sstevel@tonic-gate 					}
4770Sstevel@tonic-gate 				}
4780Sstevel@tonic-gate 				if (strncasecmp(buf+5, "to:", 3) != 0 ||
4790Sstevel@tonic-gate 				    ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) {
4800Sstevel@tonic-gate 					printf("501 5.5.4 Syntax error in parameters\r\n");
4810Sstevel@tonic-gate 					continue;
4820Sstevel@tonic-gate 				}
4830Sstevel@tonic-gate 				if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) {
4840Sstevel@tonic-gate 					printf("%s\r\n", err);
4850Sstevel@tonic-gate 					continue;
4860Sstevel@tonic-gate 				}
4870Sstevel@tonic-gate 				rcpt_num++;
4880Sstevel@tonic-gate 				printf("250 2.1.5 ok\r\n");
4890Sstevel@tonic-gate 				continue;
4900Sstevel@tonic-gate 			}
4910Sstevel@tonic-gate 			else if (strcasecmp(buf, "rset") == 0) {
4920Sstevel@tonic-gate 				printf("250 2.0.0 ok\r\n");
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate   rset:
4950Sstevel@tonic-gate 				while (rcpt_num > 0) {
4960Sstevel@tonic-gate 					free(rcpt_addr[--rcpt_num]);
4970Sstevel@tonic-gate 				}
4980Sstevel@tonic-gate 				if (return_path != NULL)
4990Sstevel@tonic-gate 					free(return_path);
5000Sstevel@tonic-gate 				return_path = NULL;
5010Sstevel@tonic-gate 				continue;
5020Sstevel@tonic-gate 			}
5030Sstevel@tonic-gate 			goto syntaxerr;
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 		case 'v':
5060Sstevel@tonic-gate 		case 'V':
5070Sstevel@tonic-gate 			if (strncasecmp(buf, "vrfy ", 5) == 0) {
5080Sstevel@tonic-gate 				printf("252 2.3.3 try RCPT to attempt delivery\r\n");
5090Sstevel@tonic-gate 				continue;
5100Sstevel@tonic-gate 			}
5110Sstevel@tonic-gate 			goto syntaxerr;
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 		default:
5140Sstevel@tonic-gate   syntaxerr:
5150Sstevel@tonic-gate 			printf("500 5.5.2 Syntax error\r\n");
5160Sstevel@tonic-gate 			continue;
5170Sstevel@tonic-gate 		}
5180Sstevel@tonic-gate 	}
5190Sstevel@tonic-gate }
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate static void
store(from,lmtprcpts)5220Sstevel@tonic-gate store(from, lmtprcpts)
5230Sstevel@tonic-gate 	char *from;
5240Sstevel@tonic-gate 	int lmtprcpts;
5250Sstevel@tonic-gate {
5260Sstevel@tonic-gate 	FILE *fp = NULL;
5270Sstevel@tonic-gate 	time_t tval;
5280Sstevel@tonic-gate 	bool fullline = TRUE;	/* current line is terminated */
5290Sstevel@tonic-gate 	bool prevfl;		/* previous line was terminated */
5300Sstevel@tonic-gate 	char line[MAXLINE];
5310Sstevel@tonic-gate 	FILE *bfp, *hfp;
5320Sstevel@tonic-gate 	char *btn, *htn;
5330Sstevel@tonic-gate 	int in_header_section;
534*4509Sjbeck 	int newfd;
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 	bfd = -1;
5370Sstevel@tonic-gate 	hfd = -1;
5380Sstevel@tonic-gate 	btn = strdup(_PATH_LOCTMP);
5390Sstevel@tonic-gate 	if ((bfd = mkstemp(btn)) == -1 || (bfp = fdopen(bfd, "w+")) == NULL) {
5400Sstevel@tonic-gate 		if (bfd != -1)
5410Sstevel@tonic-gate 			(void) close(bfd);
5420Sstevel@tonic-gate 		if (lmtprcpts) {
5430Sstevel@tonic-gate 			printf("451 4.3.0 unable to open temporary file\r\n");
5440Sstevel@tonic-gate 			return;
5450Sstevel@tonic-gate 		} else {
5460Sstevel@tonic-gate 			mailerr("451 4.3.0", "unable to open temporary file");
5470Sstevel@tonic-gate 			exit(eval);
5480Sstevel@tonic-gate 		}
5490Sstevel@tonic-gate 	}
5500Sstevel@tonic-gate 	(void) unlink(btn);
5510Sstevel@tonic-gate 	free(btn);
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 	if (lmtpmode) {
5540Sstevel@tonic-gate 		printf("354 go ahead\r\n");
5550Sstevel@tonic-gate 		fflush(stdout);
5560Sstevel@tonic-gate 	}
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 	htn = strdup(_PATH_LOCHTMP);
5590Sstevel@tonic-gate 	if ((hfd = mkstemp(htn)) == -1 || (hfp = fdopen(hfd, "w+")) == NULL) {
5600Sstevel@tonic-gate 		if (hfd != -1)
5610Sstevel@tonic-gate 			(void) close(hfd);
5620Sstevel@tonic-gate 		e_to_sys(errno);
5630Sstevel@tonic-gate 		err("unable to open temporary file");
5640Sstevel@tonic-gate 	}
5650Sstevel@tonic-gate 	(void) unlink(htn);
5660Sstevel@tonic-gate 	free(htn);
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 	in_header_section = TRUE;
5690Sstevel@tonic-gate 	content_length = 0;
5700Sstevel@tonic-gate 	fp = hfp;
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 	line[0] = '\0';
5730Sstevel@tonic-gate 	while (fgets(line, sizeof(line), stdin) != (char *)NULL)
5740Sstevel@tonic-gate 	{
5750Sstevel@tonic-gate 		size_t line_len = 0;
5760Sstevel@tonic-gate 		int peek;
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 		prevfl = fullline;	/* preserve state of previous line */
5790Sstevel@tonic-gate 		while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
5800Sstevel@tonic-gate 			line_len++;
5810Sstevel@tonic-gate 		line_len++;
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 		/* Check for dot-stuffing */
5840Sstevel@tonic-gate 		if (prevfl && lmtprcpts && line[0] == '.')
5850Sstevel@tonic-gate 		{
5860Sstevel@tonic-gate 			if (line[1] == '\n' ||
5870Sstevel@tonic-gate 			    (line[1] == '\r' && line[2] == '\n'))
5880Sstevel@tonic-gate 				goto lmtpdot;
5890Sstevel@tonic-gate 			memcpy(line, line + 1, line_len);
5900Sstevel@tonic-gate 			line_len--;
5910Sstevel@tonic-gate 		}
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 		/* Check to see if we have the full line from fgets() */
5940Sstevel@tonic-gate 		fullline = FALSE;
5950Sstevel@tonic-gate 		if (line_len > 0)
5960Sstevel@tonic-gate 		{
5970Sstevel@tonic-gate 			if (line[line_len - 1] == '\n')
5980Sstevel@tonic-gate 			{
5990Sstevel@tonic-gate 				if (line_len >= 2 &&
6000Sstevel@tonic-gate 				    line[line_len - 2] == '\r')
6010Sstevel@tonic-gate 				{
6020Sstevel@tonic-gate 					line[line_len - 2] = '\n';
6030Sstevel@tonic-gate 					line[line_len - 1] = '\0';
6040Sstevel@tonic-gate 					line_len--;
6050Sstevel@tonic-gate 				}
6060Sstevel@tonic-gate 				fullline = TRUE;
6070Sstevel@tonic-gate 			}
6080Sstevel@tonic-gate 			else if (line[line_len - 1] == '\r')
6090Sstevel@tonic-gate 			{
6100Sstevel@tonic-gate 				/* Did we just miss the CRLF? */
6110Sstevel@tonic-gate 				peek = fgetc(stdin);
6120Sstevel@tonic-gate 				if (peek == '\n')
6130Sstevel@tonic-gate 				{
6140Sstevel@tonic-gate 					line[line_len - 1] = '\n';
6150Sstevel@tonic-gate 					fullline = TRUE;
6160Sstevel@tonic-gate 				}
6170Sstevel@tonic-gate 				else
6180Sstevel@tonic-gate 					(void) ungetc(peek, stdin);
6190Sstevel@tonic-gate 			}
6200Sstevel@tonic-gate 		}
6210Sstevel@tonic-gate 		else
6220Sstevel@tonic-gate 			fullline = TRUE;
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 		if (prevfl && line[0] == '\n' && in_header_section) {
6250Sstevel@tonic-gate 			in_header_section = FALSE;
6260Sstevel@tonic-gate 			if (fflush(fp) == EOF || ferror(fp)) {
6270Sstevel@tonic-gate 				if (lmtprcpts) {
6280Sstevel@tonic-gate 					while (lmtprcpts--)
6290Sstevel@tonic-gate 						printf("451 4.3.0 temporary file write error\r\n");
6300Sstevel@tonic-gate 					fclose(fp);
6310Sstevel@tonic-gate 					return;
6320Sstevel@tonic-gate 				} else {
6330Sstevel@tonic-gate 					mailerr("451 4.3.0",
6340Sstevel@tonic-gate 						"temporary file write error");
6350Sstevel@tonic-gate 					fclose(fp);
6360Sstevel@tonic-gate 					exit(eval);
6370Sstevel@tonic-gate 				}
6380Sstevel@tonic-gate 			}
6390Sstevel@tonic-gate 			fp = bfp;
6400Sstevel@tonic-gate 			continue;
6410Sstevel@tonic-gate 		}
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 		if (in_header_section) {
6440Sstevel@tonic-gate 			if (strncasecmp("Content-Length:", line, 15) == 0) {
6450Sstevel@tonic-gate 				continue; /* skip this header */
6460Sstevel@tonic-gate 			}
6470Sstevel@tonic-gate 		} else
6480Sstevel@tonic-gate 			content_length += strlen(line);
6490Sstevel@tonic-gate 		(void) fwrite(line, sizeof(char), line_len, fp);
6500Sstevel@tonic-gate 		if (ferror(fp)) {
6510Sstevel@tonic-gate 			if (lmtprcpts) {
6520Sstevel@tonic-gate 				while (lmtprcpts--)
6530Sstevel@tonic-gate 					printf("451 4.3.0 temporary file write error\r\n");
6540Sstevel@tonic-gate 				fclose(fp);
6550Sstevel@tonic-gate 				return;
6560Sstevel@tonic-gate 			} else {
6570Sstevel@tonic-gate 				mailerr("451 4.3.0",
6580Sstevel@tonic-gate 					"temporary file write error");
6590Sstevel@tonic-gate 				fclose(fp);
6600Sstevel@tonic-gate 				exit(eval);
6610Sstevel@tonic-gate 			}
6620Sstevel@tonic-gate 		}
6630Sstevel@tonic-gate 	}
6640Sstevel@tonic-gate 	if (sigterm_caught) {
6650Sstevel@tonic-gate 		if (lmtprcpts)
6660Sstevel@tonic-gate 			while (lmtprcpts--)
6670Sstevel@tonic-gate 				printf("451 4.3.0 shutting down\r\n");
6680Sstevel@tonic-gate 		else
6690Sstevel@tonic-gate 			mailerr("451 4.3.0", "shutting down");
6700Sstevel@tonic-gate 		fclose(fp);
6710Sstevel@tonic-gate 		exit(eval);
6720Sstevel@tonic-gate 	}
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 	if (lmtprcpts) {
6750Sstevel@tonic-gate 		/* Got a premature EOF -- toss message and exit */
6760Sstevel@tonic-gate 		exit(EX_OK);
6770Sstevel@tonic-gate 	}
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 	/* If message not newline terminated, need an extra. */
6800Sstevel@tonic-gate 	if (!strchr(line, '\n')) {
6810Sstevel@tonic-gate 		(void) putc('\n', fp);
6820Sstevel@tonic-gate 		content_length++;
6830Sstevel@tonic-gate 	}
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate   lmtpdot:
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 	/* Output a newline; note, empty messages are allowed. */
6880Sstevel@tonic-gate 	(void) putc('\n', fp);
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	if (fflush(fp) == EOF || ferror(fp)) {
6910Sstevel@tonic-gate 		if (lmtprcpts) {
6920Sstevel@tonic-gate 			while (lmtprcpts--) {
6930Sstevel@tonic-gate 				printf("451 4.3.0 temporary file write error\r\n");
6940Sstevel@tonic-gate 			}
6950Sstevel@tonic-gate 			fclose(fp);
6960Sstevel@tonic-gate 			return;
6970Sstevel@tonic-gate 		} else {
6980Sstevel@tonic-gate 			mailerr("451 4.3.0", "temporary file write error");
6990Sstevel@tonic-gate 			fclose(fp);
7000Sstevel@tonic-gate 			exit(eval);
7010Sstevel@tonic-gate 		}
7020Sstevel@tonic-gate 	}
7030Sstevel@tonic-gate 
704*4509Sjbeck 	if ((newfd = dup(bfd)) >= 0) {
705*4509Sjbeck 		fclose(bfp);
706*4509Sjbeck 		bfd = newfd;
707*4509Sjbeck 	}
708*4509Sjbeck 	if ((newfd = dup(hfd)) >= 0) {
709*4509Sjbeck 		fclose(hfp);
710*4509Sjbeck 		hfd = newfd;
711*4509Sjbeck 	}
7120Sstevel@tonic-gate 	(void) time(&tval);
7130Sstevel@tonic-gate 	(void) snprintf(unix_from_line, sizeof (unix_from_line), "From %s %s",
7140Sstevel@tonic-gate 	    from, ctime(&tval));
7150Sstevel@tonic-gate 	ulen = strlen(unix_from_line);
7160Sstevel@tonic-gate }
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate static void
handle_error(err_num,bouncequota,path)7192897Sjbeck handle_error(err_num, bouncequota, path)
7202897Sjbeck 	int err_num;
7212897Sjbeck 	bool bouncequota;
7222897Sjbeck 	char *path;
7232897Sjbeck {
7242897Sjbeck #ifdef EDQUOT
7252897Sjbeck 	if (err_num == EDQUOT && bouncequota) {
7262897Sjbeck 		mailerr("552 5.2.2", "%s: %s", path, sm_errstring(err_num));
7272897Sjbeck 	} else
7282897Sjbeck #endif /* EDQUOT */
7292897Sjbeck 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(err_num));
7302897Sjbeck }
7312897Sjbeck 
7322897Sjbeck static void
deliver(hfd,bfd,name,bouncequota)7330Sstevel@tonic-gate deliver(hfd, bfd, name, bouncequota)
7340Sstevel@tonic-gate 	int hfd;
7350Sstevel@tonic-gate 	int bfd;
7360Sstevel@tonic-gate 	char *name;
7370Sstevel@tonic-gate 	bool bouncequota;
7380Sstevel@tonic-gate {
7390Sstevel@tonic-gate 	struct stat fsb, sb;
7400Sstevel@tonic-gate 	int mbfd = -1, nr, nw = 0, off;
7410Sstevel@tonic-gate 	char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
7420Sstevel@tonic-gate 	off_t curoff, cursize;
7430Sstevel@tonic-gate 	int len;
7440Sstevel@tonic-gate 	struct passwd *pw = NULL;
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	/*
7470Sstevel@tonic-gate  	* Disallow delivery to unknown names -- special mailboxes
7480Sstevel@tonic-gate  	* can be handled in the sendmail aliases file.
7490Sstevel@tonic-gate  	*/
7500Sstevel@tonic-gate 	if ((pw = getpwnam(name)) == NULL) {
7510Sstevel@tonic-gate 		eval = EX_TEMPFAIL;
7520Sstevel@tonic-gate 		mailerr("451 4.3.0", "cannot lookup name: %s", name);
7530Sstevel@tonic-gate 		return;
7540Sstevel@tonic-gate 	}
7550Sstevel@tonic-gate 	endpwent();
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate 	if (sigterm_caught) {
7580Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
7590Sstevel@tonic-gate 		return;
7600Sstevel@tonic-gate 	}
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 	/* mailbox may be NFS mounted, seteuid to user */
7630Sstevel@tonic-gate 	targ_uid = pw->pw_uid;
7640Sstevel@tonic-gate 	(void) seteuid(targ_uid);
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate 	if ((saved_uid != 0) && (src_uid != targ_uid)) {
7670Sstevel@tonic-gate 		/*
7680Sstevel@tonic-gate 		 * If saved_uid == 0 (root), anything is OK; this is
7690Sstevel@tonic-gate 		 * as it should be.  But to prevent a random user from
7700Sstevel@tonic-gate 		 * calling "mail.local foo" in an attempt to hijack
7710Sstevel@tonic-gate 		 * foo's mail-box, make sure src_uid == targ_uid o/w.
7720Sstevel@tonic-gate 		 */
7730Sstevel@tonic-gate 		warn("%s: wrong owner (is %d, should be %d)",
7740Sstevel@tonic-gate 			name, src_uid, targ_uid);
7750Sstevel@tonic-gate 		eval = EX_CANTCREAT;
7760Sstevel@tonic-gate 		return;
7770Sstevel@tonic-gate 	}
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 	path[0] = '\0';
7800Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s/%s", _PATH_MAILDIR, name);
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate 	/*
7830Sstevel@tonic-gate 	 * If the mailbox is linked or a symlink, fail.  There's an obvious
7840Sstevel@tonic-gate 	 * race here, that the file was replaced with a symbolic link after
7850Sstevel@tonic-gate 	 * the lstat returned, but before the open.  We attempt to detect
7860Sstevel@tonic-gate 	 * this by comparing the original stat information and information
7870Sstevel@tonic-gate 	 * returned by an fstat of the file descriptor returned by the open.
7880Sstevel@tonic-gate 	 *
7890Sstevel@tonic-gate 	 * NB: this is a symptom of a larger problem, that the mail spooling
7900Sstevel@tonic-gate 	 * directory is writeable by the wrong users.  If that directory is
7910Sstevel@tonic-gate 	 * writeable, system security is compromised for other reasons, and
7920Sstevel@tonic-gate 	 * it cannot be fixed here.
7930Sstevel@tonic-gate 	 *
7940Sstevel@tonic-gate 	 * If we created the mailbox, set the owner/group.  If that fails,
7950Sstevel@tonic-gate 	 * just return.  Another process may have already opened it, so we
7960Sstevel@tonic-gate 	 * can't unlink it.  Historically, binmail set the owner/group at
7970Sstevel@tonic-gate 	 * each mail delivery.  We no longer do this, assuming that if the
7980Sstevel@tonic-gate 	 * ownership or permissions were changed there was a reason.
7990Sstevel@tonic-gate 	 *
8000Sstevel@tonic-gate 	 * XXX
8010Sstevel@tonic-gate 	 * open(2) should support flock'ing the file.
8020Sstevel@tonic-gate 	 */
8030Sstevel@tonic-gate tryagain:
8040Sstevel@tonic-gate 	/* should check lock status, but... maillock return no value */
8050Sstevel@tonic-gate 	maillock(name, 10);
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	if (sigterm_caught) {
8080Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
8090Sstevel@tonic-gate 		goto err0;
8100Sstevel@tonic-gate 	}
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	if (lstat(path, &sb)) {
8130Sstevel@tonic-gate 		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY,
8140Sstevel@tonic-gate 				S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
8150Sstevel@tonic-gate 		if (mbfd != -1)
8160Sstevel@tonic-gate 			(void) fchmod(mbfd, 0660);
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 		if (mbfd == -1) {
8200Sstevel@tonic-gate 			if (errno == EEXIST) {
8210Sstevel@tonic-gate 				mailunlock();
8220Sstevel@tonic-gate 				goto tryagain;
8230Sstevel@tonic-gate 			}
8240Sstevel@tonic-gate 		}
8250Sstevel@tonic-gate 	} else if (sb.st_nlink != 1) {
8260Sstevel@tonic-gate 		mailerr("550 5.2.0", "%s: too many links", path);
8270Sstevel@tonic-gate 		goto err0;
8280Sstevel@tonic-gate 	} else if (!S_ISREG(sb.st_mode)) {
8290Sstevel@tonic-gate 		mailerr("550 5.2.0", "%s: irregular file", path);
8300Sstevel@tonic-gate 		goto err0;
8310Sstevel@tonic-gate 	} else {
8320Sstevel@tonic-gate 		mbfd = open(path, O_APPEND|O_WRONLY, 0);
8330Sstevel@tonic-gate 		if (mbfd != -1 &&
8340Sstevel@tonic-gate 		    (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
8350Sstevel@tonic-gate 		    S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
8360Sstevel@tonic-gate 		    sb.st_ino != fsb.st_ino)) {
8370Sstevel@tonic-gate 			eval = EX_TEMPFAIL;
8380Sstevel@tonic-gate 			mailerr("550 5.2.0",
8390Sstevel@tonic-gate 				"%s: fstat: file changed after open", path);
8400Sstevel@tonic-gate 			goto err1;
8410Sstevel@tonic-gate 		}
8420Sstevel@tonic-gate 	}
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate 	if (mbfd == -1) {
8450Sstevel@tonic-gate 		mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
8460Sstevel@tonic-gate 		goto err0;
8470Sstevel@tonic-gate 	}
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	if (sigterm_caught) {
8500Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
8510Sstevel@tonic-gate 		goto err0;
8520Sstevel@tonic-gate 	}
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 	/* Get the starting offset of the new message for biff. */
8550Sstevel@tonic-gate 	curoff = lseek(mbfd, (off_t)0, SEEK_END);
8560Sstevel@tonic-gate 	(void) snprintf(biffmsg, sizeof (biffmsg), "%s@%ld\n", name, curoff);
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate 	/* Copy the message into the file. */
8590Sstevel@tonic-gate 	if (lseek(hfd, (off_t)0, SEEK_SET) == (off_t)-1) {
8600Sstevel@tonic-gate 		mailerr("450 4.2.0", "temporary file: %s", strerror(errno));
8610Sstevel@tonic-gate 		goto err1;
8620Sstevel@tonic-gate 	}
8630Sstevel@tonic-gate 	/* Copy the message into the file. */
8640Sstevel@tonic-gate 	if (lseek(bfd, (off_t)0, SEEK_SET) == (off_t)-1) {
8650Sstevel@tonic-gate 		mailerr("450 4.2.0", "temporary file: %s", strerror(errno));
8660Sstevel@tonic-gate 		goto err1;
8670Sstevel@tonic-gate 	}
8680Sstevel@tonic-gate 	if ((write(mbfd, unix_from_line, ulen)) != ulen) {
8692897Sjbeck 		handle_error(errno, bouncequota, path);
8700Sstevel@tonic-gate 		goto err2;
8710Sstevel@tonic-gate 	}
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate 	if (sigterm_caught) {
8740Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
8750Sstevel@tonic-gate 		goto err2;
8760Sstevel@tonic-gate 	}
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	while ((nr = read(hfd, buf, sizeof (buf))) > 0)
8790Sstevel@tonic-gate 		for (off = 0; off < nr; nr -= nw, off += nw)
8800Sstevel@tonic-gate 			if ((nw = write(mbfd, buf + off, nr)) < 0)
8810Sstevel@tonic-gate 			{
8822897Sjbeck 				handle_error(errno, bouncequota, path);
8830Sstevel@tonic-gate 				goto err2;
8840Sstevel@tonic-gate 			}
8850Sstevel@tonic-gate 	if (nr < 0) {
8862897Sjbeck 		handle_error(errno, bouncequota, path);
8870Sstevel@tonic-gate 		goto err2;
8880Sstevel@tonic-gate 	}
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate 	if (sigterm_caught) {
8910Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
8920Sstevel@tonic-gate 		goto err2;
8930Sstevel@tonic-gate 	}
8940Sstevel@tonic-gate 
8950Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "Content-Length: %d\n\n",
8960Sstevel@tonic-gate 	    content_length);
8970Sstevel@tonic-gate 	len = strlen(buf);
8980Sstevel@tonic-gate 	if (write(mbfd, buf, len) != len) {
8992897Sjbeck 		handle_error(errno, bouncequota, path);
9000Sstevel@tonic-gate 		goto err2;
9010Sstevel@tonic-gate 	}
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	if (sigterm_caught) {
9040Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
9050Sstevel@tonic-gate 		goto err2;
9060Sstevel@tonic-gate 	}
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate 	while ((nr = read(bfd, buf, sizeof (buf))) > 0) {
9090Sstevel@tonic-gate 		for (off = 0; off < nr; nr -= nw, off += nw)
9100Sstevel@tonic-gate 			if ((nw = write(mbfd, buf + off, nr)) < 0) {
9112897Sjbeck 				handle_error(errno, bouncequota, path);
9120Sstevel@tonic-gate 				goto err2;
9130Sstevel@tonic-gate 			}
9140Sstevel@tonic-gate 		if (sigterm_caught) {
9150Sstevel@tonic-gate 			mailerr("451 4.3.0", "shutting down");
9160Sstevel@tonic-gate 			goto err2;
9170Sstevel@tonic-gate 		}
9180Sstevel@tonic-gate 	}
9190Sstevel@tonic-gate 	if (nr < 0) {
9202897Sjbeck 		handle_error(errno, bouncequota, path);
9210Sstevel@tonic-gate 		goto err2;
9220Sstevel@tonic-gate 	}
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate 	/* Flush to disk, don't wait for update. */
9250Sstevel@tonic-gate 	if (fsync(mbfd)) {
9262897Sjbeck 		handle_error(errno, bouncequota, path);
9270Sstevel@tonic-gate err2:		if (mbfd >= 0)
9280Sstevel@tonic-gate 			(void)ftruncate(mbfd, curoff);
9290Sstevel@tonic-gate err1:		(void)close(mbfd);
9300Sstevel@tonic-gate err0:		mailunlock();
9310Sstevel@tonic-gate 		(void)seteuid(saved_uid);
9320Sstevel@tonic-gate 		return;
9330Sstevel@tonic-gate 	}
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	/*
9360Sstevel@tonic-gate 	**  Save the current size so if the close() fails below
9370Sstevel@tonic-gate 	**  we can make sure no other process has changed the mailbox
9380Sstevel@tonic-gate 	**  between the failed close and the re-open()/re-lock().
9390Sstevel@tonic-gate 	**  If something else has changed the size, we shouldn't
9400Sstevel@tonic-gate 	**  try to truncate it as we may do more harm then good
9410Sstevel@tonic-gate 	**  (e.g., truncate a later message delivery).
9420Sstevel@tonic-gate 	*/
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate 	if (fstat(mbfd, &sb) < 0)
9450Sstevel@tonic-gate 		cursize = 0;
9460Sstevel@tonic-gate 	else
9470Sstevel@tonic-gate 		cursize = sb.st_size;
9480Sstevel@tonic-gate 
9490Sstevel@tonic-gate 	/* Close and check -- NFS doesn't write until the close. */
9500Sstevel@tonic-gate 	if (close(mbfd))
9510Sstevel@tonic-gate 	{
9522897Sjbeck 		handle_error(errno, bouncequota, path);
9530Sstevel@tonic-gate 		mbfd = open(path, O_WRONLY, 0);
9540Sstevel@tonic-gate 		if (mbfd < 0 ||
9550Sstevel@tonic-gate 		    cursize == 0
9560Sstevel@tonic-gate 		    || flock(mbfd, LOCK_EX) < 0 ||
9570Sstevel@tonic-gate 		    fstat(mbfd, &sb) < 0 ||
9580Sstevel@tonic-gate 		    sb.st_size != cursize ||
9590Sstevel@tonic-gate 		    sb.st_nlink != 1 ||
9600Sstevel@tonic-gate 		    !S_ISREG(sb.st_mode) ||
9610Sstevel@tonic-gate 		    sb.st_dev != fsb.st_dev ||
9620Sstevel@tonic-gate 		    sb.st_ino != fsb.st_ino ||
9630Sstevel@tonic-gate 		    sb.st_uid != fsb.st_uid)
9640Sstevel@tonic-gate 		{
9650Sstevel@tonic-gate 			/* Don't use a bogus file */
9660Sstevel@tonic-gate 			if (mbfd >= 0)
9670Sstevel@tonic-gate 			{
9680Sstevel@tonic-gate 				(void) close(mbfd);
9690Sstevel@tonic-gate 				mbfd = -1;
9700Sstevel@tonic-gate 			}
9710Sstevel@tonic-gate 		}
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate 		/* Attempt to truncate back to pre-write size */
9740Sstevel@tonic-gate 		goto err2;
9750Sstevel@tonic-gate 	} else
9760Sstevel@tonic-gate 		notifybiff(biffmsg);
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	mailunlock();
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 	(void)seteuid(saved_uid);
9810Sstevel@tonic-gate 
9820Sstevel@tonic-gate 	if (lmtpmode) {
9830Sstevel@tonic-gate 		printf("250 2.1.5 %s OK\r\n", name);
9840Sstevel@tonic-gate 	}
9850Sstevel@tonic-gate }
9860Sstevel@tonic-gate 
9870Sstevel@tonic-gate static void
notifybiff(msg)9880Sstevel@tonic-gate notifybiff(msg)
9890Sstevel@tonic-gate 	char *msg;
9900Sstevel@tonic-gate {
9910Sstevel@tonic-gate 	static struct sockaddr_in addr;
9920Sstevel@tonic-gate 	static int f = -1;
9930Sstevel@tonic-gate 	struct hostent *hp;
9940Sstevel@tonic-gate 	struct servent *sp;
9950Sstevel@tonic-gate 	int len;
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	if (msg == NULL) {
9980Sstevel@tonic-gate 		/* Be silent if biff service not available. */
9990Sstevel@tonic-gate 		if ((sp = getservbyname("biff", "udp")) == NULL)
10000Sstevel@tonic-gate 			return;
10010Sstevel@tonic-gate 		if ((hp = gethostbyname("localhost")) == NULL) {
10020Sstevel@tonic-gate 			warn("localhost: %s", strerror(errno));
10030Sstevel@tonic-gate 			return;
10040Sstevel@tonic-gate 		}
10050Sstevel@tonic-gate 		addr.sin_family = hp->h_addrtype;
10060Sstevel@tonic-gate 		(void) memmove(&addr.sin_addr, hp->h_addr, hp->h_length);
10070Sstevel@tonic-gate 		addr.sin_port = sp->s_port;
10080Sstevel@tonic-gate 		return;
10090Sstevel@tonic-gate 	}
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 	if (addr.sin_family == 0)
10120Sstevel@tonic-gate 		return; /* did not initialize */
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
10150Sstevel@tonic-gate 		warn("socket: %s", strerror(errno));
10160Sstevel@tonic-gate 		return;
10170Sstevel@tonic-gate 	}
10180Sstevel@tonic-gate 	len = strlen(msg) + 1;
10190Sstevel@tonic-gate 	if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof (addr))
10200Sstevel@tonic-gate 	    != len)
10210Sstevel@tonic-gate 		warn("sendto biff: %s", strerror(errno));
10220Sstevel@tonic-gate }
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate static void
usage()10250Sstevel@tonic-gate usage()
10260Sstevel@tonic-gate {
10270Sstevel@tonic-gate 	eval = EX_USAGE;
10280Sstevel@tonic-gate 	err("usage: mail.local [-l] [-f from] user ...");
10290Sstevel@tonic-gate }
10300Sstevel@tonic-gate 
10310Sstevel@tonic-gate static void
10320Sstevel@tonic-gate /*VARARGS2*/
10330Sstevel@tonic-gate #ifdef __STDC__
mailerr(const char * hdr,const char * fmt,...)10340Sstevel@tonic-gate mailerr(const char *hdr, const char *fmt, ...)
10350Sstevel@tonic-gate #else
10360Sstevel@tonic-gate mailerr(hdr, fmt, va_alist)
10370Sstevel@tonic-gate 	const char *hdr;
10380Sstevel@tonic-gate 	const char *fmt;
10390Sstevel@tonic-gate 	va_dcl
10400Sstevel@tonic-gate #endif
10410Sstevel@tonic-gate {
10420Sstevel@tonic-gate 	va_list ap;
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate #ifdef __STDC__
10450Sstevel@tonic-gate 	va_start(ap, fmt);
10460Sstevel@tonic-gate #else
10470Sstevel@tonic-gate 	va_start(ap);
10480Sstevel@tonic-gate #endif
10490Sstevel@tonic-gate 	if (lmtpmode)
10500Sstevel@tonic-gate 	{
10510Sstevel@tonic-gate 		if (hdr != NULL)
10520Sstevel@tonic-gate 			printf("%s ", hdr);
10530Sstevel@tonic-gate 		vprintf(fmt, ap);
10540Sstevel@tonic-gate 		printf("\r\n");
10550Sstevel@tonic-gate 	}
10560Sstevel@tonic-gate 	else
10570Sstevel@tonic-gate 	{
10580Sstevel@tonic-gate 		e_to_sys(errno);
10590Sstevel@tonic-gate 		vwarn(fmt, ap);
10600Sstevel@tonic-gate 	}
10610Sstevel@tonic-gate }
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate static void
10640Sstevel@tonic-gate /*VARARGS1*/
10650Sstevel@tonic-gate #ifdef __STDC__
err(const char * fmt,...)10660Sstevel@tonic-gate err(const char *fmt, ...)
10670Sstevel@tonic-gate #else
10680Sstevel@tonic-gate err(fmt, va_alist)
10690Sstevel@tonic-gate 	const char *fmt;
10700Sstevel@tonic-gate 	va_dcl
10710Sstevel@tonic-gate #endif
10720Sstevel@tonic-gate {
10730Sstevel@tonic-gate 	va_list ap;
10740Sstevel@tonic-gate 
10750Sstevel@tonic-gate #ifdef __STDC__
10760Sstevel@tonic-gate 	va_start(ap, fmt);
10770Sstevel@tonic-gate #else
10780Sstevel@tonic-gate 	va_start(ap);
10790Sstevel@tonic-gate #endif
10800Sstevel@tonic-gate 	vwarn(fmt, ap);
10810Sstevel@tonic-gate 	va_end(ap);
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate 	exit(eval);
10840Sstevel@tonic-gate }
10850Sstevel@tonic-gate 
10860Sstevel@tonic-gate static void
10870Sstevel@tonic-gate /*VARARGS1*/
10880Sstevel@tonic-gate #ifdef __STDC__
warn(const char * fmt,...)10890Sstevel@tonic-gate warn(const char *fmt, ...)
10900Sstevel@tonic-gate #else
10910Sstevel@tonic-gate warn(fmt, va_alist)
10920Sstevel@tonic-gate 	const char *fmt;
10930Sstevel@tonic-gate 	va_dcl
10940Sstevel@tonic-gate #endif
10950Sstevel@tonic-gate {
10960Sstevel@tonic-gate 	va_list ap;
10970Sstevel@tonic-gate 
10980Sstevel@tonic-gate #ifdef __STDC__
10990Sstevel@tonic-gate 	va_start(ap, fmt);
11000Sstevel@tonic-gate #else
11010Sstevel@tonic-gate 	va_start(ap);
11020Sstevel@tonic-gate #endif
11030Sstevel@tonic-gate 	vwarn(fmt, ap);
11040Sstevel@tonic-gate 	va_end(ap);
11050Sstevel@tonic-gate }
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate static void
vwarn(fmt,ap)11080Sstevel@tonic-gate vwarn(fmt, ap)
11090Sstevel@tonic-gate 	const char *fmt;
11100Sstevel@tonic-gate 	va_list ap;
11110Sstevel@tonic-gate {
11120Sstevel@tonic-gate 	/*
11130Sstevel@tonic-gate 	 * Log the message to stderr.
11140Sstevel@tonic-gate 	 *
11150Sstevel@tonic-gate 	 * Don't use LOG_PERROR as an openlog() flag to do this,
11160Sstevel@tonic-gate 	 * it's not portable enough.
11170Sstevel@tonic-gate 	 */
11180Sstevel@tonic-gate 	if (eval != EX_USAGE)
11190Sstevel@tonic-gate 		(void) fprintf(stderr, "mail.local: ");
11200Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, ap);
11210Sstevel@tonic-gate 	(void) fprintf(stderr, "\n");
11220Sstevel@tonic-gate 
11230Sstevel@tonic-gate 	/* Log the message to syslog. */
11240Sstevel@tonic-gate 	vsyslog(LOG_ERR, fmt, ap);
11250Sstevel@tonic-gate }
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate /*
11280Sstevel@tonic-gate  * e_to_sys --
11290Sstevel@tonic-gate  *	Guess which errno's are temporary.  Gag me.
11300Sstevel@tonic-gate  */
11310Sstevel@tonic-gate static void
e_to_sys(num)11320Sstevel@tonic-gate e_to_sys(num)
11330Sstevel@tonic-gate 	int num;
11340Sstevel@tonic-gate {
11350Sstevel@tonic-gate 	/* Temporary failures override hard errors. */
11360Sstevel@tonic-gate 	if (eval == EX_TEMPFAIL)
11370Sstevel@tonic-gate 		return;
11380Sstevel@tonic-gate 
11390Sstevel@tonic-gate 	switch (num)		/* Hopefully temporary errors. */
11400Sstevel@tonic-gate 	{
11410Sstevel@tonic-gate #ifdef EDQUOT
11420Sstevel@tonic-gate 	case EDQUOT:		/* Disc quota exceeded */
11430Sstevel@tonic-gate 		if (bouncequota)
11440Sstevel@tonic-gate 		{
11450Sstevel@tonic-gate 			eval = EX_UNAVAILABLE;
11460Sstevel@tonic-gate 			break;
11470Sstevel@tonic-gate 		}
11480Sstevel@tonic-gate 		/* FALLTHROUGH */
11490Sstevel@tonic-gate #endif /* EDQUOT */
11500Sstevel@tonic-gate #ifdef EAGAIN
11510Sstevel@tonic-gate 	case EAGAIN:		/* Resource temporarily unavailable */
11520Sstevel@tonic-gate #endif
11530Sstevel@tonic-gate #ifdef EBUSY
11540Sstevel@tonic-gate 	case EBUSY:		/* Device busy */
11550Sstevel@tonic-gate #endif
11560Sstevel@tonic-gate #ifdef EPROCLIM
11570Sstevel@tonic-gate 	case EPROCLIM:		/* Too many processes */
11580Sstevel@tonic-gate #endif
11590Sstevel@tonic-gate #ifdef EUSERS
11600Sstevel@tonic-gate 	case EUSERS:		/* Too many users */
11610Sstevel@tonic-gate #endif
11620Sstevel@tonic-gate #ifdef ECONNABORTED
11630Sstevel@tonic-gate 	case ECONNABORTED:	/* Software caused connection abort */
11640Sstevel@tonic-gate #endif
11650Sstevel@tonic-gate #ifdef ECONNREFUSED
11660Sstevel@tonic-gate 	case ECONNREFUSED:	/* Connection refused */
11670Sstevel@tonic-gate #endif
11680Sstevel@tonic-gate #ifdef ECONNRESET
11690Sstevel@tonic-gate 	case ECONNRESET:	/* Connection reset by peer */
11700Sstevel@tonic-gate #endif
11710Sstevel@tonic-gate #ifdef EDEADLK
11720Sstevel@tonic-gate 	case EDEADLK:		/* Resource deadlock avoided */
11730Sstevel@tonic-gate #endif
11740Sstevel@tonic-gate #ifdef EFBIG
11750Sstevel@tonic-gate 	case EFBIG:		/* File too large */
11760Sstevel@tonic-gate #endif
11770Sstevel@tonic-gate #ifdef EHOSTDOWN
11780Sstevel@tonic-gate 	case EHOSTDOWN:		/* Host is down */
11790Sstevel@tonic-gate #endif
11800Sstevel@tonic-gate #ifdef EHOSTUNREACH
11810Sstevel@tonic-gate 	case EHOSTUNREACH:	/* No route to host */
11820Sstevel@tonic-gate #endif
11830Sstevel@tonic-gate #ifdef EMFILE
11840Sstevel@tonic-gate 	case EMFILE:		/* Too many open files */
11850Sstevel@tonic-gate #endif
11860Sstevel@tonic-gate #ifdef ENETDOWN
11870Sstevel@tonic-gate 	case ENETDOWN:		/* Network is down */
11880Sstevel@tonic-gate #endif
11890Sstevel@tonic-gate #ifdef ENETRESET
11900Sstevel@tonic-gate 	case ENETRESET:		/* Network dropped connection on reset */
11910Sstevel@tonic-gate #endif
11920Sstevel@tonic-gate #ifdef ENETUNREACH
11930Sstevel@tonic-gate 	case ENETUNREACH:	/* Network is unreachable */
11940Sstevel@tonic-gate #endif
11950Sstevel@tonic-gate #ifdef ENFILE
11960Sstevel@tonic-gate 	case ENFILE:		/* Too many open files in system */
11970Sstevel@tonic-gate #endif
11980Sstevel@tonic-gate #ifdef ENOBUFS
11990Sstevel@tonic-gate 	case ENOBUFS:		/* No buffer space available */
12000Sstevel@tonic-gate #endif
12010Sstevel@tonic-gate #ifdef ENOMEM
12020Sstevel@tonic-gate 	case ENOMEM:		/* Cannot allocate memory */
12030Sstevel@tonic-gate #endif
12040Sstevel@tonic-gate #ifdef ENOSPC
12050Sstevel@tonic-gate 	case ENOSPC:		/* No space left on device */
12060Sstevel@tonic-gate #endif
12070Sstevel@tonic-gate #ifdef EROFS
12080Sstevel@tonic-gate 	case EROFS:		/* Read-only file system */
12090Sstevel@tonic-gate #endif
12100Sstevel@tonic-gate #ifdef ESTALE
12110Sstevel@tonic-gate 	case ESTALE:		/* Stale NFS file handle */
12120Sstevel@tonic-gate #endif
12130Sstevel@tonic-gate #ifdef ETIMEDOUT
12140Sstevel@tonic-gate 	case ETIMEDOUT:		/* Connection timed out */
12150Sstevel@tonic-gate #endif
12160Sstevel@tonic-gate #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
12170Sstevel@tonic-gate 	case EWOULDBLOCK:	/* Operation would block. */
12180Sstevel@tonic-gate #endif
12190Sstevel@tonic-gate 		eval = EX_TEMPFAIL;
12200Sstevel@tonic-gate 		break;
12210Sstevel@tonic-gate 	default:
12220Sstevel@tonic-gate 		eval = EX_UNAVAILABLE;
12230Sstevel@tonic-gate 		break;
12240Sstevel@tonic-gate 	}
12250Sstevel@tonic-gate }
1226