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