err.c revision 90795
138889Sjdp/* 2218822Sdim * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. 3130561Sobrien * All rights reserved. 438889Sjdp * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5218822Sdim * Copyright (c) 1988, 1993 638889Sjdp * The Regents of the University of California. All rights reserved. 738889Sjdp * 838889Sjdp * By using this file, you agree to the terms and conditions set 938889Sjdp * forth in the LICENSE file which can be found at the top level of 1038889Sjdp * the sendmail distribution. 1138889Sjdp * 1238889Sjdp */ 1338889Sjdp 1438889Sjdp#include <sendmail.h> 1538889Sjdp 1638889SjdpSM_RCSID("@(#)$Id: err.c,v 8.189 2002/01/09 18:52:30 ca Exp $") 1738889Sjdp 1838889Sjdp/* $FreeBSD: head/contrib/sendmail/src/err.c 90795 2002-02-17 21:58:34Z gshapiro $ */ 1938889Sjdp 2038889Sjdp#if LDAPMAP 21218822Sdim# include <lber.h> 22218822Sdim# include <ldap.h> /* for LDAP error codes */ 2338889Sjdp#endif /* LDAPMAP */ 2438889Sjdp 2538889Sjdpstatic void putoutmsg __P((char *, bool, bool)); 2638889Sjdpstatic void puterrmsg __P((char *)); 27218822Sdimstatic char *fmtmsg __P((char *, const char *, const char *, const char *, 2838889Sjdp int, const char *, va_list)); 2938889Sjdp 3038889Sjdp/* 3138889Sjdp** FATAL_ERROR -- handle a fatal exception 3238889Sjdp** 3338889Sjdp** This function is installed as the default exception handler 3438889Sjdp** in the main sendmail process, and in all child processes 3538889Sjdp** that we create. Its job is to handle exceptions that are not 3638889Sjdp** handled at a lower level. 3738889Sjdp** 3838889Sjdp** The theory is that unhandled exceptions will be 'fatal' class 3938889Sjdp** exceptions (with an "F:" prefix), such as the out-of-memory 4038889Sjdp** exception "F:sm.heap". As such, they are handled by exiting 4138889Sjdp** the process in exactly the same way that xalloc() in Sendmail 8.10 4238889Sjdp** exits the process when it fails due to lack of memory: 4338889Sjdp** we call syserr with a message beginning with "!". 4438889Sjdp** 4538889Sjdp** Parameters: 4638889Sjdp** exc -- exception which is terminating this process 4738889Sjdp** 4838889Sjdp** Returns: 4938889Sjdp** none 5038889Sjdp*/ 5138889Sjdp 5238889Sjdpvoid 53218822Sdimfatal_error(exc) 5438889Sjdp SM_EXC_T *exc; 5538889Sjdp{ 5638889Sjdp static char buf[256]; 5738889Sjdp SM_FILE_T f; 58218822Sdim 5938889Sjdp /* 6038889Sjdp ** This function may be called when the heap is exhausted. 6138889Sjdp ** The following code writes the message for 'exc' into our 6238889Sjdp ** static buffer without allocating memory or raising exceptions. 6338889Sjdp */ 6438889Sjdp 6538889Sjdp sm_strio_init(&f, buf, sizeof(buf)); 6638889Sjdp sm_exc_write(exc, &f); 6738889Sjdp (void) sm_io_flush(&f, SM_TIME_DEFAULT); 6838889Sjdp 6938889Sjdp /* 7038889Sjdp ** Terminate the process after logging an error and cleaning up. 7138889Sjdp ** Problems: 7238889Sjdp ** - syserr decides what class of error this is by looking at errno. 7338889Sjdp ** That's no good; we should look at the exc structure. 7438889Sjdp ** - The cleanup code should be moved out of syserr 7538889Sjdp ** and into individual exception handlers 7638889Sjdp ** that are part of the module they clean up after. 7738889Sjdp */ 7838889Sjdp 7938889Sjdp errno = ENOMEM; 8038889Sjdp syserr("!%s", buf); 8138889Sjdp} 8238889Sjdp 8338889Sjdp/* 8438889Sjdp** SYSERR -- Print error message. 8538889Sjdp** 8638889Sjdp** Prints an error message via sm_io_printf to the diagnostic output. 8738889Sjdp** 8838889Sjdp** If the first character of the syserr message is `!' it will 8938889Sjdp** log this as an ALERT message and exit immediately. This can 9038889Sjdp** leave queue files in an indeterminate state, so it should not 9138889Sjdp** be used lightly. 9238889Sjdp** 9338889Sjdp** If the first character of the syserr message is '!' or '@' 9438889Sjdp** then syserr knows that the process is about to be terminated, 9538889Sjdp** so the SMTP reply code defaults to 421. Otherwise, the 9638889Sjdp** reply code defaults to 451 or 554, depending on errno. 9738889Sjdp** 9838889Sjdp** Parameters: 9938889Sjdp** fmt -- the format string. An optional '!' or '@', 10038889Sjdp** followed by an optional three-digit SMTP 101130561Sobrien** reply code, followed by message text. 102218822Sdim** (others) -- parameters 103218822Sdim** 104218822Sdim** Returns: 105218822Sdim** none 106218822Sdim** Raises E:mta.quickabort if QuickAbort is set. 107218822Sdim** 10838889Sjdp** Side Effects: 10938889Sjdp** increments Errors. 11038889Sjdp** sets ExitStat. 111218822Sdim*/ 112130561Sobrien 11338889Sjdpchar MsgBuf[BUFSIZ*2]; /* text of most recent message */ 114218822Sdimstatic char HeldMessageBuf[sizeof MsgBuf]; /* for held messages */ 11538889Sjdp 116218822Sdim#if NAMED_BIND && !defined(NO_DATA) 11738889Sjdp# define NO_DATA NO_ADDRESS 11838889Sjdp#endif /* NAMED_BIND && !defined(NO_DATA) */ 11938889Sjdp 12038889Sjdpvoid 12138889Sjdp/*VARARGS1*/ 12238889Sjdp#ifdef __STDC__ 12338889Sjdpsyserr(const char *fmt, ...) 12460484Sobrien#else /* __STDC__ */ 12538889Sjdpsyserr(fmt, va_alist) 12638889Sjdp const char *fmt; 12738889Sjdp va_dcl 12838889Sjdp#endif /* __STDC__ */ 12938889Sjdp{ 13038889Sjdp register char *p; 13138889Sjdp int save_errno = errno; 13238889Sjdp bool panic; 13338889Sjdp bool exiting; 13438889Sjdp char *user; 13538889Sjdp char *enhsc; 13638889Sjdp char *errtxt; 13738889Sjdp struct passwd *pw; 13838889Sjdp char ubuf[80]; 13938889Sjdp SM_VA_LOCAL_DECL 14038889Sjdp 14160484Sobrien switch (*fmt) 14238889Sjdp { 14338889Sjdp case '!': 144218822Sdim ++fmt; 14538889Sjdp panic = true; 14638889Sjdp exiting = true; 14738889Sjdp break; 148218822Sdim case '@': 14938889Sjdp ++fmt; 15038889Sjdp panic = false; 15138889Sjdp exiting = true; 15238889Sjdp break; 15338889Sjdp default: 15438889Sjdp panic = false; 15538889Sjdp exiting = false; 15638889Sjdp break; 15738889Sjdp } 15838889Sjdp 15938889Sjdp /* format and output the error message */ 16038889Sjdp if (exiting) 161218822Sdim { 162218822Sdim /* 163218822Sdim ** Since we are terminating the process, 164218822Sdim ** we are aborting the entire SMTP session, 165218822Sdim ** rather than just the current transaction. 16638889Sjdp */ 16738889Sjdp 16838889Sjdp p = "421"; 16938889Sjdp enhsc = "4.0.0"; 17038889Sjdp } 171130561Sobrien else if (save_errno == 0) 17238889Sjdp { 17360484Sobrien p = "554"; 17438889Sjdp enhsc = "5.0.0"; 17538889Sjdp } 17638889Sjdp else 17738889Sjdp { 178218822Sdim p = "451"; 179218822Sdim enhsc = "4.0.0"; 180218822Sdim } 181218822Sdim SM_VA_START(ap, fmt); 18238889Sjdp errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap); 18338889Sjdp SM_VA_END(ap); 184218822Sdim puterrmsg(MsgBuf); 18538889Sjdp 186218822Sdim /* save this message for mailq printing */ 18738889Sjdp if (!panic && CurEnv != NULL) 18838889Sjdp { 18960484Sobrien char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 19060484Sobrien 19138889Sjdp if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 19238889Sjdp sm_free(CurEnv->e_message); 19338889Sjdp CurEnv->e_message = nmsg; 194218822Sdim } 195218822Sdim 196218822Sdim /* determine exit status if not already set */ 197218822Sdim if (ExitStat == EX_OK) 198218822Sdim { 19938889Sjdp if (save_errno == 0) 20038889Sjdp ExitStat = EX_SOFTWARE; 201218822Sdim else 202218822Sdim ExitStat = EX_OSERR; 20338889Sjdp if (tTd(54, 1)) 20438889Sjdp sm_dprintf("syserr: ExitStat = %d\n", ExitStat); 20538889Sjdp } 20638889Sjdp 20738889Sjdp pw = sm_getpwuid(RealUid); 20838889Sjdp if (pw != NULL) 20938889Sjdp user = pw->pw_name; 21038889Sjdp else 21138889Sjdp { 212218822Sdim user = ubuf; 213218822Sdim (void) sm_snprintf(ubuf, sizeof ubuf, "UID%d", (int) RealUid); 21438889Sjdp } 21538889Sjdp 21638889Sjdp if (LogLevel > 0) 21738889Sjdp sm_syslog(panic ? LOG_ALERT : LOG_CRIT, 21860484Sobrien CurEnv == NULL ? NOQID : CurEnv->e_id, 21938889Sjdp "SYSERR(%s): %.900s", 220218822Sdim user, errtxt); 221218822Sdim switch (save_errno) 22238889Sjdp { 22338889Sjdp case EBADF: 22438889Sjdp case ENFILE: 22538889Sjdp case EMFILE: 226218822Sdim case ENOTTY: 22760484Sobrien#ifdef EFBIG 22838889Sjdp case EFBIG: 22938889Sjdp#endif /* EFBIG */ 23038889Sjdp#ifdef ESPIPE 231218822Sdim case ESPIPE: 23238889Sjdp#endif /* ESPIPE */ 23338889Sjdp#ifdef EPIPE 234218822Sdim case EPIPE: 23538889Sjdp#endif /* EPIPE */ 23638889Sjdp#ifdef ENOBUFS 23738889Sjdp case ENOBUFS: 238218822Sdim#endif /* ENOBUFS */ 23938889Sjdp#ifdef ESTALE 24038889Sjdp case ESTALE: 24138889Sjdp#endif /* ESTALE */ 24238889Sjdp printopenfds(true); 24338889Sjdp mci_dump_all(true); 24438889Sjdp break; 24538889Sjdp } 246218822Sdim if (panic) 24760484Sobrien { 24838889Sjdp#if XLA 249218822Sdim xla_all_end(); 25038889Sjdp#endif /* XLA */ 25138889Sjdp sync_queue_time(); 25238889Sjdp if (tTd(0, 1)) 25338889Sjdp abort(); 254218822Sdim exit(EX_OSERR); 25560484Sobrien } 25638889Sjdp errno = 0; 257218822Sdim if (QuickAbort) 25838889Sjdp sm_exc_raisenew_x(&EtypeQuickAbort, 2); 25938889Sjdp} 26038889Sjdp/* 26138889Sjdp** USRERR -- Signal user error. 26238889Sjdp** 26338889Sjdp** This is much like syserr except it is for user errors. 26438889Sjdp** 26538889Sjdp** Parameters: 26638889Sjdp** fmt -- the format string. If it does not begin with 267218822Sdim** a three-digit SMTP reply code, 550 is assumed. 26838889Sjdp** (others) -- sm_io_printf strings 26938889Sjdp** 27060484Sobrien** Returns: 27138889Sjdp** none 272218822Sdim** Raises E:mta.quickabort if QuickAbort is set. 273218822Sdim** 27438889Sjdp** Side Effects: 275218822Sdim** increments Errors. 27638889Sjdp*/ 27738889Sjdp 27838889Sjdp/*VARARGS1*/ 27938889Sjdpvoid 28038889Sjdp#ifdef __STDC__ 28138889Sjdpusrerr(const char *fmt, ...) 28238889Sjdp#else /* __STDC__ */ 28338889Sjdpusrerr(fmt, va_alist) 28438889Sjdp const char *fmt; 28538889Sjdp va_dcl 286218822Sdim#endif /* __STDC__ */ 28760484Sobrien{ 28838889Sjdp char *enhsc; 289218822Sdim char *errtxt; 29038889Sjdp SM_VA_LOCAL_DECL 29138889Sjdp 29238889Sjdp if (fmt[0] == '5' || fmt[0] == '6') 29338889Sjdp enhsc = "5.0.0"; 294218822Sdim else if (fmt[0] == '4' || fmt[0] == '8') 29560484Sobrien enhsc = "4.0.0"; 29638889Sjdp else if (fmt[0] == '2') 297218822Sdim enhsc = "2.0.0"; 29838889Sjdp else 29938889Sjdp enhsc = NULL; 30038889Sjdp SM_VA_START(ap, fmt); 30138889Sjdp errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap); 30238889Sjdp SM_VA_END(ap); 30338889Sjdp 30438889Sjdp if (SuprErrs) 30538889Sjdp return; 30638889Sjdp 30738889Sjdp /* save this message for mailq printing */ 30838889Sjdp switch (MsgBuf[0]) 309218822Sdim { 310218822Sdim case '4': 311218822Sdim case '8': 312218822Sdim if (CurEnv->e_message != NULL) 31338889Sjdp break; 31438889Sjdp 315218822Sdim /* FALLTHROUGH */ 316218822Sdim 31738889Sjdp case '5': 31838889Sjdp case '6': 31938889Sjdp if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 32060484Sobrien sm_free(CurEnv->e_message); 32138889Sjdp if (MsgBuf[0] == '6') 32260484Sobrien { 32360484Sobrien char buf[MAXLINE]; 32438889Sjdp 32538889Sjdp (void) sm_snprintf(buf, sizeof buf, 32638889Sjdp "Postmaster warning: %.*s", 327218822Sdim (int) sizeof buf - 22, errtxt); 328218822Sdim CurEnv->e_message = 32938889Sjdp sm_rpool_strdup_x(CurEnv->e_rpool, buf); 330218822Sdim } 33160484Sobrien else 33238889Sjdp { 33338889Sjdp CurEnv->e_message = 33438889Sjdp sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 335218822Sdim } 33660484Sobrien break; 33738889Sjdp } 338218822Sdim 33938889Sjdp puterrmsg(MsgBuf); 340218822Sdim if (LogLevel > 3 && LogUsrErrs) 341218822Sdim sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt); 342218822Sdim if (QuickAbort) 34338889Sjdp sm_exc_raisenew_x(&EtypeQuickAbort, 1); 34438889Sjdp} 34538889Sjdp/* 34638889Sjdp** USRERRENH -- Signal user error. 34738889Sjdp** 34838889Sjdp** Same as usrerr but with enhanced status code. 34938889Sjdp** 35038889Sjdp** Parameters: 35138889Sjdp** enhsc -- the enhanced status code. 352218822Sdim** fmt -- the format string. If it does not begin with 35338889Sjdp** a three-digit SMTP reply code, 550 is assumed. 354218822Sdim** (others) -- sm_io_printf strings 35538889Sjdp** 35638889Sjdp** Returns: 35738889Sjdp** none 35838889Sjdp** Raises E:mta.quickabort if QuickAbort is set. 359218822Sdim** 360218822Sdim** Side Effects: 361218822Sdim** increments Errors. 362218822Sdim*/ 363218822Sdim 364218822Sdim/*VARARGS1*/ 365218822Sdimvoid 366218822Sdim#ifdef __STDC__ 367218822Sdimusrerrenh(char *enhsc, const char *fmt, ...) 368218822Sdim#else /* __STDC__ */ 36938889Sjdpusrerrenh(enhsc, fmt, va_alist) 37038889Sjdp char *enhsc; 37138889Sjdp const char *fmt; 37238889Sjdp va_dcl 37338889Sjdp#endif /* __STDC__ */ 37438889Sjdp{ 37538889Sjdp char *errtxt; 376218822Sdim SM_VA_LOCAL_DECL 37738889Sjdp 37838889Sjdp if (enhsc == NULL || *enhsc == '\0') 37938889Sjdp { 38038889Sjdp if (fmt[0] == '5' || fmt[0] == '6') 38138889Sjdp enhsc = "5.0.0"; 38238889Sjdp else if (fmt[0] == '4' || fmt[0] == '8') 38338889Sjdp enhsc = "4.0.0"; 38438889Sjdp else if (fmt[0] == '2') 38538889Sjdp enhsc = "2.0.0"; 38638889Sjdp } 38738889Sjdp SM_VA_START(ap, fmt); 38838889Sjdp errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap); 38938889Sjdp SM_VA_END(ap); 39038889Sjdp 39138889Sjdp if (SuprErrs) 39238889Sjdp return; 39338889Sjdp 39438889Sjdp /* save this message for mailq printing */ 39538889Sjdp switch (MsgBuf[0]) 396218822Sdim { 39738889Sjdp case '4': 39838889Sjdp case '8': 39938889Sjdp if (CurEnv->e_message != NULL) 40038889Sjdp break; 40138889Sjdp 40238889Sjdp /* FALLTHROUGH */ 403218822Sdim 404218822Sdim case '5': 40538889Sjdp case '6': 406218822Sdim if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 40738889Sjdp sm_free(CurEnv->e_message); 408218822Sdim if (MsgBuf[0] == '6') 40938889Sjdp { 41038889Sjdp char buf[MAXLINE]; 41138889Sjdp 41238889Sjdp (void) sm_snprintf(buf, sizeof buf, 41338889Sjdp "Postmaster warning: %.*s", 41438889Sjdp (int) sizeof buf - 22, errtxt); 41538889Sjdp CurEnv->e_message = 41638889Sjdp sm_rpool_strdup_x(CurEnv->e_rpool, buf); 41738889Sjdp } 41838889Sjdp else 419130561Sobrien { 420218822Sdim CurEnv->e_message = 42138889Sjdp sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 42238889Sjdp } 42338889Sjdp break; 42438889Sjdp } 425218822Sdim 426218822Sdim puterrmsg(MsgBuf); 427218822Sdim if (LogLevel > 3 && LogUsrErrs) 42838889Sjdp sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt); 42938889Sjdp if (QuickAbort) 43038889Sjdp sm_exc_raisenew_x(&EtypeQuickAbort, 1); 43160484Sobrien} 43238889Sjdp/* 43338889Sjdp** MESSAGE -- print message (not necessarily an error) 43438889Sjdp** 43538889Sjdp** Parameters: 43638889Sjdp** msg -- the message (sm_io_printf fmt) -- it can begin with 43738889Sjdp** an SMTP reply code. If not, 050 is assumed. 43838889Sjdp** (others) -- sm_io_printf arguments 43938889Sjdp** 44060484Sobrien** Returns: 44160484Sobrien** none 44260484Sobrien** 44360484Sobrien** Side Effects: 44460484Sobrien** none. 44560484Sobrien*/ 44660484Sobrien 44760484Sobrien/*VARARGS1*/ 44860484Sobrienvoid 44960484Sobrien#ifdef __STDC__ 45038889Sjdpmessage(const char *msg, ...) 45138889Sjdp#else /* __STDC__ */ 45260484Sobrienmessage(msg, va_alist) 45360484Sobrien const char *msg; 45438889Sjdp va_dcl 45538889Sjdp#endif /* __STDC__ */ 45638889Sjdp{ 45738889Sjdp char *errtxt; 45838889Sjdp SM_VA_LOCAL_DECL 45938889Sjdp 46038889Sjdp errno = 0; 46138889Sjdp SM_VA_START(ap, msg); 46238889Sjdp errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap); 46338889Sjdp SM_VA_END(ap); 46438889Sjdp putoutmsg(MsgBuf, false, false); 46538889Sjdp 46638889Sjdp /* save this message for mailq printing */ 46738889Sjdp switch (MsgBuf[0]) 46838889Sjdp { 46938889Sjdp case '4': 47038889Sjdp case '8': 47138889Sjdp if (CurEnv->e_message != NULL) 47238889Sjdp break; 47338889Sjdp /* FALLTHROUGH */ 47438889Sjdp 47538889Sjdp case '5': 47638889Sjdp if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 47738889Sjdp sm_free(CurEnv->e_message); 47838889Sjdp CurEnv->e_message = 47938889Sjdp sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 48038889Sjdp break; 481218822Sdim } 482218822Sdim} 483218822Sdim/* 48438889Sjdp** NMESSAGE -- print message (not necessarily an error) 48538889Sjdp** 48638889Sjdp** Just like "message" except it never puts the to... tag on. 48738889Sjdp** 48838889Sjdp** Parameters: 48938889Sjdp** msg -- the message (sm_io_printf fmt) -- if it begins 49038889Sjdp** with a three digit SMTP reply code, that is used, 49138889Sjdp** otherwise 050 is assumed. 49238889Sjdp** (others) -- sm_io_printf arguments 49338889Sjdp** 49438889Sjdp** Returns: 49538889Sjdp** none 49638889Sjdp** 49738889Sjdp** Side Effects: 49838889Sjdp** none. 49938889Sjdp*/ 50038889Sjdp 50138889Sjdp/*VARARGS1*/ 50238889Sjdpvoid 50338889Sjdp#ifdef __STDC__ 50438889Sjdpnmessage(const char *msg, ...) 50538889Sjdp#else /* __STDC__ */ 50638889Sjdpnmessage(msg, va_alist) 50738889Sjdp const char *msg; 50838889Sjdp va_dcl 50938889Sjdp#endif /* __STDC__ */ 51038889Sjdp{ 51138889Sjdp char *errtxt; 51238889Sjdp SM_VA_LOCAL_DECL 51338889Sjdp 51438889Sjdp errno = 0; 51538889Sjdp SM_VA_START(ap, msg); 51638889Sjdp errtxt = fmtmsg(MsgBuf, (char *) NULL, "050", 51738889Sjdp (char *) NULL, 0, msg, ap); 518218822Sdim SM_VA_END(ap); 51938889Sjdp putoutmsg(MsgBuf, false, false); 52038889Sjdp 52138889Sjdp /* save this message for mailq printing */ 52238889Sjdp switch (MsgBuf[0]) 52338889Sjdp { 52438889Sjdp case '4': 52538889Sjdp case '8': 52638889Sjdp if (CurEnv->e_message != NULL) 52738889Sjdp break; 52838889Sjdp /* FALLTHROUGH */ 52938889Sjdp 53038889Sjdp case '5': 53138889Sjdp if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) 53238889Sjdp sm_free(CurEnv->e_message); 53338889Sjdp CurEnv->e_message = 53438889Sjdp sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); 53538889Sjdp break; 53638889Sjdp } 53738889Sjdp} 53838889Sjdp/* 53938889Sjdp** PUTOUTMSG -- output error message to transcript and channel 54038889Sjdp** 54138889Sjdp** Parameters: 54238889Sjdp** msg -- message to output (in SMTP format). 54338889Sjdp** holdmsg -- if true, don't output a copy of the message to 54438889Sjdp** our output channel. 54538889Sjdp** heldmsg -- if true, this is a previously held message; 54638889Sjdp** don't log it to the transcript file. 54738889Sjdp** 54838889Sjdp** Returns: 54938889Sjdp** none. 550218822Sdim** 55138889Sjdp** Side Effects: 55238889Sjdp** Outputs msg to the transcript. 55338889Sjdp** If appropriate, outputs it to the channel. 55438889Sjdp** Deletes SMTP reply code number as appropriate. 555218822Sdim*/ 55638889Sjdp 55738889Sjdpstatic void 558218822Sdimputoutmsg(msg, holdmsg, heldmsg) 55938889Sjdp char *msg; 560218822Sdim bool holdmsg; 561218822Sdim bool heldmsg; 56238889Sjdp{ 56338889Sjdp char *errtxt = msg; 56438889Sjdp char msgcode = msg[0]; 56538889Sjdp 56638889Sjdp /* display for debugging */ 56738889Sjdp if (tTd(54, 8)) 56838889Sjdp sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", 56938889Sjdp heldmsg ? " (held)" : ""); 57038889Sjdp 57138889Sjdp /* map warnings to something SMTP can handle */ 57238889Sjdp if (msgcode == '6') 57338889Sjdp msg[0] = '5'; 57438889Sjdp else if (msgcode == '8') 57538889Sjdp msg[0] = '4'; 57638889Sjdp 577218822Sdim /* output to transcript if serious */ 578130561Sobrien if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL && 57938889Sjdp strchr("45", msg[0]) != NULL) 580218822Sdim (void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n", 58138889Sjdp msg); 58238889Sjdp 58338889Sjdp if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 58438889Sjdp sm_syslog(LOG_INFO, CurEnv->e_id, 58538889Sjdp "--- %s%s%s", msg, holdmsg ? " (hold)" : "", 58638889Sjdp heldmsg ? " (held)" : ""); 58738889Sjdp 58838889Sjdp if (msgcode == '8') 58938889Sjdp msg[0] = '0'; 59038889Sjdp 59138889Sjdp /* output to channel if appropriate */ 59238889Sjdp if (!Verbose && msg[0] == '0') 59338889Sjdp return; 59438889Sjdp if (holdmsg) 59538889Sjdp { 59638889Sjdp /* save for possible future display */ 59738889Sjdp msg[0] = msgcode; 59838889Sjdp if (HeldMessageBuf[0] == '5' && msgcode == '4') 59938889Sjdp return; 600218822Sdim (void) sm_strlcpy(HeldMessageBuf, msg, sizeof HeldMessageBuf); 601218822Sdim return; 60238889Sjdp } 603218822Sdim 60438889Sjdp (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 605218822Sdim 60638889Sjdp if (OutChannel == NULL) 60738889Sjdp return; 608218822Sdim 60938889Sjdp /* find actual text of error (after SMTP status codes) */ 61038889Sjdp if (ISSMTPREPLY(errtxt)) 61138889Sjdp { 61238889Sjdp int l; 61338889Sjdp 61438889Sjdp errtxt += 4; 61538889Sjdp l = isenhsc(errtxt, ' '); 61638889Sjdp if (l <= 0) 61738889Sjdp l = isenhsc(errtxt, '\0'); 61838889Sjdp if (l > 0) 61938889Sjdp errtxt += l + 1; 62038889Sjdp } 62138889Sjdp 62238889Sjdp /* if DisConnected, OutChannel now points to the transcript */ 62338889Sjdp if (!DisConnected && 624218822Sdim (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 62538889Sjdp (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n", 62638889Sjdp msg); 62738889Sjdp else 628218822Sdim (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n", 62938889Sjdp errtxt); 63038889Sjdp if (TrafficLogFile != NULL) 63138889Sjdp (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 63238889Sjdp "%05d >>> %s\n", (int) CurrentPid, 63338889Sjdp (OpMode == MD_SMTP || OpMode == MD_DAEMON) 63438889Sjdp ? msg : errtxt); 63538889Sjdp#if !PIPELINING 636218822Sdim /* XXX can't flush here for SMTP pipelining */ 637218822Sdim if (msg[3] == ' ') 638218822Sdim (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 639218822Sdim if (!sm_io_error(OutChannel) || DisConnected) 64038889Sjdp return; 64138889Sjdp 64238889Sjdp /* 64338889Sjdp ** Error on output -- if reporting lost channel, just ignore it. 64438889Sjdp ** Also, ignore errors from QUIT response (221 message) -- some 64538889Sjdp ** rude servers don't read result. 64638889Sjdp */ 64738889Sjdp 64838889Sjdp if (InChannel == NULL || sm_io_eof(InChannel) || 64938889Sjdp sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0) 65038889Sjdp return; 651218822Sdim 652218822Sdim /* can't call syserr, 'cause we are using MsgBuf */ 65338889Sjdp HoldErrs = true; 65438889Sjdp if (LogLevel > 0) 65538889Sjdp sm_syslog(LOG_CRIT, CurEnv->e_id, 65638889Sjdp "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 65738889Sjdp CURHOSTNAME, 65838889Sjdp shortenstring(msg, MAXSHORTSTR), sm_errstring(errno)); 65938889Sjdp#endif /* !PIPELINING */ 66038889Sjdp} 66138889Sjdp/* 66238889Sjdp** PUTERRMSG -- like putoutmsg, but does special processing for error messages 663218822Sdim** 66438889Sjdp** Parameters: 66538889Sjdp** msg -- the message to output. 666218822Sdim** 667218822Sdim** Returns: 66838889Sjdp** none. 66938889Sjdp** 67038889Sjdp** Side Effects: 67138889Sjdp** Sets the fatal error bit in the envelope as appropriate. 672218822Sdim*/ 673218822Sdim 67438889Sjdpstatic void 67538889Sjdpputerrmsg(msg) 676218822Sdim char *msg; 67738889Sjdp{ 678218822Sdim char msgcode = msg[0]; 67938889Sjdp 68038889Sjdp /* output the message as usual */ 68138889Sjdp putoutmsg(msg, HoldErrs, false); 68238889Sjdp 683218822Sdim /* be careful about multiple error messages */ 68438889Sjdp if (OnlyOneError) 68538889Sjdp HoldErrs = true; 68638889Sjdp 68738889Sjdp /* signal the error */ 688218822Sdim Errors++; 689218822Sdim 69038889Sjdp if (CurEnv == NULL) 69138889Sjdp return; 69238889Sjdp 69338889Sjdp if (msgcode == '6') 69438889Sjdp { 69538889Sjdp /* notify the postmaster */ 69638889Sjdp CurEnv->e_flags |= EF_PM_NOTIFY; 69738889Sjdp } 69838889Sjdp else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 699218822Sdim { 70038889Sjdp /* mark long-term fatal errors */ 70138889Sjdp CurEnv->e_flags |= EF_FATALERRS; 70238889Sjdp } 703218822Sdim} 70438889Sjdp/* 70538889Sjdp** ISENHSC -- check whether a string contains an enhanced status code 70638889Sjdp** 70738889Sjdp** Parameters: 70838889Sjdp** s -- string with possible enhanced status code. 70938889Sjdp** delim -- delim for enhanced status code. 71038889Sjdp** 71138889Sjdp** Returns: 71238889Sjdp** 0 -- no enhanced status code. 71338889Sjdp** >4 -- length of enhanced status code. 71438889Sjdp** 71538889Sjdp** Side Effects: 716218822Sdim** none. 71738889Sjdp*/ 71860484Sobrienint 71938889Sjdpisenhsc(s, delim) 72038889Sjdp const char *s; 72138889Sjdp int delim; 72238889Sjdp{ 72338889Sjdp int l, h; 72438889Sjdp 72538889Sjdp if (s == NULL) 72638889Sjdp return 0; 72738889Sjdp if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 728218822Sdim return 0; 72938889Sjdp h = 0; 73038889Sjdp l = 2; 73138889Sjdp while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 732218822Sdim ++h; 733218822Sdim if (h == 0 || s[l + h] != '.') 734218822Sdim return 0; 73538889Sjdp l += h + 1; 736218822Sdim h = 0; 737218822Sdim while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 738218822Sdim ++h; 739218822Sdim if (h == 0 || s[l + h] != delim) 74038889Sjdp return 0; 74138889Sjdp return l + h; 74238889Sjdp} 74338889Sjdp/* 74438889Sjdp** EXTENHSC -- check and extract an enhanced status code 74538889Sjdp** 74638889Sjdp** Parameters: 747218822Sdim** s -- string with possible enhanced status code. 74838889Sjdp** delim -- delim for enhanced status code. 749218822Sdim** e -- pointer to storage for enhanced status code. 75038889Sjdp** must be != NULL and have space for at least 75138889Sjdp** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3}) 752218822Sdim** 75338889Sjdp** Returns: 754** 0 -- no enhanced status code. 755** >4 -- length of enhanced status code. 756** 757** Side Effects: 758** fills e with enhanced status code. 759*/ 760 761int 762extenhsc(s, delim, e) 763 const char *s; 764 int delim; 765 char *e; 766{ 767 int l, h; 768 769 if (s == NULL) 770 return 0; 771 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 772 return 0; 773 h = 0; 774 l = 2; 775 e[0] = s[0]; 776 e[1] = '.'; 777 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 778 { 779 e[l + h] = s[l + h]; 780 ++h; 781 } 782 if (h == 0 || s[l + h] != '.') 783 return 0; 784 e[l + h] = '.'; 785 l += h + 1; 786 h = 0; 787 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 788 { 789 e[l + h] = s[l + h]; 790 ++h; 791 } 792 if (h == 0 || s[l + h] != delim) 793 return 0; 794 e[l + h] = '\0'; 795 return l + h; 796} 797/* 798** FMTMSG -- format a message into buffer. 799** 800** Parameters: 801** eb -- error buffer to get result -- MUST BE MsgBuf. 802** to -- the recipient tag for this message. 803** num -- default three digit SMTP reply code. 804** enhsc -- enhanced status code. 805** en -- the error number to display. 806** fmt -- format of string. 807** ap -- arguments for fmt. 808** 809** Returns: 810** pointer to error text beyond status codes. 811** 812** Side Effects: 813** none. 814*/ 815 816static char * 817fmtmsg(eb, to, num, enhsc, eno, fmt, ap) 818 register char *eb; 819 const char *to; 820 const char *num; 821 const char *enhsc; 822 int eno; 823 const char *fmt; 824 SM_VA_LOCAL_DECL 825{ 826 char del; 827 int l; 828 int spaceleft = sizeof MsgBuf; 829 char *errtxt; 830 831 /* output the reply code */ 832 if (ISSMTPCODE(fmt)) 833 { 834 num = fmt; 835 fmt += 4; 836 } 837 if (num[3] == '-') 838 del = '-'; 839 else 840 del = ' '; 841#if _FFR_SOFT_BOUNCE 842 if (SoftBounce && num[0] == '5') 843 { 844 /* replace 5 by 4 */ 845 (void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del); 846 } 847 else 848#endif /* _FFR_SOFT_BOUNCE */ 849 (void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del); 850 eb += 4; 851 spaceleft -= 4; 852 853 if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4) 854 { 855 /* copy enh.status code including trailing blank */ 856 l++; 857 (void) sm_strlcpy(eb, fmt, l + 1); 858 eb += l; 859 spaceleft -= l; 860 fmt += l; 861 } 862 else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4) 863 { 864 /* copy enh.status code */ 865 (void) sm_strlcpy(eb, enhsc, l + 1); 866 eb[l] = ' '; 867 eb[++l] = '\0'; 868 eb += l; 869 spaceleft -= l; 870 } 871#if _FFR_SOFT_BOUNCE 872 if (SoftBounce && eb[-l] == '5') 873 { 874 /* replace 5 by 4 */ 875 eb[-l] = '4'; 876 } 877#endif /* _FFR_SOFT_BOUNCE */ 878 errtxt = eb; 879 880 /* output the file name and line number */ 881 if (FileName != NULL) 882 { 883 (void) sm_snprintf(eb, spaceleft, "%s: line %d: ", 884 shortenstring(FileName, 83), LineNumber); 885 eb += (l = strlen(eb)); 886 spaceleft -= l; 887 } 888 889 /* 890 ** output the "to" address only if it is defined and one of the 891 ** following codes is used: 892 ** 050 internal notices, e.g., alias expansion 893 ** 250 Ok 894 ** 252 Cannot VRFY user, but will accept message and attempt delivery 895 ** 450 Requested mail action not taken: mailbox unavailable 896 ** 550 Requested action not taken: mailbox unavailable 897 ** 553 Requested action not taken: mailbox name not allowed 898 ** 899 ** Notice: this still isn't "the right thing", this code shouldn't 900 ** (indirectly) depend on CurEnv->e_to. 901 */ 902 903 if (to != NULL && to[0] != '\0' && 904 (strncmp(num, "050", 3) == 0 || 905 strncmp(num, "250", 3) == 0 || 906 strncmp(num, "252", 3) == 0 || 907 strncmp(num, "450", 3) == 0 || 908 strncmp(num, "550", 3) == 0 || 909 strncmp(num, "553", 3) == 0)) 910 { 911 (void) sm_strlcpyn(eb, spaceleft, 2, 912 shortenstring(to, MAXSHORTSTR), "... "); 913 spaceleft -= strlen(eb); 914 while (*eb != '\0') 915 *eb++ &= 0177; 916 } 917 918 /* output the message */ 919 (void) sm_vsnprintf(eb, spaceleft, fmt, ap); 920 spaceleft -= strlen(eb); 921 while (*eb != '\0') 922 *eb++ &= 0177; 923 924 /* output the error code, if any */ 925 if (eno != 0) 926 (void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno)); 927 928 return errtxt; 929} 930/* 931** BUFFER_ERRORS -- arrange to buffer future error messages 932** 933** Parameters: 934** none 935** 936** Returns: 937** none. 938*/ 939 940void 941buffer_errors() 942{ 943 HeldMessageBuf[0] = '\0'; 944 HoldErrs = true; 945} 946/* 947** FLUSH_ERRORS -- flush the held error message buffer 948** 949** Parameters: 950** print -- if set, print the message, otherwise just 951** delete it. 952** 953** Returns: 954** none. 955*/ 956 957void 958flush_errors(print) 959 bool print; 960{ 961 if (print && HeldMessageBuf[0] != '\0') 962 putoutmsg(HeldMessageBuf, false, true); 963 HeldMessageBuf[0] = '\0'; 964 HoldErrs = false; 965} 966/* 967** SM_ERRSTRING -- return string description of error code 968** 969** Parameters: 970** errnum -- the error number to translate 971** 972** Returns: 973** A string description of errnum. 974** 975** Side Effects: 976** none. 977*/ 978 979const char * 980sm_errstring(errnum) 981 int errnum; 982{ 983 char *dnsmsg; 984 char *bp; 985 static char buf[MAXLINE]; 986#if HASSTRERROR 987 char *err; 988 char errbuf[30]; 989#endif /* HASSTRERROR */ 990#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) 991 extern char *sys_errlist[]; 992 extern int sys_nerr; 993#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */ 994 995 /* 996 ** Handle special network error codes. 997 ** 998 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 999 */ 1000 1001 dnsmsg = NULL; 1002 switch (errnum) 1003 { 1004 case ETIMEDOUT: 1005 case ECONNRESET: 1006 bp = buf; 1007#if HASSTRERROR 1008 err = strerror(errnum); 1009 if (err == NULL) 1010 { 1011 (void) sm_snprintf(errbuf, sizeof errbuf, 1012 "Error %d", errnum); 1013 err = errbuf; 1014 } 1015 (void) sm_strlcpy(bp, err, SPACELEFT(buf, bp)); 1016#else /* HASSTRERROR */ 1017 if (errnum >= 0 && errnum < sys_nerr) 1018 (void) sm_strlcpy(bp, sys_errlist[errnum], 1019 SPACELEFT(buf, bp)); 1020 else 1021 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1022 "Error %d", errnum); 1023#endif /* HASSTRERROR */ 1024 bp += strlen(bp); 1025 if (CurHostName != NULL) 1026 { 1027 if (errnum == ETIMEDOUT) 1028 { 1029 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1030 " with "); 1031 bp += strlen(bp); 1032 } 1033 else 1034 { 1035 bp = buf; 1036 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1037 "Connection reset by "); 1038 bp += strlen(bp); 1039 } 1040 (void) sm_strlcpy(bp, 1041 shortenstring(CurHostName, MAXSHORTSTR), 1042 SPACELEFT(buf, bp)); 1043 bp += strlen(buf); 1044 } 1045 if (SmtpPhase != NULL) 1046 { 1047 (void) sm_snprintf(bp, SPACELEFT(buf, bp), 1048 " during %s", SmtpPhase); 1049 } 1050 return buf; 1051 1052 case EHOSTDOWN: 1053 if (CurHostName == NULL) 1054 break; 1055 (void) sm_snprintf(buf, sizeof buf, "Host %s is down", 1056 shortenstring(CurHostName, MAXSHORTSTR)); 1057 return buf; 1058 1059 case ECONNREFUSED: 1060 if (CurHostName == NULL) 1061 break; 1062 (void) sm_strlcpyn(buf, sizeof buf, 2, "Connection refused by ", 1063 shortenstring(CurHostName, MAXSHORTSTR)); 1064 return buf; 1065 1066#if NAMED_BIND 1067 case HOST_NOT_FOUND + E_DNSBASE: 1068 dnsmsg = "host not found"; 1069 break; 1070 1071 case TRY_AGAIN + E_DNSBASE: 1072 dnsmsg = "host name lookup failure"; 1073 break; 1074 1075 case NO_RECOVERY + E_DNSBASE: 1076 dnsmsg = "non-recoverable error"; 1077 break; 1078 1079 case NO_DATA + E_DNSBASE: 1080 dnsmsg = "no data known"; 1081 break; 1082#endif /* NAMED_BIND */ 1083 1084 case EPERM: 1085 /* SunOS gives "Not owner" -- this is the POSIX message */ 1086 return "Operation not permitted"; 1087 1088 /* 1089 ** Error messages used internally in sendmail. 1090 */ 1091 1092 case E_SM_OPENTIMEOUT: 1093 return "Timeout on file open"; 1094 1095 case E_SM_NOSLINK: 1096 return "Symbolic links not allowed"; 1097 1098 case E_SM_NOHLINK: 1099 return "Hard links not allowed"; 1100 1101 case E_SM_REGONLY: 1102 return "Regular files only"; 1103 1104 case E_SM_ISEXEC: 1105 return "Executable files not allowed"; 1106 1107 case E_SM_WWDIR: 1108 return "World writable directory"; 1109 1110 case E_SM_GWDIR: 1111 return "Group writable directory"; 1112 1113 case E_SM_FILECHANGE: 1114 return "File changed after open"; 1115 1116 case E_SM_WWFILE: 1117 return "World writable file"; 1118 1119 case E_SM_GWFILE: 1120 return "Group writable file"; 1121 1122 case E_SM_GRFILE: 1123 return "Group readable file"; 1124 1125 case E_SM_WRFILE: 1126 return "World readable file"; 1127 } 1128 1129 if (dnsmsg != NULL) 1130 { 1131 bp = buf; 1132 bp += sm_strlcpy(bp, "Name server: ", sizeof buf); 1133 if (CurHostName != NULL) 1134 { 1135 (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, 1136 shortenstring(CurHostName, MAXSHORTSTR), ": "); 1137 bp += strlen(bp); 1138 } 1139 (void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp)); 1140 return buf; 1141 } 1142 1143#if LDAPMAP 1144 if (errnum >= E_LDAPBASE) 1145 return ldap_err2string(errnum - E_LDAPBASE); 1146#endif /* LDAPMAP */ 1147 1148#if HASSTRERROR 1149 err = strerror(errnum); 1150 if (err == NULL) 1151 { 1152 (void) sm_snprintf(buf, sizeof buf, "Error %d", errnum); 1153 return buf; 1154 } 1155 return err; 1156#else /* HASSTRERROR */ 1157 if (errnum > 0 && errnum < sys_nerr) 1158 return sys_errlist[errnum]; 1159 1160 (void) sm_snprintf(buf, sizeof buf, "Error %d", errnum); 1161 return buf; 1162#endif /* HASSTRERROR */ 1163} 1164