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