1 /* 2 * Copyright (c) 1983, 1995 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.34 (Berkeley) 05/28/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 extern void putoutmsg __P((char *, bool, bool)); 47 extern void puterrmsg __P((char *)); 48 static void fmtmsg __P((char *, const char *, const char *, int, const char *, va_list)); 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 = sm_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 void 291 putoutmsg(msg, holdmsg, heldmsg) 292 char *msg; 293 bool holdmsg; 294 bool heldmsg; 295 { 296 /* display for debugging */ 297 if (tTd(54, 8)) 298 printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", 299 heldmsg ? " (held)" : ""); 300 301 /* output to transcript if serious */ 302 if (!heldmsg && CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL) 303 fprintf(CurEnv->e_xfp, "%s\n", msg); 304 305 /* output to channel if appropriate */ 306 if (!Verbose && msg[0] == '0') 307 return; 308 if (holdmsg) 309 { 310 /* save for possible future display */ 311 strcpy(HeldMessageBuf, msg); 312 return; 313 } 314 315 /* map warnings to something SMTP can handle */ 316 if (msg[0] == '6') 317 msg[0] = '5'; 318 319 (void) fflush(stdout); 320 321 /* if DisConnected, OutChannel now points to the transcript */ 322 if (!DisConnected && 323 (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 324 fprintf(OutChannel, "%s\r\n", msg); 325 else 326 fprintf(OutChannel, "%s\n", &msg[4]); 327 if (TrafficLogFile != NULL) 328 fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), 329 (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]); 330 if (msg[3] == ' ') 331 (void) fflush(OutChannel); 332 if (!ferror(OutChannel) || DisConnected) 333 return; 334 335 /* 336 ** Error on output -- if reporting lost channel, just ignore it. 337 ** Also, ignore errors from QUIT response (221 message) -- some 338 ** rude servers don't read result. 339 */ 340 341 if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0) 342 return; 343 344 /* can't call syserr, 'cause we are using MsgBuf */ 345 HoldErrs = TRUE; 346 #ifdef LOG 347 if (LogLevel > 0) 348 syslog(LOG_CRIT, 349 "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", 350 CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 351 CurHostName == NULL ? "NO-HOST" : CurHostName, 352 msg, errstring(errno)); 353 #endif 354 } 355 /* 356 ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 357 ** 358 ** Parameters: 359 ** msg -- the message to output. 360 ** 361 ** Returns: 362 ** none. 363 ** 364 ** Side Effects: 365 ** Sets the fatal error bit in the envelope as appropriate. 366 */ 367 368 void 369 puterrmsg(msg) 370 char *msg; 371 { 372 char msgcode = msg[0]; 373 374 /* output the message as usual */ 375 putoutmsg(msg, HoldErrs, FALSE); 376 377 /* signal the error */ 378 Errors++; 379 if (msgcode == '6') 380 { 381 /* notify the postmaster */ 382 CurEnv->e_flags |= EF_PM_NOTIFY; 383 } 384 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 385 { 386 /* mark long-term fatal errors */ 387 CurEnv->e_flags |= EF_FATALERRS; 388 } 389 } 390 /* 391 ** FMTMSG -- format a message into buffer. 392 ** 393 ** Parameters: 394 ** eb -- error buffer to get result. 395 ** to -- the recipient tag for this message. 396 ** num -- arpanet error number. 397 ** en -- the error number to display. 398 ** fmt -- format of string. 399 ** a, b, c, d, e -- arguments. 400 ** 401 ** Returns: 402 ** none. 403 ** 404 ** Side Effects: 405 ** none. 406 */ 407 408 static void 409 fmtmsg(eb, to, num, eno, fmt, ap) 410 register char *eb; 411 const char *to; 412 const char *num; 413 int eno; 414 const char *fmt; 415 va_list ap; 416 { 417 char del; 418 char *meb; 419 420 /* output the reply code */ 421 if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2])) 422 { 423 num = fmt; 424 fmt += 4; 425 } 426 if (num[3] == '-') 427 del = '-'; 428 else 429 del = ' '; 430 (void) sprintf(eb, "%3.3s%c", num, del); 431 eb += 4; 432 433 /* output the file name and line number */ 434 if (FileName != NULL) 435 { 436 (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber); 437 eb += strlen(eb); 438 } 439 440 /* output the "to" person */ 441 if (to != NULL && to[0] != '\0') 442 { 443 (void) sprintf(eb, "%s... ", shortenstring(to, 203)); 444 while (*eb != '\0') 445 *eb++ &= 0177; 446 } 447 448 meb = eb; 449 450 /* output the message */ 451 (void) vsprintf(eb, fmt, ap); 452 while (*eb != '\0') 453 *eb++ &= 0177; 454 455 /* output the error code, if any */ 456 if (eno != 0) 457 { 458 (void) sprintf(eb, ": %s", errstring(eno)); 459 eb += strlen(eb); 460 } 461 } 462 /* 463 ** BUFFER_ERRORS -- arrange to buffer future error messages 464 ** 465 ** Parameters: 466 ** none 467 ** 468 ** Returns: 469 ** none. 470 */ 471 472 void 473 buffer_errors() 474 { 475 HeldMessageBuf[0] = '\0'; 476 HoldErrs = TRUE; 477 } 478 /* 479 ** FLUSH_ERRORS -- flush the held error message buffer 480 ** 481 ** Parameters: 482 ** print -- if set, print the message, otherwise just 483 ** delete it. 484 ** 485 ** Returns: 486 ** none. 487 */ 488 489 void 490 flush_errors(print) 491 bool print; 492 { 493 if (print && HeldMessageBuf[0] != '\0') 494 putoutmsg(HeldMessageBuf, FALSE, TRUE); 495 HeldMessageBuf[0] = '\0'; 496 HoldErrs = FALSE; 497 } 498 /* 499 ** ERRSTRING -- return string description of error code 500 ** 501 ** Parameters: 502 ** errnum -- the error number to translate 503 ** 504 ** Returns: 505 ** A string description of errnum. 506 ** 507 ** Side Effects: 508 ** none. 509 */ 510 511 const char * 512 errstring(errnum) 513 int errnum; 514 { 515 char *dnsmsg; 516 static char buf[MAXLINE]; 517 # ifndef ERRLIST_PREDEFINED 518 extern char *sys_errlist[]; 519 extern int sys_nerr; 520 # endif 521 # ifdef SMTP 522 extern char *SmtpPhase; 523 # endif /* SMTP */ 524 525 /* 526 ** Handle special network error codes. 527 ** 528 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 529 */ 530 531 dnsmsg = NULL; 532 switch (errnum) 533 { 534 # if defined(DAEMON) && defined(ETIMEDOUT) 535 case ETIMEDOUT: 536 case ECONNRESET: 537 (void) strcpy(buf, sys_errlist[errnum]); 538 if (SmtpPhase != NULL) 539 { 540 (void) strcat(buf, " during "); 541 (void) strcat(buf, SmtpPhase); 542 } 543 if (CurHostName != NULL) 544 { 545 (void) strcat(buf, " with "); 546 (void) strcat(buf, CurHostName); 547 } 548 return (buf); 549 550 case EHOSTDOWN: 551 if (CurHostName == NULL) 552 break; 553 (void) sprintf(buf, "Host %s is down", CurHostName); 554 return (buf); 555 556 case ECONNREFUSED: 557 if (CurHostName == NULL) 558 break; 559 (void) sprintf(buf, "Connection refused by %s", CurHostName); 560 return (buf); 561 # endif 562 563 case EOPENTIMEOUT: 564 return "Timeout on file open"; 565 566 # if NAMED_BIND 567 case HOST_NOT_FOUND + E_DNSBASE: 568 dnsmsg = "host not found"; 569 break; 570 571 case TRY_AGAIN + E_DNSBASE: 572 dnsmsg = "host name lookup failure"; 573 break; 574 575 case NO_RECOVERY + E_DNSBASE: 576 dnsmsg = "non-recoverable error"; 577 break; 578 579 case NO_DATA + E_DNSBASE: 580 dnsmsg = "no data known"; 581 break; 582 # endif 583 584 case EPERM: 585 /* SunOS gives "Not owner" -- this is the POSIX message */ 586 return "Operation not permitted"; 587 } 588 589 if (dnsmsg != NULL) 590 { 591 (void) strcpy(buf, "Name server: "); 592 if (CurHostName != NULL) 593 { 594 (void) strcat(buf, CurHostName); 595 (void) strcat(buf, ": "); 596 } 597 (void) strcat(buf, dnsmsg); 598 return buf; 599 } 600 601 if (errnum > 0 && errnum < sys_nerr) 602 return (sys_errlist[errnum]); 603 604 (void) sprintf(buf, "Error %d", errnum); 605 return (buf); 606 } 607