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