1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)err.c 8.14 (Berkeley) 10/29/93"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <errno.h> 15 # include <netdb.h> 16 17 /* 18 ** SYSERR -- Print error message. 19 ** 20 ** Prints an error message via printf to the diagnostic 21 ** output. If LOG is defined, it logs it also. 22 ** 23 ** If the first character of the syserr message is `!' it will 24 ** log this as an ALERT message and exit immediately. This can 25 ** leave queue files in an indeterminate state, so it should not 26 ** be used lightly. 27 ** 28 ** Parameters: 29 ** f -- the format string 30 ** a, b, c, d, e -- parameters 31 ** 32 ** Returns: 33 ** none 34 ** Through TopFrame if QuickAbort is set. 35 ** 36 ** Side Effects: 37 ** increments Errors. 38 ** sets ExitStat. 39 */ 40 41 char MsgBuf[BUFSIZ*2]; /* text of most recent message */ 42 43 static void fmtmsg(); 44 45 #if defined(NAMED_BIND) && !defined(NO_DATA) 46 # define NO_DATA NO_ADDRESS 47 #endif 48 49 void 50 /*VARARGS1*/ 51 #ifdef __STDC__ 52 syserr(const char *fmt, ...) 53 #else 54 syserr(fmt, va_alist) 55 const char *fmt; 56 va_dcl 57 #endif 58 { 59 register char *p; 60 int olderrno = errno; 61 bool panic; 62 VA_LOCAL_DECL 63 64 panic = *fmt == '!'; 65 if (panic) 66 fmt++; 67 68 /* format and output the error message */ 69 if (olderrno == 0) 70 p = "554"; 71 else 72 p = "451"; 73 VA_START(fmt); 74 fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap); 75 VA_END; 76 puterrmsg(MsgBuf); 77 78 /* determine exit status if not already set */ 79 if (ExitStat == EX_OK) 80 { 81 if (olderrno == 0) 82 ExitStat = EX_SOFTWARE; 83 else 84 ExitStat = EX_OSERR; 85 } 86 87 # ifdef LOG 88 if (LogLevel > 0) 89 syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR: %s", 90 CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 91 &MsgBuf[4]); 92 # endif /* LOG */ 93 if (olderrno == EMFILE) 94 { 95 printopenfds(TRUE); 96 mci_dump_all(TRUE); 97 } 98 if (panic) 99 { 100 #ifdef XLA 101 xla_all_end(); 102 #endif 103 exit(EX_OSERR); 104 } 105 errno = 0; 106 if (QuickAbort) 107 longjmp(TopFrame, 2); 108 } 109 /* 110 ** USRERR -- Signal user error. 111 ** 112 ** This is much like syserr except it is for user errors. 113 ** 114 ** Parameters: 115 ** fmt, a, b, c, d -- printf strings 116 ** 117 ** Returns: 118 ** none 119 ** Through TopFrame if QuickAbort is set. 120 ** 121 ** Side Effects: 122 ** increments Errors. 123 */ 124 125 /*VARARGS1*/ 126 void 127 #ifdef __STDC__ 128 usrerr(const char *fmt, ...) 129 #else 130 usrerr(fmt, va_alist) 131 const char *fmt; 132 va_dcl 133 #endif 134 { 135 VA_LOCAL_DECL 136 extern char SuprErrs; 137 extern int errno; 138 139 if (SuprErrs) 140 return; 141 142 VA_START(fmt); 143 fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap); 144 VA_END; 145 puterrmsg(MsgBuf); 146 147 # ifdef LOG 148 if (LogLevel > 3 && LogUsrErrs) 149 syslog(LOG_NOTICE, "%s: %s", 150 CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 151 &MsgBuf[4]); 152 # endif /* LOG */ 153 154 if (QuickAbort) 155 longjmp(TopFrame, 1); 156 } 157 /* 158 ** MESSAGE -- print message (not necessarily an error) 159 ** 160 ** Parameters: 161 ** msg -- the message (printf fmt) -- it can begin with 162 ** an SMTP reply code. If not, 050 is assumed. 163 ** a, b, c, d, e -- printf arguments 164 ** 165 ** Returns: 166 ** none 167 ** 168 ** Side Effects: 169 ** none. 170 */ 171 172 /*VARARGS2*/ 173 void 174 #ifdef __STDC__ 175 message(const char *msg, ...) 176 #else 177 message(msg, va_alist) 178 const char *msg; 179 va_dcl 180 #endif 181 { 182 VA_LOCAL_DECL 183 184 errno = 0; 185 VA_START(msg); 186 fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap); 187 VA_END; 188 putoutmsg(MsgBuf, FALSE); 189 } 190 /* 191 ** NMESSAGE -- print message (not necessarily an error) 192 ** 193 ** Just like "message" except it never puts the to... tag on. 194 ** 195 ** Parameters: 196 ** num -- the default ARPANET error number (in ascii) 197 ** msg -- the message (printf fmt) -- if it begins 198 ** with three digits, this number overrides num. 199 ** a, b, c, d, e -- printf arguments 200 ** 201 ** Returns: 202 ** none 203 ** 204 ** Side Effects: 205 ** none. 206 */ 207 208 /*VARARGS2*/ 209 void 210 #ifdef __STDC__ 211 nmessage(const char *msg, ...) 212 #else 213 nmessage(msg, va_alist) 214 const char *msg; 215 va_dcl 216 #endif 217 { 218 VA_LOCAL_DECL 219 220 errno = 0; 221 VA_START(msg); 222 fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap); 223 VA_END; 224 putoutmsg(MsgBuf, FALSE); 225 } 226 /* 227 ** PUTOUTMSG -- output error message to transcript and channel 228 ** 229 ** Parameters: 230 ** msg -- message to output (in SMTP format). 231 ** holdmsg -- if TRUE, don't output a copy of the message to 232 ** our output channel. 233 ** 234 ** Returns: 235 ** none. 236 ** 237 ** Side Effects: 238 ** Outputs msg to the transcript. 239 ** If appropriate, outputs it to the channel. 240 ** Deletes SMTP reply code number as appropriate. 241 */ 242 243 putoutmsg(msg, holdmsg) 244 char *msg; 245 bool holdmsg; 246 { 247 /* display for debugging */ 248 if (tTd(54, 8)) 249 printf("--- %s%s\n", msg, holdmsg ? " (held)" : ""); 250 251 /* output to transcript if serious */ 252 if (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL) 253 fprintf(CurEnv->e_xfp, "%s\n", msg); 254 255 /* output to channel if appropriate */ 256 if (holdmsg || (!Verbose && msg[0] == '0')) 257 return; 258 259 /* map warnings to something SMTP can handle */ 260 if (msg[0] == '6') 261 msg[0] = '5'; 262 263 (void) fflush(stdout); 264 if (OpMode == MD_SMTP) 265 fprintf(OutChannel, "%s\r\n", msg); 266 else 267 fprintf(OutChannel, "%s\n", &msg[4]); 268 if (TrafficLogFile != NULL) 269 fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), 270 OpMode == MD_SMTP ? msg : &msg[4]); 271 if (msg[3] == ' ') 272 (void) fflush(OutChannel); 273 if (!ferror(OutChannel)) 274 return; 275 276 /* 277 ** Error on output -- if reporting lost channel, just ignore it. 278 ** Also, ignore errors from QUIT response (221 message) -- some 279 ** rude servers don't read result. 280 */ 281 282 if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0) 283 return; 284 285 /* can't call syserr, 'cause we are using MsgBuf */ 286 HoldErrs = TRUE; 287 #ifdef LOG 288 if (LogLevel > 0) 289 syslog(LOG_CRIT, 290 "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %m", 291 CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 292 CurHostName == NULL ? "NO-HOST" : CurHostName, 293 msg); 294 #endif 295 } 296 /* 297 ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 298 ** 299 ** Parameters: 300 ** msg -- the message to output. 301 ** 302 ** Returns: 303 ** none. 304 ** 305 ** Side Effects: 306 ** Sets the fatal error bit in the envelope as appropriate. 307 */ 308 309 puterrmsg(msg) 310 char *msg; 311 { 312 char msgcode = msg[0]; 313 314 /* output the message as usual */ 315 putoutmsg(msg, HoldErrs); 316 317 /* signal the error */ 318 Errors++; 319 if (msgcode == '6') 320 { 321 /* notify the postmaster */ 322 CurEnv->e_flags |= EF_PM_NOTIFY; 323 } 324 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 325 { 326 /* mark long-term fatal errors */ 327 CurEnv->e_flags |= EF_FATALERRS; 328 } 329 } 330 /* 331 ** FMTMSG -- format a message into buffer. 332 ** 333 ** Parameters: 334 ** eb -- error buffer to get result. 335 ** to -- the recipient tag for this message. 336 ** num -- arpanet error number. 337 ** en -- the error number to display. 338 ** fmt -- format of string. 339 ** a, b, c, d, e -- arguments. 340 ** 341 ** Returns: 342 ** none. 343 ** 344 ** Side Effects: 345 ** none. 346 */ 347 348 static void 349 fmtmsg(eb, to, num, eno, fmt, ap) 350 register char *eb; 351 char *to; 352 char *num; 353 int eno; 354 char *fmt; 355 va_list ap; 356 { 357 char del; 358 char *meb; 359 360 /* output the reply code */ 361 if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2])) 362 { 363 num = fmt; 364 fmt += 4; 365 } 366 if (num[3] == '-') 367 del = '-'; 368 else 369 del = ' '; 370 (void) sprintf(eb, "%3.3s%c", num, del); 371 eb += 4; 372 373 /* output the file name and line number */ 374 if (FileName != NULL) 375 { 376 (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber); 377 eb += strlen(eb); 378 } 379 380 /* output the "to" person */ 381 if (to != NULL && to[0] != '\0') 382 { 383 (void) sprintf(eb, "%s... ", to); 384 while (*eb != '\0') 385 *eb++ &= 0177; 386 } 387 388 meb = eb; 389 390 /* output the message */ 391 (void) vsprintf(eb, fmt, ap); 392 while (*eb != '\0') 393 *eb++ &= 0177; 394 395 /* output the error code, if any */ 396 if (eno != 0) 397 { 398 (void) sprintf(eb, ": %s", errstring(eno)); 399 eb += strlen(eb); 400 } 401 402 if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4')) 403 { 404 if (CurEnv->e_message != NULL) 405 free(CurEnv->e_message); 406 CurEnv->e_message = newstr(meb); 407 } 408 } 409 /* 410 ** ERRSTRING -- return string description of error code 411 ** 412 ** Parameters: 413 ** errno -- the error number to translate 414 ** 415 ** Returns: 416 ** A string description of errno. 417 ** 418 ** Side Effects: 419 ** none. 420 */ 421 422 const char * 423 errstring(errno) 424 int errno; 425 { 426 static char buf[MAXLINE]; 427 # ifndef ERRLIST_PREDEFINED 428 extern char *sys_errlist[]; 429 extern int sys_nerr; 430 # endif 431 # ifdef SMTP 432 extern char *SmtpPhase; 433 # endif /* SMTP */ 434 435 # ifdef DAEMON 436 # ifdef ETIMEDOUT 437 /* 438 ** Handle special network error codes. 439 ** 440 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 441 */ 442 443 switch (errno) 444 { 445 case ETIMEDOUT: 446 case ECONNRESET: 447 (void) strcpy(buf, sys_errlist[errno]); 448 if (SmtpPhase != NULL) 449 { 450 (void) strcat(buf, " during "); 451 (void) strcat(buf, SmtpPhase); 452 } 453 if (CurHostName != NULL) 454 { 455 (void) strcat(buf, " with "); 456 (void) strcat(buf, CurHostName); 457 } 458 return (buf); 459 460 case EHOSTDOWN: 461 if (CurHostName == NULL) 462 break; 463 (void) sprintf(buf, "Host %s is down", CurHostName); 464 return (buf); 465 466 case ECONNREFUSED: 467 if (CurHostName == NULL) 468 break; 469 (void) sprintf(buf, "Connection refused by %s", CurHostName); 470 return (buf); 471 472 case EOPENTIMEOUT: 473 return "Timeout on file open"; 474 475 # ifdef NAMED_BIND 476 case HOST_NOT_FOUND + E_DNSBASE: 477 return ("Name server: host not found"); 478 479 case TRY_AGAIN + E_DNSBASE: 480 return ("Name server: host name lookup failure"); 481 482 case NO_RECOVERY + E_DNSBASE: 483 return ("Name server: non-recoverable error"); 484 485 case NO_DATA + E_DNSBASE: 486 return ("Name server: no data known for name"); 487 # endif 488 } 489 # endif 490 # endif 491 492 if (errno > 0 && errno < sys_nerr) 493 return (sys_errlist[errno]); 494 495 (void) sprintf(buf, "Error %d", errno); 496 return (buf); 497 } 498