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.24 (Berkeley) 03/01/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 278 /* if DisConnected, OutChannel now points to the transcript */ 279 if (!DisConnected && 280 (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) 281 fprintf(OutChannel, "%s\r\n", msg); 282 else 283 fprintf(OutChannel, "%s\n", &msg[4]); 284 if (TrafficLogFile != NULL) 285 fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), 286 (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]); 287 if (msg[3] == ' ') 288 (void) fflush(OutChannel); 289 if (!ferror(OutChannel) || DisConnected) 290 return; 291 292 /* 293 ** Error on output -- if reporting lost channel, just ignore it. 294 ** Also, ignore errors from QUIT response (221 message) -- some 295 ** rude servers don't read result. 296 */ 297 298 if (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0) 299 return; 300 301 /* can't call syserr, 'cause we are using MsgBuf */ 302 HoldErrs = TRUE; 303 #ifdef LOG 304 if (LogLevel > 0) 305 syslog(LOG_CRIT, 306 "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %m", 307 CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, 308 CurHostName == NULL ? "NO-HOST" : CurHostName, 309 msg); 310 #endif 311 } 312 /* 313 ** PUTERRMSG -- like putoutmsg, but does special processing for error messages 314 ** 315 ** Parameters: 316 ** msg -- the message to output. 317 ** 318 ** Returns: 319 ** none. 320 ** 321 ** Side Effects: 322 ** Sets the fatal error bit in the envelope as appropriate. 323 */ 324 325 puterrmsg(msg) 326 char *msg; 327 { 328 char msgcode = msg[0]; 329 330 /* output the message as usual */ 331 putoutmsg(msg, HoldErrs); 332 333 /* signal the error */ 334 Errors++; 335 if (msgcode == '6') 336 { 337 /* notify the postmaster */ 338 CurEnv->e_flags |= EF_PM_NOTIFY; 339 } 340 else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags)) 341 { 342 /* mark long-term fatal errors */ 343 CurEnv->e_flags |= EF_FATALERRS; 344 } 345 } 346 /* 347 ** FMTMSG -- format a message into buffer. 348 ** 349 ** Parameters: 350 ** eb -- error buffer to get result. 351 ** to -- the recipient tag for this message. 352 ** num -- arpanet error number. 353 ** en -- the error number to display. 354 ** fmt -- format of string. 355 ** a, b, c, d, e -- arguments. 356 ** 357 ** Returns: 358 ** none. 359 ** 360 ** Side Effects: 361 ** none. 362 */ 363 364 static void 365 fmtmsg(eb, to, num, eno, fmt, ap) 366 register char *eb; 367 char *to; 368 char *num; 369 int eno; 370 char *fmt; 371 va_list ap; 372 { 373 char del; 374 char *meb; 375 376 /* output the reply code */ 377 if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2])) 378 { 379 num = fmt; 380 fmt += 4; 381 } 382 if (num[3] == '-') 383 del = '-'; 384 else 385 del = ' '; 386 (void) sprintf(eb, "%3.3s%c", num, del); 387 eb += 4; 388 389 /* output the file name and line number */ 390 if (FileName != NULL) 391 { 392 (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber); 393 eb += strlen(eb); 394 } 395 396 /* output the "to" person */ 397 if (to != NULL && to[0] != '\0') 398 { 399 (void) sprintf(eb, "%s... ", shortenstring(to, 203)); 400 while (*eb != '\0') 401 *eb++ &= 0177; 402 } 403 404 meb = eb; 405 406 /* output the message */ 407 (void) vsprintf(eb, fmt, ap); 408 while (*eb != '\0') 409 *eb++ &= 0177; 410 411 /* output the error code, if any */ 412 if (eno != 0) 413 { 414 (void) sprintf(eb, ": %s", errstring(eno)); 415 eb += strlen(eb); 416 } 417 418 if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4')) 419 { 420 if (CurEnv->e_message != NULL) 421 free(CurEnv->e_message); 422 CurEnv->e_message = newstr(meb); 423 } 424 } 425 /* 426 ** ERRSTRING -- return string description of error code 427 ** 428 ** Parameters: 429 ** errnum -- the error number to translate 430 ** 431 ** Returns: 432 ** A string description of errnum. 433 ** 434 ** Side Effects: 435 ** none. 436 */ 437 438 const char * 439 errstring(errnum) 440 int errnum; 441 { 442 char *dnsmsg; 443 static char buf[MAXLINE]; 444 # ifndef ERRLIST_PREDEFINED 445 extern char *sys_errlist[]; 446 extern int sys_nerr; 447 # endif 448 # ifdef SMTP 449 extern char *SmtpPhase; 450 # endif /* SMTP */ 451 452 /* 453 ** Handle special network error codes. 454 ** 455 ** These are 4.2/4.3bsd specific; they should be in daemon.c. 456 */ 457 458 dnsmsg = NULL; 459 switch (errnum) 460 { 461 # if defined(DAEMON) && defined(ETIMEDOUT) 462 case ETIMEDOUT: 463 case ECONNRESET: 464 (void) strcpy(buf, sys_errlist[errnum]); 465 if (SmtpPhase != NULL) 466 { 467 (void) strcat(buf, " during "); 468 (void) strcat(buf, SmtpPhase); 469 } 470 if (CurHostName != NULL) 471 { 472 (void) strcat(buf, " with "); 473 (void) strcat(buf, CurHostName); 474 } 475 return (buf); 476 477 case EHOSTDOWN: 478 if (CurHostName == NULL) 479 break; 480 (void) sprintf(buf, "Host %s is down", CurHostName); 481 return (buf); 482 483 case ECONNREFUSED: 484 if (CurHostName == NULL) 485 break; 486 (void) sprintf(buf, "Connection refused by %s", CurHostName); 487 return (buf); 488 # endif 489 490 case EOPENTIMEOUT: 491 return "Timeout on file open"; 492 493 # ifdef NAMED_BIND 494 case HOST_NOT_FOUND + E_DNSBASE: 495 dnsmsg = "host not found"; 496 break; 497 498 case TRY_AGAIN + E_DNSBASE: 499 dnsmsg = "host name lookup failure"; 500 break; 501 502 case NO_RECOVERY + E_DNSBASE: 503 dnsmsg = "non-recoverable error"; 504 break; 505 506 case NO_DATA + E_DNSBASE: 507 dnsmsg = "no data known"; 508 break; 509 # endif 510 511 case EPERM: 512 /* SunOS gives "Not owner" -- this is the POSIX message */ 513 return "Operation not permitted"; 514 } 515 516 if (dnsmsg != NULL) 517 { 518 (void) strcpy(buf, "Name server: "); 519 if (CurHostName != NULL) 520 { 521 (void) strcat(buf, CurHostName); 522 (void) strcat(buf, ": "); 523 } 524 (void) strcat(buf, dnsmsg); 525 return buf; 526 } 527 528 if (errnum > 0 && errnum < sys_nerr) 529 return (sys_errlist[errnum]); 530 531 (void) sprintf(buf, "Error %d", errnum); 532 return (buf); 533 } 534