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