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