xref: /illumos-gate/usr/src/cmd/sendmail/src/err.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
1  /*
2   * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
3   *	All rights reserved.
4   * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5   * Copyright (c) 1988, 1993
6   *	The Regents of the University of California.  All rights reserved.
7   *
8   * By using this file, you agree to the terms and conditions set
9   * forth in the LICENSE file which can be found at the top level of
10   * the sendmail distribution.
11   *
12   */
13  
14  #include <sendmail.h>
15  
16  SM_RCSID("@(#)$Id: err.c,v 8.196 2006/11/10 23:14:08 ca Exp $")
17  
18  #if LDAPMAP
19  # include <lber.h>
20  # include <ldap.h>			/* for LDAP error codes */
21  #endif /* LDAPMAP */
22  
23  static void	putoutmsg __P((char *, bool, bool));
24  static void	puterrmsg __P((char *));
25  static char	*fmtmsg __P((char *, const char *, const char *, const char *,
26  			     int, const char *, va_list));
27  
28  /*
29  **  FATAL_ERROR -- handle a fatal exception
30  **
31  **	This function is installed as the default exception handler
32  **	in the main sendmail process, and in all child processes
33  **	that we create.  Its job is to handle exceptions that are not
34  **	handled at a lower level.
35  **
36  **	The theory is that unhandled exceptions will be 'fatal' class
37  **	exceptions (with an "F:" prefix), such as the out-of-memory
38  **	exception "F:sm.heap".  As such, they are handled by exiting
39  **	the process in exactly the same way that xalloc() in Sendmail 8.10
40  **	exits the process when it fails due to lack of memory:
41  **	we call syserr with a message beginning with "!".
42  **
43  **	Parameters:
44  **		exc -- exception which is terminating this process
45  **
46  **	Returns:
47  **		none
48  */
49  
50  void
fatal_error(exc)51  fatal_error(exc)
52  	SM_EXC_T *exc;
53  {
54  	static char buf[256];
55  	SM_FILE_T f;
56  
57  	/*
58  	**  This function may be called when the heap is exhausted.
59  	**  The following code writes the message for 'exc' into our
60  	**  static buffer without allocating memory or raising exceptions.
61  	*/
62  
63  	sm_strio_init(&f, buf, sizeof(buf));
64  	sm_exc_write(exc, &f);
65  	(void) sm_io_flush(&f, SM_TIME_DEFAULT);
66  
67  	/*
68  	**  Terminate the process after logging an error and cleaning up.
69  	**  Problems:
70  	**  - syserr decides what class of error this is by looking at errno.
71  	**    That's no good; we should look at the exc structure.
72  	**  - The cleanup code should be moved out of syserr
73  	**    and into individual exception handlers
74  	**    that are part of the module they clean up after.
75  	*/
76  
77  	errno = ENOMEM;
78  	syserr("!%s", buf);
79  }
80  
81  /*
82  **  SYSERR -- Print error message.
83  **
84  **	Prints an error message via sm_io_printf to the diagnostic output.
85  **
86  **	If the first character of the syserr message is `!' it will
87  **	log this as an ALERT message and exit immediately.  This can
88  **	leave queue files in an indeterminate state, so it should not
89  **	be used lightly.
90  **
91  **	If the first character of the syserr message is '!' or '@'
92  **	then syserr knows that the process is about to be terminated,
93  **	so the SMTP reply code defaults to 421.  Otherwise, the
94  **	reply code defaults to 451 or 554, depending on errno.
95  **
96  **	Parameters:
97  **		fmt -- the format string.  An optional '!' or '@',
98  **			followed by an optional three-digit SMTP
99  **			reply code, followed by message text.
100  **		(others) -- parameters
101  **
102  **	Returns:
103  **		none
104  **		Raises E:mta.quickabort if QuickAbort is set.
105  **
106  **	Side Effects:
107  **		increments Errors.
108  **		sets ExitStat.
109  */
110  
111  char		MsgBuf[BUFSIZ*2];	/* text of most recent message */
112  static char	HeldMessageBuf[sizeof(MsgBuf)];	/* for held messages */
113  
114  #if NAMED_BIND && !defined(NO_DATA)
115  # define NO_DATA	NO_ADDRESS
116  #endif /* NAMED_BIND && !defined(NO_DATA) */
117  
118  void
119  /*VARARGS1*/
120  #ifdef __STDC__
syserr(const char * fmt,...)121  syserr(const char *fmt, ...)
122  #else /* __STDC__ */
123  syserr(fmt, va_alist)
124  	const char *fmt;
125  	va_dcl
126  #endif /* __STDC__ */
127  {
128  	register char *p;
129  	int save_errno = errno;
130  	bool panic;
131  	bool exiting;
132  	char *user;
133  	char *enhsc;
134  	char *errtxt;
135  	struct passwd *pw;
136  	char ubuf[80];
137  	SM_VA_LOCAL_DECL
138  
139  	switch (*fmt)
140  	{
141  	  case '!':
142  		++fmt;
143  		panic = true;
144  		exiting = true;
145  		break;
146  	  case '@':
147  		++fmt;
148  		panic = false;
149  		exiting = true;
150  		break;
151  	  default:
152  		panic = false;
153  		exiting = false;
154  		break;
155  	}
156  
157  	/* format and output the error message */
158  	if (exiting)
159  	{
160  		/*
161  		**  Since we are terminating the process,
162  		**  we are aborting the entire SMTP session,
163  		**  rather than just the current transaction.
164  		*/
165  
166  		p = "421";
167  		enhsc = "4.0.0";
168  	}
169  	else if (save_errno == 0)
170  	{
171  		p = "554";
172  		enhsc = "5.0.0";
173  	}
174  	else
175  	{
176  		p = "451";
177  		enhsc = "4.0.0";
178  	}
179  	SM_VA_START(ap, fmt);
180  	errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
181  	SM_VA_END(ap);
182  	puterrmsg(MsgBuf);
183  
184  	/* save this message for mailq printing */
185  	if (!panic && CurEnv != NULL)
186  	{
187  		char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
188  
189  		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
190  			sm_free(CurEnv->e_message);
191  		CurEnv->e_message = nmsg;
192  	}
193  
194  	/* determine exit status if not already set */
195  	if (ExitStat == EX_OK)
196  	{
197  		if (save_errno == 0)
198  			ExitStat = EX_SOFTWARE;
199  		else
200  			ExitStat = EX_OSERR;
201  		if (tTd(54, 1))
202  			sm_dprintf("syserr: ExitStat = %d\n", ExitStat);
203  	}
204  
205  	pw = sm_getpwuid(RealUid);
206  	if (pw != NULL)
207  		user = pw->pw_name;
208  	else
209  	{
210  		user = ubuf;
211  		(void) sm_snprintf(ubuf, sizeof(ubuf), "UID%d", (int) RealUid);
212  	}
213  
214  	if (LogLevel > 0)
215  		sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
216  			  CurEnv == NULL ? NOQID : CurEnv->e_id,
217  			  "SYSERR(%s): %.900s",
218  			  user, errtxt);
219  	switch (save_errno)
220  	{
221  	  case EBADF:
222  	  case ENFILE:
223  	  case EMFILE:
224  	  case ENOTTY:
225  #ifdef EFBIG
226  	  case EFBIG:
227  #endif /* EFBIG */
228  #ifdef ESPIPE
229  	  case ESPIPE:
230  #endif /* ESPIPE */
231  #ifdef EPIPE
232  	  case EPIPE:
233  #endif /* EPIPE */
234  #ifdef ENOBUFS
235  	  case ENOBUFS:
236  #endif /* ENOBUFS */
237  #ifdef ESTALE
238  	  case ESTALE:
239  #endif /* ESTALE */
240  		printopenfds(true);
241  		mci_dump_all(smioout, true);
242  		break;
243  	}
244  	if (panic)
245  	{
246  #if XLA
247  		xla_all_end();
248  #endif /* XLA */
249  		sync_queue_time();
250  		if (tTd(0, 1))
251  			abort();
252  		exit(EX_OSERR);
253  	}
254  	errno = 0;
255  	if (QuickAbort)
256  		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
257  }
258  /*
259  **  USRERR -- Signal user error.
260  **
261  **	This is much like syserr except it is for user errors.
262  **
263  **	Parameters:
264  **		fmt -- the format string.  If it does not begin with
265  **			a three-digit SMTP reply code, 550 is assumed.
266  **		(others) -- sm_io_printf strings
267  **
268  **	Returns:
269  **		none
270  **		Raises E:mta.quickabort if QuickAbort is set.
271  **
272  **	Side Effects:
273  **		increments Errors.
274  */
275  
276  /*VARARGS1*/
277  void
278  #ifdef __STDC__
usrerr(const char * fmt,...)279  usrerr(const char *fmt, ...)
280  #else /* __STDC__ */
281  usrerr(fmt, va_alist)
282  	const char *fmt;
283  	va_dcl
284  #endif /* __STDC__ */
285  {
286  	char *enhsc;
287  	char *errtxt;
288  	SM_VA_LOCAL_DECL
289  
290  	if (fmt[0] == '5' || fmt[0] == '6')
291  		enhsc = "5.0.0";
292  	else if (fmt[0] == '4' || fmt[0] == '8')
293  		enhsc = "4.0.0";
294  	else if (fmt[0] == '2')
295  		enhsc = "2.0.0";
296  	else
297  		enhsc = NULL;
298  	SM_VA_START(ap, fmt);
299  	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
300  	SM_VA_END(ap);
301  
302  	if (SuprErrs)
303  		return;
304  
305  	/* save this message for mailq printing */
306  	switch (MsgBuf[0])
307  	{
308  	  case '4':
309  	  case '8':
310  		if (CurEnv->e_message != NULL)
311  			break;
312  
313  		/* FALLTHROUGH */
314  
315  	  case '5':
316  	  case '6':
317  		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
318  			sm_free(CurEnv->e_message);
319  		if (MsgBuf[0] == '6')
320  		{
321  			char buf[MAXLINE];
322  
323  			(void) sm_snprintf(buf, sizeof(buf),
324  					   "Postmaster warning: %.*s",
325  					   (int) sizeof(buf) - 22, errtxt);
326  			CurEnv->e_message =
327  				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
328  		}
329  		else
330  		{
331  			CurEnv->e_message =
332  				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
333  		}
334  		break;
335  	}
336  
337  	puterrmsg(MsgBuf);
338  	if (LogLevel > 3 && LogUsrErrs)
339  		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
340  	if (QuickAbort)
341  		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
342  }
343  /*
344  **  USRERRENH -- Signal user error.
345  **
346  **	Same as usrerr but with enhanced status code.
347  **
348  **	Parameters:
349  **		enhsc -- the enhanced status code.
350  **		fmt -- the format string.  If it does not begin with
351  **			a three-digit SMTP reply code, 550 is assumed.
352  **		(others) -- sm_io_printf strings
353  **
354  **	Returns:
355  **		none
356  **		Raises E:mta.quickabort if QuickAbort is set.
357  **
358  **	Side Effects:
359  **		increments Errors.
360  */
361  
362  /*VARARGS1*/
363  void
364  #ifdef __STDC__
usrerrenh(char * enhsc,const char * fmt,...)365  usrerrenh(char *enhsc, const char *fmt, ...)
366  #else /* __STDC__ */
367  usrerrenh(enhsc, fmt, va_alist)
368  	char *enhsc;
369  	const char *fmt;
370  	va_dcl
371  #endif /* __STDC__ */
372  {
373  	char *errtxt;
374  	SM_VA_LOCAL_DECL
375  
376  	if (enhsc == NULL || *enhsc == '\0')
377  	{
378  		if (fmt[0] == '5' || fmt[0] == '6')
379  			enhsc = "5.0.0";
380  		else if (fmt[0] == '4' || fmt[0] == '8')
381  			enhsc = "4.0.0";
382  		else if (fmt[0] == '2')
383  			enhsc = "2.0.0";
384  	}
385  	SM_VA_START(ap, fmt);
386  	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
387  	SM_VA_END(ap);
388  
389  	if (SuprErrs)
390  		return;
391  
392  	/* save this message for mailq printing */
393  	switch (MsgBuf[0])
394  	{
395  	  case '4':
396  	  case '8':
397  		if (CurEnv->e_message != NULL)
398  			break;
399  
400  		/* FALLTHROUGH */
401  
402  	  case '5':
403  	  case '6':
404  		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
405  			sm_free(CurEnv->e_message);
406  		if (MsgBuf[0] == '6')
407  		{
408  			char buf[MAXLINE];
409  
410  			(void) sm_snprintf(buf, sizeof(buf),
411  					   "Postmaster warning: %.*s",
412  					   (int) sizeof(buf) - 22, errtxt);
413  			CurEnv->e_message =
414  				sm_rpool_strdup_x(CurEnv->e_rpool, buf);
415  		}
416  		else
417  		{
418  			CurEnv->e_message =
419  				sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
420  		}
421  		break;
422  	}
423  
424  	puterrmsg(MsgBuf);
425  	if (LogLevel > 3 && LogUsrErrs)
426  		sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
427  	if (QuickAbort)
428  		sm_exc_raisenew_x(&EtypeQuickAbort, 1);
429  }
430  /*
431  **  MESSAGE -- print message (not necessarily an error)
432  **
433  **	Parameters:
434  **		msg -- the message (sm_io_printf fmt) -- it can begin with
435  **			an SMTP reply code.  If not, 050 is assumed.
436  **		(others) -- sm_io_printf arguments
437  **
438  **	Returns:
439  **		none
440  **
441  **	Side Effects:
442  **		none.
443  */
444  
445  /*VARARGS1*/
446  void
447  #ifdef __STDC__
message(const char * msg,...)448  message(const char *msg, ...)
449  #else /* __STDC__ */
450  message(msg, va_alist)
451  	const char *msg;
452  	va_dcl
453  #endif /* __STDC__ */
454  {
455  	char *errtxt;
456  	SM_VA_LOCAL_DECL
457  
458  	errno = 0;
459  	SM_VA_START(ap, msg);
460  	errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
461  	SM_VA_END(ap);
462  	putoutmsg(MsgBuf, false, false);
463  
464  	/* save this message for mailq printing */
465  	switch (MsgBuf[0])
466  	{
467  	  case '4':
468  	  case '8':
469  		if (CurEnv->e_message != NULL)
470  			break;
471  		/* FALLTHROUGH */
472  
473  	  case '5':
474  		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
475  			sm_free(CurEnv->e_message);
476  		CurEnv->e_message =
477  			sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
478  		break;
479  	}
480  }
481  /*
482  **  NMESSAGE -- print message (not necessarily an error)
483  **
484  **	Just like "message" except it never puts the to... tag on.
485  **
486  **	Parameters:
487  **		msg -- the message (sm_io_printf fmt) -- if it begins
488  **			with a three digit SMTP reply code, that is used,
489  **			otherwise 050 is assumed.
490  **		(others) -- sm_io_printf arguments
491  **
492  **	Returns:
493  **		none
494  **
495  **	Side Effects:
496  **		none.
497  */
498  
499  /*VARARGS1*/
500  void
501  #ifdef __STDC__
nmessage(const char * msg,...)502  nmessage(const char *msg, ...)
503  #else /* __STDC__ */
504  nmessage(msg, va_alist)
505  	const char *msg;
506  	va_dcl
507  #endif /* __STDC__ */
508  {
509  	char *errtxt;
510  	SM_VA_LOCAL_DECL
511  
512  	errno = 0;
513  	SM_VA_START(ap, msg);
514  	errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
515  			(char *) NULL, 0, msg, ap);
516  	SM_VA_END(ap);
517  	putoutmsg(MsgBuf, false, false);
518  
519  	/* save this message for mailq printing */
520  	switch (MsgBuf[0])
521  	{
522  	  case '4':
523  	  case '8':
524  		if (CurEnv->e_message != NULL)
525  			break;
526  		/* FALLTHROUGH */
527  
528  	  case '5':
529  		if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
530  			sm_free(CurEnv->e_message);
531  		CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
532  		break;
533  	}
534  }
535  /*
536  **  PUTOUTMSG -- output error message to transcript and channel
537  **
538  **	Parameters:
539  **		msg -- message to output (in SMTP format).
540  **		holdmsg -- if true, don't output a copy of the message to
541  **			our output channel.
542  **		heldmsg -- if true, this is a previously held message;
543  **			don't log it to the transcript file.
544  **
545  **	Returns:
546  **		none.
547  **
548  **	Side Effects:
549  **		Outputs msg to the transcript.
550  **		If appropriate, outputs it to the channel.
551  **		Deletes SMTP reply code number as appropriate.
552  */
553  
554  static void
putoutmsg(msg,holdmsg,heldmsg)555  putoutmsg(msg, holdmsg, heldmsg)
556  	char *msg;
557  	bool holdmsg;
558  	bool heldmsg;
559  {
560  	char msgcode = msg[0];
561  	char *errtxt = msg;
562  	char *id;
563  
564  	/* display for debugging */
565  	if (tTd(54, 8))
566  		sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
567  			heldmsg ? " (held)" : "");
568  
569  	/* map warnings to something SMTP can handle */
570  	if (msgcode == '6')
571  		msg[0] = '5';
572  	else if (msgcode == '8')
573  		msg[0] = '4';
574  	id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
575  
576  	/* output to transcript if serious */
577  	if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
578  	    strchr("45", msg[0]) != NULL)
579  		(void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
580  				     msg);
581  
582  	if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
583  		sm_syslog(LOG_INFO, id,
584  			  "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
585  			  heldmsg ? " (held)" : "");
586  
587  	if (msgcode == '8')
588  		msg[0] = '0';
589  
590  	/* output to channel if appropriate */
591  	if (!Verbose && msg[0] == '0')
592  		return;
593  	if (holdmsg)
594  	{
595  		/* save for possible future display */
596  		msg[0] = msgcode;
597  		if (HeldMessageBuf[0] == '5' && msgcode == '4')
598  			return;
599  		(void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf));
600  		return;
601  	}
602  
603  	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
604  
605  	if (OutChannel == NULL)
606  		return;
607  
608  	/* find actual text of error (after SMTP status codes) */
609  	if (ISSMTPREPLY(errtxt))
610  	{
611  		int l;
612  
613  		errtxt += 4;
614  		l = isenhsc(errtxt, ' ');
615  		if (l <= 0)
616  			l = isenhsc(errtxt, '\0');
617  		if (l > 0)
618  			errtxt += l + 1;
619  	}
620  
621  	/* if DisConnected, OutChannel now points to the transcript */
622  	if (!DisConnected &&
623  	    (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
624  		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
625  				     msg);
626  	else
627  		(void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
628  				     errtxt);
629  	if (TrafficLogFile != NULL)
630  		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
631  				     "%05d >>> %s\n", (int) CurrentPid,
632  				     (OpMode == MD_SMTP || OpMode == MD_DAEMON)
633  					? msg : errtxt);
634  #if !PIPELINING
635  	/* XXX can't flush here for SMTP pipelining */
636  	if (msg[3] == ' ')
637  		(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
638  	if (!sm_io_error(OutChannel) || DisConnected)
639  		return;
640  
641  	/*
642  	**  Error on output -- if reporting lost channel, just ignore it.
643  	**  Also, ignore errors from QUIT response (221 message) -- some
644  	**	rude servers don't read result.
645  	*/
646  
647  	if (InChannel == NULL || sm_io_eof(InChannel) ||
648  	    sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
649  		return;
650  
651  	/* can't call syserr, 'cause we are using MsgBuf */
652  	HoldErrs = true;
653  	if (LogLevel > 0)
654  		sm_syslog(LOG_CRIT, id,
655  			  "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
656  			  CURHOSTNAME,
657  			  shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
658  #endif /* !PIPELINING */
659  }
660  /*
661  **  PUTERRMSG -- like putoutmsg, but does special processing for error messages
662  **
663  **	Parameters:
664  **		msg -- the message to output.
665  **
666  **	Returns:
667  **		none.
668  **
669  **	Side Effects:
670  **		Sets the fatal error bit in the envelope as appropriate.
671  */
672  
673  static void
puterrmsg(msg)674  puterrmsg(msg)
675  	char *msg;
676  {
677  	char msgcode = msg[0];
678  
679  	/* output the message as usual */
680  	putoutmsg(msg, HoldErrs, false);
681  
682  	/* be careful about multiple error messages */
683  	if (OnlyOneError)
684  		HoldErrs = true;
685  
686  	/* signal the error */
687  	Errors++;
688  
689  	if (CurEnv == NULL)
690  		return;
691  
692  	if (msgcode == '6')
693  	{
694  		/* notify the postmaster */
695  		CurEnv->e_flags |= EF_PM_NOTIFY;
696  	}
697  	else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
698  	{
699  		/* mark long-term fatal errors */
700  		CurEnv->e_flags |= EF_FATALERRS;
701  	}
702  }
703  /*
704  **  ISENHSC -- check whether a string contains an enhanced status code
705  **
706  **	Parameters:
707  **		s -- string with possible enhanced status code.
708  **		delim -- delim for enhanced status code.
709  **
710  **	Returns:
711  **		0  -- no enhanced status code.
712  **		>4 -- length of enhanced status code.
713  **
714  **	Side Effects:
715  **		none.
716  */
717  int
isenhsc(s,delim)718  isenhsc(s, delim)
719  	const char *s;
720  	int delim;
721  {
722  	int l, h;
723  
724  	if (s == NULL)
725  		return 0;
726  	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
727  		return 0;
728  	h = 0;
729  	l = 2;
730  	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
731  		++h;
732  	if (h == 0 || s[l + h] != '.')
733  		return 0;
734  	l += h + 1;
735  	h = 0;
736  	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
737  		++h;
738  	if (h == 0 || s[l + h] != delim)
739  		return 0;
740  	return l + h;
741  }
742  /*
743  **  EXTENHSC -- check and extract an enhanced status code
744  **
745  **	Parameters:
746  **		s -- string with possible enhanced status code.
747  **		delim -- delim for enhanced status code.
748  **		e -- pointer to storage for enhanced status code.
749  **			must be != NULL and have space for at least
750  **			10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
751  **
752  **	Returns:
753  **		0  -- no enhanced status code.
754  **		>4 -- length of enhanced status code.
755  **
756  **	Side Effects:
757  **		fills e with enhanced status code.
758  */
759  
760  int
extenhsc(s,delim,e)761  extenhsc(s, delim, e)
762  	const char *s;
763  	int delim;
764  	char *e;
765  {
766  	int l, h;
767  
768  	if (s == NULL)
769  		return 0;
770  	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
771  		return 0;
772  	h = 0;
773  	l = 2;
774  	e[0] = s[0];
775  	e[1] = '.';
776  	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
777  	{
778  		e[l + h] = s[l + h];
779  		++h;
780  	}
781  	if (h == 0 || s[l + h] != '.')
782  		return 0;
783  	e[l + h] = '.';
784  	l += h + 1;
785  	h = 0;
786  	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
787  	{
788  		e[l + h] = s[l + h];
789  		++h;
790  	}
791  	if (h == 0 || s[l + h] != delim)
792  		return 0;
793  	e[l + h] = '\0';
794  	return l + h;
795  }
796  /*
797  **  FMTMSG -- format a message into buffer.
798  **
799  **	Parameters:
800  **		eb -- error buffer to get result -- MUST BE MsgBuf.
801  **		to -- the recipient tag for this message.
802  **		num -- default three digit SMTP reply code.
803  **		enhsc -- enhanced status code.
804  **		en -- the error number to display.
805  **		fmt -- format of string.
806  **		ap -- arguments for fmt.
807  **
808  **	Returns:
809  **		pointer to error text beyond status codes.
810  **
811  **	Side Effects:
812  **		none.
813  */
814  
815  static char *
fmtmsg(eb,to,num,enhsc,eno,fmt,ap)816  fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
817  	register char *eb;
818  	const char *to;
819  	const char *num;
820  	const char *enhsc;
821  	int eno;
822  	const char *fmt;
823  	SM_VA_LOCAL_DECL
824  {
825  	char del;
826  	int l;
827  	int spaceleft = sizeof(MsgBuf);
828  	char *errtxt;
829  
830  	/* output the reply code */
831  	if (ISSMTPCODE(fmt))
832  	{
833  		num = fmt;
834  		fmt += 4;
835  	}
836  	if (num[3] == '-')
837  		del = '-';
838  	else
839  		del = ' ';
840  	if (SoftBounce && num[0] == '5')
841  	{
842  		/* replace 5 by 4 */
843  		(void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
844  	}
845  	else
846  		(void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
847  	eb += 4;
848  	spaceleft -= 4;
849  
850  	if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
851  	{
852  		/* copy enh.status code including trailing blank */
853  		l++;
854  		(void) sm_strlcpy(eb, fmt, l + 1);
855  		eb += l;
856  		spaceleft -= l;
857  		fmt += l;
858  	}
859  	else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
860  	{
861  		/* copy enh.status code */
862  		(void) sm_strlcpy(eb, enhsc, l + 1);
863  		eb[l] = ' ';
864  		eb[++l] = '\0';
865  		eb += l;
866  		spaceleft -= l;
867  	}
868  	if (SoftBounce && eb[-l] == '5')
869  	{
870  		/* replace 5 by 4 */
871  		eb[-l] = '4';
872  	}
873  	errtxt = eb;
874  
875  	/* output the file name and line number */
876  	if (FileName != NULL)
877  	{
878  		(void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
879  				   shortenstring(FileName, 83), LineNumber);
880  		eb += (l = strlen(eb));
881  		spaceleft -= l;
882  	}
883  
884  	/*
885  	**  output the "to" address only if it is defined and one of the
886  	**  following codes is used:
887  	**  050 internal notices, e.g., alias expansion
888  	**  250 Ok
889  	**  252 Cannot VRFY user, but will accept message and attempt delivery
890  	**  450 Requested mail action not taken: mailbox unavailable
891  	**  550 Requested action not taken: mailbox unavailable
892  	**  553 Requested action not taken: mailbox name not allowed
893  	**
894  	**  Notice: this still isn't "the right thing", this code shouldn't
895  	**	(indirectly) depend on CurEnv->e_to.
896  	*/
897  
898  	if (to != NULL && to[0] != '\0' &&
899  	    (strncmp(num, "050", 3) == 0 ||
900  	     strncmp(num, "250", 3) == 0 ||
901  	     strncmp(num, "252", 3) == 0 ||
902  	     strncmp(num, "450", 3) == 0 ||
903  	     strncmp(num, "550", 3) == 0 ||
904  	     strncmp(num, "553", 3) == 0))
905  	{
906  		(void) sm_strlcpyn(eb, spaceleft, 2,
907  				   shortenstring(to, MAXSHORTSTR), "... ");
908  		spaceleft -= strlen(eb);
909  		while (*eb != '\0')
910  			*eb++ &= 0177;
911  	}
912  
913  	/* output the message */
914  	(void) sm_vsnprintf(eb, spaceleft, fmt, ap);
915  	spaceleft -= strlen(eb);
916  	while (*eb != '\0')
917  		*eb++ &= 0177;
918  
919  	/* output the error code, if any */
920  	if (eno != 0)
921  		(void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
922  
923  	return errtxt;
924  }
925  /*
926  **  BUFFER_ERRORS -- arrange to buffer future error messages
927  **
928  **	Parameters:
929  **		none
930  **
931  **	Returns:
932  **		none.
933  */
934  
935  void
buffer_errors()936  buffer_errors()
937  {
938  	HeldMessageBuf[0] = '\0';
939  	HoldErrs = true;
940  }
941  /*
942  **  FLUSH_ERRORS -- flush the held error message buffer
943  **
944  **	Parameters:
945  **		print -- if set, print the message, otherwise just
946  **			delete it.
947  **
948  **	Returns:
949  **		none.
950  */
951  
952  void
flush_errors(print)953  flush_errors(print)
954  	bool print;
955  {
956  	if (print && HeldMessageBuf[0] != '\0')
957  		putoutmsg(HeldMessageBuf, false, true);
958  	HeldMessageBuf[0] = '\0';
959  	HoldErrs = false;
960  }
961  /*
962  **  SM_ERRSTRING -- return string description of error code
963  **
964  **	Parameters:
965  **		errnum -- the error number to translate
966  **
967  **	Returns:
968  **		A string description of errnum.
969  **
970  **	Side Effects:
971  **		none.
972  */
973  
974  const char *
sm_errstring(errnum)975  sm_errstring(errnum)
976  	int errnum;
977  {
978  	char *dnsmsg;
979  	char *bp;
980  	static char buf[MAXLINE];
981  #if HASSTRERROR
982  	char *err;
983  	char errbuf[30];
984  #endif /* HASSTRERROR */
985  #if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
986  	extern char *sys_errlist[];
987  	extern int sys_nerr;
988  #endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
989  
990  	/*
991  	**  Handle special network error codes.
992  	**
993  	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
994  	*/
995  
996  	dnsmsg = NULL;
997  	switch (errnum)
998  	{
999  	  case ETIMEDOUT:
1000  	  case ECONNRESET:
1001  		bp = buf;
1002  #if HASSTRERROR
1003  		err = strerror(errnum);
1004  		if (err == NULL)
1005  		{
1006  			(void) sm_snprintf(errbuf, sizeof(errbuf),
1007  					   "Error %d", errnum);
1008  			err = errbuf;
1009  		}
1010  		(void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
1011  #else /* HASSTRERROR */
1012  		if (errnum >= 0 && errnum < sys_nerr)
1013  			(void) sm_strlcpy(bp, sys_errlist[errnum],
1014  					  SPACELEFT(buf, bp));
1015  		else
1016  			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1017  				"Error %d", errnum);
1018  #endif /* HASSTRERROR */
1019  		bp += strlen(bp);
1020  		if (CurHostName != NULL)
1021  		{
1022  			if (errnum == ETIMEDOUT)
1023  			{
1024  				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1025  					" with ");
1026  				bp += strlen(bp);
1027  			}
1028  			else
1029  			{
1030  				bp = buf;
1031  				(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1032  					"Connection reset by ");
1033  				bp += strlen(bp);
1034  			}
1035  			(void) sm_strlcpy(bp,
1036  					shortenstring(CurHostName, MAXSHORTSTR),
1037  					SPACELEFT(buf, bp));
1038  			bp += strlen(buf);
1039  		}
1040  		if (SmtpPhase != NULL)
1041  		{
1042  			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
1043  				" during %s", SmtpPhase);
1044  		}
1045  		return buf;
1046  
1047  	  case EHOSTDOWN:
1048  		if (CurHostName == NULL)
1049  			break;
1050  		(void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
1051  			shortenstring(CurHostName, MAXSHORTSTR));
1052  		return buf;
1053  
1054  	  case ECONNREFUSED:
1055  		if (CurHostName == NULL)
1056  			break;
1057  		(void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
1058  			shortenstring(CurHostName, MAXSHORTSTR));
1059  		return buf;
1060  
1061  #if NAMED_BIND
1062  	  case HOST_NOT_FOUND + E_DNSBASE:
1063  		dnsmsg = "host not found";
1064  		break;
1065  
1066  	  case TRY_AGAIN + E_DNSBASE:
1067  		dnsmsg = "host name lookup failure";
1068  		break;
1069  
1070  	  case NO_RECOVERY + E_DNSBASE:
1071  		dnsmsg = "non-recoverable error";
1072  		break;
1073  
1074  	  case NO_DATA + E_DNSBASE:
1075  		dnsmsg = "no data known";
1076  		break;
1077  #endif /* NAMED_BIND */
1078  
1079  	  case EPERM:
1080  		/* SunOS gives "Not owner" -- this is the POSIX message */
1081  		return "Operation not permitted";
1082  
1083  	/*
1084  	**  Error messages used internally in sendmail.
1085  	*/
1086  
1087  	  case E_SM_OPENTIMEOUT:
1088  		return "Timeout on file open";
1089  
1090  	  case E_SM_NOSLINK:
1091  		return "Symbolic links not allowed";
1092  
1093  	  case E_SM_NOHLINK:
1094  		return "Hard links not allowed";
1095  
1096  	  case E_SM_REGONLY:
1097  		return "Regular files only";
1098  
1099  	  case E_SM_ISEXEC:
1100  		return "Executable files not allowed";
1101  
1102  	  case E_SM_WWDIR:
1103  		return "World writable directory";
1104  
1105  	  case E_SM_GWDIR:
1106  		return "Group writable directory";
1107  
1108  	  case E_SM_FILECHANGE:
1109  		return "File changed after open";
1110  
1111  	  case E_SM_WWFILE:
1112  		return "World writable file";
1113  
1114  	  case E_SM_GWFILE:
1115  		return "Group writable file";
1116  
1117  	  case E_SM_GRFILE:
1118  		return "Group readable file";
1119  
1120  	  case E_SM_WRFILE:
1121  		return "World readable file";
1122  	}
1123  
1124  	if (dnsmsg != NULL)
1125  	{
1126  		bp = buf;
1127  		bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
1128  		if (CurHostName != NULL)
1129  		{
1130  			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
1131  				shortenstring(CurHostName, MAXSHORTSTR), ": ");
1132  			bp += strlen(bp);
1133  		}
1134  		(void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
1135  		return buf;
1136  	}
1137  
1138  #if LDAPMAP
1139  	if (errnum >= E_LDAPBASE)
1140  		return ldap_err2string(errnum - E_LDAPBASE);
1141  #endif /* LDAPMAP */
1142  
1143  #if HASSTRERROR
1144  	err = strerror(errnum);
1145  	if (err == NULL)
1146  	{
1147  		(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1148  		return buf;
1149  	}
1150  	return err;
1151  #else /* HASSTRERROR */
1152  	if (errnum > 0 && errnum < sys_nerr)
1153  		return sys_errlist[errnum];
1154  
1155  	(void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
1156  	return buf;
1157  #endif /* HASSTRERROR */
1158  }
1159