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