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