1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3*0Sstevel@tonic-gate * Copyright (c) 1990, 1993, 1994 4*0Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 5*0Sstevel@tonic-gate * 6*0Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 7*0Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level 8*0Sstevel@tonic-gate * of the sendmail distribution. 9*0Sstevel@tonic-gate */ 10*0Sstevel@tonic-gate 11*0Sstevel@tonic-gate /* 12*0Sstevel@tonic-gate * Copyright 1994-2002 Sun Microsystems, Inc. All rights reserved. 13*0Sstevel@tonic-gate * Use is subject to license terms. 14*0Sstevel@tonic-gate */ 15*0Sstevel@tonic-gate 16*0Sstevel@tonic-gate #ifndef lint 17*0Sstevel@tonic-gate static char copyright[] = 18*0Sstevel@tonic-gate "@(#) Copyright (c) 1990, 1993, 1994\n\ 19*0Sstevel@tonic-gate The Regents of the University of California. All rights reserved.\n"; 20*0Sstevel@tonic-gate #endif /* not lint */ 21*0Sstevel@tonic-gate 22*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 23*0Sstevel@tonic-gate 24*0Sstevel@tonic-gate #ifndef lint 25*0Sstevel@tonic-gate static char sccsid[] = "@(#)mail.local.c 8.83 (Berkeley) 12/17/98"; 26*0Sstevel@tonic-gate static char sccsi2[] = "%W% (Sun) %G%"; 27*0Sstevel@tonic-gate #endif /* not lint */ 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/param.h> 30*0Sstevel@tonic-gate #include <sys/stat.h> 31*0Sstevel@tonic-gate #include <sys/socket.h> 32*0Sstevel@tonic-gate #include <sys/file.h> 33*0Sstevel@tonic-gate 34*0Sstevel@tonic-gate #include <netinet/in.h> 35*0Sstevel@tonic-gate 36*0Sstevel@tonic-gate #include <errno.h> 37*0Sstevel@tonic-gate #include <fcntl.h> 38*0Sstevel@tonic-gate #include <netdb.h> 39*0Sstevel@tonic-gate #include <pwd.h> 40*0Sstevel@tonic-gate #include <stdio.h> 41*0Sstevel@tonic-gate #include <stdlib.h> 42*0Sstevel@tonic-gate #include <signal.h> 43*0Sstevel@tonic-gate #include <ctype.h> 44*0Sstevel@tonic-gate #include <string.h> 45*0Sstevel@tonic-gate #include <sysexits.h> 46*0Sstevel@tonic-gate #include <time.h> 47*0Sstevel@tonic-gate #include <unistd.h> 48*0Sstevel@tonic-gate #include <maillock.h> 49*0Sstevel@tonic-gate #include <grp.h> 50*0Sstevel@tonic-gate 51*0Sstevel@tonic-gate #ifdef __STDC__ 52*0Sstevel@tonic-gate #include <stdarg.h> 53*0Sstevel@tonic-gate #else 54*0Sstevel@tonic-gate #include <varargs.h> 55*0Sstevel@tonic-gate #endif 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate #include <syslog.h> 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate #include <sysexits.h> 60*0Sstevel@tonic-gate #include <ctype.h> 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate #include <sm/conf.h> 63*0Sstevel@tonic-gate #include <sendmail/pathnames.h> 64*0Sstevel@tonic-gate 65*0Sstevel@tonic-gate /* 66*0Sstevel@tonic-gate ** If you don't have flock, you could try using lockf instead. 67*0Sstevel@tonic-gate */ 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate #ifdef LDA_USE_LOCKF 70*0Sstevel@tonic-gate # define flock(a, b) lockf(a, b, 0) 71*0Sstevel@tonic-gate # ifdef LOCK_EX 72*0Sstevel@tonic-gate # undef LOCK_EX 73*0Sstevel@tonic-gate # endif /* LOCK_EX */ 74*0Sstevel@tonic-gate # define LOCK_EX F_LOCK 75*0Sstevel@tonic-gate #endif /* LDA_USE_LOCKF */ 76*0Sstevel@tonic-gate 77*0Sstevel@tonic-gate #ifndef LOCK_EX 78*0Sstevel@tonic-gate # include <sys/file.h> 79*0Sstevel@tonic-gate #endif /* ! LOCK_EX */ 80*0Sstevel@tonic-gate 81*0Sstevel@tonic-gate #ifndef MAILER_DAEMON 82*0Sstevel@tonic-gate # define MAILER_DAEMON "MAILER-DAEMON" 83*0Sstevel@tonic-gate #endif 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gate typedef int bool; 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate #define FALSE 0 88*0Sstevel@tonic-gate #define TRUE 1 89*0Sstevel@tonic-gate 90*0Sstevel@tonic-gate bool EightBitMime = TRUE; /* advertise 8BITMIME in LMTP */ 91*0Sstevel@tonic-gate static int eval = EX_OK; /* sysexits.h error value. */ 92*0Sstevel@tonic-gate static int lmtpmode = 0; 93*0Sstevel@tonic-gate bool bouncequota = FALSE; /* permanent error when over quota */ 94*0Sstevel@tonic-gate 95*0Sstevel@tonic-gate #define _PATH_MAILDIR "/var/mail" 96*0Sstevel@tonic-gate #define _PATH_LOCTMP "/tmp/local.XXXXXX" 97*0Sstevel@tonic-gate #define _PATH_LOCHTMP "/tmp/lochd.XXXXXX" 98*0Sstevel@tonic-gate #define FALSE 0 99*0Sstevel@tonic-gate #define TRUE 1 100*0Sstevel@tonic-gate #define MAXLINE 2048 101*0Sstevel@tonic-gate 102*0Sstevel@tonic-gate static void deliver(int, int, char *, bool); 103*0Sstevel@tonic-gate static void e_to_sys(int); 104*0Sstevel@tonic-gate static void err(const char *fmt, ...); 105*0Sstevel@tonic-gate static void notifybiff(char *); 106*0Sstevel@tonic-gate static void store(char *, int); 107*0Sstevel@tonic-gate static void usage(void); 108*0Sstevel@tonic-gate static void vwarn(); 109*0Sstevel@tonic-gate static void warn(const char *fmt, ...); 110*0Sstevel@tonic-gate static void mailerr(const char *, const char *, ...); 111*0Sstevel@tonic-gate static void sigterm_handler(); 112*0Sstevel@tonic-gate 113*0Sstevel@tonic-gate static char unix_from_line[MAXLINE]; 114*0Sstevel@tonic-gate static int ulen; 115*0Sstevel@tonic-gate static int content_length; 116*0Sstevel@tonic-gate static int bfd, hfd; /* temp file */ 117*0Sstevel@tonic-gate static uid_t src_uid, targ_uid, saved_uid; 118*0Sstevel@tonic-gate static int sigterm_caught; 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gate int 121*0Sstevel@tonic-gate main(argc, argv) 122*0Sstevel@tonic-gate int argc; 123*0Sstevel@tonic-gate char *argv[]; 124*0Sstevel@tonic-gate { 125*0Sstevel@tonic-gate struct passwd *pw; 126*0Sstevel@tonic-gate int ch; 127*0Sstevel@tonic-gate uid_t uid; 128*0Sstevel@tonic-gate char *from; 129*0Sstevel@tonic-gate struct group *grpptr; 130*0Sstevel@tonic-gate void dolmtp(); 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gate openlog("mail.local", 0, LOG_MAIL); 133*0Sstevel@tonic-gate 134*0Sstevel@tonic-gate from = NULL; 135*0Sstevel@tonic-gate pw = NULL; 136*0Sstevel@tonic-gate sigterm_caught = FALSE; 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate (void) sigset(SIGTERM, sigterm_handler); 139*0Sstevel@tonic-gate 140*0Sstevel@tonic-gate while ((ch = getopt(argc, argv, "7bdf:r:l")) != EOF) 141*0Sstevel@tonic-gate switch (ch) { 142*0Sstevel@tonic-gate case '7': /* Do not advertise 8BITMIME */ 143*0Sstevel@tonic-gate EightBitMime = FALSE; 144*0Sstevel@tonic-gate break; 145*0Sstevel@tonic-gate 146*0Sstevel@tonic-gate case 'b': /* bounce mail when over quota. */ 147*0Sstevel@tonic-gate bouncequota = TRUE; 148*0Sstevel@tonic-gate break; 149*0Sstevel@tonic-gate 150*0Sstevel@tonic-gate case 'd': /* Backward compatible. */ 151*0Sstevel@tonic-gate break; 152*0Sstevel@tonic-gate case 'f': 153*0Sstevel@tonic-gate case 'r': /* Backward compatible. */ 154*0Sstevel@tonic-gate if (from != NULL) { 155*0Sstevel@tonic-gate warn("multiple -f options"); 156*0Sstevel@tonic-gate usage(); 157*0Sstevel@tonic-gate } 158*0Sstevel@tonic-gate from = optarg; 159*0Sstevel@tonic-gate break; 160*0Sstevel@tonic-gate case 'l': 161*0Sstevel@tonic-gate lmtpmode++; 162*0Sstevel@tonic-gate break; 163*0Sstevel@tonic-gate case '?': 164*0Sstevel@tonic-gate default: 165*0Sstevel@tonic-gate usage(); 166*0Sstevel@tonic-gate } 167*0Sstevel@tonic-gate argc -= optind; 168*0Sstevel@tonic-gate argv += optind; 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gate notifybiff(NULL); /* initialize biff structures */ 171*0Sstevel@tonic-gate 172*0Sstevel@tonic-gate /* 173*0Sstevel@tonic-gate * We expect sendmail will invoke us with saved id 0 174*0Sstevel@tonic-gate * We then do setgid and setuid defore delivery 175*0Sstevel@tonic-gate * setgid to mail group 176*0Sstevel@tonic-gate */ 177*0Sstevel@tonic-gate if ((grpptr = getgrnam("mail")) != NULL) 178*0Sstevel@tonic-gate (void) setgid(grpptr->gr_gid); 179*0Sstevel@tonic-gate saved_uid = geteuid(); 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate if (lmtpmode) { 182*0Sstevel@tonic-gate if (saved_uid != 0) { 183*0Sstevel@tonic-gate warn("only super-user can use -l option"); 184*0Sstevel@tonic-gate exit(EX_CANTCREAT); 185*0Sstevel@tonic-gate } 186*0Sstevel@tonic-gate dolmtp(bouncequota); 187*0Sstevel@tonic-gate } 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gate if (!*argv) 190*0Sstevel@tonic-gate usage(); 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate /* 193*0Sstevel@tonic-gate * If from not specified, use the name from getlogin() if the 194*0Sstevel@tonic-gate * uid matches, otherwise, use the name from the password file 195*0Sstevel@tonic-gate * corresponding to the uid. 196*0Sstevel@tonic-gate */ 197*0Sstevel@tonic-gate uid = getuid(); 198*0Sstevel@tonic-gate if (!from && (!(from = getlogin()) || 199*0Sstevel@tonic-gate !(pw = getpwnam(from)) || pw->pw_uid != uid)) 200*0Sstevel@tonic-gate from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; 201*0Sstevel@tonic-gate src_uid = pw ? pw->pw_uid : uid; 202*0Sstevel@tonic-gate 203*0Sstevel@tonic-gate /* 204*0Sstevel@tonic-gate * There is no way to distinguish the error status of one delivery 205*0Sstevel@tonic-gate * from the rest of the deliveries. So, if we failed hard on one 206*0Sstevel@tonic-gate * or more deliveries, but had no failures on any of the others, we 207*0Sstevel@tonic-gate * return a hard failure. If we failed temporarily on one or more 208*0Sstevel@tonic-gate * deliveries, we return a temporary failure regardless of the other 209*0Sstevel@tonic-gate * failures. This results in the delivery being reattempted later 210*0Sstevel@tonic-gate * at the expense of repeated failures and multiple deliveries. 211*0Sstevel@tonic-gate */ 212*0Sstevel@tonic-gate 213*0Sstevel@tonic-gate for (store(from, 0); *argv; ++argv) 214*0Sstevel@tonic-gate deliver(hfd, bfd, *argv, bouncequota); 215*0Sstevel@tonic-gate return (eval); 216*0Sstevel@tonic-gate } 217*0Sstevel@tonic-gate 218*0Sstevel@tonic-gate void 219*0Sstevel@tonic-gate sigterm_handler() 220*0Sstevel@tonic-gate { 221*0Sstevel@tonic-gate sigterm_caught = TRUE; 222*0Sstevel@tonic-gate (void) sigignore(SIGTERM); 223*0Sstevel@tonic-gate } 224*0Sstevel@tonic-gate 225*0Sstevel@tonic-gate char * 226*0Sstevel@tonic-gate parseaddr(s) 227*0Sstevel@tonic-gate char *s; 228*0Sstevel@tonic-gate { 229*0Sstevel@tonic-gate char *p; 230*0Sstevel@tonic-gate int len; 231*0Sstevel@tonic-gate 232*0Sstevel@tonic-gate if (*s++ != '<') 233*0Sstevel@tonic-gate return NULL; 234*0Sstevel@tonic-gate 235*0Sstevel@tonic-gate p = s; 236*0Sstevel@tonic-gate 237*0Sstevel@tonic-gate /* at-domain-list */ 238*0Sstevel@tonic-gate while (*p == '@') { 239*0Sstevel@tonic-gate p++; 240*0Sstevel@tonic-gate if (*p == '[') { 241*0Sstevel@tonic-gate p++; 242*0Sstevel@tonic-gate while (isascii(*p) && 243*0Sstevel@tonic-gate (isalnum(*p) || *p == '.' || 244*0Sstevel@tonic-gate *p == '-' || *p == ':')) 245*0Sstevel@tonic-gate p++; 246*0Sstevel@tonic-gate if (*p++ != ']') 247*0Sstevel@tonic-gate return NULL; 248*0Sstevel@tonic-gate } else { 249*0Sstevel@tonic-gate while ((isascii(*p) && isalnum(*p)) || 250*0Sstevel@tonic-gate strchr(".-_", *p)) 251*0Sstevel@tonic-gate p++; 252*0Sstevel@tonic-gate } 253*0Sstevel@tonic-gate if (*p == ',' && p[1] == '@') 254*0Sstevel@tonic-gate p++; 255*0Sstevel@tonic-gate else if (*p == ':' && p[1] != '@') 256*0Sstevel@tonic-gate p++; 257*0Sstevel@tonic-gate else 258*0Sstevel@tonic-gate return NULL; 259*0Sstevel@tonic-gate } 260*0Sstevel@tonic-gate 261*0Sstevel@tonic-gate s = p; 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate /* local-part */ 264*0Sstevel@tonic-gate if (*p == '\"') { 265*0Sstevel@tonic-gate p++; 266*0Sstevel@tonic-gate while (*p && *p != '\"') { 267*0Sstevel@tonic-gate if (*p == '\\') { 268*0Sstevel@tonic-gate if (!*++p) 269*0Sstevel@tonic-gate return NULL; 270*0Sstevel@tonic-gate } 271*0Sstevel@tonic-gate p++; 272*0Sstevel@tonic-gate } 273*0Sstevel@tonic-gate if (!*p++) 274*0Sstevel@tonic-gate return NULL; 275*0Sstevel@tonic-gate } else { 276*0Sstevel@tonic-gate while (*p && *p != '@' && *p != '>') { 277*0Sstevel@tonic-gate if (*p == '\\') { 278*0Sstevel@tonic-gate if (!*++p) 279*0Sstevel@tonic-gate return NULL; 280*0Sstevel@tonic-gate } else { 281*0Sstevel@tonic-gate if (*p <= ' ' || (*p & 128) || 282*0Sstevel@tonic-gate strchr("<>()[]\\,;:\"", *p)) 283*0Sstevel@tonic-gate return NULL; 284*0Sstevel@tonic-gate } 285*0Sstevel@tonic-gate p++; 286*0Sstevel@tonic-gate } 287*0Sstevel@tonic-gate } 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate /* @domain */ 290*0Sstevel@tonic-gate if (*p == '@') { 291*0Sstevel@tonic-gate p++; 292*0Sstevel@tonic-gate if (*p == '[') { 293*0Sstevel@tonic-gate p++; 294*0Sstevel@tonic-gate while (isascii(*p) && 295*0Sstevel@tonic-gate (isalnum(*p) || *p == '.' || 296*0Sstevel@tonic-gate *p == '-' || *p == ':')) 297*0Sstevel@tonic-gate p++; 298*0Sstevel@tonic-gate if (*p++ != ']') 299*0Sstevel@tonic-gate return NULL; 300*0Sstevel@tonic-gate } else { 301*0Sstevel@tonic-gate while ((isascii(*p) && isalnum(*p)) || 302*0Sstevel@tonic-gate strchr(".-_", *p)) 303*0Sstevel@tonic-gate p++; 304*0Sstevel@tonic-gate } 305*0Sstevel@tonic-gate } 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate if (*p++ != '>') 308*0Sstevel@tonic-gate return NULL; 309*0Sstevel@tonic-gate if (*p && *p != ' ') 310*0Sstevel@tonic-gate return NULL; 311*0Sstevel@tonic-gate len = p - s - 1; 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate if (*s == '\0' || len <= 0) 314*0Sstevel@tonic-gate { 315*0Sstevel@tonic-gate s = MAILER_DAEMON; 316*0Sstevel@tonic-gate len = strlen(s); 317*0Sstevel@tonic-gate } 318*0Sstevel@tonic-gate 319*0Sstevel@tonic-gate p = malloc(len + 1); 320*0Sstevel@tonic-gate if (p == NULL) { 321*0Sstevel@tonic-gate printf("421 4.3.0 memory exhausted\r\n"); 322*0Sstevel@tonic-gate exit(EX_TEMPFAIL); 323*0Sstevel@tonic-gate } 324*0Sstevel@tonic-gate 325*0Sstevel@tonic-gate strncpy(p, s, len); 326*0Sstevel@tonic-gate p[len] = '\0'; 327*0Sstevel@tonic-gate return p; 328*0Sstevel@tonic-gate } 329*0Sstevel@tonic-gate 330*0Sstevel@tonic-gate char * 331*0Sstevel@tonic-gate process_recipient(addr) 332*0Sstevel@tonic-gate char *addr; 333*0Sstevel@tonic-gate { 334*0Sstevel@tonic-gate if (getpwnam(addr) == NULL) { 335*0Sstevel@tonic-gate return "550 5.1.1 user unknown"; 336*0Sstevel@tonic-gate } 337*0Sstevel@tonic-gate 338*0Sstevel@tonic-gate return NULL; 339*0Sstevel@tonic-gate } 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate #define RCPT_GROW 30 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate void 344*0Sstevel@tonic-gate dolmtp(bouncequota) 345*0Sstevel@tonic-gate bool bouncequota; 346*0Sstevel@tonic-gate { 347*0Sstevel@tonic-gate char *return_path = NULL; 348*0Sstevel@tonic-gate char **rcpt_addr = NULL; 349*0Sstevel@tonic-gate int rcpt_num = 0; 350*0Sstevel@tonic-gate int rcpt_alloc = 0; 351*0Sstevel@tonic-gate bool gotlhlo = FALSE; 352*0Sstevel@tonic-gate char myhostname[MAXHOSTNAMELEN]; 353*0Sstevel@tonic-gate char buf[4096]; 354*0Sstevel@tonic-gate char *err; 355*0Sstevel@tonic-gate char *p; 356*0Sstevel@tonic-gate int i; 357*0Sstevel@tonic-gate 358*0Sstevel@tonic-gate gethostname(myhostname, sizeof myhostname - 1); 359*0Sstevel@tonic-gate 360*0Sstevel@tonic-gate printf("220 %s LMTP ready\r\n", myhostname); 361*0Sstevel@tonic-gate for (;;) { 362*0Sstevel@tonic-gate if (sigterm_caught) { 363*0Sstevel@tonic-gate for (; rcpt_num > 0; rcpt_num--) 364*0Sstevel@tonic-gate printf("451 4.3.0 shutting down\r\n"); 365*0Sstevel@tonic-gate exit(EX_OK); 366*0Sstevel@tonic-gate } 367*0Sstevel@tonic-gate fflush(stdout); 368*0Sstevel@tonic-gate if (fgets(buf, sizeof(buf)-1, stdin) == NULL) { 369*0Sstevel@tonic-gate exit(EX_OK); 370*0Sstevel@tonic-gate } 371*0Sstevel@tonic-gate p = buf + strlen(buf) - 1; 372*0Sstevel@tonic-gate if (p >= buf && *p == '\n') 373*0Sstevel@tonic-gate *p-- = '\0'; 374*0Sstevel@tonic-gate if (p >= buf && *p == '\r') 375*0Sstevel@tonic-gate *p-- = '\0'; 376*0Sstevel@tonic-gate 377*0Sstevel@tonic-gate switch (buf[0]) { 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate case 'd': 380*0Sstevel@tonic-gate case 'D': 381*0Sstevel@tonic-gate if (strcasecmp(buf, "data") == 0) { 382*0Sstevel@tonic-gate if (rcpt_num == 0) { 383*0Sstevel@tonic-gate printf("503 5.5.1 No recipients\r\n"); 384*0Sstevel@tonic-gate continue; 385*0Sstevel@tonic-gate } 386*0Sstevel@tonic-gate store(return_path, rcpt_num); 387*0Sstevel@tonic-gate if (bfd == -1 || hfd == -1) 388*0Sstevel@tonic-gate continue; 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate for (i = 0; i < rcpt_num; i++) { 391*0Sstevel@tonic-gate p = strchr(rcpt_addr[i], '+'); 392*0Sstevel@tonic-gate if (p != NULL) 393*0Sstevel@tonic-gate *p++ = '\0'; 394*0Sstevel@tonic-gate deliver(hfd, bfd, rcpt_addr[i], 395*0Sstevel@tonic-gate bouncequota); 396*0Sstevel@tonic-gate } 397*0Sstevel@tonic-gate close(bfd); 398*0Sstevel@tonic-gate close(hfd); 399*0Sstevel@tonic-gate goto rset; 400*0Sstevel@tonic-gate } 401*0Sstevel@tonic-gate goto syntaxerr; 402*0Sstevel@tonic-gate /* NOTREACHED */ 403*0Sstevel@tonic-gate break; 404*0Sstevel@tonic-gate 405*0Sstevel@tonic-gate case 'l': 406*0Sstevel@tonic-gate case 'L': 407*0Sstevel@tonic-gate if (strncasecmp(buf, "lhlo ", 5) == 0) 408*0Sstevel@tonic-gate { 409*0Sstevel@tonic-gate /* check for duplicate per RFC 1651 4.2 */ 410*0Sstevel@tonic-gate if (gotlhlo) 411*0Sstevel@tonic-gate { 412*0Sstevel@tonic-gate printf("503 %s Duplicate LHLO\r\n", 413*0Sstevel@tonic-gate myhostname); 414*0Sstevel@tonic-gate continue; 415*0Sstevel@tonic-gate } 416*0Sstevel@tonic-gate gotlhlo = TRUE; 417*0Sstevel@tonic-gate printf("250-%s\r\n", myhostname); 418*0Sstevel@tonic-gate if (EightBitMime) 419*0Sstevel@tonic-gate printf("250-8BITMIME\r\n"); 420*0Sstevel@tonic-gate printf("250-ENHANCEDSTATUSCODES\r\n"); 421*0Sstevel@tonic-gate printf("250 PIPELINING\r\n"); 422*0Sstevel@tonic-gate continue; 423*0Sstevel@tonic-gate } 424*0Sstevel@tonic-gate goto syntaxerr; 425*0Sstevel@tonic-gate /* NOTREACHED */ 426*0Sstevel@tonic-gate break; 427*0Sstevel@tonic-gate 428*0Sstevel@tonic-gate case 'm': 429*0Sstevel@tonic-gate case 'M': 430*0Sstevel@tonic-gate if (strncasecmp(buf, "mail ", 5) == 0) { 431*0Sstevel@tonic-gate if (return_path != NULL) { 432*0Sstevel@tonic-gate printf("503 5.5.1 Nested MAIL command\r\n"); 433*0Sstevel@tonic-gate continue; 434*0Sstevel@tonic-gate } 435*0Sstevel@tonic-gate if (strncasecmp(buf+5, "from:", 5) != 0 || 436*0Sstevel@tonic-gate ((return_path = parseaddr(buf+10)) == NULL)) { 437*0Sstevel@tonic-gate printf("501 5.5.4 Syntax error in parameters\r\n"); 438*0Sstevel@tonic-gate continue; 439*0Sstevel@tonic-gate } 440*0Sstevel@tonic-gate printf("250 2.5.0 ok\r\n"); 441*0Sstevel@tonic-gate continue; 442*0Sstevel@tonic-gate } 443*0Sstevel@tonic-gate goto syntaxerr; 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate case 'n': 446*0Sstevel@tonic-gate case 'N': 447*0Sstevel@tonic-gate if (strcasecmp(buf, "noop") == 0) { 448*0Sstevel@tonic-gate printf("250 2.0.0 ok\r\n"); 449*0Sstevel@tonic-gate continue; 450*0Sstevel@tonic-gate } 451*0Sstevel@tonic-gate goto syntaxerr; 452*0Sstevel@tonic-gate 453*0Sstevel@tonic-gate case 'q': 454*0Sstevel@tonic-gate case 'Q': 455*0Sstevel@tonic-gate if (strcasecmp(buf, "quit") == 0) { 456*0Sstevel@tonic-gate printf("221 2.0.0 bye\r\n"); 457*0Sstevel@tonic-gate exit(EX_OK); 458*0Sstevel@tonic-gate } 459*0Sstevel@tonic-gate goto syntaxerr; 460*0Sstevel@tonic-gate 461*0Sstevel@tonic-gate case 'r': 462*0Sstevel@tonic-gate case 'R': 463*0Sstevel@tonic-gate if (strncasecmp(buf, "rcpt ", 5) == 0) { 464*0Sstevel@tonic-gate if (return_path == NULL) { 465*0Sstevel@tonic-gate printf("503 5.5.1 Need MAIL command\r\n"); 466*0Sstevel@tonic-gate continue; 467*0Sstevel@tonic-gate } 468*0Sstevel@tonic-gate if (rcpt_num >= rcpt_alloc) { 469*0Sstevel@tonic-gate rcpt_alloc += RCPT_GROW; 470*0Sstevel@tonic-gate rcpt_addr = (char **) 471*0Sstevel@tonic-gate realloc((char *)rcpt_addr, 472*0Sstevel@tonic-gate rcpt_alloc * sizeof(char **)); 473*0Sstevel@tonic-gate if (rcpt_addr == NULL) { 474*0Sstevel@tonic-gate printf("421 4.3.0 memory exhausted\r\n"); 475*0Sstevel@tonic-gate exit(EX_TEMPFAIL); 476*0Sstevel@tonic-gate } 477*0Sstevel@tonic-gate } 478*0Sstevel@tonic-gate if (strncasecmp(buf+5, "to:", 3) != 0 || 479*0Sstevel@tonic-gate ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) { 480*0Sstevel@tonic-gate printf("501 5.5.4 Syntax error in parameters\r\n"); 481*0Sstevel@tonic-gate continue; 482*0Sstevel@tonic-gate } 483*0Sstevel@tonic-gate if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) { 484*0Sstevel@tonic-gate printf("%s\r\n", err); 485*0Sstevel@tonic-gate continue; 486*0Sstevel@tonic-gate } 487*0Sstevel@tonic-gate rcpt_num++; 488*0Sstevel@tonic-gate printf("250 2.1.5 ok\r\n"); 489*0Sstevel@tonic-gate continue; 490*0Sstevel@tonic-gate } 491*0Sstevel@tonic-gate else if (strcasecmp(buf, "rset") == 0) { 492*0Sstevel@tonic-gate printf("250 2.0.0 ok\r\n"); 493*0Sstevel@tonic-gate 494*0Sstevel@tonic-gate rset: 495*0Sstevel@tonic-gate while (rcpt_num > 0) { 496*0Sstevel@tonic-gate free(rcpt_addr[--rcpt_num]); 497*0Sstevel@tonic-gate } 498*0Sstevel@tonic-gate if (return_path != NULL) 499*0Sstevel@tonic-gate free(return_path); 500*0Sstevel@tonic-gate return_path = NULL; 501*0Sstevel@tonic-gate continue; 502*0Sstevel@tonic-gate } 503*0Sstevel@tonic-gate goto syntaxerr; 504*0Sstevel@tonic-gate 505*0Sstevel@tonic-gate case 'v': 506*0Sstevel@tonic-gate case 'V': 507*0Sstevel@tonic-gate if (strncasecmp(buf, "vrfy ", 5) == 0) { 508*0Sstevel@tonic-gate printf("252 2.3.3 try RCPT to attempt delivery\r\n"); 509*0Sstevel@tonic-gate continue; 510*0Sstevel@tonic-gate } 511*0Sstevel@tonic-gate goto syntaxerr; 512*0Sstevel@tonic-gate 513*0Sstevel@tonic-gate default: 514*0Sstevel@tonic-gate syntaxerr: 515*0Sstevel@tonic-gate printf("500 5.5.2 Syntax error\r\n"); 516*0Sstevel@tonic-gate continue; 517*0Sstevel@tonic-gate } 518*0Sstevel@tonic-gate } 519*0Sstevel@tonic-gate } 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate static void 522*0Sstevel@tonic-gate store(from, lmtprcpts) 523*0Sstevel@tonic-gate char *from; 524*0Sstevel@tonic-gate int lmtprcpts; 525*0Sstevel@tonic-gate { 526*0Sstevel@tonic-gate FILE *fp = NULL; 527*0Sstevel@tonic-gate time_t tval; 528*0Sstevel@tonic-gate bool fullline = TRUE; /* current line is terminated */ 529*0Sstevel@tonic-gate bool prevfl; /* previous line was terminated */ 530*0Sstevel@tonic-gate char line[MAXLINE]; 531*0Sstevel@tonic-gate FILE *bfp, *hfp; 532*0Sstevel@tonic-gate char *btn, *htn; 533*0Sstevel@tonic-gate int in_header_section; 534*0Sstevel@tonic-gate 535*0Sstevel@tonic-gate bfd = -1; 536*0Sstevel@tonic-gate hfd = -1; 537*0Sstevel@tonic-gate btn = strdup(_PATH_LOCTMP); 538*0Sstevel@tonic-gate if ((bfd = mkstemp(btn)) == -1 || (bfp = fdopen(bfd, "w+")) == NULL) { 539*0Sstevel@tonic-gate if (bfd != -1) 540*0Sstevel@tonic-gate (void) close(bfd); 541*0Sstevel@tonic-gate if (lmtprcpts) { 542*0Sstevel@tonic-gate printf("451 4.3.0 unable to open temporary file\r\n"); 543*0Sstevel@tonic-gate return; 544*0Sstevel@tonic-gate } else { 545*0Sstevel@tonic-gate mailerr("451 4.3.0", "unable to open temporary file"); 546*0Sstevel@tonic-gate exit(eval); 547*0Sstevel@tonic-gate } 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate (void) unlink(btn); 550*0Sstevel@tonic-gate free(btn); 551*0Sstevel@tonic-gate 552*0Sstevel@tonic-gate if (lmtpmode) { 553*0Sstevel@tonic-gate printf("354 go ahead\r\n"); 554*0Sstevel@tonic-gate fflush(stdout); 555*0Sstevel@tonic-gate } 556*0Sstevel@tonic-gate 557*0Sstevel@tonic-gate htn = strdup(_PATH_LOCHTMP); 558*0Sstevel@tonic-gate if ((hfd = mkstemp(htn)) == -1 || (hfp = fdopen(hfd, "w+")) == NULL) { 559*0Sstevel@tonic-gate if (hfd != -1) 560*0Sstevel@tonic-gate (void) close(hfd); 561*0Sstevel@tonic-gate e_to_sys(errno); 562*0Sstevel@tonic-gate err("unable to open temporary file"); 563*0Sstevel@tonic-gate } 564*0Sstevel@tonic-gate (void) unlink(htn); 565*0Sstevel@tonic-gate free(htn); 566*0Sstevel@tonic-gate 567*0Sstevel@tonic-gate in_header_section = TRUE; 568*0Sstevel@tonic-gate content_length = 0; 569*0Sstevel@tonic-gate fp = hfp; 570*0Sstevel@tonic-gate 571*0Sstevel@tonic-gate line[0] = '\0'; 572*0Sstevel@tonic-gate while (fgets(line, sizeof(line), stdin) != (char *)NULL) 573*0Sstevel@tonic-gate { 574*0Sstevel@tonic-gate size_t line_len = 0; 575*0Sstevel@tonic-gate int peek; 576*0Sstevel@tonic-gate 577*0Sstevel@tonic-gate prevfl = fullline; /* preserve state of previous line */ 578*0Sstevel@tonic-gate while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 579*0Sstevel@tonic-gate line_len++; 580*0Sstevel@tonic-gate line_len++; 581*0Sstevel@tonic-gate 582*0Sstevel@tonic-gate /* Check for dot-stuffing */ 583*0Sstevel@tonic-gate if (prevfl && lmtprcpts && line[0] == '.') 584*0Sstevel@tonic-gate { 585*0Sstevel@tonic-gate if (line[1] == '\n' || 586*0Sstevel@tonic-gate (line[1] == '\r' && line[2] == '\n')) 587*0Sstevel@tonic-gate goto lmtpdot; 588*0Sstevel@tonic-gate memcpy(line, line + 1, line_len); 589*0Sstevel@tonic-gate line_len--; 590*0Sstevel@tonic-gate } 591*0Sstevel@tonic-gate 592*0Sstevel@tonic-gate /* Check to see if we have the full line from fgets() */ 593*0Sstevel@tonic-gate fullline = FALSE; 594*0Sstevel@tonic-gate if (line_len > 0) 595*0Sstevel@tonic-gate { 596*0Sstevel@tonic-gate if (line[line_len - 1] == '\n') 597*0Sstevel@tonic-gate { 598*0Sstevel@tonic-gate if (line_len >= 2 && 599*0Sstevel@tonic-gate line[line_len - 2] == '\r') 600*0Sstevel@tonic-gate { 601*0Sstevel@tonic-gate line[line_len - 2] = '\n'; 602*0Sstevel@tonic-gate line[line_len - 1] = '\0'; 603*0Sstevel@tonic-gate line_len--; 604*0Sstevel@tonic-gate } 605*0Sstevel@tonic-gate fullline = TRUE; 606*0Sstevel@tonic-gate } 607*0Sstevel@tonic-gate else if (line[line_len - 1] == '\r') 608*0Sstevel@tonic-gate { 609*0Sstevel@tonic-gate /* Did we just miss the CRLF? */ 610*0Sstevel@tonic-gate peek = fgetc(stdin); 611*0Sstevel@tonic-gate if (peek == '\n') 612*0Sstevel@tonic-gate { 613*0Sstevel@tonic-gate line[line_len - 1] = '\n'; 614*0Sstevel@tonic-gate fullline = TRUE; 615*0Sstevel@tonic-gate } 616*0Sstevel@tonic-gate else 617*0Sstevel@tonic-gate (void) ungetc(peek, stdin); 618*0Sstevel@tonic-gate } 619*0Sstevel@tonic-gate } 620*0Sstevel@tonic-gate else 621*0Sstevel@tonic-gate fullline = TRUE; 622*0Sstevel@tonic-gate 623*0Sstevel@tonic-gate if (prevfl && line[0] == '\n' && in_header_section) { 624*0Sstevel@tonic-gate in_header_section = FALSE; 625*0Sstevel@tonic-gate if (fflush(fp) == EOF || ferror(fp)) { 626*0Sstevel@tonic-gate if (lmtprcpts) { 627*0Sstevel@tonic-gate while (lmtprcpts--) 628*0Sstevel@tonic-gate printf("451 4.3.0 temporary file write error\r\n"); 629*0Sstevel@tonic-gate fclose(fp); 630*0Sstevel@tonic-gate return; 631*0Sstevel@tonic-gate } else { 632*0Sstevel@tonic-gate mailerr("451 4.3.0", 633*0Sstevel@tonic-gate "temporary file write error"); 634*0Sstevel@tonic-gate fclose(fp); 635*0Sstevel@tonic-gate exit(eval); 636*0Sstevel@tonic-gate } 637*0Sstevel@tonic-gate } 638*0Sstevel@tonic-gate fp = bfp; 639*0Sstevel@tonic-gate continue; 640*0Sstevel@tonic-gate } 641*0Sstevel@tonic-gate 642*0Sstevel@tonic-gate if (in_header_section) { 643*0Sstevel@tonic-gate if (strncasecmp("Content-Length:", line, 15) == 0) { 644*0Sstevel@tonic-gate continue; /* skip this header */ 645*0Sstevel@tonic-gate } 646*0Sstevel@tonic-gate } else 647*0Sstevel@tonic-gate content_length += strlen(line); 648*0Sstevel@tonic-gate (void) fwrite(line, sizeof(char), line_len, fp); 649*0Sstevel@tonic-gate if (ferror(fp)) { 650*0Sstevel@tonic-gate if (lmtprcpts) { 651*0Sstevel@tonic-gate while (lmtprcpts--) 652*0Sstevel@tonic-gate printf("451 4.3.0 temporary file write error\r\n"); 653*0Sstevel@tonic-gate fclose(fp); 654*0Sstevel@tonic-gate return; 655*0Sstevel@tonic-gate } else { 656*0Sstevel@tonic-gate mailerr("451 4.3.0", 657*0Sstevel@tonic-gate "temporary file write error"); 658*0Sstevel@tonic-gate fclose(fp); 659*0Sstevel@tonic-gate exit(eval); 660*0Sstevel@tonic-gate } 661*0Sstevel@tonic-gate } 662*0Sstevel@tonic-gate } 663*0Sstevel@tonic-gate if (sigterm_caught) { 664*0Sstevel@tonic-gate if (lmtprcpts) 665*0Sstevel@tonic-gate while (lmtprcpts--) 666*0Sstevel@tonic-gate printf("451 4.3.0 shutting down\r\n"); 667*0Sstevel@tonic-gate else 668*0Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down"); 669*0Sstevel@tonic-gate fclose(fp); 670*0Sstevel@tonic-gate exit(eval); 671*0Sstevel@tonic-gate } 672*0Sstevel@tonic-gate 673*0Sstevel@tonic-gate if (lmtprcpts) { 674*0Sstevel@tonic-gate /* Got a premature EOF -- toss message and exit */ 675*0Sstevel@tonic-gate exit(EX_OK); 676*0Sstevel@tonic-gate } 677*0Sstevel@tonic-gate 678*0Sstevel@tonic-gate /* If message not newline terminated, need an extra. */ 679*0Sstevel@tonic-gate if (!strchr(line, '\n')) { 680*0Sstevel@tonic-gate (void) putc('\n', fp); 681*0Sstevel@tonic-gate content_length++; 682*0Sstevel@tonic-gate } 683*0Sstevel@tonic-gate 684*0Sstevel@tonic-gate lmtpdot: 685*0Sstevel@tonic-gate 686*0Sstevel@tonic-gate /* Output a newline; note, empty messages are allowed. */ 687*0Sstevel@tonic-gate (void) putc('\n', fp); 688*0Sstevel@tonic-gate 689*0Sstevel@tonic-gate if (fflush(fp) == EOF || ferror(fp)) { 690*0Sstevel@tonic-gate if (lmtprcpts) { 691*0Sstevel@tonic-gate while (lmtprcpts--) { 692*0Sstevel@tonic-gate printf("451 4.3.0 temporary file write error\r\n"); 693*0Sstevel@tonic-gate } 694*0Sstevel@tonic-gate fclose(fp); 695*0Sstevel@tonic-gate return; 696*0Sstevel@tonic-gate } else { 697*0Sstevel@tonic-gate mailerr("451 4.3.0", "temporary file write error"); 698*0Sstevel@tonic-gate fclose(fp); 699*0Sstevel@tonic-gate exit(eval); 700*0Sstevel@tonic-gate } 701*0Sstevel@tonic-gate } 702*0Sstevel@tonic-gate 703*0Sstevel@tonic-gate (void) time(&tval); 704*0Sstevel@tonic-gate (void) snprintf(unix_from_line, sizeof (unix_from_line), "From %s %s", 705*0Sstevel@tonic-gate from, ctime(&tval)); 706*0Sstevel@tonic-gate ulen = strlen(unix_from_line); 707*0Sstevel@tonic-gate } 708*0Sstevel@tonic-gate 709*0Sstevel@tonic-gate static void 710*0Sstevel@tonic-gate deliver(hfd, bfd, name, bouncequota) 711*0Sstevel@tonic-gate int hfd; 712*0Sstevel@tonic-gate int bfd; 713*0Sstevel@tonic-gate char *name; 714*0Sstevel@tonic-gate bool bouncequota; 715*0Sstevel@tonic-gate { 716*0Sstevel@tonic-gate struct stat fsb, sb; 717*0Sstevel@tonic-gate int mbfd = -1, nr, nw = 0, off; 718*0Sstevel@tonic-gate char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; 719*0Sstevel@tonic-gate off_t curoff, cursize; 720*0Sstevel@tonic-gate int len; 721*0Sstevel@tonic-gate struct passwd *pw = NULL; 722*0Sstevel@tonic-gate 723*0Sstevel@tonic-gate /* 724*0Sstevel@tonic-gate * Disallow delivery to unknown names -- special mailboxes 725*0Sstevel@tonic-gate * can be handled in the sendmail aliases file. 726*0Sstevel@tonic-gate */ 727*0Sstevel@tonic-gate if ((pw = getpwnam(name)) == NULL) { 728*0Sstevel@tonic-gate eval = EX_TEMPFAIL; 729*0Sstevel@tonic-gate mailerr("451 4.3.0", "cannot lookup name: %s", name); 730*0Sstevel@tonic-gate return; 731*0Sstevel@tonic-gate } 732*0Sstevel@tonic-gate endpwent(); 733*0Sstevel@tonic-gate 734*0Sstevel@tonic-gate if (sigterm_caught) { 735*0Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down"); 736*0Sstevel@tonic-gate return; 737*0Sstevel@tonic-gate } 738*0Sstevel@tonic-gate 739*0Sstevel@tonic-gate /* mailbox may be NFS mounted, seteuid to user */ 740*0Sstevel@tonic-gate targ_uid = pw->pw_uid; 741*0Sstevel@tonic-gate (void) seteuid(targ_uid); 742*0Sstevel@tonic-gate 743*0Sstevel@tonic-gate if ((saved_uid != 0) && (src_uid != targ_uid)) { 744*0Sstevel@tonic-gate /* 745*0Sstevel@tonic-gate * If saved_uid == 0 (root), anything is OK; this is 746*0Sstevel@tonic-gate * as it should be. But to prevent a random user from 747*0Sstevel@tonic-gate * calling "mail.local foo" in an attempt to hijack 748*0Sstevel@tonic-gate * foo's mail-box, make sure src_uid == targ_uid o/w. 749*0Sstevel@tonic-gate */ 750*0Sstevel@tonic-gate warn("%s: wrong owner (is %d, should be %d)", 751*0Sstevel@tonic-gate name, src_uid, targ_uid); 752*0Sstevel@tonic-gate eval = EX_CANTCREAT; 753*0Sstevel@tonic-gate return; 754*0Sstevel@tonic-gate } 755*0Sstevel@tonic-gate 756*0Sstevel@tonic-gate path[0] = '\0'; 757*0Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s/%s", _PATH_MAILDIR, name); 758*0Sstevel@tonic-gate 759*0Sstevel@tonic-gate /* 760*0Sstevel@tonic-gate * If the mailbox is linked or a symlink, fail. There's an obvious 761*0Sstevel@tonic-gate * race here, that the file was replaced with a symbolic link after 762*0Sstevel@tonic-gate * the lstat returned, but before the open. We attempt to detect 763*0Sstevel@tonic-gate * this by comparing the original stat information and information 764*0Sstevel@tonic-gate * returned by an fstat of the file descriptor returned by the open. 765*0Sstevel@tonic-gate * 766*0Sstevel@tonic-gate * NB: this is a symptom of a larger problem, that the mail spooling 767*0Sstevel@tonic-gate * directory is writeable by the wrong users. If that directory is 768*0Sstevel@tonic-gate * writeable, system security is compromised for other reasons, and 769*0Sstevel@tonic-gate * it cannot be fixed here. 770*0Sstevel@tonic-gate * 771*0Sstevel@tonic-gate * If we created the mailbox, set the owner/group. If that fails, 772*0Sstevel@tonic-gate * just return. Another process may have already opened it, so we 773*0Sstevel@tonic-gate * can't unlink it. Historically, binmail set the owner/group at 774*0Sstevel@tonic-gate * each mail delivery. We no longer do this, assuming that if the 775*0Sstevel@tonic-gate * ownership or permissions were changed there was a reason. 776*0Sstevel@tonic-gate * 777*0Sstevel@tonic-gate * XXX 778*0Sstevel@tonic-gate * open(2) should support flock'ing the file. 779*0Sstevel@tonic-gate */ 780*0Sstevel@tonic-gate tryagain: 781*0Sstevel@tonic-gate /* should check lock status, but... maillock return no value */ 782*0Sstevel@tonic-gate maillock(name, 10); 783*0Sstevel@tonic-gate 784*0Sstevel@tonic-gate if (sigterm_caught) { 785*0Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down"); 786*0Sstevel@tonic-gate goto err0; 787*0Sstevel@tonic-gate } 788*0Sstevel@tonic-gate 789*0Sstevel@tonic-gate if (lstat(path, &sb)) { 790*0Sstevel@tonic-gate mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, 791*0Sstevel@tonic-gate S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); 792*0Sstevel@tonic-gate if (mbfd != -1) 793*0Sstevel@tonic-gate (void) fchmod(mbfd, 0660); 794*0Sstevel@tonic-gate 795*0Sstevel@tonic-gate 796*0Sstevel@tonic-gate if (mbfd == -1) { 797*0Sstevel@tonic-gate if (errno == EEXIST) { 798*0Sstevel@tonic-gate mailunlock(); 799*0Sstevel@tonic-gate goto tryagain; 800*0Sstevel@tonic-gate } 801*0Sstevel@tonic-gate } 802*0Sstevel@tonic-gate } else if (sb.st_nlink != 1) { 803*0Sstevel@tonic-gate mailerr("550 5.2.0", "%s: too many links", path); 804*0Sstevel@tonic-gate goto err0; 805*0Sstevel@tonic-gate } else if (!S_ISREG(sb.st_mode)) { 806*0Sstevel@tonic-gate mailerr("550 5.2.0", "%s: irregular file", path); 807*0Sstevel@tonic-gate goto err0; 808*0Sstevel@tonic-gate } else { 809*0Sstevel@tonic-gate mbfd = open(path, O_APPEND|O_WRONLY, 0); 810*0Sstevel@tonic-gate if (mbfd != -1 && 811*0Sstevel@tonic-gate (fstat(mbfd, &fsb) || fsb.st_nlink != 1 || 812*0Sstevel@tonic-gate S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev || 813*0Sstevel@tonic-gate sb.st_ino != fsb.st_ino)) { 814*0Sstevel@tonic-gate eval = EX_TEMPFAIL; 815*0Sstevel@tonic-gate mailerr("550 5.2.0", 816*0Sstevel@tonic-gate "%s: fstat: file changed after open", path); 817*0Sstevel@tonic-gate goto err1; 818*0Sstevel@tonic-gate } 819*0Sstevel@tonic-gate } 820*0Sstevel@tonic-gate 821*0Sstevel@tonic-gate if (mbfd == -1) { 822*0Sstevel@tonic-gate mailerr("450 4.2.0", "%s: %s", path, strerror(errno)); 823*0Sstevel@tonic-gate goto err0; 824*0Sstevel@tonic-gate } 825*0Sstevel@tonic-gate 826*0Sstevel@tonic-gate if (sigterm_caught) { 827*0Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down"); 828*0Sstevel@tonic-gate goto err0; 829*0Sstevel@tonic-gate } 830*0Sstevel@tonic-gate 831*0Sstevel@tonic-gate /* Get the starting offset of the new message for biff. */ 832*0Sstevel@tonic-gate curoff = lseek(mbfd, (off_t)0, SEEK_END); 833*0Sstevel@tonic-gate (void) snprintf(biffmsg, sizeof (biffmsg), "%s@%ld\n", name, curoff); 834*0Sstevel@tonic-gate 835*0Sstevel@tonic-gate /* Copy the message into the file. */ 836*0Sstevel@tonic-gate if (lseek(hfd, (off_t)0, SEEK_SET) == (off_t)-1) { 837*0Sstevel@tonic-gate mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 838*0Sstevel@tonic-gate goto err1; 839*0Sstevel@tonic-gate } 840*0Sstevel@tonic-gate /* Copy the message into the file. */ 841*0Sstevel@tonic-gate if (lseek(bfd, (off_t)0, SEEK_SET) == (off_t)-1) { 842*0Sstevel@tonic-gate mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 843*0Sstevel@tonic-gate goto err1; 844*0Sstevel@tonic-gate } 845*0Sstevel@tonic-gate if ((write(mbfd, unix_from_line, ulen)) != ulen) { 846*0Sstevel@tonic-gate mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 847*0Sstevel@tonic-gate goto err2; 848*0Sstevel@tonic-gate } 849*0Sstevel@tonic-gate 850*0Sstevel@tonic-gate if (sigterm_caught) { 851*0Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down"); 852*0Sstevel@tonic-gate goto err2; 853*0Sstevel@tonic-gate } 854*0Sstevel@tonic-gate 855*0Sstevel@tonic-gate while ((nr = read(hfd, buf, sizeof (buf))) > 0) 856*0Sstevel@tonic-gate for (off = 0; off < nr; nr -= nw, off += nw) 857*0Sstevel@tonic-gate if ((nw = write(mbfd, buf + off, nr)) < 0) 858*0Sstevel@tonic-gate { 859*0Sstevel@tonic-gate #ifdef EDQUOT 860*0Sstevel@tonic-gate if (errno == EDQUOT && bouncequota) 861*0Sstevel@tonic-gate mailerr("552 5.2.2", "%s: %s", 862*0Sstevel@tonic-gate path, sm_errstring(errno)); 863*0Sstevel@tonic-gate #endif /* EDQUOT */ 864*0Sstevel@tonic-gate mailerr("450 4.2.0", "%s: %s", 865*0Sstevel@tonic-gate path, sm_errstring(errno)); 866*0Sstevel@tonic-gate goto err2; 867*0Sstevel@tonic-gate } 868*0Sstevel@tonic-gate if (nr < 0) { 869*0Sstevel@tonic-gate mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 870*0Sstevel@tonic-gate goto err2; 871*0Sstevel@tonic-gate } 872*0Sstevel@tonic-gate 873*0Sstevel@tonic-gate if (sigterm_caught) { 874*0Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down"); 875*0Sstevel@tonic-gate goto err2; 876*0Sstevel@tonic-gate } 877*0Sstevel@tonic-gate 878*0Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "Content-Length: %d\n\n", 879*0Sstevel@tonic-gate content_length); 880*0Sstevel@tonic-gate len = strlen(buf); 881*0Sstevel@tonic-gate if (write(mbfd, buf, len) != len) { 882*0Sstevel@tonic-gate mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 883*0Sstevel@tonic-gate goto err2; 884*0Sstevel@tonic-gate } 885*0Sstevel@tonic-gate 886*0Sstevel@tonic-gate if (sigterm_caught) { 887*0Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down"); 888*0Sstevel@tonic-gate goto err2; 889*0Sstevel@tonic-gate } 890*0Sstevel@tonic-gate 891*0Sstevel@tonic-gate while ((nr = read(bfd, buf, sizeof (buf))) > 0) { 892*0Sstevel@tonic-gate for (off = 0; off < nr; nr -= nw, off += nw) 893*0Sstevel@tonic-gate if ((nw = write(mbfd, buf + off, nr)) < 0) { 894*0Sstevel@tonic-gate mailerr("450 4.2.0", "temporary file: %s", 895*0Sstevel@tonic-gate strerror(errno)); 896*0Sstevel@tonic-gate goto err2; 897*0Sstevel@tonic-gate } 898*0Sstevel@tonic-gate if (sigterm_caught) { 899*0Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down"); 900*0Sstevel@tonic-gate goto err2; 901*0Sstevel@tonic-gate } 902*0Sstevel@tonic-gate } 903*0Sstevel@tonic-gate if (nr < 0) { 904*0Sstevel@tonic-gate mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 905*0Sstevel@tonic-gate goto err2; 906*0Sstevel@tonic-gate } 907*0Sstevel@tonic-gate 908*0Sstevel@tonic-gate /* Flush to disk, don't wait for update. */ 909*0Sstevel@tonic-gate if (fsync(mbfd)) { 910*0Sstevel@tonic-gate mailerr("450 4.2.0", "temporary file: %s", strerror(errno)); 911*0Sstevel@tonic-gate err2: if (mbfd >= 0) 912*0Sstevel@tonic-gate (void)ftruncate(mbfd, curoff); 913*0Sstevel@tonic-gate err1: (void)close(mbfd); 914*0Sstevel@tonic-gate err0: mailunlock(); 915*0Sstevel@tonic-gate (void)seteuid(saved_uid); 916*0Sstevel@tonic-gate return; 917*0Sstevel@tonic-gate } 918*0Sstevel@tonic-gate 919*0Sstevel@tonic-gate /* 920*0Sstevel@tonic-gate ** Save the current size so if the close() fails below 921*0Sstevel@tonic-gate ** we can make sure no other process has changed the mailbox 922*0Sstevel@tonic-gate ** between the failed close and the re-open()/re-lock(). 923*0Sstevel@tonic-gate ** If something else has changed the size, we shouldn't 924*0Sstevel@tonic-gate ** try to truncate it as we may do more harm then good 925*0Sstevel@tonic-gate ** (e.g., truncate a later message delivery). 926*0Sstevel@tonic-gate */ 927*0Sstevel@tonic-gate 928*0Sstevel@tonic-gate if (fstat(mbfd, &sb) < 0) 929*0Sstevel@tonic-gate cursize = 0; 930*0Sstevel@tonic-gate else 931*0Sstevel@tonic-gate cursize = sb.st_size; 932*0Sstevel@tonic-gate 933*0Sstevel@tonic-gate /* Close and check -- NFS doesn't write until the close. */ 934*0Sstevel@tonic-gate if (close(mbfd)) 935*0Sstevel@tonic-gate { 936*0Sstevel@tonic-gate #ifdef EDQUOT 937*0Sstevel@tonic-gate if (errno == EDQUOT && bouncequota) 938*0Sstevel@tonic-gate mailerr("552 5.2.2", "%s: %s", path, 939*0Sstevel@tonic-gate sm_errstring(errno)); 940*0Sstevel@tonic-gate else 941*0Sstevel@tonic-gate #endif /* EDQUOT */ 942*0Sstevel@tonic-gate mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 943*0Sstevel@tonic-gate mbfd = open(path, O_WRONLY, 0); 944*0Sstevel@tonic-gate if (mbfd < 0 || 945*0Sstevel@tonic-gate cursize == 0 946*0Sstevel@tonic-gate || flock(mbfd, LOCK_EX) < 0 || 947*0Sstevel@tonic-gate fstat(mbfd, &sb) < 0 || 948*0Sstevel@tonic-gate sb.st_size != cursize || 949*0Sstevel@tonic-gate sb.st_nlink != 1 || 950*0Sstevel@tonic-gate !S_ISREG(sb.st_mode) || 951*0Sstevel@tonic-gate sb.st_dev != fsb.st_dev || 952*0Sstevel@tonic-gate sb.st_ino != fsb.st_ino || 953*0Sstevel@tonic-gate sb.st_uid != fsb.st_uid) 954*0Sstevel@tonic-gate { 955*0Sstevel@tonic-gate /* Don't use a bogus file */ 956*0Sstevel@tonic-gate if (mbfd >= 0) 957*0Sstevel@tonic-gate { 958*0Sstevel@tonic-gate (void) close(mbfd); 959*0Sstevel@tonic-gate mbfd = -1; 960*0Sstevel@tonic-gate } 961*0Sstevel@tonic-gate } 962*0Sstevel@tonic-gate 963*0Sstevel@tonic-gate /* Attempt to truncate back to pre-write size */ 964*0Sstevel@tonic-gate goto err2; 965*0Sstevel@tonic-gate } else 966*0Sstevel@tonic-gate notifybiff(biffmsg); 967*0Sstevel@tonic-gate 968*0Sstevel@tonic-gate mailunlock(); 969*0Sstevel@tonic-gate 970*0Sstevel@tonic-gate (void)seteuid(saved_uid); 971*0Sstevel@tonic-gate 972*0Sstevel@tonic-gate if (lmtpmode) { 973*0Sstevel@tonic-gate printf("250 2.1.5 %s OK\r\n", name); 974*0Sstevel@tonic-gate } 975*0Sstevel@tonic-gate } 976*0Sstevel@tonic-gate 977*0Sstevel@tonic-gate static void 978*0Sstevel@tonic-gate notifybiff(msg) 979*0Sstevel@tonic-gate char *msg; 980*0Sstevel@tonic-gate { 981*0Sstevel@tonic-gate static struct sockaddr_in addr; 982*0Sstevel@tonic-gate static int f = -1; 983*0Sstevel@tonic-gate struct hostent *hp; 984*0Sstevel@tonic-gate struct servent *sp; 985*0Sstevel@tonic-gate int len; 986*0Sstevel@tonic-gate 987*0Sstevel@tonic-gate if (msg == NULL) { 988*0Sstevel@tonic-gate /* Be silent if biff service not available. */ 989*0Sstevel@tonic-gate if ((sp = getservbyname("biff", "udp")) == NULL) 990*0Sstevel@tonic-gate return; 991*0Sstevel@tonic-gate if ((hp = gethostbyname("localhost")) == NULL) { 992*0Sstevel@tonic-gate warn("localhost: %s", strerror(errno)); 993*0Sstevel@tonic-gate return; 994*0Sstevel@tonic-gate } 995*0Sstevel@tonic-gate addr.sin_family = hp->h_addrtype; 996*0Sstevel@tonic-gate (void) memmove(&addr.sin_addr, hp->h_addr, hp->h_length); 997*0Sstevel@tonic-gate addr.sin_port = sp->s_port; 998*0Sstevel@tonic-gate return; 999*0Sstevel@tonic-gate } 1000*0Sstevel@tonic-gate 1001*0Sstevel@tonic-gate if (addr.sin_family == 0) 1002*0Sstevel@tonic-gate return; /* did not initialize */ 1003*0Sstevel@tonic-gate 1004*0Sstevel@tonic-gate if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 1005*0Sstevel@tonic-gate warn("socket: %s", strerror(errno)); 1006*0Sstevel@tonic-gate return; 1007*0Sstevel@tonic-gate } 1008*0Sstevel@tonic-gate len = strlen(msg) + 1; 1009*0Sstevel@tonic-gate if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof (addr)) 1010*0Sstevel@tonic-gate != len) 1011*0Sstevel@tonic-gate warn("sendto biff: %s", strerror(errno)); 1012*0Sstevel@tonic-gate } 1013*0Sstevel@tonic-gate 1014*0Sstevel@tonic-gate static void 1015*0Sstevel@tonic-gate usage() 1016*0Sstevel@tonic-gate { 1017*0Sstevel@tonic-gate eval = EX_USAGE; 1018*0Sstevel@tonic-gate err("usage: mail.local [-l] [-f from] user ..."); 1019*0Sstevel@tonic-gate } 1020*0Sstevel@tonic-gate 1021*0Sstevel@tonic-gate static void 1022*0Sstevel@tonic-gate /*VARARGS2*/ 1023*0Sstevel@tonic-gate #ifdef __STDC__ 1024*0Sstevel@tonic-gate mailerr(const char *hdr, const char *fmt, ...) 1025*0Sstevel@tonic-gate #else 1026*0Sstevel@tonic-gate mailerr(hdr, fmt, va_alist) 1027*0Sstevel@tonic-gate const char *hdr; 1028*0Sstevel@tonic-gate const char *fmt; 1029*0Sstevel@tonic-gate va_dcl 1030*0Sstevel@tonic-gate #endif 1031*0Sstevel@tonic-gate { 1032*0Sstevel@tonic-gate va_list ap; 1033*0Sstevel@tonic-gate 1034*0Sstevel@tonic-gate #ifdef __STDC__ 1035*0Sstevel@tonic-gate va_start(ap, fmt); 1036*0Sstevel@tonic-gate #else 1037*0Sstevel@tonic-gate va_start(ap); 1038*0Sstevel@tonic-gate #endif 1039*0Sstevel@tonic-gate if (lmtpmode) 1040*0Sstevel@tonic-gate { 1041*0Sstevel@tonic-gate if (hdr != NULL) 1042*0Sstevel@tonic-gate printf("%s ", hdr); 1043*0Sstevel@tonic-gate vprintf(fmt, ap); 1044*0Sstevel@tonic-gate printf("\r\n"); 1045*0Sstevel@tonic-gate } 1046*0Sstevel@tonic-gate else 1047*0Sstevel@tonic-gate { 1048*0Sstevel@tonic-gate e_to_sys(errno); 1049*0Sstevel@tonic-gate vwarn(fmt, ap); 1050*0Sstevel@tonic-gate } 1051*0Sstevel@tonic-gate } 1052*0Sstevel@tonic-gate 1053*0Sstevel@tonic-gate static void 1054*0Sstevel@tonic-gate /*VARARGS1*/ 1055*0Sstevel@tonic-gate #ifdef __STDC__ 1056*0Sstevel@tonic-gate err(const char *fmt, ...) 1057*0Sstevel@tonic-gate #else 1058*0Sstevel@tonic-gate err(fmt, va_alist) 1059*0Sstevel@tonic-gate const char *fmt; 1060*0Sstevel@tonic-gate va_dcl 1061*0Sstevel@tonic-gate #endif 1062*0Sstevel@tonic-gate { 1063*0Sstevel@tonic-gate va_list ap; 1064*0Sstevel@tonic-gate 1065*0Sstevel@tonic-gate #ifdef __STDC__ 1066*0Sstevel@tonic-gate va_start(ap, fmt); 1067*0Sstevel@tonic-gate #else 1068*0Sstevel@tonic-gate va_start(ap); 1069*0Sstevel@tonic-gate #endif 1070*0Sstevel@tonic-gate vwarn(fmt, ap); 1071*0Sstevel@tonic-gate va_end(ap); 1072*0Sstevel@tonic-gate 1073*0Sstevel@tonic-gate exit(eval); 1074*0Sstevel@tonic-gate } 1075*0Sstevel@tonic-gate 1076*0Sstevel@tonic-gate static void 1077*0Sstevel@tonic-gate /*VARARGS1*/ 1078*0Sstevel@tonic-gate #ifdef __STDC__ 1079*0Sstevel@tonic-gate warn(const char *fmt, ...) 1080*0Sstevel@tonic-gate #else 1081*0Sstevel@tonic-gate warn(fmt, va_alist) 1082*0Sstevel@tonic-gate const char *fmt; 1083*0Sstevel@tonic-gate va_dcl 1084*0Sstevel@tonic-gate #endif 1085*0Sstevel@tonic-gate { 1086*0Sstevel@tonic-gate va_list ap; 1087*0Sstevel@tonic-gate 1088*0Sstevel@tonic-gate #ifdef __STDC__ 1089*0Sstevel@tonic-gate va_start(ap, fmt); 1090*0Sstevel@tonic-gate #else 1091*0Sstevel@tonic-gate va_start(ap); 1092*0Sstevel@tonic-gate #endif 1093*0Sstevel@tonic-gate vwarn(fmt, ap); 1094*0Sstevel@tonic-gate va_end(ap); 1095*0Sstevel@tonic-gate } 1096*0Sstevel@tonic-gate 1097*0Sstevel@tonic-gate static void 1098*0Sstevel@tonic-gate vwarn(fmt, ap) 1099*0Sstevel@tonic-gate const char *fmt; 1100*0Sstevel@tonic-gate va_list ap; 1101*0Sstevel@tonic-gate { 1102*0Sstevel@tonic-gate /* 1103*0Sstevel@tonic-gate * Log the message to stderr. 1104*0Sstevel@tonic-gate * 1105*0Sstevel@tonic-gate * Don't use LOG_PERROR as an openlog() flag to do this, 1106*0Sstevel@tonic-gate * it's not portable enough. 1107*0Sstevel@tonic-gate */ 1108*0Sstevel@tonic-gate if (eval != EX_USAGE) 1109*0Sstevel@tonic-gate (void) fprintf(stderr, "mail.local: "); 1110*0Sstevel@tonic-gate (void) vfprintf(stderr, fmt, ap); 1111*0Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 1112*0Sstevel@tonic-gate 1113*0Sstevel@tonic-gate /* Log the message to syslog. */ 1114*0Sstevel@tonic-gate vsyslog(LOG_ERR, fmt, ap); 1115*0Sstevel@tonic-gate } 1116*0Sstevel@tonic-gate 1117*0Sstevel@tonic-gate /* 1118*0Sstevel@tonic-gate * e_to_sys -- 1119*0Sstevel@tonic-gate * Guess which errno's are temporary. Gag me. 1120*0Sstevel@tonic-gate */ 1121*0Sstevel@tonic-gate static void 1122*0Sstevel@tonic-gate e_to_sys(num) 1123*0Sstevel@tonic-gate int num; 1124*0Sstevel@tonic-gate { 1125*0Sstevel@tonic-gate /* Temporary failures override hard errors. */ 1126*0Sstevel@tonic-gate if (eval == EX_TEMPFAIL) 1127*0Sstevel@tonic-gate return; 1128*0Sstevel@tonic-gate 1129*0Sstevel@tonic-gate switch (num) /* Hopefully temporary errors. */ 1130*0Sstevel@tonic-gate { 1131*0Sstevel@tonic-gate #ifdef EDQUOT 1132*0Sstevel@tonic-gate case EDQUOT: /* Disc quota exceeded */ 1133*0Sstevel@tonic-gate if (bouncequota) 1134*0Sstevel@tonic-gate { 1135*0Sstevel@tonic-gate eval = EX_UNAVAILABLE; 1136*0Sstevel@tonic-gate break; 1137*0Sstevel@tonic-gate } 1138*0Sstevel@tonic-gate /* FALLTHROUGH */ 1139*0Sstevel@tonic-gate #endif /* EDQUOT */ 1140*0Sstevel@tonic-gate #ifdef EAGAIN 1141*0Sstevel@tonic-gate case EAGAIN: /* Resource temporarily unavailable */ 1142*0Sstevel@tonic-gate #endif 1143*0Sstevel@tonic-gate #ifdef EBUSY 1144*0Sstevel@tonic-gate case EBUSY: /* Device busy */ 1145*0Sstevel@tonic-gate #endif 1146*0Sstevel@tonic-gate #ifdef EPROCLIM 1147*0Sstevel@tonic-gate case EPROCLIM: /* Too many processes */ 1148*0Sstevel@tonic-gate #endif 1149*0Sstevel@tonic-gate #ifdef EUSERS 1150*0Sstevel@tonic-gate case EUSERS: /* Too many users */ 1151*0Sstevel@tonic-gate #endif 1152*0Sstevel@tonic-gate #ifdef ECONNABORTED 1153*0Sstevel@tonic-gate case ECONNABORTED: /* Software caused connection abort */ 1154*0Sstevel@tonic-gate #endif 1155*0Sstevel@tonic-gate #ifdef ECONNREFUSED 1156*0Sstevel@tonic-gate case ECONNREFUSED: /* Connection refused */ 1157*0Sstevel@tonic-gate #endif 1158*0Sstevel@tonic-gate #ifdef ECONNRESET 1159*0Sstevel@tonic-gate case ECONNRESET: /* Connection reset by peer */ 1160*0Sstevel@tonic-gate #endif 1161*0Sstevel@tonic-gate #ifdef EDEADLK 1162*0Sstevel@tonic-gate case EDEADLK: /* Resource deadlock avoided */ 1163*0Sstevel@tonic-gate #endif 1164*0Sstevel@tonic-gate #ifdef EFBIG 1165*0Sstevel@tonic-gate case EFBIG: /* File too large */ 1166*0Sstevel@tonic-gate #endif 1167*0Sstevel@tonic-gate #ifdef EHOSTDOWN 1168*0Sstevel@tonic-gate case EHOSTDOWN: /* Host is down */ 1169*0Sstevel@tonic-gate #endif 1170*0Sstevel@tonic-gate #ifdef EHOSTUNREACH 1171*0Sstevel@tonic-gate case EHOSTUNREACH: /* No route to host */ 1172*0Sstevel@tonic-gate #endif 1173*0Sstevel@tonic-gate #ifdef EMFILE 1174*0Sstevel@tonic-gate case EMFILE: /* Too many open files */ 1175*0Sstevel@tonic-gate #endif 1176*0Sstevel@tonic-gate #ifdef ENETDOWN 1177*0Sstevel@tonic-gate case ENETDOWN: /* Network is down */ 1178*0Sstevel@tonic-gate #endif 1179*0Sstevel@tonic-gate #ifdef ENETRESET 1180*0Sstevel@tonic-gate case ENETRESET: /* Network dropped connection on reset */ 1181*0Sstevel@tonic-gate #endif 1182*0Sstevel@tonic-gate #ifdef ENETUNREACH 1183*0Sstevel@tonic-gate case ENETUNREACH: /* Network is unreachable */ 1184*0Sstevel@tonic-gate #endif 1185*0Sstevel@tonic-gate #ifdef ENFILE 1186*0Sstevel@tonic-gate case ENFILE: /* Too many open files in system */ 1187*0Sstevel@tonic-gate #endif 1188*0Sstevel@tonic-gate #ifdef ENOBUFS 1189*0Sstevel@tonic-gate case ENOBUFS: /* No buffer space available */ 1190*0Sstevel@tonic-gate #endif 1191*0Sstevel@tonic-gate #ifdef ENOMEM 1192*0Sstevel@tonic-gate case ENOMEM: /* Cannot allocate memory */ 1193*0Sstevel@tonic-gate #endif 1194*0Sstevel@tonic-gate #ifdef ENOSPC 1195*0Sstevel@tonic-gate case ENOSPC: /* No space left on device */ 1196*0Sstevel@tonic-gate #endif 1197*0Sstevel@tonic-gate #ifdef EROFS 1198*0Sstevel@tonic-gate case EROFS: /* Read-only file system */ 1199*0Sstevel@tonic-gate #endif 1200*0Sstevel@tonic-gate #ifdef ESTALE 1201*0Sstevel@tonic-gate case ESTALE: /* Stale NFS file handle */ 1202*0Sstevel@tonic-gate #endif 1203*0Sstevel@tonic-gate #ifdef ETIMEDOUT 1204*0Sstevel@tonic-gate case ETIMEDOUT: /* Connection timed out */ 1205*0Sstevel@tonic-gate #endif 1206*0Sstevel@tonic-gate #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) 1207*0Sstevel@tonic-gate case EWOULDBLOCK: /* Operation would block. */ 1208*0Sstevel@tonic-gate #endif 1209*0Sstevel@tonic-gate eval = EX_TEMPFAIL; 1210*0Sstevel@tonic-gate break; 1211*0Sstevel@tonic-gate default: 1212*0Sstevel@tonic-gate eval = EX_UNAVAILABLE; 1213*0Sstevel@tonic-gate break; 1214*0Sstevel@tonic-gate } 1215*0Sstevel@tonic-gate } 1216