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