1 /* 2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1990, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 * 11 */ 12 13 #ifndef lint 14 static char copyright[] = 15 "@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\ 16 All rights reserved.\n\ 17 Copyright (c) 1990, 1993, 1994\n\ 18 The Regents of the University of California. All rights reserved.\n"; 19 #endif /* ! lint */ 20 21 #ifndef lint 22 static char id[] = "@(#)$Id: mail.local.c,v 8.143.4.13 2000/07/18 05:41:38 gshapiro Exp $"; 23 #endif /* ! lint */ 24 25 /* 26 ** This is not intended to work on System V derived systems 27 ** such as Solaris or HP-UX, since they use a totally different 28 ** approach to mailboxes (essentially, they have a setgid program 29 ** rather than setuid, and they rely on the ability to "give away" 30 ** files to do their work). IT IS NOT A BUG that this doesn't 31 ** work on such architectures. 32 */ 33 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/stat.h> 38 #include <sys/socket.h> 39 #include <sys/file.h> 40 41 #include <netinet/in.h> 42 #include <arpa/nameser.h> 43 44 #include <fcntl.h> 45 #include <netdb.h> 46 #include <pwd.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <syslog.h> 51 #include <time.h> 52 #include <unistd.h> 53 #ifdef EX_OK 54 # undef EX_OK /* unistd.h may have another use for this */ 55 #endif /* EX_OK */ 56 #include <sysexits.h> 57 #include <ctype.h> 58 59 #ifndef __P 60 # include "sendmail/cdefs.h" 61 #endif /* ! __P */ 62 #include "sendmail/useful.h" 63 64 extern size_t strlcpy __P((char *, const char *, size_t)); 65 extern size_t strlcat __P((char *, const char *, size_t)); 66 67 #if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || defined(IRIX64) || defined(IRIX5) || defined(IRIX6) 68 # ifndef HASSTRERROR 69 # define HASSTRERROR 1 70 # endif /* ! HASSTRERROR */ 71 #endif /* defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || 72 defined(IRIX64) || defined(IRIX5) || defined(IRIX6) */ 73 74 #include "sendmail/errstring.h" 75 76 77 #ifndef LOCKTO_RM 78 # define LOCKTO_RM 300 /* timeout for stale lockfile removal */ 79 #endif /* LOCKTO_RM */ 80 #ifndef LOCKTO_GLOB 81 # define LOCKTO_GLOB 400 /* global timeout for lockfile creation */ 82 #endif /* LOCKTO_GLOB */ 83 84 #ifdef __STDC__ 85 # include <stdarg.h> 86 # define REALLOC(ptr, size) realloc(ptr, size) 87 #else /* __STDC__ */ 88 # include <varargs.h> 89 /* define a realloc() which works for NULL pointers */ 90 # define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size)) 91 #endif /* __STDC__ */ 92 93 #if (defined(sun) && defined(__svr4__)) || defined(__SVR4) 94 # define USE_LOCKF 1 95 # define USE_SETEUID 1 96 # define _PATH_MAILDIR "/var/mail" 97 #endif /* (defined(sun) && defined(__svr4__)) || defined(__SVR4) */ 98 99 #ifdef NCR_MP_RAS3 100 # define USE_LOCKF 1 101 # define HASSNPRINTF 1 102 # define _PATH_MAILDIR "/var/mail" 103 #endif /* NCR_MP_RAS3 */ 104 105 #if defined(_AIX) 106 # define USE_LOCKF 1 107 # define USE_SETEUID 1 108 # define USE_VSYSLOG 0 109 #endif /* defined(_AIX) */ 110 111 #if defined(__hpux) 112 # define USE_LOCKF 1 113 # define USE_SETRESUID 1 114 # define USE_VSYSLOG 0 115 #endif /* defined(__hpux) */ 116 117 #ifdef DGUX 118 # define HASSNPRINTF 1 119 # define USE_LOCKF 1 120 # define USE_VSYSLOG 0 121 #endif /* DGUX */ 122 123 #if defined(_CRAY) 124 # if !defined(MAXPATHLEN) 125 # define MAXPATHLEN PATHSIZE 126 # endif /* !defined(MAXPATHLEN) */ 127 # define USE_VSYSLOG 0 128 # define _PATH_MAILDIR "/usr/spool/mail" 129 #endif /* defined(_CRAY) */ 130 131 #if defined(ultrix) 132 # define USE_VSYSLOG 0 133 #endif /* defined(ultrix) */ 134 135 #if defined(__osf__) 136 # define USE_VSYSLOG 0 137 #endif /* defined(__osf__) */ 138 139 #if defined(NeXT) && !defined(__APPLE__) 140 # include <libc.h> 141 # define _PATH_MAILDIR "/usr/spool/mail" 142 # define S_IRUSR S_IREAD 143 # define S_IWUSR S_IWRITE 144 #endif /* defined(NeXT) && !defined(__APPLE__) */ 145 146 #if defined(IRIX64) || defined(IRIX5) || defined(IRIX6) 147 # include <paths.h> 148 #endif /* defined(IRIX64) || defined(IRIX5) || defined(IRIX6) */ 149 150 /* 151 * If you don't have flock, you could try using lockf instead. 152 */ 153 154 #ifdef USE_LOCKF 155 # define flock(a, b) lockf(a, b, 0) 156 # ifdef LOCK_EX 157 # undef LOCK_EX 158 # endif /* LOCK_EX */ 159 # define LOCK_EX F_LOCK 160 #endif /* USE_LOCKF */ 161 162 #ifndef USE_VSYSLOG 163 # define USE_VSYSLOG 1 164 #endif /* ! USE_VSYSLOG */ 165 166 #ifndef LOCK_EX 167 # include <sys/file.h> 168 #endif /* ! LOCK_EX */ 169 170 #if defined(BSD4_4) || defined(__GLIBC__) 171 # include <paths.h> 172 # define _PATH_LOCTMP "/tmp/local.XXXXXX" 173 #endif /* defined(BSD4_4) || defined(__GLIBC__) */ 174 175 #ifdef BSD4_4 176 # define HAS_ST_GEN 1 177 #else /* BSD4_4 */ 178 # ifndef _BSD_VA_LIST_ 179 # define _BSD_VA_LIST_ va_list 180 # endif /* ! _BSD_VA_LIST_ */ 181 #endif /* BSD4_4 */ 182 183 #if defined(BSD4_4) || defined(linux) 184 # define HASSNPRINTF 1 185 #else /* defined(BSD4_4) || defined(linux) */ 186 # ifndef ultrix 187 extern FILE *fdopen __P((int, const char *)); 188 # endif /* ! ultrix */ 189 #endif /* defined(BSD4_4) || defined(linux) */ 190 191 #if SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) 192 # define CONTENTLENGTH 1 /* Needs the Content-Length header */ 193 #endif /* SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) */ 194 195 #if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) 196 # define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ 197 #endif /* SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) */ 198 199 #ifdef HPUX11 200 # define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ 201 #endif /* HPUX11 */ 202 203 #if _AIX4 >= 40300 204 # define HASSNPRINTF 1 /* has snprintf starting in 4.3 */ 205 #endif /* _AIX4 >= 40300 */ 206 207 #if !HASSNPRINTF 208 extern int snprintf __P((char *, size_t, const char *, ...)); 209 # ifndef _CRAY 210 extern int vsnprintf __P((char *, size_t, const char *, ...)); 211 # endif /* ! _CRAY */ 212 #endif /* !HASSNPRINTF */ 213 214 /* 215 ** If you don't have setreuid, and you have saved uids, and you have 216 ** a seteuid() call that doesn't try to emulate using setuid(), then 217 ** you can try defining USE_SETEUID. 218 */ 219 #ifdef USE_SETEUID 220 # define setreuid(r, e) seteuid(e) 221 #endif /* USE_SETEUID */ 222 223 /* 224 ** And of course on hpux you have setresuid() 225 */ 226 #ifdef USE_SETRESUID 227 # define setreuid(r, e) setresuid(-1, e, -1) 228 #endif /* USE_SETRESUID */ 229 230 #ifndef _PATH_LOCTMP 231 # define _PATH_LOCTMP "/tmp/local.XXXXXX" 232 #endif /* ! _PATH_LOCTMP */ 233 # ifndef _PATH_MAILDIR 234 # define _PATH_MAILDIR "/var/spool/mail" 235 # endif /* ! _PATH_MAILDIR */ 236 237 #ifndef S_ISREG 238 # define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG) 239 #endif /* ! S_ISREG */ 240 241 #ifdef MAILLOCK 242 # include <maillock.h> 243 #endif /* MAILLOCK */ 244 245 #ifndef INADDRSZ 246 # define INADDRSZ 4 /* size of an IPv4 address in bytes */ 247 #endif /* ! INADDRSZ */ 248 249 #ifndef MAILER_DAEMON 250 # define MAILER_DAEMON "MAILER-DAEMON" 251 #endif /* ! MAILER_DAEMON */ 252 253 #ifdef CONTENTLENGTH 254 char ContentHdr[40] = "Content-Length: "; 255 off_t HeaderLength; 256 off_t BodyLength; 257 #endif /* CONTENTLENGTH */ 258 259 bool EightBitMime = TRUE; /* advertise 8BITMIME in LMTP */ 260 int ExitVal = EX_OK; /* sysexits.h error value. */ 261 bool LMTPMode = FALSE; 262 bool bouncequota = FALSE; /* permanent error when over quota */ 263 264 void deliver __P((int, char *, bool)); 265 int e_to_sys __P((int)); 266 void notifybiff __P((char *)); 267 int store __P((char *, int)); 268 void usage __P((void)); 269 void vwarn __P((const char *, _BSD_VA_LIST_)); 270 int lockmbox __P((char *)); 271 void unlockmbox __P((void)); 272 void mailerr __P((const char *, const char *, ...)); 273 274 275 int 276 main(argc, argv) 277 int argc; 278 char *argv[]; 279 { 280 struct passwd *pw; 281 int ch, fd; 282 uid_t uid; 283 char *from; 284 extern char *optarg; 285 extern int optind; 286 extern void dolmtp __P((bool)); 287 288 289 /* make sure we have some open file descriptors */ 290 for (fd = 10; fd < 30; fd++) 291 (void) close(fd); 292 293 /* use a reasonable umask */ 294 (void) umask(0077); 295 296 # ifdef LOG_MAIL 297 openlog("mail.local", 0, LOG_MAIL); 298 # else /* LOG_MAIL */ 299 openlog("mail.local", 0); 300 # endif /* LOG_MAIL */ 301 302 from = NULL; 303 while ((ch = getopt(argc, argv, "7bdf:r:l")) != -1) 304 { 305 switch(ch) 306 { 307 case '7': /* Do not advertise 8BITMIME */ 308 EightBitMime = FALSE; 309 break; 310 311 case 'b': /* bounce mail when over quota. */ 312 bouncequota = TRUE; 313 break; 314 315 case 'd': /* Backward compatible. */ 316 break; 317 318 case 'f': 319 case 'r': /* Backward compatible. */ 320 if (from != NULL) 321 { 322 mailerr(NULL, "multiple -f options"); 323 usage(); 324 } 325 from = optarg; 326 break; 327 328 case 'l': 329 LMTPMode = TRUE; 330 break; 331 332 case '?': 333 default: 334 usage(); 335 } 336 } 337 argc -= optind; 338 argv += optind; 339 340 /* initialize biff structures */ 341 notifybiff(NULL); 342 343 if (LMTPMode) 344 dolmtp(bouncequota); 345 346 if (*argv == '\0') 347 usage(); 348 349 /* 350 ** If from not specified, use the name from getlogin() if the 351 ** uid matches, otherwise, use the name from the password file 352 ** corresponding to the uid. 353 */ 354 uid = getuid(); 355 356 if (from == NULL && ((from = getlogin()) == NULL || 357 (pw = getpwnam(from)) == NULL || 358 pw->pw_uid != uid)) 359 from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???"; 360 361 /* 362 ** There is no way to distinguish the error status of one delivery 363 ** from the rest of the deliveries. So, if we failed hard on one 364 ** or more deliveries, but had no failures on any of the others, we 365 ** return a hard failure. If we failed temporarily on one or more 366 ** deliveries, we return a temporary failure regardless of the other 367 ** failures. This results in the delivery being reattempted later 368 ** at the expense of repeated failures and multiple deliveries. 369 */ 370 for (fd = store(from, 0); *argv; ++argv) 371 deliver(fd, *argv, bouncequota); 372 exit(ExitVal); 373 /* NOTREACHED */ 374 return ExitVal; 375 } 376 377 char * 378 parseaddr(s, rcpt) 379 char *s; 380 bool rcpt; 381 { 382 char *p; 383 int l; 384 385 if (*s++ != '<') 386 return NULL; 387 388 p = s; 389 390 /* at-domain-list */ 391 while (*p == '@') 392 { 393 p++; 394 while (*p != ',' && *p != ':' && *p != '\0') 395 p++; 396 if (*p == '\0') 397 return NULL; 398 399 /* Skip over , or : */ 400 p++; 401 } 402 403 s = p; 404 405 /* local-part */ 406 while (*p != '\0' && *p != '@' && *p != '>') 407 { 408 if (*p == '\\') 409 { 410 if (*++p == '\0') 411 return NULL; 412 } 413 else if (*p == '\"') 414 { 415 p++; 416 while (*p != '\0' && *p != '\"') 417 { 418 if (*p == '\\') 419 { 420 if (*++p == '\0') 421 return NULL; 422 } 423 p++; 424 } 425 if (*p == '\0' || *(p + 1) == '\0') 426 return NULL; 427 } 428 /* +detail ? */ 429 if (*p == '+' && rcpt) 430 *p = '\0'; 431 p++; 432 } 433 434 /* @domain */ 435 if (*p == '@') 436 { 437 if (rcpt) 438 *p++ = '\0'; 439 while (*p != '\0' && *p != '>') 440 p++; 441 } 442 443 if (*p != '>') 444 return NULL; 445 else 446 *p = '\0'; 447 p++; 448 449 if (*p != '\0' && *p != ' ') 450 return NULL; 451 452 if (*s == '\0') 453 s = MAILER_DAEMON; 454 455 l = strlen(s) + 1; 456 p = malloc(l); 457 if (p == NULL) 458 { 459 printf("421 4.3.0 memory exhausted\r\n"); 460 exit(EX_TEMPFAIL); 461 } 462 463 (void) strlcpy(p, s, l); 464 return p; 465 } 466 467 char * 468 process_recipient(addr) 469 char *addr; 470 { 471 if (getpwnam(addr) == NULL) 472 return "550 5.1.1 user unknown"; 473 return NULL; 474 } 475 476 #define RCPT_GROW 30 477 478 void 479 dolmtp(bouncequota) 480 bool bouncequota; 481 { 482 char *return_path = NULL; 483 char **rcpt_addr = NULL; 484 int rcpt_num = 0; 485 int rcpt_alloc = 0; 486 bool gotlhlo = FALSE; 487 char *err; 488 int msgfd; 489 char *p; 490 int i; 491 char myhostname[1024]; 492 char buf[4096]; 493 494 (void) gethostname(myhostname, sizeof myhostname - 1); 495 496 printf("220 %s LMTP ready\r\n", myhostname); 497 for (;;) 498 { 499 (void) fflush(stdout); 500 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) 501 exit(EX_OK); 502 p = buf + strlen(buf) - 1; 503 if (p >= buf && *p == '\n') 504 *p-- = '\0'; 505 if (p >= buf && *p == '\r') 506 *p-- = '\0'; 507 508 switch (buf[0]) 509 { 510 case 'd': 511 case 'D': 512 if (strcasecmp(buf, "data") == 0) 513 { 514 if (rcpt_num == 0) 515 { 516 printf("503 5.5.1 No recipients\r\n"); 517 continue; 518 } 519 msgfd = store(return_path, rcpt_num); 520 if (msgfd == -1) 521 continue; 522 523 for (i = 0; i < rcpt_num; i++) 524 { 525 p = strchr(rcpt_addr[i], '+'); 526 if (p != NULL) 527 *p++ = '\0'; 528 deliver(msgfd, rcpt_addr[i], bouncequota); 529 } 530 (void) close(msgfd); 531 goto rset; 532 } 533 goto syntaxerr; 534 /* NOTREACHED */ 535 break; 536 537 case 'l': 538 case 'L': 539 if (strncasecmp(buf, "lhlo ", 5) == 0) 540 { 541 /* check for duplicate per RFC 1651 4.2 */ 542 if (gotlhlo) 543 { 544 printf("503 %s Duplicate LHLO\r\n", 545 myhostname); 546 continue; 547 } 548 gotlhlo = TRUE; 549 printf("250-%s\r\n", myhostname); 550 if (EightBitMime) 551 printf("250-8BITMIME\r\n"); 552 printf("250-ENHANCEDSTATUSCODES\r\n"); 553 printf("250 PIPELINING\r\n"); 554 continue; 555 } 556 goto syntaxerr; 557 /* NOTREACHED */ 558 break; 559 560 case 'm': 561 case 'M': 562 if (strncasecmp(buf, "mail ", 5) == 0) 563 { 564 if (return_path != NULL) 565 { 566 printf("503 5.5.1 Nested MAIL command\r\n"); 567 continue; 568 } 569 if (strncasecmp(buf+5, "from:", 5) != 0 || 570 ((return_path = parseaddr(buf + 10, 571 FALSE)) == NULL)) 572 { 573 printf("501 5.5.4 Syntax error in parameters\r\n"); 574 continue; 575 } 576 printf("250 2.5.0 ok\r\n"); 577 continue; 578 } 579 goto syntaxerr; 580 /* NOTREACHED */ 581 break; 582 583 case 'n': 584 case 'N': 585 if (strcasecmp(buf, "noop") == 0) 586 { 587 printf("250 2.0.0 ok\r\n"); 588 continue; 589 } 590 goto syntaxerr; 591 /* NOTREACHED */ 592 break; 593 594 case 'q': 595 case 'Q': 596 if (strcasecmp(buf, "quit") == 0) 597 { 598 printf("221 2.0.0 bye\r\n"); 599 exit(EX_OK); 600 } 601 goto syntaxerr; 602 /* NOTREACHED */ 603 break; 604 605 case 'r': 606 case 'R': 607 if (strncasecmp(buf, "rcpt ", 5) == 0) 608 { 609 if (return_path == NULL) 610 { 611 printf("503 5.5.1 Need MAIL command\r\n"); 612 continue; 613 } 614 if (rcpt_num >= rcpt_alloc) 615 { 616 rcpt_alloc += RCPT_GROW; 617 rcpt_addr = (char **) 618 REALLOC((char *)rcpt_addr, 619 rcpt_alloc * 620 sizeof(char **)); 621 if (rcpt_addr == NULL) 622 { 623 printf("421 4.3.0 memory exhausted\r\n"); 624 exit(EX_TEMPFAIL); 625 } 626 } 627 if (strncasecmp(buf + 5, "to:", 3) != 0 || 628 ((rcpt_addr[rcpt_num] = parseaddr(buf + 8, 629 TRUE)) == NULL)) 630 { 631 printf("501 5.5.4 Syntax error in parameters\r\n"); 632 continue; 633 } 634 if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) 635 { 636 printf("%s\r\n", err); 637 continue; 638 } 639 rcpt_num++; 640 printf("250 2.1.5 ok\r\n"); 641 continue; 642 } 643 else if (strcasecmp(buf, "rset") == 0) 644 { 645 printf("250 2.0.0 ok\r\n"); 646 647 rset: 648 while (rcpt_num) 649 free(rcpt_addr[--rcpt_num]); 650 if (return_path != NULL) 651 free(return_path); 652 return_path = NULL; 653 continue; 654 } 655 goto syntaxerr; 656 /* NOTREACHED */ 657 break; 658 659 case 'v': 660 case 'V': 661 if (strncasecmp(buf, "vrfy ", 5) == 0) 662 { 663 printf("252 2.3.3 try RCPT to attempt delivery\r\n"); 664 continue; 665 } 666 goto syntaxerr; 667 /* NOTREACHED */ 668 break; 669 670 default: 671 syntaxerr: 672 printf("500 5.5.2 Syntax error\r\n"); 673 continue; 674 /* NOTREACHED */ 675 break; 676 } 677 } 678 } 679 680 int 681 store(from, lmtprcpts) 682 char *from; 683 int lmtprcpts; 684 { 685 FILE *fp = NULL; 686 time_t tval; 687 bool eline; 688 bool fullline = TRUE; /* current line is terminated */ 689 bool prevfl; /* previous line was terminated */ 690 char line[2048]; 691 int fd; 692 char tmpbuf[sizeof _PATH_LOCTMP + 1]; 693 694 (void) umask(0077); 695 (void) strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf); 696 if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) 697 { 698 if (lmtprcpts) 699 { 700 printf("451 4.3.0 unable to open temporary file\r\n"); 701 return -1; 702 } 703 else 704 { 705 mailerr("451 4.3.0", "unable to open temporary file"); 706 exit(ExitVal); 707 } 708 } 709 (void) unlink(tmpbuf); 710 711 if (LMTPMode) 712 { 713 printf("354 go ahead\r\n"); 714 (void) fflush(stdout); 715 } 716 717 (void) time(&tval); 718 (void) fprintf(fp, "From %s %s", from, ctime(&tval)); 719 720 #ifdef CONTENTLENGTH 721 HeaderLength = 0; 722 BodyLength = -1; 723 #endif /* CONTENTLENGTH */ 724 725 line[0] = '\0'; 726 eline = TRUE; 727 while (fgets(line, sizeof(line), stdin) != (char *)NULL) 728 { 729 size_t line_len = 0; 730 int peek; 731 732 prevfl = fullline; /* preserve state of previous line */ 733 while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 734 line_len++; 735 line_len++; 736 737 /* Check for dot-stuffing */ 738 if (prevfl && lmtprcpts && line[0] == '.') 739 { 740 if (line[1] == '\n' || 741 (line[1] == '\r' && line[2] == '\n')) 742 goto lmtpdot; 743 memcpy(line, line + 1, line_len); 744 line_len--; 745 } 746 747 /* Check to see if we have the full line from fgets() */ 748 fullline = FALSE; 749 if (line_len > 0) 750 { 751 if (line[line_len - 1] == '\n') 752 { 753 if (line_len >= 2 && 754 line[line_len - 2] == '\r') 755 { 756 line[line_len - 2] = '\n'; 757 line[line_len - 1] = '\0'; 758 line_len--; 759 } 760 fullline = TRUE; 761 } 762 else if (line[line_len - 1] == '\r') 763 { 764 /* Did we just miss the CRLF? */ 765 peek = fgetc(stdin); 766 if (peek == '\n') 767 { 768 line[line_len - 1] = '\n'; 769 fullline = TRUE; 770 } 771 else 772 (void) ungetc(peek, stdin); 773 } 774 } 775 else 776 fullline = TRUE; 777 778 #ifdef CONTENTLENGTH 779 if (prevfl && line[0] == '\n' && HeaderLength == 0) 780 { 781 eline = FALSE; 782 HeaderLength = ftell(fp); 783 if (HeaderLength <= 0) 784 { 785 /* 786 ** shouldn't happen, unless ftell() is 787 ** badly broken 788 */ 789 790 HeaderLength = -1; 791 } 792 } 793 #else /* CONTENTLENGTH */ 794 if (prevfl && line[0] == '\n') 795 eline = TRUE; 796 #endif /* CONTENTLENGTH */ 797 else 798 { 799 if (eline && line[0] == 'F' && 800 !memcmp(line, "From ", 5)) 801 (void)putc('>', fp); 802 eline = FALSE; 803 #ifdef CONTENTLENGTH 804 /* discard existing "Content-Length:" headers */ 805 if (prevfl && HeaderLength == 0 && 806 (line[0] == 'C' || line[0] == 'c') && 807 strncasecmp(line, ContentHdr, 15) == 0) 808 { 809 /* 810 ** be paranoid: clear the line 811 ** so no "wrong matches" may occur later 812 */ 813 line[0] = '\0'; 814 continue; 815 } 816 #endif /* CONTENTLENGTH */ 817 818 } 819 (void) fwrite(line, sizeof(char), line_len, fp); 820 if (ferror(fp)) 821 { 822 if (lmtprcpts) 823 { 824 while (lmtprcpts--) 825 printf("451 4.3.0 temporary file write error\r\n"); 826 (void) fclose(fp); 827 return -1; 828 } 829 else 830 { 831 mailerr("451 4.3.0", 832 "temporary file write error"); 833 (void) fclose(fp); 834 exit(ExitVal); 835 } 836 } 837 } 838 839 if (lmtprcpts) 840 { 841 /* Got a premature EOF -- toss message and exit */ 842 exit(EX_OK); 843 } 844 845 /* If message not newline terminated, need an extra. */ 846 if (strchr(line, '\n') == NULL) 847 (void) putc('\n', fp); 848 849 lmtpdot: 850 851 #ifdef CONTENTLENGTH 852 BodyLength = ftell(fp); 853 if (HeaderLength == 0 && BodyLength > 0) /* empty body */ 854 { 855 HeaderLength = BodyLength; 856 BodyLength = 0; 857 } 858 else 859 BodyLength = BodyLength - HeaderLength - 1 ; 860 861 if (HeaderLength > 0 && BodyLength >= 0) 862 { 863 extern char *quad_to_string(); 864 865 if (sizeof BodyLength > sizeof(long)) 866 snprintf(line, sizeof line, "%s\n", 867 quad_to_string(BodyLength)); 868 else 869 snprintf(line, sizeof line, "%ld\n", (long) BodyLength); 870 strlcpy(&ContentHdr[16], line, sizeof(ContentHdr) - 16); 871 } 872 else 873 BodyLength = -1; /* Something is wrong here */ 874 #endif /* CONTENTLENGTH */ 875 876 /* Output a newline; note, empty messages are allowed. */ 877 (void) putc('\n', fp); 878 879 if (fflush(fp) == EOF || ferror(fp) != 0) 880 { 881 if (lmtprcpts) 882 { 883 while (lmtprcpts--) 884 printf("451 4.3.0 temporary file write error\r\n"); 885 (void) fclose(fp); 886 return -1; 887 } 888 else 889 { 890 mailerr("451 4.3.0", "temporary file write error"); 891 (void) fclose(fp); 892 exit(ExitVal); 893 } 894 } 895 return fd; 896 } 897 898 void 899 deliver(fd, name, bouncequota) 900 int fd; 901 char *name; 902 bool bouncequota; 903 { 904 struct stat fsb; 905 struct stat sb; 906 struct passwd *pw; 907 char path[MAXPATHLEN]; 908 int mbfd, nr = 0, nw, off; 909 char *p; 910 off_t curoff; 911 #ifdef CONTENTLENGTH 912 off_t headerbytes; 913 int readamount; 914 #endif /* CONTENTLENGTH */ 915 char biffmsg[100], buf[8*1024]; 916 extern char *quad_to_string(); 917 918 919 /* 920 ** Disallow delivery to unknown names -- special mailboxes can be 921 ** handled in the sendmail aliases file. 922 */ 923 if ((pw = getpwnam(name)) == NULL) 924 { 925 if (ExitVal != EX_TEMPFAIL) 926 ExitVal = EX_UNAVAILABLE; 927 if (LMTPMode) 928 { 929 if (ExitVal == EX_TEMPFAIL) 930 printf("451 4.3.0 cannot lookup name: %s\r\n", name); 931 else 932 printf("550 5.1.1 unknown name: %s\r\n", name); 933 } 934 else 935 { 936 char *errcode = NULL; 937 938 if (ExitVal == EX_TEMPFAIL) 939 errcode = "451 4.3.0"; 940 else 941 errcode = "550 5.1.1"; 942 mailerr(errcode, "unknown name: %s", name); 943 } 944 return; 945 } 946 endpwent(); 947 948 /* 949 ** Keep name reasonably short to avoid buffer overruns. 950 ** This isn't necessary on BSD because of the proper 951 ** definition of snprintf(), but it can cause problems 952 ** on other systems. 953 ** Also, clear out any bogus characters. 954 */ 955 956 if (strlen(name) > 40) 957 name[40] = '\0'; 958 for (p = name; *p != '\0'; p++) 959 { 960 if (!isascii(*p)) 961 *p &= 0x7f; 962 else if (!isprint(*p)) 963 *p = '.'; 964 } 965 966 967 (void) snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name); 968 969 970 /* 971 ** If the mailbox is linked or a symlink, fail. There's an obvious 972 ** race here, that the file was replaced with a symbolic link after 973 ** the lstat returned, but before the open. We attempt to detect 974 ** this by comparing the original stat information and information 975 ** returned by an fstat of the file descriptor returned by the open. 976 ** 977 ** NB: this is a symptom of a larger problem, that the mail spooling 978 ** directory is writeable by the wrong users. If that directory is 979 ** writeable, system security is compromised for other reasons, and 980 ** it cannot be fixed here. 981 ** 982 ** If we created the mailbox, set the owner/group. If that fails, 983 ** just return. Another process may have already opened it, so we 984 ** can't unlink it. Historically, binmail set the owner/group at 985 ** each mail delivery. We no longer do this, assuming that if the 986 ** ownership or permissions were changed there was a reason. 987 ** 988 ** XXX 989 ** open(2) should support flock'ing the file. 990 */ 991 992 tryagain: 993 #ifdef MAILLOCK 994 p = name; 995 #else /* MAILLOCK */ 996 p = path; 997 #endif /* MAILLOCK */ 998 if ((off = lockmbox(p)) != 0) 999 { 1000 if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL) 1001 { 1002 ExitVal = EX_TEMPFAIL; 1003 mailerr("451 4.3.0", 1004 "lockmailbox %s failed; error code %d %s", 1005 p, off, errno > 0 ? errstring(errno) : ""); 1006 } 1007 else 1008 { 1009 mailerr("551 5.3.0", 1010 "lockmailbox %s failed; error code %d %s", 1011 p, off, errno > 0 ? errstring(errno) : ""); 1012 } 1013 return; 1014 } 1015 1016 if (lstat(path, &sb) < 0) 1017 { 1018 int save_errno; 1019 int mode = S_IRUSR|S_IWUSR; 1020 gid_t gid = pw->pw_gid; 1021 1022 #ifdef MAILGID 1023 (void) umask(0007); 1024 gid = MAILGID; 1025 mode |= S_IRGRP|S_IWGRP; 1026 #endif /* MAILGID */ 1027 1028 mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, mode); 1029 1030 save_errno = errno; 1031 1032 if (lstat(path, &sb) < 0) 1033 { 1034 ExitVal = EX_CANTCREAT; 1035 mailerr("550 5.2.0", 1036 "%s: lstat: file changed after open", path); 1037 goto err1; 1038 } 1039 else 1040 sb.st_uid = pw->pw_uid; 1041 if (mbfd == -1) 1042 { 1043 if (save_errno == EEXIST) 1044 goto tryagain; 1045 } 1046 else if (fchown(mbfd, pw->pw_uid, gid) < 0) 1047 { 1048 mailerr("451 4.3.0", "chown %u.%u: %s", 1049 pw->pw_uid, gid, name); 1050 goto err1; 1051 } 1052 } 1053 else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) 1054 { 1055 mailerr("550 5.2.0", "%s: irregular file", path); 1056 goto err0; 1057 } 1058 else if (sb.st_uid != pw->pw_uid) 1059 { 1060 ExitVal = EX_CANTCREAT; 1061 mailerr("550 5.2.0", "%s: wrong ownership (%d)", 1062 path, sb.st_uid); 1063 goto err0; 1064 } 1065 else 1066 mbfd = open(path, O_APPEND|O_WRONLY, 0); 1067 1068 if (mbfd == -1) 1069 { 1070 mailerr("450 4.2.0", "%s: %s", path, errstring(errno)); 1071 goto err0; 1072 } 1073 else if (fstat(mbfd, &fsb) < 0 || 1074 fsb.st_nlink != 1 || 1075 sb.st_nlink != 1 || 1076 !S_ISREG(fsb.st_mode) || 1077 sb.st_dev != fsb.st_dev || 1078 sb.st_ino != fsb.st_ino || 1079 #if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 1080 sb.st_gen != fsb.st_gen || 1081 #endif /* HAS_ST_GEN && 0 */ 1082 sb.st_uid != fsb.st_uid) 1083 { 1084 ExitVal = EX_TEMPFAIL; 1085 mailerr("550 5.2.0", "%s: fstat: file changed after open", 1086 path); 1087 goto err1; 1088 } 1089 1090 1091 /* Wait until we can get a lock on the file. */ 1092 if (flock(mbfd, LOCK_EX) < 0) 1093 { 1094 mailerr("450 4.2.0", "%s: %s", path, errstring(errno)); 1095 goto err1; 1096 } 1097 1098 /* Get the starting offset of the new message for biff. */ 1099 curoff = lseek(mbfd, (off_t)0, SEEK_END); 1100 if (sizeof curoff > sizeof(long)) 1101 (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%s\n", 1102 name, quad_to_string(curoff)); 1103 else 1104 (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%ld\n", 1105 name, (long) curoff); 1106 1107 /* Copy the message into the file. */ 1108 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) 1109 { 1110 mailerr("450 4.2.0", "temporary file: %s", 1111 errstring(errno)); 1112 goto err1; 1113 } 1114 if (setreuid(0, pw->pw_uid) < 0) 1115 { 1116 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", 1117 pw->pw_uid, errstring(errno), getuid(), geteuid()); 1118 goto err1; 1119 } 1120 #ifdef DEBUG 1121 fprintf(stderr, "new euid = %d\n", geteuid()); 1122 #endif /* DEBUG */ 1123 #ifdef CONTENTLENGTH 1124 headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; 1125 for (;;) 1126 { 1127 if (headerbytes == 0) 1128 { 1129 snprintf(buf, sizeof buf, "%s", ContentHdr); 1130 nr = strlen(buf); 1131 headerbytes = -1; 1132 readamount = 0; 1133 } 1134 else if (headerbytes > sizeof(buf) || headerbytes < 0) 1135 readamount = sizeof(buf); 1136 else 1137 readamount = headerbytes; 1138 if (readamount != 0) 1139 nr = read(fd, buf, readamount); 1140 if (nr <= 0) 1141 break; 1142 if (headerbytes > 0) 1143 headerbytes -= nr ; 1144 1145 #else /* CONTENTLENGTH */ 1146 while ((nr = read(fd, buf, sizeof(buf))) > 0) 1147 { 1148 #endif /* CONTENTLENGTH */ 1149 for (off = 0; off < nr; off += nw) 1150 { 1151 if ((nw = write(mbfd, buf + off, nr - off)) < 0) 1152 { 1153 #ifdef EDQUOT 1154 if (errno == EDQUOT && bouncequota) 1155 mailerr("552 5.2.2", "%s: %s", 1156 path, errstring(errno)); 1157 else 1158 #endif /* EDQUOT */ 1159 mailerr("450 4.2.0", "%s: %s", 1160 path, errstring(errno)); 1161 goto err3; 1162 } 1163 } 1164 } 1165 if (nr < 0) 1166 { 1167 mailerr("450 4.2.0", "temporary file: %s", 1168 errstring(errno)); 1169 goto err3; 1170 } 1171 1172 /* Flush to disk, don't wait for update. */ 1173 if (fsync(mbfd) < 0) 1174 { 1175 mailerr("450 4.2.0", "%s: %s", path, errstring(errno)); 1176 err3: 1177 if (setreuid(0, 0) < 0) 1178 { 1179 #if 0 1180 /* already printed an error above for this recipient */ 1181 (void) e_to_sys(errno); 1182 mailerr("450 4.2.0", "setreuid(0, 0): %s", 1183 errstring(errno)); 1184 #endif /* 0 */ 1185 } 1186 #ifdef DEBUG 1187 fprintf(stderr, "reset euid = %d\n", geteuid()); 1188 #endif /* DEBUG */ 1189 (void) ftruncate(mbfd, curoff); 1190 err1: (void) close(mbfd); 1191 err0: unlockmbox(); 1192 return; 1193 } 1194 1195 /* Close and check -- NFS doesn't write until the close. */ 1196 if (close(mbfd)) 1197 { 1198 #ifdef EDQUOT 1199 if (errno == EDQUOT && bouncequota) 1200 mailerr("552 5.2.2", "%s: %s", path, errstring(errno)); 1201 else 1202 #endif /* EDQUOT */ 1203 mailerr("450 4.2.0", "%s: %s", path, errstring(errno)); 1204 (void) truncate(path, curoff); 1205 } 1206 else 1207 notifybiff(biffmsg); 1208 1209 if (setreuid(0, 0) < 0) 1210 { 1211 mailerr("450 4.2.0", "setreuid(0, 0): %s", 1212 errstring(errno)); 1213 goto err0; 1214 } 1215 #ifdef DEBUG 1216 fprintf(stderr, "reset euid = %d\n", geteuid()); 1217 #endif /* DEBUG */ 1218 unlockmbox(); 1219 if (LMTPMode) 1220 printf("250 2.1.5 %s OK\r\n", name); 1221 } 1222 1223 /* 1224 ** user.lock files are necessary for compatibility with other 1225 ** systems, e.g., when the mail spool file is NFS exported. 1226 ** Alas, mailbox locking is more than just a local matter. 1227 ** EPA 11/94. 1228 */ 1229 1230 bool Locked = FALSE; 1231 1232 #ifdef MAILLOCK 1233 int 1234 lockmbox(name) 1235 char *name; 1236 { 1237 int r; 1238 1239 if (Locked) 1240 return 0; 1241 if ((r = maillock(name, 15)) == L_SUCCESS) 1242 { 1243 Locked = TRUE; 1244 return 0; 1245 } 1246 switch (r) 1247 { 1248 case L_TMPLOCK: /* Can't create tmp file */ 1249 case L_TMPWRITE: /* Can't write pid into lockfile */ 1250 case L_MAXTRYS: /* Failed after retrycnt attempts */ 1251 errno = 0; 1252 r = EX_TEMPFAIL; 1253 break; 1254 case L_ERROR: /* Check errno for reason */ 1255 r = errno; 1256 break; 1257 default: /* other permanent errors */ 1258 errno = 0; 1259 r = EX_UNAVAILABLE; 1260 break; 1261 } 1262 return r; 1263 } 1264 1265 void 1266 unlockmbox() 1267 { 1268 if (Locked) 1269 mailunlock(); 1270 Locked = FALSE; 1271 } 1272 #else /* MAILLOCK */ 1273 1274 char LockName[MAXPATHLEN]; 1275 1276 int 1277 lockmbox(path) 1278 char *path; 1279 { 1280 int statfailed = 0; 1281 time_t start; 1282 1283 if (Locked) 1284 return 0; 1285 if (strlen(path) + 6 > sizeof LockName) 1286 return EX_SOFTWARE; 1287 (void) snprintf(LockName, sizeof LockName, "%s.lock", path); 1288 (void) time(&start); 1289 for (; ; sleep(5)) 1290 { 1291 int fd; 1292 struct stat st; 1293 time_t now; 1294 1295 /* global timeout */ 1296 (void) time(&now); 1297 if (now > start + LOCKTO_GLOB) 1298 { 1299 errno = 0; 1300 return EX_TEMPFAIL; 1301 } 1302 fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, 0); 1303 if (fd >= 0) 1304 { 1305 /* defeat lock checking programs which test pid */ 1306 (void) write(fd, "0", 2); 1307 Locked = TRUE; 1308 (void) close(fd); 1309 return 0; 1310 } 1311 if (stat(LockName, &st) < 0) 1312 { 1313 if (statfailed++ > 5) 1314 { 1315 errno = 0; 1316 return EX_TEMPFAIL; 1317 } 1318 continue; 1319 } 1320 statfailed = 0; 1321 (void) time(&now); 1322 if (now < st.st_ctime + LOCKTO_RM) 1323 continue; 1324 1325 /* try to remove stale lockfile */ 1326 if (unlink(LockName) < 0) 1327 return errno; 1328 } 1329 } 1330 1331 void 1332 unlockmbox() 1333 { 1334 if (!Locked) 1335 return; 1336 (void) unlink(LockName); 1337 Locked = FALSE; 1338 } 1339 #endif /* MAILLOCK */ 1340 1341 void 1342 notifybiff(msg) 1343 char *msg; 1344 { 1345 static bool initialized = FALSE; 1346 static int f = -1; 1347 struct hostent *hp; 1348 struct servent *sp; 1349 int len; 1350 static struct sockaddr_in addr; 1351 1352 if (!initialized) 1353 { 1354 initialized = TRUE; 1355 1356 /* Be silent if biff service not available. */ 1357 if ((sp = getservbyname("biff", "udp")) == NULL || 1358 (hp = gethostbyname("localhost")) == NULL || 1359 hp->h_length != INADDRSZ) 1360 return; 1361 1362 addr.sin_family = hp->h_addrtype; 1363 memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ); 1364 addr.sin_port = sp->s_port; 1365 } 1366 1367 /* No message, just return */ 1368 if (msg == NULL) 1369 return; 1370 1371 /* Couldn't initialize addr struct */ 1372 if (addr.sin_family == AF_UNSPEC) 1373 return; 1374 1375 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 1376 return; 1377 len = strlen(msg) + 1; 1378 (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr)); 1379 } 1380 1381 void 1382 usage() 1383 { 1384 ExitVal = EX_USAGE; 1385 mailerr(NULL, "usage: mail.local [-l] [-f from] user ..."); 1386 exit(ExitVal); 1387 } 1388 1389 void 1390 #ifdef __STDC__ 1391 mailerr(const char *hdr, const char *fmt, ...) 1392 #else /* __STDC__ */ 1393 mailerr(hdr, fmt, va_alist) 1394 const char *hdr; 1395 const char *fmt; 1396 va_dcl 1397 #endif /* __STDC__ */ 1398 { 1399 va_list ap; 1400 1401 #ifdef __STDC__ 1402 va_start(ap, fmt); 1403 #else /* __STDC__ */ 1404 va_start(ap); 1405 #endif /* __STDC__ */ 1406 if (LMTPMode) 1407 { 1408 if (hdr != NULL) 1409 printf("%s ", hdr); 1410 (void) vprintf(fmt, ap); 1411 (void) printf("\r\n"); 1412 } 1413 else 1414 { 1415 (void) e_to_sys(errno); 1416 vwarn(fmt, ap); 1417 } 1418 } 1419 1420 void 1421 vwarn(fmt, ap) 1422 const char *fmt; 1423 _BSD_VA_LIST_ ap; 1424 { 1425 /* 1426 ** Log the message to stderr. 1427 ** 1428 ** Don't use LOG_PERROR as an openlog() flag to do this, 1429 ** it's not portable enough. 1430 */ 1431 1432 if (ExitVal != EX_USAGE) 1433 (void) fprintf(stderr, "mail.local: "); 1434 (void) vfprintf(stderr, fmt, ap); 1435 (void) fprintf(stderr, "\n"); 1436 1437 #if USE_VSYSLOG 1438 /* Log the message to syslog. */ 1439 vsyslog(LOG_ERR, fmt, ap); 1440 #else /* USE_VSYSLOG */ 1441 { 1442 char fmtbuf[10240]; 1443 1444 (void) vsnprintf(fmtbuf, sizeof fmtbuf, fmt, ap); 1445 syslog(LOG_ERR, "%s", fmtbuf); 1446 } 1447 #endif /* USE_VSYSLOG */ 1448 } 1449 1450 /* 1451 * e_to_sys -- 1452 * Guess which errno's are temporary. Gag me. 1453 */ 1454 int 1455 e_to_sys(num) 1456 int num; 1457 { 1458 /* Temporary failures override hard errors. */ 1459 if (ExitVal == EX_TEMPFAIL) 1460 return ExitVal; 1461 1462 switch (num) /* Hopefully temporary errors. */ 1463 { 1464 #ifdef EDQUOT 1465 case EDQUOT: /* Disc quota exceeded */ 1466 if (bouncequota) 1467 { 1468 ExitVal = EX_UNAVAILABLE; 1469 break; 1470 } 1471 /* FALLTHROUGH */ 1472 #endif /* EDQUOT */ 1473 #ifdef EAGAIN 1474 case EAGAIN: /* Resource temporarily unavailable */ 1475 #endif /* EAGAIN */ 1476 #ifdef EBUSY 1477 case EBUSY: /* Device busy */ 1478 #endif /* EBUSY */ 1479 #ifdef EPROCLIM 1480 case EPROCLIM: /* Too many processes */ 1481 #endif /* EPROCLIM */ 1482 #ifdef EUSERS 1483 case EUSERS: /* Too many users */ 1484 #endif /* EUSERS */ 1485 #ifdef ECONNABORTED 1486 case ECONNABORTED: /* Software caused connection abort */ 1487 #endif /* ECONNABORTED */ 1488 #ifdef ECONNREFUSED 1489 case ECONNREFUSED: /* Connection refused */ 1490 #endif /* ECONNREFUSED */ 1491 #ifdef ECONNRESET 1492 case ECONNRESET: /* Connection reset by peer */ 1493 #endif /* ECONNRESET */ 1494 #ifdef EDEADLK 1495 case EDEADLK: /* Resource deadlock avoided */ 1496 #endif /* EDEADLK */ 1497 #ifdef EFBIG 1498 case EFBIG: /* File too large */ 1499 #endif /* EFBIG */ 1500 #ifdef EHOSTDOWN 1501 case EHOSTDOWN: /* Host is down */ 1502 #endif /* EHOSTDOWN */ 1503 #ifdef EHOSTUNREACH 1504 case EHOSTUNREACH: /* No route to host */ 1505 #endif /* EHOSTUNREACH */ 1506 #ifdef EMFILE 1507 case EMFILE: /* Too many open files */ 1508 #endif /* EMFILE */ 1509 #ifdef ENETDOWN 1510 case ENETDOWN: /* Network is down */ 1511 #endif /* ENETDOWN */ 1512 #ifdef ENETRESET 1513 case ENETRESET: /* Network dropped connection on reset */ 1514 #endif /* ENETRESET */ 1515 #ifdef ENETUNREACH 1516 case ENETUNREACH: /* Network is unreachable */ 1517 #endif /* ENETUNREACH */ 1518 #ifdef ENFILE 1519 case ENFILE: /* Too many open files in system */ 1520 #endif /* ENFILE */ 1521 #ifdef ENOBUFS 1522 case ENOBUFS: /* No buffer space available */ 1523 #endif /* ENOBUFS */ 1524 #ifdef ENOMEM 1525 case ENOMEM: /* Cannot allocate memory */ 1526 #endif /* ENOMEM */ 1527 #ifdef ENOSPC 1528 case ENOSPC: /* No space left on device */ 1529 #endif /* ENOSPC */ 1530 #ifdef EROFS 1531 case EROFS: /* Read-only file system */ 1532 #endif /* EROFS */ 1533 #ifdef ESTALE 1534 case ESTALE: /* Stale NFS file handle */ 1535 #endif /* ESTALE */ 1536 #ifdef ETIMEDOUT 1537 case ETIMEDOUT: /* Connection timed out */ 1538 #endif /* ETIMEDOUT */ 1539 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK 1540 case EWOULDBLOCK: /* Operation would block. */ 1541 #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */ 1542 ExitVal = EX_TEMPFAIL; 1543 break; 1544 1545 default: 1546 ExitVal = EX_UNAVAILABLE; 1547 break; 1548 } 1549 return ExitVal; 1550 } 1551 1552 #if defined(ultrix) || defined(_CRAY) 1553 /* 1554 * Copyright (c) 1987, 1993 1555 * The Regents of the University of California. All rights reserved. 1556 * 1557 * Redistribution and use in source and binary forms, with or without 1558 * modification, are permitted provided that the following conditions 1559 * are met: 1560 * 1. Redistributions of source code must retain the above copyright 1561 * notice, this list of conditions and the following disclaimer. 1562 * 2. Redistributions in binary form must reproduce the above copyright 1563 * notice, this list of conditions and the following disclaimer in the 1564 * documentation and/or other materials provided with the distribution. 1565 * 3. All advertising materials mentioning features or use of this software 1566 * must display the following acknowledgement: 1567 * This product includes software developed by the University of 1568 * California, Berkeley and its contributors. 1569 * 4. Neither the name of the University nor the names of its contributors 1570 * may be used to endorse or promote products derived from this software 1571 * without specific prior written permission. 1572 * 1573 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1574 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1575 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1576 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1577 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1578 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1579 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1580 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1581 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1582 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1583 * SUCH DAMAGE. 1584 */ 1585 1586 # if defined(LIBC_SCCS) && !defined(lint) 1587 static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; 1588 # endif /* defined(LIBC_SCCS) && !defined(lint) */ 1589 1590 # include <sys/types.h> 1591 # include <sys/stat.h> 1592 # include <fcntl.h> 1593 # include <errno.h> 1594 # include <stdio.h> 1595 # include <ctype.h> 1596 1597 static int _gettemp(); 1598 1599 mkstemp(path) 1600 char *path; 1601 { 1602 int fd; 1603 1604 return (_gettemp(path, &fd) ? fd : -1); 1605 } 1606 1607 # if 0 1608 char * 1609 mktemp(path) 1610 char *path; 1611 { 1612 return(_gettemp(path, (int *)NULL) ? path : (char *)NULL); 1613 } 1614 # endif /* 0 */ 1615 1616 static 1617 _gettemp(path, doopen) 1618 char *path; 1619 register int *doopen; 1620 { 1621 extern int errno; 1622 register char *start, *trv; 1623 struct stat sbuf; 1624 u_int pid; 1625 1626 pid = getpid(); 1627 for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ 1628 while (*--trv == 'X') 1629 { 1630 *trv = (pid % 10) + '0'; 1631 pid /= 10; 1632 } 1633 1634 /* 1635 * check the target directory; if you have six X's and it 1636 * doesn't exist this runs for a *very* long time. 1637 */ 1638 for (start = trv + 1;; --trv) 1639 { 1640 if (trv <= path) 1641 break; 1642 if (*trv == '/') 1643 { 1644 *trv = '\0'; 1645 if (stat(path, &sbuf) < 0) 1646 return(0); 1647 if (!S_ISDIR(sbuf.st_mode)) 1648 { 1649 errno = ENOTDIR; 1650 return(0); 1651 } 1652 *trv = '/'; 1653 break; 1654 } 1655 } 1656 1657 for (;;) 1658 { 1659 if (doopen) 1660 { 1661 if ((*doopen = 1662 open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) 1663 return(1); 1664 if (errno != EEXIST) 1665 return(0); 1666 } 1667 else if (stat(path, &sbuf) < 0) 1668 return(errno == ENOENT ? 1 : 0); 1669 1670 /* tricky little algorithm for backward compatibility */ 1671 for (trv = start;;) 1672 { 1673 if (!*trv) 1674 return(0); 1675 if (*trv == 'z') 1676 *trv++ = 'a'; 1677 else 1678 { 1679 if (isascii(*trv) && isdigit(*trv)) 1680 *trv = 'a'; 1681 else 1682 ++*trv; 1683 break; 1684 } 1685 } 1686 } 1687 /* NOTREACHED */ 1688 } 1689 #endif /* defined(ultrix) || defined(_CRAY) */ 1690