xref: /onnv-gate/usr/src/cmd/sendmail/aux/mail.local.c (revision 2897:78c74f149c12)
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*2897Sjbeck  * Copyright 1994-2006 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
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
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 *
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 *
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
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
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;
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 	bfd = -1;
5360Sstevel@tonic-gate 	hfd = -1;
5370Sstevel@tonic-gate 	btn = strdup(_PATH_LOCTMP);
5380Sstevel@tonic-gate 	if ((bfd = mkstemp(btn)) == -1 || (bfp = fdopen(bfd, "w+")) == NULL) {
5390Sstevel@tonic-gate 		if (bfd != -1)
5400Sstevel@tonic-gate 			(void) close(bfd);
5410Sstevel@tonic-gate 		if (lmtprcpts) {
5420Sstevel@tonic-gate 			printf("451 4.3.0 unable to open temporary file\r\n");
5430Sstevel@tonic-gate 			return;
5440Sstevel@tonic-gate 		} else {
5450Sstevel@tonic-gate 			mailerr("451 4.3.0", "unable to open temporary file");
5460Sstevel@tonic-gate 			exit(eval);
5470Sstevel@tonic-gate 		}
5480Sstevel@tonic-gate 	}
5490Sstevel@tonic-gate 	(void) unlink(btn);
5500Sstevel@tonic-gate 	free(btn);
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	if (lmtpmode) {
5530Sstevel@tonic-gate 		printf("354 go ahead\r\n");
5540Sstevel@tonic-gate 		fflush(stdout);
5550Sstevel@tonic-gate 	}
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	htn = strdup(_PATH_LOCHTMP);
5580Sstevel@tonic-gate 	if ((hfd = mkstemp(htn)) == -1 || (hfp = fdopen(hfd, "w+")) == NULL) {
5590Sstevel@tonic-gate 		if (hfd != -1)
5600Sstevel@tonic-gate 			(void) close(hfd);
5610Sstevel@tonic-gate 		e_to_sys(errno);
5620Sstevel@tonic-gate 		err("unable to open temporary file");
5630Sstevel@tonic-gate 	}
5640Sstevel@tonic-gate 	(void) unlink(htn);
5650Sstevel@tonic-gate 	free(htn);
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 	in_header_section = TRUE;
5680Sstevel@tonic-gate 	content_length = 0;
5690Sstevel@tonic-gate 	fp = hfp;
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	line[0] = '\0';
5720Sstevel@tonic-gate 	while (fgets(line, sizeof(line), stdin) != (char *)NULL)
5730Sstevel@tonic-gate 	{
5740Sstevel@tonic-gate 		size_t line_len = 0;
5750Sstevel@tonic-gate 		int peek;
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 		prevfl = fullline;	/* preserve state of previous line */
5780Sstevel@tonic-gate 		while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
5790Sstevel@tonic-gate 			line_len++;
5800Sstevel@tonic-gate 		line_len++;
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 		/* Check for dot-stuffing */
5830Sstevel@tonic-gate 		if (prevfl && lmtprcpts && line[0] == '.')
5840Sstevel@tonic-gate 		{
5850Sstevel@tonic-gate 			if (line[1] == '\n' ||
5860Sstevel@tonic-gate 			    (line[1] == '\r' && line[2] == '\n'))
5870Sstevel@tonic-gate 				goto lmtpdot;
5880Sstevel@tonic-gate 			memcpy(line, line + 1, line_len);
5890Sstevel@tonic-gate 			line_len--;
5900Sstevel@tonic-gate 		}
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 		/* Check to see if we have the full line from fgets() */
5930Sstevel@tonic-gate 		fullline = FALSE;
5940Sstevel@tonic-gate 		if (line_len > 0)
5950Sstevel@tonic-gate 		{
5960Sstevel@tonic-gate 			if (line[line_len - 1] == '\n')
5970Sstevel@tonic-gate 			{
5980Sstevel@tonic-gate 				if (line_len >= 2 &&
5990Sstevel@tonic-gate 				    line[line_len - 2] == '\r')
6000Sstevel@tonic-gate 				{
6010Sstevel@tonic-gate 					line[line_len - 2] = '\n';
6020Sstevel@tonic-gate 					line[line_len - 1] = '\0';
6030Sstevel@tonic-gate 					line_len--;
6040Sstevel@tonic-gate 				}
6050Sstevel@tonic-gate 				fullline = TRUE;
6060Sstevel@tonic-gate 			}
6070Sstevel@tonic-gate 			else if (line[line_len - 1] == '\r')
6080Sstevel@tonic-gate 			{
6090Sstevel@tonic-gate 				/* Did we just miss the CRLF? */
6100Sstevel@tonic-gate 				peek = fgetc(stdin);
6110Sstevel@tonic-gate 				if (peek == '\n')
6120Sstevel@tonic-gate 				{
6130Sstevel@tonic-gate 					line[line_len - 1] = '\n';
6140Sstevel@tonic-gate 					fullline = TRUE;
6150Sstevel@tonic-gate 				}
6160Sstevel@tonic-gate 				else
6170Sstevel@tonic-gate 					(void) ungetc(peek, stdin);
6180Sstevel@tonic-gate 			}
6190Sstevel@tonic-gate 		}
6200Sstevel@tonic-gate 		else
6210Sstevel@tonic-gate 			fullline = TRUE;
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 		if (prevfl && line[0] == '\n' && in_header_section) {
6240Sstevel@tonic-gate 			in_header_section = FALSE;
6250Sstevel@tonic-gate 			if (fflush(fp) == EOF || ferror(fp)) {
6260Sstevel@tonic-gate 				if (lmtprcpts) {
6270Sstevel@tonic-gate 					while (lmtprcpts--)
6280Sstevel@tonic-gate 						printf("451 4.3.0 temporary file write error\r\n");
6290Sstevel@tonic-gate 					fclose(fp);
6300Sstevel@tonic-gate 					return;
6310Sstevel@tonic-gate 				} else {
6320Sstevel@tonic-gate 					mailerr("451 4.3.0",
6330Sstevel@tonic-gate 						"temporary file write error");
6340Sstevel@tonic-gate 					fclose(fp);
6350Sstevel@tonic-gate 					exit(eval);
6360Sstevel@tonic-gate 				}
6370Sstevel@tonic-gate 			}
6380Sstevel@tonic-gate 			fp = bfp;
6390Sstevel@tonic-gate 			continue;
6400Sstevel@tonic-gate 		}
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 		if (in_header_section) {
6430Sstevel@tonic-gate 			if (strncasecmp("Content-Length:", line, 15) == 0) {
6440Sstevel@tonic-gate 				continue; /* skip this header */
6450Sstevel@tonic-gate 			}
6460Sstevel@tonic-gate 		} else
6470Sstevel@tonic-gate 			content_length += strlen(line);
6480Sstevel@tonic-gate 		(void) fwrite(line, sizeof(char), line_len, fp);
6490Sstevel@tonic-gate 		if (ferror(fp)) {
6500Sstevel@tonic-gate 			if (lmtprcpts) {
6510Sstevel@tonic-gate 				while (lmtprcpts--)
6520Sstevel@tonic-gate 					printf("451 4.3.0 temporary file write error\r\n");
6530Sstevel@tonic-gate 				fclose(fp);
6540Sstevel@tonic-gate 				return;
6550Sstevel@tonic-gate 			} else {
6560Sstevel@tonic-gate 				mailerr("451 4.3.0",
6570Sstevel@tonic-gate 					"temporary file write error");
6580Sstevel@tonic-gate 				fclose(fp);
6590Sstevel@tonic-gate 				exit(eval);
6600Sstevel@tonic-gate 			}
6610Sstevel@tonic-gate 		}
6620Sstevel@tonic-gate 	}
6630Sstevel@tonic-gate 	if (sigterm_caught) {
6640Sstevel@tonic-gate 		if (lmtprcpts)
6650Sstevel@tonic-gate 			while (lmtprcpts--)
6660Sstevel@tonic-gate 				printf("451 4.3.0 shutting down\r\n");
6670Sstevel@tonic-gate 		else
6680Sstevel@tonic-gate 			mailerr("451 4.3.0", "shutting down");
6690Sstevel@tonic-gate 		fclose(fp);
6700Sstevel@tonic-gate 		exit(eval);
6710Sstevel@tonic-gate 	}
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 	if (lmtprcpts) {
6740Sstevel@tonic-gate 		/* Got a premature EOF -- toss message and exit */
6750Sstevel@tonic-gate 		exit(EX_OK);
6760Sstevel@tonic-gate 	}
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 	/* If message not newline terminated, need an extra. */
6790Sstevel@tonic-gate 	if (!strchr(line, '\n')) {
6800Sstevel@tonic-gate 		(void) putc('\n', fp);
6810Sstevel@tonic-gate 		content_length++;
6820Sstevel@tonic-gate 	}
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate   lmtpdot:
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	/* Output a newline; note, empty messages are allowed. */
6870Sstevel@tonic-gate 	(void) putc('\n', fp);
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate 	if (fflush(fp) == EOF || ferror(fp)) {
6900Sstevel@tonic-gate 		if (lmtprcpts) {
6910Sstevel@tonic-gate 			while (lmtprcpts--) {
6920Sstevel@tonic-gate 				printf("451 4.3.0 temporary file write error\r\n");
6930Sstevel@tonic-gate 			}
6940Sstevel@tonic-gate 			fclose(fp);
6950Sstevel@tonic-gate 			return;
6960Sstevel@tonic-gate 		} else {
6970Sstevel@tonic-gate 			mailerr("451 4.3.0", "temporary file write error");
6980Sstevel@tonic-gate 			fclose(fp);
6990Sstevel@tonic-gate 			exit(eval);
7000Sstevel@tonic-gate 		}
7010Sstevel@tonic-gate 	}
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 	(void) time(&tval);
7040Sstevel@tonic-gate 	(void) snprintf(unix_from_line, sizeof (unix_from_line), "From %s %s",
7050Sstevel@tonic-gate 	    from, ctime(&tval));
7060Sstevel@tonic-gate 	ulen = strlen(unix_from_line);
7070Sstevel@tonic-gate }
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate static void
710*2897Sjbeck handle_error(err_num, bouncequota, path)
711*2897Sjbeck 	int err_num;
712*2897Sjbeck 	bool bouncequota;
713*2897Sjbeck 	char *path;
714*2897Sjbeck {
715*2897Sjbeck #ifdef EDQUOT
716*2897Sjbeck 	if (err_num == EDQUOT && bouncequota) {
717*2897Sjbeck 		mailerr("552 5.2.2", "%s: %s", path, sm_errstring(err_num));
718*2897Sjbeck 	} else
719*2897Sjbeck #endif /* EDQUOT */
720*2897Sjbeck 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(err_num));
721*2897Sjbeck }
722*2897Sjbeck 
723*2897Sjbeck static void
7240Sstevel@tonic-gate deliver(hfd, bfd, name, bouncequota)
7250Sstevel@tonic-gate 	int hfd;
7260Sstevel@tonic-gate 	int bfd;
7270Sstevel@tonic-gate 	char *name;
7280Sstevel@tonic-gate 	bool bouncequota;
7290Sstevel@tonic-gate {
7300Sstevel@tonic-gate 	struct stat fsb, sb;
7310Sstevel@tonic-gate 	int mbfd = -1, nr, nw = 0, off;
7320Sstevel@tonic-gate 	char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
7330Sstevel@tonic-gate 	off_t curoff, cursize;
7340Sstevel@tonic-gate 	int len;
7350Sstevel@tonic-gate 	struct passwd *pw = NULL;
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 	/*
7380Sstevel@tonic-gate  	* Disallow delivery to unknown names -- special mailboxes
7390Sstevel@tonic-gate  	* can be handled in the sendmail aliases file.
7400Sstevel@tonic-gate  	*/
7410Sstevel@tonic-gate 	if ((pw = getpwnam(name)) == NULL) {
7420Sstevel@tonic-gate 		eval = EX_TEMPFAIL;
7430Sstevel@tonic-gate 		mailerr("451 4.3.0", "cannot lookup name: %s", name);
7440Sstevel@tonic-gate 		return;
7450Sstevel@tonic-gate 	}
7460Sstevel@tonic-gate 	endpwent();
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate 	if (sigterm_caught) {
7490Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
7500Sstevel@tonic-gate 		return;
7510Sstevel@tonic-gate 	}
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate 	/* mailbox may be NFS mounted, seteuid to user */
7540Sstevel@tonic-gate 	targ_uid = pw->pw_uid;
7550Sstevel@tonic-gate 	(void) seteuid(targ_uid);
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate 	if ((saved_uid != 0) && (src_uid != targ_uid)) {
7580Sstevel@tonic-gate 		/*
7590Sstevel@tonic-gate 		 * If saved_uid == 0 (root), anything is OK; this is
7600Sstevel@tonic-gate 		 * as it should be.  But to prevent a random user from
7610Sstevel@tonic-gate 		 * calling "mail.local foo" in an attempt to hijack
7620Sstevel@tonic-gate 		 * foo's mail-box, make sure src_uid == targ_uid o/w.
7630Sstevel@tonic-gate 		 */
7640Sstevel@tonic-gate 		warn("%s: wrong owner (is %d, should be %d)",
7650Sstevel@tonic-gate 			name, src_uid, targ_uid);
7660Sstevel@tonic-gate 		eval = EX_CANTCREAT;
7670Sstevel@tonic-gate 		return;
7680Sstevel@tonic-gate 	}
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 	path[0] = '\0';
7710Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s/%s", _PATH_MAILDIR, name);
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 	/*
7740Sstevel@tonic-gate 	 * If the mailbox is linked or a symlink, fail.  There's an obvious
7750Sstevel@tonic-gate 	 * race here, that the file was replaced with a symbolic link after
7760Sstevel@tonic-gate 	 * the lstat returned, but before the open.  We attempt to detect
7770Sstevel@tonic-gate 	 * this by comparing the original stat information and information
7780Sstevel@tonic-gate 	 * returned by an fstat of the file descriptor returned by the open.
7790Sstevel@tonic-gate 	 *
7800Sstevel@tonic-gate 	 * NB: this is a symptom of a larger problem, that the mail spooling
7810Sstevel@tonic-gate 	 * directory is writeable by the wrong users.  If that directory is
7820Sstevel@tonic-gate 	 * writeable, system security is compromised for other reasons, and
7830Sstevel@tonic-gate 	 * it cannot be fixed here.
7840Sstevel@tonic-gate 	 *
7850Sstevel@tonic-gate 	 * If we created the mailbox, set the owner/group.  If that fails,
7860Sstevel@tonic-gate 	 * just return.  Another process may have already opened it, so we
7870Sstevel@tonic-gate 	 * can't unlink it.  Historically, binmail set the owner/group at
7880Sstevel@tonic-gate 	 * each mail delivery.  We no longer do this, assuming that if the
7890Sstevel@tonic-gate 	 * ownership or permissions were changed there was a reason.
7900Sstevel@tonic-gate 	 *
7910Sstevel@tonic-gate 	 * XXX
7920Sstevel@tonic-gate 	 * open(2) should support flock'ing the file.
7930Sstevel@tonic-gate 	 */
7940Sstevel@tonic-gate tryagain:
7950Sstevel@tonic-gate 	/* should check lock status, but... maillock return no value */
7960Sstevel@tonic-gate 	maillock(name, 10);
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 	if (sigterm_caught) {
7990Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
8000Sstevel@tonic-gate 		goto err0;
8010Sstevel@tonic-gate 	}
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	if (lstat(path, &sb)) {
8040Sstevel@tonic-gate 		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY,
8050Sstevel@tonic-gate 				S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
8060Sstevel@tonic-gate 		if (mbfd != -1)
8070Sstevel@tonic-gate 			(void) fchmod(mbfd, 0660);
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 		if (mbfd == -1) {
8110Sstevel@tonic-gate 			if (errno == EEXIST) {
8120Sstevel@tonic-gate 				mailunlock();
8130Sstevel@tonic-gate 				goto tryagain;
8140Sstevel@tonic-gate 			}
8150Sstevel@tonic-gate 		}
8160Sstevel@tonic-gate 	} else if (sb.st_nlink != 1) {
8170Sstevel@tonic-gate 		mailerr("550 5.2.0", "%s: too many links", path);
8180Sstevel@tonic-gate 		goto err0;
8190Sstevel@tonic-gate 	} else if (!S_ISREG(sb.st_mode)) {
8200Sstevel@tonic-gate 		mailerr("550 5.2.0", "%s: irregular file", path);
8210Sstevel@tonic-gate 		goto err0;
8220Sstevel@tonic-gate 	} else {
8230Sstevel@tonic-gate 		mbfd = open(path, O_APPEND|O_WRONLY, 0);
8240Sstevel@tonic-gate 		if (mbfd != -1 &&
8250Sstevel@tonic-gate 		    (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
8260Sstevel@tonic-gate 		    S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
8270Sstevel@tonic-gate 		    sb.st_ino != fsb.st_ino)) {
8280Sstevel@tonic-gate 			eval = EX_TEMPFAIL;
8290Sstevel@tonic-gate 			mailerr("550 5.2.0",
8300Sstevel@tonic-gate 				"%s: fstat: file changed after open", path);
8310Sstevel@tonic-gate 			goto err1;
8320Sstevel@tonic-gate 		}
8330Sstevel@tonic-gate 	}
8340Sstevel@tonic-gate 
8350Sstevel@tonic-gate 	if (mbfd == -1) {
8360Sstevel@tonic-gate 		mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
8370Sstevel@tonic-gate 		goto err0;
8380Sstevel@tonic-gate 	}
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	if (sigterm_caught) {
8410Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
8420Sstevel@tonic-gate 		goto err0;
8430Sstevel@tonic-gate 	}
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 	/* Get the starting offset of the new message for biff. */
8460Sstevel@tonic-gate 	curoff = lseek(mbfd, (off_t)0, SEEK_END);
8470Sstevel@tonic-gate 	(void) snprintf(biffmsg, sizeof (biffmsg), "%s@%ld\n", name, curoff);
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	/* Copy the message into the file. */
8500Sstevel@tonic-gate 	if (lseek(hfd, (off_t)0, SEEK_SET) == (off_t)-1) {
8510Sstevel@tonic-gate 		mailerr("450 4.2.0", "temporary file: %s", strerror(errno));
8520Sstevel@tonic-gate 		goto err1;
8530Sstevel@tonic-gate 	}
8540Sstevel@tonic-gate 	/* Copy the message into the file. */
8550Sstevel@tonic-gate 	if (lseek(bfd, (off_t)0, SEEK_SET) == (off_t)-1) {
8560Sstevel@tonic-gate 		mailerr("450 4.2.0", "temporary file: %s", strerror(errno));
8570Sstevel@tonic-gate 		goto err1;
8580Sstevel@tonic-gate 	}
8590Sstevel@tonic-gate 	if ((write(mbfd, unix_from_line, ulen)) != ulen) {
860*2897Sjbeck 		handle_error(errno, bouncequota, path);
8610Sstevel@tonic-gate 		goto err2;
8620Sstevel@tonic-gate 	}
8630Sstevel@tonic-gate 
8640Sstevel@tonic-gate 	if (sigterm_caught) {
8650Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
8660Sstevel@tonic-gate 		goto err2;
8670Sstevel@tonic-gate 	}
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 	while ((nr = read(hfd, buf, sizeof (buf))) > 0)
8700Sstevel@tonic-gate 		for (off = 0; off < nr; nr -= nw, off += nw)
8710Sstevel@tonic-gate 			if ((nw = write(mbfd, buf + off, nr)) < 0)
8720Sstevel@tonic-gate 			{
873*2897Sjbeck 				handle_error(errno, bouncequota, path);
8740Sstevel@tonic-gate 				goto err2;
8750Sstevel@tonic-gate 			}
8760Sstevel@tonic-gate 	if (nr < 0) {
877*2897Sjbeck 		handle_error(errno, bouncequota, path);
8780Sstevel@tonic-gate 		goto err2;
8790Sstevel@tonic-gate 	}
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate 	if (sigterm_caught) {
8820Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
8830Sstevel@tonic-gate 		goto err2;
8840Sstevel@tonic-gate 	}
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "Content-Length: %d\n\n",
8870Sstevel@tonic-gate 	    content_length);
8880Sstevel@tonic-gate 	len = strlen(buf);
8890Sstevel@tonic-gate 	if (write(mbfd, buf, len) != len) {
890*2897Sjbeck 		handle_error(errno, bouncequota, path);
8910Sstevel@tonic-gate 		goto err2;
8920Sstevel@tonic-gate 	}
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	if (sigterm_caught) {
8950Sstevel@tonic-gate 		mailerr("451 4.3.0", "shutting down");
8960Sstevel@tonic-gate 		goto err2;
8970Sstevel@tonic-gate 	}
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 	while ((nr = read(bfd, buf, sizeof (buf))) > 0) {
9000Sstevel@tonic-gate 		for (off = 0; off < nr; nr -= nw, off += nw)
9010Sstevel@tonic-gate 			if ((nw = write(mbfd, buf + off, nr)) < 0) {
902*2897Sjbeck 				handle_error(errno, bouncequota, path);
9030Sstevel@tonic-gate 				goto err2;
9040Sstevel@tonic-gate 			}
9050Sstevel@tonic-gate 		if (sigterm_caught) {
9060Sstevel@tonic-gate 			mailerr("451 4.3.0", "shutting down");
9070Sstevel@tonic-gate 			goto err2;
9080Sstevel@tonic-gate 		}
9090Sstevel@tonic-gate 	}
9100Sstevel@tonic-gate 	if (nr < 0) {
911*2897Sjbeck 		handle_error(errno, bouncequota, path);
9120Sstevel@tonic-gate 		goto err2;
9130Sstevel@tonic-gate 	}
9140Sstevel@tonic-gate 
9150Sstevel@tonic-gate 	/* Flush to disk, don't wait for update. */
9160Sstevel@tonic-gate 	if (fsync(mbfd)) {
917*2897Sjbeck 		handle_error(errno, bouncequota, path);
9180Sstevel@tonic-gate err2:		if (mbfd >= 0)
9190Sstevel@tonic-gate 			(void)ftruncate(mbfd, curoff);
9200Sstevel@tonic-gate err1:		(void)close(mbfd);
9210Sstevel@tonic-gate err0:		mailunlock();
9220Sstevel@tonic-gate 		(void)seteuid(saved_uid);
9230Sstevel@tonic-gate 		return;
9240Sstevel@tonic-gate 	}
9250Sstevel@tonic-gate 
9260Sstevel@tonic-gate 	/*
9270Sstevel@tonic-gate 	**  Save the current size so if the close() fails below
9280Sstevel@tonic-gate 	**  we can make sure no other process has changed the mailbox
9290Sstevel@tonic-gate 	**  between the failed close and the re-open()/re-lock().
9300Sstevel@tonic-gate 	**  If something else has changed the size, we shouldn't
9310Sstevel@tonic-gate 	**  try to truncate it as we may do more harm then good
9320Sstevel@tonic-gate 	**  (e.g., truncate a later message delivery).
9330Sstevel@tonic-gate 	*/
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	if (fstat(mbfd, &sb) < 0)
9360Sstevel@tonic-gate 		cursize = 0;
9370Sstevel@tonic-gate 	else
9380Sstevel@tonic-gate 		cursize = sb.st_size;
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 	/* Close and check -- NFS doesn't write until the close. */
9410Sstevel@tonic-gate 	if (close(mbfd))
9420Sstevel@tonic-gate 	{
943*2897Sjbeck 		handle_error(errno, bouncequota, path);
9440Sstevel@tonic-gate 		mbfd = open(path, O_WRONLY, 0);
9450Sstevel@tonic-gate 		if (mbfd < 0 ||
9460Sstevel@tonic-gate 		    cursize == 0
9470Sstevel@tonic-gate 		    || flock(mbfd, LOCK_EX) < 0 ||
9480Sstevel@tonic-gate 		    fstat(mbfd, &sb) < 0 ||
9490Sstevel@tonic-gate 		    sb.st_size != cursize ||
9500Sstevel@tonic-gate 		    sb.st_nlink != 1 ||
9510Sstevel@tonic-gate 		    !S_ISREG(sb.st_mode) ||
9520Sstevel@tonic-gate 		    sb.st_dev != fsb.st_dev ||
9530Sstevel@tonic-gate 		    sb.st_ino != fsb.st_ino ||
9540Sstevel@tonic-gate 		    sb.st_uid != fsb.st_uid)
9550Sstevel@tonic-gate 		{
9560Sstevel@tonic-gate 			/* Don't use a bogus file */
9570Sstevel@tonic-gate 			if (mbfd >= 0)
9580Sstevel@tonic-gate 			{
9590Sstevel@tonic-gate 				(void) close(mbfd);
9600Sstevel@tonic-gate 				mbfd = -1;
9610Sstevel@tonic-gate 			}
9620Sstevel@tonic-gate 		}
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate 		/* Attempt to truncate back to pre-write size */
9650Sstevel@tonic-gate 		goto err2;
9660Sstevel@tonic-gate 	} else
9670Sstevel@tonic-gate 		notifybiff(biffmsg);
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 	mailunlock();
9700Sstevel@tonic-gate 
9710Sstevel@tonic-gate 	(void)seteuid(saved_uid);
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate 	if (lmtpmode) {
9740Sstevel@tonic-gate 		printf("250 2.1.5 %s OK\r\n", name);
9750Sstevel@tonic-gate 	}
9760Sstevel@tonic-gate }
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate static void
9790Sstevel@tonic-gate notifybiff(msg)
9800Sstevel@tonic-gate 	char *msg;
9810Sstevel@tonic-gate {
9820Sstevel@tonic-gate 	static struct sockaddr_in addr;
9830Sstevel@tonic-gate 	static int f = -1;
9840Sstevel@tonic-gate 	struct hostent *hp;
9850Sstevel@tonic-gate 	struct servent *sp;
9860Sstevel@tonic-gate 	int len;
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 	if (msg == NULL) {
9890Sstevel@tonic-gate 		/* Be silent if biff service not available. */
9900Sstevel@tonic-gate 		if ((sp = getservbyname("biff", "udp")) == NULL)
9910Sstevel@tonic-gate 			return;
9920Sstevel@tonic-gate 		if ((hp = gethostbyname("localhost")) == NULL) {
9930Sstevel@tonic-gate 			warn("localhost: %s", strerror(errno));
9940Sstevel@tonic-gate 			return;
9950Sstevel@tonic-gate 		}
9960Sstevel@tonic-gate 		addr.sin_family = hp->h_addrtype;
9970Sstevel@tonic-gate 		(void) memmove(&addr.sin_addr, hp->h_addr, hp->h_length);
9980Sstevel@tonic-gate 		addr.sin_port = sp->s_port;
9990Sstevel@tonic-gate 		return;
10000Sstevel@tonic-gate 	}
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	if (addr.sin_family == 0)
10030Sstevel@tonic-gate 		return; /* did not initialize */
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
10060Sstevel@tonic-gate 		warn("socket: %s", strerror(errno));
10070Sstevel@tonic-gate 		return;
10080Sstevel@tonic-gate 	}
10090Sstevel@tonic-gate 	len = strlen(msg) + 1;
10100Sstevel@tonic-gate 	if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof (addr))
10110Sstevel@tonic-gate 	    != len)
10120Sstevel@tonic-gate 		warn("sendto biff: %s", strerror(errno));
10130Sstevel@tonic-gate }
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate static void
10160Sstevel@tonic-gate usage()
10170Sstevel@tonic-gate {
10180Sstevel@tonic-gate 	eval = EX_USAGE;
10190Sstevel@tonic-gate 	err("usage: mail.local [-l] [-f from] user ...");
10200Sstevel@tonic-gate }
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate static void
10230Sstevel@tonic-gate /*VARARGS2*/
10240Sstevel@tonic-gate #ifdef __STDC__
10250Sstevel@tonic-gate mailerr(const char *hdr, const char *fmt, ...)
10260Sstevel@tonic-gate #else
10270Sstevel@tonic-gate mailerr(hdr, fmt, va_alist)
10280Sstevel@tonic-gate 	const char *hdr;
10290Sstevel@tonic-gate 	const char *fmt;
10300Sstevel@tonic-gate 	va_dcl
10310Sstevel@tonic-gate #endif
10320Sstevel@tonic-gate {
10330Sstevel@tonic-gate 	va_list ap;
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate #ifdef __STDC__
10360Sstevel@tonic-gate 	va_start(ap, fmt);
10370Sstevel@tonic-gate #else
10380Sstevel@tonic-gate 	va_start(ap);
10390Sstevel@tonic-gate #endif
10400Sstevel@tonic-gate 	if (lmtpmode)
10410Sstevel@tonic-gate 	{
10420Sstevel@tonic-gate 		if (hdr != NULL)
10430Sstevel@tonic-gate 			printf("%s ", hdr);
10440Sstevel@tonic-gate 		vprintf(fmt, ap);
10450Sstevel@tonic-gate 		printf("\r\n");
10460Sstevel@tonic-gate 	}
10470Sstevel@tonic-gate 	else
10480Sstevel@tonic-gate 	{
10490Sstevel@tonic-gate 		e_to_sys(errno);
10500Sstevel@tonic-gate 		vwarn(fmt, ap);
10510Sstevel@tonic-gate 	}
10520Sstevel@tonic-gate }
10530Sstevel@tonic-gate 
10540Sstevel@tonic-gate static void
10550Sstevel@tonic-gate /*VARARGS1*/
10560Sstevel@tonic-gate #ifdef __STDC__
10570Sstevel@tonic-gate err(const char *fmt, ...)
10580Sstevel@tonic-gate #else
10590Sstevel@tonic-gate err(fmt, va_alist)
10600Sstevel@tonic-gate 	const char *fmt;
10610Sstevel@tonic-gate 	va_dcl
10620Sstevel@tonic-gate #endif
10630Sstevel@tonic-gate {
10640Sstevel@tonic-gate 	va_list ap;
10650Sstevel@tonic-gate 
10660Sstevel@tonic-gate #ifdef __STDC__
10670Sstevel@tonic-gate 	va_start(ap, fmt);
10680Sstevel@tonic-gate #else
10690Sstevel@tonic-gate 	va_start(ap);
10700Sstevel@tonic-gate #endif
10710Sstevel@tonic-gate 	vwarn(fmt, ap);
10720Sstevel@tonic-gate 	va_end(ap);
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate 	exit(eval);
10750Sstevel@tonic-gate }
10760Sstevel@tonic-gate 
10770Sstevel@tonic-gate static void
10780Sstevel@tonic-gate /*VARARGS1*/
10790Sstevel@tonic-gate #ifdef __STDC__
10800Sstevel@tonic-gate warn(const char *fmt, ...)
10810Sstevel@tonic-gate #else
10820Sstevel@tonic-gate warn(fmt, va_alist)
10830Sstevel@tonic-gate 	const char *fmt;
10840Sstevel@tonic-gate 	va_dcl
10850Sstevel@tonic-gate #endif
10860Sstevel@tonic-gate {
10870Sstevel@tonic-gate 	va_list ap;
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate #ifdef __STDC__
10900Sstevel@tonic-gate 	va_start(ap, fmt);
10910Sstevel@tonic-gate #else
10920Sstevel@tonic-gate 	va_start(ap);
10930Sstevel@tonic-gate #endif
10940Sstevel@tonic-gate 	vwarn(fmt, ap);
10950Sstevel@tonic-gate 	va_end(ap);
10960Sstevel@tonic-gate }
10970Sstevel@tonic-gate 
10980Sstevel@tonic-gate static void
10990Sstevel@tonic-gate vwarn(fmt, ap)
11000Sstevel@tonic-gate 	const char *fmt;
11010Sstevel@tonic-gate 	va_list ap;
11020Sstevel@tonic-gate {
11030Sstevel@tonic-gate 	/*
11040Sstevel@tonic-gate 	 * Log the message to stderr.
11050Sstevel@tonic-gate 	 *
11060Sstevel@tonic-gate 	 * Don't use LOG_PERROR as an openlog() flag to do this,
11070Sstevel@tonic-gate 	 * it's not portable enough.
11080Sstevel@tonic-gate 	 */
11090Sstevel@tonic-gate 	if (eval != EX_USAGE)
11100Sstevel@tonic-gate 		(void) fprintf(stderr, "mail.local: ");
11110Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, ap);
11120Sstevel@tonic-gate 	(void) fprintf(stderr, "\n");
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate 	/* Log the message to syslog. */
11150Sstevel@tonic-gate 	vsyslog(LOG_ERR, fmt, ap);
11160Sstevel@tonic-gate }
11170Sstevel@tonic-gate 
11180Sstevel@tonic-gate /*
11190Sstevel@tonic-gate  * e_to_sys --
11200Sstevel@tonic-gate  *	Guess which errno's are temporary.  Gag me.
11210Sstevel@tonic-gate  */
11220Sstevel@tonic-gate static void
11230Sstevel@tonic-gate e_to_sys(num)
11240Sstevel@tonic-gate 	int num;
11250Sstevel@tonic-gate {
11260Sstevel@tonic-gate 	/* Temporary failures override hard errors. */
11270Sstevel@tonic-gate 	if (eval == EX_TEMPFAIL)
11280Sstevel@tonic-gate 		return;
11290Sstevel@tonic-gate 
11300Sstevel@tonic-gate 	switch (num)		/* Hopefully temporary errors. */
11310Sstevel@tonic-gate 	{
11320Sstevel@tonic-gate #ifdef EDQUOT
11330Sstevel@tonic-gate 	case EDQUOT:		/* Disc quota exceeded */
11340Sstevel@tonic-gate 		if (bouncequota)
11350Sstevel@tonic-gate 		{
11360Sstevel@tonic-gate 			eval = EX_UNAVAILABLE;
11370Sstevel@tonic-gate 			break;
11380Sstevel@tonic-gate 		}
11390Sstevel@tonic-gate 		/* FALLTHROUGH */
11400Sstevel@tonic-gate #endif /* EDQUOT */
11410Sstevel@tonic-gate #ifdef EAGAIN
11420Sstevel@tonic-gate 	case EAGAIN:		/* Resource temporarily unavailable */
11430Sstevel@tonic-gate #endif
11440Sstevel@tonic-gate #ifdef EBUSY
11450Sstevel@tonic-gate 	case EBUSY:		/* Device busy */
11460Sstevel@tonic-gate #endif
11470Sstevel@tonic-gate #ifdef EPROCLIM
11480Sstevel@tonic-gate 	case EPROCLIM:		/* Too many processes */
11490Sstevel@tonic-gate #endif
11500Sstevel@tonic-gate #ifdef EUSERS
11510Sstevel@tonic-gate 	case EUSERS:		/* Too many users */
11520Sstevel@tonic-gate #endif
11530Sstevel@tonic-gate #ifdef ECONNABORTED
11540Sstevel@tonic-gate 	case ECONNABORTED:	/* Software caused connection abort */
11550Sstevel@tonic-gate #endif
11560Sstevel@tonic-gate #ifdef ECONNREFUSED
11570Sstevel@tonic-gate 	case ECONNREFUSED:	/* Connection refused */
11580Sstevel@tonic-gate #endif
11590Sstevel@tonic-gate #ifdef ECONNRESET
11600Sstevel@tonic-gate 	case ECONNRESET:	/* Connection reset by peer */
11610Sstevel@tonic-gate #endif
11620Sstevel@tonic-gate #ifdef EDEADLK
11630Sstevel@tonic-gate 	case EDEADLK:		/* Resource deadlock avoided */
11640Sstevel@tonic-gate #endif
11650Sstevel@tonic-gate #ifdef EFBIG
11660Sstevel@tonic-gate 	case EFBIG:		/* File too large */
11670Sstevel@tonic-gate #endif
11680Sstevel@tonic-gate #ifdef EHOSTDOWN
11690Sstevel@tonic-gate 	case EHOSTDOWN:		/* Host is down */
11700Sstevel@tonic-gate #endif
11710Sstevel@tonic-gate #ifdef EHOSTUNREACH
11720Sstevel@tonic-gate 	case EHOSTUNREACH:	/* No route to host */
11730Sstevel@tonic-gate #endif
11740Sstevel@tonic-gate #ifdef EMFILE
11750Sstevel@tonic-gate 	case EMFILE:		/* Too many open files */
11760Sstevel@tonic-gate #endif
11770Sstevel@tonic-gate #ifdef ENETDOWN
11780Sstevel@tonic-gate 	case ENETDOWN:		/* Network is down */
11790Sstevel@tonic-gate #endif
11800Sstevel@tonic-gate #ifdef ENETRESET
11810Sstevel@tonic-gate 	case ENETRESET:		/* Network dropped connection on reset */
11820Sstevel@tonic-gate #endif
11830Sstevel@tonic-gate #ifdef ENETUNREACH
11840Sstevel@tonic-gate 	case ENETUNREACH:	/* Network is unreachable */
11850Sstevel@tonic-gate #endif
11860Sstevel@tonic-gate #ifdef ENFILE
11870Sstevel@tonic-gate 	case ENFILE:		/* Too many open files in system */
11880Sstevel@tonic-gate #endif
11890Sstevel@tonic-gate #ifdef ENOBUFS
11900Sstevel@tonic-gate 	case ENOBUFS:		/* No buffer space available */
11910Sstevel@tonic-gate #endif
11920Sstevel@tonic-gate #ifdef ENOMEM
11930Sstevel@tonic-gate 	case ENOMEM:		/* Cannot allocate memory */
11940Sstevel@tonic-gate #endif
11950Sstevel@tonic-gate #ifdef ENOSPC
11960Sstevel@tonic-gate 	case ENOSPC:		/* No space left on device */
11970Sstevel@tonic-gate #endif
11980Sstevel@tonic-gate #ifdef EROFS
11990Sstevel@tonic-gate 	case EROFS:		/* Read-only file system */
12000Sstevel@tonic-gate #endif
12010Sstevel@tonic-gate #ifdef ESTALE
12020Sstevel@tonic-gate 	case ESTALE:		/* Stale NFS file handle */
12030Sstevel@tonic-gate #endif
12040Sstevel@tonic-gate #ifdef ETIMEDOUT
12050Sstevel@tonic-gate 	case ETIMEDOUT:		/* Connection timed out */
12060Sstevel@tonic-gate #endif
12070Sstevel@tonic-gate #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
12080Sstevel@tonic-gate 	case EWOULDBLOCK:	/* Operation would block. */
12090Sstevel@tonic-gate #endif
12100Sstevel@tonic-gate 		eval = EX_TEMPFAIL;
12110Sstevel@tonic-gate 		break;
12120Sstevel@tonic-gate 	default:
12130Sstevel@tonic-gate 		eval = EX_UNAVAILABLE;
12140Sstevel@tonic-gate 		break;
12150Sstevel@tonic-gate 	}
12160Sstevel@tonic-gate }
1217