xref: /illumos-gate/usr/src/cmd/sendmail/util/mail.local.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
1*955eb5e1SGarrett D'Amore /*
2*955eb5e1SGarrett D'Amore  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3*955eb5e1SGarrett D'Amore  * Copyright (c) 1990, 1993, 1994
4*955eb5e1SGarrett D'Amore  *	The Regents of the University of California.  All rights reserved.
5*955eb5e1SGarrett D'Amore  *
6*955eb5e1SGarrett D'Amore  * By using this file, you agree to the terms and conditions set
7*955eb5e1SGarrett D'Amore  * forth in the LICENSE file which can be found at the top level
8*955eb5e1SGarrett D'Amore  * of the sendmail distribution.
9*955eb5e1SGarrett D'Amore  */
10*955eb5e1SGarrett D'Amore 
11*955eb5e1SGarrett D'Amore /*
12*955eb5e1SGarrett D'Amore  * Copyright 1994-2007 Sun Microsystems, Inc.  All rights reserved.
13*955eb5e1SGarrett D'Amore  * Use is subject to license terms.
14*955eb5e1SGarrett D'Amore  */
15*955eb5e1SGarrett D'Amore 
16*955eb5e1SGarrett D'Amore #ifndef lint
17*955eb5e1SGarrett D'Amore static char copyright[] =
18*955eb5e1SGarrett D'Amore "@(#) Copyright (c) 1990, 1993, 1994\n\
19*955eb5e1SGarrett D'Amore 	The Regents of the University of California.  All rights reserved.\n";
20*955eb5e1SGarrett D'Amore #endif /* not lint */
21*955eb5e1SGarrett D'Amore 
22*955eb5e1SGarrett D'Amore #ifndef lint
23*955eb5e1SGarrett D'Amore static char sccsid[] = "@(#)mail.local.c	8.83 (Berkeley) 12/17/98";
24*955eb5e1SGarrett D'Amore static char sccsi2[] = "%W% (Sun) %G%";
25*955eb5e1SGarrett D'Amore #endif /* not lint */
26*955eb5e1SGarrett D'Amore 
27*955eb5e1SGarrett D'Amore #include <sys/param.h>
28*955eb5e1SGarrett D'Amore #include <sys/stat.h>
29*955eb5e1SGarrett D'Amore #include <sys/socket.h>
30*955eb5e1SGarrett D'Amore #include <sys/file.h>
31*955eb5e1SGarrett D'Amore 
32*955eb5e1SGarrett D'Amore #include <netinet/in.h>
33*955eb5e1SGarrett D'Amore 
34*955eb5e1SGarrett D'Amore #include <errno.h>
35*955eb5e1SGarrett D'Amore #include <fcntl.h>
36*955eb5e1SGarrett D'Amore #include <netdb.h>
37*955eb5e1SGarrett D'Amore #include <pwd.h>
38*955eb5e1SGarrett D'Amore #include <stdio.h>
39*955eb5e1SGarrett D'Amore #include <stdlib.h>
40*955eb5e1SGarrett D'Amore #include <signal.h>
41*955eb5e1SGarrett D'Amore #include <ctype.h>
42*955eb5e1SGarrett D'Amore #include <string.h>
43*955eb5e1SGarrett D'Amore #include <sysexits.h>
44*955eb5e1SGarrett D'Amore #include <time.h>
45*955eb5e1SGarrett D'Amore #include <unistd.h>
46*955eb5e1SGarrett D'Amore #include <maillock.h>
47*955eb5e1SGarrett D'Amore #include <grp.h>
48*955eb5e1SGarrett D'Amore 
49*955eb5e1SGarrett D'Amore #ifdef __STDC__
50*955eb5e1SGarrett D'Amore #include <stdarg.h>
51*955eb5e1SGarrett D'Amore #else
52*955eb5e1SGarrett D'Amore #include <varargs.h>
53*955eb5e1SGarrett D'Amore #endif
54*955eb5e1SGarrett D'Amore 
55*955eb5e1SGarrett D'Amore #include <syslog.h>
56*955eb5e1SGarrett D'Amore 
57*955eb5e1SGarrett D'Amore #include <sysexits.h>
58*955eb5e1SGarrett D'Amore #include <ctype.h>
59*955eb5e1SGarrett D'Amore 
60*955eb5e1SGarrett D'Amore #include <sm/conf.h>
61*955eb5e1SGarrett D'Amore #include <sendmail/pathnames.h>
62*955eb5e1SGarrett D'Amore 
63*955eb5e1SGarrett D'Amore /*
64*955eb5e1SGarrett D'Amore **  If you don't have flock, you could try using lockf instead.
65*955eb5e1SGarrett D'Amore */
66*955eb5e1SGarrett D'Amore 
67*955eb5e1SGarrett D'Amore #ifdef LDA_USE_LOCKF
68*955eb5e1SGarrett D'Amore # define flock(a, b)	lockf(a, b, 0)
69*955eb5e1SGarrett D'Amore # ifdef LOCK_EX
70*955eb5e1SGarrett D'Amore #  undef LOCK_EX
71*955eb5e1SGarrett D'Amore # endif /* LOCK_EX */
72*955eb5e1SGarrett D'Amore # define LOCK_EX        F_LOCK
73*955eb5e1SGarrett D'Amore #endif /* LDA_USE_LOCKF */
74*955eb5e1SGarrett D'Amore 
75*955eb5e1SGarrett D'Amore #ifndef LOCK_EX
76*955eb5e1SGarrett D'Amore # include <sys/file.h>
77*955eb5e1SGarrett D'Amore #endif /* ! LOCK_EX */
78*955eb5e1SGarrett D'Amore 
79*955eb5e1SGarrett D'Amore #ifndef MAILER_DAEMON
80*955eb5e1SGarrett D'Amore # define MAILER_DAEMON	"MAILER-DAEMON"
81*955eb5e1SGarrett D'Amore #endif
82*955eb5e1SGarrett D'Amore 
83*955eb5e1SGarrett D'Amore typedef int bool;
84*955eb5e1SGarrett D'Amore 
85*955eb5e1SGarrett D'Amore #define	FALSE	0
86*955eb5e1SGarrett D'Amore #define	TRUE	1
87*955eb5e1SGarrett D'Amore 
88*955eb5e1SGarrett D'Amore bool	EightBitMime = TRUE;		/* advertise 8BITMIME in LMTP */
89*955eb5e1SGarrett D'Amore static int eval = EX_OK;			/* sysexits.h error value. */
90*955eb5e1SGarrett D'Amore static int lmtpmode = 0;
91*955eb5e1SGarrett D'Amore bool	bouncequota = FALSE;		/* permanent error when over quota */
92*955eb5e1SGarrett D'Amore 
93*955eb5e1SGarrett D'Amore #define	_PATH_MAILDIR	"/var/mail"
94*955eb5e1SGarrett D'Amore #define	_PATH_LOCTMP	"/tmp/local.XXXXXX"
95*955eb5e1SGarrett D'Amore #define	_PATH_LOCHTMP	"/tmp/lochd.XXXXXX"
96*955eb5e1SGarrett D'Amore #define	FALSE 0
97*955eb5e1SGarrett D'Amore #define	TRUE  1
98*955eb5e1SGarrett D'Amore #define	MAXLINE 2048
99*955eb5e1SGarrett D'Amore 
100*955eb5e1SGarrett D'Amore static void	deliver(int, int, char *, bool);
101*955eb5e1SGarrett D'Amore static void	e_to_sys(int);
102*955eb5e1SGarrett D'Amore static void	err(const char *fmt, ...);
103*955eb5e1SGarrett D'Amore static void	notifybiff(char *);
104*955eb5e1SGarrett D'Amore static void	store(char *, int);
105*955eb5e1SGarrett D'Amore static void	usage(void);
106*955eb5e1SGarrett D'Amore static void	vwarn();
107*955eb5e1SGarrett D'Amore static void	warn(const char *fmt, ...);
108*955eb5e1SGarrett D'Amore static void	mailerr(const char *, const char *, ...);
109*955eb5e1SGarrett D'Amore static void	sigterm_handler();
110*955eb5e1SGarrett D'Amore 
111*955eb5e1SGarrett D'Amore static char	unix_from_line[MAXLINE];
112*955eb5e1SGarrett D'Amore static int	ulen;
113*955eb5e1SGarrett D'Amore static int	content_length;
114*955eb5e1SGarrett D'Amore static int	bfd, hfd; /* temp file */
115*955eb5e1SGarrett D'Amore static uid_t	src_uid, targ_uid, saved_uid;
116*955eb5e1SGarrett D'Amore static int	sigterm_caught;
117*955eb5e1SGarrett D'Amore 
118*955eb5e1SGarrett D'Amore int
main(argc,argv)119*955eb5e1SGarrett D'Amore main(argc, argv)
120*955eb5e1SGarrett D'Amore 	int argc;
121*955eb5e1SGarrett D'Amore 	char *argv[];
122*955eb5e1SGarrett D'Amore {
123*955eb5e1SGarrett D'Amore 	struct passwd *pw;
124*955eb5e1SGarrett D'Amore 	int ch;
125*955eb5e1SGarrett D'Amore 	uid_t uid;
126*955eb5e1SGarrett D'Amore 	char *from;
127*955eb5e1SGarrett D'Amore 	struct  group *grpptr;
128*955eb5e1SGarrett D'Amore 	void dolmtp();
129*955eb5e1SGarrett D'Amore 
130*955eb5e1SGarrett D'Amore 	openlog("mail.local", 0, LOG_MAIL);
131*955eb5e1SGarrett D'Amore 
132*955eb5e1SGarrett D'Amore 	from = NULL;
133*955eb5e1SGarrett D'Amore 	pw = NULL;
134*955eb5e1SGarrett D'Amore 	sigterm_caught = FALSE;
135*955eb5e1SGarrett D'Amore 
136*955eb5e1SGarrett D'Amore 	(void) sigset(SIGTERM, sigterm_handler);
137*955eb5e1SGarrett D'Amore 
138*955eb5e1SGarrett D'Amore 	while ((ch = getopt(argc, argv, "7bdf:r:l")) != EOF)
139*955eb5e1SGarrett D'Amore 		switch (ch) {
140*955eb5e1SGarrett D'Amore 		case '7':		/* Do not advertise 8BITMIME */
141*955eb5e1SGarrett D'Amore 			EightBitMime = FALSE;
142*955eb5e1SGarrett D'Amore 			break;
143*955eb5e1SGarrett D'Amore 
144*955eb5e1SGarrett D'Amore 		case 'b':		/* bounce mail when over quota. */
145*955eb5e1SGarrett D'Amore 			bouncequota = TRUE;
146*955eb5e1SGarrett D'Amore 			break;
147*955eb5e1SGarrett D'Amore 
148*955eb5e1SGarrett D'Amore 		case 'd':		/* Backward compatible. */
149*955eb5e1SGarrett D'Amore 			break;
150*955eb5e1SGarrett D'Amore 		case 'f':
151*955eb5e1SGarrett D'Amore 		case 'r':		/* Backward compatible. */
152*955eb5e1SGarrett D'Amore 			if (from != NULL) {
153*955eb5e1SGarrett D'Amore 				warn("multiple -f options");
154*955eb5e1SGarrett D'Amore 				usage();
155*955eb5e1SGarrett D'Amore 			}
156*955eb5e1SGarrett D'Amore 			from = optarg;
157*955eb5e1SGarrett D'Amore 			break;
158*955eb5e1SGarrett D'Amore 		case 'l':
159*955eb5e1SGarrett D'Amore 			lmtpmode++;
160*955eb5e1SGarrett D'Amore 			break;
161*955eb5e1SGarrett D'Amore 		case '?':
162*955eb5e1SGarrett D'Amore 		default:
163*955eb5e1SGarrett D'Amore 			usage();
164*955eb5e1SGarrett D'Amore 		}
165*955eb5e1SGarrett D'Amore 	argc -= optind;
166*955eb5e1SGarrett D'Amore 	argv += optind;
167*955eb5e1SGarrett D'Amore 
168*955eb5e1SGarrett D'Amore 	notifybiff(NULL); /* initialize biff structures */
169*955eb5e1SGarrett D'Amore 
170*955eb5e1SGarrett D'Amore 	/*
171*955eb5e1SGarrett D'Amore 	 * We expect sendmail will invoke us with saved id 0
172*955eb5e1SGarrett D'Amore 	 * We then do setgid and setuid defore delivery
173*955eb5e1SGarrett D'Amore 	 * setgid to mail group
174*955eb5e1SGarrett D'Amore 	 */
175*955eb5e1SGarrett D'Amore 	if ((grpptr = getgrnam("mail")) != NULL)
176*955eb5e1SGarrett D'Amore 		(void) setgid(grpptr->gr_gid);
177*955eb5e1SGarrett D'Amore 	saved_uid = geteuid();
178*955eb5e1SGarrett D'Amore 
179*955eb5e1SGarrett D'Amore 	if (lmtpmode) {
180*955eb5e1SGarrett D'Amore 		if (saved_uid != 0) {
181*955eb5e1SGarrett D'Amore 			warn("only super-user can use -l option");
182*955eb5e1SGarrett D'Amore 			exit(EX_CANTCREAT);
183*955eb5e1SGarrett D'Amore 		}
184*955eb5e1SGarrett D'Amore 		dolmtp(bouncequota);
185*955eb5e1SGarrett D'Amore 	}
186*955eb5e1SGarrett D'Amore 
187*955eb5e1SGarrett D'Amore 	if (!*argv)
188*955eb5e1SGarrett D'Amore 		usage();
189*955eb5e1SGarrett D'Amore 
190*955eb5e1SGarrett D'Amore 	/*
191*955eb5e1SGarrett D'Amore 	 * If from not specified, use the name from getlogin() if the
192*955eb5e1SGarrett D'Amore 	 * uid matches, otherwise, use the name from the password file
193*955eb5e1SGarrett D'Amore 	 * corresponding to the uid.
194*955eb5e1SGarrett D'Amore 	 */
195*955eb5e1SGarrett D'Amore 	uid = getuid();
196*955eb5e1SGarrett D'Amore 	if (!from && (!(from = getlogin()) ||
197*955eb5e1SGarrett D'Amore 	    !(pw = getpwnam(from)) || pw->pw_uid != uid))
198*955eb5e1SGarrett D'Amore 		from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
199*955eb5e1SGarrett D'Amore 	src_uid = pw ? pw->pw_uid : uid;
200*955eb5e1SGarrett D'Amore 
201*955eb5e1SGarrett D'Amore 	/*
202*955eb5e1SGarrett D'Amore 	 * There is no way to distinguish the error status of one delivery
203*955eb5e1SGarrett D'Amore 	 * from the rest of the deliveries.  So, if we failed hard on one
204*955eb5e1SGarrett D'Amore 	 * or more deliveries, but had no failures on any of the others, we
205*955eb5e1SGarrett D'Amore 	 * return a hard failure.  If we failed temporarily on one or more
206*955eb5e1SGarrett D'Amore 	 * deliveries, we return a temporary failure regardless of the other
207*955eb5e1SGarrett D'Amore 	 * failures.  This results in the delivery being reattempted later
208*955eb5e1SGarrett D'Amore 	 * at the expense of repeated failures and multiple deliveries.
209*955eb5e1SGarrett D'Amore 	 */
210*955eb5e1SGarrett D'Amore 
211*955eb5e1SGarrett D'Amore 	for (store(from, 0); *argv; ++argv)
212*955eb5e1SGarrett D'Amore 		deliver(hfd, bfd, *argv, bouncequota);
213*955eb5e1SGarrett D'Amore 	return (eval);
214*955eb5e1SGarrett D'Amore }
215*955eb5e1SGarrett D'Amore 
216*955eb5e1SGarrett D'Amore void
sigterm_handler()217*955eb5e1SGarrett D'Amore sigterm_handler()
218*955eb5e1SGarrett D'Amore {
219*955eb5e1SGarrett D'Amore 	sigterm_caught = TRUE;
220*955eb5e1SGarrett D'Amore 	(void) sigignore(SIGTERM);
221*955eb5e1SGarrett D'Amore }
222*955eb5e1SGarrett D'Amore 
223*955eb5e1SGarrett D'Amore char *
parseaddr(s)224*955eb5e1SGarrett D'Amore parseaddr(s)
225*955eb5e1SGarrett D'Amore 	char *s;
226*955eb5e1SGarrett D'Amore {
227*955eb5e1SGarrett D'Amore 	char *p;
228*955eb5e1SGarrett D'Amore 	int len;
229*955eb5e1SGarrett D'Amore 
230*955eb5e1SGarrett D'Amore 	if (*s++ != '<')
231*955eb5e1SGarrett D'Amore 		return NULL;
232*955eb5e1SGarrett D'Amore 
233*955eb5e1SGarrett D'Amore 	p = s;
234*955eb5e1SGarrett D'Amore 
235*955eb5e1SGarrett D'Amore 	/* at-domain-list */
236*955eb5e1SGarrett D'Amore 	while (*p == '@') {
237*955eb5e1SGarrett D'Amore 		p++;
238*955eb5e1SGarrett D'Amore 		if (*p == '[') {
239*955eb5e1SGarrett D'Amore 			p++;
240*955eb5e1SGarrett D'Amore 			while (isascii(*p) &&
241*955eb5e1SGarrett D'Amore 			       (isalnum(*p) || *p == '.' ||
242*955eb5e1SGarrett D'Amore 				*p == '-' || *p == ':'))
243*955eb5e1SGarrett D'Amore 				p++;
244*955eb5e1SGarrett D'Amore 			if (*p++ != ']')
245*955eb5e1SGarrett D'Amore 				return NULL;
246*955eb5e1SGarrett D'Amore 		} else {
247*955eb5e1SGarrett D'Amore 			while ((isascii(*p) && isalnum(*p)) ||
248*955eb5e1SGarrett D'Amore 			       strchr(".-_", *p))
249*955eb5e1SGarrett D'Amore 				p++;
250*955eb5e1SGarrett D'Amore 		}
251*955eb5e1SGarrett D'Amore 		if (*p == ',' && p[1] == '@')
252*955eb5e1SGarrett D'Amore 			p++;
253*955eb5e1SGarrett D'Amore 		else if (*p == ':' && p[1] != '@')
254*955eb5e1SGarrett D'Amore 			p++;
255*955eb5e1SGarrett D'Amore 		else
256*955eb5e1SGarrett D'Amore 			return NULL;
257*955eb5e1SGarrett D'Amore 	}
258*955eb5e1SGarrett D'Amore 
259*955eb5e1SGarrett D'Amore 	s = p;
260*955eb5e1SGarrett D'Amore 
261*955eb5e1SGarrett D'Amore 	/* local-part */
262*955eb5e1SGarrett D'Amore 	if (*p == '\"') {
263*955eb5e1SGarrett D'Amore 		p++;
264*955eb5e1SGarrett D'Amore 		while (*p && *p != '\"') {
265*955eb5e1SGarrett D'Amore 			if (*p == '\\') {
266*955eb5e1SGarrett D'Amore 				if (!*++p)
267*955eb5e1SGarrett D'Amore 					return NULL;
268*955eb5e1SGarrett D'Amore 			}
269*955eb5e1SGarrett D'Amore 			p++;
270*955eb5e1SGarrett D'Amore 		}
271*955eb5e1SGarrett D'Amore 		if (!*p++)
272*955eb5e1SGarrett D'Amore 			return NULL;
273*955eb5e1SGarrett D'Amore 	} else {
274*955eb5e1SGarrett D'Amore 		while (*p && *p != '@' && *p != '>') {
275*955eb5e1SGarrett D'Amore 			if (*p == '\\') {
276*955eb5e1SGarrett D'Amore 				if (!*++p)
277*955eb5e1SGarrett D'Amore 					return NULL;
278*955eb5e1SGarrett D'Amore 			} else {
279*955eb5e1SGarrett D'Amore 			if (*p <= ' ' || (*p & 128) ||
280*955eb5e1SGarrett D'Amore 			    strchr("<>()[]\\,;:\"", *p))
281*955eb5e1SGarrett D'Amore 				return NULL;
282*955eb5e1SGarrett D'Amore 			}
283*955eb5e1SGarrett D'Amore 			p++;
284*955eb5e1SGarrett D'Amore 		}
285*955eb5e1SGarrett D'Amore 	}
286*955eb5e1SGarrett D'Amore 
287*955eb5e1SGarrett D'Amore 	/* @domain */
288*955eb5e1SGarrett D'Amore 	if (*p == '@') {
289*955eb5e1SGarrett D'Amore 		p++;
290*955eb5e1SGarrett D'Amore 		if (*p == '[') {
291*955eb5e1SGarrett D'Amore 			p++;
292*955eb5e1SGarrett D'Amore 			while (isascii(*p) &&
293*955eb5e1SGarrett D'Amore 			       (isalnum(*p) || *p == '.' ||
294*955eb5e1SGarrett D'Amore 				*p == '-' || *p == ':'))
295*955eb5e1SGarrett D'Amore 				p++;
296*955eb5e1SGarrett D'Amore 			if (*p++ != ']')
297*955eb5e1SGarrett D'Amore 				return NULL;
298*955eb5e1SGarrett D'Amore 		} else {
299*955eb5e1SGarrett D'Amore 			while ((isascii(*p) && isalnum(*p)) ||
300*955eb5e1SGarrett D'Amore 			       strchr(".-_", *p))
301*955eb5e1SGarrett D'Amore 				p++;
302*955eb5e1SGarrett D'Amore 		}
303*955eb5e1SGarrett D'Amore 	}
304*955eb5e1SGarrett D'Amore 
305*955eb5e1SGarrett D'Amore 	if (*p++ != '>')
306*955eb5e1SGarrett D'Amore 		return NULL;
307*955eb5e1SGarrett D'Amore 	if (*p && *p != ' ')
308*955eb5e1SGarrett D'Amore 		return NULL;
309*955eb5e1SGarrett D'Amore 	len = p - s - 1;
310*955eb5e1SGarrett D'Amore 
311*955eb5e1SGarrett D'Amore 	if (*s == '\0' || len <= 0)
312*955eb5e1SGarrett D'Amore 	{
313*955eb5e1SGarrett D'Amore 		s = MAILER_DAEMON;
314*955eb5e1SGarrett D'Amore 		len = strlen(s);
315*955eb5e1SGarrett D'Amore 	}
316*955eb5e1SGarrett D'Amore 
317*955eb5e1SGarrett D'Amore 	p = malloc(len + 1);
318*955eb5e1SGarrett D'Amore 	if (p == NULL) {
319*955eb5e1SGarrett D'Amore 		printf("421 4.3.0 memory exhausted\r\n");
320*955eb5e1SGarrett D'Amore 		exit(EX_TEMPFAIL);
321*955eb5e1SGarrett D'Amore 	}
322*955eb5e1SGarrett D'Amore 
323*955eb5e1SGarrett D'Amore 	strncpy(p, s, len);
324*955eb5e1SGarrett D'Amore 	p[len] = '\0';
325*955eb5e1SGarrett D'Amore 	return p;
326*955eb5e1SGarrett D'Amore }
327*955eb5e1SGarrett D'Amore 
328*955eb5e1SGarrett D'Amore char *
process_recipient(addr)329*955eb5e1SGarrett D'Amore process_recipient(addr)
330*955eb5e1SGarrett D'Amore 	char *addr;
331*955eb5e1SGarrett D'Amore {
332*955eb5e1SGarrett D'Amore 	if (getpwnam(addr) == NULL) {
333*955eb5e1SGarrett D'Amore 		return "550 5.1.1 user unknown";
334*955eb5e1SGarrett D'Amore 	}
335*955eb5e1SGarrett D'Amore 
336*955eb5e1SGarrett D'Amore 	return NULL;
337*955eb5e1SGarrett D'Amore }
338*955eb5e1SGarrett D'Amore 
339*955eb5e1SGarrett D'Amore #define RCPT_GROW	30
340*955eb5e1SGarrett D'Amore 
341*955eb5e1SGarrett D'Amore void
dolmtp(bouncequota)342*955eb5e1SGarrett D'Amore dolmtp(bouncequota)
343*955eb5e1SGarrett D'Amore 	bool bouncequota;
344*955eb5e1SGarrett D'Amore {
345*955eb5e1SGarrett D'Amore 	char *return_path = NULL;
346*955eb5e1SGarrett D'Amore 	char **rcpt_addr = NULL;
347*955eb5e1SGarrett D'Amore 	int rcpt_num = 0;
348*955eb5e1SGarrett D'Amore 	int rcpt_alloc = 0;
349*955eb5e1SGarrett D'Amore 	bool gotlhlo = FALSE;
350*955eb5e1SGarrett D'Amore 	char myhostname[MAXHOSTNAMELEN];
351*955eb5e1SGarrett D'Amore 	char buf[4096];
352*955eb5e1SGarrett D'Amore 	char *err;
353*955eb5e1SGarrett D'Amore 	char *p;
354*955eb5e1SGarrett D'Amore 	int i;
355*955eb5e1SGarrett D'Amore 
356*955eb5e1SGarrett D'Amore 	gethostname(myhostname, sizeof myhostname - 1);
357*955eb5e1SGarrett D'Amore 
358*955eb5e1SGarrett D'Amore 	printf("220 %s LMTP ready\r\n", myhostname);
359*955eb5e1SGarrett D'Amore 	for (;;) {
360*955eb5e1SGarrett D'Amore 		if (sigterm_caught) {
361*955eb5e1SGarrett D'Amore 			for (; rcpt_num > 0; rcpt_num--)
362*955eb5e1SGarrett D'Amore 				printf("451 4.3.0 shutting down\r\n");
363*955eb5e1SGarrett D'Amore 			exit(EX_OK);
364*955eb5e1SGarrett D'Amore 		}
365*955eb5e1SGarrett D'Amore 		fflush(stdout);
366*955eb5e1SGarrett D'Amore 		if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
367*955eb5e1SGarrett D'Amore 			exit(EX_OK);
368*955eb5e1SGarrett D'Amore 		}
369*955eb5e1SGarrett D'Amore 		p = buf + strlen(buf) - 1;
370*955eb5e1SGarrett D'Amore 		if (p >= buf && *p == '\n')
371*955eb5e1SGarrett D'Amore 			*p-- = '\0';
372*955eb5e1SGarrett D'Amore 		if (p >= buf && *p == '\r')
373*955eb5e1SGarrett D'Amore 			*p-- = '\0';
374*955eb5e1SGarrett D'Amore 
375*955eb5e1SGarrett D'Amore 		switch (buf[0]) {
376*955eb5e1SGarrett D'Amore 
377*955eb5e1SGarrett D'Amore 		case 'd':
378*955eb5e1SGarrett D'Amore 		case 'D':
379*955eb5e1SGarrett D'Amore 			if (strcasecmp(buf, "data") == 0) {
380*955eb5e1SGarrett D'Amore 				if (rcpt_num == 0) {
381*955eb5e1SGarrett D'Amore 					printf("503 5.5.1 No recipients\r\n");
382*955eb5e1SGarrett D'Amore 					continue;
383*955eb5e1SGarrett D'Amore 				}
384*955eb5e1SGarrett D'Amore 				store(return_path, rcpt_num);
385*955eb5e1SGarrett D'Amore 				if (bfd == -1 || hfd == -1)
386*955eb5e1SGarrett D'Amore 					continue;
387*955eb5e1SGarrett D'Amore 
388*955eb5e1SGarrett D'Amore 				for (i = 0; i < rcpt_num; i++) {
389*955eb5e1SGarrett D'Amore 					p = strchr(rcpt_addr[i], '+');
390*955eb5e1SGarrett D'Amore 					if (p != NULL)
391*955eb5e1SGarrett D'Amore 						*p++ = '\0';
392*955eb5e1SGarrett D'Amore 					deliver(hfd, bfd, rcpt_addr[i],
393*955eb5e1SGarrett D'Amore 						bouncequota);
394*955eb5e1SGarrett D'Amore 				}
395*955eb5e1SGarrett D'Amore 				close(bfd);
396*955eb5e1SGarrett D'Amore 				close(hfd);
397*955eb5e1SGarrett D'Amore 				goto rset;
398*955eb5e1SGarrett D'Amore 			}
399*955eb5e1SGarrett D'Amore 			goto syntaxerr;
400*955eb5e1SGarrett D'Amore 			/* NOTREACHED */
401*955eb5e1SGarrett D'Amore 			break;
402*955eb5e1SGarrett D'Amore 
403*955eb5e1SGarrett D'Amore 		case 'l':
404*955eb5e1SGarrett D'Amore 		case 'L':
405*955eb5e1SGarrett D'Amore 			if (strncasecmp(buf, "lhlo ", 5) == 0)
406*955eb5e1SGarrett D'Amore 			{
407*955eb5e1SGarrett D'Amore 				/* check for duplicate per RFC 1651 4.2 */
408*955eb5e1SGarrett D'Amore 				if (gotlhlo)
409*955eb5e1SGarrett D'Amore 				{
410*955eb5e1SGarrett D'Amore 					printf("503 %s Duplicate LHLO\r\n",
411*955eb5e1SGarrett D'Amore 					       myhostname);
412*955eb5e1SGarrett D'Amore 					continue;
413*955eb5e1SGarrett D'Amore 				}
414*955eb5e1SGarrett D'Amore 				gotlhlo = TRUE;
415*955eb5e1SGarrett D'Amore 				printf("250-%s\r\n", myhostname);
416*955eb5e1SGarrett D'Amore 				if (EightBitMime)
417*955eb5e1SGarrett D'Amore 					printf("250-8BITMIME\r\n");
418*955eb5e1SGarrett D'Amore 				printf("250-ENHANCEDSTATUSCODES\r\n");
419*955eb5e1SGarrett D'Amore 				printf("250 PIPELINING\r\n");
420*955eb5e1SGarrett D'Amore 				continue;
421*955eb5e1SGarrett D'Amore 			}
422*955eb5e1SGarrett D'Amore 			goto syntaxerr;
423*955eb5e1SGarrett D'Amore 			/* NOTREACHED */
424*955eb5e1SGarrett D'Amore 			break;
425*955eb5e1SGarrett D'Amore 
426*955eb5e1SGarrett D'Amore 		case 'm':
427*955eb5e1SGarrett D'Amore 		case 'M':
428*955eb5e1SGarrett D'Amore 			if (strncasecmp(buf, "mail ", 5) == 0) {
429*955eb5e1SGarrett D'Amore 				if (return_path != NULL) {
430*955eb5e1SGarrett D'Amore 					printf("503 5.5.1 Nested MAIL command\r\n");
431*955eb5e1SGarrett D'Amore 					continue;
432*955eb5e1SGarrett D'Amore 				}
433*955eb5e1SGarrett D'Amore 				if (strncasecmp(buf+5, "from:", 5) != 0 ||
434*955eb5e1SGarrett D'Amore 				    ((return_path = parseaddr(buf+10)) == NULL)) {
435*955eb5e1SGarrett D'Amore 					printf("501 5.5.4 Syntax error in parameters\r\n");
436*955eb5e1SGarrett D'Amore 					continue;
437*955eb5e1SGarrett D'Amore 				}
438*955eb5e1SGarrett D'Amore 				printf("250 2.5.0 ok\r\n");
439*955eb5e1SGarrett D'Amore 				continue;
440*955eb5e1SGarrett D'Amore 			}
441*955eb5e1SGarrett D'Amore 			goto syntaxerr;
442*955eb5e1SGarrett D'Amore 
443*955eb5e1SGarrett D'Amore 		case 'n':
444*955eb5e1SGarrett D'Amore 		case 'N':
445*955eb5e1SGarrett D'Amore 			if (strcasecmp(buf, "noop") == 0) {
446*955eb5e1SGarrett D'Amore 				printf("250 2.0.0 ok\r\n");
447*955eb5e1SGarrett D'Amore 				continue;
448*955eb5e1SGarrett D'Amore 			}
449*955eb5e1SGarrett D'Amore 			goto syntaxerr;
450*955eb5e1SGarrett D'Amore 
451*955eb5e1SGarrett D'Amore 		case 'q':
452*955eb5e1SGarrett D'Amore 		case 'Q':
453*955eb5e1SGarrett D'Amore 			if (strcasecmp(buf, "quit") == 0) {
454*955eb5e1SGarrett D'Amore 				printf("221 2.0.0 bye\r\n");
455*955eb5e1SGarrett D'Amore 				exit(EX_OK);
456*955eb5e1SGarrett D'Amore 			}
457*955eb5e1SGarrett D'Amore 			goto syntaxerr;
458*955eb5e1SGarrett D'Amore 
459*955eb5e1SGarrett D'Amore 		case 'r':
460*955eb5e1SGarrett D'Amore 		case 'R':
461*955eb5e1SGarrett D'Amore 			if (strncasecmp(buf, "rcpt ", 5) == 0) {
462*955eb5e1SGarrett D'Amore 				if (return_path == NULL) {
463*955eb5e1SGarrett D'Amore 					printf("503 5.5.1 Need MAIL command\r\n");
464*955eb5e1SGarrett D'Amore 					continue;
465*955eb5e1SGarrett D'Amore 				}
466*955eb5e1SGarrett D'Amore 				if (rcpt_num >= rcpt_alloc) {
467*955eb5e1SGarrett D'Amore 					rcpt_alloc += RCPT_GROW;
468*955eb5e1SGarrett D'Amore 					rcpt_addr = (char **)
469*955eb5e1SGarrett D'Amore 						realloc((char *)rcpt_addr,
470*955eb5e1SGarrett D'Amore 							rcpt_alloc * sizeof(char **));
471*955eb5e1SGarrett D'Amore 					if (rcpt_addr == NULL) {
472*955eb5e1SGarrett D'Amore 						printf("421 4.3.0 memory exhausted\r\n");
473*955eb5e1SGarrett D'Amore 						exit(EX_TEMPFAIL);
474*955eb5e1SGarrett D'Amore 					}
475*955eb5e1SGarrett D'Amore 				}
476*955eb5e1SGarrett D'Amore 				if (strncasecmp(buf+5, "to:", 3) != 0 ||
477*955eb5e1SGarrett D'Amore 				    ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) {
478*955eb5e1SGarrett D'Amore 					printf("501 5.5.4 Syntax error in parameters\r\n");
479*955eb5e1SGarrett D'Amore 					continue;
480*955eb5e1SGarrett D'Amore 				}
481*955eb5e1SGarrett D'Amore 				if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) {
482*955eb5e1SGarrett D'Amore 					printf("%s\r\n", err);
483*955eb5e1SGarrett D'Amore 					continue;
484*955eb5e1SGarrett D'Amore 				}
485*955eb5e1SGarrett D'Amore 				rcpt_num++;
486*955eb5e1SGarrett D'Amore 				printf("250 2.1.5 ok\r\n");
487*955eb5e1SGarrett D'Amore 				continue;
488*955eb5e1SGarrett D'Amore 			}
489*955eb5e1SGarrett D'Amore 			else if (strcasecmp(buf, "rset") == 0) {
490*955eb5e1SGarrett D'Amore 				printf("250 2.0.0 ok\r\n");
491*955eb5e1SGarrett D'Amore 
492*955eb5e1SGarrett D'Amore   rset:
493*955eb5e1SGarrett D'Amore 				while (rcpt_num > 0) {
494*955eb5e1SGarrett D'Amore 					free(rcpt_addr[--rcpt_num]);
495*955eb5e1SGarrett D'Amore 				}
496*955eb5e1SGarrett D'Amore 				if (return_path != NULL)
497*955eb5e1SGarrett D'Amore 					free(return_path);
498*955eb5e1SGarrett D'Amore 				return_path = NULL;
499*955eb5e1SGarrett D'Amore 				continue;
500*955eb5e1SGarrett D'Amore 			}
501*955eb5e1SGarrett D'Amore 			goto syntaxerr;
502*955eb5e1SGarrett D'Amore 
503*955eb5e1SGarrett D'Amore 		case 'v':
504*955eb5e1SGarrett D'Amore 		case 'V':
505*955eb5e1SGarrett D'Amore 			if (strncasecmp(buf, "vrfy ", 5) == 0) {
506*955eb5e1SGarrett D'Amore 				printf("252 2.3.3 try RCPT to attempt delivery\r\n");
507*955eb5e1SGarrett D'Amore 				continue;
508*955eb5e1SGarrett D'Amore 			}
509*955eb5e1SGarrett D'Amore 			goto syntaxerr;
510*955eb5e1SGarrett D'Amore 
511*955eb5e1SGarrett D'Amore 		default:
512*955eb5e1SGarrett D'Amore   syntaxerr:
513*955eb5e1SGarrett D'Amore 			printf("500 5.5.2 Syntax error\r\n");
514*955eb5e1SGarrett D'Amore 			continue;
515*955eb5e1SGarrett D'Amore 		}
516*955eb5e1SGarrett D'Amore 	}
517*955eb5e1SGarrett D'Amore }
518*955eb5e1SGarrett D'Amore 
519*955eb5e1SGarrett D'Amore static void
store(from,lmtprcpts)520*955eb5e1SGarrett D'Amore store(from, lmtprcpts)
521*955eb5e1SGarrett D'Amore 	char *from;
522*955eb5e1SGarrett D'Amore 	int lmtprcpts;
523*955eb5e1SGarrett D'Amore {
524*955eb5e1SGarrett D'Amore 	FILE *fp = NULL;
525*955eb5e1SGarrett D'Amore 	time_t tval;
526*955eb5e1SGarrett D'Amore 	bool fullline = TRUE;	/* current line is terminated */
527*955eb5e1SGarrett D'Amore 	bool prevfl;		/* previous line was terminated */
528*955eb5e1SGarrett D'Amore 	char line[MAXLINE];
529*955eb5e1SGarrett D'Amore 	FILE *bfp, *hfp;
530*955eb5e1SGarrett D'Amore 	char *btn, *htn;
531*955eb5e1SGarrett D'Amore 	int in_header_section;
532*955eb5e1SGarrett D'Amore 	int newfd;
533*955eb5e1SGarrett D'Amore 
534*955eb5e1SGarrett D'Amore 	bfd = -1;
535*955eb5e1SGarrett D'Amore 	hfd = -1;
536*955eb5e1SGarrett D'Amore 	btn = strdup(_PATH_LOCTMP);
537*955eb5e1SGarrett D'Amore 	if ((bfd = mkstemp(btn)) == -1 || (bfp = fdopen(bfd, "w+")) == NULL) {
538*955eb5e1SGarrett D'Amore 		if (bfd != -1)
539*955eb5e1SGarrett D'Amore 			(void) close(bfd);
540*955eb5e1SGarrett D'Amore 		if (lmtprcpts) {
541*955eb5e1SGarrett D'Amore 			printf("451 4.3.0 unable to open temporary file\r\n");
542*955eb5e1SGarrett D'Amore 			return;
543*955eb5e1SGarrett D'Amore 		} else {
544*955eb5e1SGarrett D'Amore 			mailerr("451 4.3.0", "unable to open temporary file");
545*955eb5e1SGarrett D'Amore 			exit(eval);
546*955eb5e1SGarrett D'Amore 		}
547*955eb5e1SGarrett D'Amore 	}
548*955eb5e1SGarrett D'Amore 	(void) unlink(btn);
549*955eb5e1SGarrett D'Amore 	free(btn);
550*955eb5e1SGarrett D'Amore 
551*955eb5e1SGarrett D'Amore 	if (lmtpmode) {
552*955eb5e1SGarrett D'Amore 		printf("354 go ahead\r\n");
553*955eb5e1SGarrett D'Amore 		fflush(stdout);
554*955eb5e1SGarrett D'Amore 	}
555*955eb5e1SGarrett D'Amore 
556*955eb5e1SGarrett D'Amore 	htn = strdup(_PATH_LOCHTMP);
557*955eb5e1SGarrett D'Amore 	if ((hfd = mkstemp(htn)) == -1 || (hfp = fdopen(hfd, "w+")) == NULL) {
558*955eb5e1SGarrett D'Amore 		if (hfd != -1)
559*955eb5e1SGarrett D'Amore 			(void) close(hfd);
560*955eb5e1SGarrett D'Amore 		e_to_sys(errno);
561*955eb5e1SGarrett D'Amore 		err("unable to open temporary file");
562*955eb5e1SGarrett D'Amore 	}
563*955eb5e1SGarrett D'Amore 	(void) unlink(htn);
564*955eb5e1SGarrett D'Amore 	free(htn);
565*955eb5e1SGarrett D'Amore 
566*955eb5e1SGarrett D'Amore 	in_header_section = TRUE;
567*955eb5e1SGarrett D'Amore 	content_length = 0;
568*955eb5e1SGarrett D'Amore 	fp = hfp;
569*955eb5e1SGarrett D'Amore 
570*955eb5e1SGarrett D'Amore 	line[0] = '\0';
571*955eb5e1SGarrett D'Amore 	while (fgets(line, sizeof(line), stdin) != (char *)NULL)
572*955eb5e1SGarrett D'Amore 	{
573*955eb5e1SGarrett D'Amore 		size_t line_len = 0;
574*955eb5e1SGarrett D'Amore 		int peek;
575*955eb5e1SGarrett D'Amore 
576*955eb5e1SGarrett D'Amore 		prevfl = fullline;	/* preserve state of previous line */
577*955eb5e1SGarrett D'Amore 		while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
578*955eb5e1SGarrett D'Amore 			line_len++;
579*955eb5e1SGarrett D'Amore 		line_len++;
580*955eb5e1SGarrett D'Amore 
581*955eb5e1SGarrett D'Amore 		/* Check for dot-stuffing */
582*955eb5e1SGarrett D'Amore 		if (prevfl && lmtprcpts && line[0] == '.')
583*955eb5e1SGarrett D'Amore 		{
584*955eb5e1SGarrett D'Amore 			if (line[1] == '\n' ||
585*955eb5e1SGarrett D'Amore 			    (line[1] == '\r' && line[2] == '\n'))
586*955eb5e1SGarrett D'Amore 				goto lmtpdot;
587*955eb5e1SGarrett D'Amore 			memcpy(line, line + 1, line_len);
588*955eb5e1SGarrett D'Amore 			line_len--;
589*955eb5e1SGarrett D'Amore 		}
590*955eb5e1SGarrett D'Amore 
591*955eb5e1SGarrett D'Amore 		/* Check to see if we have the full line from fgets() */
592*955eb5e1SGarrett D'Amore 		fullline = FALSE;
593*955eb5e1SGarrett D'Amore 		if (line_len > 0)
594*955eb5e1SGarrett D'Amore 		{
595*955eb5e1SGarrett D'Amore 			if (line[line_len - 1] == '\n')
596*955eb5e1SGarrett D'Amore 			{
597*955eb5e1SGarrett D'Amore 				if (line_len >= 2 &&
598*955eb5e1SGarrett D'Amore 				    line[line_len - 2] == '\r')
599*955eb5e1SGarrett D'Amore 				{
600*955eb5e1SGarrett D'Amore 					line[line_len - 2] = '\n';
601*955eb5e1SGarrett D'Amore 					line[line_len - 1] = '\0';
602*955eb5e1SGarrett D'Amore 					line_len--;
603*955eb5e1SGarrett D'Amore 				}
604*955eb5e1SGarrett D'Amore 				fullline = TRUE;
605*955eb5e1SGarrett D'Amore 			}
606*955eb5e1SGarrett D'Amore 			else if (line[line_len - 1] == '\r')
607*955eb5e1SGarrett D'Amore 			{
608*955eb5e1SGarrett D'Amore 				/* Did we just miss the CRLF? */
609*955eb5e1SGarrett D'Amore 				peek = fgetc(stdin);
610*955eb5e1SGarrett D'Amore 				if (peek == '\n')
611*955eb5e1SGarrett D'Amore 				{
612*955eb5e1SGarrett D'Amore 					line[line_len - 1] = '\n';
613*955eb5e1SGarrett D'Amore 					fullline = TRUE;
614*955eb5e1SGarrett D'Amore 				}
615*955eb5e1SGarrett D'Amore 				else
616*955eb5e1SGarrett D'Amore 					(void) ungetc(peek, stdin);
617*955eb5e1SGarrett D'Amore 			}
618*955eb5e1SGarrett D'Amore 		}
619*955eb5e1SGarrett D'Amore 		else
620*955eb5e1SGarrett D'Amore 			fullline = TRUE;
621*955eb5e1SGarrett D'Amore 
622*955eb5e1SGarrett D'Amore 		if (prevfl && line[0] == '\n' && in_header_section) {
623*955eb5e1SGarrett D'Amore 			in_header_section = FALSE;
624*955eb5e1SGarrett D'Amore 			if (fflush(fp) == EOF || ferror(fp)) {
625*955eb5e1SGarrett D'Amore 				if (lmtprcpts) {
626*955eb5e1SGarrett D'Amore 					while (lmtprcpts--)
627*955eb5e1SGarrett D'Amore 						printf("451 4.3.0 temporary file write error\r\n");
628*955eb5e1SGarrett D'Amore 					fclose(fp);
629*955eb5e1SGarrett D'Amore 					return;
630*955eb5e1SGarrett D'Amore 				} else {
631*955eb5e1SGarrett D'Amore 					mailerr("451 4.3.0",
632*955eb5e1SGarrett D'Amore 						"temporary file write error");
633*955eb5e1SGarrett D'Amore 					fclose(fp);
634*955eb5e1SGarrett D'Amore 					exit(eval);
635*955eb5e1SGarrett D'Amore 				}
636*955eb5e1SGarrett D'Amore 			}
637*955eb5e1SGarrett D'Amore 			fp = bfp;
638*955eb5e1SGarrett D'Amore 			continue;
639*955eb5e1SGarrett D'Amore 		}
640*955eb5e1SGarrett D'Amore 
641*955eb5e1SGarrett D'Amore 		if (in_header_section) {
642*955eb5e1SGarrett D'Amore 			if (strncasecmp("Content-Length:", line, 15) == 0) {
643*955eb5e1SGarrett D'Amore 				continue; /* skip this header */
644*955eb5e1SGarrett D'Amore 			}
645*955eb5e1SGarrett D'Amore 		} else
646*955eb5e1SGarrett D'Amore 			content_length += strlen(line);
647*955eb5e1SGarrett D'Amore 		(void) fwrite(line, sizeof(char), line_len, fp);
648*955eb5e1SGarrett D'Amore 		if (ferror(fp)) {
649*955eb5e1SGarrett D'Amore 			if (lmtprcpts) {
650*955eb5e1SGarrett D'Amore 				while (lmtprcpts--)
651*955eb5e1SGarrett D'Amore 					printf("451 4.3.0 temporary file write error\r\n");
652*955eb5e1SGarrett D'Amore 				fclose(fp);
653*955eb5e1SGarrett D'Amore 				return;
654*955eb5e1SGarrett D'Amore 			} else {
655*955eb5e1SGarrett D'Amore 				mailerr("451 4.3.0",
656*955eb5e1SGarrett D'Amore 					"temporary file write error");
657*955eb5e1SGarrett D'Amore 				fclose(fp);
658*955eb5e1SGarrett D'Amore 				exit(eval);
659*955eb5e1SGarrett D'Amore 			}
660*955eb5e1SGarrett D'Amore 		}
661*955eb5e1SGarrett D'Amore 	}
662*955eb5e1SGarrett D'Amore 	if (sigterm_caught) {
663*955eb5e1SGarrett D'Amore 		if (lmtprcpts)
664*955eb5e1SGarrett D'Amore 			while (lmtprcpts--)
665*955eb5e1SGarrett D'Amore 				printf("451 4.3.0 shutting down\r\n");
666*955eb5e1SGarrett D'Amore 		else
667*955eb5e1SGarrett D'Amore 			mailerr("451 4.3.0", "shutting down");
668*955eb5e1SGarrett D'Amore 		fclose(fp);
669*955eb5e1SGarrett D'Amore 		exit(eval);
670*955eb5e1SGarrett D'Amore 	}
671*955eb5e1SGarrett D'Amore 
672*955eb5e1SGarrett D'Amore 	if (lmtprcpts) {
673*955eb5e1SGarrett D'Amore 		/* Got a premature EOF -- toss message and exit */
674*955eb5e1SGarrett D'Amore 		exit(EX_OK);
675*955eb5e1SGarrett D'Amore 	}
676*955eb5e1SGarrett D'Amore 
677*955eb5e1SGarrett D'Amore 	/* If message not newline terminated, need an extra. */
678*955eb5e1SGarrett D'Amore 	if (!strchr(line, '\n')) {
679*955eb5e1SGarrett D'Amore 		(void) putc('\n', fp);
680*955eb5e1SGarrett D'Amore 		content_length++;
681*955eb5e1SGarrett D'Amore 	}
682*955eb5e1SGarrett D'Amore 
683*955eb5e1SGarrett D'Amore   lmtpdot:
684*955eb5e1SGarrett D'Amore 
685*955eb5e1SGarrett D'Amore 	/* Output a newline; note, empty messages are allowed. */
686*955eb5e1SGarrett D'Amore 	(void) putc('\n', fp);
687*955eb5e1SGarrett D'Amore 
688*955eb5e1SGarrett D'Amore 	if (fflush(fp) == EOF || ferror(fp)) {
689*955eb5e1SGarrett D'Amore 		if (lmtprcpts) {
690*955eb5e1SGarrett D'Amore 			while (lmtprcpts--) {
691*955eb5e1SGarrett D'Amore 				printf("451 4.3.0 temporary file write error\r\n");
692*955eb5e1SGarrett D'Amore 			}
693*955eb5e1SGarrett D'Amore 			fclose(fp);
694*955eb5e1SGarrett D'Amore 			return;
695*955eb5e1SGarrett D'Amore 		} else {
696*955eb5e1SGarrett D'Amore 			mailerr("451 4.3.0", "temporary file write error");
697*955eb5e1SGarrett D'Amore 			fclose(fp);
698*955eb5e1SGarrett D'Amore 			exit(eval);
699*955eb5e1SGarrett D'Amore 		}
700*955eb5e1SGarrett D'Amore 	}
701*955eb5e1SGarrett D'Amore 
702*955eb5e1SGarrett D'Amore 	if ((newfd = dup(bfd)) >= 0) {
703*955eb5e1SGarrett D'Amore 		fclose(bfp);
704*955eb5e1SGarrett D'Amore 		bfd = newfd;
705*955eb5e1SGarrett D'Amore 	}
706*955eb5e1SGarrett D'Amore 	if ((newfd = dup(hfd)) >= 0) {
707*955eb5e1SGarrett D'Amore 		fclose(hfp);
708*955eb5e1SGarrett D'Amore 		hfd = newfd;
709*955eb5e1SGarrett D'Amore 	}
710*955eb5e1SGarrett D'Amore 	(void) time(&tval);
711*955eb5e1SGarrett D'Amore 	(void) snprintf(unix_from_line, sizeof (unix_from_line), "From %s %s",
712*955eb5e1SGarrett D'Amore 	    from, ctime(&tval));
713*955eb5e1SGarrett D'Amore 	ulen = strlen(unix_from_line);
714*955eb5e1SGarrett D'Amore }
715*955eb5e1SGarrett D'Amore 
716*955eb5e1SGarrett D'Amore static void
handle_error(err_num,bouncequota,path)717*955eb5e1SGarrett D'Amore handle_error(err_num, bouncequota, path)
718*955eb5e1SGarrett D'Amore 	int err_num;
719*955eb5e1SGarrett D'Amore 	bool bouncequota;
720*955eb5e1SGarrett D'Amore 	char *path;
721*955eb5e1SGarrett D'Amore {
722*955eb5e1SGarrett D'Amore #ifdef EDQUOT
723*955eb5e1SGarrett D'Amore 	if (err_num == EDQUOT && bouncequota) {
724*955eb5e1SGarrett D'Amore 		mailerr("552 5.2.2", "%s: %s", path, sm_errstring(err_num));
725*955eb5e1SGarrett D'Amore 	} else
726*955eb5e1SGarrett D'Amore #endif /* EDQUOT */
727*955eb5e1SGarrett D'Amore 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(err_num));
728*955eb5e1SGarrett D'Amore }
729*955eb5e1SGarrett D'Amore 
730*955eb5e1SGarrett D'Amore static void
deliver(hfd,bfd,name,bouncequota)731*955eb5e1SGarrett D'Amore deliver(hfd, bfd, name, bouncequota)
732*955eb5e1SGarrett D'Amore 	int hfd;
733*955eb5e1SGarrett D'Amore 	int bfd;
734*955eb5e1SGarrett D'Amore 	char *name;
735*955eb5e1SGarrett D'Amore 	bool bouncequota;
736*955eb5e1SGarrett D'Amore {
737*955eb5e1SGarrett D'Amore 	struct stat fsb, sb;
738*955eb5e1SGarrett D'Amore 	int mbfd = -1, nr, nw = 0, off;
739*955eb5e1SGarrett D'Amore 	char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
740*955eb5e1SGarrett D'Amore 	off_t curoff, cursize;
741*955eb5e1SGarrett D'Amore 	int len;
742*955eb5e1SGarrett D'Amore 	struct passwd *pw = NULL;
743*955eb5e1SGarrett D'Amore 
744*955eb5e1SGarrett D'Amore 	/*
745*955eb5e1SGarrett D'Amore  	* Disallow delivery to unknown names -- special mailboxes
746*955eb5e1SGarrett D'Amore  	* can be handled in the sendmail aliases file.
747*955eb5e1SGarrett D'Amore  	*/
748*955eb5e1SGarrett D'Amore 	if ((pw = getpwnam(name)) == NULL) {
749*955eb5e1SGarrett D'Amore 		eval = EX_TEMPFAIL;
750*955eb5e1SGarrett D'Amore 		mailerr("451 4.3.0", "cannot lookup name: %s", name);
751*955eb5e1SGarrett D'Amore 		return;
752*955eb5e1SGarrett D'Amore 	}
753*955eb5e1SGarrett D'Amore 	endpwent();
754*955eb5e1SGarrett D'Amore 
755*955eb5e1SGarrett D'Amore 	if (sigterm_caught) {
756*955eb5e1SGarrett D'Amore 		mailerr("451 4.3.0", "shutting down");
757*955eb5e1SGarrett D'Amore 		return;
758*955eb5e1SGarrett D'Amore 	}
759*955eb5e1SGarrett D'Amore 
760*955eb5e1SGarrett D'Amore 	/* mailbox may be NFS mounted, seteuid to user */
761*955eb5e1SGarrett D'Amore 	targ_uid = pw->pw_uid;
762*955eb5e1SGarrett D'Amore 	(void) seteuid(targ_uid);
763*955eb5e1SGarrett D'Amore 
764*955eb5e1SGarrett D'Amore 	if ((saved_uid != 0) && (src_uid != targ_uid)) {
765*955eb5e1SGarrett D'Amore 		/*
766*955eb5e1SGarrett D'Amore 		 * If saved_uid == 0 (root), anything is OK; this is
767*955eb5e1SGarrett D'Amore 		 * as it should be.  But to prevent a random user from
768*955eb5e1SGarrett D'Amore 		 * calling "mail.local foo" in an attempt to hijack
769*955eb5e1SGarrett D'Amore 		 * foo's mail-box, make sure src_uid == targ_uid o/w.
770*955eb5e1SGarrett D'Amore 		 */
771*955eb5e1SGarrett D'Amore 		warn("%s: wrong owner (is %d, should be %d)",
772*955eb5e1SGarrett D'Amore 			name, src_uid, targ_uid);
773*955eb5e1SGarrett D'Amore 		eval = EX_CANTCREAT;
774*955eb5e1SGarrett D'Amore 		return;
775*955eb5e1SGarrett D'Amore 	}
776*955eb5e1SGarrett D'Amore 
777*955eb5e1SGarrett D'Amore 	path[0] = '\0';
778*955eb5e1SGarrett D'Amore 	(void) snprintf(path, sizeof (path), "%s/%s", _PATH_MAILDIR, name);
779*955eb5e1SGarrett D'Amore 
780*955eb5e1SGarrett D'Amore 	/*
781*955eb5e1SGarrett D'Amore 	 * If the mailbox is linked or a symlink, fail.  There's an obvious
782*955eb5e1SGarrett D'Amore 	 * race here, that the file was replaced with a symbolic link after
783*955eb5e1SGarrett D'Amore 	 * the lstat returned, but before the open.  We attempt to detect
784*955eb5e1SGarrett D'Amore 	 * this by comparing the original stat information and information
785*955eb5e1SGarrett D'Amore 	 * returned by an fstat of the file descriptor returned by the open.
786*955eb5e1SGarrett D'Amore 	 *
787*955eb5e1SGarrett D'Amore 	 * NB: this is a symptom of a larger problem, that the mail spooling
788*955eb5e1SGarrett D'Amore 	 * directory is writeable by the wrong users.  If that directory is
789*955eb5e1SGarrett D'Amore 	 * writeable, system security is compromised for other reasons, and
790*955eb5e1SGarrett D'Amore 	 * it cannot be fixed here.
791*955eb5e1SGarrett D'Amore 	 *
792*955eb5e1SGarrett D'Amore 	 * If we created the mailbox, set the owner/group.  If that fails,
793*955eb5e1SGarrett D'Amore 	 * just return.  Another process may have already opened it, so we
794*955eb5e1SGarrett D'Amore 	 * can't unlink it.  Historically, binmail set the owner/group at
795*955eb5e1SGarrett D'Amore 	 * each mail delivery.  We no longer do this, assuming that if the
796*955eb5e1SGarrett D'Amore 	 * ownership or permissions were changed there was a reason.
797*955eb5e1SGarrett D'Amore 	 *
798*955eb5e1SGarrett D'Amore 	 * XXX
799*955eb5e1SGarrett D'Amore 	 * open(2) should support flock'ing the file.
800*955eb5e1SGarrett D'Amore 	 */
801*955eb5e1SGarrett D'Amore tryagain:
802*955eb5e1SGarrett D'Amore 	/* should check lock status, but... maillock return no value */
803*955eb5e1SGarrett D'Amore 	maillock(name, 10);
804*955eb5e1SGarrett D'Amore 
805*955eb5e1SGarrett D'Amore 	if (sigterm_caught) {
806*955eb5e1SGarrett D'Amore 		mailerr("451 4.3.0", "shutting down");
807*955eb5e1SGarrett D'Amore 		goto err0;
808*955eb5e1SGarrett D'Amore 	}
809*955eb5e1SGarrett D'Amore 
810*955eb5e1SGarrett D'Amore 	if (lstat(path, &sb)) {
811*955eb5e1SGarrett D'Amore 		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY,
812*955eb5e1SGarrett D'Amore 				S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
813*955eb5e1SGarrett D'Amore 		if (mbfd != -1)
814*955eb5e1SGarrett D'Amore 			(void) fchmod(mbfd, 0660);
815*955eb5e1SGarrett D'Amore 
816*955eb5e1SGarrett D'Amore 
817*955eb5e1SGarrett D'Amore 		if (mbfd == -1) {
818*955eb5e1SGarrett D'Amore 			if (errno == EEXIST) {
819*955eb5e1SGarrett D'Amore 				mailunlock();
820*955eb5e1SGarrett D'Amore 				goto tryagain;
821*955eb5e1SGarrett D'Amore 			}
822*955eb5e1SGarrett D'Amore 		}
823*955eb5e1SGarrett D'Amore 	} else if (sb.st_nlink != 1) {
824*955eb5e1SGarrett D'Amore 		mailerr("550 5.2.0", "%s: too many links", path);
825*955eb5e1SGarrett D'Amore 		goto err0;
826*955eb5e1SGarrett D'Amore 	} else if (!S_ISREG(sb.st_mode)) {
827*955eb5e1SGarrett D'Amore 		mailerr("550 5.2.0", "%s: irregular file", path);
828*955eb5e1SGarrett D'Amore 		goto err0;
829*955eb5e1SGarrett D'Amore 	} else {
830*955eb5e1SGarrett D'Amore 		mbfd = open(path, O_APPEND|O_WRONLY, 0);
831*955eb5e1SGarrett D'Amore 		if (mbfd != -1 &&
832*955eb5e1SGarrett D'Amore 		    (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
833*955eb5e1SGarrett D'Amore 		    S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
834*955eb5e1SGarrett D'Amore 		    sb.st_ino != fsb.st_ino)) {
835*955eb5e1SGarrett D'Amore 			eval = EX_TEMPFAIL;
836*955eb5e1SGarrett D'Amore 			mailerr("550 5.2.0",
837*955eb5e1SGarrett D'Amore 				"%s: fstat: file changed after open", path);
838*955eb5e1SGarrett D'Amore 			goto err1;
839*955eb5e1SGarrett D'Amore 		}
840*955eb5e1SGarrett D'Amore 	}
841*955eb5e1SGarrett D'Amore 
842*955eb5e1SGarrett D'Amore 	if (mbfd == -1) {
843*955eb5e1SGarrett D'Amore 		mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
844*955eb5e1SGarrett D'Amore 		goto err0;
845*955eb5e1SGarrett D'Amore 	}
846*955eb5e1SGarrett D'Amore 
847*955eb5e1SGarrett D'Amore 	if (sigterm_caught) {
848*955eb5e1SGarrett D'Amore 		mailerr("451 4.3.0", "shutting down");
849*955eb5e1SGarrett D'Amore 		goto err0;
850*955eb5e1SGarrett D'Amore 	}
851*955eb5e1SGarrett D'Amore 
852*955eb5e1SGarrett D'Amore 	/* Get the starting offset of the new message for biff. */
853*955eb5e1SGarrett D'Amore 	curoff = lseek(mbfd, (off_t)0, SEEK_END);
854*955eb5e1SGarrett D'Amore 	(void) snprintf(biffmsg, sizeof (biffmsg), "%s@%ld\n", name, curoff);
855*955eb5e1SGarrett D'Amore 
856*955eb5e1SGarrett D'Amore 	/* Copy the message into the file. */
857*955eb5e1SGarrett D'Amore 	if (lseek(hfd, (off_t)0, SEEK_SET) == (off_t)-1) {
858*955eb5e1SGarrett D'Amore 		mailerr("450 4.2.0", "temporary file: %s", strerror(errno));
859*955eb5e1SGarrett D'Amore 		goto err1;
860*955eb5e1SGarrett D'Amore 	}
861*955eb5e1SGarrett D'Amore 	/* Copy the message into the file. */
862*955eb5e1SGarrett D'Amore 	if (lseek(bfd, (off_t)0, SEEK_SET) == (off_t)-1) {
863*955eb5e1SGarrett D'Amore 		mailerr("450 4.2.0", "temporary file: %s", strerror(errno));
864*955eb5e1SGarrett D'Amore 		goto err1;
865*955eb5e1SGarrett D'Amore 	}
866*955eb5e1SGarrett D'Amore 	if ((write(mbfd, unix_from_line, ulen)) != ulen) {
867*955eb5e1SGarrett D'Amore 		handle_error(errno, bouncequota, path);
868*955eb5e1SGarrett D'Amore 		goto err2;
869*955eb5e1SGarrett D'Amore 	}
870*955eb5e1SGarrett D'Amore 
871*955eb5e1SGarrett D'Amore 	if (sigterm_caught) {
872*955eb5e1SGarrett D'Amore 		mailerr("451 4.3.0", "shutting down");
873*955eb5e1SGarrett D'Amore 		goto err2;
874*955eb5e1SGarrett D'Amore 	}
875*955eb5e1SGarrett D'Amore 
876*955eb5e1SGarrett D'Amore 	while ((nr = read(hfd, buf, sizeof (buf))) > 0)
877*955eb5e1SGarrett D'Amore 		for (off = 0; off < nr; nr -= nw, off += nw)
878*955eb5e1SGarrett D'Amore 			if ((nw = write(mbfd, buf + off, nr)) < 0)
879*955eb5e1SGarrett D'Amore 			{
880*955eb5e1SGarrett D'Amore 				handle_error(errno, bouncequota, path);
881*955eb5e1SGarrett D'Amore 				goto err2;
882*955eb5e1SGarrett D'Amore 			}
883*955eb5e1SGarrett D'Amore 	if (nr < 0) {
884*955eb5e1SGarrett D'Amore 		handle_error(errno, bouncequota, path);
885*955eb5e1SGarrett D'Amore 		goto err2;
886*955eb5e1SGarrett D'Amore 	}
887*955eb5e1SGarrett D'Amore 
888*955eb5e1SGarrett D'Amore 	if (sigterm_caught) {
889*955eb5e1SGarrett D'Amore 		mailerr("451 4.3.0", "shutting down");
890*955eb5e1SGarrett D'Amore 		goto err2;
891*955eb5e1SGarrett D'Amore 	}
892*955eb5e1SGarrett D'Amore 
893*955eb5e1SGarrett D'Amore 	(void) snprintf(buf, sizeof (buf), "Content-Length: %d\n\n",
894*955eb5e1SGarrett D'Amore 	    content_length);
895*955eb5e1SGarrett D'Amore 	len = strlen(buf);
896*955eb5e1SGarrett D'Amore 	if (write(mbfd, buf, len) != len) {
897*955eb5e1SGarrett D'Amore 		handle_error(errno, bouncequota, path);
898*955eb5e1SGarrett D'Amore 		goto err2;
899*955eb5e1SGarrett D'Amore 	}
900*955eb5e1SGarrett D'Amore 
901*955eb5e1SGarrett D'Amore 	if (sigterm_caught) {
902*955eb5e1SGarrett D'Amore 		mailerr("451 4.3.0", "shutting down");
903*955eb5e1SGarrett D'Amore 		goto err2;
904*955eb5e1SGarrett D'Amore 	}
905*955eb5e1SGarrett D'Amore 
906*955eb5e1SGarrett D'Amore 	while ((nr = read(bfd, buf, sizeof (buf))) > 0) {
907*955eb5e1SGarrett D'Amore 		for (off = 0; off < nr; nr -= nw, off += nw)
908*955eb5e1SGarrett D'Amore 			if ((nw = write(mbfd, buf + off, nr)) < 0) {
909*955eb5e1SGarrett D'Amore 				handle_error(errno, bouncequota, path);
910*955eb5e1SGarrett D'Amore 				goto err2;
911*955eb5e1SGarrett D'Amore 			}
912*955eb5e1SGarrett D'Amore 		if (sigterm_caught) {
913*955eb5e1SGarrett D'Amore 			mailerr("451 4.3.0", "shutting down");
914*955eb5e1SGarrett D'Amore 			goto err2;
915*955eb5e1SGarrett D'Amore 		}
916*955eb5e1SGarrett D'Amore 	}
917*955eb5e1SGarrett D'Amore 	if (nr < 0) {
918*955eb5e1SGarrett D'Amore 		handle_error(errno, bouncequota, path);
919*955eb5e1SGarrett D'Amore 		goto err2;
920*955eb5e1SGarrett D'Amore 	}
921*955eb5e1SGarrett D'Amore 
922*955eb5e1SGarrett D'Amore 	/* Flush to disk, don't wait for update. */
923*955eb5e1SGarrett D'Amore 	if (fsync(mbfd)) {
924*955eb5e1SGarrett D'Amore 		handle_error(errno, bouncequota, path);
925*955eb5e1SGarrett D'Amore err2:		if (mbfd >= 0)
926*955eb5e1SGarrett D'Amore 			(void)ftruncate(mbfd, curoff);
927*955eb5e1SGarrett D'Amore err1:		(void)close(mbfd);
928*955eb5e1SGarrett D'Amore err0:		mailunlock();
929*955eb5e1SGarrett D'Amore 		(void)seteuid(saved_uid);
930*955eb5e1SGarrett D'Amore 		return;
931*955eb5e1SGarrett D'Amore 	}
932*955eb5e1SGarrett D'Amore 
933*955eb5e1SGarrett D'Amore 	/*
934*955eb5e1SGarrett D'Amore 	**  Save the current size so if the close() fails below
935*955eb5e1SGarrett D'Amore 	**  we can make sure no other process has changed the mailbox
936*955eb5e1SGarrett D'Amore 	**  between the failed close and the re-open()/re-lock().
937*955eb5e1SGarrett D'Amore 	**  If something else has changed the size, we shouldn't
938*955eb5e1SGarrett D'Amore 	**  try to truncate it as we may do more harm then good
939*955eb5e1SGarrett D'Amore 	**  (e.g., truncate a later message delivery).
940*955eb5e1SGarrett D'Amore 	*/
941*955eb5e1SGarrett D'Amore 
942*955eb5e1SGarrett D'Amore 	if (fstat(mbfd, &sb) < 0)
943*955eb5e1SGarrett D'Amore 		cursize = 0;
944*955eb5e1SGarrett D'Amore 	else
945*955eb5e1SGarrett D'Amore 		cursize = sb.st_size;
946*955eb5e1SGarrett D'Amore 
947*955eb5e1SGarrett D'Amore 	/* Close and check -- NFS doesn't write until the close. */
948*955eb5e1SGarrett D'Amore 	if (close(mbfd))
949*955eb5e1SGarrett D'Amore 	{
950*955eb5e1SGarrett D'Amore 		handle_error(errno, bouncequota, path);
951*955eb5e1SGarrett D'Amore 		mbfd = open(path, O_WRONLY, 0);
952*955eb5e1SGarrett D'Amore 		if (mbfd < 0 ||
953*955eb5e1SGarrett D'Amore 		    cursize == 0
954*955eb5e1SGarrett D'Amore 		    || flock(mbfd, LOCK_EX) < 0 ||
955*955eb5e1SGarrett D'Amore 		    fstat(mbfd, &sb) < 0 ||
956*955eb5e1SGarrett D'Amore 		    sb.st_size != cursize ||
957*955eb5e1SGarrett D'Amore 		    sb.st_nlink != 1 ||
958*955eb5e1SGarrett D'Amore 		    !S_ISREG(sb.st_mode) ||
959*955eb5e1SGarrett D'Amore 		    sb.st_dev != fsb.st_dev ||
960*955eb5e1SGarrett D'Amore 		    sb.st_ino != fsb.st_ino ||
961*955eb5e1SGarrett D'Amore 		    sb.st_uid != fsb.st_uid)
962*955eb5e1SGarrett D'Amore 		{
963*955eb5e1SGarrett D'Amore 			/* Don't use a bogus file */
964*955eb5e1SGarrett D'Amore 			if (mbfd >= 0)
965*955eb5e1SGarrett D'Amore 			{
966*955eb5e1SGarrett D'Amore 				(void) close(mbfd);
967*955eb5e1SGarrett D'Amore 				mbfd = -1;
968*955eb5e1SGarrett D'Amore 			}
969*955eb5e1SGarrett D'Amore 		}
970*955eb5e1SGarrett D'Amore 
971*955eb5e1SGarrett D'Amore 		/* Attempt to truncate back to pre-write size */
972*955eb5e1SGarrett D'Amore 		goto err2;
973*955eb5e1SGarrett D'Amore 	} else
974*955eb5e1SGarrett D'Amore 		notifybiff(biffmsg);
975*955eb5e1SGarrett D'Amore 
976*955eb5e1SGarrett D'Amore 	mailunlock();
977*955eb5e1SGarrett D'Amore 
978*955eb5e1SGarrett D'Amore 	(void)seteuid(saved_uid);
979*955eb5e1SGarrett D'Amore 
980*955eb5e1SGarrett D'Amore 	if (lmtpmode) {
981*955eb5e1SGarrett D'Amore 		printf("250 2.1.5 %s OK\r\n", name);
982*955eb5e1SGarrett D'Amore 	}
983*955eb5e1SGarrett D'Amore }
984*955eb5e1SGarrett D'Amore 
985*955eb5e1SGarrett D'Amore static void
notifybiff(msg)986*955eb5e1SGarrett D'Amore notifybiff(msg)
987*955eb5e1SGarrett D'Amore 	char *msg;
988*955eb5e1SGarrett D'Amore {
989*955eb5e1SGarrett D'Amore 	static struct sockaddr_in addr;
990*955eb5e1SGarrett D'Amore 	static int f = -1;
991*955eb5e1SGarrett D'Amore 	struct hostent *hp;
992*955eb5e1SGarrett D'Amore 	struct servent *sp;
993*955eb5e1SGarrett D'Amore 	int len;
994*955eb5e1SGarrett D'Amore 
995*955eb5e1SGarrett D'Amore 	if (msg == NULL) {
996*955eb5e1SGarrett D'Amore 		/* Be silent if biff service not available. */
997*955eb5e1SGarrett D'Amore 		if ((sp = getservbyname("biff", "udp")) == NULL)
998*955eb5e1SGarrett D'Amore 			return;
999*955eb5e1SGarrett D'Amore 		if ((hp = gethostbyname("localhost")) == NULL) {
1000*955eb5e1SGarrett D'Amore 			warn("localhost: %s", strerror(errno));
1001*955eb5e1SGarrett D'Amore 			return;
1002*955eb5e1SGarrett D'Amore 		}
1003*955eb5e1SGarrett D'Amore 		addr.sin_family = hp->h_addrtype;
1004*955eb5e1SGarrett D'Amore 		(void) memmove(&addr.sin_addr, hp->h_addr, hp->h_length);
1005*955eb5e1SGarrett D'Amore 		addr.sin_port = sp->s_port;
1006*955eb5e1SGarrett D'Amore 		return;
1007*955eb5e1SGarrett D'Amore 	}
1008*955eb5e1SGarrett D'Amore 
1009*955eb5e1SGarrett D'Amore 	if (addr.sin_family == 0)
1010*955eb5e1SGarrett D'Amore 		return; /* did not initialize */
1011*955eb5e1SGarrett D'Amore 
1012*955eb5e1SGarrett D'Amore 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
1013*955eb5e1SGarrett D'Amore 		warn("socket: %s", strerror(errno));
1014*955eb5e1SGarrett D'Amore 		return;
1015*955eb5e1SGarrett D'Amore 	}
1016*955eb5e1SGarrett D'Amore 	len = strlen(msg) + 1;
1017*955eb5e1SGarrett D'Amore 	if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof (addr))
1018*955eb5e1SGarrett D'Amore 	    != len)
1019*955eb5e1SGarrett D'Amore 		warn("sendto biff: %s", strerror(errno));
1020*955eb5e1SGarrett D'Amore }
1021*955eb5e1SGarrett D'Amore 
1022*955eb5e1SGarrett D'Amore static void
usage()1023*955eb5e1SGarrett D'Amore usage()
1024*955eb5e1SGarrett D'Amore {
1025*955eb5e1SGarrett D'Amore 	eval = EX_USAGE;
1026*955eb5e1SGarrett D'Amore 	err("usage: mail.local [-l] [-f from] user ...");
1027*955eb5e1SGarrett D'Amore }
1028*955eb5e1SGarrett D'Amore 
1029*955eb5e1SGarrett D'Amore static void
1030*955eb5e1SGarrett D'Amore /*VARARGS2*/
1031*955eb5e1SGarrett D'Amore #ifdef __STDC__
mailerr(const char * hdr,const char * fmt,...)1032*955eb5e1SGarrett D'Amore mailerr(const char *hdr, const char *fmt, ...)
1033*955eb5e1SGarrett D'Amore #else
1034*955eb5e1SGarrett D'Amore mailerr(hdr, fmt, va_alist)
1035*955eb5e1SGarrett D'Amore 	const char *hdr;
1036*955eb5e1SGarrett D'Amore 	const char *fmt;
1037*955eb5e1SGarrett D'Amore 	va_dcl
1038*955eb5e1SGarrett D'Amore #endif
1039*955eb5e1SGarrett D'Amore {
1040*955eb5e1SGarrett D'Amore 	va_list ap;
1041*955eb5e1SGarrett D'Amore 
1042*955eb5e1SGarrett D'Amore #ifdef __STDC__
1043*955eb5e1SGarrett D'Amore 	va_start(ap, fmt);
1044*955eb5e1SGarrett D'Amore #else
1045*955eb5e1SGarrett D'Amore 	va_start(ap);
1046*955eb5e1SGarrett D'Amore #endif
1047*955eb5e1SGarrett D'Amore 	if (lmtpmode)
1048*955eb5e1SGarrett D'Amore 	{
1049*955eb5e1SGarrett D'Amore 		if (hdr != NULL)
1050*955eb5e1SGarrett D'Amore 			printf("%s ", hdr);
1051*955eb5e1SGarrett D'Amore 		vprintf(fmt, ap);
1052*955eb5e1SGarrett D'Amore 		printf("\r\n");
1053*955eb5e1SGarrett D'Amore 	}
1054*955eb5e1SGarrett D'Amore 	else
1055*955eb5e1SGarrett D'Amore 	{
1056*955eb5e1SGarrett D'Amore 		e_to_sys(errno);
1057*955eb5e1SGarrett D'Amore 		vwarn(fmt, ap);
1058*955eb5e1SGarrett D'Amore 	}
1059*955eb5e1SGarrett D'Amore }
1060*955eb5e1SGarrett D'Amore 
1061*955eb5e1SGarrett D'Amore static void
1062*955eb5e1SGarrett D'Amore /*VARARGS1*/
1063*955eb5e1SGarrett D'Amore #ifdef __STDC__
err(const char * fmt,...)1064*955eb5e1SGarrett D'Amore err(const char *fmt, ...)
1065*955eb5e1SGarrett D'Amore #else
1066*955eb5e1SGarrett D'Amore err(fmt, va_alist)
1067*955eb5e1SGarrett D'Amore 	const char *fmt;
1068*955eb5e1SGarrett D'Amore 	va_dcl
1069*955eb5e1SGarrett D'Amore #endif
1070*955eb5e1SGarrett D'Amore {
1071*955eb5e1SGarrett D'Amore 	va_list ap;
1072*955eb5e1SGarrett D'Amore 
1073*955eb5e1SGarrett D'Amore #ifdef __STDC__
1074*955eb5e1SGarrett D'Amore 	va_start(ap, fmt);
1075*955eb5e1SGarrett D'Amore #else
1076*955eb5e1SGarrett D'Amore 	va_start(ap);
1077*955eb5e1SGarrett D'Amore #endif
1078*955eb5e1SGarrett D'Amore 	vwarn(fmt, ap);
1079*955eb5e1SGarrett D'Amore 	va_end(ap);
1080*955eb5e1SGarrett D'Amore 
1081*955eb5e1SGarrett D'Amore 	exit(eval);
1082*955eb5e1SGarrett D'Amore }
1083*955eb5e1SGarrett D'Amore 
1084*955eb5e1SGarrett D'Amore static void
1085*955eb5e1SGarrett D'Amore /*VARARGS1*/
1086*955eb5e1SGarrett D'Amore #ifdef __STDC__
warn(const char * fmt,...)1087*955eb5e1SGarrett D'Amore warn(const char *fmt, ...)
1088*955eb5e1SGarrett D'Amore #else
1089*955eb5e1SGarrett D'Amore warn(fmt, va_alist)
1090*955eb5e1SGarrett D'Amore 	const char *fmt;
1091*955eb5e1SGarrett D'Amore 	va_dcl
1092*955eb5e1SGarrett D'Amore #endif
1093*955eb5e1SGarrett D'Amore {
1094*955eb5e1SGarrett D'Amore 	va_list ap;
1095*955eb5e1SGarrett D'Amore 
1096*955eb5e1SGarrett D'Amore #ifdef __STDC__
1097*955eb5e1SGarrett D'Amore 	va_start(ap, fmt);
1098*955eb5e1SGarrett D'Amore #else
1099*955eb5e1SGarrett D'Amore 	va_start(ap);
1100*955eb5e1SGarrett D'Amore #endif
1101*955eb5e1SGarrett D'Amore 	vwarn(fmt, ap);
1102*955eb5e1SGarrett D'Amore 	va_end(ap);
1103*955eb5e1SGarrett D'Amore }
1104*955eb5e1SGarrett D'Amore 
1105*955eb5e1SGarrett D'Amore static void
vwarn(fmt,ap)1106*955eb5e1SGarrett D'Amore vwarn(fmt, ap)
1107*955eb5e1SGarrett D'Amore 	const char *fmt;
1108*955eb5e1SGarrett D'Amore 	va_list ap;
1109*955eb5e1SGarrett D'Amore {
1110*955eb5e1SGarrett D'Amore 	/*
1111*955eb5e1SGarrett D'Amore 	 * Log the message to stderr.
1112*955eb5e1SGarrett D'Amore 	 *
1113*955eb5e1SGarrett D'Amore 	 * Don't use LOG_PERROR as an openlog() flag to do this,
1114*955eb5e1SGarrett D'Amore 	 * it's not portable enough.
1115*955eb5e1SGarrett D'Amore 	 */
1116*955eb5e1SGarrett D'Amore 	if (eval != EX_USAGE)
1117*955eb5e1SGarrett D'Amore 		(void) fprintf(stderr, "mail.local: ");
1118*955eb5e1SGarrett D'Amore 	(void) vfprintf(stderr, fmt, ap);
1119*955eb5e1SGarrett D'Amore 	(void) fprintf(stderr, "\n");
1120*955eb5e1SGarrett D'Amore 
1121*955eb5e1SGarrett D'Amore 	/* Log the message to syslog. */
1122*955eb5e1SGarrett D'Amore 	vsyslog(LOG_ERR, fmt, ap);
1123*955eb5e1SGarrett D'Amore }
1124*955eb5e1SGarrett D'Amore 
1125*955eb5e1SGarrett D'Amore /*
1126*955eb5e1SGarrett D'Amore  * e_to_sys --
1127*955eb5e1SGarrett D'Amore  *	Guess which errno's are temporary.  Gag me.
1128*955eb5e1SGarrett D'Amore  */
1129*955eb5e1SGarrett D'Amore static void
e_to_sys(num)1130*955eb5e1SGarrett D'Amore e_to_sys(num)
1131*955eb5e1SGarrett D'Amore 	int num;
1132*955eb5e1SGarrett D'Amore {
1133*955eb5e1SGarrett D'Amore 	/* Temporary failures override hard errors. */
1134*955eb5e1SGarrett D'Amore 	if (eval == EX_TEMPFAIL)
1135*955eb5e1SGarrett D'Amore 		return;
1136*955eb5e1SGarrett D'Amore 
1137*955eb5e1SGarrett D'Amore 	switch (num)		/* Hopefully temporary errors. */
1138*955eb5e1SGarrett D'Amore 	{
1139*955eb5e1SGarrett D'Amore #ifdef EDQUOT
1140*955eb5e1SGarrett D'Amore 	case EDQUOT:		/* Disc quota exceeded */
1141*955eb5e1SGarrett D'Amore 		if (bouncequota)
1142*955eb5e1SGarrett D'Amore 		{
1143*955eb5e1SGarrett D'Amore 			eval = EX_UNAVAILABLE;
1144*955eb5e1SGarrett D'Amore 			break;
1145*955eb5e1SGarrett D'Amore 		}
1146*955eb5e1SGarrett D'Amore #endif /* EDQUOT */
1147*955eb5e1SGarrett D'Amore #ifdef EAGAIN
1148*955eb5e1SGarrett D'Amore 		/* FALLTHROUGH */
1149*955eb5e1SGarrett D'Amore 	case EAGAIN:		/* Resource temporarily unavailable */
1150*955eb5e1SGarrett D'Amore #endif
1151*955eb5e1SGarrett D'Amore #ifdef EBUSY
1152*955eb5e1SGarrett D'Amore 	case EBUSY:		/* Device busy */
1153*955eb5e1SGarrett D'Amore #endif
1154*955eb5e1SGarrett D'Amore #ifdef EPROCLIM
1155*955eb5e1SGarrett D'Amore 	case EPROCLIM:		/* Too many processes */
1156*955eb5e1SGarrett D'Amore #endif
1157*955eb5e1SGarrett D'Amore #ifdef EUSERS
1158*955eb5e1SGarrett D'Amore 	case EUSERS:		/* Too many users */
1159*955eb5e1SGarrett D'Amore #endif
1160*955eb5e1SGarrett D'Amore #ifdef ECONNABORTED
1161*955eb5e1SGarrett D'Amore 	case ECONNABORTED:	/* Software caused connection abort */
1162*955eb5e1SGarrett D'Amore #endif
1163*955eb5e1SGarrett D'Amore #ifdef ECONNREFUSED
1164*955eb5e1SGarrett D'Amore 	case ECONNREFUSED:	/* Connection refused */
1165*955eb5e1SGarrett D'Amore #endif
1166*955eb5e1SGarrett D'Amore #ifdef ECONNRESET
1167*955eb5e1SGarrett D'Amore 	case ECONNRESET:	/* Connection reset by peer */
1168*955eb5e1SGarrett D'Amore #endif
1169*955eb5e1SGarrett D'Amore #ifdef EDEADLK
1170*955eb5e1SGarrett D'Amore 	case EDEADLK:		/* Resource deadlock avoided */
1171*955eb5e1SGarrett D'Amore #endif
1172*955eb5e1SGarrett D'Amore #ifdef EFBIG
1173*955eb5e1SGarrett D'Amore 	case EFBIG:		/* File too large */
1174*955eb5e1SGarrett D'Amore #endif
1175*955eb5e1SGarrett D'Amore #ifdef EHOSTDOWN
1176*955eb5e1SGarrett D'Amore 	case EHOSTDOWN:		/* Host is down */
1177*955eb5e1SGarrett D'Amore #endif
1178*955eb5e1SGarrett D'Amore #ifdef EHOSTUNREACH
1179*955eb5e1SGarrett D'Amore 	case EHOSTUNREACH:	/* No route to host */
1180*955eb5e1SGarrett D'Amore #endif
1181*955eb5e1SGarrett D'Amore #ifdef EMFILE
1182*955eb5e1SGarrett D'Amore 	case EMFILE:		/* Too many open files */
1183*955eb5e1SGarrett D'Amore #endif
1184*955eb5e1SGarrett D'Amore #ifdef ENETDOWN
1185*955eb5e1SGarrett D'Amore 	case ENETDOWN:		/* Network is down */
1186*955eb5e1SGarrett D'Amore #endif
1187*955eb5e1SGarrett D'Amore #ifdef ENETRESET
1188*955eb5e1SGarrett D'Amore 	case ENETRESET:		/* Network dropped connection on reset */
1189*955eb5e1SGarrett D'Amore #endif
1190*955eb5e1SGarrett D'Amore #ifdef ENETUNREACH
1191*955eb5e1SGarrett D'Amore 	case ENETUNREACH:	/* Network is unreachable */
1192*955eb5e1SGarrett D'Amore #endif
1193*955eb5e1SGarrett D'Amore #ifdef ENFILE
1194*955eb5e1SGarrett D'Amore 	case ENFILE:		/* Too many open files in system */
1195*955eb5e1SGarrett D'Amore #endif
1196*955eb5e1SGarrett D'Amore #ifdef ENOBUFS
1197*955eb5e1SGarrett D'Amore 	case ENOBUFS:		/* No buffer space available */
1198*955eb5e1SGarrett D'Amore #endif
1199*955eb5e1SGarrett D'Amore #ifdef ENOMEM
1200*955eb5e1SGarrett D'Amore 	case ENOMEM:		/* Cannot allocate memory */
1201*955eb5e1SGarrett D'Amore #endif
1202*955eb5e1SGarrett D'Amore #ifdef ENOSPC
1203*955eb5e1SGarrett D'Amore 	case ENOSPC:		/* No space left on device */
1204*955eb5e1SGarrett D'Amore #endif
1205*955eb5e1SGarrett D'Amore #ifdef EROFS
1206*955eb5e1SGarrett D'Amore 	case EROFS:		/* Read-only file system */
1207*955eb5e1SGarrett D'Amore #endif
1208*955eb5e1SGarrett D'Amore #ifdef ESTALE
1209*955eb5e1SGarrett D'Amore 	case ESTALE:		/* Stale NFS file handle */
1210*955eb5e1SGarrett D'Amore #endif
1211*955eb5e1SGarrett D'Amore #ifdef ETIMEDOUT
1212*955eb5e1SGarrett D'Amore 	case ETIMEDOUT:		/* Connection timed out */
1213*955eb5e1SGarrett D'Amore #endif
1214*955eb5e1SGarrett D'Amore #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1215*955eb5e1SGarrett D'Amore 	case EWOULDBLOCK:	/* Operation would block. */
1216*955eb5e1SGarrett D'Amore #endif
1217*955eb5e1SGarrett D'Amore 		eval = EX_TEMPFAIL;
1218*955eb5e1SGarrett D'Amore 		break;
1219*955eb5e1SGarrett D'Amore 	default:
1220*955eb5e1SGarrett D'Amore 		eval = EX_UNAVAILABLE;
1221*955eb5e1SGarrett D'Amore 		break;
1222*955eb5e1SGarrett D'Amore 	}
1223*955eb5e1SGarrett D'Amore }
1224