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