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