Index: head/usr.sbin/sendmail/mail.local/mail.local.c =================================================================== --- head/usr.sbin/sendmail/mail.local/mail.local.c (revision 19843) +++ head/usr.sbin/sendmail/mail.local/mail.local.c (revision 19844) @@ -1,935 +1,935 @@ /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: mail.local.c,v 1.2 1996/10/29 05:22:52 peter Exp $ + * $Id: mail.local.c,v 1.3 1996/10/29 05:35:24 peter Exp $ */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1990, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mail.local.c 8.30 (Berkeley) 10/9/96"; +static char sccsid[] = "@(#)mail.local.c 8.33 (Berkeley) 11/13/96"; #endif /* not lint */ /* * This is not intended to compile on System V derived systems * such as Solaris or HP-UX, since they use a totally different * approach to mailboxes (essentially, they have a setgid program * rather than setuid, and they rely on the ability to "give away" * files to do their work). IT IS NOT A BUG that this doesn't * compile on such architectures. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EX_OK # undef EX_OK /* unistd.h may have another use for this */ #endif #include #include #if __STDC__ #include #else #include #endif #if (defined(sun) && defined(__svr4__)) || defined(__SVR4) # define USE_LOCKF 1 # define USE_SETEUID 1 # define _PATH_MAILDIR "/var/mail" #endif #if defined(_AIX) # define USE_LOCKF 1 # define USE_VSYSLOG 0 #endif #if defined(ultrix) # define USE_VSYSLOG 0 #endif #if defined(__osf__) # define USE_VSYSLOG 0 #endif #if defined(NeXT) # include # define _PATH_MAILDIR "/usr/spool/mail" # define __dead /* empty */ # define S_IRUSR S_IREAD # define S_IWUSR S_IWRITE #endif /* * If you don't have flock, you could try using lockf instead. */ #ifdef USE_LOCKF # define flock(a, b) lockf(a, b, 0) # define LOCK_EX F_LOCK #endif #ifndef USE_VSYSLOG # define USE_VSYSLOG 1 #endif #ifndef LOCK_EX # include #endif #ifdef BSD4_4 # include "pathnames.h" #endif #ifndef __P # ifdef __STDC__ # define __P(protos) protos # else # define __P(protos) () # define const # endif #endif #ifndef __dead # if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__) # define __dead __volatile # else # define __dead # endif #endif #ifndef BSD4_4 # define _BSD_VA_LIST_ va_list #endif #if !defined(BSD4_4) && !defined(linux) extern char *strerror __P((int)); extern int snprintf __P((char *, int, const char *, ...)); extern FILE *fdopen __P((int, const char *)); #endif /* * If you don't have setreuid, and you have saved uids, and you have * a seteuid() call that doesn't try to emulate using setuid(), then * you can try defining USE_SETEUID. */ #ifdef USE_SETEUID # define setreuid(r, e) seteuid(e) #endif #ifndef _PATH_LOCTMP # define _PATH_LOCTMP "/tmp/local.XXXXXX" #endif #ifndef _PATH_MAILDIR # define _PATH_MAILDIR "/var/spool/mail" #endif #ifndef S_ISREG # define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG) #endif int eval = EX_OK; /* sysexits.h error value. */ void deliver __P((int, char *, int, int)); void e_to_sys __P((int)); void err __P((const char *, ...)) __dead2; void notifybiff __P((char *)); int store __P((char *)); void usage __P((void)); void vwarn __P((const char *, _BSD_VA_LIST_)); void warn __P((const char *, ...)); void lockmbox __P((char *)); void unlockmbox __P((void)); int main(argc, argv) int argc; char *argv[]; { struct passwd *pw; int ch, fd, nobiff, nofsync; uid_t uid; char *from; extern char *optarg; extern int optind; /* make sure we have some open file descriptors */ for (fd = 10; fd < 30; fd++) (void) close(fd); /* use a reasonable umask */ (void) umask(0077); #ifdef LOG_MAIL openlog("mail.local", 0, LOG_MAIL); #else openlog("mail.local", 0); #endif from = NULL; nobiff = 0; nofsync = 0; while ((ch = getopt(argc, argv, "bdf:r:s")) != EOF) switch(ch) { case 'b': nobiff++; break; case 'd': /* Backward compatible. */ break; case 'f': case 'r': /* Backward compatible. */ if (from != NULL) { warn("multiple -f options"); usage(); } from = optarg; break; case 's': nofsync++; break; case '?': default: usage(); } argc -= optind; argv += optind; if (!*argv) usage(); /* * If from not specified, use the name from getlogin() if the * uid matches, otherwise, use the name from the password file * corresponding to the uid. */ uid = getuid(); if (!from && (!(from = getlogin()) || !(pw = getpwnam(from)) || pw->pw_uid != uid)) from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; /* * There is no way to distinguish the error status of one delivery * from the rest of the deliveries. So, if we failed hard on one * or more deliveries, but had no failures on any of the others, we * return a hard failure. If we failed temporarily on one or more * deliveries, we return a temporary failure regardless of the other * failures. This results in the delivery being reattempted later * at the expense of repeated failures and multiple deliveries. */ for (fd = store(from); *argv; ++argv) deliver(fd, *argv, nobiff, nofsync); exit(eval); } int store(from) char *from; { FILE *fp; time_t tval; int fd, eline; char line[2048]; char tmpbuf[sizeof _PATH_LOCTMP + 1]; strcpy(tmpbuf, _PATH_LOCTMP); if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { e_to_sys(errno); err("unable to open temporary file"); } (void)unlink(tmpbuf); (void)time(&tval); (void)fprintf(fp, "From %s %s", from, ctime(&tval)); line[0] = '\0'; for (eline = 1; fgets(line, sizeof(line), stdin);) { if (line[0] == '\n') eline = 1; else { if (eline && line[0] == 'F' && !memcmp(line, "From ", 5)) (void)putc('>', fp); eline = 0; } (void)fprintf(fp, "%s", line); if (ferror(fp)) { e_to_sys(errno); err("temporary file write error"); } } /* If message not newline terminated, need an extra. */ if (!strchr(line, '\n')) (void)putc('\n', fp); /* Output a newline; note, empty messages are allowed. */ (void)putc('\n', fp); if (fflush(fp) == EOF || ferror(fp)) { e_to_sys(errno); err("temporary file write error"); } return (fd); } void deliver(fd, name, nobiff, nofsync) int fd, nobiff, nofsync; char *name; { struct stat fsb, sb; struct passwd *pw; int mbfd, nr, nw, off; char *p; char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; off_t curoff; /* * Disallow delivery to unknown names -- special mailboxes can be * handled in the sendmail aliases file. */ if (!(pw = getpwnam(name))) { if (eval != EX_TEMPFAIL) eval = EX_UNAVAILABLE; warn("unknown name: %s", name); return; } endpwent(); /* * Keep name reasonably short to avoid buffer overruns. * This isn't necessary on BSD because of the proper * definition of snprintf(), but it can cause problems * on other systems. * Also, clear out any bogus characters. */ if (strlen(name) > 40) name[40] = '\0'; for (p = name; *p != '\0'; p++) { if (!isascii(*p)) *p &= 0x7f; else if (!isprint(*p)) *p = '.'; } (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name); /* * If the mailbox is linked or a symlink, fail. There's an obvious * race here, that the file was replaced with a symbolic link after * the lstat returned, but before the open. We attempt to detect * this by comparing the original stat information and information * returned by an fstat of the file descriptor returned by the open. * * NB: this is a symptom of a larger problem, that the mail spooling * directory is writeable by the wrong users. If that directory is * writeable, system security is compromised for other reasons, and * it cannot be fixed here. * * If we created the mailbox, set the owner/group. If that fails, * just return. Another process may have already opened it, so we * can't unlink it. Historically, binmail set the owner/group at * each mail delivery. We no longer do this, assuming that if the * ownership or permissions were changed there was a reason. * * XXX * open(2) should support flock'ing the file. */ tryagain: lockmbox(path); if (lstat(path, &sb)) { mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); if (mbfd == -1) { if (errno == EEXIST) goto tryagain; } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) { e_to_sys(errno); warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name); goto err1; } } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) { e_to_sys(errno); warn("%s: irregular file", path); goto err0; } else if (sb.st_uid != pw->pw_uid) { eval = EX_CANTCREAT; warn("%s: wrong ownership (%d)", path, sb.st_uid); goto err0; } else { mbfd = open(path, O_APPEND|O_WRONLY, 0); if (mbfd != -1 && (fstat(mbfd, &fsb) || fsb.st_nlink != 1 || !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid)) { eval = EX_CANTCREAT; warn("%s: file changed after open", path); goto err1; } } if (mbfd == -1) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); goto err0; } /* Wait until we can get a lock on the file. */ if (flock(mbfd, LOCK_EX)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); goto err1; } if (!nobiff) { /* Get the starting offset of the new message for biff. */ curoff = lseek(mbfd, (off_t)0, SEEK_END); (void)snprintf(biffmsg, sizeof(biffmsg), sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n", name, curoff); } /* Copy the message into the file. */ if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { e_to_sys(errno); warn("temporary file: %s", strerror(errno)); goto err1; } if (setreuid(0, pw->pw_uid) < 0) { e_to_sys(errno); warn("setreuid(0, %d): %s (r=%d, e=%d)", pw->pw_uid, strerror(errno), getuid(), geteuid()); goto err1; } #ifdef DEBUG printf("new euid = %d\n", geteuid()); #endif while ((nr = read(fd, buf, sizeof(buf))) > 0) for (off = 0; off < nr; off += nw) if ((nw = write(mbfd, buf + off, nr - off)) < 0) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); goto err3; } if (nr < 0) { e_to_sys(errno); warn("temporary file: %s", strerror(errno)); goto err3; } /* Flush to disk, don't wait for update. */ if (!nofsync && fsync(mbfd)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); err3: if (setreuid(0, 0) < 0) { e_to_sys(errno); warn("setreuid(0, 0): %s", strerror(errno)); } #ifdef DEBUG printf("reset euid = %d\n", geteuid()); #endif err2: (void)ftruncate(mbfd, curoff); err1: (void)close(mbfd); err0: unlockmbox(); return; } /* Close and check -- NFS doesn't write until the close. */ if (close(mbfd)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); unlockmbox(); return; } if (setreuid(0, 0) < 0) { e_to_sys(errno); warn("setreuid(0, 0): %s", strerror(errno)); } #ifdef DEBUG printf("reset euid = %d\n", geteuid()); #endif unlockmbox(); if (!nobiff) notifybiff(biffmsg); } /* * user.lock files are necessary for compatibility with other * systems, e.g., when the mail spool file is NFS exported. * Alas, mailbox locking is more than just a local matter. * EPA 11/94. */ char lockname[MAXPATHLEN]; int locked = 0; void lockmbox(path) char *path; { int statfailed = 0; if (locked) return; sprintf(lockname, "%s.lock", path); for (;; sleep(5)) { int fd; struct stat st; time_t now; fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0); if (fd >= 0) { locked = 1; close(fd); return; } if (stat(lockname, &st) < 0) { if (statfailed++ > 5) return; continue; } statfailed = 0; time(&now); if (now < st.st_ctime + 300) continue; unlink(lockname); } } void unlockmbox() { if (!locked) return; unlink(lockname); locked = 0; } void notifybiff(msg) char *msg; { static struct sockaddr_in addr; static int f = -1; struct hostent *hp; struct servent *sp; int len; if (!addr.sin_family) { /* Be silent if biff service not available. */ if (!(sp = getservbyname("biff", "udp"))) return; if (!(hp = gethostbyname("localhost"))) { warn("localhost: %s", strerror(errno)); return; } addr.sin_family = hp->h_addrtype; memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); addr.sin_port = sp->s_port; } if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { warn("socket: %s", strerror(errno)); return; } len = strlen(msg) + 1; if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)) != len) warn("sendto biff: %s", strerror(errno)); } void usage() { eval = EX_USAGE; err("usage: mail.local [-b] [-f from] [-s] user ..."); } #if __STDC__ void err(const char *fmt, ...) #else void err(fmt, va_alist) const char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif vwarn(fmt, ap); va_end(ap); exit(eval); } void #if __STDC__ warn(const char *fmt, ...) #else warn(fmt, va_alist) const char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif vwarn(fmt, ap); va_end(ap); } void vwarn(fmt, ap) const char *fmt; _BSD_VA_LIST_ ap; { /* * Log the message to stderr. * * Don't use LOG_PERROR as an openlog() flag to do this, * it's not portable enough. */ if (eval != EX_USAGE) (void)fprintf(stderr, "mail.local: "); (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, "\n"); #if USE_VSYSLOG /* Log the message to syslog. */ vsyslog(LOG_ERR, fmt, ap); #else { char fmtbuf[10240]; (void) sprintf(fmtbuf, fmt, ap); syslog(LOG_ERR, "%s", fmtbuf); } #endif } /* * e_to_sys -- * Guess which errno's are temporary. Gag me. */ void e_to_sys(num) int num; { /* Temporary failures override hard errors. */ if (eval == EX_TEMPFAIL) return; switch(num) { /* Hopefully temporary errors. */ #ifdef EAGAIN case EAGAIN: /* Resource temporarily unavailable */ #endif #ifdef EDQUOT case EDQUOT: /* Disc quota exceeded */ #endif #ifdef EBUSY case EBUSY: /* Device busy */ #endif #ifdef EPROCLIM case EPROCLIM: /* Too many processes */ #endif #ifdef EUSERS case EUSERS: /* Too many users */ #endif #ifdef ECONNABORTED case ECONNABORTED: /* Software caused connection abort */ #endif #ifdef ECONNREFUSED case ECONNREFUSED: /* Connection refused */ #endif #ifdef ECONNRESET case ECONNRESET: /* Connection reset by peer */ #endif #ifdef EDEADLK case EDEADLK: /* Resource deadlock avoided */ #endif #ifdef EFBIG case EFBIG: /* File too large */ #endif #ifdef EHOSTDOWN case EHOSTDOWN: /* Host is down */ #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: /* No route to host */ #endif #ifdef EMFILE case EMFILE: /* Too many open files */ #endif #ifdef ENETDOWN case ENETDOWN: /* Network is down */ #endif #ifdef ENETRESET case ENETRESET: /* Network dropped connection on reset */ #endif #ifdef ENETUNREACH case ENETUNREACH: /* Network is unreachable */ #endif #ifdef ENFILE case ENFILE: /* Too many open files in system */ #endif #ifdef ENOBUFS case ENOBUFS: /* No buffer space available */ #endif #ifdef ENOMEM case ENOMEM: /* Cannot allocate memory */ #endif #ifdef ENOSPC case ENOSPC: /* No space left on device */ #endif #ifdef EROFS case EROFS: /* Read-only file system */ #endif #ifdef ESTALE case ESTALE: /* Stale NFS file handle */ #endif #ifdef ETIMEDOUT case ETIMEDOUT: /* Connection timed out */ #endif #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK case EWOULDBLOCK: /* Operation would block. */ #endif eval = EX_TEMPFAIL; break; default: eval = EX_UNAVAILABLE; break; } } #if !defined(BSD4_4) && !defined(__osf__) char * strerror(eno) int eno; { extern int sys_nerr; extern char *sys_errlist[]; static char ebuf[60]; if (eno >= 0 && eno <= sys_nerr) return sys_errlist[eno]; (void) sprintf(ebuf, "Error %d", eno); return ebuf; } # endif #if !defined(BSD4_4) && !defined(linux) # if __STDC__ snprintf(char *buf, int bufsiz, const char *fmt, ...) # else snprintf(buf, bufsiz, fmt, va_alist) char *buf; int bufsiz; const char *fmt; va_dcl # endif { va_list ap; # if __STDC__ va_start(ap, fmt); # else va_start(ap); # endif vsprintf(buf, fmt, ap); va_end(ap); } #endif #ifdef ultrix /* * Copyright (c) 1987, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include static int _gettemp(); mkstemp(path) char *path; { int fd; return (_gettemp(path, &fd) ? fd : -1); } /* char * mktemp(path) char *path; { return(_gettemp(path, (int *)NULL) ? path : (char *)NULL); } */ static _gettemp(path, doopen) char *path; register int *doopen; { extern int errno; register char *start, *trv; struct stat sbuf; u_int pid; pid = getpid(); for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ while (*--trv == 'X') { *trv = (pid % 10) + '0'; pid /= 10; } /* * check the target directory; if you have six X's and it * doesn't exist this runs for a *very* long time. */ for (start = trv + 1;; --trv) { if (trv <= path) break; if (*trv == '/') { *trv = '\0'; if (stat(path, &sbuf)) return(0); if (!S_ISDIR(sbuf.st_mode)) { errno = ENOTDIR; return(0); } *trv = '/'; break; } } for (;;) { if (doopen) { if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) return(1); if (errno != EEXIST) return(0); } else if (stat(path, &sbuf)) return(errno == ENOENT ? 1 : 0); /* tricky little algorithm for backward compatibility */ for (trv = start;;) { if (!*trv) return(0); if (*trv == 'z') *trv++ = 'a'; else { if (isdigit(*trv)) *trv = 'a'; else ++*trv; break; } } } /*NOTREACHED*/ } #endif Index: head/usr.sbin/sendmail/makemap/makemap.c =================================================================== --- head/usr.sbin/sendmail/makemap/makemap.c (revision 19843) +++ head/usr.sbin/sendmail/makemap/makemap.c (revision 19844) @@ -1,509 +1,533 @@ /* * Copyright (c) 1992 Eric P. Allman. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)makemap.c 8.17 (Berkeley) 9/25/96"; +static char sccsid[] = "@(#)makemap.c 8.18 (Berkeley) 11/13/96"; #endif /* not lint */ #include #include #include #include #include #include #ifndef ISC_UNIX # include #endif #define NOT_SENDMAIL #include "useful.h" #include "conf.h" #ifdef NDBM #include #endif #ifdef NEWDB #include #endif enum type { T_DBM, T_BTREE, T_HASH, T_ERR, T_UNKNOWN }; union dbent { #ifdef NDBM datum dbm; #endif #ifdef NEWDB DBT db; #endif struct { char *data; size_t size; } xx; }; #define BUFSIZE 1024 main(argc, argv) int argc; char **argv; { char *progname; bool inclnull = FALSE; bool notrunc = FALSE; bool allowreplace = FALSE; bool allowdups = FALSE; bool verbose = FALSE; bool foldcase = TRUE; int exitstat; int opt; char *typename; char *mapname; char *ext; char *lext; int lineno; int st; int mode; int putflags; + long dbcachesize = 1024 * 1024; enum type type; int fd; union { #ifdef NDBM DBM *dbm; #endif #ifdef NEWDB DB *db; #endif void *dbx; } dbp; union dbent key, val; #ifdef NEWDB BTREEINFO bti; + HASHINFO hinfo; #endif char ibuf[BUFSIZE]; char fbuf[MAXNAME]; char lbuf[MAXNAME]; extern char *optarg; extern int optind; extern bool lockfile(); progname = argv[0]; - while ((opt = getopt(argc, argv, "Ndforv")) != EOF) +#ifdef FFR_CFLAG +#define OPTIONS "Nc:dforv" +#else +#define OPTIONS "Ndforv" +#endif + while ((opt = getopt(argc, argv, OPTIONS)) != EOF) { switch (opt) { case 'N': inclnull = TRUE; break; +#ifdef FFR_CFLAG + case 'c': + dbcachesize = atol(optarg); + break; +#endif + case 'd': allowdups = TRUE; break; case 'f': foldcase = FALSE; break; case 'o': notrunc = TRUE; break; case 'r': allowreplace = TRUE; break; case 'v': verbose = TRUE; break; default: type = T_ERR; break; } } argc -= optind; argv += optind; if (argc != 2) type = T_ERR; else { typename = argv[0]; mapname = argv[1]; ext = NULL; lext = NULL; if (strcmp(typename, "dbm") == 0) { type = T_DBM; lext = ".dir"; } else if (strcmp(typename, "btree") == 0) { type = T_BTREE; ext = ".db"; } else if (strcmp(typename, "hash") == 0) { type = T_HASH; ext = ".db"; } else type = T_UNKNOWN; } switch (type) { case T_ERR: +#ifdef FFR_CFLAG + fprintf(stderr, "Usage: %s [-N] [-c cachesize] [-d] [-f] [-o] [-r] [-v] type mapname\n", progname); +#else fprintf(stderr, "Usage: %s [-N] [-d] [-f] [-o] [-r] [-v] type mapname\n", progname); +#endif exit(EX_USAGE); case T_UNKNOWN: fprintf(stderr, "%s: Unknown database type %s\n", progname, typename); exit(EX_USAGE); #ifndef NDBM case T_DBM: #endif #ifndef NEWDB case T_BTREE: case T_HASH: #endif fprintf(stderr, "%s: Type %s not supported in this version\n", progname, typename); exit(EX_UNAVAILABLE); #ifdef NEWDB case T_BTREE: bzero(&bti, sizeof bti); if (allowdups) bti.flags |= R_DUP; if (allowdups || allowreplace) putflags = 0; else putflags = R_NOOVERWRITE; break; case T_HASH: if (allowreplace) putflags = 0; else putflags = R_NOOVERWRITE; break; #endif #ifdef NDBM case T_DBM: if (allowdups) { fprintf(stderr, "%s: Type %s does not support -d (allow dups)\n", progname, typename); exit(EX_UNAVAILABLE); } if (allowreplace) putflags = DBM_REPLACE; else putflags = DBM_INSERT; break; #endif } /* ** Adjust file names. */ if (ext != NULL) { int el, fl; el = strlen(ext); fl = strlen(mapname); if (fl < el || strcmp(&mapname[fl - el], ext) != 0) { strcpy(fbuf, mapname); strcat(fbuf, ext); mapname = fbuf; } } strcpy(lbuf, mapname); if (lext != NULL) strcat(lbuf, lext); /* ** Create the database. */ mode = O_RDWR; if (!notrunc) mode |= O_CREAT|O_TRUNC; #ifdef O_EXLOCK mode |= O_EXLOCK; #else /* pre-lock the database */ fd = open(lbuf, mode & ~O_TRUNC, 0644); if (fd < 0) { fprintf(stderr, "%s: cannot create type %s map %s\n", progname, typename, mapname); exit(EX_CANTCREAT); } (void) lockfile(fd); #endif switch (type) { #ifdef NDBM case T_DBM: dbp.dbm = dbm_open(mapname, mode, 0644); break; #endif #ifdef NEWDB case T_HASH: - dbp.db = dbopen(mapname, mode, 0644, DB_HASH, NULL); + /* tweak some parameters for performance */ + hinfo.nelem = 4096; + hinfo.cachesize = dbcachesize; + + dbp.db = dbopen(mapname, mode, 0644, DB_HASH, &hinfo); if (dbp.db != NULL) { # if OLD_NEWDB (void) (*dbp.db->sync)(dbp.db); # else (void) (*dbp.db->sync)(dbp.db, 0); # endif } break; case T_BTREE: + /* tweak some parameters for performance */ + bti.cachesize = dbcachesize; + dbp.db = dbopen(mapname, mode, 0644, DB_BTREE, &bti); if (dbp.db != NULL) { # if OLD_NEWDB (void) (*dbp.db->sync)(dbp.db); # else (void) (*dbp.db->sync)(dbp.db, 0); # endif } break; #endif default: fprintf(stderr, "%s: internal error: type %d\n", progname, type); exit(EX_SOFTWARE); } if (dbp.dbx == NULL) { fprintf(stderr, "%s: cannot open type %s map %s\n", progname, typename, mapname); exit(EX_CANTCREAT); } /* ** Copy the data */ lineno = 0; exitstat = EX_OK; while (fgets(ibuf, sizeof ibuf, stdin) != NULL) { register char *p; lineno++; /* ** Parse the line. */ p = strchr(ibuf, '\n'); if (p != NULL) *p = '\0'; else if (!feof(stdin)) { fprintf(stderr, "%s: %s: line %d: line too long (%d bytes max)\n", progname, mapname, lineno, sizeof ibuf); continue; } if (ibuf[0] == '\0' || ibuf[0] == '#') continue; if (isspace(ibuf[0])) { fprintf(stderr, "%s: %s: line %d: syntax error (leading space)\n", progname, mapname, lineno); continue; } key.xx.data = ibuf; for (p = ibuf; *p != '\0' && !isspace(*p); p++) { if (foldcase && isupper(*p)) *p = tolower(*p); } key.xx.size = p - key.xx.data; if (inclnull) key.xx.size++; if (*p != '\0') *p++ = '\0'; while (isspace(*p)) p++; if (*p == '\0') { fprintf(stderr, "%s: %s: line %d: no RHS for LHS %s\n", progname, mapname, lineno, key.xx.data); continue; } val.xx.data = p; val.xx.size = strlen(p); if (inclnull) val.xx.size++; /* ** Do the database insert. */ if (verbose) { printf("key=`%s', val=`%s'\n", key.xx.data, val.xx.data); } switch (type) { #ifdef NDBM case T_DBM: st = dbm_store(dbp.dbm, key.dbm, val.dbm, putflags); break; #endif #ifdef NEWDB case T_BTREE: case T_HASH: st = (*dbp.db->put)(dbp.db, &key.db, &val.db, putflags); break; #endif } if (st < 0) { fprintf(stderr, "%s: %s: line %d: key %s: put error\n", progname, mapname, lineno, key.xx.data); perror(mapname); exitstat = EX_IOERR; } else if (st > 0) { fprintf(stderr, "%s: %s: line %d: key %s: duplicate key\n", progname, mapname, lineno, key.xx.data); } } /* ** Now close the database. */ switch (type) { #ifdef NDBM case T_DBM: dbm_close(dbp.dbm); break; #endif #ifdef NEWDB case T_HASH: case T_BTREE: if ((*dbp.db->close)(dbp.db) < 0) { fprintf(stderr, "%s: %s: error on close\n", progname, mapname); perror(mapname); exitstat = EX_IOERR; } #endif } #ifndef O_EXLOCK /* release locks */ close(fd); #endif exit (exitstat); } /* ** LOCKFILE -- lock a file using flock or (shudder) fcntl locking ** ** Parameters: ** fd -- the file descriptor of the file. ** ** Returns: ** TRUE if the lock was acquired. ** FALSE otherwise. */ bool lockfile(fd) int fd; { # if !HASFLOCK int action; struct flock lfd; extern int errno; bzero(&lfd, sizeof lfd); lfd.l_type = F_WRLCK; action = F_SETLKW; if (fcntl(fd, action, &lfd) >= 0) return TRUE; /* ** On SunOS, if you are testing using -oQ/tmp/mqueue or ** -oA/tmp/aliases or anything like that, and /tmp is mounted ** as type "tmp" (that is, served from swap space), the ** previous fcntl will fail with "Invalid argument" errors. ** Since this is fairly common during testing, we will assume ** that this indicates that the lock is successfully grabbed. */ if (errno == EINVAL) return TRUE; # else /* HASFLOCK */ if (flock(fd, LOCK_EX) >= 0) return TRUE; # endif return FALSE; } Index: head/usr.sbin/sendmail/src/collect.c =================================================================== --- head/usr.sbin/sendmail/src/collect.c (revision 19843) +++ head/usr.sbin/sendmail/src/collect.c (revision 19844) @@ -1,751 +1,752 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)collect.c 8.58 (Berkeley) 9/18/96"; +static char sccsid[] = "@(#)collect.c 8.60 (Berkeley) 11/15/96"; #endif /* not lint */ # include # include "sendmail.h" /* ** COLLECT -- read & parse message header & make temp file. ** ** Creates a temporary file name and copies the standard ** input to that file. Leading UNIX-style "From" lines are ** stripped off (after important information is extracted). ** ** Parameters: ** fp -- file to read. ** smtpmode -- if set, we are running SMTP: give an RFC821 ** style message to say we are ready to collect ** input, and never ignore a single dot to mean ** end of message. ** requeueflag -- this message will be requeued later, so ** don't do final processing on it. ** hdrp -- the location to stash the header. ** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** Temp file is created and filled. ** The from person may be set. */ static jmp_buf CtxCollectTimeout; static void collecttimeout(); static bool CollectProgress; static EVENT *CollectTimeout; /* values for input state machine */ #define IS_NORM 0 /* middle of line */ #define IS_BOL 1 /* beginning of line */ #define IS_DOT 2 /* read a dot at beginning of line */ #define IS_DOTCR 3 /* read ".\r" at beginning of line */ #define IS_CR 4 /* read a carriage return */ /* values for message state machine */ #define MS_UFROM 0 /* reading Unix from line */ #define MS_HEADER 1 /* reading message header */ #define MS_BODY 2 /* reading message body */ void collect(fp, smtpmode, requeueflag, hdrp, e) FILE *fp; bool smtpmode; bool requeueflag; HDR **hdrp; register ENVELOPE *e; { register FILE *tf; bool ignrdot = smtpmode ? FALSE : IgnrDot; time_t dbto = smtpmode ? TimeOuts.to_datablock : 0; register char *bp; int c = '\0'; bool inputerr = FALSE; bool headeronly; char *buf; int buflen; int istate; int mstate; u_char *pbp; u_char peekbuf[8]; char dfname[20]; char bufbuf[MAXLINE]; extern bool isheader(); extern void eatheader(); extern void tferror(); headeronly = hdrp != NULL; /* ** Create the temp file name and create the file. */ if (!headeronly) { struct stat stbuf; strcpy(dfname, queuename(e, 'd')); if ((tf = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL) { syserr("Cannot create %s", dfname); e->e_flags |= EF_NO_BODY_RETN; finis(); } if (fstat(fileno(tf), &stbuf) < 0) e->e_dfino = -1; else { e->e_dfdev = stbuf.st_dev; e->e_dfino = stbuf.st_ino; } HasEightBits = FALSE; e->e_msgsize = 0; e->e_flags |= EF_HAS_DF; } /* ** Tell ARPANET to go ahead. */ if (smtpmode) message("354 Enter mail, end with \".\" on a line by itself"); if (tTd(30, 2)) printf("collect\n"); /* ** Read the message. ** ** This is done using two interleaved state machines. ** The input state machine is looking for things like ** hidden dots; the message state machine is handling ** the larger picture (e.g., header versus body). */ buf = bp = bufbuf; buflen = sizeof bufbuf; pbp = peekbuf; istate = IS_BOL; mstate = SaveFrom ? MS_HEADER : MS_UFROM; CollectProgress = FALSE; if (dbto != 0) { /* handle possible input timeout */ if (setjmp(CtxCollectTimeout) != 0) { #ifdef LOG if (LogLevel > 2) syslog(LOG_NOTICE, "timeout waiting for input from %s during message collect", CurHostName ? CurHostName : ""); #endif errno = 0; usrerr("451 timeout waiting for input during message collect"); goto readerr; } CollectTimeout = setevent(dbto, collecttimeout, dbto); } for (;;) { if (tTd(30, 35)) printf("top, istate=%d, mstate=%d\n", istate, mstate); for (;;) { if (pbp > peekbuf) c = *--pbp; else { while (!feof(fp) && !ferror(fp)) { errno = 0; c = getc(fp); if (errno != EINTR) break; clearerr(fp); } CollectProgress = TRUE; if (TrafficLogFile != NULL && !headeronly) { if (istate == IS_BOL) fprintf(TrafficLogFile, "%05d <<< ", getpid()); if (c == EOF) fprintf(TrafficLogFile, "[EOF]\n"); else putc(c, TrafficLogFile); } if (c == EOF) goto readerr; if (SevenBitInput) c &= 0x7f; else HasEightBits |= bitset(0x80, c); } if (tTd(30, 94)) printf("istate=%d, c=%c (0x%x)\n", istate, c, c); switch (istate) { case IS_BOL: if (c == '.') { istate = IS_DOT; continue; } break; case IS_DOT: if (c == '\n' && !ignrdot && !bitset(EF_NL_NOT_EOL, e->e_flags)) goto readerr; else if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags)) { istate = IS_DOTCR; continue; } else if (c != '.' || (OpMode != MD_SMTP && OpMode != MD_DAEMON && OpMode != MD_ARPAFTP)) { *pbp++ = c; c = '.'; } break; case IS_DOTCR: if (c == '\n' && !ignrdot) goto readerr; else { /* push back the ".\rx" */ *pbp++ = c; *pbp++ = '\r'; c = '.'; } break; case IS_CR: if (c == '\n') istate = IS_BOL; else { ungetc(c, fp); c = '\r'; istate = IS_NORM; } goto bufferchar; } if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags)) { istate = IS_CR; continue; } else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags)) istate = IS_BOL; else istate = IS_NORM; bufferchar: if (!headeronly) e->e_msgsize++; if (mstate == MS_BODY) { /* just put the character out */ if (MaxMessageSize <= 0 || e->e_msgsize <= MaxMessageSize) putc(c, tf); continue; } /* header -- buffer up */ if (bp >= &buf[buflen - 2]) { char *obuf; if (mstate != MS_HEADER) break; /* out of space for header */ obuf = buf; if (buflen < MEMCHUNKSIZE) buflen *= 2; else buflen += MEMCHUNKSIZE; buf = xalloc(buflen); bcopy(obuf, buf, bp - obuf); bp = &buf[bp - obuf]; if (obuf != bufbuf) free(obuf); } if (c >= 0200 && c <= 0237) { #if 0 /* causes complaints -- figure out something for 8.9 */ usrerr("Illegal character 0x%x in header", c); #endif } else if (c != '\0') *bp++ = c; if (istate == IS_BOL) break; } *bp = '\0'; nextstate: if (tTd(30, 35)) printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n", istate, mstate, buf); switch (mstate) { extern int chompheader(); case MS_UFROM: mstate = MS_HEADER; #ifndef NOTUNIX if (strncmp(buf, "From ", 5) == 0) { extern void eatfrom(); bp = buf; eatfrom(buf, e); continue; } #endif /* fall through */ case MS_HEADER: if (!isheader(buf)) { mstate = MS_BODY; goto nextstate; } /* check for possible continuation line */ do { clearerr(fp); errno = 0; c = getc(fp); } while (errno == EINTR); if (c != EOF) ungetc(c, fp); if (c == ' ' || c == '\t') { /* yep -- defer this */ continue; } /* trim off trailing CRLF or NL */ if (*--bp != '\n' || *--bp != '\r') bp++; *bp = '\0'; if (bitset(H_EOH, chompheader(buf, FALSE, hdrp, e))) mstate = MS_BODY; break; case MS_BODY: if (tTd(30, 1)) printf("EOH\n"); if (headeronly) goto readerr; bp = buf; /* toss blank line */ if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) && bp[0] == '\r' && bp[1] == '\n') || (!bitset(EF_NL_NOT_EOL, e->e_flags) && bp[0] == '\n')) { break; } /* if not a blank separator, write it out */ if (MaxMessageSize <= 0 || e->e_msgsize <= MaxMessageSize) { while (*bp != '\0') putc(*bp++, tf); } break; } bp = buf; } readerr: if ((feof(fp) && smtpmode) || ferror(fp)) { const char *errmsg = errstring(errno); if (tTd(30, 1)) printf("collect: premature EOM: %s\n", errmsg); #ifdef LOG if (LogLevel >= 2) syslog(LOG_WARNING, "collect: premature EOM: %s", errmsg); #endif inputerr = TRUE; } /* reset global timer */ clrevent(CollectTimeout); if (headeronly) return; if (tf != NULL && (fflush(tf) != 0 || ferror(tf) || fsync(fileno(tf)) < 0 || fclose(tf) < 0)) { tferror(tf, e); flush_errors(TRUE); finis(); } /* An EOF when running SMTP is an error */ if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) { char *host; char *problem; host = RealHostName; if (host == NULL) host = "localhost"; if (feof(fp)) problem = "unexpected close"; else if (ferror(fp)) problem = "I/O error"; else problem = "read timeout"; # ifdef LOG if (LogLevel > 0 && feof(fp)) syslog(LOG_NOTICE, "collect: %s on connection from %.100s, sender=%s: %s", problem, host, shortenstring(e->e_from.q_paddr, 203), errstring(errno)); # endif if (feof(fp)) usrerr("451 collect: %s on connection from %s, from=%s", problem, host, shortenstring(e->e_from.q_paddr, 203)); else syserr("451 collect: %s on connection from %s, from=%s", problem, host, shortenstring(e->e_from.q_paddr, 203)); /* don't return an error indication */ e->e_to = NULL; e->e_flags &= ~EF_FATALERRS; e->e_flags |= EF_CLRQUEUE; /* and don't try to deliver the partial message either */ if (InChild) ExitStat = EX_QUIT; finis(); } /* ** Find out some information from the headers. ** Examples are who is the from person & the date. */ - eatheader(e, !requeueflag); + eatheader(e, TRUE); if (GrabTo && e->e_sendqueue == NULL) usrerr("No recipient addresses found in header"); /* collect statistics */ if (OpMode != MD_VERIFY) { extern void markstats(); markstats(e, (ADDRESS *) NULL); } /* ** Add an Apparently-To: line if we have no recipient lines. */ if (hvalue("to", e->e_header) != NULL || hvalue("cc", e->e_header) != NULL || hvalue("apparently-to", e->e_header) != NULL) { /* have a valid recipient header -- delete Bcc: headers */ e->e_flags |= EF_DELETE_BCC; } else if (hvalue("bcc", e->e_header) == NULL) { /* no valid recipient headers */ register ADDRESS *q; char *hdr = NULL; extern void addheader(); /* create an Apparently-To: field */ /* that or reject the message.... */ switch (NoRecipientAction) { case NRA_ADD_APPARENTLY_TO: hdr = "Apparently-To"; break; case NRA_ADD_TO: hdr = "To"; break; case NRA_ADD_BCC: addheader("Bcc", "", &e->e_header); break; case NRA_ADD_TO_UNDISCLOSED: addheader("To", "undisclosed-recipients:;", &e->e_header); break; } if (hdr != NULL) { for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (q->q_alias != NULL) continue; if (tTd(30, 3)) printf("Adding %s: %s\n", hdr, q->q_paddr); addheader(hdr, q->q_paddr, &e->e_header); } } } /* check for message too large */ if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) { + e->e_flags |= EF_NO_BODY_RETN; e->e_status = "5.2.3"; usrerr("552 Message exceeds maximum fixed size (%ld)", MaxMessageSize); # ifdef LOG if (LogLevel > 6) syslog(LOG_NOTICE, "%s: message size (%ld) exceeds maximum (%ld)", e->e_id, e->e_msgsize, MaxMessageSize); # endif } /* check for illegal 8-bit data */ if (HasEightBits) { e->e_flags |= EF_HAS8BIT; if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode) && !bitset(EF_IS_MIME, e->e_flags)) { e->e_status = "5.6.1"; usrerr("554 Eight bit data not allowed"); } } else { /* if it claimed to be 8 bits, well, it lied.... */ if (e->e_bodytype != NULL && strcasecmp(e->e_bodytype, "8BITMIME") == 0) e->e_bodytype = "7BIT"; } if ((e->e_dfp = fopen(dfname, "r")) == NULL) { /* we haven't acked receipt yet, so just chuck this */ syserr("Cannot reopen %s", dfname); finis(); } } static void collecttimeout(timeout) time_t timeout; { /* if no progress was made, die now */ if (!CollectProgress) longjmp(CtxCollectTimeout, 1); /* otherwise reset the timeout */ CollectTimeout = setevent(timeout, collecttimeout, timeout); CollectProgress = FALSE; } /* ** TFERROR -- signal error on writing the temporary file. ** ** Parameters: ** tf -- the file pointer for the temporary file. ** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** Gives an error message. ** Arranges for following output to go elsewhere. */ void tferror(tf, e) FILE *tf; register ENVELOPE *e; { setstat(EX_IOERR); if (errno == ENOSPC) { struct stat st; long avail; long bsize; extern long freediskspace __P((char *, long *)); e->e_flags |= EF_NO_BODY_RETN; if (fstat(fileno(tf), &st) < 0) st.st_size = 0; (void) freopen(queuename(e, 'd'), "w", tf); if (st.st_size <= 0) fprintf(tf, "\n*** Mail could not be accepted"); else if (sizeof st.st_size > sizeof (long)) fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", st.st_size); else fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", st.st_size); fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", MyHostName); avail = freediskspace(QueueDir, &bsize); if (avail > 0) { if (bsize > 1024) avail *= bsize / 1024; else if (bsize < 1024) avail /= 1024 / bsize; fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", avail); } e->e_status = "4.3.1"; usrerr("452 Out of disk space for temp file"); } else syserr("collect: Cannot write tf%s", e->e_id); (void) freopen("/dev/null", "w", tf); } /* ** EATFROM -- chew up a UNIX style from line and process ** ** This does indeed make some assumptions about the format ** of UNIX messages. ** ** Parameters: ** fm -- the from line. ** ** Returns: ** none. ** ** Side Effects: ** extracts what information it can from the header, ** such as the date. */ # ifndef NOTUNIX char *DowList[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL }; char *MonthList[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; void eatfrom(fm, e) char *fm; register ENVELOPE *e; { register char *p; register char **dt; if (tTd(30, 2)) printf("eatfrom(%s)\n", fm); /* find the date part */ p = fm; while (*p != '\0') { /* skip a word */ while (*p != '\0' && *p != ' ') p++; while (*p == ' ') p++; if (!(isascii(*p) && isupper(*p)) || p[3] != ' ' || p[13] != ':' || p[16] != ':') continue; /* we have a possible date */ for (dt = DowList; *dt != NULL; dt++) if (strncmp(*dt, p, 3) == 0) break; if (*dt == NULL) continue; for (dt = MonthList; *dt != NULL; dt++) if (strncmp(*dt, &p[4], 3) == 0) break; if (*dt != NULL) break; } if (*p != '\0') { char *q; /* we have found a date */ q = xalloc(25); (void) strncpy(q, p, 25); q[24] = '\0'; q = arpadate(q); define('a', newstr(q), e); } } # endif /* NOTUNIX */ Index: head/usr.sbin/sendmail/src/conf.c =================================================================== --- head/usr.sbin/sendmail/src/conf.c (revision 19843) +++ head/usr.sbin/sendmail/src/conf.c (revision 19844) @@ -1,4553 +1,4666 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)conf.c 8.312 (Berkeley) 10/17/96"; +static char sccsid[] = "@(#)conf.c 8.315 (Berkeley) 11/10/96"; #endif /* not lint */ # include "sendmail.h" # include "pathnames.h" # include # include /* ** CONF.C -- Sendmail Configuration Tables. ** ** Defines the configuration of this installation. ** ** Configuration Variables: ** HdrInfo -- a table describing well-known header fields. ** Each entry has the field name and some flags, ** which are described in sendmail.h. ** ** Notes: ** I have tried to put almost all the reasonable ** configuration information into the configuration ** file read at runtime. My intent is that anything ** here is a function of the version of UNIX you ** are running, or is really static -- for example ** the headers are a superset of widely used ** protocols. If you find yourself playing with ** this file too much, you may be making a mistake! */ /* ** Header info table ** Final (null) entry contains the flags used for any other field. ** ** Not all of these are actually handled specially by sendmail ** at this time. They are included as placeholders, to let ** you know that "someday" I intend to have sendmail do ** something with them. */ struct hdrinfo HdrInfo[] = { /* originator fields, most to least significant */ { "resent-sender", H_FROM|H_RESENT }, { "resent-from", H_FROM|H_RESENT }, { "resent-reply-to", H_FROM|H_RESENT }, { "sender", H_FROM }, { "from", H_FROM }, { "reply-to", H_FROM }, { "errors-to", H_FROM|H_ERRORSTO }, { "full-name", H_ACHECK }, { "return-receipt-to", H_RECEIPTTO }, /* destination fields */ { "to", H_RCPT }, { "resent-to", H_RCPT|H_RESENT }, { "cc", H_RCPT }, { "resent-cc", H_RCPT|H_RESENT }, { "bcc", H_RCPT|H_BCC }, { "resent-bcc", H_RCPT|H_BCC|H_RESENT }, { "apparently-to", H_RCPT }, /* message identification and control */ { "message-id", 0 }, { "resent-message-id", H_RESENT }, { "message", H_EOH }, { "text", H_EOH }, /* date fields */ { "date", 0 }, { "resent-date", H_RESENT }, /* trace fields */ { "received", H_TRACE|H_FORCE }, { "x400-received", H_TRACE|H_FORCE }, { "via", H_TRACE|H_FORCE }, { "mail-from", H_TRACE|H_FORCE }, /* miscellaneous fields */ { "comments", H_FORCE|H_ENCODABLE }, { "return-path", H_FORCE|H_ACHECK }, { "content-transfer-encoding", H_CTE }, { "content-type", H_CTYPE }, { "content-length", H_ACHECK }, { "subject", H_ENCODABLE }, { NULL, 0 } }; /* ** Location of system files/databases/etc. */ char *PidFile = _PATH_SENDMAILPID; /* stores daemon proc id */ /* ** Privacy values */ struct prival PrivacyValues[] = { { "public", PRIV_PUBLIC }, { "needmailhelo", PRIV_NEEDMAILHELO }, { "needexpnhelo", PRIV_NEEDEXPNHELO }, { "needvrfyhelo", PRIV_NEEDVRFYHELO }, { "noexpn", PRIV_NOEXPN }, { "novrfy", PRIV_NOVRFY }, { "restrictmailq", PRIV_RESTRICTMAILQ }, { "restrictqrun", PRIV_RESTRICTQRUN }, { "authwarnings", PRIV_AUTHWARNINGS }, { "noreceipts", PRIV_NORECEIPTS }, { "goaway", PRIV_GOAWAY }, { NULL, 0 } }; /* ** Miscellaneous stuff. */ int DtableSize = 50; /* max open files; reset in 4.2bsd */ /* ** SETDEFAULTS -- set default values ** ** Because of the way freezing is done, these must be initialized ** using direct code. ** ** Parameters: ** e -- the default envelope. ** ** Returns: ** none. ** ** Side Effects: ** Initializes a bunch of global variables to their ** default values. */ #define MINUTES * 60 #define HOURS * 60 MINUTES #define DAYS * 24 HOURS #ifndef MAXRULERECURSION # define MAXRULERECURSION 50 /* max ruleset recursion depth */ #endif void setdefaults(e) register ENVELOPE *e; { int i; extern void inittimeouts(); extern void setdefuser(); extern void setupmaps(); extern void setupmailers(); SpaceSub = ' '; /* option B */ QueueLA = 8; /* option x */ RefuseLA = 12; /* option X */ WkRecipFact = 30000L; /* option y */ WkClassFact = 1800L; /* option z */ WkTimeFact = 90000L; /* option Z */ QueueFactor = WkRecipFact * 20; /* option q */ FileMode = (RealUid != geteuid()) ? 0644 : 0600; /* option F */ DefUid = 1; /* option u */ DefGid = 1; /* option g */ CheckpointInterval = 10; /* option C */ MaxHopCount = 25; /* option h */ e->e_sendmode = SM_FORK; /* option d */ e->e_errormode = EM_PRINT; /* option e */ SevenBitInput = FALSE; /* option 7 */ MaxMciCache = 1; /* option k */ MciCacheTimeout = 5 MINUTES; /* option K */ LogLevel = 9; /* option L */ inittimeouts(NULL); /* option r */ PrivacyFlags = 0; /* option p */ #if MIME8TO7 MimeMode = MM_CVTMIME|MM_PASS8BIT; /* option 8 */ #else MimeMode = MM_PASS8BIT; #endif for (i = 0; i < MAXTOCLASS; i++) { TimeOuts.to_q_return[i] = 5 DAYS; /* option T */ TimeOuts.to_q_warning[i] = 0; /* option T */ } ServiceSwitchFile = "/etc/service.switch"; ServiceCacheMaxAge = (time_t) 10; HostsFile = _PATH_HOSTS; MustQuoteChars = "@,;:\\()[].'"; MciInfoTimeout = 30 MINUTES; MaxRuleRecursion = MAXRULERECURSION; MaxAliasRecursion = 10; MaxMacroRecursion = 10; ColonOkInAddr = TRUE; DoubleBounceAddr = "postmaster"; setdefuser(); setupmaps(); setupmailers(); } /* ** SETDEFUSER -- set/reset DefUser using DefUid (for initgroups()) */ void setdefuser() { struct passwd *defpwent; static char defuserbuf[40]; DefUser = defuserbuf; defpwent = sm_getpwuid(DefUid); snprintf(defuserbuf, sizeof defuserbuf, "%s", defpwent == NULL ? "nobody" : defpwent->pw_name); } /* ** HOST_MAP_INIT -- initialize host class structures */ bool host_map_init __P((MAP *map, char *args)); bool host_map_init(MAP *map, char *args) { register char *p = args; for (;;) { while (isascii(*p) && isspace(*p)) p++; if (*p != '-') break; switch (*++p) { case 'a': map->map_app = ++p; break; case 'm': map->map_mflags |= MF_MATCHONLY; break; case 't': map->map_mflags |= MF_NODEFER; break; } while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p != '\0') *p++ = '\0'; } if (map->map_app != NULL) map->map_app = newstr(map->map_app); return TRUE; } /* ** SETUPMAILERS -- initialize default mailers */ void setupmailers() { char buf[100]; extern void makemailer(); strcpy(buf, "prog, P=/bin/sh, F=lsoDq9, T=DNS/RFC822/X-Unix, A=sh -c \201u"); makemailer(buf); strcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=DNS/RFC822/X-Unix, A=FILE \201u"); makemailer(buf); strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u"); makemailer(buf); } /* ** SETUPMAPS -- set up map classes */ #define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \ { \ extern bool parse __P((MAP *, char *)); \ extern bool open __P((MAP *, int)); \ extern void close __P((MAP *)); \ extern char *lookup __P((MAP *, char *, char **, int *)); \ extern void store __P((MAP *, char *, char *)); \ s = stab(name, ST_MAPCLASS, ST_ENTER); \ s->s_mapclass.map_cname = name; \ s->s_mapclass.map_ext = ext; \ s->s_mapclass.map_cflags = flags; \ s->s_mapclass.map_parse = parse; \ s->s_mapclass.map_open = open; \ s->s_mapclass.map_close = close; \ s->s_mapclass.map_lookup = lookup; \ s->s_mapclass.map_store = store; \ } void setupmaps() { register STAB *s; #ifdef NEWDB MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, hash_map_open, db_map_close, db_map_lookup, db_map_store); MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, bt_map_open, db_map_close, db_map_lookup, db_map_store); #endif #ifdef NDBM MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, ndbm_map_open, ndbm_map_close, ndbm_map_lookup, ndbm_map_store); #endif #ifdef NIS MAPDEF("nis", NULL, MCF_ALIASOK, map_parseargs, nis_map_open, null_map_close, nis_map_lookup, null_map_store); #endif #ifdef NISPLUS MAPDEF("nisplus", NULL, MCF_ALIASOK, map_parseargs, nisplus_map_open, null_map_close, nisplus_map_lookup, null_map_store); #endif #ifdef LDAPMAP MAPDEF("ldapx", NULL, 0, ldap_map_parseargs, ldap_map_open, ldap_map_close, ldap_map_lookup, null_map_store); #endif #ifdef HESIOD MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY, map_parseargs, hes_map_open, null_map_close, hes_map_lookup, null_map_store); #endif #if NETINFO MAPDEF("netinfo", NULL, MCF_ALIASOK, map_parseargs, ni_map_open, null_map_close, ni_map_lookup, null_map_store); #endif #if 0 MAPDEF("dns", NULL, 0, dns_map_init, null_map_open, null_map_close, dns_map_lookup, null_map_store); #endif #if NAMED_BIND /* best MX DNS lookup */ MAPDEF("bestmx", NULL, MCF_OPTFILE, map_parseargs, null_map_open, null_map_close, bestmx_map_lookup, null_map_store); #endif MAPDEF("host", NULL, 0, host_map_init, null_map_open, null_map_close, host_map_lookup, null_map_store); MAPDEF("text", NULL, MCF_ALIASOK, map_parseargs, text_map_open, null_map_close, text_map_lookup, null_map_store); MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY, map_parseargs, stab_map_open, null_map_close, stab_map_lookup, stab_map_store); MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE, map_parseargs, impl_map_open, impl_map_close, impl_map_lookup, impl_map_store); /* access to system passwd file */ MAPDEF("user", NULL, MCF_OPTFILE, map_parseargs, user_map_open, null_map_close, user_map_lookup, null_map_store); /* dequote map */ MAPDEF("dequote", NULL, 0, dequote_init, null_map_open, null_map_close, dequote_map, null_map_store); #if USERDB /* user database */ MAPDEF("userdb", ".db", 0, map_parseargs, null_map_open, null_map_close, udb_map_lookup, null_map_store); #endif /* arbitrary programs */ MAPDEF("program", NULL, MCF_ALIASOK, map_parseargs, null_map_open, null_map_close, prog_map_lookup, null_map_store); /* sequenced maps */ MAPDEF("sequence", NULL, MCF_ALIASOK, seq_map_parse, null_map_open, null_map_close, seq_map_lookup, seq_map_store); /* switched interface to sequenced maps */ MAPDEF("switch", NULL, MCF_ALIASOK, map_parseargs, switch_map_open, null_map_close, seq_map_lookup, seq_map_store); /* null map lookup -- really for internal use only */ MAPDEF("null", NULL, MCF_ALIASOK|MCF_OPTFILE, map_parseargs, null_map_open, null_map_close, null_map_lookup, null_map_store); } #undef MAPDEF /* ** INITHOSTMAPS -- initial host-dependent maps ** ** This should act as an interface to any local service switch ** provided by the host operating system. ** ** Parameters: ** none ** ** Returns: ** none ** ** Side Effects: ** Should define maps "host" and "users" as necessary ** for this OS. If they are not defined, they will get ** a default value later. It should check to make sure ** they are not defined first, since it's possible that ** the config file has provided an override. */ void inithostmaps() { register int i; int nmaps; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; char buf[MAXLINE]; /* ** Set up default hosts maps. */ #if 0 nmaps = switch_map_find("hosts", maptype, mapreturn); for (i = 0; i < nmaps; i++) { if (strcmp(maptype[i], "files") == 0 && stab("hosts.files", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "hosts.files text -k 0 -v 1 /etc/hosts"); (void) makemapentry(buf); } #if NAMED_BIND else if (strcmp(maptype[i], "dns") == 0 && stab("hosts.dns", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "hosts.dns dns A"); (void) makemapentry(buf); } #endif #ifdef NISPLUS else if (strcmp(maptype[i], "nisplus") == 0 && stab("hosts.nisplus", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "hosts.nisplus nisplus -k name -v address -d hosts.org_dir"); (void) makemapentry(buf); } #endif #ifdef NIS else if (strcmp(maptype[i], "nis") == 0 && stab("hosts.nis", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "hosts.nis nis -d -k 0 -v 1 hosts.byname"); (void) makemapentry(buf); } #endif #if NETINFO else if (strcmp(maptype[i], "netinfo") == 0) && stab("hosts.netinfo", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "hosts.netinfo netinfo -v name /machines"); (void) makemapentry(buf); } #endif } #endif /* ** Make sure we have a host map. */ if (stab("host", ST_MAP, ST_FIND) == NULL) { /* user didn't initialize: set up host map */ strcpy(buf, "host host"); #if NAMED_BIND if (ConfigLevel >= 2) strcat(buf, " -a."); #endif (void) makemapentry(buf); } /* ** Set up default aliases maps */ nmaps = switch_map_find("aliases", maptype, mapreturn); for (i = 0; i < nmaps; i++) { if (strcmp(maptype[i], "files") == 0 && stab("aliases.files", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases.files null"); (void) makemapentry(buf); } #ifdef NISPLUS else if (strcmp(maptype[i], "nisplus") == 0 && stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion -d mail_aliases.org_dir"); (void) makemapentry(buf); } #endif #ifdef NIS else if (strcmp(maptype[i], "nis") == 0 && stab("aliases.nis", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases.nis nis -d mail.aliases"); (void) makemapentry(buf); } #endif #ifdef NETINFO else if (strcmp(maptype[i], "netinfo") == 0 && stab("aliases.netinfo", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases.netinfo netinfo -z, /aliases"); (void) makemapentry(buf); } #endif #ifdef HESIOD else if (strcmp(maptype[i], "hesiod") == 0 && stab("aliases.hesiod", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases.hesiod hesiod aliases"); (void) makemapentry(buf); } #endif } if (stab("aliases", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases switch aliases"); (void) makemapentry(buf); } #if 0 /* "user" map class is a better choice */ /* ** Set up default users maps. */ nmaps = switch_map_find("passwd", maptype, mapreturn); for (i = 0; i < nmaps; i++) { if (strcmp(maptype[i], "files") == 0 && stab("users.files", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "users.files text -m -z: -k0 -v6 /etc/passwd"); (void) makemapentry(buf); } #ifdef NISPLUS else if (strcmp(maptype[i], "nisplus") == 0 && stab("users.nisplus", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "users.nisplus nisplus -m -kname -vhome -d passwd.org_dir"); (void) makemapentry(buf); } #endif #ifdef NIS else if (strcmp(maptype[i], "nis") == 0 && stab("users.nis", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "users.nis nis -m -d passwd.byname"); (void) makemapentry(buf); } #endif #ifdef HESIOD else if (strcmp(maptype[i], "hesiod") == 0) && stab("users.hesiod", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "users.hesiod hesiod"); (void) makemapentry(buf); } #endif } if (stab("users", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "users switch -m passwd"); (void) makemapentry(buf); } #endif } /* ** SWITCH_MAP_FIND -- find the list of types associated with a map ** ** This is the system-dependent interface to the service switch. ** ** Parameters: ** service -- the name of the service of interest. ** maptype -- an out-array of strings containing the types ** of access to use for this service. There can ** be at most MAXMAPSTACK types for a single service. ** mapreturn -- an out-array of return information bitmaps ** for the map. ** ** Returns: ** The number of map types filled in, or -1 for failure. */ #if defined(SOLARIS) || (defined(sony_news) && defined(__svr4)) # define _USE_SUN_NSSWITCH_ #endif #ifdef _USE_SUN_NSSWITCH_ # include #endif #if defined(ultrix) || (defined(__osf__) && defined(__alpha)) # define _USE_DEC_SVC_CONF_ #endif #ifdef _USE_DEC_SVC_CONF_ # include #endif int switch_map_find(service, maptype, mapreturn) char *service; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; { int svcno; #ifdef _USE_SUN_NSSWITCH_ struct __nsw_switchconfig *nsw_conf; enum __nsw_parse_err pserr; struct __nsw_lookup *lk; static struct __nsw_lookup lkp0 = { "files", {1, 0, 0, 0}, NULL, NULL }; static struct __nsw_switchconfig lkp_default = { 0, "sendmail", 3, &lkp0 }; for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; if ((nsw_conf = __nsw_getconfig(service, &pserr)) == NULL) lk = lkp_default.lookups; else lk = nsw_conf->lookups; svcno = 0; while (lk != NULL) { maptype[svcno] = lk->service_name; if (lk->actions[__NSW_NOTFOUND] == __NSW_RETURN) mapreturn[MA_NOTFOUND] |= 1 << svcno; if (lk->actions[__NSW_TRYAGAIN] == __NSW_RETURN) mapreturn[MA_TRYAGAIN] |= 1 << svcno; if (lk->actions[__NSW_UNAVAIL] == __NSW_RETURN) mapreturn[MA_TRYAGAIN] |= 1 << svcno; svcno++; lk = lk->next; } return svcno; #endif #ifdef _USE_DEC_SVC_CONF_ struct svcinfo *svcinfo; int svc; for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; svcinfo = getsvc(); if (svcinfo == NULL) goto punt; if (strcmp(service, "hosts") == 0) svc = SVC_HOSTS; else if (strcmp(service, "aliases") == 0) svc = SVC_ALIASES; else if (strcmp(service, "passwd") == 0) svc = SVC_PASSWD; else return -1; for (svcno = 0; svcno < SVC_PATHSIZE; svcno++) { switch (svcinfo->svcpath[svc][svcno]) { case SVC_LOCAL: maptype[svcno] = "files"; break; case SVC_YP: maptype[svcno] = "nis"; break; case SVC_BIND: maptype[svcno] = "dns"; break; #ifdef SVC_HESIOD case SVC_HESIOD: maptype[svcno] = "hesiod"; break; #endif case SVC_LAST: return svcno; } } return svcno; #endif #if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) /* ** Fall-back mechanism. */ STAB *st; time_t now = curtime(); for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; if ((now - ServiceCacheTime) > (time_t) ServiceCacheMaxAge) { /* (re)read service switch */ register FILE *fp; if (ConfigFileRead) ServiceCacheTime = now; fp = fopen(ServiceSwitchFile, "r"); if (fp != NULL) { char buf[MAXLINE]; while (fgets(buf, sizeof buf, fp) != NULL) { register char *p; p = strpbrk(buf, "#\n"); if (p != NULL) *p = '\0'; p = strpbrk(buf, " \t"); if (p != NULL) *p++ = '\0'; if (buf[0] == '\0') continue; while (isspace(*p)) p++; if (*p == '\0') continue; /* ** Find/allocate space for this service entry. ** Space for all of the service strings ** are allocated at once. This means ** that we only have to free the first ** one to free all of them. */ st = stab(buf, ST_SERVICE, ST_ENTER); if (st->s_service[0] != NULL) free((void *) st->s_service[0]); p = newstr(p); for (svcno = 0; svcno < MAXMAPSTACK; ) { if (*p == '\0') break; st->s_service[svcno++] = p; p = strpbrk(p, " \t"); if (p == NULL) break; *p++ = '\0'; while (isspace(*p)) p++; } if (svcno < MAXMAPSTACK) st->s_service[svcno] = NULL; } fclose(fp); } } /* look up entry in cache */ st = stab(service, ST_SERVICE, ST_FIND); if (st != NULL && st->s_service[0] != NULL) { /* extract data */ svcno = 0; while (svcno < MAXMAPSTACK) { maptype[svcno] = st->s_service[svcno]; if (maptype[svcno++] == NULL) break; } return --svcno; } #endif /* if the service file doesn't work, use an absolute fallback */ punt: for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; svcno = 0; if (strcmp(service, "aliases") == 0) { maptype[svcno++] = "files"; #ifdef AUTO_NIS_ALIASES # ifdef NISPLUS maptype[svcno++] = "nisplus"; # endif # ifdef NIS maptype[svcno++] = "nis"; # endif #endif return svcno; } if (strcmp(service, "hosts") == 0) { # if NAMED_BIND maptype[svcno++] = "dns"; # else # if defined(sun) && !defined(BSD) && !defined(_USE_SUN_NSSWITCH_) /* SunOS */ maptype[svcno++] = "nis"; # endif # endif maptype[svcno++] = "files"; return svcno; } return -1; } /* ** USERNAME -- return the user id of the logged in user. ** ** Parameters: ** none. ** ** Returns: ** The login name of the logged in user. ** ** Side Effects: ** none. ** ** Notes: ** The return value is statically allocated. */ char * username() { static char *myname = NULL; extern char *getlogin(); register struct passwd *pw; /* cache the result */ if (myname == NULL) { myname = getlogin(); if (myname == NULL || myname[0] == '\0') { pw = sm_getpwuid(RealUid); if (pw != NULL) myname = newstr(pw->pw_name); } else { uid_t uid = RealUid; myname = newstr(myname); if ((pw = sm_getpwnam(myname)) == NULL || (uid != 0 && uid != pw->pw_uid)) { pw = sm_getpwuid(uid); if (pw != NULL) myname = newstr(pw->pw_name); } } if (myname == NULL || myname[0] == '\0') { syserr("554 Who are you?"); myname = "postmaster"; } } return (myname); } /* ** TTYPATH -- Get the path of the user's tty ** ** Returns the pathname of the user's tty. Returns NULL if ** the user is not logged in or if s/he has write permission ** denied. ** ** Parameters: ** none ** ** Returns: ** pathname of the user's tty. ** NULL if not logged in or write permission denied. ** ** Side Effects: ** none. ** ** WARNING: ** Return value is in a local buffer. ** ** Called By: ** savemail */ char * ttypath() { struct stat stbuf; register char *pathn; extern char *ttyname(); extern char *getlogin(); /* compute the pathname of the controlling tty */ if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL && (pathn = ttyname(0)) == NULL) { errno = 0; return (NULL); } /* see if we have write permission */ if (stat(pathn, &stbuf) < 0 || !bitset(02, stbuf.st_mode)) { errno = 0; return (NULL); } /* see if the user is logged in */ if (getlogin() == NULL) return (NULL); /* looks good */ return (pathn); } /* ** CHECKCOMPAT -- check for From and To person compatible. ** ** This routine can be supplied on a per-installation basis ** to determine whether a person is allowed to send a message. ** This allows restriction of certain types of internet ** forwarding or registration of users. ** ** If the hosts are found to be incompatible, an error ** message should be given using "usrerr" and an EX_ code ** should be returned. You can also set to->q_status to ** a DSN-style status code. ** ** EF_NO_BODY_RETN can be set in e->e_flags to suppress the ** body during the return-to-sender function; this should be done ** on huge messages. This bit may already be set by the ESMTP ** protocol. ** ** Parameters: ** to -- the person being sent to. ** ** Returns: ** an exit status ** ** Side Effects: ** none (unless you include the usrerr stuff) */ int checkcompat(to, e) register ADDRESS *to; register ENVELOPE *e; { # ifdef lint if (to == NULL) to++; # endif /* lint */ if (tTd(49, 1)) printf("checkcompat(to=%s, from=%s)\n", to->q_paddr, e->e_from.q_paddr); # ifdef EXAMPLE_CODE /* this code is intended as an example only */ register STAB *s; s = stab("arpa", ST_MAILER, ST_FIND); if (s != NULL && strcmp(e->e_from.q_mailer->m_name, "local") != 0 && to->q_mailer == s->s_mailer) { usrerr("553 No ARPA mail through this machine: see your system administration"); /* e->e_flags |= EF_NO_BODY_RETN; to supress body on return */ to->q_status = "5.7.1"; return (EX_UNAVAILABLE); } # endif /* EXAMPLE_CODE */ return (EX_OK); } /* ** SETSIGNAL -- set a signal handler ** ** This is essentially old BSD "signal(3)". */ sigfunc_t setsignal(sig, handler) int sig; sigfunc_t handler; { #if defined(SYS5SIGNALS) || defined(BSD4_3) return signal(sig, handler); #else struct sigaction n, o; bzero(&n, sizeof n); # if USE_SA_SIGACTION n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler; n.sa_flags = SA_RESTART|SA_SIGINFO; # else n.sa_handler = handler; # ifdef SA_RESTART n.sa_flags = SA_RESTART; # endif # endif if (sigaction(sig, &n, &o) < 0) return SIG_ERR; return o.sa_handler; #endif } /* ** RELEASESIGNAL -- release a held signal ** ** Parameters: ** sig -- the signal to release. ** ** Returns: ** 0 on success. ** -1 on failure. */ int releasesignal(sig) int sig; { #ifdef BSD4_3 # ifndef sigmask # define sigmask(s) (1 << ((s) - 1)) # endif return sigsetmask(sigblock(0) & ~sigmask(sig)); #else # ifdef ALTOS_SYSTEM_V sigrelse(sig) ; # else sigset_t sset; sigemptyset(&sset); sigaddset(&sset, sig); return sigprocmask(SIG_UNBLOCK, &sset, NULL); # endif #endif } /* ** HOLDSIGS -- arrange to hold all signals ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Arranges that signals are held. */ void holdsigs() { } /* ** RLSESIGS -- arrange to release all signals ** ** This undoes the effect of holdsigs. ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Arranges that signals are released. */ void rlsesigs() { } /* ** INIT_MD -- do machine dependent initializations ** ** Systems that have global modes that should be set should do ** them here rather than in main. */ #ifdef _AUX_SOURCE # include #endif #if SHARE_V1 # include #endif void init_md(argc, argv) int argc; char **argv; { #ifdef _AUX_SOURCE setcompat(getcompat() | COMPAT_BSDPROT); #endif #ifdef SUN_EXTENSIONS init_md_sun(); #endif #if _CONVEX_SOURCE /* keep gethostby*() from stripping the local domain name */ set_domain_trim_off(); #endif -#if SECUREWARE +#if SECUREWARE || defined(_SCO_unix_) set_auth_parameters(argc, argv); + +# ifdef _SCO_unix_ + /* + ** This is required for highest security levels (the kernel + ** won't let it call set*uid() or run setuid binaries without + ** it). It may be necessary on other SECUREWARE systems. + */ + + if (getluid() == -1) + setluid(0); +# endif #endif #ifdef VENDOR_DEFAULT VendorCode = VENDOR_DEFAULT; #else VendorCode = VENDOR_BERKELEY; #endif } /* ** INIT_VENDOR_MACROS -- vendor-dependent macro initializations ** ** Called once, on startup. ** ** Parameters: ** e -- the global envelope. ** ** Returns: ** none. ** ** Side Effects: ** vendor-dependent. */ void init_vendor_macros(e) register ENVELOPE *e; { } /* ** GETLA -- get the current load average ** ** This code stolen from la.c. ** ** Parameters: ** none. ** ** Returns: ** The current load average as an integer. ** ** Side Effects: ** none. */ /* try to guess what style of load average we have */ #define LA_ZERO 1 /* always return load average as zero */ #define LA_INT 2 /* read kmem for avenrun; interpret as long */ #define LA_FLOAT 3 /* read kmem for avenrun; interpret as float */ #define LA_SUBR 4 /* call getloadavg */ #define LA_MACH 5 /* MACH load averages (as on NeXT boxes) */ #define LA_SHORT 6 /* read kmem for avenrun; interpret as short */ #define LA_PROCSTR 7 /* read string ("1.17") from /proc/loadavg */ #define LA_READKSYM 8 /* SVR4: use MIOC_READKSYM ioctl call */ #define LA_DGUX 9 /* special DGUX implementation */ #define LA_HPUX 10 /* special HPUX implementation */ #define LA_IRIX6 11 /* special IRIX 6.2 implementation */ +#define LA_KSTAT 12 /* special Solaris kstat(3k) implementation */ +#define LA_DEVSHORT 13 /* read short from a device */ /* do guesses based on general OS type */ #ifndef LA_TYPE # define LA_TYPE LA_ZERO #endif #ifndef FSHIFT # if defined(unixpc) # define FSHIFT 5 # endif # if defined(__alpha) || defined(IRIX) # define FSHIFT 10 # endif #endif #ifndef FSHIFT # define FSHIFT 8 #endif #ifndef FSCALE # define FSCALE (1 << FSHIFT) #endif #ifndef LA_AVENRUN # ifdef SYSTEM5 # define LA_AVENRUN "avenrun" # else # define LA_AVENRUN "_avenrun" # endif #endif /* _PATH_KMEM should be defined in */ #ifndef _PATH_KMEM # define _PATH_KMEM "/dev/kmem" #endif #if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT) #include #ifdef IRIX64 # define nlist nlist64 #endif /* _PATH_UNIX should be defined in */ #ifndef _PATH_UNIX # if defined(SYSTEM5) # define _PATH_UNIX "/unix" # else # define _PATH_UNIX "/vmunix" # endif #endif #ifdef _AUX_SOURCE struct nlist Nl[2]; #else struct nlist Nl[] = { { LA_AVENRUN }, { 0 }, }; #endif #define X_AVENRUN 0 int getla() { static int kmem = -1; #if LA_TYPE == LA_INT long avenrun[3]; #else # if LA_TYPE == LA_SHORT short avenrun[3]; # else double avenrun[3]; # endif #endif extern int errno; extern off_t lseek(); if (kmem < 0) { #ifdef _AUX_SOURCE strcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN); Nl[1].n_name[0] = '\0'; #endif #if defined(_AIX3) || defined(_AIX4) if (knlist(Nl, 1, sizeof Nl[0]) < 0) #else if (nlist(_PATH_UNIX, Nl) < 0) #endif { if (tTd(3, 1)) printf("getla: nlist(%s): %s\n", _PATH_UNIX, errstring(errno)); return (-1); } if (Nl[X_AVENRUN].n_value == 0) { if (tTd(3, 1)) printf("getla: nlist(%s, %s) ==> 0\n", _PATH_UNIX, LA_AVENRUN); return (-1); } #ifdef NAMELISTMASK Nl[X_AVENRUN].n_value &= NAMELISTMASK; #endif kmem = open(_PATH_KMEM, 0, 0); if (kmem < 0) { if (tTd(3, 1)) printf("getla: open(/dev/kmem): %s\n", errstring(errno)); return (-1); } (void) fcntl(kmem, F_SETFD, 1); } if (tTd(3, 20)) printf("getla: symbol address = %#x\n", Nl[X_AVENRUN].n_value); if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 || read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) { /* thank you Ian */ if (tTd(3, 1)) printf("getla: lseek or read: %s\n", errstring(errno)); return (-1); } # if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) if (tTd(3, 5)) { # if LA_TYPE == LA_SHORT printf("getla: avenrun = %d", avenrun[0]); if (tTd(3, 15)) printf(", %d, %d", avenrun[1], avenrun[2]); # else printf("getla: avenrun = %ld", avenrun[0]); if (tTd(3, 15)) printf(", %ld, %ld", avenrun[1], avenrun[2]); # endif printf("\n"); } if (tTd(3, 1)) printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); # else /* LA_TYPE == LA_FLOAT */ if (tTd(3, 5)) { printf("getla: avenrun = %g", avenrun[0]); if (tTd(3, 15)) printf(", %g, %g", avenrun[1], avenrun[2]); printf("\n"); } if (tTd(3, 1)) printf("getla: %d\n", (int) (avenrun[0] +0.5)); return ((int) (avenrun[0] + 0.5)); # endif } #endif /* LA_TYPE == LA_INT or LA_SHORT or LA_FLOAT */ #if LA_TYPE == LA_READKSYM # include getla() { static int kmem = -1; long avenrun[3]; extern int errno; struct mioc_rksym mirk; if (kmem < 0) { kmem = open("/dev/kmem", 0, 0); if (kmem < 0) { if (tTd(3, 1)) printf("getla: open(/dev/kmem): %s\n", errstring(errno)); return (-1); } (void) fcntl(kmem, F_SETFD, 1); } mirk.mirk_symname = LA_AVENRUN; mirk.mirk_buf = avenrun; mirk.mirk_buflen = sizeof(avenrun); if (ioctl(kmem, MIOC_READKSYM, &mirk) < 0) { if (tTd(3, 1)) printf("getla: ioctl(MIOC_READKSYM) failed: %s\n", errstring(errno)); return -1; } if (tTd(3, 5)) { printf("getla: avenrun = %d", avenrun[0]); if (tTd(3, 15)) printf(", %d, %d", avenrun[1], avenrun[2]); printf("\n"); } if (tTd(3, 1)) printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); } #endif /* LA_TYPE == LA_READKSYM */ #if LA_TYPE == LA_DGUX # include int getla() { struct dg_sys_info_load_info load_info; dg_sys_info((long *)&load_info, DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0); if (tTd(3, 1)) printf("getla: %d\n", (int) (load_info.one_minute + 0.5)); return((int) (load_info.one_minute + 0.5)); } #endif /* LA_TYPE == LA_DGUX */ #if LA_TYPE == LA_HPUX /* forward declarations to keep gcc from complaining */ struct pst_dynamic; struct pst_status; struct pst_static; struct pst_vminfo; struct pst_diskinfo; struct pst_processor; struct pst_lv; struct pst_swapinfo; # include # include int getla() { struct pst_dynamic pstd; if (pstat_getdynamic(&pstd, sizeof(struct pst_dynamic), (size_t) 1, 0) == -1) return 0; if (tTd(3, 1)) printf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5)); return (int) (pstd.psd_avg_1_min + 0.5); } #endif /* LA_TYPE == LA_HPUX */ #if LA_TYPE == LA_SUBR int getla() { double avenrun[3]; if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0) { if (tTd(3, 1)) perror("getla: getloadavg failed:"); return (-1); } if (tTd(3, 1)) printf("getla: %d\n", (int) (avenrun[0] +0.5)); return ((int) (avenrun[0] + 0.5)); } #endif /* LA_TYPE == LA_SUBR */ #if LA_TYPE == LA_MACH /* ** This has been tested on NEXTSTEP release 2.1/3.X. */ #if defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0 # include #else # include #endif int getla() { processor_set_t default_set; kern_return_t error; unsigned int info_count; struct processor_set_basic_info info; host_t host; error = processor_set_default(host_self(), &default_set); if (error != KERN_SUCCESS) { if (tTd(3, 1)) perror("getla: processor_set_default failed:"); return -1; } info_count = PROCESSOR_SET_BASIC_INFO_COUNT; if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO, &host, (processor_set_info_t)&info, &info_count) != KERN_SUCCESS) { if (tTd(3, 1)) perror("getla: processor_set_info failed:"); return -1; } if (tTd(3, 1)) printf("getla: %d\n", (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE); return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE; } #endif /* LA_TYPE == LA_MACH */ #if LA_TYPE == LA_PROCSTR /* ** Read /proc/loadavg for the load average. This is assumed to be ** in a format like "0.15 0.12 0.06". ** ** Initially intended for Linux. This has been in the kernel ** since at least 0.99.15. */ # ifndef _PATH_LOADAVG # define _PATH_LOADAVG "/proc/loadavg" # endif int getla() { double avenrun; register int result; FILE *fp; fp = fopen(_PATH_LOADAVG, "r"); if (fp == NULL) { if (tTd(3, 1)) printf("getla: fopen(%s): %s\n", _PATH_LOADAVG, errstring(errno)); return -1; } result = fscanf(fp, "%lf", &avenrun); fclose(fp); if (result != 1) { if (tTd(3, 1)) printf("getla: fscanf() = %d: %s\n", result, errstring(errno)); return -1; } if (tTd(3, 1)) printf("getla(): %.2f\n", avenrun); return ((int) (avenrun + 0.5)); } #endif /* LA_TYPE == LA_PROCSTR */ #if LA_TYPE == LA_IRIX6 #include #include #include #define X_AVENRUN 0 struct nlist Nl32[] = { { LA_AVENRUN }, { 0 }, }; struct nlist64 Nl64[] = { { LA_AVENRUN }, { 0 }, }; int getla(void) { static int kmem = -1; static enum { getla_none, getla_32, getla_64 } kernel_type = getla_none; uint32_t avenrun32[3]; uint64_t avenrun64[3]; if (kernel_type == getla_none) { /* Try 32 bit kernel ... */ errno = 0; if (nlist(_PATH_UNIX, Nl32) == 0) { if (tTd(3, 20)) printf("getla: Kernel is 32bit\n"); if (Nl32[X_AVENRUN].n_value == 0) { if (tTd(3, 1)) printf("getla: nlist(%s, %s) ==> 0\n", _PATH_UNIX, LA_AVENRUN); } else kernel_type = getla_32; } else if (errno != 0) { if (tTd(3, 1)) printf("getla: nlist(%s): %s\n", _PATH_UNIX, errstring(errno)); } else { if (tTd(3, 20)) printf("getla: Kernel is not 32bit\n"); } /* Try 64 bit kernel ... */ errno = 0; if (nlist64(_PATH_UNIX, Nl64) == 0) { if (tTd(3, 20)) printf("getla: Kernel is 64bit\n"); if (Nl64[X_AVENRUN].n_value == 0) { if (tTd(3, 1)) printf("getla: nlist(%s, %s) ==> 0\n", _PATH_UNIX, LA_AVENRUN); } else kernel_type = getla_64; } else if (errno != 0) { if (tTd(3, 1)) printf("getla: nlist64(%s): %s\n", _PATH_UNIX, errstring(errno)); } else { if (tTd(3, 20)) printf("getla: Kernel is not 64bit\n"); } } if (kernel_type == getla_none) { if (tTd(3, 1)) printf("getla: Failed to determine kernel type\n"); return -1; } if (kmem < 0) { kmem = open(_PATH_KMEM, 0, 0); if (kmem < 0) { if (tTd(3, 1)) printf("getla: open(/dev/kmem): %s\n", errstring(errno)); return -1; } (void) fcntl(kmem, F_SETFD, 1); } switch (kernel_type) { case getla_32: if (lseek(kmem, (off_t) Nl32[X_AVENRUN].n_value, SEEK_SET) == -1 || read(kmem, (char *) avenrun32, sizeof(avenrun32)) < sizeof(avenrun32)) { if (tTd(3, 1)) printf("getla: lseek or read: %s\n", errstring(errno)); return -1; } if (tTd(3, 5)) { printf("getla: avenrun{32} = %ld", (long int) avenrun32[0]); if (tTd(3, 15)) printf(", %ld, %ld", (long int)avenrun32[1], (long int)avenrun32[2]); printf("\n"); } if (tTd(3, 1)) printf("getla: %d\n", (int) (avenrun32[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun32[0] + FSCALE/2) >> FSHIFT); case getla_64: /* Using of lseek64 is perhaps overkill ... */ if (lseek64(kmem, (off64_t) Nl64[X_AVENRUN].n_value, SEEK_SET) == -1 || read(kmem, (char *) avenrun64, sizeof(avenrun64)) < sizeof(avenrun64)) { if (tTd(3, 1)) printf("getla: lseek64 or read: %s\n", errstring(errno)); return -1; } if (tTd(3, 5)) { printf("getla: avenrun{64} = %lld", (long long int) avenrun64[0]); if (tTd(3, 15)) printf(", %lld, %lld", (long long int) avenrun64[1], (long long int) avenrun64[2]); printf("\n"); } if (tTd(3, 1)) printf("getla: %d\n", (int) (avenrun64[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun64[0] + FSCALE/2) >> FSHIFT); } return -1; } #endif +#if LA_TYPE == LA_KSTAT + +#include + +int +getla() +{ + kstat_ctl_t *kc; + kstat_t *ksp; + kstat_named_t *ksn; + int la; + + kc = kstat_open(); + if (kc == NULL) + { + if (tTd(3, 1)) + printf("getla: kstat_open(): %s\n", + errstring(errno)); + return -1; + } + ksp = kstat_lookup(kc, "unix", 0, "system_misc"); /* NULL on error */ + if (ksp == NULL) + { + if (tTd(3, 1)) + printf("getla: kstat_lookup(): %s\n", + errstring(errno); + return -1; + } + if (kstat_read(kc, ksp, NULL) < 0) + { + if (tTd(3, 1)) + printf("getla: kstat_read(): %s\n", + errstring(errno); + return -1; + } + ksn = (kstat_named_t *) kstat_data_lookup(ksp, "avenrun_1min"); + la = (ksn->value.ul + FSCALE/2) >> FSHIFT; + kstat_close(kc); + return la; +} + +#endif /* LA_TYPE == LA_KSTAT */ + +#if LA_TYPE == LA_DEVSHORT + +/* +** Read /dev/table/avenrun for the load average. This should contain +** three shorts for the 1, 5, and 15 minute loads. We only read the +** first, since that's all we care about. +** +** Intended for SCO OpenServer 5. +*/ + +# ifndef _PATH_AVENRUN +# define _PATH_AVENRUN "/dev/table/avenrun" +# endif + +int +getla() +{ + static int afd = -1; + short avenrun; + int loadav; + int r; + + errno = EBADF; + + if (afd == -1 || lseek(afd, 0L, SEEK_SET) == -1) + { + if (errno != EBADF) + return -1; + afd = open(_PATH_AVENRUN, O_RDONLY|O_SYNC); + if (afd < 0) + { + syslog(LOG_ERR, "can't open %s: %m", _PATH_AVENRUN); + return -1; + } + } + + r = read(afd, &avenrun, sizeof avenrun); + + if (tTd(3, 5)) + printf("getla: avenrun = %d\n", avenrun); + loadav = (int) (avenrun + FSCALE/2) >> FSHIFT; + if (tTd(3, 1)) + printf("getla: %d\n", loadav); + return loadav; +} + +#endif /* LA_TYPE == LA_DEVSHORT */ + #if LA_TYPE == LA_ZERO +int getla() { if (tTd(3, 1)) printf("getla: ZERO\n"); return (0); } #endif /* LA_TYPE == LA_ZERO */ /* * Copyright 1989 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: Many and varied... */ /* Non Apollo stuff removed by Don Lewis 11/15/93 */ #ifdef apollo # undef volatile # include /* ARGSUSED */ int getloadavg( call_data ) caddr_t call_data; /* pointer to (double) return value */ { double *avenrun = (double *) call_data; int i; status_$t st; long loadav[3]; proc1_$get_loadav(loadav, &st); *avenrun = loadav[0] / (double) (1 << 16); return(0); } # endif /* apollo */ /* ** SHOULDQUEUE -- should this message be queued or sent? ** ** Compares the message cost to the load average to decide. ** ** Parameters: ** pri -- the priority of the message in question. ** ctime -- the message creation time. ** ** Returns: ** TRUE -- if this message should be queued up for the ** time being. ** FALSE -- if the load is low enough to send this message. ** ** Side Effects: ** none. */ bool shouldqueue(pri, ctime) long pri; time_t ctime; { bool rval; if (tTd(3, 30)) printf("shouldqueue: CurrentLA=%d, pri=%ld: ", CurrentLA, pri); if (CurrentLA < QueueLA) { if (tTd(3, 30)) printf("FALSE (CurrentLA < QueueLA)\n"); return (FALSE); } #if 0 /* this code is reported to cause oscillation around RefuseLA */ if (CurrentLA >= RefuseLA && QueueLA < RefuseLA) { if (tTd(3, 30)) printf("TRUE (CurrentLA >= RefuseLA)\n"); return (TRUE); } #endif rval = pri > (QueueFactor / (CurrentLA - QueueLA + 1)); if (tTd(3, 30)) printf("%s (by calculation)\n", rval ? "TRUE" : "FALSE"); return rval; } /* ** REFUSECONNECTIONS -- decide if connections should be refused ** ** Parameters: ** port -- port number (for error messages only) ** ** Returns: ** TRUE if incoming SMTP connections should be refused ** (for now). ** FALSE if we should accept new work. ** ** Side Effects: ** Sets process title when it is rejecting connections. */ bool refuseconnections(port) int port; { time_t now; static time_t lastconn = (time_t) 0; static int conncnt = 0; extern bool enoughdiskspace(); extern void setproctitle __P((const char *, ...)); #ifdef XLA if (!xla_smtp_ok()) return TRUE; #endif now = curtime(); if (now != lastconn) { lastconn = now; conncnt = 0; } else if (conncnt++ > ConnRateThrottle && ConnRateThrottle > 0) { /* sleep to flatten out connection load */ setproctitle("deferring connections on port %d: %d per second", port, ConnRateThrottle); #ifdef LOG if (LogLevel >= 14) syslog(LOG_INFO, "deferring connections on port %d: %d per second", port, ConnRateThrottle); #endif sleep(1); } CurrentLA = getla(); if (CurrentLA >= RefuseLA) { setproctitle("rejecting connections on port %d: load average: %d", port, CurrentLA); #ifdef LOG if (LogLevel >= 14) syslog(LOG_INFO, "rejecting connections on port %d: load average: %d", port, CurrentLA); #endif + return TRUE; } - else if (!enoughdiskspace(MinBlocksFree + 1)) + + if (!enoughdiskspace(MinBlocksFree + 1)) { setproctitle("rejecting connections on port %d: min free: %d", port, MinBlocksFree); #ifdef LOG if (LogLevel >= 14) syslog(LOG_INFO, "rejecting connections on port %d: min free: %d", port, MinBlocksFree); #endif + return TRUE; } - else if (MaxChildren > 0 && CurChildren >= MaxChildren) + + if (MaxChildren > 0 && CurChildren >= MaxChildren) { - setproctitle("rejecting connections on port %d: %d children, max %d", - port, CurChildren, MaxChildren); -#ifdef LOG - if (LogLevel >= 14) - syslog(LOG_INFO, "rejecting connections on port %d: %d children, max %d", + proc_list_probe(); + if (CurChildren >= MaxChildren) + { + setproctitle("rejecting connections on port %d: %d children, max %d", port, CurChildren, MaxChildren); +#ifdef LOG + if (LogLevel >= 14) + syslog(LOG_INFO, "rejecting connections on port %d: %d children, max %d", + port, CurChildren, MaxChildren); #endif + return TRUE; + } } - else - return FALSE; - return TRUE; + + return FALSE; } /* ** SETPROCTITLE -- set process title for ps ** ** Parameters: ** fmt -- a printf style format string. ** a, b, c -- possible parameters to fmt. ** ** Returns: ** none. ** ** Side Effects: ** Clobbers argv of our main procedure so ps(1) will ** display the title. */ #define SPT_NONE 0 /* don't use it at all */ #define SPT_REUSEARGV 1 /* cover argv with title information */ #define SPT_BUILTIN 2 /* use libc builtin */ #define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */ #define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */ #define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */ #define SPT_SCO 6 /* write kernel u. area */ #ifndef SPT_TYPE # define SPT_TYPE SPT_REUSEARGV #endif #if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN # if SPT_TYPE == SPT_PSTAT # include # endif # if SPT_TYPE == SPT_PSSTRINGS # include # include # ifndef PS_STRINGS /* hmmmm.... apparently not available after all */ # undef SPT_TYPE # define SPT_TYPE SPT_REUSEARGV # else # ifndef NKPDE /* FreeBSD 2.0 */ # define NKPDE 63 typedef unsigned int *pt_entry_t; # endif # endif # endif # if SPT_TYPE == SPT_PSSTRINGS # define SETPROC_STATIC static # else # define SETPROC_STATIC # endif # if SPT_TYPE == SPT_SYSMIPS # include # include # endif # if SPT_TYPE == SPT_SCO # include # include # include # include # if PSARGSZ > MAXLINE # define SPT_BUFSIZE PSARGSZ # endif # endif # ifndef SPT_PADCHAR # define SPT_PADCHAR ' ' # endif # ifndef SPT_BUFSIZE # define SPT_BUFSIZE MAXLINE # endif #endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */ /* ** Pointers for setproctitle. ** This allows "ps" listings to give more useful information. */ char **Argv = NULL; /* pointer to argument vector */ char *LastArgv = NULL; /* end of argv */ void initsetproctitle(argc, argv, envp) int argc; char **argv; char **envp; { register int i; extern char **environ; /* ** Move the environment so setproctitle can use the space at ** the top of memory. */ for (i = 0; envp[i] != NULL; i++) continue; environ = (char **) xalloc(sizeof (char *) * (i + 1)); for (i = 0; envp[i] != NULL; i++) environ[i] = newstr(envp[i]); environ[i] = NULL; /* ** Save start and extent of argv for setproctitle. */ Argv = argv; if (i > 0) LastArgv = envp[i - 1] + strlen(envp[i - 1]); else LastArgv = argv[argc - 1] + strlen(argv[argc - 1]); } #if SPT_TYPE != SPT_BUILTIN /*VARARGS1*/ void # ifdef __STDC__ setproctitle(const char *fmt, ...) # else setproctitle(fmt, va_alist) const char *fmt; va_dcl # endif { # if SPT_TYPE != SPT_NONE register char *p; register int i; SETPROC_STATIC char buf[SPT_BUFSIZE]; VA_LOCAL_DECL # if SPT_TYPE == SPT_PSTAT union pstun pst; # endif # if SPT_TYPE == SPT_SCO off_t seek_off; static int kmem = -1; static int kmempid = -1; struct user u; # endif p = buf; /* print sendmail: heading for grep */ (void) strcpy(p, "sendmail: "); p += strlen(p); /* print the argument string */ VA_START(fmt); (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap); VA_END; i = strlen(buf); # if SPT_TYPE == SPT_PSTAT pst.pst_command = buf; pstat(PSTAT_SETCMD, pst, i, 0, 0); # endif # if SPT_TYPE == SPT_PSSTRINGS PS_STRINGS->ps_nargvstr = 1; PS_STRINGS->ps_argvstr = buf; # endif # if SPT_TYPE == SPT_SYSMIPS sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf); # endif # if SPT_TYPE == SPT_SCO if (kmem < 0 || kmempid != getpid()) { if (kmem >= 0) close(kmem); kmem = open(_PATH_KMEM, O_RDWR, 0); if (kmem < 0) return; (void) fcntl(kmem, F_SETFD, 1); kmempid = getpid(); } buf[PSARGSZ - 1] = '\0'; seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u; if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off) (void) write(kmem, buf, PSARGSZ); # endif # if SPT_TYPE == SPT_REUSEARGV if (i > LastArgv - Argv[0] - 2) { i = LastArgv - Argv[0] - 2; buf[i] = '\0'; } (void) strcpy(Argv[0], buf); p = &Argv[0][i]; while (p < LastArgv) *p++ = SPT_PADCHAR; Argv[1] = NULL; # endif # endif /* SPT_TYPE != SPT_NONE */ } #endif /* SPT_TYPE != SPT_BUILTIN */ /* ** REAPCHILD -- pick up the body of my child, lest it become a zombie ** ** Parameters: ** sig -- the signal that got us here (unused). ** ** Returns: ** none. ** ** Side Effects: ** Picks up extant zombies. */ void reapchild(sig) int sig; { int olderrno = errno; pid_t pid; # ifdef HASWAITPID auto int status; int count; count = 0; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (count++ > 1000) { #ifdef LOG if (LogLevel > 0) syslog(LOG_ALERT, "reapchild: waitpid loop: pid=%d, status=%x", pid, status); #endif break; } proc_list_drop(pid); } # else # ifdef WNOHANG union wait status; while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0) proc_list_drop(pid); # else /* WNOHANG */ auto int status; while ((pid = wait(&status)) > 0) proc_list_drop(pid); # endif /* WNOHANG */ # endif # ifdef SYS5SIGNALS (void) setsignal(SIGCHLD, reapchild); # endif errno = olderrno; } /* ** PUTENV -- emulation of putenv() in terms of setenv() ** ** Not needed on Posix-compliant systems. ** This doesn't have full Posix semantics, but it's good enough ** for sendmail. ** ** Parameter: ** env -- the environment to put. ** ** Returns: ** none. */ #ifdef NEEDPUTENV # if NEEDPUTENV == 2 /* no setenv(3) call available */ int putenv(str) char *str; { char **current; int matchlen, envlen=0; char *tmp; char **newenv; static int first=1; extern char **environ; /* * find out how much of str to match when searching * for a string to replace. */ if ((tmp = strchr(str, '=')) == NULL || tmp == str) matchlen = strlen(str); else matchlen = (int) (tmp - str); ++matchlen; /* * Search for an existing string in the environment and find the * length of environ. If found, replace and exit. */ for (current=environ; *current; current++) { ++envlen; if (strncmp(str, *current, matchlen) == 0) { /* found it, now insert the new version */ *current = (char *)str; return(0); } } /* * There wasn't already a slot so add space for a new slot. * If this is our first time through, use malloc(), else realloc(). */ if (first) { newenv = (char **) malloc(sizeof(char *) * (envlen + 2)); if (newenv == NULL) return(-1); first=0; (void) memcpy(newenv, environ, sizeof(char *) * envlen); } else { newenv = (char **) realloc((char *)environ, sizeof(char *) * (envlen + 2)); if (newenv == NULL) return(-1); } /* actually add in the new entry */ environ = newenv; environ[envlen] = (char *)str; environ[envlen+1] = NULL; return(0); } #else /* implement putenv() in terms of setenv() */ int putenv(env) char *env; { char *p; int l; char nbuf[100]; p = strchr(env, '='); if (p == NULL) return 0; l = p - env; if (l > sizeof nbuf - 1) l = sizeof nbuf - 1; bcopy(env, nbuf, l); nbuf[l] = '\0'; return setenv(nbuf, ++p, 1); } # endif #endif /* ** UNSETENV -- remove a variable from the environment ** ** Not needed on newer systems. ** ** Parameters: ** name -- the string name of the environment variable to be ** deleted from the current environment. ** ** Returns: ** none. ** ** Globals: ** environ -- a pointer to the current environment. ** ** Side Effects: ** Modifies environ. */ #ifndef HASUNSETENV void unsetenv(name) char *name; { extern char **environ; register char **pp; int len = strlen(name); for (pp = environ; *pp != NULL; pp++) { if (strncmp(name, *pp, len) == 0 && ((*pp)[len] == '=' || (*pp)[len] == '\0')) break; } for (; *pp != NULL; pp++) *pp = pp[1]; } #endif /* ** GETDTABLESIZE -- return number of file descriptors ** ** Only on non-BSD systems ** ** Parameters: ** none ** ** Returns: ** size of file descriptor table ** ** Side Effects: ** none */ #ifdef SOLARIS # include #endif int getdtsize() { #ifdef RLIMIT_NOFILE struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) return rl.rlim_cur; #endif # ifdef HASGETDTABLESIZE return getdtablesize(); # else # ifdef _SC_OPEN_MAX return sysconf(_SC_OPEN_MAX); # else return NOFILE; # endif # endif } /* ** UNAME -- get the UUCP name of this system. */ #ifndef HASUNAME int uname(name) struct utsname *name; { FILE *file; char *n; name->nodename[0] = '\0'; /* try /etc/whoami -- one line with the node name */ if ((file = fopen("/etc/whoami", "r")) != NULL) { (void) fgets(name->nodename, NODE_LENGTH + 1, file); (void) fclose(file); n = strchr(name->nodename, '\n'); if (n != NULL) *n = '\0'; if (name->nodename[0] != '\0') return (0); } /* try /usr/include/whoami.h -- has a #define somewhere */ if ((file = fopen("/usr/include/whoami.h", "r")) != NULL) { char buf[MAXLINE]; while (fgets(buf, MAXLINE, file) != NULL) if (sscanf(buf, "#define sysname \"%*[^\"]\"", NODE_LENGTH, name->nodename) > 0) break; (void) fclose(file); if (name->nodename[0] != '\0') return (0); } #ifdef TRUST_POPEN /* ** Popen is known to have security holes. */ /* try uuname -l to return local name */ if ((file = popen("uuname -l", "r")) != NULL) { (void) fgets(name, NODE_LENGTH + 1, file); (void) pclose(file); n = strchr(name, '\n'); if (n != NULL) *n = '\0'; if (name->nodename[0] != '\0') return (0); } #endif return (-1); } #endif /* HASUNAME */ /* ** INITGROUPS -- initialize groups ** ** Stub implementation for System V style systems */ #ifndef HASINITGROUPS initgroups(name, basegid) char *name; int basegid; { return 0; } #endif /* ** SETSID -- set session id (for non-POSIX systems) */ #ifndef HASSETSID pid_t setsid __P ((void)) { #ifdef TIOCNOTTY int fd; fd = open("/dev/tty", O_RDWR, 0); if (fd >= 0) { (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0); (void) close(fd); } #endif /* TIOCNOTTY */ # ifdef SYS5SETPGRP return setpgrp(); # else return setpgid(0, getpid()); # endif } #endif /* ** FSYNC -- dummy fsync */ #ifdef NEEDFSYNC fsync(fd) int fd; { # ifdef O_SYNC return fcntl(fd, F_SETFL, O_SYNC); # else /* nothing we can do */ return 0; # endif } #endif /* ** DGUX_INET_ADDR -- inet_addr for DG/UX ** ** Data General DG/UX version of inet_addr returns a struct in_addr ** instead of a long. This patches things. Only needed on versions ** prior to 5.4.3. */ #ifdef DGUX_5_4_2 #undef inet_addr long dgux_inet_addr(host) char *host; { struct in_addr haddr; haddr = inet_addr(host); return haddr.s_addr; } #endif /* ** GETOPT -- for old systems or systems with bogus implementations */ #ifdef NEEDGETOPT /* * Copyright (c) 1985 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* ** this version hacked to add `atend' flag to allow state machine ** to reset if invoked by the program to scan args for a 2nd time */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getopt.c 4.3 (Berkeley) 3/9/86"; #endif /* LIBC_SCCS and not lint */ #include /* * get option letter from argument vector */ #ifdef _CONVEX_SOURCE extern int optind, opterr, optopt; extern char *optarg; #else int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = 0; /* character checked for validity */ char *optarg = NULL; /* argument associated with option */ #endif #define BADCH (int)'?' #define EMSG "" #define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \ fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);} getopt(nargc,nargv,ostr) int nargc; char *const *nargv; const char *ostr; { static char *place = EMSG; /* option letter processing */ static char atend = 0; register char *oli; /* option letter list index */ if (atend) { atend = 0; place = EMSG; } if(!*place) { /* update scanning pointer */ if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) { atend++; return(EOF); } if (*place == '-') { /* found "--" */ ++optind; atend++; return(EOF); } } /* option letter okay? */ if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) { if (!*place) ++optind; tell(": illegal option -- "); } if (oli && *++oli != ':') { /* don't need argument */ optarg = NULL; if (!*place) ++optind; } else { /* need an argument */ if (*place) optarg = place; /* no white space */ else if (nargc <= ++optind) { /* no arg */ place = EMSG; tell(": option requires an argument -- "); } else optarg = nargv[optind]; /* white space */ place = EMSG; ++optind; } return(optopt); /* dump back option letter */ } #endif /* ** VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version */ #ifdef NEEDVPRINTF #define MAXARG 16 vfprintf(fp, fmt, ap) FILE *fp; char *fmt; char **ap; { char *bp[MAXARG]; int i = 0; while (*ap && i < MAXARG) bp[i++] = *ap++; fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7], bp[8], bp[9], bp[10], bp[11], bp[12], bp[13], bp[14], bp[15]); } vsprintf(s, fmt, ap) char *s; char *fmt; char **ap; { char *bp[MAXARG]; int i = 0; while (*ap && i < MAXARG) bp[i++] = *ap++; sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7], bp[8], bp[9], bp[10], bp[11], bp[12], bp[13], bp[14], bp[15]); } #endif /* ** SNPRINTF, VSNPRINT -- counted versions of printf ** ** These versions have been grabbed off the net. They have been ** cleaned up to compile properly and support for .precision and ** %lx has been added. */ #if !HASSNPRINTF /************************************************************** * Original: * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 * A bombproof version of doprnt (dopr) included. * Sigh. This sort of thing is always nasty do deal with. Note that * the version here does not include floating point... * * snprintf() is used instead of sprintf() as it does limit checks * for string length. This covers a nasty loophole. * * The other functions are there to prevent NULL pointers from * causing nast effects. **************************************************************/ static void dopr(); static char *end; static int SnprfOverflow; /* VARARGS3 */ int # ifdef __STDC__ snprintf(char *str, size_t count, const char *fmt, ...) # else snprintf(str, count, fmt, va_alist) char *str; size_t count; const char *fmt; va_dcl #endif { int len; VA_LOCAL_DECL VA_START(fmt); len = vsnprintf(str, count, fmt, ap); VA_END; return len; } # ifndef luna2 int vsnprintf(str, count, fmt, args) char *str; size_t count; const char *fmt; va_list args; { str[0] = 0; end = str + count - 1; SnprfOverflow = 0; dopr( str, fmt, args ); if (count > 0) end[0] = 0; if (SnprfOverflow && tTd(57, 2)) printf("\nvsnprintf overflow, len = %d, str = %s", count, shortenstring(str, 203)); return strlen(str); } /* * dopr(): poor man's version of doprintf */ static void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth)); static void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad)); static void dostr __P(( char * , int )); static char *output; static void dopr_outch __P(( int c )); static void dopr( buffer, format, args ) char *buffer; char *format; va_list args; { int ch; long value; int longflag = 0; int pointflag = 0; int maxwidth = 0; char *strvalue; int ljust; int len; int zpad; output = buffer; while( (ch = *format++) ){ switch( ch ){ case '%': ljust = len = zpad = maxwidth = 0; longflag = pointflag = 0; nextch: ch = *format++; switch( ch ){ case 0: dostr( "**end of format**" , 0); return; case '-': ljust = 1; goto nextch; case '0': /* set zero padding if len not set */ if(len==0 && !pointflag) zpad = '0'; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (pointflag) maxwidth = maxwidth*10 + ch - '0'; else len = len*10 + ch - '0'; goto nextch; case '*': if (pointflag) maxwidth = va_arg( args, int ); else len = va_arg( args, int ); goto nextch; case '.': pointflag = 1; goto nextch; case 'l': longflag = 1; goto nextch; case 'u': case 'U': /*fmtnum(value,base,dosign,ljust,len,zpad) */ if( longflag ){ value = va_arg( args, long ); } else { value = va_arg( args, int ); } fmtnum( value, 10,0, ljust, len, zpad ); break; case 'o': case 'O': /*fmtnum(value,base,dosign,ljust,len,zpad) */ if( longflag ){ value = va_arg( args, long ); } else { value = va_arg( args, int ); } fmtnum( value, 8,0, ljust, len, zpad ); break; case 'd': case 'D': if( longflag ){ value = va_arg( args, long ); } else { value = va_arg( args, int ); } fmtnum( value, 10,1, ljust, len, zpad ); break; case 'x': if( longflag ){ value = va_arg( args, long ); } else { value = va_arg( args, int ); } fmtnum( value, 16,0, ljust, len, zpad ); break; case 'X': if( longflag ){ value = va_arg( args, long ); } else { value = va_arg( args, int ); } fmtnum( value,-16,0, ljust, len, zpad ); break; case 's': strvalue = va_arg( args, char *); if (maxwidth > 0 || !pointflag) { if (pointflag && len > maxwidth) len = maxwidth; /* Adjust padding */ fmtstr( strvalue,ljust,len,zpad, maxwidth); } break; case 'c': ch = va_arg( args, int ); dopr_outch( ch ); break; case '%': dopr_outch( ch ); continue; default: dostr( "???????" , 0); } break; default: dopr_outch( ch ); break; } } *output = 0; } static void fmtstr( value, ljust, len, zpad, maxwidth ) char *value; int ljust, len, zpad, maxwidth; { int padlen, strlen; /* amount to pad */ if( value == 0 ){ value = ""; } for( strlen = 0; value[strlen]; ++ strlen ); /* strlen */ if (strlen > maxwidth && maxwidth) strlen = maxwidth; padlen = len - strlen; if( padlen < 0 ) padlen = 0; if( ljust ) padlen = -padlen; while( padlen > 0 ) { dopr_outch( ' ' ); --padlen; } dostr( value, maxwidth ); while( padlen < 0 ) { dopr_outch( ' ' ); ++padlen; } } static void fmtnum( value, base, dosign, ljust, len, zpad ) long value; int base, dosign, ljust, len, zpad; { int signvalue = 0; unsigned long uvalue; char convert[20]; int place = 0; int padlen = 0; /* amount to pad */ int caps = 0; /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n", value, base, dosign, ljust, len, zpad )); */ uvalue = value; if( dosign ){ if( value < 0 ) { signvalue = '-'; uvalue = -value; } } if( base < 0 ){ caps = 1; base = -base; } do{ convert[place++] = (caps? "0123456789ABCDEF":"0123456789abcdef") [uvalue % (unsigned)base ]; uvalue = (uvalue / (unsigned)base ); }while(uvalue); convert[place] = 0; padlen = len - place; if( padlen < 0 ) padlen = 0; if( ljust ) padlen = -padlen; /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n", convert,place,signvalue,padlen)); */ if( zpad && padlen > 0 ){ if( signvalue ){ dopr_outch( signvalue ); --padlen; signvalue = 0; } while( padlen > 0 ){ dopr_outch( zpad ); --padlen; } } while( padlen > 0 ) { dopr_outch( ' ' ); --padlen; } if( signvalue ) dopr_outch( signvalue ); while( place > 0 ) dopr_outch( convert[--place] ); while( padlen < 0 ){ dopr_outch( ' ' ); ++padlen; } } static void dostr( str , cut) char *str; int cut; { if (cut) { while(*str && cut-- > 0) dopr_outch(*str++); } else { while(*str) dopr_outch(*str++); } } static void dopr_outch( c ) int c; { #if 0 if( iscntrl(c) && c != '\n' && c != '\t' ){ c = '@' + (c & 0x1F); if( end == 0 || output < end ) *output++ = '^'; } #endif if( end == 0 || output < end ) *output++ = c; else SnprfOverflow++; } # endif /* !luna2 */ #endif /* !HASSNPRINTF */ /* ** USERSHELLOK -- tell if a user's shell is ok for unrestricted use ** ** Parameters: ** user -- the name of the user we are checking. ** shell -- the user's shell from /etc/passwd ** ** Returns: ** TRUE -- if it is ok to use this for unrestricted access. ** FALSE -- if the shell is restricted. */ #if !HASGETUSERSHELL # ifndef _PATH_SHELLS # define _PATH_SHELLS "/etc/shells" # endif # if defined(_AIX3) || defined(_AIX4) # include # include # endif char *DefaultUserShells[] = { "/bin/sh", /* standard shell */ "/usr/bin/sh", "/bin/csh", /* C shell */ "/usr/bin/csh", #ifdef __hpux # ifdef V4FS "/usr/bin/rsh", /* restricted Bourne shell */ "/usr/bin/ksh", /* Korn shell */ "/usr/bin/rksh", /* restricted Korn shell */ "/usr/bin/pam", "/usr/bin/keysh", /* key shell (extended Korn shell) */ "/usr/bin/posix/sh", # else "/bin/rsh", /* restricted Bourne shell */ "/bin/ksh", /* Korn shell */ "/bin/rksh", /* restricted Korn shell */ "/bin/pam", "/usr/bin/keysh", /* key shell (extended Korn shell) */ "/bin/posix/sh", # endif #endif #if defined(_AIX3) || defined(_AIX4) "/bin/ksh", /* Korn shell */ "/usr/bin/ksh", "/bin/tsh", /* trusted shell */ "/usr/bin/tsh", "/bin/bsh", /* Bourne shell */ "/usr/bin/bsh", #endif #ifdef __svr4__ "/bin/ksh", /* Korn shell */ "/usr/bin/ksh", #endif NULL }; #endif #define WILDCARD_SHELL "/SENDMAIL/ANY/SHELL/" bool usershellok(user, shell) char *user; char *shell; { #if HASGETUSERSHELL register char *p; extern char *getusershell(); if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') || ConfigLevel <= 1) return TRUE; setusershell(); while ((p = getusershell()) != NULL) if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0) break; endusershell(); return p != NULL; #else # if USEGETCONFATTR auto char *v; # endif register FILE *shellf; char buf[MAXLINE]; if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't')) return TRUE; # if USEGETCONFATTR /* ** Naturally IBM has a "better" idea..... ** ** What a crock. This interface isn't documented, it is ** considered part of the security library (-ls), and it ** only works if you are running as root (since the list ** of valid shells is obviously a source of great concern). ** I recommend that you do NOT define USEGETCONFATTR, ** especially since you are going to have to set up an ** /etc/shells anyhow to handle the cases where getconfattr ** fails. */ if (getconfattr(SC_SYS_LOGIN, SC_SHELLS, &v, SEC_LIST) == 0 && v != NULL) { while (*v != '\0') { if (strcmp(v, shell) == 0 || strcmp(v, WILDCARD_SHELL) == 0) return TRUE; v += strlen(v) + 1; } return FALSE; } # endif shellf = fopen(_PATH_SHELLS, "r"); if (shellf == NULL) { /* no /etc/shells; see if it is one of the std shells */ char **d; for (d = DefaultUserShells; *d != NULL; d++) { if (strcmp(shell, *d) == 0) return TRUE; } return FALSE; } while (fgets(buf, sizeof buf, shellf) != NULL) { register char *p, *q; p = buf; while (*p != '\0' && *p != '#' && *p != '/') p++; if (*p == '#' || *p == '\0') continue; q = p; while (*p != '\0' && *p != '#' && !isspace(*p)) p++; *p = '\0'; if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0) { fclose(shellf); return TRUE; } } fclose(shellf); return FALSE; #endif } /* ** FREEDISKSPACE -- see how much free space is on the queue filesystem ** ** Only implemented if you have statfs. ** ** Parameters: ** dir -- the directory in question. ** bsize -- a variable into which the filesystem ** block size is stored. ** ** Returns: ** The number of bytes free on the queue filesystem. ** -1 if the statfs call fails. ** ** Side effects: ** Puts the filesystem block size into bsize. */ /* statfs types */ #define SFS_NONE 0 /* no statfs implementation */ #define SFS_USTAT 1 /* use ustat */ #define SFS_4ARGS 2 /* use four-argument statfs call */ #define SFS_VFS 3 /* use implementation */ #define SFS_MOUNT 4 /* use implementation */ #define SFS_STATFS 5 /* use implementation */ #define SFS_STATVFS 6 /* use implementation */ #ifndef SFS_TYPE # define SFS_TYPE SFS_NONE #endif #if SFS_TYPE == SFS_USTAT # include #endif #if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS # include #endif #if SFS_TYPE == SFS_VFS # include #endif #if SFS_TYPE == SFS_MOUNT # include #endif #if SFS_TYPE == SFS_STATVFS # include #endif long freediskspace(dir, bsize) char *dir; long *bsize; { #if SFS_TYPE != SFS_NONE # if SFS_TYPE == SFS_USTAT struct ustat fs; struct stat statbuf; # define FSBLOCKSIZE DEV_BSIZE # define SFS_BAVAIL f_tfree # else # if defined(ultrix) struct fs_data fs; # define SFS_BAVAIL fd_bfreen # define FSBLOCKSIZE 1024L # else # if SFS_TYPE == SFS_STATVFS struct statvfs fs; # define FSBLOCKSIZE fs.f_frsize # else struct statfs fs; # define FSBLOCKSIZE fs.f_bsize # endif # endif # endif # ifndef SFS_BAVAIL # define SFS_BAVAIL f_bavail # endif # if SFS_TYPE == SFS_USTAT if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0) # else # if SFS_TYPE == SFS_4ARGS if (statfs(dir, &fs, sizeof fs, 0) == 0) # else # if SFS_TYPE == SFS_STATVFS if (statvfs(dir, &fs) == 0) # else # if defined(ultrix) if (statfs(dir, &fs) > 0) # else if (statfs(dir, &fs) == 0) # endif # endif # endif # endif { if (bsize != NULL) *bsize = FSBLOCKSIZE; if (fs.SFS_BAVAIL < 0) return 0; else return fs.SFS_BAVAIL; } #endif return (-1); } /* ** ENOUGHDISKSPACE -- is there enough free space on the queue fs? ** ** Only implemented if you have statfs. ** ** Parameters: ** msize -- the size to check against. If zero, we don't yet ** know how big the message will be, so just check for ** a "reasonable" amount. ** ** Returns: ** TRUE if there is enough space. ** FALSE otherwise. */ bool enoughdiskspace(msize) long msize; { long bfree, bsize; if (MinBlocksFree <= 0 && msize <= 0) { if (tTd(4, 80)) printf("enoughdiskspace: no threshold\n"); return TRUE; } if ((bfree = freediskspace(QueueDir, &bsize)) >= 0) { if (tTd(4, 80)) printf("enoughdiskspace: bavail=%ld, need=%ld\n", bfree, msize); /* convert msize to block count */ msize = msize / bsize + 1; if (MinBlocksFree >= 0) msize += MinBlocksFree; if (bfree < msize) { #ifdef LOG if (LogLevel > 0) syslog(LOG_ALERT, "%s: low on space (have %ld, %s needs %ld in %s)", CurEnv->e_id == NULL ? "[NOQUEUE]" : CurEnv->e_id, bfree, CurHostName == NULL ? "SMTP-DAEMON" : CurHostName, msize, QueueDir); #endif return FALSE; } } else if (tTd(4, 80)) printf("enoughdiskspace failure: min=%ld, need=%ld: %s\n", MinBlocksFree, msize, errstring(errno)); return TRUE; } /* ** TRANSIENTERROR -- tell if an error code indicates a transient failure ** ** This looks at an errno value and tells if this is likely to ** go away if retried later. ** ** Parameters: ** err -- the errno code to classify. ** ** Returns: ** TRUE if this is probably transient. ** FALSE otherwise. */ bool transienterror(err) int err; { switch (err) { case EIO: /* I/O error */ case ENXIO: /* Device not configured */ case EAGAIN: /* Resource temporarily unavailable */ case ENOMEM: /* Cannot allocate memory */ case ENODEV: /* Operation not supported by device */ case ENFILE: /* Too many open files in system */ case EMFILE: /* Too many open files */ case ENOSPC: /* No space left on device */ #ifdef ETIMEDOUT case ETIMEDOUT: /* Connection timed out */ #endif #ifdef ESTALE case ESTALE: /* Stale NFS file handle */ #endif #ifdef ENETDOWN case ENETDOWN: /* Network is down */ #endif #ifdef ENETUNREACH case ENETUNREACH: /* Network is unreachable */ #endif #ifdef ENETRESET case ENETRESET: /* Network dropped connection on reset */ #endif #ifdef ECONNABORTED case ECONNABORTED: /* Software caused connection abort */ #endif #ifdef ECONNRESET case ECONNRESET: /* Connection reset by peer */ #endif #ifdef ENOBUFS case ENOBUFS: /* No buffer space available */ #endif #ifdef ESHUTDOWN case ESHUTDOWN: /* Can't send after socket shutdown */ #endif #ifdef ECONNREFUSED case ECONNREFUSED: /* Connection refused */ #endif #ifdef EHOSTDOWN case EHOSTDOWN: /* Host is down */ #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: /* No route to host */ #endif #ifdef EDQUOT case EDQUOT: /* Disc quota exceeded */ #endif #ifdef EPROCLIM case EPROCLIM: /* Too many processes */ #endif #ifdef EUSERS case EUSERS: /* Too many users */ #endif #ifdef EDEADLK case EDEADLK: /* Resource deadlock avoided */ #endif #ifdef EISCONN case EISCONN: /* Socket already connected */ #endif #ifdef EINPROGRESS case EINPROGRESS: /* Operation now in progress */ #endif #ifdef EALREADY case EALREADY: /* Operation already in progress */ #endif #ifdef EADDRINUSE case EADDRINUSE: /* Address already in use */ #endif #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: /* Can't assign requested address */ #endif #ifdef ETXTBSY case ETXTBSY: /* (Apollo) file locked */ #endif #if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR)) case ENOSR: /* Out of streams resources */ #endif case EOPENTIMEOUT: /* PSEUDO: open timed out */ return TRUE; } /* nope, must be permanent */ return FALSE; } /* ** LOCKFILE -- lock a file using flock or (shudder) fcntl locking ** ** Parameters: ** fd -- the file descriptor of the file. ** filename -- the file name (for error messages). ** ext -- the filename extension. ** type -- type of the lock. Bits can be: ** LOCK_EX -- exclusive lock. ** LOCK_NB -- non-blocking. ** ** Returns: ** TRUE if the lock was acquired. ** FALSE otherwise. */ bool lockfile(fd, filename, ext, type) int fd; char *filename; char *ext; int type; { # if !HASFLOCK int action; struct flock lfd; if (ext == NULL) ext = ""; bzero(&lfd, sizeof lfd); if (bitset(LOCK_UN, type)) lfd.l_type = F_UNLCK; else if (bitset(LOCK_EX, type)) lfd.l_type = F_WRLCK; else lfd.l_type = F_RDLCK; if (bitset(LOCK_NB, type)) action = F_SETLK; else action = F_SETLKW; if (tTd(55, 60)) printf("lockfile(%s%s, action=%d, type=%d): ", filename, ext, action, lfd.l_type); if (fcntl(fd, action, &lfd) >= 0) { if (tTd(55, 60)) printf("SUCCESS\n"); return TRUE; } if (tTd(55, 60)) printf("(%s) ", errstring(errno)); /* ** On SunOS, if you are testing using -oQ/tmp/mqueue or ** -oA/tmp/aliases or anything like that, and /tmp is mounted ** as type "tmp" (that is, served from swap space), the ** previous fcntl will fail with "Invalid argument" errors. ** Since this is fairly common during testing, we will assume ** that this indicates that the lock is successfully grabbed. */ if (errno == EINVAL) { if (tTd(55, 60)) printf("SUCCESS\n"); return TRUE; } if (!bitset(LOCK_NB, type) || (errno != EACCES && errno != EAGAIN)) { int omode = -1; # ifdef F_GETFL int oerrno = errno; (void) fcntl(fd, F_GETFL, &omode); errno = oerrno; # endif syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", filename, ext, fd, type, omode, geteuid()); } # else if (ext == NULL) ext = ""; if (tTd(55, 60)) printf("lockfile(%s%s, type=%o): ", filename, ext, type); if (flock(fd, type) >= 0) { if (tTd(55, 60)) printf("SUCCESS\n"); return TRUE; } if (tTd(55, 60)) printf("(%s) ", errstring(errno)); if (!bitset(LOCK_NB, type) || errno != EWOULDBLOCK) { int omode = -1; # ifdef F_GETFL int oerrno = errno; (void) fcntl(fd, F_GETFL, &omode); errno = oerrno; # endif syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", filename, ext, fd, type, omode, geteuid()); } # endif if (tTd(55, 60)) printf("FAIL\n"); return FALSE; } /* ** CHOWNSAFE -- tell if chown is "safe" (executable only by root) ** ** Parameters: ** fd -- the file descriptor to check. ** ** Returns: ** TRUE -- if only root can chown the file to an arbitrary ** user. ** FALSE -- if an arbitrary user can give away a file. */ bool chownsafe(fd) int fd; { #ifdef __hpux char *s; int tfd; uid_t o_uid, o_euid; gid_t o_gid, o_egid; bool rval; struct stat stbuf; o_uid = getuid(); o_euid = geteuid(); o_gid = getgid(); o_egid = getegid(); fstat(fd, &stbuf); setresuid(stbuf.st_uid, stbuf.st_uid, -1); setresgid(stbuf.st_gid, stbuf.st_gid, -1); s = tmpnam(NULL); tfd = open(s, O_RDONLY|O_CREAT, 0600); rval = fchown(tfd, DefUid, DefGid) != 0; close(tfd); setresuid(o_uid, o_euid, -1); setresgid(o_gid, o_egid, -1); unlink(s); return rval; #else # ifdef _POSIX_CHOWN_RESTRICTED # if _POSIX_CHOWN_RESTRICTED == -1 return FALSE; # else return TRUE; # endif # else # ifdef _PC_CHOWN_RESTRICTED int rval; /* ** Some systems (e.g., SunOS) seem to have the call and the ** #define _PC_CHOWN_RESTRICTED, but don't actually implement ** the call. This heuristic checks for that. */ errno = 0; rval = fpathconf(fd, _PC_CHOWN_RESTRICTED); if (errno == 0) return rval > 0; # endif # ifdef BSD return TRUE; # else return FALSE; # endif # endif #endif } /* ** RESETLIMITS -- reset system controlled resource limits ** ** This is to avoid denial-of-service attacks ** ** Parameters: ** none ** ** Returns: ** none */ #if HASSETRLIMIT # ifdef RLIMIT_NEEDS_SYS_TIME_H # include # endif # include #endif #ifndef FD_SETSIZE # define FD_SETSIZE 256 #endif void resetlimits() { #if HASSETRLIMIT struct rlimit lim; lim.rlim_cur = lim.rlim_max = RLIM_INFINITY; (void) setrlimit(RLIMIT_CPU, &lim); (void) setrlimit(RLIMIT_FSIZE, &lim); # ifdef RLIMIT_NOFILE lim.rlim_cur = lim.rlim_max = FD_SETSIZE; (void) setrlimit(RLIMIT_NOFILE, &lim); # endif #else # if HASULIMIT (void) ulimit(2, 0x3fffff); (void) ulimit(4, FD_SETSIZE); # endif #endif errno = 0; } /* ** GETCFNAME -- return the name of the .cf file. ** ** Some systems (e.g., NeXT) determine this dynamically. */ char * getcfname() { if (ConfFile != NULL) return ConfFile; #if NETINFO { extern char *ni_propval(); char *cflocation; cflocation = ni_propval("/locations", NULL, "sendmail", "sendmail.cf", '\0'); if (cflocation != NULL) return cflocation; } #endif return _PATH_SENDMAILCF; } /* ** SETVENDOR -- process vendor code from V configuration line ** ** Parameters: ** vendor -- string representation of vendor. ** ** Returns: ** TRUE -- if ok. ** FALSE -- if vendor code could not be processed. ** ** Side Effects: ** It is reasonable to set mode flags here to tweak ** processing in other parts of the code if necessary. ** For example, if you are a vendor that uses $%y to ** indicate YP lookups, you could enable that here. */ bool setvendor(vendor) char *vendor; { if (strcasecmp(vendor, "Berkeley") == 0) { VendorCode = VENDOR_BERKELEY; return TRUE; } /* add vendor extensions here */ #ifdef SUN_EXTENSIONS if (strcasecmp(vendor, "Sun") == 0) { VendorCode = VENDOR_SUN; return TRUE; } #endif return FALSE; } /* ** VENDOR_PRE_DEFAULTS, VENDOR_POST_DEFAULTS -- set vendor-specific defaults ** ** Vendor_pre_defaults is called before reading the configuration ** file; vendor_post_defaults is called immediately after. ** ** Parameters: ** e -- the global environment to initialize. ** ** Returns: ** none. */ #if SHARE_V1 int DefShareUid; /* default share uid to run as -- unused??? */ #endif void vendor_pre_defaults(e) ENVELOPE *e; { #if SHARE_V1 /* OTHERUID is defined in shares.h, do not be alarmed */ DefShareUid = OTHERUID; #endif #ifdef SUN_EXTENSIONS sun_pre_defaults(e); #endif } void vendor_post_defaults(e) ENVELOPE *e; { #ifdef SUN_EXTENSIONS sun_post_defaults(e); #endif } /* ** VENDOR_DAEMON_SETUP -- special vendor setup needed for daemon mode */ void vendor_daemon_setup(e) ENVELOPE *e; { #if SECUREWARE if (getluid() != -1) { usrerr("Daemon cannot have LUID"); exit(EX_USAGE); } #endif /* SECUREWARE */ } /* ** VENDOR_SET_UID -- do setup for setting a user id ** ** This is called when we are still root. ** ** Parameters: ** uid -- the uid we are about to become. ** ** Returns: ** none. */ void vendor_set_uid(uid) UID_T uid; { /* ** We need to setup the share groups (lnodes) ** and and auditing inforation (luid's) ** before we loose our ``root''ness. */ #if SHARE_V1 if (setupshares(uid, syserr) != 0) syserr("Unable to set up shares"); #endif #if SECUREWARE (void) setup_secure(uid); #endif } /* ** VALIDATE_CONNECTION -- check connection for rationality ** ** If the connection is rejected, this routine should log an ** appropriate message -- but should never issue any SMTP protocol. ** ** Parameters: ** sap -- a pointer to a SOCKADDR naming the peer. ** hostname -- the name corresponding to sap. ** e -- the current envelope. ** ** Returns: ** TRUE -- if the connection should be accepted. ** FALSE -- if it should be rejected. */ #if TCPWRAPPERS # include int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif bool validate_connection(sap, hostname, e) SOCKADDR *sap; char *hostname; ENVELOPE *e; { if (rscheck("check_relay", hostname, anynet_ntoa(sap), e) != EX_OK) return FALSE; #if TCPWRAPPERS if (!hosts_ctl("sendmail", hostname, anynet_ntoa(sap), STRING_UNKNOWN)) return FALSE; #endif return TRUE; } /* ** STRTOL -- convert string to long integer ** ** For systems that don't have it in the C library. ** ** This is taken verbatim from the 4.4-Lite C library. */ #ifdef NEEDSTRTOL #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include /* * Convert a string to a long integer. * * Ignores `locale' stuff. Assumes that the upper and lower case * alphabets and digits are each contiguous. */ long strtol(nptr, endptr, base) const char *nptr; char **endptr; register int base; { register const char *s = nptr; register unsigned long acc; register int c; register unsigned long cutoff; register int neg = 0, any, cutlim; /* * Skip white space and pick up leading +/- sign if any. * If base is 0, allow 0x for hex and 0 for octal, else * assume decimal; if base is already 16, allow 0x. */ do { c = *s++; } while (isspace(c)); if (c == '-') { neg = 1; c = *s++; } else if (c == '+') c = *s++; if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { c = s[1]; s += 2; base = 16; } if (base == 0) base = c == '0' ? 8 : 10; /* * Compute the cutoff value between legal numbers and illegal * numbers. That is the largest legal value, divided by the * base. An input number that is greater than this value, if * followed by a legal input character, is too big. One that * is equal to this value may be valid or not; the limit * between valid and invalid numbers is then based on the last * digit. For instance, if the range for longs is * [-2147483648..2147483647] and the input base is 10, * cutoff will be set to 214748364 and cutlim to either * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated * a value > 214748364, or equal but the next digit is > 7 (or 8), * the number is too big, and we will return a range error. * * Set any if any `digits' consumed; make it negative to indicate * overflow. */ cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; cutlim = cutoff % (unsigned long)base; cutoff /= (unsigned long)base; for (acc = 0, any = 0;; c = *s++) { if (isdigit(c)) c -= '0'; else if (isalpha(c)) c -= isupper(c) ? 'A' - 10 : 'a' - 10; else break; if (c >= base) break; if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim) any = -1; else { any = 1; acc *= base; acc += c; } } if (any < 0) { acc = neg ? LONG_MIN : LONG_MAX; errno = ERANGE; } else if (neg) acc = -acc; if (endptr != 0) *endptr = (char *)(any ? s - 1 : nptr); return (acc); } #endif /* ** STRSTR -- find first substring in string ** ** Parameters: ** big -- the big (full) string. ** little -- the little (sub) string. ** ** Returns: ** A pointer to the first instance of little in big. ** big if little is the null string. ** NULL if little is not contained in big. */ #ifdef NEEDSTRSTR char * strstr(big, little) char *big; char *little; { register char *p = big; int l; if (*little == '\0') return big; l = strlen(little); while ((p = strchr(p, *little)) != NULL) { if (strncmp(p, little, l) == 0) return p; p++; } return NULL; } #endif /* ** SM_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX ** ** Some operating systems have wierd problems with the gethostbyXXX ** routines. For example, Solaris versions at least through 2.3 ** don't properly deliver a canonical h_name field. This tries to ** work around these problems. */ struct hostent * sm_gethostbyname(name) char *name; { struct hostent *h; #if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) # if SOLARIS == 20300 || SOLARIS == 203 static struct hostent hp; static char buf[1000]; extern struct hostent *_switch_gethostbyname_r(); if (tTd(61, 10)) printf("_switch_gethostbyname_r(%s)... ", name); h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno); # else extern struct hostent *__switch_gethostbyname(); if (tTd(61, 10)) printf("__switch_gethostbyname(%s)... ", name); h = __switch_gethostbyname(name); # endif #else int nmaps; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; char hbuf[MAXNAME]; if (tTd(61, 10)) printf("gethostbyname(%s)... ", name); h = gethostbyname(name); if (h == NULL) { if (tTd(61, 10)) printf("failure\n"); nmaps = switch_map_find("hosts", maptype, mapreturn); while (--nmaps >= 0) if (strcmp(maptype[nmaps], "nis") == 0 || strcmp(maptype[nmaps], "files") == 0) break; if (nmaps >= 0) { /* try short name */ if (strlen(name) > (SIZE_T) sizeof hbuf - 1) return NULL; strcpy(hbuf, name); shorten_hostname(hbuf); /* if it hasn't been shortened, there's no point */ if (strcmp(hbuf, name) != 0) { if (tTd(61, 10)) printf("gethostbyname(%s)... ", hbuf); h = gethostbyname(hbuf); } } } #endif if (tTd(61, 10)) { if (h == NULL) printf("failure\n"); else printf("%s\n", h->h_name); } return h; } struct hostent * sm_gethostbyaddr(addr, len, type) char *addr; int len; int type; { #if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) # if SOLARIS == 20300 || SOLARIS == 203 static struct hostent hp; static char buf[1000]; extern struct hostent *_switch_gethostbyaddr_r(); return _switch_gethostbyaddr_r(addr, len, type, &hp, buf, sizeof(buf), &h_errno); # else extern struct hostent *__switch_gethostbyaddr(); return __switch_gethostbyaddr(addr, len, type); # endif #else return gethostbyaddr(addr, len, type); #endif } /* ** SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid */ struct passwd * sm_getpwnam(user) char *user; { return getpwnam(user); } struct passwd * sm_getpwuid(uid) UID_T uid; { return getpwuid(uid); } /* ** SECUREWARE_SETUP_SECURE -- Convex SecureWare setup ** ** Set up the trusted computing environment for C2 level security ** under SecureWare. ** ** Parameters: ** uid -- uid of the user to initialize in the TCB ** ** Returns: ** none ** ** Side Effects: ** Initialized the user in the trusted computing base */ #if SECUREWARE # include # include void secureware_setup_secure(uid) UID_T uid; { int rc; if (getluid() != -1) return; if ((rc = set_secure_info(uid)) != SSI_GOOD_RETURN) { switch (rc) { case SSI_NO_PRPW_ENTRY: syserr("No protected passwd entry, uid = %d", uid); break; case SSI_LOCKED: syserr("Account has been disabled, uid = %d", uid); break; case SSI_RETIRED: syserr("Account has been retired, uid = %d", uid); break; case SSI_BAD_SET_LUID: syserr("Could not set LUID, uid = %d", uid); break; case SSI_BAD_SET_PRIVS: syserr("Could not set kernel privs, uid = %d", uid); default: syserr("Unknown return code (%d) from set_secure_info(%d)", rc, uid); break; } exit(EX_NOPERM); } } #endif /* SECUREWARE */ /* ** LOAD_IF_NAMES -- load interface-specific names into $=w ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Loads $=w with the names of all the interfaces. */ #ifdef SIOCGIFCONF struct rtentry; struct mbuf; # include # ifndef SUNOS403 # include # endif # include #endif void load_if_names() { #ifdef SIOCGIFCONF int s; int i; struct ifconf ifc; char interfacebuf[1024]; s = socket(AF_INET, SOCK_DGRAM, 0); if (s == -1) return; /* get the list of known IP address from the kernel */ ifc.ifc_buf = interfacebuf; ifc.ifc_len = sizeof interfacebuf; if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { if (tTd(0, 4)) printf("SIOGIFCONF failed: %s\n", errstring(errno)); close(s); return; } /* scan the list of IP address */ if (tTd(0, 40)) printf("scanning for interface specific names, ifc_len=%d\n", ifc.ifc_len); for (i = 0; i < ifc.ifc_len; ) { struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i]; struct sockaddr *sa = &ifr->ifr_addr; struct in_addr ia; struct hostent *hp; #ifdef SIOCGIFFLAGS struct ifreq ifrf; #endif char ip_addr[256]; extern char *inet_ntoa(); extern struct hostent *gethostbyaddr(); #ifdef BSD4_4_SOCKADDR if (sa->sa_len > sizeof ifr->ifr_addr) i += sizeof ifr->ifr_name + sa->sa_len; else #endif i += sizeof *ifr; if (tTd(0, 20)) printf("%s\n", anynet_ntoa((SOCKADDR *) sa)); if (ifr->ifr_addr.sa_family != AF_INET) continue; #ifdef SIOCGIFFLAGS bzero(&ifrf, sizeof(struct ifreq)); strncpy(ifrf.ifr_name, ifr->ifr_name, sizeof(ifrf.ifr_name)); ioctl(s, SIOCGIFFLAGS, (char *) &ifrf); if (tTd(0, 41)) printf("\tflags: %x\n", ifrf.ifr_flags); if (!bitset(IFF_UP, ifrf.ifr_flags)) continue; #else if (!bitset(IFF_UP, ifr->ifr_flags)) continue; #endif /* extract IP address from the list*/ ia = (((struct sockaddr_in *) sa)->sin_addr); if (ia.s_addr == INADDR_ANY || ia.s_addr == INADDR_NONE) { message("WARNING: interface %s is UP with %s address", ifr->ifr_name, inet_ntoa(ia)); continue; } /* save IP address in text from */ (void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]", sizeof ip_addr - 3, inet_ntoa(ia)); if (!wordinclass(ip_addr, 'w')) { setclass('w', ip_addr); if (tTd(0, 4)) printf("\ta.k.a.: %s\n", ip_addr); } /* skip "loopback" interface "lo" */ if (strcmp("lo0", ifr->ifr_name) == 0) continue; /* lookup name with IP address */ hp = sm_gethostbyaddr((char *) &ia, sizeof(ia), AF_INET); if (hp == NULL) { #ifdef LOG if (LogLevel > 3) syslog(LOG_WARNING, "gethostbyaddr() failed for %.100s\n", inet_ntoa(ia)); #endif continue; } /* save its cname */ if (!wordinclass((char *) hp->h_name, 'w')) { setclass('w', (char *) hp->h_name); if (tTd(0, 4)) printf("\ta.k.a.: %s\n", hp->h_name); } /* save all it aliases name */ while (*hp->h_aliases) { if (!wordinclass(*hp->h_aliases, 'w')) { setclass('w', *hp->h_aliases); if (tTd(0, 4)) printf("\ta.k.a.: %s\n", *hp->h_aliases); } hp->h_aliases++; } } close(s); #endif } /* ** HARD_SYSLOG -- call syslog repeatedly until it works ** ** Needed on HP-UX, which apparently doesn't guarantee that ** syslog succeeds during interrupt handlers. */ #ifdef __hpux # define MAXSYSLOGTRIES 100 # undef syslog # ifdef V4FS # define XCNST const # define CAST (const char *) # else # define XCNST # define CAST # endif void # ifdef __STDC__ hard_syslog(int pri, XCNST char *msg, ...) # else hard_syslog(pri, msg, va_alist) int pri; XCNST char *msg; va_dcl # endif { int i; char buf[SYSLOG_BUFSIZE * 2]; VA_LOCAL_DECL; VA_START(msg); vsnprintf(buf, sizeof buf, msg, ap); VA_END; for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; ) continue; } # undef CAST #endif /* ** LOCAL_HOSTNAME_LENGTH ** ** This is required to get sendmail to compile against BIND 4.9.x ** on Ultrix. */ #if defined(ultrix) && NAMED_BIND # include # if __RES >= 19931104 && __RES < 19950621 int local_hostname_length(hostname) char *hostname; { int len_host, len_domain; if (!*_res.defdname) res_init(); len_host = strlen(hostname); len_domain = strlen(_res.defdname); if (len_host > len_domain && (strcasecmp(hostname + len_host - len_domain,_res.defdname) == 0) && hostname[len_host - len_domain - 1] == '.') return len_host - len_domain - 1; else return 0; } # endif #endif /* ** Compile-Time options */ char *CompileOptions[] = { #if HESIOD "HESIOD", #endif #if HES_GETMAILHOST "HES_GETMAILHOST", #endif #if LDAPMAP "LDAPMAP", #endif #ifdef LOG "LOG", #endif #if MATCHGECOS "MATCHGECOS", #endif #if MIME7TO8 "MIME7TO8", #endif #if MIME8TO7 "MIME8TO7", #endif #if NAMED_BIND "NAMED_BIND", #endif #if NDBM "NDBM", #endif #if NETINET "NETINET", #endif #if NETINFO "NETINFO", #endif #if NETISO "NETISO", #endif #if NETNS "NETNS", #endif #if NETUNIX "NETUNIX", #endif #if NETX25 "NETX25", #endif #if NEWDB "NEWDB", #endif #if NIS "NIS", #endif #if NISPLUS "NISPLUS", #endif #if QUEUE "QUEUE", #endif #if SCANF "SCANF", #endif #if SMTP "SMTP", #endif #if SMTPDEBUG "SMTPDEBUG", #endif #if SUID_ROOT_FILES_OK "SUID_ROOT_FILES_OK", #endif #if TCPWRAPPERS "TCPWRAPPERS", #endif #if USERDB "USERDB", #endif #if XDEBUG "XDEBUG", #endif #if XLA "XLA", #endif NULL }; /* ** OS compile options. */ char *OsCompileOptions[] = { #if HASFCHMOD "HASFCHMOD", #endif #if HASFLOCK "HASFLOCK", #endif #if HASGETDTABLESIZE "HASGETDTABLESIZE", #endif #if HASGETUSERSHELL "HASGETUSERSHELL", #endif #if HASINITGROUPS "HASINITGROUPS", #endif #if HASLSTAT "HASLSTAT", #endif #if HASSETREUID "HASSETREUID", #endif #if HASSETRLIMIT "HASSETRLIMIT", #endif #if HASSETSID "HASSETSID", #endif #if HASSETUSERCONTEXT "HASSETUSERCONTEXT", #endif #if HASSETVBUF "HASSETVBUF", #endif #if HASSIGSETMASK "HASSIGSETMASK", #endif #if HASSNPRINTF "HASSNPRINTF", #endif #if HASULIMIT "HASULIMIT", #endif #if HASUNAME "HASUNAME", #endif #if HASUNSETENV "HASUNSETENV", #endif #if HASWAITPID "HASWAITPID", #endif #if IDENTPROTO "IDENTPROTO", #endif #if IP_SRCROUTE "IP_SRCROUTE", #endif #if NEEDFSYNC "NEEDFSYNC", #endif #if NOFTRUNCATE "NOFTRUNCATE", #endif #if RLIMIT_NEEDS_SYS_TIME_H "RLIMIT_NEEDS_SYS_TIME_H", #endif #if SECUREWARE "SECUREWARE", #endif #if SHARE_V1 "SHARE_V1", #endif #if SYS5SETPGRP "SYS5SETPGRP", #endif #if SYSTEM5 "SYSTEM5", #endif #if USE_SA_SIGACTION "USE_SA_SIGACTION", #endif #if USESETEUID "USESETEUID", #endif NULL }; Index: head/usr.sbin/sendmail/src/conf.h =================================================================== --- head/usr.sbin/sendmail/src/conf.h (revision 19843) +++ head/usr.sbin/sendmail/src/conf.h (revision 19844) @@ -1,2088 +1,2115 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)conf.h 8.267 (Berkeley) 10/17/96 + * @(#)conf.h 8.272 (Berkeley) 11/16/96 */ /* ** CONF.H -- All user-configurable parameters for sendmail ** ** Send updates to sendmail@Sendmail.ORG so they will be ** included in the next release. */ +#ifdef __GNUC__ struct rusage; /* forward declaration to get gcc to shut up in wait.h */ +#endif # include # include # include # include # include # include # include # include # include # include /********************************************************************** ** Table sizes, etc.... ** There shouldn't be much need to change these.... **********************************************************************/ # define MAXLINE 2048 /* max line length */ # define MAXNAME 256 /* max length of a name */ # define MAXPV 40 /* max # of parms to mailers */ # define MAXATOM 200 /* max atoms per address */ # define MAXMAILERS 25 /* maximum mailers known to system */ # define MAXRWSETS 200 /* max # of sets of rewriting rules */ # define MAXPRIORITIES 25 /* max values for Precedence: field */ # define MAXMXHOSTS 100 /* max # of MX records for one host */ # define SMTPLINELIM 990 /* maximum SMTP line length */ # define MAXKEY 128 /* maximum size of a database key */ # define MEMCHUNKSIZE 1024 /* chunk size for memory allocation */ # define MAXUSERENVIRON 100 /* max envars saved, must be >= 3 */ # define MAXALIASDB 12 /* max # of alias databases */ # define MAXMAPSTACK 12 /* max # of stacked or sequenced maps */ # define MAXTOCLASS 8 /* max # of message timeout classes */ # define MAXMIMEARGS 20 /* max args in Content-Type: */ # define MAXMIMENESTING 20 /* max MIME multipart nesting */ # define QUEUESEGSIZE 1000 /* increment for queue size */ /********************************************************************** ** Compilation options. ** #define these to 1 if they are available; ** #define them to 0 otherwise. ** All can be overridden from Makefile. **********************************************************************/ # ifndef NETINET # define NETINET 1 /* include internet support */ # endif # ifndef NETISO # define NETISO 0 /* do not include ISO socket support */ # endif # ifndef NAMED_BIND # define NAMED_BIND 1 /* use Berkeley Internet Domain Server */ # endif # ifndef XDEBUG # define XDEBUG 1 /* enable extended debugging */ # endif # ifndef MATCHGECOS # define MATCHGECOS 1 /* match user names from gecos field */ # endif # ifndef DSN # define DSN 1 /* include delivery status notification code */ # endif # if !defined(USERDB) && (defined(NEWDB) || defined(HESIOD)) # define USERDB 1 /* look in user database */ # endif # ifndef MIME8TO7 # define MIME8TO7 1 /* 8->7 bit MIME conversions */ # endif # ifndef MIME7TO8 # define MIME7TO8 1 /* 7->8 bit MIME conversions */ # endif /********************************************************************** ** "Hard" compilation options. ** #define these if they are available; comment them out otherwise. ** These cannot be overridden from the Makefile, and should really not ** be turned off unless absolutely necessary. **********************************************************************/ # define LOG /* enable logging -- don't turn off */ /********************************************************************** ** End of site-specific configuration. **********************************************************************/ /* ** General "standard C" defines. ** ** These may be undone later, to cope with systems that claim to ** be Standard C but aren't. Gcc is the biggest offender -- it ** doesn't realize that the library is part of the language. ** ** Life would be much easier if we could get rid of this sort ** of bozo problems. */ #ifdef __STDC__ # define HASSETVBUF 1 /* we have setvbuf(3) in libc */ #endif /* ** Assume you have standard calls; can be #undefed below if necessary. */ # define HASLSTAT 1 /* has lstat(2) call */ /********************************************************************** ** Operating system configuration. ** ** Unless you are porting to a new OS, you shouldn't have to ** change these. **********************************************************************/ /* ** HP-UX -- tested for 8.07, 9.00, and 9.01. ** ** If V4FS is defined, compile for HP-UX 10.0. */ #ifdef __hpux /* common definitions for HP-UX 9.x and 10.x */ # undef m_flags /* conflict between db.h & sys/sysmacros.h on HP 300 */ # define SYSTEM5 1 /* include all the System V defines */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define USESETEUID 1 /* has useable seteuid(2) call */ # define seteuid(e) setresuid(-1, e, -1) # define IP_SRCROUTE 1 /* can check IP source routing */ # define LA_TYPE LA_HPUX # define SPT_TYPE SPT_PSTAT # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define GIDSET_T gid_t # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ # endif # define syslog hard_syslog # ifdef V4FS /* HP-UX 10.x */ # define _PATH_UNIX "/stand/vmunix" # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" # endif # ifndef IDENTPROTO # define IDENTPROTO 1 /* TCP/IP implementation fixed in 10.0 */ # endif # else /* HP-UX 9.x */ # define _PATH_UNIX "/hp-ux" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # ifdef __STDC__ extern void hard_syslog(int, char *, ...); # endif # endif #endif /* ** IBM AIX 4.x */ #ifdef _AIX4 # define _AIX3 1 /* pull in AIX3 stuff */ -# define HASSETREUID 1 /* setreuid(2) works */ +# define USESETEUID 1 /* seteuid(2) works */ +# define TZ_TYPE TZ_NAME /* use tzname[] vector */ +# if _AIX4 >= 40200 +# define HASSETREUID 1 /* setreuid(2) works as of AIX 4.2 */ +# endif #endif /* ** IBM AIX 3.x -- actually tested for 3.2.3 */ #ifdef _AIX3 # include # include /* to get byte order */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ # define GIDSET_T gid_t # define SFS_TYPE SFS_STATFS /* use statfs() impl */ # define SPT_PADCHAR '\0' /* pad process title with nulls */ # define LA_TYPE LA_INT # define FSHIFT 16 # define LA_AVENRUN "avenrun" #endif /* ** IBM AIX 2.2.1 -- actually tested for osupdate level 2706+1773 ** ** From Mark Whetzel . */ #ifdef AIX /* AIX/RT compiler pre-defines this */ # include # include /* AIX/RT resource.h does NOT include this */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define HASFCHMOD 0 /* does not have fchmod(2) syscall */ # define HASSETREUID 1 /* use setreuid(2) -lbsd system call */ # define HASSETVBUF 1 /* use setvbuf(2) system call */ # define HASSETRLIMIT 0 /* does not have setrlimit call */ # define HASFLOCK 0 /* does not have flock call - use fcntl */ # define HASULIMIT 1 /* use ulimit instead of setrlimit call */ # define NEEDGETOPT 1 /* Do we need theirs or ours */ # define SYS5SETPGRP 1 /* don't have setpgid on AIX/RT */ # define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ # define BSD4_3 1 /* NOT bsd 4.4 or posix signals */ # define GIDSET_T int # define SFS_TYPE SFS_STATFS /* use statfs() impl */ # define SPT_PADCHAR '\0' /* pad process title with nulls */ # define LA_TYPE LA_SUBR /* use our ported loadavgd daemon */ # define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ # define ARBPTR_T int * # define void int typedef int pid_t; /* RTisms for BSD compatibility, specified in the Makefile define BSD 1 define BSD_INCLUDES 1 define BSD_REMAP_SIGNAL_TO_SIGVEC RTisms needed above */ /* make this sendmail in a completely different place */ # define _PATH_VENDORCF "/usr/local/newmail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/usr/local/newmail/sendmail.pid" # endif #endif /* ** Silicon Graphics IRIX ** ** Compiles on 4.0.1. ** ** Use IRIX64 instead of IRIX for 64-bit IRIX (6.0). ** Use IRIX5 instead of IRIX for IRIX 5.x. ** ** This version tries to be adaptive using _MIPS_SIM: ** _MIPS_SIM == _ABIO32 (= 1) Abi: -32 on IRIX 6.2 ** _MIPS_SIM == _ABIN32 (= 2) Abi: -n32 on IRIX 6.2 ** _MIPS_SIM == _ABI64 (= 3) Abi: -64 on IRIX 6.2 ** ** _MIPS_SIM is 1 also on IRIX 5.3 ** ** IRIX64 changes from Mark R. Levinson . ** IRIX5 changes from Kari E. Hurtta . ** Adaptive changes from Kari E. Hurtta . */ #if defined(__sgi) # ifndef IRIX # define IRIX # endif # if _MIPS_SIM > 0 && !defined(IRIX5) # define IRIX5 /* IRIX5 or IRIX6 */ # endif # if _MIPS_SIM > 1 && !defined(IRIX6) && !defined(IRIX64) # define IRIX6 /* IRIX6 */ # endif #endif #ifdef IRIX # define SYSTEM5 1 /* this is a System-V derived system */ # define HASSETREUID 1 /* has setreuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define IP_SRCROUTE 1 /* can check IP source routing */ # define setpgid BSDsetpgrp # define GIDSET_T gid_t # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ # define SFS_BAVAIL f_bfree /* alternate field name */ # ifdef IRIX6 # define LA_TYPE LA_IRIX6 /* figure out at run time */ # else # define LA_TYPE LA_INT # ifdef IRIX64 # define NAMELISTMASK 0x7fffffffffffffff /* mask for nlist() values */ # else # define NAMELISTMASK 0x7fffffff /* mask for nlist() values */ # endif # endif # if defined(IRIX64) || defined(IRIX5) # include # include # define ARGV_T char *const * # define HASSETRLIMIT 1 /* has setrlimit(2) syscall */ # define HASGETDTABLESIZE 1 /* has getdtablesize(2) syscall */ # else # define ARGV_T const char ** # define WAITUNION 1 /* use "union wait" as wait argument type */ # endif #endif /* ** SunOS and Solaris ** ** Tested on SunOS 4.1.x (a.k.a. Solaris 1.1.x) and ** Solaris 2.4 (a.k.a. SunOS 5.4). */ #if defined(sun) && !defined(BSD) # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define IP_SRCROUTE 1 /* can check IP source routing */ # ifndef LA_TYPE # define LA_TYPE LA_INT # endif # ifdef SOLARIS_2_3 # define SOLARIS 20300 /* for back compat only -- use -DSOLARIS=20300 */ # endif # if defined(NOT_SENDMAIL) && !defined(SOLARIS) && defined(sun) && (defined(__svr4__) || defined(__SVR4)) # define SOLARIS 1 /* unknown Solaris version */ # endif # ifdef SOLARIS /* Solaris 2.x (a.k.a. SunOS 5.x) */ # ifndef __svr4__ # define __svr4__ /* use all System V Releae 4 defines below */ # endif # include # define GIDSET_T gid_t # define USE_SA_SIGACTION 1 /* use sa_sigaction field */ # ifndef _PATH_UNIX # define _PATH_UNIX "/dev/ksyms" # endif # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" # endif # ifndef _PATH_HOSTS # define _PATH_HOSTS "/etc/inet/hosts" # endif # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */ # endif # if SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) # define USESETEUID 1 /* seteuid works as of 2.3 */ # endif # if SOLARIS >= 20500 || (SOLARIS < 10000 && SOLARIS >= 205) # define HASSNPRINTF 1 /* has snprintf starting in 2.5 */ # define HASSETREUID 1 /* setreuid works as of 2.5 */ # if SOLARIS == 20500 || SOLARIS == 205 # define snprintf __snprintf /* but names it oddly in 2.5 */ # define vsnprintf __vsnprintf # endif +# ifndef LA_TYPE +# define LA_TYPE LA_KSTAT /* use kstat(3k) -- may work in < 2.5 */ +# endif # endif # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ # endif # else /* SunOS 4.0.3 or 4.1.x */ # define HASSETREUID 1 /* has setreuid(2) call */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ # include # ifdef SUNOS403 /* special tweaking for SunOS 4.0.3 */ # include # define BSD4_3 1 /* 4.3 BSD-based */ # define NEEDSTRSTR 1 /* need emulation of strstr(3) routine */ # define WAITUNION 1 /* use "union wait" as wait argument type */ # undef WIFEXITED # undef WEXITSTATUS # undef HASUNAME # define setpgid setpgrp typedef int pid_t; extern char *getenv(); # else /* 4.1.x specifics */ # define HASSETSID 1 /* has Posix setsid(2) call */ # define HASSETVBUF 1 /* we have setvbuf(3) in libc */ # endif # endif #endif /* ** DG/UX ** ** Tested on 5.4.2 and 5.4.3. Use DGUX_5_4_2 to get the ** older support. ** 5.4.3 changes from Mark T. Robinson . */ #ifdef DGUX_5_4_2 # define DGUX 1 #endif #ifdef DGUX # define SYSTEM5 1 # define LA_TYPE LA_DGUX # define HASSETREUID 1 /* has setreuid(2) call */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASSETSID 1 /* has Posix setsid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define IP_SRCROUTE 0 /* does not have */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) */ # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # define SPT_TYPE SPT_NONE /* don't use setproctitle */ # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ /* these include files must be included early on DG/UX */ # include # include /* compiler doesn't understand const? */ # define const # ifdef DGUX_5_4_2 # define inet_addr dgux_inet_addr extern long dgux_inet_addr(); # endif #endif /* ** Digital Ultrix 4.2A or 4.3 ** ** Apparently, fcntl locking is broken on 4.2A, in that locks are ** not dropped when the process exits. This causes major problems, ** so flock is the only alternative. */ #ifdef ultrix # define HASSETREUID 1 /* has setreuid(2) call */ # define HASUNSETENV 1 /* has unsetenv(3) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # ifndef BROKEN_RES_SEARCH # define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */ # endif # ifdef vax # define LA_TYPE LA_FLOAT # else # define LA_TYPE LA_INT # define LA_AVENRUN "avenrun" # endif # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # ifndef IDENTPROTO # define IDENTPROTO 0 /* pre-4.4 TCP/IP implementation is broken */ # endif #endif /* ** OSF/1 for KSR. ** ** Contributed by Todd C. Miller */ #ifdef __ksr__ # define __osf__ 1 /* get OSF/1 defines below */ # ifndef TZ_TYPE # define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ # endif #endif /* ** OSF/1 for Intel Paragon. ** ** Contributed by Jeff A. Earickson ** of Intel Scalable Systems Divison. */ #ifdef __PARAGON__ # define __osf__ 1 /* get OSF/1 defines below */ # ifndef TZ_TYPE # define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ # endif #endif /* ** OSF/1 (tested on Alpha) -- now known as Digital UNIX. ** ** Tested for 3.2 and 4.0. */ #ifdef __osf__ # define HASUNSETENV 1 /* has unsetenv(3) call */ # define USESETEUID 1 /* has useable seteuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define IP_SRCROUTE 1 /* can check IP source routing */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define LA_TYPE LA_INT # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # define _PATH_VENDOR_CF "/var/adm/sendmail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/var/run/sendmail.pid" # endif #endif /* ** NeXTstep */ #ifdef NeXT # define HASINITGROUPS 1 /* has initgroups(3) call */ # define NEEDPUTENV 2 /* need putenv(3) call; no setenv(3) call */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define WAITUNION 1 /* use "union wait" as wait argument type */ # define UID_T int /* compiler gripes on uid_t */ # define GID_T int /* ditto for gid_t */ +# define MODE_T int /* and mode_t */ # define sleep sleepX # define setpgid setpgrp # ifndef LA_TYPE # define LA_TYPE LA_MACH # endif # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # ifndef _POSIX_SOURCE typedef int pid_t; # undef WEXITSTATUS # undef WIFEXITED # endif # define _PATH_VENDOR_CF "/etc/sendmail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail/sendmail.pid" # endif #endif /* ** 4.4 BSD ** ** See also BSD defines. */ #if defined(BSD4_4) && !defined(__bsdi__) # include # define HASUNSETENV 1 /* has unsetenv(3) call */ # define USESETEUID 1 /* has useable seteuid(2) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # include # define ERRLIST_PREDEFINED /* don't declare sys_errlist */ # define BSD4_4_SOCKADDR /* has sa_len */ # define NETLINK 1 /* supports AF_LINK */ # ifndef LA_TYPE # define LA_TYPE LA_SUBR # endif # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # define SPT_TYPE SPT_PSSTRINGS /* use PS_STRINGS pointer */ #endif /* ** BSD/OS (was BSD/386) (all versions) ** From Tony Sanders, BSDI */ #ifdef __bsdi__ # include # define HASUNSETENV 1 /* has the unsetenv(3) call */ # define HASSETSID 1 /* has the setsid(2) POSIX syscall */ # define USESETEUID 1 /* has useable seteuid(2) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # define HASUNAME 1 /* has uname(2) syscall */ # include # define ERRLIST_PREDEFINED /* don't declare sys_errlist */ # define BSD4_4_SOCKADDR /* has sa_len */ # define NETLINK 1 /* supports AF_LINK */ # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # ifndef LA_TYPE # define LA_TYPE LA_SUBR # endif # define GIDSET_T gid_t # if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199312 /* version 1.1 or later */ # undef SPT_TYPE # define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ # else /* version 1.0 or earlier */ # ifndef OLD_NEWDB # define OLD_NEWDB 1 /* old version of newdb library */ # endif # define SPT_PADCHAR '\0' /* pad process title with nulls */ # endif #endif /* ** FreeBSD / NetBSD (all architectures, all versions) ** ** 4.3BSD clone, closer to 4.4BSD for FreeBSD 1.x and NetBSD 0.9x ** 4.4BSD-Lite based for FreeBSD 2.x and NetBSD 1.x ** ** See also BSD defines. */ #if defined(__FreeBSD__) || defined(__NetBSD__) # include # define HASUNSETENV 1 /* has unsetenv(3) call */ # define HASSETSID 1 /* has the setsid(2) POSIX syscall */ # define USESETEUID 1 /* has useable seteuid(2) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # define HASUNAME 1 /* has uname(2) syscall */ # define ERRLIST_PREDEFINED /* don't declare sys_errlist */ # define BSD4_4_SOCKADDR /* has sa_len */ # define NETLINK 1 /* supports AF_LINK */ # define GIDSET_T gid_t # ifndef LA_TYPE # define LA_TYPE LA_SUBR # endif # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # if defined(__NetBSD__) && (NetBSD > 199307 || NetBSD0_9 > 1) # undef SPT_TYPE # define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ # endif # if defined(__FreeBSD__) # undef SPT_TYPE # if __FreeBSD__ == 2 # include /* and this works */ # if __FreeBSD_version >= 199512 /* 2.2-current right now */ # include # define SPT_TYPE SPT_BUILTIN # endif # endif # ifndef SPT_TYPE # define SPT_TYPE SPT_REUSEARGV # define SPT_PADCHAR '\0' /* pad process title with nulls */ # endif # endif #endif /* ** Mach386 ** ** For mt Xinu's Mach386 system. */ #if defined(MACH) && defined(i386) # define MACH386 1 # define HASUNSETENV 1 /* has unsetenv(3) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define NEEDSTRTOL 1 /* need the strtol() function */ # define setpgid setpgrp # ifndef LA_TYPE # define LA_TYPE LA_FLOAT # endif # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # undef HASSETVBUF /* don't actually have setvbuf(3) */ # undef WEXITSTATUS # undef WIFEXITED # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif #endif /* ** 4.3 BSD -- this is for very old systems ** ** Should work for mt Xinu MORE/BSD and Mips UMIPS-BSD 2.1. ** ** You'll also have to install a new resolver library. ** I don't guarantee that support for this environment is complete. */ #if defined(oldBSD43) || defined(MORE_BSD) || defined(umipsbsd) # define NEEDVPRINTF 1 /* need a replacement for vprintf(3) */ # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define ARBPTR_T char * # define setpgid setpgrp # ifndef LA_TYPE # define LA_TYPE LA_FLOAT # endif # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # undef WEXITSTATUS # undef WIFEXITED typedef short pid_t; extern int errno; #endif /* ** SCO Unix ** ** This includes three parts: ** ** The first is for SCO OpenServer 5. ** (Contributed by Keith Reynolds ). ** ** SCO OpenServer 5 has a compiler version number macro, ** which we can use to figure out what version we're on. ** This may have to change in future releases. ** ** The second is for SCO UNIX 3.2v4.2/Open Desktop 3.0. ** (Contributed by Philippe Brand ). ** ** The third is for SCO UNIX 3.2v4.0/Open Desktop 2.0 and earlier. */ +/* SCO OpenServer 5 */ #if _SCO_DS >= 1 # include # define _SCO_unix_4_2 -# define HASSNPRINTF 1 /* has snprintf() call */ -# define HASFCHMOD 1 /* has fchmod() call */ -# define HASSETRLIMIT 1 /* has setrlimit() call */ +# define HASSNPRINTF 1 /* has snprintf(3) call */ +# define HASFCHMOD 1 /* has fchmod(2) call */ +# define HASSETRLIMIT 1 /* has setrlimit(2) call */ +# define USESETEUID 1 /* has seteuid(2) call */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */ # define RLIMIT_NEEDS_SYS_TIME_H 1 +# ifndef LA_TYPE +# define LA_TYPE LA_DEVSHORT +# endif +# define _PATH_AVENRUN "/dev/table/avenrun" #endif - +/* SCO UNIX 3.2v4.2/Open Desktop 3.0 */ #ifdef _SCO_unix_4_2 # define _SCO_unix_ # define HASSETREUID 1 /* has setreuid(2) call */ #endif +/* SCO UNIX 3.2v4.0 Open Desktop 2.0 and earlier */ #ifdef _SCO_unix_ # include /* needed for IP_SRCROUTE */ # define SYSTEM5 1 /* include all the System V defines */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# define NOFTRUNCATE 0 /* has (simulated) ftruncate call */ # define MAXPATHLEN PATHSIZE -# define LA_TYPE LA_SHORT # define SFS_TYPE SFS_4ARGS /* use 4-arg impl */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define SPT_TYPE SPT_SCO /* write kernel u. area */ # define TZ_TYPE TZ_TM_NAME /* use tm->tm_name */ +# define UID_T uid_t +# define GID_T gid_t +# define GIDSET_T gid_t # define _PATH_UNIX "/unix" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif /* stuff fixed in later releases */ # ifndef _SCO_unix_4_2 # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # endif # ifndef _SCO_DS -# define NOFTRUNCATE 0 /* does not have ftruncate(3) call */ +# define ftruncate chsize /* use chsize(2) to emulate ftruncate */ # define NEEDFSYNC 1 /* needs the fsync(2) call stub */ # define NETUNIX 0 /* no unix domain socket support */ +# define LA_TYPE LA_SHORT # endif #endif /* ** ISC (SunSoft) Unix. ** ** Contributed by J.J. Bailey */ #ifdef ISC_UNIX # include # include /* needed for IP_SRCROUTE */ # include # define SYSTEM5 1 /* include all the System V defines */ # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define HASSETREUID 1 /* has setreuid(2) call */ # define NEEDFSYNC 1 /* needs the fsync(2) call stub */ # define NETUNIX 0 /* no unix domain socket support */ # define MAXPATHLEN 1024 # define LA_TYPE LA_SHORT # define SFS_TYPE SFS_STATFS /* use statfs() impl */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define _PATH_UNIX "/unix" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif #endif /* ** Altos System V (5.3.1) ** Contributed by Tim Rice . */ #ifdef ALTOS_SYSTEM_V # include # include # define SYSTEM5 1 /* include all the System V defines */ # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define WAITUNION 1 /* use "union wait" as wait argument type */ # define NEEDFSYNC 1 /* no fsync(2) in system library */ # define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ # define MAXPATHLEN PATH_MAX # define LA_TYPE LA_SHORT # define SFS_TYPE SFS_STATFS /* use statfs() impl */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ # define NETUNIX 0 /* no unix domain socket support */ # undef WIFEXITED # undef WEXITSTATUS # define strtoul strtol /* gcc library bogosity */ typedef unsigned short uid_t; typedef unsigned short gid_t; typedef short pid_t; /* some stuff that should have been in the include files */ # include extern char *malloc(); extern struct passwd *getpwent(); extern struct passwd *getpwnam(); extern struct passwd *getpwuid(); extern char *getenv(); extern struct group *getgrgid(); extern struct group *getgrnam(); #endif /* ** ConvexOS 11.0 and later ** ** "Todd C. Miller" claims this ** works on 9.1 as well. ** ** ConvexOS 11.5 and later, should work on 11.0 as defined. ** For pre-ConvexOOS 11.0, define NEEDGETOPT, undef IDENTPROTO ** ** Eric Schnoebelen (eric@cirr.com) For CONVEX Computer Corp. ** (now the CONVEX Technologies Center of Hewlett Packard) */ #ifdef _CONVEX_SOURCE # define HASGETDTABLESIZE 1 /* has getdtablesize(2) */ # define HASINITGROUPS 1 /* has initgroups(3) */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASSETSID 1 /* has POSIX setsid(2) call */ # define HASUNSETENV 1 /* has unsetenv(3) */ # define HASFLOCK 1 /* has flock(2) */ # define HASSETRLIMIT 1 /* has setrlimit(2) */ # define HASSETREUID 1 /* has setreuid(2) */ # define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_error=0 */ # define NEEDPUTENV 1 /* needs putenv (written in terms of setenv) */ # define NEEDGETOPT 0 /* need replacement for getopt(3) */ # define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ # define LA_TYPE LA_FLOAT # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef S_IREAD # define S_IREAD _S_IREAD # define S_IWRITE _S_IWRITE # define S_IEXEC _S_IEXEC # define S_IFMT _S_IFMT # define S_IFCHR _S_IFCHR # define S_IFBLK _S_IFBLK # endif # ifndef TZ_TYPE # define TZ_TYPE TZ_TIMEZONE # endif # ifndef IDENTPROTO # define IDENTPROTO 1 # endif # ifndef SHARE_V1 # define SHARE_V1 1 /* version 1 of the fair share scheduler */ # endif # if !defined(__GNUC__ ) # define UID_T int /* GNUC gets it right, ConvexC botches */ # define GID_T int /* GNUC gets it right, ConvexC botches */ # endif # if SECUREWARE # define FORK fork /* SecureWare wants the real fork! */ # else # define FORK vfork /* the rest of the OS versions don't care */ # endif #endif /* ** RISC/os 4.52 ** ** Gives a ton of warning messages, but otherwise compiles. */ #ifdef RISCOS # define HASUNSETENV 1 /* has unsetenv(3) call */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define WAITUNION 1 /* use "union wait" as wait argument type */ # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define NEEDPUTENV 1 /* need putenv(3) call */ # define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define LA_TYPE LA_INT # define LA_AVENRUN "avenrun" # define _PATH_UNIX "/unix" # undef WIFEXITED # define setpgid setpgrp extern int errno; typedef int pid_t; #define SIGFUNC_DEFINED typedef int (*sigfunc_t)(); extern char *getenv(); extern void *malloc(); # include #endif /* ** Linux 0.99pl10 and above... ** ** Thanks to, in reverse order of contact: ** ** John Kennedy ** Andrew Pam ** Florian La Roche ** Karl London ** ** Last compiled against: [06/10/96 @ 09:21:40 PM (Monday)] ** sendmail 8.8-a4 named bind-4.9.4-T4B db-1.85 ** gcc 2.7.2 libc-5.3.12 linux 2.0.0 ** ** NOTE: Override HASFLOCK as you will but, as of 1.99.6, mixed-style ** file locking is no longer allowed. In particular, make sure ** your DBM library and sendmail are both using either flock(2) ** *or* fcntl(2) file locking, but not both. */ #ifdef __linux__ # define BSD 1 /* include BSD defines */ # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASUNSETENV 1 /* has unsetenv(3) call */ # ifndef HASSNPRINTF # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # endif # define ERRLIST_PREDEFINED /* don't declare sys_errlist */ # define GIDSET_T gid_t /* from */ # define HASGETUSERSHELL 0 /* getusershell(3) broken in Slackware 2.0 */ # define IP_SRCROUTE 0 /* linux <= 1.2.8 doesn't support IP_OPTIONS */ # ifndef HASFLOCK # include # if LINUX_VERSION_CODE < 66399 # define HASFLOCK 0 /* flock(2) is broken after 0.99.13 */ # else # define HASFLOCK 1 /* flock(2) fixed after 1.3.95 */ # endif # endif # ifndef LA_TYPE # define LA_TYPE LA_PROCSTR # endif # define SFS_TYPE SFS_VFS /* use statfs() impl */ # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/var/run/sendmail.pid" # endif # define TZ_TYPE TZ_TNAME # include # undef atol /* wounded in */ #endif /* ** DELL SVR4 Issue 2.2, and others ** From Kimmo Suominen ** ** It's on #ifdef DELL_SVR4 because Solaris also gets __svr4__ ** defined, and the definitions conflict. ** ** Peter Wemm claims that the setreuid ** trick works on DELL 2.2 (SVR4.0/386 version 4.0) and ESIX 4.0.3A ** (SVR4.0/386 version 3.0). */ #ifdef DELL_SVR4 /* no changes necessary */ /* see general __svr4__ defines below */ #endif /* ** Apple A/UX 3.0 */ #ifdef _AUX_SOURCE # include # define BSD /* has BSD routines */ # define HASSETRLIMIT 0 /* ... but not setrlimit(2) */ # define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASSETVBUF 1 /* we have setvbuf(3) in libc */ # define SIGFUNC_DEFINED /* sigfunc_t already defined */ # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # ifndef LA_TYPE # define LA_TYPE LA_INT # define FSHIFT 16 # endif # define LA_AVENRUN "avenrun" # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define TZ_TYPE TZ_TZNAME # ifndef _PATH_UNIX # define _PATH_UNIX "/unix" /* should be in */ # endif # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # undef WIFEXITED # undef WEXITSTATUS #endif /* ** Encore UMAX V ** ** Not extensively tested. */ #ifdef UMAXV # define HASUNAME 1 /* use System V uname(2) system call */ # define HASSETVBUF 1 /* we have setvbuf(3) in libc */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ # define MAXPATHLEN PATH_MAX extern struct passwd *getpwent(), *getpwnam(), *getpwuid(); extern struct group *getgrent(), *getgrnam(), *getgrgid(); # undef WIFEXITED # undef WEXITSTATUS #endif /* ** Stardent Titan 3000 running TitanOS 4.2. ** ** Must be compiled in "cc -43" mode. ** ** From Kate Hedstrom . ** ** Note the tweaking below after the BSD defines are set. */ #ifdef titan # define setpgid setpgrp typedef int pid_t; # undef WIFEXITED # undef WEXITSTATUS #endif /* ** Sequent DYNIX 3.2.0 ** ** From Jim Davis . */ #ifdef sequent # define BSD 1 # define HASUNSETENV 1 # define BSD4_3 1 /* to get signal() in conf.c */ # define WAITUNION 1 # define LA_TYPE LA_FLOAT # ifdef _POSIX_VERSION # undef _POSIX_VERSION /* set in */ # endif # undef HASSETVBUF /* don't actually have setvbuf(3) */ # define setpgid setpgrp /* Have to redefine WIFEXITED to take an int, to work with waitfor() */ # undef WIFEXITED # define WIFEXITED(s) (((union wait*)&(s))->w_stopval != WSTOPPED && \ ((union wait*)&(s))->w_termsig == 0) # define WEXITSTATUS(s) (((union wait*)&(s))->w_retcode) typedef int pid_t; # define isgraph(c) (isprint(c) && (c != ' ')) # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # ifndef _PATH_UNIX # define _PATH_UNIX "/dynix" # endif # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" #endif /* ** Sequent DYNIX/ptx v2.0 (and higher) ** ** For DYNIX/ptx v1.x, undefine HASSETREUID. ** ** From Tim Wright . ** Update from Jack Woolley , 26 Dec 1995, ** for DYNIX/ptx 4.0.2. */ #ifdef _SEQUENT_ # include # define SYSTEM5 1 /* include all the System V defines */ # define HASSETSID 1 /* has POSIX setsid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASSETREUID 1 /* has setreuid(2) call */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define GIDSET_T gid_t # define LA_TYPE LA_INT # define SFS_TYPE SFS_STATFS /* use statfs() impl */ # define SPT_TYPE SPT_NONE /* don't use setproctitle */ # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif #endif /* ** Cray Unicos ** ** Ported by David L. Kensiski, Sterling Sofware */ #ifdef UNICOS # define SYSTEM5 1 /* include all the System V defines */ # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define MAXPATHLEN PATHSIZE # define LA_TYPE LA_ZERO # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ # define SFS_BAVAIL f_bfree /* alternate field name */ #endif /* ** Apollo DomainOS ** ** From Todd Martin & Don Lewis ** ** 15 Jan 1994; updated 2 Aug 1995 ** */ #ifdef apollo # define HASSETREUID 1 /* has setreuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(2) call */ # define IP_SRCROUTE 0 /* does not have */ # define SPT_TYPE SPT_NONE /* don't use setproctitle */ # define LA_TYPE LA_SUBR /* use getloadavg.c */ # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define TZ_TYPE TZ_TZNAME # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif # undef S_IFSOCK /* S_IFSOCK and S_IFIFO are the same */ # undef S_IFIFO # define S_IFIFO 0010000 # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # define RLIMIT_NEEDS_SYS_TIME_H 1 #endif /* ** UnixWare 2.x */ #ifdef UNIXWARE2 # define UNIXWARE 1 # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ #endif /* ** UnixWare 1.1.2. ** ** Updated by Petr Lampa . ** From Evan Champion . */ #ifdef UNIXWARE # include # define SYSTEM5 1 # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define HASSETREUID 1 # define HASSETSID 1 # define HASINITGROUPS 1 # define GIDSET_T gid_t # define SLEEP_T unsigned # define SFS_TYPE SFS_STATVFS # define LA_TYPE LA_ZERO # undef WIFEXITED # undef WEXITSTATUS # define _PATH_UNIX "/unix" # define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" # endif # define SYSLOG_BUFSIZE 128 #endif /* ** Intergraph CLIX 3.1 ** ** From Paul Southworth */ #ifdef CLIX # define SYSTEM5 1 /* looks like System V */ # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # endif # define DEV_BSIZE 512 /* device block size not defined */ # define GIDSET_T gid_t # undef LOG /* syslog not available */ # define NEEDFSYNC 1 /* no fsync in system library */ # define GETSHORT _getshort #endif /* ** NCR MP-RAS 2.x (SysVr4) with Wollongong TCP/IP ** ** From Kevin Darcy . */ #ifdef NCR_MP_RAS2 # include # define __svr4__ # define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ # define SYSLOG_BUFSIZE 1024 # define SPT_TYPE SPT_NONE #endif /* ** NCR MP-RAS 3.x (SysVr4) with STREAMware TCP/IP ** ** From Tom Moore */ #ifdef NCR_MP_RAS3 # define __svr4__ # define SYSLOG_BUFSIZE 1024 # define SPT_TYPE SPT_NONE #endif /* ** Tandem NonStop-UX SVR4 ** ** From Rick McCarty . */ #ifdef NonStop_UX_BXX # define __svr4__ #endif /* ** Hitachi 3050R & 3050RX Workstations running HI-UX/WE2. ** ** Tested for 1.04 and 1.03 ** From Akihiro Hashimoto ("Hash") . */ #ifdef __H3050R # define SYSTEM5 1 /* include all the System V defines */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define setreuid(r, e) setresuid(r, e, -1) # define LA_TYPE LA_FLOAT # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define HASSETVBUF /* HI-UX has no setlinebuf */ # ifndef GIDSET_T # define GIDSET_T gid_t # endif # ifndef _PATH_UNIX # define _PATH_UNIX "/HI-UX" # endif # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ # endif /* avoid m_flags conflict between db.h & sys/sysmacros.h on HIUX 3050 */ # undef m_flags # ifdef __STDC__ extern int syslog(int, char *, ...); # endif #endif /* ** Amdahl UTS System V 2.1.5 (SVr3-based) ** ** From: Janet Jackson . */ #ifdef _UTS # include # undef HASLSTAT /* has symlinks, but they cause problems */ # define NEEDFSYNC 1 /* system fsync(2) fails on non-EFS filesys */ # define SYS5SIGNALS 1 /* System V signal semantics */ # define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASINITGROUPS 1 /* has initgroups(3) function */ # define HASSETVBUF 1 /* has setvbuf(3) function */ # define HASSIGSETMASK 0 /* does not have sigsetmask(2) function */ # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* does not have getusershell(3) function */ # endif # define GIDSET_T gid_t /* type of 2nd arg to getgroups(2) isn't int */ # define LA_TYPE LA_ZERO /* doesn't have load average */ # define SFS_TYPE SFS_4ARGS /* use 4-arg statfs() */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define _PATH_UNIX "/unix" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" #endif /* ** Cray Computer Corporation's CSOS ** ** From Scott Bolte . */ #ifdef _CRAYCOM # define SYSTEM5 1 /* include all the System V defines */ # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define NEEDFSYNC 1 /* no fsync in system library */ # define MAXPATHLEN PATHSIZE # define LA_TYPE LA_ZERO # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define _POSIX_CHOWN_RESTRICTED -1 extern struct group *getgrent(), *getgrnam(), *getgrgid(); #endif /* ** Sony NEWS-OS 4.2.1R and 6.0.3 ** ** From Motonori NAKAMURA . */ #ifdef sony_news # ifndef __svr4 /* NEWS-OS 4.2.1R */ # ifndef BSD # define BSD /* has BSD routines */ # endif # define HASUNSETENV 1 /* has unsetenv(2) call */ # undef HASSETVBUF /* don't actually have setvbuf(3) */ # define WAITUNION 1 /* use "union wait" as wait argument type */ # define LA_TYPE LA_INT # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define setpgid setpgrp # undef WIFEXITED # undef WEXITSTATUS typedef int pid_t; typedef int (*sigfunc_t)(); # define SIGFUNC_DEFINED # else /* NEWS-OS 6.0.3 with /bin/cc */ # ifndef __svr4__ # define __svr4__ /* use all System V Releae 4 defines below */ # endif # define HASSETSID 1 /* has Posix setsid(2) call */ # define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */ # define LA_TYPE LA_READKSYM /* use MIOC_READKSYM ioctl */ # ifndef SPT_TYPE # define SPT_TYPE SPT_SYSMIPS /* use sysmips() (OS 6.0.2 or later) */ # endif # define GIDSET_T gid_t # undef WIFEXITED # undef WEXITSTATUS # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 1024 # endif # define _PATH_UNIX "/stand/unix" # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" # endif # endif #endif /* ** Omron LUNA/UNIOS-B 3.0, LUNA2/Mach and LUNA88K Mach ** ** From Motonori NAKAMURA . */ #ifdef luna # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # define HASUNSETENV 1 /* has unsetenv(2) call */ # define NEEDPUTENV 1 /* need putenv(3) call */ # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ # define WAITUNION 1 /* use "union wait" as wait argument type */ # ifdef uniosb # include # define NEEDVPRINTF 1 /* need a replacement for vprintf(3) */ # define LA_TYPE LA_INT # define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ # endif # ifdef luna2 # define LA_TYPE LA_SUBR # define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ # endif # ifdef luna88k # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # define LA_TYPE LA_INT # endif # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define setpgid setpgrp # undef WIFEXITED # undef WEXITSTATUS typedef int pid_t; typedef int (*sigfunc_t)(); # define SIGFUNC_DEFINED extern char *getenv(); extern int errno; # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" #endif /* ** NEC EWS-UX/V 4.2 (with /usr/ucb/cc) ** ** From Motonori NAKAMURA . */ #if defined(nec_ews_svr4) || defined(_nec_ews_svr4) # ifndef __svr4__ # define __svr4__ /* use all System V Releae 4 defines below */ # endif # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define HASSETSID 1 /* has Posix setsid(2) call */ # define LA_TYPE LA_READKSYM /* use MIOC_READSYM ioctl */ # define SFS_TYPE SFS_USTAT /* use System V ustat(2) syscall */ # define GIDSET_T gid_t # undef WIFEXITED # undef WEXITSTATUS # define NAMELISTMASK 0x7fffffff /* mask for nlist() values */ # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */ # endif #endif /* ** Fujitsu/ICL UXP/DS (For the DS/90 Series) ** ** From Diego R. Lopez . ** Additional changes from Fumio Moriya and Toshiaki Nomura of the ** Fujitsu Fresoftware gruop . */ #ifdef __uxp__ # include # include # include # define __svr4__ # define HASGETUSERSHELL 0 # define HASFLOCK 0 # if UXPDS == 10 # define HASSNPRINTF 0 /* no snprintf(3) or vsnprintf(3) */ # else # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # endif # define _PATH_UNIX "/stand/unix" # define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" # endif #endif /* ** Pyramid DC/OSx ** ** From Earle Ake . */ #ifdef DCOSx # define GIDSET_T gid_t # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif #endif /* ** Concurrent Computer Corporation Maxion ** ** From Donald R. Laster Jr. . */ #ifdef __MAXION__ # include # define __svr4__ 1 /* SVR4.2MP */ # define HASSETREUID 1 /* have setreuid(2) */ # define HASLSTAT 1 /* have lstat(2) */ # define HASSETRLIMIT 1 /* have setrlimit(2) */ # define HASGETDTABLESIZE 1 /* have getdtablesize(2) */ # define HASSNPRINTF 1 /* have snprintf(3) */ # define HASGETUSERSHELL 1 /* have getusershell(3) */ # define NOFTRUNCATE 1 /* do not have ftruncate(2) */ # define SLEEP_T unsigned # define SFS_TYPE SFS_STATVFS # define SFS_BAVAIL f_bavail # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 256 /* Use 256 bytes */ # endif # undef WUNTRACED # undef WIFEXITED # undef WIFSIGNALED # undef WIFSTOPPED # undef WEXITSTATUS # undef WTERMSIG # undef WSTOPSIG #endif /********************************************************************** ** End of Per-Operating System defines **********************************************************************/ /********************************************************************** ** More general defines **********************************************************************/ /* general BSD defines */ #ifdef BSD # define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */ # define HASSETREUID 1 /* has setreuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # ifndef IP_SRCROUTE # define IP_SRCROUTE 1 /* can check IP source routing */ # endif # ifndef HASSETRLIMIT # define HASSETRLIMIT 1 /* has setrlimit(2) call */ # endif # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # ifndef TZ_TYPE # define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone variable */ # endif #endif /* general System V Release 4 defines */ #ifdef __svr4__ # define SYSTEM5 1 # define USESETEUID 1 /* has useable seteuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define BSD_COMP 1 /* get BSD ioctl calls */ # ifndef HASSETRLIMIT # define HASSETRLIMIT 1 /* has setrlimit(2) call */ # endif # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # endif # ifndef HASFCHMOD # define HASFCHMOD 1 /* most (all?) SVr4s seem to have fchmod(2) */ # endif # ifndef _PATH_UNIX # define _PATH_UNIX "/unix" # endif # ifndef _PATH_VENDOR_CF # define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" # endif # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" # endif # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 128 # endif # ifndef SFS_TYPE # define SFS_TYPE SFS_STATVFS # endif /* SVr4 uses different routines for setjmp/longjmp with signal support */ # define jmp_buf sigjmp_buf # define setjmp(env) sigsetjmp(env, 1) # define longjmp(env, val) siglongjmp(env, val) #endif /* general System V defines */ #ifdef SYSTEM5 # include # define HASUNAME 1 /* use System V uname(2) system call */ # define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ # define HASSETVBUF 1 /* we have setvbuf(3) in libc */ # ifndef HASULIMIT # define HASULIMIT 1 /* has the ulimit(2) syscall */ # endif # ifndef LA_TYPE # ifdef MIOC_READKSYM # define LA_TYPE LA_READKSYM /* use MIOC_READKSYM ioctl */ # else # define LA_TYPE LA_INT /* assume integer load average */ # endif # endif # ifndef SFS_TYPE # define SFS_TYPE SFS_USTAT /* use System V ustat(2) syscall */ # endif # ifndef TZ_TYPE # define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ # endif # define bcopy(s, d, l) (memmove((d), (s), (l))) # define bzero(d, l) (memset((d), '\0', (l))) # define bcmp(s, d, l) (memcmp((s), (d), (l))) #endif /* general POSIX defines */ #ifdef _POSIX_VERSION # define HASSETSID 1 /* has Posix setsid(2) call */ # define HASWAITPID 1 /* has Posix waitpid(2) call */ # if _POSIX_VERSION >= 199500 && !defined(USESETEUID) # define USESETEUID 1 /* has useable seteuid(2) call */ # endif #endif /* ** Tweaking for systems that (for example) claim to be BSD or POSIX ** but don't have all the standard BSD or POSIX routines (boo hiss). */ #ifdef titan # undef HASINITGROUPS /* doesn't have initgroups(3) call */ #endif #ifdef _CRAYCOM # undef HASSETSID /* despite POSIX claim, doesn't have setsid */ #endif #ifdef ISC_UNIX # undef bcopy /* despite SystemV claim, uses BSD bcopy */ #endif #ifdef ALTOS_SYSTEM_V # undef bcopy /* despite SystemV claim, uses BSD bcopy */ # undef bzero /* despite SystemV claim, uses BSD bzero */ # undef bcmp /* despite SystemV claim, uses BSD bcmp */ #endif /* ** Due to a "feature" in some operating systems such as Ultrix 4.3 and ** HPUX 8.0, if you receive a "No route to host" message (ICMP message ** ICMP_UNREACH_HOST) on _any_ connection, all connections to that host ** are closed. Some firewalls return this error if you try to connect ** to the IDENT port (113), so you can't receive email from these hosts ** on these systems. The firewall really should use a more specific ** message such as ICMP_UNREACH_PROTOCOL or _PORT or _NET_PROHIB. If ** not explicitly set to zero above, default it on. */ #ifndef IDENTPROTO # define IDENTPROTO 1 /* use IDENT proto (RFC 1413) */ #endif #ifndef IP_SRCROUTE # define IP_SRCROUTE 1 /* Detect IP source routing */ #endif #ifndef HASGETUSERSHELL # define HASGETUSERSHELL 1 /* libc has getusershell(3) call */ #endif #ifndef NETUNIX # define NETUNIX 1 /* include unix domain support */ #endif #ifndef HASFLOCK # define HASFLOCK 0 /* assume no flock(2) support */ #endif #ifndef HASSETREUID # define HASSETREUID 0 /* assume no setreuid(2) call */ #endif #ifndef HASFCHMOD # define HASFCHMOD 0 /* assume no fchmod(2) syscall */ #endif #ifndef USESETEUID # define USESETEUID 0 /* assume no seteuid(2) call or no saved ids */ #endif #ifndef HASSETRLIMIT # define HASSETRLIMIT 0 /* assume no setrlimit(2) support */ #endif #ifndef HASULIMIT # define HASULIMIT 0 /* assume no ulimit(2) support */ #endif #ifndef OLD_NEWDB # define OLD_NEWDB 0 /* assume newer version of newdb */ #endif #ifndef SECUREWARE # define SECUREWARE 0 /* assume no SecureWare C2 auditing hooks */ #endif /* heuristic setting of HASSETSIGMASK; can override above */ #ifndef HASSIGSETMASK # ifdef SIGVTALRM # define HASSETSIGMASK 1 # else # define HASSETSIGMASK 0 # endif #endif /* ** If no type for argument two of getgroups call is defined, assume ** it's an integer -- unfortunately, there seem to be several choices ** here. */ #ifndef GIDSET_T # define GIDSET_T int #endif #ifndef UID_T # define UID_T uid_t #endif #ifndef GID_T # define GID_T gid_t #endif #ifndef SIZE_T # define SIZE_T size_t +#endif + +#ifndef MODE_T +# define MODE_T mode_t #endif #ifndef ARGV_T # define ARGV_T char ** #endif /********************************************************************** ** Remaining definitions should never have to be changed. They are ** primarily to provide back compatibility for older systems -- for ** example, it includes some POSIX compatibility definitions **********************************************************************/ /* System 5 compatibility */ #ifndef S_ISREG # define S_ISREG(foo) ((foo & S_IFMT) == S_IFREG) #endif #ifndef S_ISDIR # define S_ISDIR(foo) ((foo & S_IFMT) == S_IFDIR) #endif #if !defined(S_ISLNK) && defined(S_IFLNK) # define S_ISLNK(foo) ((foo & S_IFMT) == S_IFLNK) #endif #ifndef S_IWUSR # define S_IWUSR 0200 #endif #ifndef S_IWGRP # define S_IWGRP 0020 #endif #ifndef S_IWOTH # define S_IWOTH 0002 #endif /* ** Older systems don't have this error code -- it should be in ** /usr/include/sysexits.h. */ # ifndef EX_CONFIG # define EX_CONFIG 78 /* configuration error */ # endif /* pseudo-code used in server SMTP */ # define EX_QUIT 22 /* drop out of server immediately */ /* ** These are used in a few cases where we need some special ** error codes, but where the system doesn't provide something ** reasonable. They are printed in errstring. */ #ifndef E_PSEUDOBASE # define E_PSEUDOBASE 256 #endif #define EOPENTIMEOUT (E_PSEUDOBASE + 0) /* timeout on open */ #define E_DNSBASE (E_PSEUDOBASE + 20) /* base for DNS h_errno */ /* type of arbitrary pointer */ #ifndef ARBPTR_T # define ARBPTR_T void * #endif #ifndef __P # include "cdefs.h" #endif #if NAMED_BIND # include # ifdef __svr4__ # ifdef NOERROR # undef NOERROR /* avoid compiler conflict with stream.h */ # endif # endif # ifndef __ksr__ extern int h_errno; # endif #endif /* ** The size of an IP address -- can't use sizeof because of problems ** on Crays, where everything is 64 bits. This will break if/when ** IP addresses are expanded to eight bytes. */ #ifndef INADDRSZ # define INADDRSZ 4 #endif /* ** The size of various known types -- for reading network protocols. ** Again, we can't use sizeof because of compiler randomness. */ #ifndef INT16SZ # define INT16SZ 2 #endif #ifndef INT32SZ # define INT32SZ 4 #endif /* ** Do some required dependencies */ #if NETINET || NETISO # define SMTP 1 /* enable user and server SMTP */ # define QUEUE 1 /* enable queueing */ # define DAEMON 1 /* include the daemon (requires IPC & SMTP) */ #endif /* ** Arrange to use either varargs or stdargs */ # ifdef __STDC__ # include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap, f) # define VA_END va_end(ap) # else # include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap) # define VA_END va_end(ap) # endif #ifdef HASUNAME # include # ifdef newstr # undef newstr # endif #else /* ! HASUNAME */ # define NODE_LENGTH 32 struct utsname { char nodename[NODE_LENGTH+1]; }; #endif /* HASUNAME */ #if !defined(MAXHOSTNAMELEN) && !defined(_SCO_unix_) && !defined(NonStop_UX_BXX) && !defined(ALTOS_SYSTEM_V) # define MAXHOSTNAMELEN 256 #endif #if !defined(SIGCHLD) && defined(SIGCLD) # define SIGCHLD SIGCLD #endif #ifndef STDIN_FILENO # define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO # define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO # define STDERR_FILENO 2 #endif #ifndef LOCK_SH # define LOCK_SH 0x01 /* shared lock */ # define LOCK_EX 0x02 /* exclusive lock */ # define LOCK_NB 0x04 /* non-blocking lock */ # define LOCK_UN 0x08 /* unlock */ #endif #ifndef SEEK_SET # define SEEK_SET 0 # define SEEK_CUR 1 # define SEEK_END 2 #endif #ifndef SIG_ERR # define SIG_ERR ((void (*)()) -1) #endif #ifndef WEXITSTATUS # define WEXITSTATUS(st) (((st) >> 8) & 0377) #endif #ifndef WIFEXITED # define WIFEXITED(st) (((st) & 0377) == 0) #endif #ifndef SIGFUNC_DEFINED typedef void (*sigfunc_t) __P((int)); #endif /* size of syslog buffer */ #ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 1024 #endif /* ** Size of tobuf (deliver.c) ** Tweak this to match your syslog implementation. It will have to ** allow for the extra information printed. */ #ifndef TOBUFSIZE # if (SYSLOG_BUFSIZE) > 768 # define TOBUFSIZE (SYSLOG_BUFSIZE - 512) # else # define TOBUFSIZE 256 # endif #endif /* TOBUFSIZE must never be permitted to exceed MAXLINE - 128 */ #if TOBUFSIZE > (MAXLINE - 128) # undef TOBUFSIZE # define TOBUFSIZE (MAXLINE - 128) #endif /* ** Size of prescan buffer. ** Despite comments in the _sendmail_ book, this probably should ** not be changed; there are some hard-to-define dependencies. */ # define PSBUFSIZE (MAXNAME + MAXATOM) /* size of prescan buffer */ /* fork routine -- set above using #ifdef _osname_ or in Makefile */ # ifndef FORK # define FORK fork /* function to call to fork mailer */ # endif /* ** Default to using scanf in readcf. */ #ifndef SCANF # define SCANF 1 #endif Index: head/usr.sbin/sendmail/src/daemon.c =================================================================== --- head/usr.sbin/sendmail/src/daemon.c (revision 19843) +++ head/usr.sbin/sendmail/src/daemon.c (revision 19844) @@ -1,1765 +1,1775 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "sendmail.h" #ifndef lint #ifdef DAEMON -static char sccsid[] = "@(#)daemon.c 8.145 (Berkeley) 10/12/96 (with daemon mode)"; +static char sccsid[] = "@(#)daemon.c 8.148 (Berkeley) 11/8/96 (with daemon mode)"; #else -static char sccsid[] = "@(#)daemon.c 8.145 (Berkeley) 10/12/96 (without daemon mode)"; +static char sccsid[] = "@(#)daemon.c 8.148 (Berkeley) 11/8/96 (without daemon mode)"; #endif #endif /* not lint */ #ifdef DAEMON # include #if NAMED_BIND # include # ifndef NO_DATA # define NO_DATA NO_ADDRESS # endif #endif #if IP_SRCROUTE # include # include # include #endif /* ** DAEMON.C -- routines to use when running as a daemon. ** ** This entire file is highly dependent on the 4.2 BSD ** interprocess communication primitives. No attempt has ** been made to make this file portable to Version 7, ** Version 6, MPX files, etc. If you should try such a ** thing yourself, I recommend chucking the entire file ** and starting from scratch. Basic semantics are: ** ** getrequests(e) ** Opens a port and initiates a connection. ** Returns in a child. Must set InChannel and ** OutChannel appropriately. ** clrdaemon() ** Close any open files associated with getting ** the connection; this is used when running the queue, ** etc., to avoid having extra file descriptors during ** the queue run and to avoid confusing the network ** code (if it cares). ** makeconnection(host, port, outfile, infile, e) ** Make a connection to the named host on the given ** port. Set *outfile and *infile to the files ** appropriate for communication. Returns zero on ** success, else an exit status describing the ** error. ** host_map_lookup(map, hbuf, avp, pstat) ** Convert the entry in hbuf into a canonical form. */ /* ** GETREQUESTS -- open mail IPC port and get requests. ** ** Parameters: ** e -- the current envelope. ** ** Returns: ** TRUE -- if a "null server" should be used -- that is, one ** that rejects all commands. ** FALSE -- to use a normal server. ** ** Side Effects: ** Waits until some interesting activity occurs. When ** it does, a child is created to process it, and the ** parent waits for completion. Return from this ** routine is always in the child. The file pointers ** "InChannel" and "OutChannel" should be set to point ** to the communication channel. */ int DaemonSocket = -1; /* fd describing socket */ SOCKADDR DaemonAddr; /* socket for incoming */ int ListenQueueSize = 10; /* size of listen queue */ int TcpRcvBufferSize = 0; /* size of TCP receive buffer */ int TcpSndBufferSize = 0; /* size of TCP send buffer */ bool getrequests(e) ENVELOPE *e; { int t; bool refusingconnections = TRUE; FILE *pidf; int socksize; #if XDEBUG bool j_has_dot; #endif extern void reapchild(); extern int opendaemonsocket __P((bool)); /* ** Set up the address for the mailer. */ if (DaemonAddr.sin.sin_family == 0) DaemonAddr.sin.sin_family = AF_INET; if (DaemonAddr.sin.sin_addr.s_addr == 0) DaemonAddr.sin.sin_addr.s_addr = INADDR_ANY; if (DaemonAddr.sin.sin_port == 0) { register struct servent *sp; sp = getservbyname("smtp", "tcp"); if (sp == NULL) { syserr("554 service \"smtp\" unknown"); DaemonAddr.sin.sin_port = htons(25); } else DaemonAddr.sin.sin_port = sp->s_port; } /* ** Try to actually open the connection. */ if (tTd(15, 1)) printf("getrequests: port 0x%x\n", DaemonAddr.sin.sin_port); /* get a socket for the SMTP connection */ socksize = opendaemonsocket(TRUE); (void) setsignal(SIGCHLD, reapchild); /* write the pid to the log file for posterity */ pidf = fopen(PidFile, "w"); if (pidf != NULL) { extern char *CommandLineArgs; /* write the process id on line 1 */ fprintf(pidf, "%ld\n", (long) getpid()); /* line 2 contains all command line flags */ fprintf(pidf, "%s\n", CommandLineArgs); /* flush and close */ fclose(pidf); } #if XDEBUG { char jbuf[MAXHOSTNAMELEN]; expand("\201j", jbuf, sizeof jbuf, e); j_has_dot = strchr(jbuf, '.') != NULL; } #endif if (tTd(15, 1)) printf("getrequests: %d\n", DaemonSocket); for (;;) { - register int pid; + register pid_t pid; auto int lotherend; extern bool refuseconnections(); extern int getla(); /* see if we are rejecting connections */ if (refuseconnections(ntohs(DaemonAddr.sin.sin_port))) { if (DaemonSocket >= 0) { /* close socket so peer will fail quickly */ (void) close(DaemonSocket); DaemonSocket = -1; } refusingconnections = TRUE; sleep(15); continue; } /* arrange to (re)open the socket if necessary */ if (refusingconnections) { (void) opendaemonsocket(FALSE); refusingconnections = FALSE; } #if XDEBUG /* check for disaster */ { char jbuf[MAXHOSTNAMELEN]; extern void dumpstate __P((char *)); expand("\201j", jbuf, sizeof jbuf, e); if (!wordinclass(jbuf, 'w')) { dumpstate("daemon lost $j"); syslog(LOG_ALERT, "daemon process doesn't have $j in $=w; see syslog"); abort(); } else if (j_has_dot && strchr(jbuf, '.') == NULL) { dumpstate("daemon $j lost dot"); syslog(LOG_ALERT, "daemon process $j lost dot; see syslog"); abort(); } } #endif /* wait for a connection */ setproctitle("accepting connections on port %d", ntohs(DaemonAddr.sin.sin_port)); +#if 0 + /* + ** Andrew Sun claims that this will + ** fix the SVr4 problem. But it seems to have gone away, + ** so is it worth doing this? + */ + + if (SetNonBlocking(DaemonSocket, FALSE) < 0) + log an error here; +#endif do { errno = 0; lotherend = socksize; t = accept(DaemonSocket, (struct sockaddr *)&RealHostAddr, &lotherend); } while (t < 0 && errno == EINTR); if (t < 0) { syserr("getrequests: accept"); /* arrange to re-open the socket next time around */ (void) close(DaemonSocket); DaemonSocket = -1; refusingconnections = TRUE; sleep(5); continue; } /* ** Create a subprocess to process the mail. */ if (tTd(15, 2)) printf("getrequests: forking (fd = %d)\n", t); pid = fork(); if (pid < 0) { syserr("daemon: cannot fork"); sleep(10); (void) close(t); continue; } if (pid == 0) { char *p; extern void intsig(); FILE *inchannel, *outchannel; bool nullconn; /* ** CHILD -- return to caller. ** Collect verified idea of sending host. ** Verify calling user id if possible here. */ (void) setsignal(SIGCHLD, SIG_DFL); (void) setsignal(SIGHUP, intsig); (void) close(DaemonSocket); setproctitle("startup with %s", anynet_ntoa(&RealHostAddr)); /* determine host name */ p = hostnamebyanyaddr(&RealHostAddr); if (strlen(p) > (SIZE_T) MAXNAME) p[MAXNAME] = '\0'; RealHostName = newstr(p); setproctitle("startup with %s", p); if ((inchannel = fdopen(t, "r")) == NULL || (t = dup(t)) < 0 || (outchannel = fdopen(t, "w")) == NULL) { syserr("cannot open SMTP server channel, fd=%d", t); exit(0); } InChannel = inchannel; OutChannel = outchannel; DisConnected = FALSE; /* validate the connection */ HoldErrs = TRUE; nullconn = !validate_connection(&RealHostAddr, RealHostName, e); HoldErrs = FALSE; if (nullconn) break; #ifdef XLA if (!xla_host_ok(RealHostName)) { message("421 Too many SMTP sessions for this host"); exit(0); } #endif if (tTd(15, 2)) printf("getreq: returning (normal server)\n"); return FALSE; } /* parent -- keep track of children */ proc_list_add(pid); /* close the port so that others will hang (for a while) */ (void) close(t); } if (tTd(15, 2)) printf("getreq: returning (null server)\n"); return TRUE; } /* ** OPENDAEMONSOCKET -- open the SMTP socket ** ** Deals with setting all appropriate options. DaemonAddr must ** be set up in advance. ** ** Parameters: ** firsttime -- set if this is the initial open. ** ** Returns: ** Size in bytes of the daemon socket addr. ** ** Side Effects: ** Leaves DaemonSocket set to the open socket. ** Exits if the socket cannot be created. */ #define MAXOPENTRIES 10 /* maximum number of tries to open connection */ int opendaemonsocket(firsttime) bool firsttime; { int on = 1; int socksize = 0; int ntries = 0; int saveerrno; if (tTd(15, 2)) printf("opendaemonsocket()\n"); do { if (ntries > 0) sleep(5); if (firsttime || DaemonSocket < 0) { DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0); if (DaemonSocket < 0) { saveerrno = errno; syserr("opendaemonsocket: can't create server SMTP socket"); severe: # ifdef LOG if (LogLevel > 0) syslog(LOG_ALERT, "problem creating SMTP socket"); # endif /* LOG */ DaemonSocket = -1; continue; } /* turn on network debugging? */ if (tTd(15, 101)) (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on); (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on); (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on); #ifdef SO_RCVBUF if (TcpRcvBufferSize > 0) { if (setsockopt(DaemonSocket, SOL_SOCKET, SO_RCVBUF, (char *) &TcpRcvBufferSize, sizeof(TcpRcvBufferSize)) < 0) syserr("opendaemonsocket: setsockopt(SO_RCVBUF)"); } #endif switch (DaemonAddr.sa.sa_family) { # if NETINET case AF_INET: socksize = sizeof DaemonAddr.sin; break; # endif # if NETISO case AF_ISO: socksize = sizeof DaemonAddr.siso; break; # endif default: socksize = sizeof DaemonAddr; break; } if (bind(DaemonSocket, &DaemonAddr.sa, socksize) < 0) { /* probably another daemon already */ saveerrno = errno; syserr("opendaemonsocket: cannot bind"); (void) close(DaemonSocket); goto severe; } } if (!firsttime && listen(DaemonSocket, ListenQueueSize) < 0) { saveerrno = errno; syserr("opendaemonsocket: cannot listen"); (void) close(DaemonSocket); goto severe; } return socksize; } while (ntries++ < MAXOPENTRIES && transienterror(saveerrno)); syserr("!opendaemonsocket: server SMTP socket wedged: exiting"); finis(); return -1; /* avoid compiler warning on IRIX */ } /* ** CLRDAEMON -- reset the daemon connection ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** releases any resources used by the passive daemon. */ void clrdaemon() { if (DaemonSocket >= 0) (void) close(DaemonSocket); DaemonSocket = -1; } /* ** SETDAEMONOPTIONS -- set options for running the daemon ** ** Parameters: ** p -- the options line. ** ** Returns: ** none. */ void setdaemonoptions(p) register char *p; { if (DaemonAddr.sa.sa_family == AF_UNSPEC) DaemonAddr.sa.sa_family = AF_INET; while (p != NULL) { register char *f; register char *v; while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; f = p; p = strchr(p, ','); if (p != NULL) *p++ = '\0'; v = strchr(f, '='); if (v == NULL) continue; while (isascii(*++v) && isspace(*v)) continue; if (isascii(*f) && islower(*f)) *f = toupper(*f); switch (*f) { case 'F': /* address family */ if (isascii(*v) && isdigit(*v)) DaemonAddr.sa.sa_family = atoi(v); #if NETINET else if (strcasecmp(v, "inet") == 0) DaemonAddr.sa.sa_family = AF_INET; #endif #if NETISO else if (strcasecmp(v, "iso") == 0) DaemonAddr.sa.sa_family = AF_ISO; #endif #if NETNS else if (strcasecmp(v, "ns") == 0) DaemonAddr.sa.sa_family = AF_NS; #endif #if NETX25 else if (strcasecmp(v, "x.25") == 0) DaemonAddr.sa.sa_family = AF_CCITT; #endif else syserr("554 Unknown address family %s in Family=option", v); break; case 'A': /* address */ switch (DaemonAddr.sa.sa_family) { #if NETINET case AF_INET: if (isascii(*v) && isdigit(*v)) DaemonAddr.sin.sin_addr.s_addr = inet_addr(v); else { register struct hostent *hp; hp = sm_gethostbyname(v); if (hp == NULL) syserr("554 host \"%s\" unknown", v); else bcopy(hp->h_addr, &DaemonAddr.sin.sin_addr, INADDRSZ); } break; #endif default: syserr("554 Address= option unsupported for family %d", DaemonAddr.sa.sa_family); break; } break; case 'P': /* port */ switch (DaemonAddr.sa.sa_family) { short port; #if NETINET case AF_INET: if (isascii(*v) && isdigit(*v)) DaemonAddr.sin.sin_port = htons(atoi(v)); else { register struct servent *sp; sp = getservbyname(v, "tcp"); if (sp == NULL) syserr("554 service \"%s\" unknown", v); else DaemonAddr.sin.sin_port = sp->s_port; } break; #endif #if NETISO case AF_ISO: /* assume two byte transport selector */ if (isascii(*v) && isdigit(*v)) port = htons(atoi(v)); else { register struct servent *sp; sp = getservbyname(v, "tcp"); if (sp == NULL) syserr("554 service \"%s\" unknown", v); else port = sp->s_port; } bcopy((char *) &port, TSEL(&DaemonAddr.siso), 2); break; #endif default: syserr("554 Port= option unsupported for family %d", DaemonAddr.sa.sa_family); break; } break; case 'L': /* listen queue size */ ListenQueueSize = atoi(v); break; case 'S': /* send buffer size */ TcpSndBufferSize = atoi(v); break; case 'R': /* receive buffer size */ TcpRcvBufferSize = atoi(v); break; default: syserr("554 DaemonPortOptions parameter \"%s\" unknown", f); } } } /* ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. ** ** Parameters: ** host -- the name of the host. ** port -- the port number to connect to. ** mci -- a pointer to the mail connection information ** structure to be filled in. ** e -- the current envelope. ** ** Returns: ** An exit code telling whether the connection could be ** made and if not why not. ** ** Side Effects: ** none. */ static jmp_buf CtxConnectTimeout; static void connecttimeout() { errno = ETIMEDOUT; longjmp(CtxConnectTimeout, 1); } SOCKADDR CurHostAddr; /* address of current host */ int makeconnection(host, port, mci, e) char *host; u_short port; register MCI *mci; ENVELOPE *e; { register int i = 0; register int s; register struct hostent *hp = (struct hostent *)NULL; SOCKADDR addr; int sav_errno; int addrlen; bool firstconnect; EVENT *ev; /* ** Set up the address for the mailer. ** Accept "[a.b.c.d]" syntax for host name. */ #if NAMED_BIND h_errno = 0; #endif errno = 0; bzero(&CurHostAddr, sizeof CurHostAddr); SmtpPhase = mci->mci_phase = "initial connection"; CurHostName = host; if (host[0] == '[') { long hid; register char *p = strchr(host, ']'); if (p != NULL) { *p = '\0'; #if NETINET hid = inet_addr(&host[1]); if (hid == INADDR_NONE) #endif { /* try it as a host name (avoid MX lookup) */ hp = sm_gethostbyname(&host[1]); if (hp == NULL && p[-1] == '.') { #if NAMED_BIND int oldopts = _res.options; _res.options &= ~(RES_DEFNAMES|RES_DNSRCH); #endif p[-1] = '\0'; hp = sm_gethostbyname(&host[1]); p[-1] = '.'; #if NAMED_BIND _res.options = oldopts; #endif } *p = ']'; goto gothostent; } *p = ']'; } if (p == NULL) { extern char MsgBuf[]; usrerr("553 Invalid numeric domain spec \"%s\"", host); - mci->mci_status = "5.1.2"; - mci->mci_rstatus = newstr(MsgBuf); - return (EX_NOHOST); + mci_setstat(mci, EX_NOHOST, "5.1.2", MsgBuf); + return EX_NOHOST; } #if NETINET addr.sin.sin_family = AF_INET; /*XXX*/ addr.sin.sin_addr.s_addr = hid; #endif } else { /* contortion to get around SGI cc complaints */ { register char *p = &host[strlen(host) - 1]; hp = sm_gethostbyname(host); if (hp == NULL && *p == '.') { #if NAMED_BIND int oldopts = _res.options; _res.options &= ~(RES_DEFNAMES|RES_DNSRCH); #endif *p = '\0'; hp = sm_gethostbyname(host); *p = '.'; #if NAMED_BIND _res.options = oldopts; #endif } } gothostent: if (hp == NULL) { #if NAMED_BIND /* check for name server timeouts */ if (errno == ETIMEDOUT || h_errno == TRY_AGAIN || (errno == ECONNREFUSED && UseNameServer)) { - mci->mci_status = "4.4.3"; - mci->mci_rstatus = NULL; - return (EX_TEMPFAIL); + mci_setstat(mci, EX_TEMPFAIL, "4.4.3", NULL); + return EX_TEMPFAIL; } #endif + mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); return (EX_NOHOST); } addr.sa.sa_family = hp->h_addrtype; switch (hp->h_addrtype) { #if NETINET case AF_INET: bcopy(hp->h_addr, &addr.sin.sin_addr, INADDRSZ); break; #endif default: bcopy(hp->h_addr, addr.sa.sa_data, hp->h_length); break; } i = 1; } /* ** Determine the port number. */ if (port == 0) { register struct servent *sp = getservbyname("smtp", "tcp"); if (sp == NULL) { #ifdef LOG if (LogLevel > 2) syslog(LOG_ERR, "makeconnection: service \"smtp\" unknown"); #endif port = htons(25); } else port = sp->s_port; } switch (addr.sa.sa_family) { #if NETINET case AF_INET: addr.sin.sin_port = port; addrlen = sizeof (struct sockaddr_in); break; #endif #if NETISO case AF_ISO: /* assume two byte transport selector */ bcopy((char *) &port, TSEL((struct sockaddr_iso *) &addr), 2); addrlen = sizeof (struct sockaddr_iso); break; #endif default: syserr("Can't connect to address family %d", addr.sa.sa_family); + mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); return (EX_NOHOST); } /* ** Try to actually open the connection. */ #ifdef XLA /* if too many connections, don't bother trying */ if (!xla_noqueue_ok(host)) return EX_TEMPFAIL; #endif firstconnect = TRUE; for (;;) { if (tTd(16, 1)) printf("makeconnection (%s [%s])\n", host, anynet_ntoa(&addr)); /* save for logging */ CurHostAddr = addr; if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags)) { int rport = IPPORT_RESERVED - 1; s = rresvport(&rport); } else { s = socket(AF_INET, SOCK_STREAM, 0); } if (s < 0) { sav_errno = errno; syserr("makeconnection: cannot create socket"); - goto failure; +#ifdef XLA + xla_host_end(host); +#endif + mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); + return EX_TEMPFAIL; } #ifdef SO_SNDBUF if (TcpSndBufferSize > 0) { if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &TcpSndBufferSize, sizeof(TcpSndBufferSize)) < 0) syserr("makeconnection: setsockopt(SO_SNDBUF)"); } #endif if (tTd(16, 1)) printf("makeconnection: fd=%d\n", s); /* turn on network debugging? */ if (tTd(16, 101)) { int on = 1; (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on); } if (e->e_xfp != NULL) (void) fflush(e->e_xfp); /* for debugging */ errno = 0; /* for debugging */ /* ** Linux seems to hang in connect for 90 minutes (!!!). ** Time out the connect to avoid this problem. */ if (setjmp(CtxConnectTimeout) == 0) { if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0) ev = setevent(TimeOuts.to_iconnect, connecttimeout, 0); else if (TimeOuts.to_connect != 0) ev = setevent(TimeOuts.to_connect, connecttimeout, 0); else ev = NULL; if (connect(s, (struct sockaddr *) &addr, addrlen) >= 0) { if (ev != NULL) clrevent(ev); break; } } sav_errno = errno; if (ev != NULL) clrevent(ev); /* if running demand-dialed connection, try again */ if (DialDelay > 0 && firstconnect) { if (tTd(16, 1)) printf("Connect failed (%s); trying again...\n", errstring(sav_errno)); firstconnect = FALSE; sleep(DialDelay); continue; } /* couldn't connect.... figure out why */ (void) close(s); if (hp != NULL && hp->h_addr_list[i]) { if (tTd(16, 1)) printf("Connect failed (%s); trying new address....\n", errstring(sav_errno)); switch (addr.sa.sa_family) { #if NETINET case AF_INET: bcopy(hp->h_addr_list[i++], &addr.sin.sin_addr, INADDRSZ); break; #endif default: bcopy(hp->h_addr_list[i++], addr.sa.sa_data, hp->h_length); break; } continue; } - /* failure, decide if temporary or not */ - failure: + /* couldn't open connection */ #ifdef XLA xla_host_end(host); #endif - if (transienterror(sav_errno)) - return EX_TEMPFAIL; - else - { - message("%s", errstring(sav_errno)); - return (EX_UNAVAILABLE); - } + mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL); + return EX_TEMPFAIL; } /* connection ok, put it into canonical form */ if ((mci->mci_out = fdopen(s, "w")) == NULL || (s = dup(s)) < 0 || (mci->mci_in = fdopen(s, "r")) == NULL) { syserr("cannot open SMTP client channel, fd=%d", s); + mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); return EX_TEMPFAIL; } + mci_setstat(mci, EX_OK, NULL, NULL); return (EX_OK); } /* ** MYHOSTNAME -- return the name of this host. ** ** Parameters: ** hostbuf -- a place to return the name of this host. ** size -- the size of hostbuf. ** ** Returns: ** A list of aliases for this host. ** ** Side Effects: ** Adds numeric codes to $=w. */ struct hostent * myhostname(hostbuf, size) char hostbuf[]; int size; { register struct hostent *hp; if (gethostname(hostbuf, size) < 0) { (void) strcpy(hostbuf, "localhost"); } hp = sm_gethostbyname(hostbuf); if (hp == NULL) return NULL; if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL) { (void) strncpy(hostbuf, hp->h_name, size - 1); hostbuf[size - 1] = '\0'; } /* ** If there is still no dot in the name, try looking for a ** dotted alias. */ if (strchr(hostbuf, '.') == NULL) { char **ha; for (ha = hp->h_aliases; *ha != NULL; ha++) { if (strchr(*ha, '.') != NULL) { (void) strncpy(hostbuf, *ha, size - 1); hostbuf[size - 1] = '\0'; break; } } } /* ** If _still_ no dot, wait for a while and try again -- it is ** possible that some service is starting up. This can result ** in excessive delays if the system is badly configured, but ** there really isn't a way around that, particularly given that ** the config file hasn't been read at this point. ** All in all, a bit of a mess. */ if (strchr(hostbuf, '.') == NULL && !getcanonname(hostbuf, size, TRUE)) { #ifdef LOG syslog(LOG_CRIT, "My unqualified host name (%s) unknown; sleeping for retry", hostbuf); #endif message("My unqualified host name (%s) unknown; sleeping for retry", hostbuf); sleep(60); if (!getcanonname(hostbuf, size, TRUE)) { #ifdef LOG syslog(LOG_ALERT, "unable to qualify my own domain name (%s) -- using short name", hostbuf); #endif message("WARNING: unable to qualify my own domain name (%s) -- using short name", hostbuf); } } return (hp); } /* ** GETAUTHINFO -- get the real host name asociated with a file descriptor ** ** Uses RFC1413 protocol to try to get info from the other end. ** ** Parameters: ** fd -- the descriptor ** ** Returns: ** The user@host information associated with this descriptor. */ static jmp_buf CtxAuthTimeout; static void authtimeout() { longjmp(CtxAuthTimeout, 1); } char * getauthinfo(fd) int fd; { int falen; register char *p; SOCKADDR la; int lalen; register struct servent *sp; volatile int s; int i; EVENT *ev; int nleft; char ibuf[MAXNAME + 1]; static char hbuf[MAXNAME * 2 + 2]; falen = sizeof RealHostAddr; if (isatty(fd) || getpeername(fd, &RealHostAddr.sa, &falen) < 0 || falen <= 0 || RealHostAddr.sa.sa_family == 0) { (void) snprintf(hbuf, sizeof hbuf, "%s@localhost", RealUserName); if (tTd(9, 1)) printf("getauthinfo: %s\n", hbuf); return hbuf; } if (RealHostName == NULL) { /* translate that to a host name */ RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr)); if (strlen(RealHostName) > MAXNAME) RealHostName[MAXNAME - 1] = '\0'; } if (TimeOuts.to_ident == 0) goto noident; lalen = sizeof la; if (RealHostAddr.sa.sa_family != AF_INET || getsockname(fd, &la.sa, &lalen) < 0 || lalen <= 0 || la.sa.sa_family != AF_INET) { /* no ident info */ goto noident; } /* create ident query */ (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n", ntohs(RealHostAddr.sin.sin_port), ntohs(la.sin.sin_port)); /* create local address */ la.sin.sin_port = 0; /* create foreign address */ sp = getservbyname("auth", "tcp"); if (sp != NULL) RealHostAddr.sin.sin_port = sp->s_port; else RealHostAddr.sin.sin_port = htons(113); s = -1; if (setjmp(CtxAuthTimeout) != 0) { if (s >= 0) (void) close(s); goto noident; } /* put a timeout around the whole thing */ ev = setevent(TimeOuts.to_ident, authtimeout, 0); /* connect to foreign IDENT server using same address as SMTP socket */ s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { clrevent(ev); goto noident; } if (bind(s, &la.sa, sizeof la.sin) < 0 || connect(s, &RealHostAddr.sa, sizeof RealHostAddr.sin) < 0) { goto closeident; } if (tTd(9, 10)) printf("getauthinfo: sent %s", ibuf); /* send query */ if (write(s, ibuf, strlen(ibuf)) < 0) goto closeident; /* get result */ p = &ibuf[0]; nleft = sizeof ibuf - 1; while ((i = read(s, p, nleft)) > 0) { p += i; nleft -= i; *p = '\0'; if (strchr(ibuf, '\n') != NULL) break; } (void) close(s); clrevent(ev); if (i < 0 || p == &ibuf[0]) goto noident; if (*--p == '\n' && *--p == '\r') p--; *++p = '\0'; if (tTd(9, 3)) printf("getauthinfo: got %s\n", ibuf); /* parse result */ p = strchr(ibuf, ':'); if (p == NULL) { /* malformed response */ goto noident; } while (isascii(*++p) && isspace(*p)) continue; if (strncasecmp(p, "userid", 6) != 0) { /* presumably an error string */ goto noident; } p += 6; while (isascii(*p) && isspace(*p)) p++; if (*p++ != ':') { /* either useridxx or malformed response */ goto noident; } /* p now points to the OSTYPE field */ p = strchr(p, ':'); if (p == NULL) { /* malformed response */ goto noident; } /* 1413 says don't do this -- but it's broken otherwise */ while (isascii(*++p) && isspace(*p)) continue; /* p now points to the authenticated name -- copy carefully */ cleanstrcpy(hbuf, p, MAXNAME); i = strlen(hbuf); snprintf(&hbuf[i], sizeof hbuf - i, "@%s", RealHostName == NULL ? "localhost" : RealHostName); goto postident; closeident: (void) close(s); clrevent(ev); noident: if (RealHostName == NULL) { if (tTd(9, 1)) printf("getauthinfo: NULL\n"); return NULL; } snprintf(hbuf, sizeof hbuf, "%s", RealHostName); postident: #if IP_SRCROUTE /* ** Extract IP source routing information. ** ** Format of output for a connection from site a through b ** through c to d: ** loose: @site-c@site-b:site-a ** strict: !@site-c@site-b:site-a ** ** o - pointer within ipopt_list structure. ** q - pointer within ls/ss rr route data ** p - pointer to hbuf */ if (RealHostAddr.sa.sa_family == AF_INET) { int ipoptlen, j; u_char *q; u_char *o; int l; struct in_addr addr; struct ipoption ipopt; ipoptlen = sizeof ipopt; if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS, (char *) &ipopt, &ipoptlen) < 0) goto noipsr; if (ipoptlen == 0) goto noipsr; o = (u_char *) ipopt.ipopt_list; while (o != NULL && o < (u_char *) &ipopt + ipoptlen) { switch (*o) { case IPOPT_EOL: o = NULL; break; case IPOPT_NOP: o++; break; case IPOPT_SSRR: case IPOPT_LSRR: p = &hbuf[strlen(hbuf)]; l = sizeof hbuf - (hbuf - p) - 6; snprintf(p, SPACELEFT(hbuf, p), " [%s@%.*s", *o == IPOPT_SSRR ? "!" : "", l > 240 ? 120 : l / 2, inet_ntoa(ipopt.ipopt_dst)); i = strlen(p); p += i; l -= strlen(p); /* o[1] is option length */ j = *++o / sizeof(struct in_addr) - 1; /* q skips length and router pointer to data */ q = o + 2; for ( ; j >= 0; j--) { memcpy(&addr, q, sizeof(addr)); snprintf(p, SPACELEFT(hbuf, p), "%c%.*s", j != 0 ? '@' : ':', l > 240 ? 120 : j == 0 ? l : l / 2, inet_ntoa(addr)); i = strlen(p); p += i; l -= i + 1; q += sizeof(struct in_addr); } o += *o; break; default: /* Skip over option */ o += o[1]; break; } } snprintf(p, SPACELEFT(hbuf, p), "]"); goto postipsr; } #endif noipsr: if (RealHostName != NULL && RealHostName[0] != '[') { p = &hbuf[strlen(hbuf)]; (void) snprintf(p, SPACELEFT(hbuf, p), " [%.100s]", anynet_ntoa(&RealHostAddr)); } postipsr: if (tTd(9, 1)) printf("getauthinfo: %s\n", hbuf); return hbuf; } /* ** HOST_MAP_LOOKUP -- turn a hostname into canonical form ** ** Parameters: ** map -- a pointer to this map (unused). ** name -- the (presumably unqualified) hostname. ** av -- unused -- for compatibility with other mapping ** functions. ** statp -- an exit status (out parameter) -- set to ** EX_TEMPFAIL if the name server is unavailable. ** ** Returns: ** The mapping, if found. ** NULL if no mapping found. ** ** Side Effects: ** Looks up the host specified in hbuf. If it is not ** the canonical name for that host, return the canonical ** name. */ char * host_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { register struct hostent *hp; struct in_addr in_addr; char *cp; register STAB *s; char hbuf[MAXNAME + 1]; /* ** See if we have already looked up this name. If so, just ** return it. */ s = stab(name, ST_NAMECANON, ST_ENTER); if (bitset(NCF_VALID, s->s_namecanon.nc_flags)) { if (tTd(9, 1)) printf("host_map_lookup(%s) => CACHE %s\n", name, s->s_namecanon.nc_cname == NULL ? "NULL" : s->s_namecanon.nc_cname); errno = s->s_namecanon.nc_errno; #if NAMED_BIND h_errno = s->s_namecanon.nc_herrno; #endif *statp = s->s_namecanon.nc_stat; if (*statp == EX_TEMPFAIL) { CurEnv->e_status = "4.4.3"; message("851 %s: Name server timeout", shortenstring(name, 33)); } return s->s_namecanon.nc_cname; } /* ** If we are running without a regular network connection (usually ** dial-on-demand) and we are just queueing, we want to avoid DNS ** lookups because those could try to connect to a server. */ if (CurEnv->e_sendmode == SM_DEFER) { if (tTd(9, 1)) printf("host_map_lookup(%s) => DEFERRED\n", name); *statp = EX_TEMPFAIL; return NULL; } /* ** If first character is a bracket, then it is an address ** lookup. Address is copied into a temporary buffer to ** strip the brackets and to preserve name if address is ** unknown. */ if (*name != '[') { if (tTd(9, 1)) printf("host_map_lookup(%s) => ", name); s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */ snprintf(hbuf, sizeof hbuf, "%s", name); if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX)) { if (tTd(9, 1)) printf("%s\n", hbuf); if (bitset(MF_MATCHONLY, map->map_mflags)) { cp = map_rewrite(map, name, strlen(name), av); s->s_namecanon.nc_cname = newstr(hbuf); } else { cp = map_rewrite(map, hbuf, strlen(hbuf), av); s->s_namecanon.nc_cname = newstr(cp); } return cp; } else { s->s_namecanon.nc_errno = errno; #if NAMED_BIND s->s_namecanon.nc_herrno = h_errno; if (tTd(9, 1)) printf("FAIL (%d)\n", h_errno); switch (h_errno) { case TRY_AGAIN: if (UseNameServer) { CurEnv->e_status = "4.4.3"; message("851 %s: Name server timeout", shortenstring(name, 33)); } *statp = EX_TEMPFAIL; break; case HOST_NOT_FOUND: case NO_DATA: *statp = EX_NOHOST; break; case NO_RECOVERY: *statp = EX_SOFTWARE; break; default: *statp = EX_UNAVAILABLE; break; } #else if (tTd(9, 1)) printf("FAIL\n"); *statp = EX_NOHOST; #endif s->s_namecanon.nc_stat = *statp; return NULL; } } if ((cp = strchr(name, ']')) == NULL) return (NULL); *cp = '\0'; in_addr.s_addr = inet_addr(&name[1]); /* nope -- ask the name server */ hp = sm_gethostbyaddr((char *)&in_addr, INADDRSZ, AF_INET); s->s_namecanon.nc_errno = errno; #if NAMED_BIND s->s_namecanon.nc_herrno = h_errno; #endif s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */ if (hp == NULL) { s->s_namecanon.nc_stat = *statp = EX_NOHOST; return (NULL); } /* found a match -- copy out */ cp = map_rewrite(map, (char *) hp->h_name, strlen(hp->h_name), av); s->s_namecanon.nc_stat = *statp = EX_OK; s->s_namecanon.nc_cname = newstr(cp); return cp; } /* ** ANYNET_NTOA -- convert a network address to printable form. ** ** Parameters: ** sap -- a pointer to a sockaddr structure. ** ** Returns: ** A printable version of that sockaddr. */ #if NETLINK # include #endif char * anynet_ntoa(sap) register SOCKADDR *sap; { register char *bp; register char *ap; int l; static char buf[100]; /* check for null/zero family */ if (sap == NULL) return "NULLADDR"; if (sap->sa.sa_family == 0) return "0"; switch (sap->sa.sa_family) { #if NETUNIX case AF_UNIX: if (sap->sunix.sun_path[0] != '\0') snprintf(buf, sizeof buf, "[UNIX: %.64s]", sap->sunix.sun_path); else snprintf(buf, sizeof buf, "[UNIX: localhost]"); return buf; #endif #if NETINET case AF_INET: return inet_ntoa(sap->sin.sin_addr); #endif #if NETLINK case AF_LINK: snprintf(buf, sizeof buf, "[LINK: %s]", link_ntoa((struct sockaddr_dl *) &sap->sa)); return buf; #endif default: /* this case is needed when nothing is #defined */ /* in order to keep the switch syntactically correct */ break; } /* unknown family -- just dump bytes */ (void) snprintf(buf, sizeof buf, "Family %d: ", sap->sa.sa_family); bp = &buf[strlen(buf)]; ap = sap->sa.sa_data; for (l = sizeof sap->sa.sa_data; --l >= 0; ) { (void) snprintf(bp, SPACELEFT(buf, bp), "%02x:", *ap++ & 0377); bp += 3; } *--bp = '\0'; return buf; } /* ** HOSTNAMEBYANYADDR -- return name of host based on address ** ** Parameters: ** sap -- SOCKADDR pointer ** ** Returns: ** text representation of host name. ** ** Side Effects: ** none. */ char * hostnamebyanyaddr(sap) register SOCKADDR *sap; { register struct hostent *hp; int saveretry; #if NAMED_BIND /* shorten name server timeout to avoid higher level timeouts */ saveretry = _res.retry; _res.retry = 3; #endif /* NAMED_BIND */ switch (sap->sa.sa_family) { #if NETINET case AF_INET: hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr, INADDRSZ, AF_INET); break; #endif #if NETISO case AF_ISO: hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr, sizeof sap->siso.siso_addr, AF_ISO); break; #endif case AF_UNIX: hp = NULL; break; default: hp = sm_gethostbyaddr(sap->sa.sa_data, sizeof sap->sa.sa_data, sap->sa.sa_family); break; } #if NAMED_BIND _res.retry = saveretry; #endif /* NAMED_BIND */ if (hp != NULL) return (char *) hp->h_name; else { /* produce a dotted quad */ static char buf[203]; (void) snprintf(buf, sizeof buf, "[%.200s]", anynet_ntoa(sap)); return buf; } } # else /* DAEMON */ /* code for systems without sophisticated networking */ /* ** MYHOSTNAME -- stub version for case of no daemon code. ** ** Can't convert to upper case here because might be a UUCP name. ** ** Mark, you can change this to be anything you want...... */ char ** myhostname(hostbuf, size) char hostbuf[]; int size; { register FILE *f; hostbuf[0] = '\0'; f = fopen("/usr/include/whoami", "r"); if (f != NULL) { (void) fgets(hostbuf, size, f); fixcrlf(hostbuf, TRUE); (void) fclose(f); } return (NULL); } /* ** GETAUTHINFO -- get the real host name asociated with a file descriptor ** ** Parameters: ** fd -- the descriptor ** ** Returns: ** The host name associated with this descriptor, if it can ** be determined. ** NULL otherwise. ** ** Side Effects: ** none */ char * getauthinfo(fd) int fd; { return NULL; } /* ** MAPHOSTNAME -- turn a hostname into canonical form ** ** Parameters: ** map -- a pointer to the database map. ** name -- a buffer containing a hostname. ** avp -- a pointer to a (cf file defined) argument vector. ** statp -- an exit status (out parameter). ** ** Returns: ** mapped host name ** FALSE otherwise. ** ** Side Effects: ** Looks up the host specified in name. If it is not ** the canonical name for that host, replace it with ** the canonical name. If the name is unknown, or it ** is already the canonical name, leave it unchanged. */ /*ARGSUSED*/ char * host_map_lookup(map, name, avp, statp) MAP *map; char *name; char **avp; char *statp; { register struct hostent *hp; hp = sm_gethostbyname(name); if (hp != NULL) return hp->h_name; *statp = EX_NOHOST; return NULL; } #endif /* DAEMON */ Index: head/usr.sbin/sendmail/src/deliver.c =================================================================== --- head/usr.sbin/sendmail/src/deliver.c (revision 19843) +++ head/usr.sbin/sendmail/src/deliver.c (revision 19844) @@ -1,3311 +1,3373 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)deliver.c 8.246 (Berkeley) 10/17/96"; +static char sccsid[] = "@(#)deliver.c 8.251 (Berkeley) 11/11/96"; #endif /* not lint */ #include "sendmail.h" #include #if NAMED_BIND #include extern int h_errno; #endif #ifdef SMTP extern char SmtpError[]; #endif /* ** SENDALL -- actually send all the messages. ** ** Parameters: ** e -- the envelope to send. ** mode -- the delivery mode to use. If SM_DEFAULT, use ** the current e->e_sendmode. ** ** Returns: ** none. ** ** Side Effects: ** Scans the send lists and sends everything it finds. ** Delivers any appropriate error messages. ** If we are running in a non-interactive mode, takes the ** appropriate action. */ void sendall(e, mode) ENVELOPE *e; int mode; { register ADDRESS *q; char *owner; int otherowners; register ENVELOPE *ee; ENVELOPE *splitenv = NULL; bool oldverbose = Verbose; bool somedeliveries = FALSE; - int pid; + pid_t pid; extern void sendenvelope(); /* ** If we have had global, fatal errors, don't bother sending ** the message at all if we are in SMTP mode. Local errors ** (e.g., a single address failing) will still cause the other ** addresses to be sent. */ if (bitset(EF_FATALERRS, e->e_flags) && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) { e->e_flags |= EF_CLRQUEUE; return; } /* determine actual delivery mode */ CurrentLA = getla(); if (mode == SM_DEFAULT) { mode = e->e_sendmode; if (mode != SM_VERIFY && mode != SM_DEFER && shouldqueue(e->e_msgpriority, e->e_ctime)) mode = SM_QUEUE; } if (tTd(13, 1)) { extern void printenvflags(); printf("\n===== SENDALL: mode %c, id %s, e_from ", mode, e->e_id); printaddr(&e->e_from, FALSE); printf("\te_flags = "); printenvflags(e); printf("sendqueue:\n"); printaddr(e->e_sendqueue, TRUE); } /* ** Do any preprocessing necessary for the mode we are running. ** Check to make sure the hop count is reasonable. ** Delete sends to the sender in mailing lists. */ CurEnv = e; if (tTd(62, 1)) checkfds(NULL); if (e->e_hopcount > MaxHopCount) { errno = 0; #ifdef QUEUE queueup(e, mode == SM_QUEUE || mode == SM_DEFER); #endif e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; syserr("554 Too many hops %d (%d max): from %s via %s, to %s", e->e_hopcount, MaxHopCount, e->e_from.q_paddr, RealHostName == NULL ? "localhost" : RealHostName, e->e_sendqueue->q_paddr); e->e_sendqueue->q_status = "5.4.6"; return; } /* ** Do sender deletion. ** ** If the sender has the QQUEUEUP flag set, skip this. ** This can happen if the name server is hosed when you ** are trying to send mail. The result is that the sender ** is instantiated in the queue as a recipient. */ if (!bitset(EF_METOO, e->e_flags) && !bitset(QQUEUEUP, e->e_from.q_flags)) { if (tTd(13, 5)) { printf("sendall: QDONTSEND "); printaddr(&e->e_from, FALSE); } e->e_from.q_flags |= QDONTSEND; (void) recipient(&e->e_from, &e->e_sendqueue, 0, e); } /* ** Handle alias owners. ** ** We scan up the q_alias chain looking for owners. ** We discard owners that are the same as the return path. */ for (q = e->e_sendqueue; q != NULL; q = q->q_next) { register struct address *a; for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias) continue; if (a != NULL) q->q_owner = a->q_owner; if (q->q_owner != NULL && !bitset(QDONTSEND, q->q_flags) && strcmp(q->q_owner, e->e_from.q_paddr) == 0) q->q_owner = NULL; } if (tTd(13, 25)) { printf("\nAfter first owner pass, sendq =\n"); printaddr(e->e_sendqueue, TRUE); } owner = ""; otherowners = 1; while (owner != NULL && otherowners > 0) { if (tTd(13, 28)) printf("owner = \"%s\", otherowners = %d\n", owner, otherowners); owner = NULL; otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0; for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (tTd(13, 30)) { printf("Checking "); printaddr(q, FALSE); } if (bitset(QDONTSEND, q->q_flags)) { if (tTd(13, 30)) printf(" ... QDONTSEND\n"); continue; } if (tTd(13, 29) && !tTd(13, 30)) { printf("Checking "); printaddr(q, FALSE); } if (q->q_owner != NULL) { if (owner == NULL) { if (tTd(13, 40)) printf(" ... First owner = \"%s\"\n", q->q_owner); owner = q->q_owner; } else if (owner != q->q_owner) { if (strcmp(owner, q->q_owner) == 0) { if (tTd(13, 40)) printf(" ... Same owner = \"%s\"\n", owner); /* make future comparisons cheap */ q->q_owner = owner; } else { if (tTd(13, 40)) printf(" ... Another owner \"%s\"\n", q->q_owner); otherowners++; } owner = q->q_owner; } else if (tTd(13, 40)) printf(" ... Same owner = \"%s\"\n", owner); } else { if (tTd(13, 40)) printf(" ... Null owner\n"); otherowners++; } /* ** If this mailer is expensive, and if we don't ** want to make connections now, just mark these ** addresses and return. This is useful if we ** want to batch connections to reduce load. This ** will cause the messages to be queued up, and a ** daemon will come along to send the messages later. */ if (bitset(QBADADDR|QQUEUEUP, q->q_flags)) { if (tTd(13, 30)) printf(" ... QBADADDR|QQUEUEUP\n"); continue; } if (NoConnect && !Verbose && bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) { if (tTd(13, 30)) printf(" ... expensive\n"); q->q_flags |= QQUEUEUP; } else { if (tTd(13, 30)) printf(" ... deliverable\n"); somedeliveries = TRUE; } } if (owner != NULL && otherowners > 0) { extern HDR *copyheader(); extern ADDRESS *copyqueue(); /* ** Split this envelope into two. */ ee = (ENVELOPE *) xalloc(sizeof(ENVELOPE)); *ee = *e; ee->e_id = NULL; (void) queuename(ee, '\0'); if (tTd(13, 1)) printf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", e->e_id, ee->e_id, owner, otherowners); ee->e_header = copyheader(e->e_header); ee->e_sendqueue = copyqueue(e->e_sendqueue); ee->e_errorqueue = copyqueue(e->e_errorqueue); ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); ee->e_flags |= EF_NORECEIPT; setsender(owner, ee, NULL, TRUE); if (tTd(13, 5)) { printf("sendall(split): QDONTSEND "); printaddr(&ee->e_from, FALSE); } ee->e_from.q_flags |= QDONTSEND; ee->e_dfp = NULL; ee->e_xfp = NULL; ee->e_errormode = EM_MAIL; ee->e_sibling = splitenv; splitenv = ee; for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (q->q_owner == owner) { q->q_flags |= QDONTSEND; q->q_flags &= ~(QQUEUEUP|QBADADDR); if (tTd(13, 6)) printf("\t... stripping %s from original envelope\n", q->q_paddr); } } for (q = ee->e_sendqueue; q != NULL; q = q->q_next) { if (q->q_owner != owner) { q->q_flags |= QDONTSEND; q->q_flags &= ~(QQUEUEUP|QBADADDR); if (tTd(13, 6)) printf("\t... dropping %s from cloned envelope\n", q->q_paddr); } else { /* clear DSN parameters */ q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; if (tTd(13, 6)) printf("\t... moving %s to cloned envelope\n", q->q_paddr); } } if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) { char df1buf[20], df2buf[20]; ee->e_dfp = NULL; snprintf(df1buf, sizeof df1buf, "%s", queuename(e, 'd')); snprintf(df2buf, sizeof df2buf, "%s", queuename(ee, 'd')); if (link(df1buf, df2buf) < 0) { int saverrno = errno; syserr("sendall: link(%s, %s)", df1buf, df2buf); if (saverrno == EEXIST) { if (unlink(df2buf) < 0) { syserr("!sendall: unlink(%s): permanent", df2buf); /*NOTREACHED*/ } if (link(df1buf, df2buf) < 0) { syserr("!sendall: link(%s, %s): permanent", df1buf, df2buf); /*NOTREACHED*/ } } } } #ifdef LOG if (LogLevel > 4) syslog(LOG_INFO, "%s: clone %s, owner=%s", ee->e_id, e->e_id, owner); #endif } } if (owner != NULL) { setsender(owner, e, NULL, TRUE); if (tTd(13, 5)) { printf("sendall(owner): QDONTSEND "); printaddr(&e->e_from, FALSE); } e->e_from.q_flags |= QDONTSEND; e->e_errormode = EM_MAIL; e->e_flags |= EF_NORECEIPT; e->e_flags &= ~EF_FATALERRS; } /* if nothing to be delivered, just queue up everything */ if (!somedeliveries && mode != SM_QUEUE && mode != SM_DEFER && mode != SM_VERIFY) { if (tTd(13, 29)) printf("No deliveries: auto-queuing\n"); mode = SM_QUEUE; } # ifdef QUEUE if ((mode == SM_QUEUE || mode == SM_DEFER || mode == SM_FORK || (mode != SM_VERIFY && SuperSafe)) && (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) { /* be sure everything is instantiated in the queue */ queueup(e, mode == SM_QUEUE || mode == SM_DEFER); for (ee = splitenv; ee != NULL; ee = ee->e_sibling) queueup(ee, mode == SM_QUEUE || mode == SM_DEFER); } #endif /* QUEUE */ if (tTd(62, 10)) checkfds("after envelope splitting"); /* ** If we belong in background, fork now. */ if (tTd(13, 20)) { printf("sendall: final mode = %c\n", mode); if (tTd(13, 21)) { printf("\n================ Final Send Queue(s) =====================\n"); printf("\n *** Envelope %s, e_from=%s ***\n", e->e_id, e->e_from.q_paddr); printaddr(e->e_sendqueue, TRUE); for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { printf("\n *** Envelope %s, e_from=%s ***\n", ee->e_id, ee->e_from.q_paddr); printaddr(ee->e_sendqueue, TRUE); } printf("==========================================================\n\n"); } } switch (mode) { case SM_VERIFY: Verbose = TRUE; break; case SM_QUEUE: case SM_DEFER: queueonly: if (e->e_nrcpts > 0) e->e_flags |= EF_INQUEUE; return; case SM_FORK: if (e->e_xfp != NULL) (void) fflush(e->e_xfp); # if !HASFLOCK /* ** Since fcntl locking has the interesting semantic that ** the lock is owned by a process, not by an open file ** descriptor, we have to flush this to the queue, and ** then restart from scratch in the child. */ { /* save id for future use */ char *qid = e->e_id; /* now drop the envelope in the parent */ e->e_flags |= EF_INQUEUE; dropenvelope(e, FALSE); /* arrange to reacquire lock after fork */ e->e_id = qid; } for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { /* save id for future use */ char *qid = ee->e_id; /* drop envelope in parent */ ee->e_flags |= EF_INQUEUE; dropenvelope(ee, FALSE); /* and save qid for reacquisition */ ee->e_id = qid; } # endif /* !HASFLOCK */ pid = fork(); if (pid < 0) { goto queueonly; } else if (pid > 0) { # if HASFLOCK /* be sure we leave the temp files to our child */ /* can't call unlockqueue to avoid unlink of xfp */ if (e->e_lockfp != NULL) (void) xfclose(e->e_lockfp, "sendenvelope lockfp", e->e_id); e->e_lockfp = NULL; /* close any random open files in the envelope */ closexscript(e); if (e->e_dfp != NULL) (void) xfclose(e->e_dfp, "sendenvelope dfp", e->e_id); e->e_dfp = NULL; e->e_flags &= ~EF_HAS_DF; # endif /* make sure the parent doesn't own the envelope */ e->e_id = NULL; /* catch intermediate zombie */ (void) waitfor(pid); return; } /* double fork to avoid zombies */ pid = fork(); if (pid > 0) exit(EX_OK); /* be sure we are immune from the terminal */ disconnect(1, e); /* prevent parent from waiting if there was an error */ if (pid < 0) { e->e_flags |= EF_INQUEUE; finis(); } /* ** Close any cached connections. ** ** We don't send the QUIT protocol because the parent ** still knows about the connection. ** ** This should only happen when delivering an error ** message. */ mci_flush(FALSE, NULL); # if HASFLOCK break; # else /* ** Now reacquire and run the various queue files. */ for (ee = splitenv; ee != NULL; ee = e->e_sibling) (void) dowork(ee->e_id, FALSE, FALSE, ee); (void) dowork(e->e_id, FALSE, FALSE, e); finis(); # endif /* !HASFLOCK */ } sendenvelope(e, mode); dropenvelope(e, TRUE); for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { CurEnv = ee; if (mode != SM_VERIFY) openxscript(ee); sendenvelope(ee, mode); dropenvelope(ee, TRUE); } CurEnv = e; Verbose = oldverbose; if (mode == SM_FORK) finis(); } void sendenvelope(e, mode) register ENVELOPE *e; char mode; { register ADDRESS *q; bool didany; if (tTd(13, 10)) printf("sendenvelope(%s) e_flags=0x%x\n", e->e_id == NULL ? "[NOQUEUE]" : e->e_id, e->e_flags); #ifdef LOG if (LogLevel > 80) syslog(LOG_DEBUG, "%s: sendenvelope, flags=0x%x", e->e_id == NULL ? "[NOQUEUE]" : e->e_id, e->e_flags); #endif /* ** If we have had global, fatal errors, don't bother sending ** the message at all if we are in SMTP mode. Local errors ** (e.g., a single address failing) will still cause the other ** addresses to be sent. */ if (bitset(EF_FATALERRS, e->e_flags) && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) { e->e_flags |= EF_CLRQUEUE; return; } /* ** Run through the list and send everything. ** ** Set EF_GLOBALERRS so that error messages during delivery ** result in returned mail. */ e->e_nsent = 0; e->e_flags |= EF_GLOBALERRS; define(macid("{envid}", NULL), e->e_envid, e); define(macid("{bodytype}", NULL), e->e_bodytype, e); didany = FALSE; /* now run through the queue */ for (q = e->e_sendqueue; q != NULL; q = q->q_next) { #if XDEBUG char wbuf[MAXNAME + 20]; (void) snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", MAXNAME, q->q_paddr); checkfd012(wbuf); #endif if (mode == SM_VERIFY) { e->e_to = q->q_paddr; if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) { if (q->q_host != NULL && q->q_host[0] != '\0') message("deliverable: mailer %s, host %s, user %s", q->q_mailer->m_name, q->q_host, q->q_user); else message("deliverable: mailer %s, user %s", q->q_mailer->m_name, q->q_user); } } else if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) { extern int deliver __P((ENVELOPE *, ADDRESS *)); # ifdef QUEUE /* ** Checkpoint the send list every few addresses */ if (e->e_nsent >= CheckpointInterval) { queueup(e, FALSE); e->e_nsent = 0; } # endif /* QUEUE */ (void) deliver(e, q); didany = TRUE; } } if (didany) { e->e_dtime = curtime(); e->e_ntries++; } #if XDEBUG checkfd012("end of sendenvelope"); #endif } /* ** DOFORK -- do a fork, retrying a couple of times on failure. ** ** This MUST be a macro, since after a vfork we are running ** two processes on the same stack!!! ** ** Parameters: ** none. ** ** Returns: ** From a macro??? You've got to be kidding! ** ** Side Effects: ** Modifies the ==> LOCAL <== variable 'pid', leaving: ** pid of child in parent, zero in child. ** -1 on unrecoverable error. ** ** Notes: ** I'm awfully sorry this looks so awful. That's ** vfork for you..... */ # define NFORKTRIES 5 # ifndef FORK # define FORK fork # endif # define DOFORK(fORKfN) \ {\ register int i;\ \ for (i = NFORKTRIES; --i >= 0; )\ {\ pid = fORKfN();\ if (pid >= 0)\ break;\ if (i > 0)\ sleep((unsigned) NFORKTRIES - i);\ }\ } /* ** DOFORK -- simple fork interface to DOFORK. ** ** Parameters: ** none. ** ** Returns: ** pid of child in parent. ** zero in child. ** -1 on error. ** ** Side Effects: ** returns twice, once in parent and once in child. */ int dofork() { - register int pid = -1; + register pid_t pid = -1; DOFORK(fork); return (pid); } /* ** DELIVER -- Deliver a message to a list of addresses. ** ** This routine delivers to everyone on the same host as the ** user on the head of the list. It is clever about mailers ** that don't handle multiple users. It is NOT guaranteed ** that it will deliver to all these addresses however -- so ** deliver should be called once for each address on the ** list. ** ** Parameters: ** e -- the envelope to deliver. ** firstto -- head of the address list to deliver to. ** ** Returns: ** zero -- successfully delivered. ** else -- some failure, see ExitStat for more info. ** ** Side Effects: ** The standard input is passed off to someone. */ #ifndef NO_UID # define NO_UID ((uid_t) -1) #endif #ifndef NO_GID # define NO_GID ((gid_t) -1) #endif int deliver(e, firstto) register ENVELOPE *e; ADDRESS *firstto; { char *host; /* host being sent to */ char *user; /* user being sent to */ char **pvp; register char **mvp; register char *p; register MAILER *m; /* mailer for this recipient */ ADDRESS *volatile ctladdr; ADDRESS *volatile contextaddr = NULL; register MCI *volatile mci; register ADDRESS *to = firstto; volatile bool clever = FALSE; /* running user smtp to this mailer */ ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ int rcode; /* response code */ char *firstsig; /* signature of firstto */ - int pid = -1; + pid_t pid = -1; char *volatile curhost; register volatile u_short port = 0; time_t xstart; bool suidwarn; + bool anyok; /* at least one address was OK */ int mpvect[2]; int rpvect[2]; char *pv[MAXPV+1]; char tobuf[TOBUFSIZE]; /* text line of to people */ char buf[MAXNAME + 1]; char rpathbuf[MAXNAME + 1]; /* translated return path */ extern int checkcompat(); extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int)); errno = 0; if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags)) return (0); suidwarn = geteuid() == 0; #if NAMED_BIND /* unless interactive, try twice, over a minute */ if (OpMode == MD_DAEMON || OpMode == MD_SMTP) { _res.retrans = 30; _res.retry = 2; } #endif m = to->q_mailer; host = to->q_host; CurEnv = e; /* just in case */ e->e_statmsg = NULL; #ifdef SMTP SmtpError[0] = '\0'; #endif xstart = curtime(); if (tTd(10, 1)) printf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", e->e_id, m->m_name, host, to->q_user); if (tTd(10, 100)) printopenfds(FALSE); /* ** Do initial argv setup. ** Insert the mailer name. Notice that $x expansion is ** NOT done on the mailer name. Then, if the mailer has ** a picky -f flag, we insert it as appropriate. This ** code does not check for 'pv' overflow; this places a ** manifest lower limit of 4 for MAXPV. ** The from address rewrite is expected to make ** the address relative to the other end. */ /* rewrite from address, using rewriting rules */ rcode = EX_OK; if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) p = e->e_sender; else p = e->e_from.q_paddr; p = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); if (strlen(p) >= (SIZE_T) sizeof rpathbuf) { p = shortenstring(p, 203); syserr("remotename: huge return %s", p); } snprintf(rpathbuf, sizeof rpathbuf, "%s", p); define('g', rpathbuf, e); /* translated return path */ define('h', host, e); /* to host */ Errors = 0; pvp = pv; *pvp++ = m->m_argv[0]; /* insert -f or -r flag as appropriate */ if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags))) { if (bitnset(M_FOPT, m->m_flags)) *pvp++ = "-f"; else *pvp++ = "-r"; *pvp++ = newstr(rpathbuf); } /* ** Append the other fixed parts of the argv. These run ** up to the first entry containing "$u". There can only ** be one of these, and there are only a few more slots ** in the pv after it. */ for (mvp = m->m_argv; (p = *++mvp) != NULL; ) { /* can't use strchr here because of sign extension problems */ while (*p != '\0') { if ((*p++ & 0377) == MACROEXPAND) { if (*p == 'u') break; } } if (*p != '\0') break; /* this entry is safe -- go ahead and process it */ expand(*mvp, buf, sizeof buf, e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV - 3]) { syserr("554 Too many parameters to %s before $u", pv[0]); return (-1); } } /* ** If we have no substitution for the user name in the argument ** list, we know that we must supply the names otherwise -- and ** SMTP is the answer!! */ if (*mvp == NULL) { /* running SMTP */ # ifdef SMTP clever = TRUE; *pvp = NULL; # else /* SMTP */ /* oops! we don't implement SMTP */ syserr("554 SMTP style mailer not implemented"); return (EX_SOFTWARE); # endif /* SMTP */ } /* ** At this point *mvp points to the argument with $u. We ** run through our address list and append all the addresses ** we can. If we run out of space, do not fret! We can ** always send another copy later. */ tobuf[0] = '\0'; e->e_to = tobuf; ctladdr = NULL; firstsig = hostsignature(firstto->q_mailer, firstto->q_host, e); for (; to != NULL; to = to->q_next) { /* avoid sending multiple recipients to dumb mailers */ if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags)) break; /* if already sent or not for this host, don't send */ if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags) || to->q_mailer != firstto->q_mailer || strcmp(hostsignature(to->q_mailer, to->q_host, e), firstsig) != 0) continue; /* avoid overflowing tobuf */ if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2)) break; if (tTd(10, 1)) { printf("\nsend to "); printaddr(to, FALSE); } /* compute effective uid/gid when sending */ if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) contextaddr = ctladdr = getctladdr(to); if (tTd(10, 2)) { printf("ctladdr="); printaddr(ctladdr, FALSE); } user = to->q_user; e->e_to = to->q_paddr; if (tTd(10, 5)) { printf("deliver: QDONTSEND "); printaddr(to, FALSE); } to->q_flags |= QDONTSEND; /* ** Check to see that these people are allowed to ** talk to each other. */ if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize) { e->e_flags |= EF_NO_BODY_RETN; to->q_status = "5.2.3"; usrerr("552 Message is too large; %ld bytes max", m->m_maxsize); giveresponse(EX_UNAVAILABLE, m, NULL, ctladdr, xstart, e); continue; } #if NAMED_BIND h_errno = 0; #endif /* do config file checking of compatibility */ rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, e); if (rcode == EX_OK) { /* do in-code checking */ rcode = checkcompat(to, e); } if (rcode != EX_OK) { markfailure(e, to, NULL, rcode); giveresponse(rcode, m, NULL, ctladdr, xstart, e); continue; } /* ** Strip quote bits from names if the mailer is dumb ** about them. */ if (bitnset(M_STRIPQ, m->m_flags)) { stripquotes(user); stripquotes(host); } /* hack attack -- delivermail compatibility */ if (m == ProgMailer && *user == '|') user++; /* ** If an error message has already been given, don't ** bother to send to this address. ** ** >>>>>>>>>> This clause assumes that the local mailer ** >> NOTE >> cannot do any further aliasing; that ** >>>>>>>>>> function is subsumed by sendmail. */ if (bitset(QBADADDR|QQUEUEUP, to->q_flags)) continue; /* ** See if this user name is "special". ** If the user name has a slash in it, assume that this ** is a file -- send it off without further ado. Note ** that this type of addresses is not processed along ** with the others, so we fudge on the To person. */ if (strcmp(m->m_mailer, "[FILE]") == 0) { rcode = mailfile(user, ctladdr, SFF_CREAT, e); giveresponse(rcode, m, NULL, ctladdr, xstart, e); e->e_nsent++; if (rcode == EX_OK) { to->q_flags |= QSENT; markstats(e, to); if (bitnset(M_LOCALMAILER, m->m_flags) && bitset(QPINGONSUCCESS, to->q_flags)) { to->q_flags |= QDELIVERED; to->q_status = "2.1.5"; fprintf(e->e_xfp, "%s... Successfully delivered\n", to->q_paddr); } } to->q_statdate = curtime(); continue; } /* ** Address is verified -- add this user to mailer ** argv, and add it to the print list of recipients. */ /* link together the chain of recipients */ to->q_tchain = tochain; tochain = to; /* create list of users for error messages */ (void) strcat(tobuf, ","); (void) strcat(tobuf, to->q_paddr); define('u', user, e); /* to user */ p = to->q_home; if (p == NULL && ctladdr != NULL) p = ctladdr->q_home; define('z', p, e); /* user's home */ /* ** Expand out this user into argument list. */ if (!clever) { expand(*mvp, buf, sizeof buf, e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV - 2]) { /* allow some space for trailing parms */ break; } } } /* see if any addresses still exist */ if (tobuf[0] == '\0') { define('g', (char *) NULL, e); return (0); } /* print out messages as full list */ e->e_to = tobuf + 1; /* ** Fill out any parameters after the $u parameter. */ while (!clever && *++mvp != NULL) { expand(*mvp, buf, sizeof buf, e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV]) syserr("554 deliver: pv overflow after $u for %s", pv[0]); } *pvp++ = NULL; /* ** Call the mailer. ** The argument vector gets built, pipes ** are created as necessary, and we fork & exec as ** appropriate. ** If we are running SMTP, we just need to clean up. */ /*XXX this seems a bit wierd */ if (ctladdr == NULL && m != ProgMailer && m != FileMailer && bitset(QGOODUID, e->e_from.q_flags)) ctladdr = &e->e_from; #if NAMED_BIND if (ConfigLevel < 2) _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ #endif if (tTd(11, 1)) { printf("openmailer:"); printav(pv); } errno = 0; #if NAMED_BIND h_errno = 0; #endif CurHostName = NULL; /* ** Deal with the special case of mail handled through an IPC ** connection. ** In this case we don't actually fork. We must be ** running SMTP for this to work. We will return a ** zero pid to indicate that we are running IPC. ** We also handle a debug version that just talks to stdin/out. */ curhost = NULL; SmtpPhase = NULL; mci = NULL; #if XDEBUG { char wbuf[MAXLINE]; /* make absolutely certain 0, 1, and 2 are in use */ snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", shortenstring(e->e_to, 203), m->m_name); checkfd012(wbuf); } #endif /* check for 8-bit available */ if (bitset(EF_HAS8BIT, e->e_flags) && bitnset(M_7BITS, m->m_flags) && (bitset(EF_DONT_MIME, e->e_flags) || !(bitset(MM_MIME8BIT, MimeMode) || (bitset(EF_IS_MIME, e->e_flags) && bitset(MM_CVTMIME, MimeMode))))) { usrerr("554 Cannot send 8-bit data to 7-bit destination"); rcode = EX_DATAERR; e->e_status = "5.6.3"; goto give_up; } if (tTd(62, 8)) checkfds("before delivery"); /* check for Local Person Communication -- not for mortals!!! */ if (strcmp(m->m_mailer, "[LPC]") == 0) { mci = (MCI *) xalloc(sizeof *mci); bzero((char *) mci, sizeof *mci); mci->mci_in = stdin; mci->mci_out = stdout; mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; mci->mci_mailer = m; } else if (strcmp(m->m_mailer, "[IPC]") == 0 || strcmp(m->m_mailer, "[TCP]") == 0) { #ifdef DAEMON register int i; if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') { syserr("null host name for %s mailer", m->m_mailer); rcode = EX_CONFIG; goto give_up; } CurHostName = pv[1]; curhost = hostsignature(m, pv[1], e); if (curhost == NULL || curhost[0] == '\0') { syserr("null host signature for %s", pv[1]); rcode = EX_CONFIG; goto give_up; } if (!clever) { syserr("554 non-clever IPC"); rcode = EX_CONFIG; goto give_up; } if (pv[2] != NULL) { port = htons(atoi(pv[2])); if (port == 0) { struct servent *sp = getservbyname(pv[2], "tcp"); if (sp == NULL) syserr("Service %s unknown", pv[2]); else port = sp->s_port; } } tryhost: while (*curhost != '\0') { register char *p; static char hostbuf[MAXNAME + 1]; extern int makeconnection __P((char *, u_short, MCI *, ENVELOPE *)); /* pull the next host from the signature */ p = strchr(curhost, ':'); if (p == NULL) p = (char *) &curhost[strlen(curhost)]; if (p == curhost) { syserr("deliver: null host name in signature"); curhost++; continue; } strncpy(hostbuf, curhost, p - curhost); hostbuf[p - curhost] = '\0'; if (*p != '\0') p++; curhost = p; /* see if we already know that this host is fried */ CurHostName = hostbuf; mci = mci_get(hostbuf, m); if (mci->mci_state != MCIS_CLOSED) { if (tTd(11, 1)) { printf("openmailer: "); mci_dump(mci, FALSE); } CurHostName = mci->mci_host; message("Using cached %sSMTP connection to %s via %s...", bitset(MCIF_ESMTP, mci->mci_flags) ? "E" : "", hostbuf, m->m_name); break; } mci->mci_mailer = m; if (mci->mci_exitstat != EX_OK) continue; if (mci_lock_host(mci) != EX_OK) { - mci->mci_exitstat = EX_TEMPFAIL; + mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); continue; } /* try the connection */ setproctitle("%s %s: %s", e->e_id, hostbuf, "user open"); if (port == 0) message("Connecting to %s via %s...", hostbuf, m->m_name); else message("Connecting to %s port %d via %s...", hostbuf, port, m->m_name); i = makeconnection(hostbuf, port, mci, e); mci->mci_lastuse = curtime(); mci->mci_exitstat = i; mci->mci_errno = errno; #if NAMED_BIND mci->mci_herrno = h_errno; #endif if (i == EX_OK) { mci->mci_state = MCIS_OPENING; mci_cache(mci); if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d === CONNECT %s\n", getpid(), hostbuf); break; } else { if (tTd(11, 1)) printf("openmailer: makeconnection => stat=%d, errno=%d\n", i, errno); mci_unlock_host(mci); } /* enter status of this host */ setstat(i); /* should print some message here for -v mode */ } if (mci == NULL) { syserr("deliver: no host name"); rcode = EX_SOFTWARE; goto give_up; } mci->mci_pid = 0; #else /* no DAEMON */ syserr("554 openmailer: no IPC"); if (tTd(11, 1)) printf("openmailer: NULL\n"); rcode = EX_UNAVAILABLE; goto give_up; #endif /* DAEMON */ } else { /* flush any expired connections */ (void) mci_scan(NULL); /* announce the connection to verbose listeners */ if (host == NULL || host[0] == '\0') message("Connecting to %s...", m->m_name); else message("Connecting to %s via %s...", host, m->m_name); if (TrafficLogFile != NULL) { char **av; fprintf(TrafficLogFile, "%05d === EXEC", getpid()); for (av = pv; *av != NULL; av++) fprintf(TrafficLogFile, " %s", *av); fprintf(TrafficLogFile, "\n"); } #if XDEBUG checkfd012("before creating mail pipe"); #endif /* create a pipe to shove the mail through */ if (pipe(mpvect) < 0) { syserr("%s... openmailer(%s): pipe (to mailer)", shortenstring(e->e_to, 203), m->m_name); if (tTd(11, 1)) printf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } #if XDEBUG /* make sure we didn't get one of the standard I/O files */ if (mpvect[0] < 3 || mpvect[1] < 3) { syserr("%s... openmailer(%s): bogus mpvect %d %d", shortenstring(e->e_to, 203), m->m_name, mpvect[0], mpvect[1]); printopenfds(TRUE); if (tTd(11, 1)) printf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } /* make sure system call isn't dead meat */ checkfdopen(mpvect[0], "mpvect[0]"); checkfdopen(mpvect[1], "mpvect[1]"); if (mpvect[0] == mpvect[1] || (e->e_lockfp != NULL && (mpvect[0] == fileno(e->e_lockfp) || mpvect[1] == fileno(e->e_lockfp)))) { if (e->e_lockfp == NULL) syserr("%s... openmailer(%s): overlapping mpvect %d %d", shortenstring(e->e_to, 203), m->m_name, mpvect[0], mpvect[1]); else syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", shortenstring(e->e_to, 203), m->m_name, mpvect[0], mpvect[1], fileno(e->e_lockfp)); } #endif /* if this mailer speaks smtp, create a return pipe */ #ifdef SMTP if (clever) { if (pipe(rpvect) < 0) { syserr("%s... openmailer(%s): pipe (from mailer)", shortenstring(e->e_to, 203), m->m_name); (void) close(mpvect[0]); (void) close(mpvect[1]); if (tTd(11, 1)) printf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } # if XDEBUG checkfdopen(rpvect[0], "rpvect[0]"); checkfdopen(rpvect[1], "rpvect[1]"); # endif } #endif /* ** Actually fork the mailer process. ** DOFORK is clever about retrying. ** ** Dispose of SIGCHLD signal catchers that may be laying ** around so that endmail will get it. */ if (e->e_xfp != NULL) (void) fflush(e->e_xfp); /* for debugging */ (void) fflush(stdout); # ifdef SIGCHLD (void) setsignal(SIGCHLD, SIG_DFL); # endif /* SIGCHLD */ DOFORK(FORK); /* pid is set by DOFORK */ if (pid < 0) { /* failure */ syserr("%s... openmailer(%s): cannot fork", shortenstring(e->e_to, 203), m->m_name); (void) close(mpvect[0]); (void) close(mpvect[1]); #ifdef SMTP if (clever) { (void) close(rpvect[0]); (void) close(rpvect[1]); } #endif if (tTd(11, 1)) printf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } else if (pid == 0) { int i; int saveerrno; uid_t new_euid = NO_UID; uid_t new_ruid = NO_UID; gid_t new_gid = NO_GID; struct stat stb; extern int DtableSize; if (e->e_lockfp != NULL) (void) close(fileno(e->e_lockfp)); /* child -- set up input & exec mailer */ (void) setsignal(SIGINT, SIG_IGN); (void) setsignal(SIGHUP, SIG_IGN); (void) setsignal(SIGTERM, SIG_DFL); if (m != FileMailer || stat(tochain->q_user, &stb) < 0) stb.st_mode = 0; #if HASSETUSERCONTEXT /* ** Set user resources. */ if (contextaddr != NULL) { struct passwd *pwd; if (contextaddr->q_ruser != NULL) pwd = sm_getpwnam(contextaddr->q_ruser); else pwd = sm_getpwnam(contextaddr->q_user); if (pwd != NULL) (void) setusercontext(NULL, pwd, pwd->m_uid, LOGIN_SETRESOURCES|LOGIN_SETPRIORITY); } #endif /* tweak niceness */ if (m->m_nice != 0) nice(m->m_nice); /* reset group id */ if (bitnset(M_SPECIFIC_UID, m->m_flags)) new_gid = m->m_gid; else if (bitset(S_ISGID, stb.st_mode)) new_gid = stb.st_gid; else if (ctladdr != NULL && ctladdr->q_gid != 0) { if (!DontInitGroups) (void) initgroups(ctladdr->q_ruser != NULL ? ctladdr->q_ruser : ctladdr->q_user, ctladdr->q_gid); new_gid = ctladdr->q_gid; } else { if (!DontInitGroups) (void) initgroups(DefUser, DefGid); if (m->m_gid == 0) new_gid = DefGid; else new_gid = m->m_gid; } if (new_gid != NO_GID && setgid(new_gid) < 0 && suidwarn) syserr("openmailer: setgid(%ld) failed", (long) new_gid); /* reset user id */ endpwent(); if (bitnset(M_SPECIFIC_UID, m->m_flags)) new_euid = m->m_uid; if (bitset(S_ISUID, stb.st_mode)) new_ruid = stb.st_uid; else if (ctladdr != NULL && ctladdr->q_uid != 0) new_ruid = ctladdr->q_uid; else { if (m->m_uid == 0) new_ruid = DefUid; else new_ruid = m->m_uid; } if (new_euid != NO_UID) { vendor_set_uid(new_euid); #if USESETEUID if (seteuid(new_euid) < 0 && suidwarn) syserr("openmailer: seteuid(%ld) failed", (long) new_euid); #else # if HASSETREUID if (setreuid(new_ruid, new_euid) < 0 && suidwarn) syserr("openmailer: setreuid(%ld, %ld) failed", (long) new_ruid, (long) new_euid); # else if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) syserr("openmailer: setuid(%ld) failed", (long) new_euid); # endif #endif } else if (new_ruid != NO_UID) { vendor_set_uid(new_ruid); if (setuid(new_ruid) < 0 && suidwarn) syserr("openmailer: setuid(%ld) failed", (long) new_ruid); } if (tTd(11, 2)) printf("openmailer: running as r/euid=%d/%d\n", getuid(), geteuid()); /* move into some "safe" directory */ if (m->m_execdir != NULL) { char *p, *q; char buf[MAXLINE + 1]; for (p = m->m_execdir; p != NULL; p = q) { q = strchr(p, ':'); if (q != NULL) *q = '\0'; expand(p, buf, sizeof buf, e); if (q != NULL) *q++ = ':'; if (tTd(11, 20)) printf("openmailer: trydir %s\n", buf); if (buf[0] != '\0' && chdir(buf) >= 0) break; } } /* arrange to filter std & diag output of command */ #ifdef SMTP if (clever) { (void) close(rpvect[0]); if (dup2(rpvect[1], STDOUT_FILENO) < 0) { syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", shortenstring(e->e_to, 203), m->m_name, rpvect[1]); _exit(EX_OSERR); } (void) close(rpvect[1]); } else if (OpMode == MD_SMTP || OpMode == MD_DAEMON || HoldErrs || DisConnected) { /* put mailer output in transcript */ if (dup2(fileno(e->e_xfp), STDOUT_FILENO) < 0) { syserr("%s... openmailer(%s): cannot dup xscript %d for stdout", shortenstring(e->e_to, 203), m->m_name, fileno(e->e_xfp)); _exit(EX_OSERR); } } #endif if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) { syserr("%s... openmailer(%s): cannot dup stdout for stderr", shortenstring(e->e_to, 203), m->m_name); _exit(EX_OSERR); } /* arrange to get standard input */ (void) close(mpvect[1]); if (dup2(mpvect[0], STDIN_FILENO) < 0) { syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", shortenstring(e->e_to, 203), m->m_name, mpvect[0]); _exit(EX_OSERR); } (void) close(mpvect[0]); /* arrange for all the files to be closed */ for (i = 3; i < DtableSize; i++) { register int j; if ((j = fcntl(i, F_GETFD, 0)) != -1) (void) fcntl(i, F_SETFD, j | 1); } /* run disconnected from terminal */ (void) setsid(); /* try to execute the mailer */ execve(m->m_mailer, (ARGV_T) pv, (ARGV_T) UserEnviron); saveerrno = errno; syserr("Cannot exec %s", m->m_mailer); if (bitnset(M_LOCALMAILER, m->m_flags) || transienterror(saveerrno)) _exit(EX_OSERR); _exit(EX_UNAVAILABLE); } /* ** Set up return value. */ mci = (MCI *) xalloc(sizeof *mci); bzero((char *) mci, sizeof *mci); mci->mci_mailer = m; mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; mci->mci_pid = pid; (void) close(mpvect[0]); mci->mci_out = fdopen(mpvect[1], "w"); if (mci->mci_out == NULL) { syserr("deliver: cannot create mailer output channel, fd=%d", mpvect[1]); (void) close(mpvect[1]); #ifdef SMTP if (clever) { (void) close(rpvect[0]); (void) close(rpvect[1]); } #endif rcode = EX_OSERR; goto give_up; } #ifdef SMTP if (clever) { (void) close(rpvect[1]); mci->mci_in = fdopen(rpvect[0], "r"); if (mci->mci_in == NULL) { syserr("deliver: cannot create mailer input channel, fd=%d", mpvect[1]); (void) close(rpvect[0]); fclose(mci->mci_out); mci->mci_out = NULL; rcode = EX_OSERR; goto give_up; } } else #endif { mci->mci_flags |= MCIF_TEMP; mci->mci_in = NULL; } } /* ** If we are in SMTP opening state, send initial protocol. */ if (bitnset(M_7BITS, m->m_flags) && (!clever || mci->mci_state == MCIS_CLOSED)) mci->mci_flags |= MCIF_7BIT; #ifdef SMTP if (clever && mci->mci_state != MCIS_CLOSED) { extern void smtpinit __P((MAILER *, MCI *, ENVELOPE *)); smtpinit(m, mci, e); } #endif if (bitset(EF_HAS8BIT, e->e_flags) && !bitset(EF_DONT_MIME, e->e_flags) && bitnset(M_7BITS, m->m_flags)) mci->mci_flags |= MCIF_CVT8TO7; else mci->mci_flags &= ~MCIF_CVT8TO7; #if MIME7TO8 if (bitnset(M_MAKE8BIT, m->m_flags) && !bitset(MCIF_7BIT, mci->mci_flags) && (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && (strcasecmp(p, "quoted-printable") == 0 || strcasecmp(p, "base64") == 0) && (p = hvalue("Content-Type", e->e_header)) != NULL) { /* may want to convert 7 -> 8 */ /* XXX should really parse it here -- and use a class XXX */ if (strncasecmp(p, "text/plain", 10) == 0 && (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) mci->mci_flags |= MCIF_CVT7TO8; } #endif if (tTd(11, 1)) { printf("openmailer: "); mci_dump(mci, FALSE); } if (mci->mci_state != MCIS_OPEN) { /* couldn't open the mailer */ rcode = mci->mci_exitstat; errno = mci->mci_errno; #if NAMED_BIND h_errno = mci->mci_herrno; #endif if (rcode == EX_OK) { /* shouldn't happen */ syserr("554 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", (long) mci, rcode, errno, mci->mci_state, firstsig); mci_dump_all(TRUE); rcode = EX_SOFTWARE; } #ifdef DAEMON else if (curhost != NULL && *curhost != '\0') { /* try next MX site */ goto tryhost; } #endif } else if (!clever) { /* ** Format and send message. */ putfromline(mci, e); (*e->e_puthdr)(mci, e->e_header, e); (*e->e_putbody)(mci, e, NULL); /* get the exit status */ rcode = endmailer(mci, e, pv); } else #ifdef SMTP { extern int smtpmailfrom __P((MAILER *, MCI *, ENVELOPE *)); extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *)); extern int smtpdata __P((MAILER *, MCI *, ENVELOPE *)); + extern int smtpgetstat __P((MAILER *, MCI *, ENVELOPE *)); /* ** Send the MAIL FROM: protocol */ rcode = smtpmailfrom(m, mci, e); if (rcode == EX_OK) { register char *t = tobuf; register int i; /* send the recipient list */ tobuf[0] = '\0'; for (to = tochain; to != NULL; to = to->q_tchain) { e->e_to = to->q_paddr; if ((i = smtprcpt(to, m, mci, e)) != EX_OK) { markfailure(e, to, mci, i); giveresponse(i, m, mci, ctladdr, xstart, e); } else { *t++ = ','; for (p = to->q_paddr; *p; *t++ = *p++) continue; *t = '\0'; } } /* now send the data */ if (tobuf[0] == '\0') { rcode = EX_OK; e->e_to = NULL; if (bitset(MCIF_CACHED, mci->mci_flags)) smtprset(m, mci, e); } else { e->e_to = tobuf + 1; rcode = smtpdata(m, mci, e); } - - /* now close the connection */ - if (!bitset(MCIF_CACHED, mci->mci_flags)) - smtpquit(m, mci, e); } if (rcode == EX_TEMPFAIL && curhost != NULL && *curhost != '\0') { /* try next MX site */ goto tryhost; } } #else /* not SMTP */ { syserr("554 deliver: need SMTP compiled to use clever mailer"); rcode = EX_CONFIG; goto give_up; } #endif /* SMTP */ #if NAMED_BIND if (ConfigLevel < 2) _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ #endif if (tTd(62, 1)) checkfds("after delivery"); /* ** Do final status disposal. ** We check for something in tobuf for the SMTP case. ** If we got a temporary failure, arrange to queue the ** addressees. */ give_up: - if (tobuf[0] != '\0') - giveresponse(rcode, m, mci, ctladdr, xstart, e); - if (rcode == EX_OK) - markstats(e, tochain); - mci_store_persistent(mci); +#ifdef SMTP +# if FFR_LMTP + if (bitnset(M_LMTP, m->m_flags)) + { + tobuf[0] = '\0'; + anyok = FALSE; + } + else +# endif +#endif + anyok = rcode == EX_OK; for (to = tochain; to != NULL; to = to->q_tchain) { /* see if address already marked */ if (bitset(QBADADDR|QQUEUEUP, to->q_flags)) continue; - /* mark bad addresses */ - if (rcode != EX_OK) +#ifdef SMTP +# if FFR_LMTP + /* if running LMTP, get the status for each address */ + if (bitnset(M_LMTP, m->m_flags)) { - markfailure(e, to, mci, rcode); - continue; + rcode = smtpgetstat(m, mci, e); + if (rcode == EX_OK) + { + strcat(tobuf, ","); + strcat(tobuf, to->q_paddr); + anyok = TRUE; + } + else + { + e->e_to = to->q_paddr; + markfailure(e, to, mci, rcode); + giveresponse(rcode, m, mci, ctladdr, xstart, e); + e->e_to = tobuf + 1; + continue; + } } + else +# endif +#endif + { + /* mark bad addresses */ + if (rcode != EX_OK) + { + markfailure(e, to, mci, rcode); + continue; + } + } /* successful delivery */ to->q_flags |= QSENT; to->q_statdate = curtime(); e->e_nsent++; if (bitnset(M_LOCALMAILER, m->m_flags) && bitset(QPINGONSUCCESS, to->q_flags)) { to->q_flags |= QDELIVERED; to->q_status = "2.1.5"; fprintf(e->e_xfp, "%s... Successfully delivered\n", to->q_paddr); } else if (bitset(QPINGONSUCCESS, to->q_flags) && bitset(QPRIMARY, to->q_flags) && !bitset(MCIF_DSN, mci->mci_flags)) { to->q_flags |= QRELAYED; fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n", to->q_paddr); } } +#ifdef SMTP +# if FFR_LMTP + if (bitnset(M_LMTP, m->m_flags)) + { + /* + ** Global information applies to the last recipient only; + ** clear it out to avoid bogus errors. + */ + + rcode = EX_OK; + e->e_statmsg = NULL; + + /* reset the mci state for the next transaction */ + if (mci->mci_state == MCIS_ACTIVE) + mci->mci_state = MCIS_OPEN; + } +# endif +#endif + + if (tobuf[0] != '\0') + giveresponse(rcode, m, mci, ctladdr, xstart, e); + if (anyok) + markstats(e, tochain); + mci_store_persistent(mci); + +#ifdef SMTP + /* now close the connection */ + if (clever && mci->mci_state != MCIS_CLOSED && + !bitset(MCIF_CACHED, mci->mci_flags)) + smtpquit(m, mci, e); +#endif + /* ** Restore state and return. */ #if XDEBUG { char wbuf[MAXLINE]; /* make absolutely certain 0, 1, and 2 are in use */ snprintf(wbuf, sizeof wbuf, "%s... end of deliver(%s)", e->e_to == NULL ? "NO-TO-LIST" : shortenstring(e->e_to, 203), m->m_name); checkfd012(wbuf); } #endif errno = 0; define('g', (char *) NULL, e); return (rcode); } /* ** MARKFAILURE -- mark a failure on a specific address. ** ** Parameters: ** e -- the envelope we are sending. ** q -- the address to mark. ** mci -- mailer connection information. ** rcode -- the code signifying the particular failure. ** ** Returns: ** none. ** ** Side Effects: ** marks the address (and possibly the envelope) with the ** failure so that an error will be returned or ** the message will be queued, as appropriate. */ void markfailure(e, q, mci, rcode) register ENVELOPE *e; register ADDRESS *q; register MCI *mci; int rcode; { char *stat = NULL; switch (rcode) { case EX_OK: break; case EX_TEMPFAIL: case EX_IOERR: case EX_OSERR: q->q_flags |= QQUEUEUP; break; default: q->q_flags |= QBADADDR; break; } /* find most specific error code possible */ if (mci != NULL && mci->mci_status != NULL) { q->q_status = mci->mci_status; q->q_rstatus = mci->mci_rstatus; } else if (e->e_status != NULL) { q->q_status = e->e_status; q->q_rstatus = NULL; } else { switch (rcode) { case EX_USAGE: stat = "5.5.4"; break; case EX_DATAERR: stat = "5.5.2"; break; case EX_NOUSER: stat = "5.1.1"; break; case EX_NOHOST: stat = "5.1.2"; break; case EX_NOINPUT: case EX_CANTCREAT: case EX_NOPERM: stat = "5.3.0"; break; case EX_UNAVAILABLE: case EX_SOFTWARE: case EX_OSFILE: case EX_PROTOCOL: case EX_CONFIG: stat = "5.5.0"; break; case EX_OSERR: case EX_IOERR: stat = "4.5.0"; break; case EX_TEMPFAIL: stat = "4.2.0"; break; } if (stat != NULL) q->q_status = stat; } q->q_statdate = curtime(); if (CurHostName != NULL && CurHostName[0] != '\0') q->q_statmta = newstr(CurHostName); if (rcode != EX_OK && q->q_rstatus == NULL && q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && strcasecmp(q->q_mailer->m_diagtype, "UNIX") == 0) { char buf[30]; (void) snprintf(buf, sizeof buf, "%d", rcode); q->q_rstatus = newstr(buf); } } /* ** ENDMAILER -- Wait for mailer to terminate. ** ** We should never get fatal errors (e.g., segmentation ** violation), so we report those specially. For other ** errors, we choose a status message (into statmsg), ** and if it represents an error, we print it. ** ** Parameters: ** pid -- pid of mailer. ** e -- the current envelope. ** pv -- the parameter vector that invoked the mailer ** (for error messages). ** ** Returns: ** exit code of mailer. ** ** Side Effects: ** none. */ int endmailer(mci, e, pv) register MCI *mci; register ENVELOPE *e; char **pv; { int st; /* close any connections */ if (mci->mci_in != NULL) (void) xfclose(mci->mci_in, mci->mci_mailer->m_name, "mci_in"); if (mci->mci_out != NULL) (void) xfclose(mci->mci_out, mci->mci_mailer->m_name, "mci_out"); mci->mci_in = mci->mci_out = NULL; mci->mci_state = MCIS_CLOSED; /* in the IPC case there is nothing to wait for */ if (mci->mci_pid == 0) return (EX_OK); #ifdef FFR_TIMEOUT_WAIT put a timeout around the wait #endif /* wait for the mailer process to die and collect status */ st = waitfor(mci->mci_pid); if (st == -1) { - syserr("endmailer %s: wait", pv[0]); + syserr("endmailer %s: wait", mci->mci_mailer->m_name); return (EX_SOFTWARE); } if (WIFEXITED(st)) { /* normal death -- return status */ return (WEXITSTATUS(st)); } /* it died a horrid death */ syserr("451 mailer %s died with signal %o", mci->mci_mailer->m_name, st); /* log the arguments */ if (pv != NULL && e->e_xfp != NULL) { register char **av; fprintf(e->e_xfp, "Arguments:"); for (av = pv; *av != NULL; av++) fprintf(e->e_xfp, " %s", *av); fprintf(e->e_xfp, "\n"); } ExitStat = EX_TEMPFAIL; return (EX_TEMPFAIL); } /* ** GIVERESPONSE -- Interpret an error response from a mailer ** ** Parameters: ** stat -- the status code from the mailer (high byte ** only; core dumps must have been taken care of ** already). ** m -- the mailer info for this mailer. ** mci -- the mailer connection info -- can be NULL if the ** response is given before the connection is made. ** ctladdr -- the controlling address for the recipient ** address(es). ** xstart -- the transaction start time, for computing ** transaction delays. ** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** Errors may be incremented. ** ExitStat may be set. */ void giveresponse(stat, m, mci, ctladdr, xstart, e) int stat; register MAILER *m; register MCI *mci; ADDRESS *ctladdr; time_t xstart; ENVELOPE *e; { register const char *statmsg; extern char *SysExMsg[]; register int i; extern int N_SysEx; char buf[MAXLINE]; /* ** Compute status message from code. */ i = stat - EX__BASE; if (stat == 0) { statmsg = "250 Sent"; if (e->e_statmsg != NULL) { (void) snprintf(buf, sizeof buf, "%s (%s)", statmsg, shortenstring(e->e_statmsg, 403)); statmsg = buf; } } else if (i < 0 || i > N_SysEx) { (void) snprintf(buf, sizeof buf, "554 unknown mailer error %d", stat); stat = EX_UNAVAILABLE; statmsg = buf; } else if (stat == EX_TEMPFAIL) { char *bp = buf; snprintf(bp, SPACELEFT(buf, bp), "%s", SysExMsg[i] + 1); bp += strlen(bp); #if NAMED_BIND if (h_errno == TRY_AGAIN) statmsg = errstring(h_errno+E_DNSBASE); else #endif { if (errno != 0) statmsg = errstring(errno); else { #ifdef SMTP statmsg = SmtpError; #else /* SMTP */ statmsg = NULL; #endif /* SMTP */ } } if (statmsg != NULL && statmsg[0] != '\0') snprintf(bp, SPACELEFT(buf, bp), ": %s", statmsg); statmsg = buf; } #if NAMED_BIND else if (stat == EX_NOHOST && h_errno != 0) { statmsg = errstring(h_errno + E_DNSBASE); (void) snprintf(buf, sizeof buf, "%s (%s)", SysExMsg[i] + 1, statmsg); statmsg = buf; } #endif else { statmsg = SysExMsg[i]; if (*statmsg++ == ':') { (void) snprintf(buf, sizeof buf, "%s: %s", statmsg, errstring(errno)); statmsg = buf; } } /* ** Print the message as appropriate */ if (stat == EX_OK || stat == EX_TEMPFAIL) { extern char MsgBuf[]; message("%s", &statmsg[4]); if (stat == EX_TEMPFAIL && e->e_xfp != NULL) fprintf(e->e_xfp, "%s\n", &MsgBuf[4]); } else { char mbuf[8]; Errors++; snprintf(mbuf, sizeof mbuf, "%.3s %%s", statmsg); usrerr(mbuf, &statmsg[4]); } /* ** Final cleanup. ** Log a record of the transaction. Compute the new ** ExitStat -- if we already had an error, stick with ** that. */ if (LogLevel > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6)) logdelivery(m, mci, &statmsg[4], ctladdr, xstart, e); if (tTd(11, 2)) printf("giveresponse: stat=%d, e->e_message=%s\n", stat, e->e_message == NULL ? "" : e->e_message); if (stat != EX_TEMPFAIL) setstat(stat); if (stat != EX_OK && (stat != EX_TEMPFAIL || e->e_message == NULL)) { if (e->e_message != NULL) free(e->e_message); e->e_message = newstr(&statmsg[4]); } errno = 0; #if NAMED_BIND h_errno = 0; #endif } /* ** LOGDELIVERY -- log the delivery in the system log ** ** Care is taken to avoid logging lines that are too long, because ** some versions of syslog have an unfortunate proclivity for core ** dumping. This is a hack, to be sure, that is at best empirical. ** ** Parameters: ** m -- the mailer info. Can be NULL for initial queue. ** mci -- the mailer connection info -- can be NULL if the ** log is occuring when no connection is active. ** stat -- the message to print for the status. ** ctladdr -- the controlling address for the to list. ** xstart -- the transaction start time, used for ** computing transaction delay. ** e -- the current envelope. ** ** Returns: ** none ** ** Side Effects: ** none */ void logdelivery(m, mci, stat, ctladdr, xstart, e) MAILER *m; register MCI *mci; const char *stat; ADDRESS *ctladdr; time_t xstart; register ENVELOPE *e; { # ifdef LOG register char *bp; register char *p; int l; char buf[1024]; # if (SYSLOG_BUFSIZE) >= 256 /* ctladdr: max 106 bytes */ bp = buf; if (ctladdr != NULL) { snprintf(bp, SPACELEFT(buf, bp), ", ctladdr=%s", shortenstring(ctladdr->q_paddr, 83)); bp += strlen(bp); if (bitset(QGOODUID, ctladdr->q_flags)) { (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", ctladdr->q_uid, ctladdr->q_gid); bp += strlen(bp); } } /* delay & xdelay: max 41 bytes */ snprintf(bp, SPACELEFT(buf, bp), ", delay=%s", pintvl(curtime() - e->e_ctime, TRUE)); bp += strlen(bp); if (xstart != (time_t) 0) { snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s", pintvl(curtime() - xstart, TRUE)); bp += strlen(bp); } /* mailer: assume about 19 bytes (max 10 byte mailer name) */ if (m != NULL) { snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name); bp += strlen(bp); } /* relay: max 66 bytes for IPv4 addresses */ if (mci != NULL && mci->mci_host != NULL) { # ifdef DAEMON extern SOCKADDR CurHostAddr; # endif snprintf(bp, SPACELEFT(buf, bp), ", relay=%s", shortenstring(mci->mci_host, 40)); bp += strlen(bp); # ifdef DAEMON if (CurHostAddr.sa.sa_family != 0) { snprintf(bp, SPACELEFT(buf, bp), " [%s]", anynet_ntoa(&CurHostAddr)); } # endif } else if (strcmp(stat, "queued") != 0) { char *p = macvalue('h', e); if (p != NULL && p[0] != '\0') { snprintf(bp, SPACELEFT(buf, bp), ", relay=%s", shortenstring(p, 40)); } } bp += strlen(bp); #define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) #if (STATLEN) < 63 # undef STATLEN # define STATLEN 63 #endif #if (STATLEN) > 203 # undef STATLEN # define STATLEN 203 #endif /* stat: max 210 bytes */ if ((bp - buf) > (sizeof buf - ((STATLEN) + 20))) { /* desperation move -- truncate data */ bp = buf + sizeof buf - ((STATLEN) + 17); strcpy(bp, "..."); bp += 3; } (void) strcpy(bp, ", stat="); bp += strlen(bp); (void) strcpy(bp, shortenstring(stat, (STATLEN))); /* id, to: max 13 + TOBUFSIZE bytes */ l = SYSLOG_BUFSIZE - 100 - strlen(buf); p = e->e_to; while (strlen(p) >= (SIZE_T) l) { register char *q = strchr(p + l, ','); if (q == NULL) break; syslog(LOG_INFO, "%s: to=%.*s [more]%s", e->e_id, ++q - p, p, buf); p = q; } syslog(LOG_INFO, "%s: to=%s%s", e->e_id, p, buf); # else /* we have a very short log buffer size */ l = SYSLOG_BUFSIZE - 85; p = e->e_to; while (strlen(p) >= (SIZE_T) l) { register char *q = strchr(p + l, ','); if (q == NULL) break; syslog(LOG_INFO, "%s: to=%.*s [more]", e->e_id, ++q - p, p); p = q; } syslog(LOG_INFO, "%s: to=%s", e->e_id, p); if (ctladdr != NULL) { bp = buf; snprintf(bp, SPACELEFT(buf, bp), "ctladdr=%s", shortenstring(ctladdr->q_paddr, 83)); bp += strlen(bp); if (bitset(QGOODUID, ctladdr->q_flags)) { (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", ctladdr->q_uid, ctladdr->q_gid); bp += strlen(bp); } syslog(LOG_INFO, "%s: %s", e->e_id, buf); } bp = buf; snprintf(bp, SPACELEFT(buf, bp), "delay=%s", pintvl(curtime() - e->e_ctime, TRUE)); bp += strlen(bp); if (xstart != (time_t) 0) { snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s", pintvl(curtime() - xstart, TRUE)); bp += strlen(bp); } if (m != NULL) { snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name); bp += strlen(bp); } syslog(LOG_INFO, "%s: %.1000s", e->e_id, buf); buf[0] = '\0'; bp = buf; if (mci != NULL && mci->mci_host != NULL) { # ifdef DAEMON extern SOCKADDR CurHostAddr; # endif snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", mci->mci_host); bp += strlen(bp); # ifdef DAEMON if (CurHostAddr.sa.sa_family != 0) snprintf(bp, SPACELEFT(buf, bp), " [%.100s]", anynet_ntoa(&CurHostAddr)); # endif } else if (strcmp(stat, "queued") != 0) { char *p = macvalue('h', e); if (p != NULL && p[0] != '\0') snprintf(buf, sizeof buf, "relay=%.100s", p); } if (buf[0] != '\0') syslog(LOG_INFO, "%s: %.1000s", e->e_id, buf); syslog(LOG_INFO, "%s: stat=%s", e->e_id, shortenstring(stat, 63)); # endif /* short log buffer */ # endif /* LOG */ } /* ** PUTFROMLINE -- output a UNIX-style from line (or whatever) ** ** This can be made an arbitrary message separator by changing $l ** ** One of the ugliest hacks seen by human eyes is contained herein: ** UUCP wants those stupid "remote from " lines. Why oh why ** does a well-meaning programmer such as myself have to deal with ** this kind of antique garbage???? ** ** Parameters: ** mci -- the connection information. ** e -- the envelope. ** ** Returns: ** none ** ** Side Effects: ** outputs some text to fp. */ void putfromline(mci, e) register MCI *mci; ENVELOPE *e; { char *template = UnixFromLine; char buf[MAXLINE]; char xbuf[MAXLINE]; if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) return; if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) { char *bang; expand("\201g", buf, sizeof buf, e); bang = strchr(buf, '!'); if (bang == NULL) { char *at; char hname[MAXNAME]; /* ** If we can construct a UUCP path, do so */ at = strrchr(buf, '@'); if (at == NULL) { expand( "\201k", hname, sizeof hname, e); at = hname; } else *at++ = '\0'; (void) snprintf(xbuf, sizeof xbuf, "From %.800s \201d remote from %.100s\n", buf, at); } else { *bang++ = '\0'; (void) snprintf(xbuf, sizeof xbuf, "From %.800s \201d remote from %.100s\n", bang, buf); template = xbuf; } } expand(template, buf, sizeof buf, e); putxline(buf, mci, PXLF_NOTHINGSPECIAL); } /* ** PUTBODY -- put the body of a message. ** ** Parameters: ** mci -- the connection information. ** e -- the envelope to put out. ** separator -- if non-NULL, a message separator that must ** not be permitted in the resulting message. ** ** Returns: ** none. ** ** Side Effects: ** The message is written onto fp. */ /* values for output state variable */ #define OS_HEAD 0 /* at beginning of line */ #define OS_CR 1 /* read a carriage return */ #define OS_INLINE 2 /* putting rest of line */ void putbody(mci, e, separator) register MCI *mci; register ENVELOPE *e; char *separator; { char buf[MAXLINE]; /* ** Output the body of the message */ if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) { char *df = queuename(e, 'd'); e->e_dfp = fopen(df, "r"); if (e->e_dfp == NULL) syserr("putbody: Cannot open %s for %s from %s", df, e->e_to, e->e_from.q_paddr); } if (e->e_dfp == NULL) { if (bitset(MCIF_INHEADER, mci->mci_flags)) { putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; } putline("<<< No Message Collected >>>", mci); goto endofmessage; } if (e->e_dfino == (ino_t) 0) { struct stat stbuf; if (fstat(fileno(e->e_dfp), &stbuf) < 0) e->e_dfino = -1; else { e->e_dfdev = stbuf.st_dev; e->e_dfino = stbuf.st_ino; } } rewind(e->e_dfp); #if MIME8TO7 if (bitset(MCIF_CVT8TO7, mci->mci_flags)) { char *boundaries[MAXMIMENESTING + 1]; /* ** Do 8 to 7 bit MIME conversion. */ /* make sure it looks like a MIME message */ if (hvalue("MIME-Version", e->e_header) == NULL) putline("MIME-Version: 1.0", mci); if (hvalue("Content-Type", e->e_header) == NULL) { snprintf(buf, sizeof buf, "Content-Type: text/plain; charset=%s", defcharset(e)); putline(buf, mci); } /* now do the hard work */ boundaries[0] = NULL; mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER); } # if MIME7TO8 else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) { mime7to8(mci, e->e_header, e); } # endif else #endif { int ostate; register char *bp; register char *pbp; register int c; int padc; char *buflim; int pos = 0; char peekbuf[10]; /* we can pass it through unmodified */ if (bitset(MCIF_INHEADER, mci->mci_flags)) { putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; } /* determine end of buffer; allow for short mailer lines */ buflim = &buf[sizeof buf - 1]; if (mci->mci_mailer->m_linelimit > 0 && mci->mci_mailer->m_linelimit < sizeof buf - 1) buflim = &buf[mci->mci_mailer->m_linelimit - 1]; /* copy temp file to output with mapping */ ostate = OS_HEAD; bp = buf; pbp = peekbuf; while (!ferror(mci->mci_out)) { register char *xp; if (pbp > peekbuf) c = *--pbp; else if ((c = getc(e->e_dfp)) == EOF) break; if (bitset(MCIF_7BIT, mci->mci_flags)) c &= 0x7f; switch (ostate) { case OS_HEAD: if (c != '\r' && c != '\n' && bp < buflim) { *bp++ = c; break; } /* check beginning of line for special cases */ *bp = '\0'; pos = 0; padc = EOF; if (buf[0] == 'F' && bitnset(M_ESCFROM, mci->mci_mailer->m_flags) && strncmp(buf, "From ", 5) == 0) { padc = '>'; } if (buf[0] == '-' && buf[1] == '-' && separator != NULL) { /* possible separator */ int sl = strlen(separator); if (strncmp(&buf[2], separator, sl) == 0) padc = ' '; } if (buf[0] == '.' && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { padc = '.'; } /* now copy out saved line */ if (TrafficLogFile != NULL) { fprintf(TrafficLogFile, "%05d >>> ", getpid()); if (padc != EOF) putc(padc, TrafficLogFile); for (xp = buf; xp < bp; xp++) putc(*xp, TrafficLogFile); if (c == '\n') fputs(mci->mci_mailer->m_eol, TrafficLogFile); } if (padc != EOF) { putc(padc, mci->mci_out); pos++; } for (xp = buf; xp < bp; xp++) putc(*xp, mci->mci_out); if (c == '\n') { fputs(mci->mci_mailer->m_eol, mci->mci_out); pos = 0; } else { pos += bp - buf; if (c != '\r') *pbp++ = c; } bp = buf; /* determine next state */ if (c == '\n') ostate = OS_HEAD; else if (c == '\r') ostate = OS_CR; else ostate = OS_INLINE; continue; case OS_CR: if (c == '\n') { /* got CRLF */ fputs(mci->mci_mailer->m_eol, mci->mci_out); if (TrafficLogFile != NULL) { fputs(mci->mci_mailer->m_eol, TrafficLogFile); } ostate = OS_HEAD; continue; } /* had a naked carriage return */ *pbp++ = c; c = '\r'; ostate = OS_INLINE; goto putch; case OS_INLINE: if (c == '\r') { ostate = OS_CR; continue; } putch: if (mci->mci_mailer->m_linelimit > 0 && pos > mci->mci_mailer->m_linelimit && c != '\n') { putc('!', mci->mci_out); fputs(mci->mci_mailer->m_eol, mci->mci_out); if (TrafficLogFile != NULL) { fprintf(TrafficLogFile, "!%s", mci->mci_mailer->m_eol); } ostate = OS_HEAD; *pbp++ = c; continue; } if (TrafficLogFile != NULL) putc(c, TrafficLogFile); putc(c, mci->mci_out); pos++; ostate = c == '\n' ? OS_HEAD : OS_INLINE; break; } } /* make sure we are at the beginning of a line */ if (bp > buf) { *bp = '\0'; fputs(buf, mci->mci_out); pos += bp - buf; } if (pos > 0) fputs(mci->mci_mailer->m_eol, mci->mci_out); } if (ferror(e->e_dfp)) { syserr("putbody: df%s: read error", e->e_id); ExitStat = EX_IOERR; } endofmessage: /* some mailers want extra blank line at end of message */ if (bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && buf[0] != '\0' && buf[0] != '\n') putline("", mci); (void) fflush(mci->mci_out); if (ferror(mci->mci_out) && errno != EPIPE) { syserr("putbody: write error"); ExitStat = EX_IOERR; } errno = 0; } /* ** MAILFILE -- Send a message to a file. ** ** If the file has the setuid/setgid bits set, but NO execute ** bits, sendmail will try to become the owner of that file ** rather than the real user. Obviously, this only works if ** sendmail runs as root. ** ** This could be done as a subordinate mailer, except that it ** is used implicitly to save messages in ~/dead.letter. We ** view this as being sufficiently important as to include it ** here. For example, if the system is dying, we shouldn't have ** to create another process plus some pipes to save the message. ** ** Parameters: ** filename -- the name of the file to send to. ** ctladdr -- the controlling address header -- includes ** the userid/groupid to be when sending. ** sfflags -- flags for opening. ** e -- the current envelope. ** ** Returns: ** The exit code associated with the operation. ** ** Side Effects: ** none. */ int mailfile(filename, ctladdr, sfflags, e) char *filename; ADDRESS *ctladdr; int sfflags; register ENVELOPE *e; { register FILE *f; - register int pid = -1; + register pid_t pid = -1; int mode; bool suidwarn = geteuid() == 0; if (tTd(11, 1)) { printf("mailfile %s\n ctladdr=", filename); printaddr(ctladdr, FALSE); } if (e->e_xfp != NULL) fflush(e->e_xfp); /* ** Special case /dev/null. This allows us to restrict file ** delivery to regular files only. */ if (strcmp(filename, "/dev/null") == 0) return EX_OK; /* ** Fork so we can change permissions here. ** Note that we MUST use fork, not vfork, because of ** the complications of calling subroutines, etc. */ DOFORK(fork); if (pid < 0) return (EX_OSERR); else if (pid == 0) { /* child -- actually write to file */ struct stat stb; MCI mcibuf; int oflags = O_WRONLY|O_APPEND; if (e->e_lockfp != NULL) (void) close(fileno(e->e_lockfp)); (void) setsignal(SIGINT, SIG_DFL); (void) setsignal(SIGHUP, SIG_DFL); (void) setsignal(SIGTERM, SIG_DFL); (void) umask(OldUmask); e->e_to = filename; ExitStat = EX_OK; #ifdef HASLSTAT if ((SafeFileEnv != NULL ? lstat(filename, &stb) : stat(filename, &stb)) < 0) #else if (stat(filename, &stb) < 0) #endif { stb.st_mode = FileMode; oflags |= O_CREAT|O_EXCL; } else if (bitset(0111, stb.st_mode) || stb.st_nlink != 1 || (SafeFileEnv != NULL && !S_ISREG(stb.st_mode))) exit(EX_CANTCREAT); mode = stb.st_mode; /* limit the errors to those actually caused in the child */ errno = 0; ExitStat = EX_OK; if (ctladdr != NULL || bitset(SFF_RUNASREALUID, sfflags)) { /* ignore setuid and setgid bits */ mode &= ~(S_ISGID|S_ISUID); } /* we have to open the dfile BEFORE setuid */ if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) { char *df = queuename(e, 'd'); e->e_dfp = fopen(df, "r"); if (e->e_dfp == NULL) { syserr("mailfile: Cannot open %s for %s from %s", df, e->e_to, e->e_from.q_paddr); } } /* select a new user to run as */ if (!bitset(SFF_RUNASREALUID, sfflags)) { if (bitset(S_ISUID, mode)) { RealUserName = NULL; RealUid = stb.st_uid; } else if (ctladdr != NULL && ctladdr->q_uid != 0) { if (ctladdr->q_ruser != NULL) RealUserName = ctladdr->q_ruser; else RealUserName = ctladdr->q_user; RealUid = ctladdr->q_uid; } else if (FileMailer != NULL && FileMailer->m_uid != 0) { RealUserName = DefUser; RealUid = FileMailer->m_uid; } else { RealUserName = DefUser; RealUid = DefUid; } /* select a new group to run as */ if (bitset(S_ISGID, mode)) RealGid = stb.st_gid; else if (ctladdr != NULL && ctladdr->q_uid != 0) RealGid = ctladdr->q_gid; else if (FileMailer != NULL && FileMailer->m_gid != 0) RealGid = FileMailer->m_gid; else RealGid = DefGid; } /* last ditch */ if (!bitset(SFF_ROOTOK, sfflags)) { if (RealUid == 0) RealUid = DefUid; if (RealGid == 0) RealGid = DefGid; } /* set group id list (needs /etc/group access) */ if (RealUserName != NULL && !DontInitGroups) (void) initgroups(RealUserName, RealGid); /* if you have a safe environment, go into it */ if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') { int i; if (chroot(SafeFileEnv) < 0) { syserr("mailfile: Cannot chroot(%s)", SafeFileEnv); exit(EX_CANTCREAT); } i = strlen(SafeFileEnv); if (strncmp(SafeFileEnv, filename, i) == 0) filename += i; } if (chdir("/") < 0) syserr("mailfile: cannot chdir(/)"); /* now reset the group and user ids */ endpwent(); if (setgid(RealGid) < 0 && suidwarn) syserr("mailfile: setgid(%ld) failed", (long) RealGid); vendor_set_uid(RealUid); if (setuid(RealUid) < 0 && suidwarn) syserr("mailfile: setuid(%ld) failed", (long) RealUid); sfflags |= SFF_NOPATHCHECK; sfflags &= ~SFF_OPENASROOT; f = safefopen(filename, oflags, FileMode, sfflags); if (f == NULL) { message("554 cannot open: %s", errstring(errno)); exit(EX_CANTCREAT); } if (fstat(fileno(f), &stb) < 0) { message("554 cannot fstat %s", errstring(errno)); exit(EX_CANTCREAT); } bzero(&mcibuf, sizeof mcibuf); mcibuf.mci_mailer = FileMailer; mcibuf.mci_out = f; if (bitnset(M_7BITS, FileMailer->m_flags)) mcibuf.mci_flags |= MCIF_7BIT; putfromline(&mcibuf, e); (*e->e_puthdr)(&mcibuf, e->e_header, e); (*e->e_putbody)(&mcibuf, e, NULL); putline("\n", &mcibuf); if (fflush(f) < 0 || ferror(f)) { message("451 I/O error: %s", errstring(errno)); setstat(EX_IOERR); } /* reset ISUID & ISGID bits for paranoid systems */ #if HASFCHMOD - (void) fchmod(fileno(f), (int) stb.st_mode); + (void) fchmod(fileno(f), (MODE_T) stb.st_mode); #else - (void) chmod(filename, (int) stb.st_mode); + (void) chmod(filename, (MODE_T) stb.st_mode); #endif (void) xfclose(f, "mailfile", filename); (void) fflush(stdout); exit(ExitStat); /*NOTREACHED*/ } else { /* parent -- wait for exit status */ int st; st = waitfor(pid); if (WIFEXITED(st)) return (WEXITSTATUS(st)); else { syserr("child died on signal %d", st); return (EX_UNAVAILABLE); } /*NOTREACHED*/ } return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ } /* ** HOSTSIGNATURE -- return the "signature" for a host. ** ** The signature describes how we are going to send this -- it ** can be just the hostname (for non-Internet hosts) or can be ** an ordered list of MX hosts. ** ** Parameters: ** m -- the mailer describing this host. ** host -- the host name. ** e -- the current envelope. ** ** Returns: ** The signature for this host. ** ** Side Effects: ** Can tweak the symbol table. */ char * hostsignature(m, host, e) register MAILER *m; char *host; ENVELOPE *e; { register char *p; register STAB *s; int i; int len; #if NAMED_BIND int nmx; char *hp; char *endp; int oldoptions = _res.options; char *mxhosts[MAXMXHOSTS + 1]; #endif /* ** Check to see if this uses IPC -- if not, it can't have MX records. */ p = m->m_mailer; if (strcmp(p, "[IPC]") != 0 && strcmp(p, "[TCP]") != 0) { /* just an ordinary mailer */ return host; } /* ** Look it up in the symbol table. */ s = stab(host, ST_HOSTSIG, ST_ENTER); if (s->s_hostsig != NULL) return s->s_hostsig; /* ** Not already there -- create a signature. */ #if NAMED_BIND if (ConfigLevel < 2) _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ for (hp = host; hp != NULL; hp = endp) { endp = strchr(hp, ':'); if (endp != NULL) *endp = '\0'; if (bitnset(M_NOMX, m->m_flags)) { /* skip MX lookups */ nmx = 1; mxhosts[0] = hp; } else { auto int rcode; nmx = getmxrr(hp, mxhosts, TRUE, &rcode); if (nmx <= 0) { register MCI *mci; /* update the connection info for this host */ mci = mci_get(hp, m); - mci->mci_lastuse = curtime(); - mci->mci_exitstat = rcode; mci->mci_errno = errno; mci->mci_herrno = h_errno; + mci->mci_lastuse = curtime(); + mci_setstat(mci, rcode, NULL, NULL); /* use the original host name as signature */ nmx = 1; mxhosts[0] = hp; } } len = 0; for (i = 0; i < nmx; i++) { len += strlen(mxhosts[i]) + 1; } if (s->s_hostsig != NULL) len += strlen(s->s_hostsig) + 1; p = xalloc(len); if (s->s_hostsig != NULL) { (void) strcpy(p, s->s_hostsig); free(s->s_hostsig); s->s_hostsig = p; p += strlen(p); *p++ = ':'; } else s->s_hostsig = p; for (i = 0; i < nmx; i++) { if (i != 0) *p++ = ':'; strcpy(p, mxhosts[i]); p += strlen(p); } if (endp != NULL) *endp++ = ':'; } makelower(s->s_hostsig); if (ConfigLevel < 2) _res.options = oldoptions; #else /* not using BIND -- the signature is just the host name */ s->s_hostsig = host; #endif if (tTd(17, 1)) printf("hostsignature(%s) = %s\n", host, s->s_hostsig); return s->s_hostsig; } Index: head/usr.sbin/sendmail/src/domain.c =================================================================== --- head/usr.sbin/sendmail/src/domain.c (revision 19843) +++ head/usr.sbin/sendmail/src/domain.c (revision 19844) @@ -1,875 +1,896 @@ /* * Copyright (c) 1986, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "sendmail.h" #ifndef lint #if NAMED_BIND -static char sccsid[] = "@(#)domain.c 8.63 (Berkeley) 9/15/96 (with name server)"; +static char sccsid[] = "@(#)domain.c 8.64 (Berkeley) 10/30/96 (with name server)"; #else -static char sccsid[] = "@(#)domain.c 8.63 (Berkeley) 9/15/96 (without name server)"; +static char sccsid[] = "@(#)domain.c 8.64 (Berkeley) 10/30/96 (without name server)"; #endif #endif /* not lint */ #if NAMED_BIND #include #include #include +/* +** The standard udp packet size PACKETSZ (512) is not sufficient for some +** nameserver answers containing very many resource records. The resolver +** may switch to tcp and retry if it detects udp packet overflow. +** Also note that the resolver routines res_query and res_search return +** the size of the *un*truncated answer in case the supplied answer buffer +** it not big enough to accommodate the entire answer. +*/ + +#ifndef MAXPACKET +# define MAXPACKET 8192 /* max packet size used internally by BIND */ +#endif + typedef union { HEADER qb1; - u_char qb2[PACKETSZ]; + u_char qb2[MAXPACKET]; } querybuf; #ifndef MXHOSTBUFSIZE # define MXHOSTBUFSIZE (128 * MAXMXHOSTS) #endif static char MXHostBuf[MXHOSTBUFSIZE]; #ifndef MAXDNSRCH # define MAXDNSRCH 6 /* number of possible domains to search */ #endif #ifndef MAX # define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifndef NO_DATA # define NO_DATA NO_ADDRESS #endif #ifndef HFIXEDSZ # define HFIXEDSZ 12 /* sizeof(HEADER) */ #endif #define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ #if defined(__RES) && (__RES >= 19940415) # define RES_UNC_T char * #else # define RES_UNC_T u_char * #endif /* ** GETMXRR -- get MX resource records for a domain ** ** Parameters: ** host -- the name of the host to MX. ** mxhosts -- a pointer to a return buffer of MX records. ** droplocalhost -- If TRUE, all MX records less preferred ** than the local host (as determined by $=w) will ** be discarded. ** rcode -- a pointer to an EX_ status code. ** ** Returns: ** The number of MX records found. ** -1 if there is an internal failure. ** If no MX records are found, mxhosts[0] is set to host ** and 1 is returned. */ int getmxrr(host, mxhosts, droplocalhost, rcode) char *host; char **mxhosts; bool droplocalhost; int *rcode; { register u_char *eom, *cp; register int i, j, n; int nmx = 0; register char *bp; HEADER *hp; querybuf answer; int ancount, qdcount, buflen; bool seenlocal = FALSE; u_short pref, type; u_short localpref = 256; char *fallbackMX = FallBackMX; bool trycanon = FALSE; int (*resfunc)(); extern int res_query(), res_search(); u_short prefer[MAXMXHOSTS]; int weight[MAXMXHOSTS]; extern int mxrand __P((char *)); if (tTd(8, 2)) printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost); if (fallbackMX != NULL && droplocalhost && wordinclass(fallbackMX, 'w')) { /* don't use fallback for this pass */ fallbackMX = NULL; } *rcode = EX_OK; /* efficiency hack -- numeric or non-MX lookups */ if (host[0] == '[') goto punt; /* ** If we don't have MX records in our host switch, don't ** try for MX records. Note that this really isn't "right", ** since we might be set up to try NIS first and then DNS; ** if the host is found in NIS we really shouldn't be doing ** MX lookups. However, that should be a degenerate case. */ if (!UseNameServer) goto punt; if (HasWildcardMX && ConfigLevel >= 6) resfunc = res_query; else resfunc = res_search; errno = 0; n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer)); if (n < 0) { if (tTd(8, 1)) printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", (host == NULL) ? "" : host, errno, h_errno); switch (h_errno) { case NO_DATA: trycanon = TRUE; /* fall through */ case NO_RECOVERY: /* no MX data on this host */ goto punt; case HOST_NOT_FOUND: #if BROKEN_RES_SEARCH case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ #endif /* host doesn't exist in DNS; might be in /etc/hosts */ trycanon = TRUE; *rcode = EX_NOHOST; goto punt; case TRY_AGAIN: /* couldn't connect to the name server */ if (fallbackMX != NULL) { /* name server is hosed -- push to fallback */ mxhosts[nmx++] = fallbackMX; return nmx; } /* it might come up later; better queue it up */ *rcode = EX_TEMPFAIL; break; default: syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n", host, h_errno); *rcode = EX_OSERR; break; } /* irreconcilable differences */ return (-1); } + /* avoid problems after truncation in tcp packets */ + if (n > sizeof(answer)) + n = sizeof(answer); + /* find first satisfactory answer */ hp = (HEADER *)&answer; cp = (u_char *)&answer + HFIXEDSZ; eom = (u_char *)&answer + n; for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) if ((n = dn_skipname(cp, eom)) < 0) goto punt; buflen = sizeof(MXHostBuf) - 1; bp = MXHostBuf; ancount = ntohs(hp->ancount); while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) { if ((n = dn_expand((u_char *)&answer, eom, cp, (RES_UNC_T) bp, buflen)) < 0) break; cp += n; GETSHORT(type, cp); cp += INT16SZ + INT32SZ; GETSHORT(n, cp); if (type != T_MX) { if (tTd(8, 8) || _res.options & RES_DEBUG) printf("unexpected answer type %d, size %d\n", type, n); cp += n; continue; } GETSHORT(pref, cp); if ((n = dn_expand((u_char *)&answer, eom, cp, (RES_UNC_T) bp, buflen)) < 0) break; cp += n; if (wordinclass(bp, 'w')) { if (tTd(8, 3)) printf("found localhost (%s) in MX list, pref=%d\n", bp, pref); if (droplocalhost) { if (!seenlocal || pref < localpref) localpref = pref; seenlocal = TRUE; continue; } weight[nmx] = 0; } else weight[nmx] = mxrand(bp); prefer[nmx] = pref; mxhosts[nmx++] = bp; n = strlen(bp); bp += n; if (bp[-1] != '.') { *bp++ = '.'; n++; } *bp++ = '\0'; buflen -= n + 1; } /* sort the records */ for (i = 0; i < nmx; i++) { for (j = i + 1; j < nmx; j++) { if (prefer[i] > prefer[j] || (prefer[i] == prefer[j] && weight[i] > weight[j])) { register int temp; register char *temp1; temp = prefer[i]; prefer[i] = prefer[j]; prefer[j] = temp; temp1 = mxhosts[i]; mxhosts[i] = mxhosts[j]; mxhosts[j] = temp1; temp = weight[i]; weight[i] = weight[j]; weight[j] = temp; } } if (seenlocal && prefer[i] >= localpref) { /* truncate higher preference part of list */ nmx = i; } } /* delete duplicates from list (yes, some bozos have duplicates) */ for (i = 0; i < nmx - 1; ) { if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) i++; else { /* compress out duplicate */ for (j = i + 1; j < nmx; j++) mxhosts[j] = mxhosts[j + 1]; nmx--; } } if (nmx == 0) { punt: if (seenlocal && (!TryNullMXList || sm_gethostbyname(host) == NULL)) { /* ** If we have deleted all MX entries, this is ** an error -- we should NEVER send to a host that ** has an MX, and this should have been caught ** earlier in the config file. ** ** Some sites prefer to go ahead and try the ** A record anyway; that case is handled by ** setting TryNullMXList. I believe this is a ** bad idea, but it's up to you.... */ *rcode = EX_CONFIG; syserr("MX list for %s points back to %s", host, MyHostName); return -1; } if (strlen(host) >= (SIZE_T) sizeof MXHostBuf) { *rcode = EX_CONFIG; syserr("Host name %s too long", shortenstring(host, 203)); return -1; } snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host); mxhosts[0] = MXHostBuf; if (host[0] == '[') { register char *p; /* this may be an MX suppression-style address */ p = strchr(MXHostBuf, ']'); if (p != NULL) { *p = '\0'; if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) { nmx++; *p = ']'; } else { trycanon = TRUE; mxhosts[0]++; } } } if (trycanon && getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE)) { bp = &MXHostBuf[strlen(MXHostBuf)]; if (bp[-1] != '.') { *bp++ = '.'; *bp = '\0'; } nmx = 1; } } /* if we have a default lowest preference, include that */ if (fallbackMX != NULL && !seenlocal) mxhosts[nmx++] = fallbackMX; return (nmx); } /* ** MXRAND -- create a randomizer for equal MX preferences ** ** If two MX hosts have equal preferences we want to randomize ** the selection. But in order for signatures to be the same, ** we need to randomize the same way each time. This function ** computes a pseudo-random hash function from the host name. ** ** Parameters: ** host -- the name of the host. ** ** Returns: ** A random but repeatable value based on the host name. ** ** Side Effects: ** none. */ int mxrand(host) register char *host; { int hfunc; static unsigned int seed; if (seed == 0) { seed = (int) curtime() & 0xffff; if (seed == 0) seed++; } if (tTd(17, 9)) printf("mxrand(%s)", host); hfunc = seed; while (*host != '\0') { int c = *host++; if (isascii(c) && isupper(c)) c = tolower(c); hfunc = ((hfunc << 1) ^ c) % 2003; } hfunc &= 0xff; hfunc++; if (tTd(17, 9)) printf(" = %d\n", hfunc); return hfunc; } /* ** BESTMX -- find the best MX for a name ** ** This is really a hack, but I don't see any obvious way ** to generalize it at the moment. */ char * bestmx_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { int nmx; auto int rcode; int saveopts = _res.options; char *mxhosts[MAXMXHOSTS + 1]; _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); nmx = getmxrr(name, mxhosts, FALSE, &rcode); _res.options = saveopts; if (nmx <= 0) return NULL; if (bitset(MF_MATCHONLY, map->map_mflags)) return map_rewrite(map, name, strlen(name), NULL); else return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); } /* ** DNS_GETCANONNAME -- get the canonical name for named host using DNS ** ** This algorithm tries to be smart about wildcard MX records. ** This is hard to do because DNS doesn't tell is if we matched ** against a wildcard or a specific MX. ** ** We always prefer A & CNAME records, since these are presumed ** to be specific. ** ** If we match an MX in one pass and lose it in the next, we use ** the old one. For example, consider an MX matching *.FOO.BAR.COM. ** A hostname bletch.foo.bar.com will match against this MX, but ** will stop matching when we try bletch.bar.com -- so we know ** that bletch.foo.bar.com must have been right. This fails if ** there was also an MX record matching *.BAR.COM, but there are ** some things that just can't be fixed. ** ** Parameters: ** host -- a buffer containing the name of the host. ** This is a value-result parameter. ** hbsize -- the size of the host buffer. ** trymx -- if set, try MX records as well as A and CNAME. ** statp -- pointer to place to store status. ** ** Returns: ** TRUE -- if the host matched. ** FALSE -- otherwise. */ bool dns_getcanonname(host, hbsize, trymx, statp) char *host; int hbsize; bool trymx; int *statp; { register u_char *eom, *ap; register char *cp; register int n; HEADER *hp; querybuf answer; int ancount, qdcount; int ret; char **domain; int type; char **dp; char *mxmatch; bool amatch; bool gotmx = FALSE; int qtype; int loopcnt; char *xp; - char nbuf[MAX(PACKETSZ, MAXDNAME*2+2)]; + char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)]; char *searchlist[MAXDNSRCH+2]; extern char *gethostalias(); if (tTd(8, 2)) printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); if ((_res.options & RES_INIT) == 0 && res_init() == -1) { *statp = EX_UNAVAILABLE; return FALSE; } /* ** Initialize domain search list. If there is at least one ** dot in the name, search the unmodified name first so we ** find "vse.CS" in Czechoslovakia instead of in the local ** domain (e.g., vse.CS.Berkeley.EDU). ** ** Older versions of the resolver could create this ** list by tearing apart the host name. */ loopcnt = 0; cnameloop: /* Check for dots in the name */ for (cp = host, n = 0; *cp != '\0'; cp++) if (*cp == '.') n++; /* ** If this is a simple name, determine whether it matches an ** alias in the file defined by the environment variable HOSTALIASES. */ if (n == 0 && (xp = gethostalias(host)) != NULL) { if (loopcnt++ > MAXCNAMEDEPTH) { syserr("loop in ${HOSTALIASES} file"); } else { strncpy(host, xp, hbsize); host[hbsize - 1] = '\0'; goto cnameloop; } } /* ** Build the search list. ** If there is at least one dot in name, start with a null ** domain to search the unmodified name first. ** If name does not end with a dot and search up local domain ** tree desired, append each local domain component to the ** search list; if name contains no dots and default domain ** name is desired, append default domain name to search list; ** else if name ends in a dot, remove that dot. */ dp = searchlist; if (n > 0) *dp++ = ""; if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) { for (domain = _res.dnsrch; *domain != NULL; ) *dp++ = *domain++; } else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) { *dp++ = _res.defdname; } else if (*cp == '.') { *cp = '\0'; } *dp = NULL; /* ** Now loop through the search list, appending each domain in turn ** name and searching for a match. */ mxmatch = NULL; qtype = T_ANY; for (dp = searchlist; *dp != NULL; ) { if (qtype == T_ANY) gotmx = FALSE; if (tTd(8, 5)) printf("dns_getcanonname: trying %s.%s (%s)\n", host, *dp, qtype == T_ANY ? "ANY" : qtype == T_A ? "A" : qtype == T_MX ? "MX" : "???"); ret = res_querydomain(host, *dp, C_IN, qtype, answer.qb2, sizeof(answer.qb2)); if (ret <= 0) { if (tTd(8, 7)) printf("\tNO: errno=%d, h_errno=%d\n", errno, h_errno); if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) { /* the name server seems to be down */ h_errno = TRY_AGAIN; *statp = EX_TEMPFAIL; return FALSE; } if (h_errno != HOST_NOT_FOUND) { /* might have another type of interest */ if (qtype == T_ANY) { qtype = T_A; continue; } else if (qtype == T_A && !gotmx && trymx) { qtype = T_MX; continue; } } /* definite no -- try the next domain */ dp++; qtype = T_ANY; continue; } else if (tTd(8, 7)) printf("\tYES\n"); + + /* avoid problems after truncation in tcp packets */ + if (ret > sizeof(answer)) + ret = sizeof(answer); /* ** Appear to have a match. Confirm it by searching for A or ** CNAME records. If we don't have a local domain ** wild card MX record, we will accept MX as well. */ hp = (HEADER *) &answer; ap = (u_char *) &answer + HFIXEDSZ; eom = (u_char *) &answer + ret; /* skip question part of response -- we know what we asked */ for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) { if ((ret = dn_skipname(ap, eom)) < 0) { if (tTd(8, 20)) printf("qdcount failure (%d)\n", ntohs(hp->qdcount)); *statp = EX_SOFTWARE; return FALSE; /* ???XXX??? */ } } amatch = FALSE; for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n) { n = dn_expand((u_char *) &answer, eom, ap, (RES_UNC_T) nbuf, sizeof nbuf); if (n < 0) break; ap += n; GETSHORT(type, ap); ap += INT16SZ + INT32SZ; GETSHORT(n, ap); switch (type) { case T_MX: gotmx = TRUE; if (**dp != '\0' && HasWildcardMX) { /* ** If we are using MX matches and have ** not yet gotten one, save this one ** but keep searching for an A or ** CNAME match. */ if (trymx && mxmatch == NULL) mxmatch = *dp; continue; } /* ** If we did not append a domain name, this ** must have been a canonical name to start ** with. Even if we did append a domain name, ** in the absence of a wildcard MX this must ** still be a real MX match. ** Such MX matches are as good as an A match, ** fall through. */ case T_A: /* Flag that a good match was found */ amatch = TRUE; /* continue in case a CNAME also exists */ continue; case T_CNAME: if (DontExpandCnames) { /* got CNAME -- guaranteed canonical */ amatch = TRUE; break; } if (loopcnt++ > MAXCNAMEDEPTH) { /*XXX should notify postmaster XXX*/ message("DNS failure: CNAME loop for %s", host); if (CurEnv->e_message == NULL) { char ebuf[MAXLINE]; snprintf(ebuf, sizeof ebuf, "Deferred: DNS failure: CNAME loop for %.100s", host); CurEnv->e_message = newstr(ebuf); } h_errno = NO_RECOVERY; *statp = EX_CONFIG; return FALSE; } /* value points at name */ if ((ret = dn_expand((u_char *)&answer, eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0) break; (void)strncpy(host, nbuf, hbsize); /* XXX */ host[hbsize - 1] = '\0'; /* ** RFC 1034 section 3.6 specifies that CNAME ** should point at the canonical name -- but ** urges software to try again anyway. */ goto cnameloop; default: /* not a record of interest */ continue; } } if (amatch) { /* ** Got a good match -- either an A, CNAME, or an ** exact MX record. Save it and get out of here. */ mxmatch = *dp; break; } /* ** Nothing definitive yet. ** If this was a T_ANY query, we don't really know what ** was returned -- it might have been a T_NS, ** for example. Try T_A to be more specific ** during the next pass. ** If this was a T_A query and we haven't yet found a MX ** match, try T_MX if allowed to do so. ** Otherwise, try the next domain. */ if (qtype == T_ANY) qtype = T_A; else if (qtype == T_A && !gotmx && trymx) qtype = T_MX; else { qtype = T_ANY; dp++; } } /* if nothing was found, we are done */ if (mxmatch == NULL) { *statp = EX_NOHOST; return FALSE; } /* ** Create canonical name and return. ** If saved domain name is null, name was already canonical. ** Otherwise append the saved domain name. */ (void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, *mxmatch == '\0' ? "" : ".", MAXDNAME, mxmatch); strncpy(host, nbuf, hbsize); host[hbsize - 1] = '\0'; if (tTd(8, 5)) printf("dns_getcanonname: %s\n", host); *statp = EX_OK; return TRUE; } char * gethostalias(host) char *host; { char *fname; FILE *fp; register char *p = NULL; char buf[MAXLINE]; static char hbuf[MAXDNAME]; fname = getenv("HOSTALIASES"); if (fname == NULL || (fp = safefopen(fname, O_RDONLY, 0, SFF_REGONLY)) == NULL) return NULL; while (fgets(buf, sizeof buf, fp) != NULL) { for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) continue; if (*p == 0) { /* syntax error */ continue; } *p++ = '\0'; if (strcasecmp(buf, host) == 0) break; } if (feof(fp)) { /* no match */ fclose(fp); return NULL; } /* got a match; extract the equivalent name */ while (*p != '\0' && isascii(*p) && isspace(*p)) p++; host = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; *p = '\0'; strncpy(hbuf, host, sizeof hbuf - 1); hbuf[sizeof hbuf - 1] = '\0'; return hbuf; } #endif /* NAMED_BIND */ Index: head/usr.sbin/sendmail/src/main.c =================================================================== --- head/usr.sbin/sendmail/src/main.c (revision 19843) +++ head/usr.sbin/sendmail/src/main.c (revision 19844) @@ -1,2409 +1,2402 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)main.c 8.211 (Berkeley) 10/12/96"; +static char sccsid[] = "@(#)main.c 8.215 (Berkeley) 11/16/96"; #endif /* not lint */ #define _DEFINE #include "sendmail.h" #include #if NAMED_BIND #include #endif # ifdef lint char edata, end; # endif /* lint */ /* ** SENDMAIL -- Post mail to a set of destinations. ** ** This is the basic mail router. All user mail programs should ** call this routine to actually deliver mail. Sendmail in ** turn calls a bunch of mail servers that do the real work of ** delivering the mail. ** ** Sendmail is driven by tables read in from /usr/lib/sendmail.cf ** (read by readcf.c). Some more static configuration info, ** including some code that you may want to tailor for your ** installation, is in conf.c. You may also want to touch ** daemon.c (if you have some other IPC mechanism), acct.c ** (to change your accounting), names.c (to adjust the name ** server mechanism). ** ** Usage: ** /usr/lib/sendmail [flags] addr ... ** ** See the associated documentation for details. ** ** Author: ** Eric Allman, UCB/INGRES (until 10/81). ** Britton-Lee, Inc., purveyors of fine ** database computers (11/81 - 10/88). ** International Computer Science Institute ** (11/88 - 9/89). ** UCB/Mammoth Project (10/89 - 7/95). ** InReference, Inc. (8/95 - present). ** The support of the my employers is gratefully acknowledged. ** Few of them (Britton-Lee in particular) have had ** anything to gain from my involvement in this project. */ int NextMailer; /* "free" index into Mailer struct */ char *FullName; /* sender's full name */ ENVELOPE BlankEnvelope; /* a "blank" envelope */ ENVELOPE MainEnvelope; /* the envelope around the basic letter */ ADDRESS NullAddress = /* a null address */ { "", "", NULL, "" }; char *CommandLineArgs; /* command line args for pid file */ bool Warn_Q_option = FALSE; /* warn about Q option use */ char **SaveArgv; /* argument vector for re-execing */ static void obsolete(); extern void printmailer __P((MAILER *)); extern void tTflag __P((char *)); #ifdef DAEMON #ifndef SMTP ERROR %%%% Cannot have daemon mode without SMTP %%%% ERROR #endif /* SMTP */ #endif /* DAEMON */ #define MAXCONFIGLEVEL 7 /* highest config version level known */ int main(argc, argv, envp) int argc; char **argv; char **envp; { register char *p; char **av; extern char Version[]; char *ep, *from; typedef int (*fnptr)(); STAB *st; register int i; int j; bool queuemode = FALSE; /* process queue requests */ bool safecf = TRUE; bool warn_C_flag = FALSE; char warn_f_flag = '\0'; bool run_in_foreground = FALSE; /* -bD mode */ static bool reenter = FALSE; struct passwd *pw; struct stat stb; struct hostent *hp; bool nullserver; char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ static char rnamebuf[MAXNAME]; /* holds RealUserName */ char *emptyenviron[1]; extern int DtableSize; extern int optind; extern int opterr; extern char *optarg; extern char **environ; extern time_t convtime(); extern void intsig(); extern struct hostent *myhostname(); extern char *getauthinfo(); extern char *getcfname(); extern void sigusr1(); extern void sighup(); extern void initmacros __P((ENVELOPE *)); extern void init_md __P((int, char **)); extern int getdtsize __P((void)); extern void tTsetup __P((u_char *, int, char *)); extern void setdefaults __P((ENVELOPE *)); extern void initsetproctitle __P((int, char **, char **)); extern void init_vendor_macros __P((ENVELOPE *)); extern void load_if_names __P((void)); extern void vendor_pre_defaults __P((ENVELOPE *)); extern void vendor_post_defaults __P((ENVELOPE *)); extern void readcf __P((char *, bool, ENVELOPE *)); extern void printqueue __P((void)); extern void sendtoargv __P((char **, ENVELOPE *)); extern void resetlimits __P((void)); /* ** Check to see if we reentered. ** This would normally happen if e_putheader or e_putbody ** were NULL when invoked. */ if (reenter) { syserr("main: reentered!"); abort(); } reenter = TRUE; /* avoid null pointer dereferences */ TermEscape.te_rv_on = TermEscape.te_rv_off = ""; /* do machine-dependent initializations */ init_md(argc, argv); #ifdef SIGUSR1 /* arrange to dump state on user-1 signal */ setsignal(SIGUSR1, sigusr1); #endif /* in 4.4BSD, the table can be huge; impose a reasonable limit */ DtableSize = getdtsize(); if (DtableSize > 256) DtableSize = 256; /* ** Be sure we have enough file descriptors. ** But also be sure that 0, 1, & 2 are open. */ i = open("/dev/null", O_RDWR, 0); if (fstat(STDIN_FILENO, &stb) < 0 && errno != EOPNOTSUPP) (void) dup2(i, STDIN_FILENO); if (fstat(STDOUT_FILENO, &stb) < 0 && errno != EOPNOTSUPP) (void) dup2(i, STDOUT_FILENO); if (fstat(STDERR_FILENO, &stb) < 0 && errno != EOPNOTSUPP) (void) dup2(i, STDERR_FILENO); if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) (void) close(i); i = DtableSize; while (--i > 0) { if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) (void) close(i); } errno = 0; #ifdef LOG # ifdef LOG_MAIL openlog("sendmail", LOG_PID, LOG_MAIL); # else openlog("sendmail", LOG_PID); # endif #endif tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); /* Handle any non-getoptable constructions. */ obsolete(argv); /* ** Do a quick prescan of the argument list. */ #if defined(__osf__) || defined(_AIX3) # define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x" #endif #if defined(sony_news) # define OPTIONS "B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:" #endif #ifndef OPTIONS # define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:" #endif opterr = 0; while ((j = getopt(argc, argv, OPTIONS)) != EOF) { switch (j) { case 'd': /* hack attack -- see if should use ANSI mode */ if (strcmp(optarg, "ANSI") == 0) { TermEscape.te_rv_on = "\033[7m"; TermEscape.te_rv_off = "\033[0m"; break; } tTflag(optarg); setbuf(stdout, (char *) NULL); break; } } opterr = 1; /* set up the blank envelope */ BlankEnvelope.e_puthdr = putheader; BlankEnvelope.e_putbody = putbody; BlankEnvelope.e_xfp = NULL; STRUCTCOPY(NullAddress, BlankEnvelope.e_from); CurEnv = &BlankEnvelope; STRUCTCOPY(NullAddress, MainEnvelope.e_from); /* ** Set default values for variables. ** These cannot be in initialized data space. */ setdefaults(&BlankEnvelope); RealUid = getuid(); RealGid = getgid(); pw = sm_getpwuid(RealUid); if (pw != NULL) (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); else (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid); RealUserName = rnamebuf; /* save command line arguments */ i = 0; for (av = argv; *av != NULL; ) i += strlen(*av++) + 1; SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1)); CommandLineArgs = xalloc(i); p = CommandLineArgs; for (av = argv, i = 0; *av != NULL; ) { SaveArgv[i++] = newstr(*av); if (av != argv) *p++ = ' '; strcpy(p, *av++); p += strlen(p); } SaveArgv[i] = NULL; if (tTd(0, 1)) { int ll; extern char *CompileOptions[]; printf("Version %s\n Compiled with:", Version); av = CompileOptions; ll = 7; while (*av != NULL) { if (ll + strlen(*av) > 63) { putchar('\n'); ll = 0; } if (ll == 0) { putchar('\t'); putchar('\t'); } else putchar(' '); printf("%s", *av); ll += strlen(*av++) + 1; } putchar('\n'); } if (tTd(0, 10)) { int ll; extern char *OsCompileOptions[]; printf(" OS Defines:"); av = OsCompileOptions; ll = 7; while (*av != NULL) { if (ll + strlen(*av) > 63) { putchar('\n'); ll = 0; } if (ll == 0) { putchar('\t'); putchar('\t'); } else putchar(' '); printf("%s", *av); ll += strlen(*av++) + 1; } putchar('\n'); #ifdef _PATH_UNIX printf("Kernel symbols:\t%s\n", _PATH_UNIX); #endif printf(" Def Conf file:\t%s\n", getcfname()); printf(" Pid file:\t%s\n", PidFile); } InChannel = stdin; OutChannel = stdout; /* initialize for setproctitle */ initsetproctitle(argc, argv, envp); /* clear sendmail's environment */ ExternalEnviron = environ; emptyenviron[0] = NULL; environ = emptyenviron; /* prime the child environment */ setuserenv("AGENT", "sendmail"); if (setsignal(SIGINT, SIG_IGN) != SIG_IGN) (void) setsignal(SIGINT, intsig); (void) setsignal(SIGTERM, intsig); (void) setsignal(SIGPIPE, SIG_IGN); OldUmask = umask(022); OpMode = MD_DELIVER; FullName = getextenv("NAME"); + /* + ** Initialize name server if it is going to be used. + */ + #if NAMED_BIND - if (tTd(8, 8)) - { + if (!bitset(RES_INIT, _res.options)) res_init(); + if (tTd(8, 8)) _res.options |= RES_DEBUG; - } +# ifdef RES_NOALIASES + _res.options |= RES_NOALIASES; +# endif #endif errno = 0; from = NULL; /* initialize some macros, etc. */ initmacros(CurEnv); init_vendor_macros(CurEnv); /* version */ define('v', Version, CurEnv); /* hostname */ hp = myhostname(jbuf, sizeof jbuf); if (jbuf[0] != '\0') { struct utsname utsname; if (tTd(0, 4)) printf("canonical name: %s\n", jbuf); define('w', newstr(jbuf), CurEnv); /* must be new string */ define('j', newstr(jbuf), CurEnv); setclass('w', jbuf); p = strchr(jbuf, '.'); if (p != NULL) { if (p[1] != '\0') { define('m', newstr(&p[1]), CurEnv); } while (p != NULL && strchr(&p[1], '.') != NULL) { *p = '\0'; if (tTd(0, 4)) printf("\ta.k.a.: %s\n", jbuf); setclass('w', jbuf); *p++ = '.'; p = strchr(p, '.'); } } if (uname(&utsname) >= 0) p = utsname.nodename; else { if (tTd(0, 22)) printf("uname failed (%s)\n", errstring(errno)); makelower(jbuf); p = jbuf; } if (tTd(0, 4)) printf(" UUCP nodename: %s\n", p); p = newstr(p); define('k', p, CurEnv); setclass('k', p); setclass('w', p); } if (hp != NULL) { for (av = hp->h_aliases; av != NULL && *av != NULL; av++) { if (tTd(0, 4)) printf("\ta.k.a.: %s\n", *av); setclass('w', *av); } #if NETINET if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ) { register int i; for (i = 0; hp->h_addr_list[i] != NULL; i++) { char ipbuf[103]; snprintf(ipbuf, sizeof ipbuf, "[%.100s]", inet_ntoa(*((struct in_addr *) hp->h_addr_list[i]))); if (tTd(0, 4)) printf("\ta.k.a.: %s\n", ipbuf); setclass('w', ipbuf); } } #endif } /* probe interfaces and locate any additional names */ load_if_names(); /* current time */ define('b', arpadate((char *) NULL), CurEnv); /* ** Crack argv. */ av = argv; p = strrchr(*av, '/'); if (p++ == NULL) p = *av; if (strcmp(p, "newaliases") == 0) OpMode = MD_INITALIAS; else if (strcmp(p, "mailq") == 0) OpMode = MD_PRINT; else if (strcmp(p, "smtpd") == 0) OpMode = MD_DAEMON; else if (strcmp(p, "hoststat") == 0) OpMode = MD_HOSTSTAT; else if (strcmp(p, "purgestat") == 0) OpMode = MD_PURGESTAT; optind = 1; while ((j = getopt(argc, argv, OPTIONS)) != EOF) { switch (j) { case 'b': /* operations mode */ switch (j = *optarg) { case MD_DAEMON: case MD_FGDAEMON: # ifndef DAEMON usrerr("Daemon mode not implemented"); ExitStat = EX_USAGE; break; # endif /* DAEMON */ case MD_SMTP: # ifndef SMTP usrerr("I don't speak SMTP"); ExitStat = EX_USAGE; break; # endif /* SMTP */ case MD_INITALIAS: - /* fall through ... */ - case MD_DELIVER: case MD_VERIFY: case MD_TEST: case MD_PRINT: case MD_HOSTSTAT: case MD_PURGESTAT: case MD_ARPAFTP: OpMode = j; break; case MD_FREEZE: usrerr("Frozen configurations unsupported"); ExitStat = EX_USAGE; break; default: usrerr("Invalid operation mode %c", j); ExitStat = EX_USAGE; break; } break; case 'B': /* body type */ CurEnv->e_bodytype = optarg; break; case 'C': /* select configuration file (already done) */ if (RealUid != 0) warn_C_flag = TRUE; ConfFile = optarg; endpwent(); (void) setgid(RealGid); (void) setuid(RealUid); safecf = FALSE; break; case 'd': /* debugging -- already done */ break; case 'f': /* from address */ case 'r': /* obsolete -f flag */ if (from != NULL) { usrerr("More than one \"from\" person"); ExitStat = EX_USAGE; break; } from = newstr(denlstring(optarg, TRUE, TRUE)); if (strcmp(RealUserName, from) != 0) warn_f_flag = j; break; case 'F': /* set full name */ FullName = newstr(optarg); break; case 'h': /* hop count */ CurEnv->e_hopcount = strtol(optarg, &ep, 10); if (*ep) { usrerr("Bad hop count (%s)", optarg); ExitStat = EX_USAGE; } break; case 'n': /* don't alias */ NoAlias = TRUE; break; case 'N': /* delivery status notifications */ DefaultNotify |= QHASNOTIFY; if (strcasecmp(optarg, "never") == 0) break; for (p = optarg; p != NULL; optarg = p) { p = strchr(p, ','); if (p != NULL) *p++ = '\0'; if (strcasecmp(optarg, "success") == 0) DefaultNotify |= QPINGONSUCCESS; else if (strcasecmp(optarg, "failure") == 0) DefaultNotify |= QPINGONFAILURE; else if (strcasecmp(optarg, "delay") == 0) DefaultNotify |= QPINGONDELAY; else { usrerr("Invalid -N argument"); ExitStat = EX_USAGE; } } break; case 'o': /* set option */ setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv); break; case 'O': /* set option (long form) */ setoption(' ', optarg, FALSE, TRUE, CurEnv); break; case 'p': /* set protocol */ p = strchr(optarg, ':'); if (p != NULL) { *p++ = '\0'; if (*p != '\0') { ep = xalloc(strlen(p) + 1); cleanstrcpy(ep, p, MAXNAME); define('s', ep, CurEnv); } } if (*optarg != '\0') { ep = xalloc(strlen(optarg) + 1); cleanstrcpy(ep, optarg, MAXNAME); define('r', ep, CurEnv); } break; case 'q': /* run queue files at intervals */ # ifdef QUEUE FullName = NULL; queuemode = TRUE; switch (optarg[0]) { case 'I': QueueLimitId = newstr(&optarg[1]); break; case 'R': QueueLimitRecipient = newstr(&optarg[1]); break; case 'S': QueueLimitSender = newstr(&optarg[1]); break; default: QueueIntvl = convtime(optarg, 'm'); break; } # else /* QUEUE */ usrerr("I don't know about queues"); ExitStat = EX_USAGE; # endif /* QUEUE */ break; case 'R': /* DSN RET: what to return */ if (bitset(EF_RET_PARAM, CurEnv->e_flags)) { usrerr("Duplicate -R flag"); ExitStat = EX_USAGE; break; } CurEnv->e_flags |= EF_RET_PARAM; if (strcasecmp(optarg, "hdrs") == 0) CurEnv->e_flags |= EF_NO_BODY_RETN; else if (strcasecmp(optarg, "full") != 0) { usrerr("Invalid -R value"); ExitStat = EX_USAGE; } break; case 't': /* read recipients from message */ GrabTo = TRUE; break; case 'U': /* initial (user) submission */ UserSubmission = TRUE; break; case 'V': /* DSN ENVID: set "original" envelope id */ if (!xtextok(optarg)) { usrerr("Invalid syntax in -V flag"); ExitStat = EX_USAGE; } else CurEnv->e_envid = newstr(optarg); break; case 'X': /* traffic log file */ endpwent(); setgid(RealGid); setuid(RealUid); TrafficLogFile = fopen(optarg, "a"); if (TrafficLogFile == NULL) { syserr("cannot open %s", optarg); ExitStat = EX_CANTCREAT; break; } #ifdef HASSETVBUF setvbuf(TrafficLogFile, NULL, _IOLBF, 0); #else setlinebuf(TrafficLogFile); #endif break; /* compatibility flags */ case 'c': /* connect to non-local mailers */ case 'i': /* don't let dot stop me */ case 'm': /* send to me too */ case 'T': /* set timeout interval */ case 'v': /* give blow-by-blow description */ setoption(j, "T", FALSE, TRUE, CurEnv); break; case 'e': /* error message disposition */ case 'M': /* define macro */ setoption(j, optarg, FALSE, TRUE, CurEnv); break; case 's': /* save From lines in headers */ setoption('f', "T", FALSE, TRUE, CurEnv); break; # ifdef DBM case 'I': /* initialize alias DBM file */ OpMode = MD_INITALIAS; break; # endif /* DBM */ # if defined(__osf__) || defined(_AIX3) case 'x': /* random flag that OSF/1 & AIX mailx passes */ break; # endif # if defined(sony_news) case 'E': case 'J': /* ignore flags for Japanese code conversion impremented on Sony NEWS */ break; # endif default: ExitStat = EX_USAGE; finis(); break; } } av += optind; /* ** Do basic initialization. ** Read system control file. ** Extract special fields for local use. */ /* set up ${opMode} for use in config file */ { char mbuf[2]; mbuf[0] = OpMode; mbuf[1] = '\0'; define(MID_OPMODE, newstr(mbuf), CurEnv); } #if XDEBUG checkfd012("before readcf"); #endif vendor_pre_defaults(CurEnv); readcf(getcfname(), safecf, CurEnv); ConfigFileRead = TRUE; vendor_post_defaults(CurEnv); /* avoid denial-of-service attacks */ resetlimits(); if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) { /* drop privileges -- daemon mode done after socket/bind */ if (RunAsGid != 0) (void) setgid(RunAsGid); if (RunAsUid != 0) (void) setuid(RunAsUid); } /* ** Find our real host name for future logging. */ p = getauthinfo(STDIN_FILENO); define('_', p, CurEnv); /* suppress error printing if errors mailed back or whatever */ if (CurEnv->e_errormode != EM_PRINT) HoldErrs = TRUE; /* set up the $=m class now, after .cf has a chance to redefine $m */ expand("\201m", jbuf, sizeof jbuf, CurEnv); setclass('m', jbuf); if (tTd(0, 1)) { printf("\n============ SYSTEM IDENTITY (after readcf) ============"); printf("\n (short domain name) $w = "); xputs(macvalue('w', CurEnv)); printf("\n (canonical domain name) $j = "); xputs(macvalue('j', CurEnv)); printf("\n (subdomain name) $m = "); xputs(macvalue('m', CurEnv)); printf("\n (node name) $k = "); xputs(macvalue('k', CurEnv)); printf("\n========================================================\n\n"); } /* - ** Initialize name server if it is going to be used. - */ - -#if NAMED_BIND - if (UseNameServer && !bitset(RES_INIT, _res.options)) - res_init(); -# ifdef RES_NOALIASES - _res.options |= RES_NOALIASES; -# endif -#endif - - /* ** Do more command line checking -- these are things that ** have to modify the results of reading the config file. */ /* process authorization warnings from command line */ if (warn_C_flag) auth_warning(CurEnv, "Processed by %s with -C %s", RealUserName, ConfFile); if (Warn_Q_option) auth_warning(CurEnv, "Processed from queue %s", QueueDir); /* check body type for legality */ if (CurEnv->e_bodytype == NULL) /* nothing */ ; else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0) SevenBitInput = TRUE; else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0) SevenBitInput = FALSE; else { usrerr("Illegal body type %s", CurEnv->e_bodytype); CurEnv->e_bodytype = NULL; } /* tweak default DSN notifications */ if (DefaultNotify == 0) DefaultNotify = QPINGONFAILURE|QPINGONDELAY; /* Enforce use of local time (null string overrides this) */ if (TimeZoneSpec == NULL) unsetenv("TZ"); else if (TimeZoneSpec[0] != '\0') setuserenv("TZ", TimeZoneSpec); else setuserenv("TZ", NULL); tzset(); /* check for sane configuration level */ if (ConfigLevel > MAXCONFIGLEVEL) { - syserr("Warning: .cf version level (%d) exceeds program functionality (%d)", - ConfigLevel, MAXCONFIGLEVEL); + syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)", + ConfigLevel, Version, MAXCONFIGLEVEL); } /* need MCI cache to have persistence */ if (HostStatDir != NULL && MaxMciCache == 0) { HostStatDir = NULL; printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n"); } /* need HostStatusDir in order to have SingleThreadDelivery */ if (SingleThreadDelivery && HostStatDir == NULL) { SingleThreadDelivery = FALSE; printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n"); } if (MeToo) BlankEnvelope.e_flags |= EF_METOO; switch (OpMode) { case MD_TEST: /* don't have persistent host status in test mode */ HostStatDir = NULL; break; case MD_FGDAEMON: run_in_foreground = TRUE; OpMode = MD_DAEMON; /* fall through ... */ case MD_DAEMON: /* check for permissions */ if (RealUid != 0) { #ifdef LOG - syslog(LOG_ALERT, "uid %d tried to start daemon mode", - RealUid); + if (LogLevel > 1) + syslog(LOG_ALERT, "user %d attempted to run daemon", + RealUid); #endif usrerr("Permission denied"); exit(EX_USAGE); } vendor_daemon_setup(CurEnv); /* remove things that don't make sense in daemon mode */ FullName = NULL; GrabTo = FALSE; /* arrange to restart on hangup signal */ #ifdef LOG if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/') syslog(LOG_WARNING, "daemon invoked without full pathname; kill -1 won't work"); #endif setsignal(SIGHUP, sighup); /* workaround: can't seem to release the signal in the parent */ releasesignal(SIGHUP); break; case MD_INITALIAS: Verbose = TRUE; /* fall through... */ default: /* arrange to exit cleanly on hangup signal */ setsignal(SIGHUP, intsig); break; } /* full names can't have newlines */ if (FullName != NULL && strchr(FullName, '\n') != NULL) FullName = newstr(denlstring(FullName, TRUE, TRUE)); /* do heuristic mode adjustment */ if (Verbose) { /* turn off noconnect option */ setoption('c', "F", TRUE, FALSE, CurEnv); /* turn on interactive delivery */ setoption('d', "", TRUE, FALSE, CurEnv); } if (ConfigLevel < 3) { UseErrorsTo = TRUE; } /* set options that were previous macros */ if (SmtpGreeting == NULL) { if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL) SmtpGreeting = newstr(p); else SmtpGreeting = "\201j Sendmail \201v ready at \201b"; } if (UnixFromLine == NULL) { if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL) UnixFromLine = newstr(p); else UnixFromLine = "From \201g \201d"; } /* our name for SMTP codes */ expand("\201j", jbuf, sizeof jbuf, CurEnv); MyHostName = jbuf; if (strchr(jbuf, '.') == NULL) message("WARNING: local host name (%s) is not qualified; fix $j in config file", jbuf); /* make certain that this name is part of the $=w class */ setclass('w', MyHostName); /* the indices of built-in mailers */ st = stab("local", ST_MAILER, ST_FIND); if (st != NULL) LocalMailer = st->s_mailer; else if (OpMode != MD_TEST || !warn_C_flag) syserr("No local mailer defined"); st = stab("prog", ST_MAILER, ST_FIND); if (st == NULL) syserr("No prog mailer defined"); else { ProgMailer = st->s_mailer; clrbitn(M_MUSER, ProgMailer->m_flags); } st = stab("*file*", ST_MAILER, ST_FIND); if (st == NULL) syserr("No *file* mailer defined"); else { FileMailer = st->s_mailer; clrbitn(M_MUSER, FileMailer->m_flags); } st = stab("*include*", ST_MAILER, ST_FIND); if (st == NULL) syserr("No *include* mailer defined"); else InclMailer = st->s_mailer; if (ConfigLevel < 6) { /* heuristic tweaking of local mailer for back compat */ if (LocalMailer != NULL) { setbitn(M_ALIASABLE, LocalMailer->m_flags); setbitn(M_HASPWENT, LocalMailer->m_flags); setbitn(M_TRYRULESET5, LocalMailer->m_flags); setbitn(M_CHECKINCLUDE, LocalMailer->m_flags); setbitn(M_CHECKPROG, LocalMailer->m_flags); setbitn(M_CHECKFILE, LocalMailer->m_flags); setbitn(M_CHECKUDB, LocalMailer->m_flags); } if (ProgMailer != NULL) setbitn(M_RUNASRCPT, ProgMailer->m_flags); if (FileMailer != NULL) setbitn(M_RUNASRCPT, FileMailer->m_flags); /* propogate some envariables into children */ setuserenv("ISP", NULL); setuserenv("SYSTYPE", NULL); } if (ConfigLevel < 7) { if (LocalMailer != NULL) setbitn(M_VRFY250, LocalMailer->m_flags); if (ProgMailer != NULL) setbitn(M_VRFY250, ProgMailer->m_flags); if (FileMailer != NULL) setbitn(M_VRFY250, FileMailer->m_flags); } /* MIME Content-Types that cannot be transfer encoded */ setclass('n', "multipart/signed"); /* MIME message/xxx subtypes that can be treated as messages */ setclass('s', "rfc822"); /* MIME Content-Transfer-Encodings that can be encoded */ setclass('e', "7bit"); setclass('e', "8bit"); setclass('e', "binary"); #ifdef USE_B_CLASS /* MIME Content-Types that should be treated as binary */ setclass('b', "image"); setclass('b', "audio"); setclass('b', "video"); setclass('b', "application/octet-stream"); #endif /* operate in queue directory */ if (OpMode == MD_TEST) /* nothing -- just avoid further if clauses */ ; else if (QueueDir == NULL) { syserr("QueueDirectory (Q) option must be set"); ExitStat = EX_CONFIG; } else if (chdir(QueueDir) < 0) { syserr("cannot chdir(%s)", QueueDir); ExitStat = EX_CONFIG; } /* check host status directory for validity */ if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE)) { /* cannot use this value */ if (tTd(0, 2)) printf("Cannot use HostStatusDirectory = %s: %s\n", HostStatDir, errstring(errno)); HostStatDir = NULL; } # ifdef QUEUE if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags)) { struct stat stbuf; /* check to see if we own the queue directory */ if (stat(".", &stbuf) < 0) syserr("main: cannot stat %s", QueueDir); if (stbuf.st_uid != RealUid) { /* nope, really a botch */ usrerr("You do not have permission to process the queue"); exit (EX_NOPERM); } } # endif /* QUEUE */ /* if we've had errors so far, exit now */ if (ExitStat != EX_OK && OpMode != MD_TEST) { endpwent(); setuid(RealUid); exit(ExitStat); } #if XDEBUG checkfd012("before main() initmaps"); #endif /* ** Do operation-mode-dependent initialization. */ switch (OpMode) { case MD_PRINT: /* print the queue */ #ifdef QUEUE dropenvelope(CurEnv, TRUE); printqueue(); endpwent(); setuid(RealUid); exit(EX_OK); #else /* QUEUE */ usrerr("No queue to print"); finis(); #endif /* QUEUE */ case MD_HOSTSTAT: mci_traverse_persistent(mci_print_persistent, NULL); exit(EX_OK); break; case MD_PURGESTAT: mci_traverse_persistent(mci_purge_persistent, NULL); exit(EX_OK); break; case MD_INITALIAS: /* initialize alias database */ initmaps(TRUE, CurEnv); endpwent(); setuid(RealUid); exit(ExitStat); case MD_SMTP: nullserver = FALSE; /* fall through... */ case MD_DAEMON: /* reset DSN parameters */ DefaultNotify = QPINGONFAILURE|QPINGONDELAY; CurEnv->e_envid = NULL; CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); /* don't open alias database -- done in srvrsmtp */ break; default: /* open the alias database */ initmaps(FALSE, CurEnv); break; } if (tTd(0, 15)) { extern void printrules __P((void)); /* print configuration table (or at least part of it) */ if (tTd(0, 90)) printrules(); for (i = 0; i < MAXMAILERS; i++) { if (Mailer[i] != NULL) printmailer(Mailer[i]); } } /* ** Switch to the main envelope. */ CurEnv = newenvelope(&MainEnvelope, CurEnv); MainEnvelope.e_flags = BlankEnvelope.e_flags; /* ** If test mode, read addresses from stdin and process. */ if (OpMode == MD_TEST) { char buf[MAXLINE]; void intindebug(); if (isatty(fileno(stdin))) Verbose = TRUE; if (Verbose) { printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); printf("Enter
\n"); } if (setjmp(TopFrame) > 0) printf("\n"); (void) setsignal(SIGINT, intindebug); for (;;) { extern void testmodeline __P((char *, ENVELOPE *)); if (Verbose) printf("> "); (void) fflush(stdout); if (fgets(buf, sizeof buf, stdin) == NULL) finis(); p = strchr(buf, '\n'); if (p != NULL) *p = '\0'; if (!Verbose) printf("> %s\n", buf); testmodeline(buf, CurEnv); } } # ifdef QUEUE /* ** If collecting stuff from the queue, go start doing that. */ if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0) { (void) unsetenv("HOSTALIASES"); runqueue(FALSE); finis(); } # endif /* QUEUE */ /* ** If a daemon, wait for a request. ** getrequests will always return in a child. ** If we should also be processing the queue, start ** doing it in background. ** We check for any errors that might have happened ** during startup. */ if (OpMode == MD_DAEMON || QueueIntvl != 0) { char dtype[200]; extern bool getrequests __P((ENVELOPE *)); if (!run_in_foreground && !tTd(99, 100)) { /* put us in background */ i = fork(); if (i < 0) syserr("daemon: cannot fork"); if (i != 0) exit(0); /* disconnect from our controlling tty */ disconnect(2, CurEnv); } dtype[0] = '\0'; if (OpMode == MD_DAEMON) strcat(dtype, "+SMTP"); if (QueueIntvl != 0) { strcat(dtype, "+queueing@"); strcat(dtype, pintvl(QueueIntvl, TRUE)); } if (tTd(0, 1)) strcat(dtype, "+debugging"); #ifdef LOG syslog(LOG_INFO, "starting daemon (%s): %s", Version, dtype + 1); #endif #ifdef XLA xla_create_file(); #endif # ifdef QUEUE if (queuemode) { runqueue(TRUE); if (OpMode != MD_DAEMON) for (;;) pause(); } # endif /* QUEUE */ dropenvelope(CurEnv, TRUE); #ifdef DAEMON nullserver = getrequests(CurEnv); /* drop privileges */ if (RunAsGid != 0) (void) setgid(RunAsGid); if (RunAsUid != 0) (void) setuid(RunAsUid); /* at this point we are in a child: reset state */ (void) newenvelope(CurEnv, CurEnv); /* ** Get authentication data */ p = getauthinfo(fileno(InChannel)); define('_', p, &BlankEnvelope); #endif /* DAEMON */ } # ifdef SMTP /* ** If running SMTP protocol, start collecting and executing ** commands. This will never return. */ if (OpMode == MD_SMTP || OpMode == MD_DAEMON) { char pbuf[20]; extern void smtp __P((bool, ENVELOPE *)); /* ** Save some macros for check_* rulesets. */ define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope); define(macid("{client_addr}", NULL), newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope); if (RealHostAddr.sa.sa_family == AF_INET) snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port); else snprintf(pbuf, sizeof pbuf, "0"); define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope); smtp(nullserver, CurEnv); } # endif /* SMTP */ clearenvelope(CurEnv, FALSE); if (OpMode == MD_VERIFY) { CurEnv->e_sendmode = SM_VERIFY; CurEnv->e_errormode = EM_PRINT; PostMasterCopy = NULL; HoldErrs = FALSE; } else { /* interactive -- all errors are global */ CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER; } /* ** Do basic system initialization and set the sender */ initsys(CurEnv); if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't')) auth_warning(CurEnv, "%s set sender to %s using -%c", RealUserName, from, warn_f_flag); setsender(from, CurEnv, NULL, FALSE); if (macvalue('s', CurEnv) == NULL) define('s', RealHostName, CurEnv); if (*av == NULL && !GrabTo) { CurEnv->e_flags |= EF_GLOBALERRS; usrerr("Recipient names must be specified"); /* collect body for UUCP return */ if (OpMode != MD_VERIFY) collect(InChannel, FALSE, FALSE, NULL, CurEnv); finis(); } /* ** Scan argv and deliver the message to everyone. */ sendtoargv(av, CurEnv); /* if we have had errors sofar, arrange a meaningful exit stat */ if (Errors > 0 && ExitStat == EX_OK) ExitStat = EX_USAGE; /* ** Read the input mail. */ CurEnv->e_to = NULL; if (OpMode != MD_VERIFY || GrabTo) { CurEnv->e_flags |= EF_GLOBALERRS; collect(InChannel, FALSE, FALSE, NULL, CurEnv); } errno = 0; if (tTd(1, 1)) printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr); /* ** Actually send everything. ** If verifying, just ack. */ CurEnv->e_from.q_flags |= QDONTSEND; if (tTd(1, 5)) { printf("main: QDONTSEND "); printaddr(&CurEnv->e_from, FALSE); } CurEnv->e_to = NULL; sendall(CurEnv, SM_DEFAULT); /* ** All done. ** Don't send return error message if in VERIFY mode. */ finis(); /*NOTREACHED*/ return -1; } void intindebug() { longjmp(TopFrame, 1); } /* ** FINIS -- Clean up and exit. ** ** Parameters: ** none ** ** Returns: ** never ** ** Side Effects: ** exits sendmail */ void finis() { if (tTd(2, 1)) { extern void printenvflags(); printf("\n====finis: stat %d e_id=%s e_flags=", ExitStat, CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id); printenvflags(CurEnv); } if (tTd(2, 9)) printopenfds(FALSE); /* clean up temp files */ CurEnv->e_to = NULL; if (CurEnv->e_id != NULL) dropenvelope(CurEnv, TRUE); /* flush any cached connections */ mci_flush(TRUE, NULL); # ifdef XLA /* clean up extended load average stuff */ xla_all_end(); # endif /* and exit */ # ifdef LOG if (LogLevel > 78) syslog(LOG_DEBUG, "finis, pid=%d", getpid()); # endif /* LOG */ if (ExitStat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET) ExitStat = EX_OK; /* reset uid for process accounting */ endpwent(); setuid(RealUid); exit(ExitStat); } /* ** INTSIG -- clean up on interrupt ** ** This just arranges to exit. It pessimises in that it ** may resend a message. ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Unlocks the current job. */ void intsig() { #ifdef LOG if (LogLevel > 79) syslog(LOG_DEBUG, "%s: interrupt", CurEnv->e_id == NULL ? "[NOQUEUE]" : CurEnv->e_id); #endif FileName = NULL; unlockqueue(CurEnv); #ifdef XLA xla_all_end(); #endif /* reset uid for process accounting */ endpwent(); setuid(RealUid); exit(EX_OK); } /* ** INITMACROS -- initialize the macro system ** ** This just involves defining some macros that are actually ** used internally as metasymbols to be themselves. ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** initializes several macros to be themselves. */ struct metamac MetaMacros[] = { /* LHS pattern matching characters */ { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE }, { '=', MATCHCLASS }, { '~', MATCHNCLASS }, /* these are RHS metasymbols */ { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER }, { '>', CALLSUBR }, /* the conditional operations */ { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI }, /* the hostname lookup characters */ { '[', HOSTBEGIN }, { ']', HOSTEND }, { '(', LOOKUPBEGIN }, { ')', LOOKUPEND }, /* miscellaneous control characters */ { '&', MACRODEXPAND }, { '\0' } }; #define MACBINDING(name, mid) \ stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \ MacroName[mid] = name; void initmacros(e) register ENVELOPE *e; { register struct metamac *m; register int c; char buf[5]; extern char *MacroName[256]; for (m = MetaMacros; m->metaname != '\0'; m++) { buf[0] = m->metaval; buf[1] = '\0'; define(m->metaname, newstr(buf), e); } buf[0] = MATCHREPL; buf[2] = '\0'; for (c = '0'; c <= '9'; c++) { buf[1] = c; define(c, newstr(buf), e); } /* set defaults for some macros sendmail will use later */ define('n', "MAILER-DAEMON", e); /* set up external names for some internal macros */ MACBINDING("opMode", MID_OPMODE); /*XXX should probably add equivalents for all short macros here XXX*/ } /* ** DISCONNECT -- remove our connection with any foreground process ** ** Parameters: ** droplev -- how "deeply" we should drop the line. ** 0 -- ignore signals, mail back errors, make sure ** output goes to stdout. ** 1 -- also, make stdout go to transcript. ** 2 -- also, disconnect from controlling terminal ** (only for daemon mode). ** e -- the current envelope. ** ** Returns: ** none ** ** Side Effects: ** Trys to insure that we are immune to vagaries of ** the controlling tty. */ void disconnect(droplev, e) int droplev; register ENVELOPE *e; { int fd; if (tTd(52, 1)) printf("disconnect: In %d Out %d, e=%x\n", fileno(InChannel), fileno(OutChannel), e); if (tTd(52, 100)) { printf("don't\n"); return; } #ifdef LOG if (LogLevel > 93) syslog(LOG_DEBUG, "%s: disconnect level %d", e->e_id == NULL ? "[NOQUEUE]" : e->e_id, droplev); #endif /* be sure we don't get nasty signals */ (void) setsignal(SIGINT, SIG_IGN); (void) setsignal(SIGQUIT, SIG_IGN); /* we can't communicate with our caller, so.... */ HoldErrs = TRUE; CurEnv->e_errormode = EM_MAIL; Verbose = FALSE; DisConnected = TRUE; /* all input from /dev/null */ if (InChannel != stdin) { (void) fclose(InChannel); InChannel = stdin; } (void) freopen("/dev/null", "r", stdin); /* output to the transcript */ if (OutChannel != stdout) { (void) fclose(OutChannel); OutChannel = stdout; } if (droplev > 0) { if (e->e_xfp == NULL) fd = open("/dev/null", O_WRONLY, 0666); else fd = fileno(e->e_xfp); (void) fflush(stdout); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (e->e_xfp == NULL) close(fd); } /* drop our controlling TTY completely if possible */ if (droplev > 1) { (void) setsid(); errno = 0; } #if XDEBUG checkfd012("disconnect"); #endif # ifdef LOG if (LogLevel > 71) syslog(LOG_DEBUG, "in background, pid=%d", getpid()); # endif /* LOG */ errno = 0; } static void obsolete(argv) char *argv[]; { register char *ap; register char *op; while ((ap = *++argv) != NULL) { /* Return if "--" or not an option of any form. */ if (ap[0] != '-' || ap[1] == '-') return; /* skip over options that do have a value */ op = strchr(OPTIONS, ap[1]); if (op != NULL && *++op == ':' && ap[2] == '\0' && ap[1] != 'd' && #if defined(sony_news) ap[1] != 'E' && ap[1] != 'J' && #endif argv[1] != NULL && argv[1][0] != '-') { argv++; continue; } /* If -C doesn't have an argument, use sendmail.cf. */ #define __DEFPATH "sendmail.cf" if (ap[1] == 'C' && ap[2] == '\0') { *argv = xalloc(sizeof(__DEFPATH) + 2); argv[0][0] = '-'; argv[0][1] = 'C'; (void)strcpy(&argv[0][2], __DEFPATH); } /* If -q doesn't have an argument, run it once. */ if (ap[1] == 'q' && ap[2] == '\0') *argv = "-q0"; /* if -d doesn't have an argument, use 0-99.1 */ if (ap[1] == 'd' && ap[2] == '\0') *argv = "-d0-99.1"; # if defined(sony_news) /* if -E doesn't have an argument, use -EC */ if (ap[1] == 'E' && ap[2] == '\0') *argv = "-EC"; /* if -J doesn't have an argument, use -JJ */ if (ap[1] == 'J' && ap[2] == '\0') *argv = "-JJ"; # endif } } /* ** AUTH_WARNING -- specify authorization warning ** ** Parameters: ** e -- the current envelope. ** msg -- the text of the message. ** args -- arguments to the message. ** ** Returns: ** none. */ void #ifdef __STDC__ auth_warning(register ENVELOPE *e, const char *msg, ...) #else auth_warning(e, msg, va_alist) register ENVELOPE *e; const char *msg; va_dcl #endif { char buf[MAXLINE]; VA_LOCAL_DECL if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) { register char *p; static char hostbuf[48]; extern struct hostent *myhostname(); if (hostbuf[0] == '\0') (void) myhostname(hostbuf, sizeof hostbuf); (void) snprintf(buf, sizeof buf, "%s: ", hostbuf); p = &buf[strlen(buf)]; VA_START(msg); vsnprintf(p, SPACELEFT(buf, p), msg, ap); VA_END; addheader("X-Authentication-Warning", buf, &e->e_header); #ifdef LOG if (LogLevel > 3) syslog(LOG_INFO, "%s: Authentication-Warning: %.400s", e->e_id == NULL ? "[NOQUEUE]" : e->e_id, buf); #endif } } /* ** GETEXTENV -- get from external environment ** ** Parameters: ** envar -- the name of the variable to retrieve ** ** Returns: ** The value, if any. */ char * getextenv(envar) const char *envar; { char **envp; int l; l = strlen(envar); for (envp = ExternalEnviron; *envp != NULL; envp++) { if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=') return &(*envp)[l + 1]; } return NULL; } /* ** SETUSERENV -- set an environment in the propogated environment ** ** Parameters: ** envar -- the name of the environment variable. ** value -- the value to which it should be set. If ** null, this is extracted from the incoming ** environment. If that is not set, the call ** to setuserenv is ignored. ** ** Returns: ** none. */ void setuserenv(envar, value) const char *envar; const char *value; { int i; char **evp = UserEnviron; char *p; if (value == NULL) { value = getextenv(envar); if (value == NULL) return; } i = strlen(envar); p = (char *) xalloc(strlen(value) + i + 2); strcpy(p, envar); p[i++] = '='; strcpy(&p[i], value); while (*evp != NULL && strncmp(*evp, p, i) != 0) evp++; if (*evp != NULL) { *evp++ = p; } else if (evp < &UserEnviron[MAXUSERENVIRON]) { *evp++ = p; *evp = NULL; } /* make sure it is in our environment as well */ if (putenv(p) < 0) syserr("setuserenv: putenv(%s) failed", p); } /* ** DUMPSTATE -- dump state ** ** For debugging. */ void dumpstate(when) char *when; { #ifdef LOG register char *j = macvalue('j', CurEnv); int rs; syslog(LOG_DEBUG, "--- dumping state on %s: $j = %s ---", when, j == NULL ? "" : j); if (j != NULL) { if (!wordinclass(j, 'w')) syslog(LOG_DEBUG, "*** $j not in $=w ***"); } syslog(LOG_DEBUG, "CurChildren = %d", CurChildren); syslog(LOG_DEBUG, "--- open file descriptors: ---"); printopenfds(TRUE); syslog(LOG_DEBUG, "--- connection cache: ---"); mci_dump_all(TRUE); rs = strtorwset("debug_dumpstate", NULL, ST_FIND); if (rs > 0) { int stat; register char **pvp; char *pv[MAXATOM + 1]; pv[0] = NULL; stat = rewrite(pv, rs, 0, CurEnv); syslog(LOG_DEBUG, "--- ruleset debug_dumpstate returns stat %d, pv: ---", stat); for (pvp = pv; *pvp != NULL; pvp++) syslog(LOG_DEBUG, "%s", *pvp); } syslog(LOG_DEBUG, "--- end of state dump ---"); #endif } void sigusr1() { dumpstate("user signal"); } void sighup() { #ifdef LOG if (LogLevel > 3) syslog(LOG_INFO, "restarting %s on signal", SaveArgv[0]); #endif releasesignal(SIGHUP); - if (setuid(RealUid) < 0 || setgid(RealGid) < 0) + if (setgid(RealGid) < 0 || setuid(RealUid) < 0) { #ifdef LOG if (LogLevel > 0) syslog(LOG_ALERT, "could not set[ug]id(%d, %d): %m", RealUid, RealGid); #endif exit(EX_OSERR); } execv(SaveArgv[0], (ARGV_T) SaveArgv); #ifdef LOG if (LogLevel > 0) syslog(LOG_ALERT, "could not exec %s: %m", SaveArgv[0]); #endif exit(EX_OSFILE); } /* ** TESTMODELINE -- process a test mode input line ** ** Parameters: ** line -- the input line. ** e -- the current environment. ** Syntax: ** # a comment ** .X process X as a configuration line ** =X dump a configuration item (such as mailers) ** $X dump a macro or class ** /X try an activity ** X normal process through rule set X */ void testmodeline(line, e) char *line; ENVELOPE *e; { register char *p; char *q; auto char *delimptr; int mid; int i, rs; STAB *map; char **s; struct rewrite *rw; ADDRESS a; static int tryflags = RF_COPYNONE; char exbuf[MAXLINE]; extern bool invalidaddr __P((char *, char *)); extern char *crackaddr __P((char *)); extern void dump_class __P((STAB *, int)); extern void translate_dollars __P((char *)); extern void help __P((char *)); switch (line[0]) { case '#': case 0: return; case '?': help("-bt"); return; case '.': /* config-style settings */ switch (line[1]) { case 'D': mid = macid(&line[2], &delimptr); if (mid == '\0') return; translate_dollars(delimptr); define(mid, newstr(delimptr), e); break; case 'C': if (line[2] == '\0') /* not to call syserr() */ return; mid = macid(&line[2], &delimptr); if (mid == '\0') return; translate_dollars(delimptr); expand(delimptr, exbuf, sizeof exbuf, e); p = exbuf; while (*p != '\0') { register char *wd; char delim; while (*p != '\0' && isascii(*p) && isspace(*p)) p++; wd = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; delim = *p; *p = '\0'; if (wd[0] != '\0') setclass(mid, wd); *p = delim; } break; case '\0': printf("Usage: .[DC]macro value(s)\n"); break; default: printf("Unknown \".\" command %s\n", line); break; } return; case '=': /* config-style settings */ switch (line[1]) { case 'S': /* dump rule set */ rs = strtorwset(&line[2], NULL, ST_FIND); if (rs < 0) { printf("Undefined ruleset %s\n", &line[2]); return; } rw = RewriteRules[rs]; if (rw == NULL) return; do { putchar('R'); s = rw->r_lhs; while (*s != NULL) { xputs(*s++); putchar(' '); } putchar('\t'); putchar('\t'); s = rw->r_rhs; while (*s != NULL) { xputs(*s++); putchar(' '); } putchar('\n'); } while ((rw = rw->r_next) != NULL); break; case 'M': for (i = 0; i < MAXMAILERS; i++) { if (Mailer[i] != NULL) printmailer(Mailer[i]); } break; case '\0': printf("Usage: =Sruleset or =M\n"); break; default: printf("Unknown \"=\" command %s\n", line); break; } return; case '-': /* set command-line-like opts */ switch (line[1]) { case 'd': tTflag(&line[2]); break; case '\0': printf("Usage: -d{debug arguments}\n"); break; default: printf("Unknown \"-\" command %s\n", line); break; } return; case '$': if (line[1] == '=') { mid = macid(&line[2], NULL); if (mid != '\0') stabapply(dump_class, mid); return; } mid = macid(&line[1], NULL); if (mid == '\0') return; p = macvalue(mid, e); if (p == NULL) printf("Undefined\n"); else { xputs(p); printf("\n"); } return; case '/': /* miscellaneous commands */ p = &line[strlen(line)]; while (--p >= line && isascii(*p) && isspace(*p)) *p = '\0'; p = strpbrk(line, " \t"); if (p != NULL) { while (isascii(*p) && isspace(*p)) *p++ = '\0'; } else p = ""; if (line[1] == '\0') { printf("Usage: /[canon|map|mx|parse|try|tryflags]\n"); return; } if (strcasecmp(&line[1], "mx") == 0) { #if NAMED_BIND /* look up MX records */ int nmx; auto int rcode; char *mxhosts[MAXMXHOSTS + 1]; if (*p == '\0') { printf("Usage: /mx address\n"); return; } nmx = getmxrr(p, mxhosts, FALSE, &rcode); printf("getmxrr(%s) returns %d value(s):\n", p, nmx); for (i = 0; i < nmx; i++) printf("\t%s\n", mxhosts[i]); #else printf("No MX code compiled in\n"); #endif } else if (strcasecmp(&line[1], "canon") == 0) { char host[MAXHOSTNAMELEN]; if (*p == '\0') { printf("Usage: /canon address\n"); return; } else if (strlen(p) >= sizeof host) { printf("Name too long\n"); return; } strcpy(host, p); (void) getcanonname(host, sizeof(host), HasWildcardMX); printf("getcanonname(%s) returns %s\n", p, host); } else if (strcasecmp(&line[1], "map") == 0) { auto int rcode = EX_OK; if (*p == '\0') { printf("Usage: /map mapname key\n"); return; } for (q = p; *q != '\0' && !isspace(*q); q++) continue; if (*q == '\0') { printf("No key specified\n"); return; } *q++ = '\0'; map = stab(p, ST_MAP, ST_FIND); if (map == NULL) { printf("Map named \"%s\" not found\n", p); return; } printf("map_lookup: %s (%s) ", p, q); p = (*map->s_map.map_class->map_lookup) (&map->s_map, q, NULL, &rcode); if (p == NULL) printf("no match (%d)\n", rcode); else printf("returns %s (%d)\n", p, rcode); } else if (strcasecmp(&line[1], "try") == 0) { MAILER *m; STAB *s; auto int rcode = EX_OK; q = strpbrk(p, " \t"); if (q != NULL) { while (isascii(*q) && isspace(*q)) *q++ = '\0'; } if (q == NULL || *q == '\0') { printf("Usage: /try mailer address\n"); return; } s = stab(p, ST_MAILER, ST_FIND); if (s == NULL) { printf("Unknown mailer %s\n", p); return; } m = s->s_mailer; printf("Trying %s %s address %s for mailer %s\n", bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient", q, p); p = remotename(q, m, tryflags, &rcode, CurEnv); printf("Rcode = %d, addr = %s\n", rcode, p == NULL ? "" : p); e->e_to = NULL; } else if (strcasecmp(&line[1], "tryflags") == 0) { if (*p == '\0') { printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n"); return; } for (; *p != '\0'; p++) { switch (*p) { case 'H': case 'h': tryflags |= RF_HEADERADDR; break; case 'E': case 'e': tryflags &= ~RF_HEADERADDR; break; case 'S': case 's': tryflags |= RF_SENDERADDR; break; case 'R': case 'r': tryflags &= ~RF_SENDERADDR; break; } } } else if (strcasecmp(&line[1], "parse") == 0) { if (*p == '\0') { printf("Usage: /parse address\n"); return; } q = crackaddr(p); printf("Cracked address = "); xputs(q); printf("\nParsing %s %s address\n", bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient"); if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL) printf("Cannot parse\n"); else if (a.q_host != NULL && a.q_host[0] != '\0') printf("mailer %s, host %s, user %s\n", a.q_mailer->m_name, a.q_host, a.q_user); else printf("mailer %s, user %s\n", a.q_mailer->m_name, a.q_user); e->e_to = NULL; } else { printf("Unknown \"/\" command %s\n", line); } return; } for (p = line; isascii(*p) && isspace(*p); p++) continue; q = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p == '\0') { printf("No address!\n"); return; } *p = '\0'; if (invalidaddr(p + 1, NULL)) return; do { register char **pvp; char pvpbuf[PSBUFSIZE]; pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf, &delimptr, NULL); if (pvp == NULL) continue; p = q; while (*p != '\0') { int stat; int rs = strtorwset(p, NULL, ST_FIND); if (rs < 0) { printf("Undefined ruleset %s\n", p); break; } stat = rewrite(pvp, rs, 0, e); if (stat != EX_OK) printf("== Ruleset %s (%d) status %d\n", p, rs, stat); while (*p != '\0' && *p++ != ',') continue; } } while (*(p = delimptr) != '\0'); } void dump_class(s, id) register STAB *s; int id; { if (s->s_type != ST_CLASS) return; if (bitnset(id & 0xff, s->s_class)) printf("%s\n", s->s_name); } Index: head/usr.sbin/sendmail/src/mime.c =================================================================== --- head/usr.sbin/sendmail/src/mime.c (revision 19843) +++ head/usr.sbin/sendmail/src/mime.c (revision 19844) @@ -1,1184 +1,1184 @@ /* * Copyright (c) 1994, 1996 Eric P. Allman * Copyright (c) 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ # include "sendmail.h" # include #ifndef lint -static char sccsid[] = "@(#)mime.c 8.48 (Berkeley) 10/18/96"; +static char sccsid[] = "@(#)mime.c 8.49 (Berkeley) 10/30/96"; #endif /* not lint */ /* ** MIME support. ** ** I am indebted to John Beck of Hewlett-Packard, who contributed ** his code to me for inclusion. As it turns out, I did not use ** his code since he used a "minimum change" approach that used ** several temp files, and I wanted a "minimum impact" approach ** that would avoid copying. However, looking over his code ** helped me cement my understanding of the problem. ** ** I also looked at, but did not directly use, Nathaniel ** Borenstein's "code.c" module. Again, it functioned as ** a file-to-file translator, which did not fit within my ** design bounds, but it was a useful base for understanding ** the problem. */ #if MIME8TO7 /* character set for hex and base64 encoding */ char Base16Code[] = "0123456789ABCDEF"; char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* types of MIME boundaries */ #define MBT_SYNTAX 0 /* syntax error */ #define MBT_NOTSEP 1 /* not a boundary */ #define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */ #define MBT_FINAL 3 /* final boundary (trailing -- included) */ static char *MimeBoundaryNames[] = { "SYNTAX", "NOTSEP", "INTERMED", "FINAL" }; bool MapNLtoCRLF; extern int mimeboundary __P((char *, char **)); /* ** MIME8TO7 -- output 8 bit body in 7 bit format ** ** The header has already been output -- this has to do the ** 8 to 7 bit conversion. It would be easy if we didn't have ** to deal with nested formats (multipart/xxx and message/rfc822). ** ** We won't be called if we don't have to do a conversion, and ** appropriate MIME-Version: and Content-Type: fields have been ** output. Any Content-Transfer-Encoding: field has not been ** output, and we can add it here. ** ** Parameters: ** mci -- mailer connection information. ** header -- the header for this body part. ** e -- envelope. ** boundaries -- the currently pending message boundaries. ** NULL if we are processing the outer portion. ** flags -- to tweak processing. ** ** Returns: ** An indicator of what terminated the message part: ** MBT_FINAL -- the final boundary ** MBT_INTERMED -- an intermediate boundary ** MBT_NOTSEP -- an end of file */ struct args { char *field; /* name of field */ char *value; /* value of that field */ }; int mime8to7(mci, header, e, boundaries, flags) register MCI *mci; HDR *header; register ENVELOPE *e; char **boundaries; int flags; { register char *p; int linelen; int bt; off_t offset; size_t sectionsize, sectionhighbits; int i; char *type; char *subtype; char *cte; char **pvp; int argc = 0; char *bp; bool use_qp = FALSE; struct args argv[MAXMIMEARGS]; char bbuf[128]; char buf[MAXLINE]; char pvpbuf[MAXLINE]; extern u_char MimeTokenTab[256]; extern int mime_getchar __P((FILE *, char **, int *)); extern int mime_getchar_crlf __P((FILE *, char **, int *)); if (tTd(43, 1)) { printf("mime8to7: flags = %x, boundaries =", flags); if (boundaries[0] == NULL) printf(" "); else { for (i = 0; boundaries[i] != NULL; i++) printf(" %s", boundaries[i]); } printf("\n"); } MapNLtoCRLF = TRUE; p = hvalue("Content-Transfer-Encoding", header); if (p == NULL || (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, MimeTokenTab)) == NULL || pvp[0] == NULL) { cte = NULL; } else { cataddr(pvp, NULL, buf, sizeof buf, '\0'); cte = newstr(buf); } type = subtype = NULL; p = hvalue("Content-Type", header); if (p == NULL) { if (bitset(M87F_DIGEST, flags)) p = "message/rfc822"; else p = "text/plain"; } if (p != NULL && (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, MimeTokenTab)) != NULL && pvp[0] != NULL) { if (tTd(43, 40)) { for (i = 0; pvp[i] != NULL; i++) printf("pvp[%d] = \"%s\"\n", i, pvp[i]); } type = *pvp++; if (*pvp != NULL && strcmp(*pvp, "/") == 0 && *++pvp != NULL) { subtype = *pvp++; } /* break out parameters */ while (*pvp != NULL && argc < MAXMIMEARGS) { /* skip to semicolon separator */ while (*pvp != NULL && strcmp(*pvp, ";") != 0) pvp++; if (*pvp++ == NULL || *pvp == NULL) break; /* extract field name */ argv[argc].field = *pvp++; /* see if there is a value */ if (*pvp != NULL && strcmp(*pvp, "=") == 0 && (*++pvp == NULL || strcmp(*pvp, ";") != 0)) { argv[argc].value = *pvp; argc++; } } } /* check for disaster cases */ if (type == NULL) type = "-none-"; if (subtype == NULL) subtype = "-none-"; /* don't propogate some flags more than one level into the message */ flags &= ~M87F_DIGEST; /* ** Check for cases that can not be encoded. ** ** For example, you can't encode certain kinds of types ** or already-encoded messages. If we find this case, ** just copy it through. */ snprintf(buf, sizeof buf, "%.100s/%.100s", type, subtype); if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e'))) flags |= M87F_NO8BIT; #ifdef USE_B_CLASS if (wordinclass(buf, 'b') || wordinclass(type, 'b')) MapNLtoCRLF = FALSE; #endif if (wordinclass(buf, 'q') || wordinclass(type, 'q')) use_qp = TRUE; /* ** Multipart requires special processing. ** ** Do a recursive descent into the message. */ if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags)) { int blen; if (strcasecmp(subtype, "digest") == 0) flags |= M87F_DIGEST; for (i = 0; i < argc; i++) { if (strcasecmp(argv[i].field, "boundary") == 0) break; } if (i >= argc) { syserr("mime8to7: Content-Type: \"%s\": missing boundary", p); p = "---"; /* avoid bounce loops */ e->e_flags |= EF_DONT_MIME; } else { p = argv[i].value; stripquotes(p); } blen = strlen(p); if (blen > sizeof bbuf - 1) { syserr("mime8to7: multipart boundary \"%s\" too long", p); blen = sizeof bbuf - 1; /* avoid bounce loops */ e->e_flags |= EF_DONT_MIME; } strncpy(bbuf, p, blen); bbuf[blen] = '\0'; if (tTd(43, 1)) printf("mime8to7: multipart boundary \"%s\"\n", bbuf); for (i = 0; i < MAXMIMENESTING; i++) if (boundaries[i] == NULL) break; if (i >= MAXMIMENESTING) { syserr("mime8to7: multipart nesting boundary too deep"); /* avoid bounce loops */ e->e_flags |= EF_DONT_MIME; } else { boundaries[i] = bbuf; boundaries[i + 1] = NULL; } mci->mci_flags |= MCIF_INMIME; /* skip the early "comment" prologue */ putline("", mci); while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { bt = mimeboundary(buf, boundaries); if (bt != MBT_NOTSEP) break; putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); if (tTd(43, 99)) printf(" ...%s", buf); } if (feof(e->e_dfp)) bt = MBT_FINAL; while (bt != MBT_FINAL) { auto HDR *hdr = NULL; snprintf(buf, sizeof buf, "--%s", bbuf); putline(buf, mci); if (tTd(43, 35)) printf(" ...%s\n", buf); collect(e->e_dfp, FALSE, FALSE, &hdr, e); if (tTd(43, 101)) putline("+++after collect", mci); putheader(mci, hdr, e); if (tTd(43, 101)) putline("+++after putheader", mci); bt = mime8to7(mci, hdr, e, boundaries, flags); } snprintf(buf, sizeof buf, "--%s--", bbuf); putline(buf, mci); if (tTd(43, 35)) printf(" ...%s\n", buf); boundaries[i] = NULL; mci->mci_flags &= ~MCIF_INMIME; /* skip the late "comment" epilogue */ while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { bt = mimeboundary(buf, boundaries); if (bt != MBT_NOTSEP) break; putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); if (tTd(43, 99)) printf(" ...%s", buf); } if (feof(e->e_dfp)) bt = MBT_FINAL; if (tTd(43, 3)) printf("\t\t\tmime8to7=>%s (multipart)\n", MimeBoundaryNames[bt]); return bt; } /* ** Message/xxx types -- recurse exactly once. ** ** Class 's' is predefined to have "rfc822" only. */ if (strcasecmp(type, "message") == 0) { if (!wordinclass(subtype, 's')) { flags |= M87F_NO8BIT; } else { auto HDR *hdr = NULL; putline("", mci); mci->mci_flags |= MCIF_INMIME; collect(e->e_dfp, FALSE, FALSE, &hdr, e); if (tTd(43, 101)) putline("+++after collect", mci); putheader(mci, hdr, e); if (tTd(43, 101)) putline("+++after putheader", mci); if (hvalue("MIME-Version", hdr) == NULL) putline("MIME-Version: 1.0", mci); bt = mime8to7(mci, hdr, e, boundaries, flags); mci->mci_flags &= ~MCIF_INMIME; return bt; } } /* ** Non-compound body type ** ** Compute the ratio of seven to eight bit characters; ** use that as a heuristic to decide how to do the ** encoding. */ sectionsize = sectionhighbits = 0; if (!bitset(M87F_NO8BIT, flags)) { /* remember where we were */ offset = ftell(e->e_dfp); if (offset == -1) syserr("mime8to7: cannot ftell on df%s", e->e_id); /* do a scan of this body type to count character types */ while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { if (mimeboundary(buf, boundaries) != MBT_NOTSEP) break; for (p = buf; *p != '\0'; p++) { /* count bytes with the high bit set */ sectionsize++; if (bitset(0200, *p)) sectionhighbits++; } /* ** Heuristic: if 1/4 of the first 4K bytes are 8-bit, ** assume base64. This heuristic avoids double-reading ** large graphics or video files. */ if (sectionsize >= 4096 && sectionhighbits > sectionsize / 4) break; } /* return to the original offset for processing */ /* XXX use relative seeks to handle >31 bit file sizes? */ if (fseek(e->e_dfp, offset, SEEK_SET) < 0) syserr("mime8to7: cannot fseek on df%s", e->e_id); else clearerr(e->e_dfp); } /* ** Heuristically determine encoding method. ** If more than 1/8 of the total characters have the ** eighth bit set, use base64; else use quoted-printable. ** However, only encode binary encoded data as base64, ** since otherwise the NL=>CRLF mapping will be a problem. */ if (tTd(43, 8)) { printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n", sectionhighbits, sectionsize, cte == NULL ? "[none]" : cte, type == NULL ? "[none]" : type, subtype == NULL ? "[none]" : subtype); } if (cte != NULL && strcasecmp(cte, "binary") == 0) sectionsize = sectionhighbits; linelen = 0; bp = buf; if (sectionhighbits == 0) { /* no encoding necessary */ if (cte != NULL) { snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %.200s", cte); putline(buf, mci); if (tTd(43, 36)) printf(" ...%s\n", buf); } putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { bt = mimeboundary(buf, boundaries); if (bt != MBT_NOTSEP) break; putline(buf, mci); } if (feof(e->e_dfp)) bt = MBT_FINAL; } else if (!MapNLtoCRLF || (sectionsize / 8 < sectionhighbits && !use_qp)) { /* use base64 encoding */ int c1, c2; if (tTd(43, 36)) printf(" ...Content-Transfer-Encoding: base64\n"); putline("Content-Transfer-Encoding: base64", mci); snprintf(buf, sizeof buf, "X-MIME-Autoconverted: from 8bit to base64 by %s id %s", MyHostName, e->e_id); putline(buf, mci); putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF) { if (linelen > 71) { *bp = '\0'; putline(buf, mci); linelen = 0; bp = buf; } linelen += 4; *bp++ = Base64Code[(c1 >> 2)]; c1 = (c1 & 0x03) << 4; c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); if (c2 == EOF) { *bp++ = Base64Code[c1]; *bp++ = '='; *bp++ = '='; break; } c1 |= (c2 >> 4) & 0x0f; *bp++ = Base64Code[c1]; c1 = (c2 & 0x0f) << 2; c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); if (c2 == EOF) { *bp++ = Base64Code[c1]; *bp++ = '='; break; } c1 |= (c2 >> 6) & 0x03; *bp++ = Base64Code[c1]; *bp++ = Base64Code[c2 & 0x3f]; } *bp = '\0'; putline(buf, mci); } else { /* use quoted-printable encoding */ int c1, c2; int fromstate; BITMAP badchars; /* set up map of characters that must be mapped */ clrbitmap(badchars); for (c1 = 0x00; c1 < 0x20; c1++) setbitn(c1, badchars); clrbitn('\t', badchars); for (c1 = 0x7f; c1 < 0x100; c1++) setbitn(c1, badchars); setbitn('=', badchars); if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags)) for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++) setbitn(*p, badchars); if (tTd(43, 36)) printf(" ...Content-Transfer-Encoding: quoted-printable\n"); putline("Content-Transfer-Encoding: quoted-printable", mci); snprintf(buf, sizeof buf, "X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s", MyHostName, e->e_id); putline(buf, mci); putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; fromstate = 0; c2 = '\n'; while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF) { if (c1 == '\n') { if (c2 == ' ' || c2 == '\t') { *bp++ = '='; *bp++ = Base16Code[(c2 >> 4) & 0x0f]; *bp++ = Base16Code[c2 & 0x0f]; } if (buf[0] == '.' && bp == &buf[1]) { buf[0] = '='; *bp++ = Base16Code[('.' >> 4) & 0x0f]; *bp++ = Base16Code['.' & 0x0f]; } *bp = '\0'; putline(buf, mci); linelen = fromstate = 0; bp = buf; c2 = c1; continue; } if (c2 == ' ' && linelen == 4 && fromstate == 4 && bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) { *bp++ = '='; *bp++ = '2'; *bp++ = '0'; linelen += 3; } else if (c2 == ' ' || c2 == '\t') { *bp++ = c2; linelen++; } if (linelen > 72 && (linelen > 75 || c1 != '.' || (linelen > 73 && c2 == '.'))) { if (linelen > 73 && c2 == '.') bp--; else c2 = '\n'; *bp++ = '='; *bp = '\0'; putline(buf, mci); linelen = fromstate = 0; bp = buf; if (c2 == '.') { *bp++ = '.'; linelen++; } } if (bitnset(c1 & 0xff, badchars)) { *bp++ = '='; *bp++ = Base16Code[(c1 >> 4) & 0x0f]; *bp++ = Base16Code[c1 & 0x0f]; linelen += 3; } else if (c1 != ' ' && c1 != '\t') { if (linelen < 4 && c1 == "From"[linelen]) fromstate++; *bp++ = c1; linelen++; } c2 = c1; } /* output any saved character */ if (c2 == ' ' || c2 == '\t') { *bp++ = '='; *bp++ = Base16Code[(c2 >> 4) & 0x0f]; *bp++ = Base16Code[c2 & 0x0f]; linelen += 3; } if (linelen > 0 || boundaries[0] != NULL) { *bp = '\0'; putline(buf, mci); } } if (tTd(43, 3)) printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); return bt; } /* ** MIME_GETCHAR -- get a character for MIME processing ** ** Treats boundaries as EOF. ** ** Parameters: ** fp -- the input file. ** boundaries -- the current MIME boundaries. ** btp -- if the return value is EOF, *btp is set to ** the type of the boundary. ** ** Returns: ** The next character in the input stream. */ int mime_getchar(fp, boundaries, btp) register FILE *fp; char **boundaries; int *btp; { int c; static u_char *bp = NULL; static int buflen = 0; static bool atbol = TRUE; /* at beginning of line */ static int bt = MBT_SYNTAX; /* boundary type of next EOF */ static u_char buf[128]; /* need not be a full line */ if (buflen > 0) { buflen--; return *bp++; } bp = buf; buflen = 0; c = getc(fp); if (c == '\n') { /* might be part of a MIME boundary */ *bp++ = c; atbol = TRUE; c = getc(fp); if (c == '\n') { ungetc(c, fp); return c; } } if (c != EOF) *bp++ = c; else bt = MBT_FINAL; if (atbol && c == '-') { /* check for a message boundary */ c = getc(fp); if (c != '-') { if (c != EOF) *bp++ = c; else bt = MBT_FINAL; buflen = bp - buf - 1; bp = buf; return *bp++; } /* got "--", now check for rest of separator */ *bp++ = '-'; while (bp < &buf[sizeof buf - 2] && (c = getc(fp)) != EOF && c != '\n') { *bp++ = c; } *bp = '\0'; bt = mimeboundary((char *) &buf[1], boundaries); switch (bt) { case MBT_FINAL: case MBT_INTERMED: /* we have a message boundary */ buflen = 0; *btp = bt; return EOF; } atbol = c == '\n'; if (c != EOF) *bp++ = c; } buflen = bp - buf - 1; if (buflen < 0) { *btp = bt; return EOF; } bp = buf; return *bp++; } /* ** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF ** ** Parameters: ** fp -- the input file. ** boundaries -- the current MIME boundaries. ** btp -- if the return value is EOF, *btp is set to ** the type of the boundary. ** ** Returns: ** The next character in the input stream. */ int mime_getchar_crlf(fp, boundaries, btp) register FILE *fp; char **boundaries; int *btp; { static bool sendlf = FALSE; int c; if (sendlf) { sendlf = FALSE; return '\n'; } c = mime_getchar(fp, boundaries, btp); if (c == '\n' && MapNLtoCRLF) { sendlf = TRUE; return '\r'; } return c; } /* ** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type ** ** Parameters: ** line -- the input line. ** boundaries -- the set of currently pending boundaries. ** ** Returns: ** MBT_NOTSEP -- if this is not a separator line ** MBT_INTERMED -- if this is an intermediate separator ** MBT_FINAL -- if this is a final boundary ** MBT_SYNTAX -- if this is a boundary for the wrong ** enclosure -- i.e., a syntax error. */ int mimeboundary(line, boundaries) register char *line; char **boundaries; { int type = MBT_NOTSEP; int i; int savec; extern int isboundary __P((char *, char **)); if (line[0] != '-' || line[1] != '-' || boundaries == NULL) return MBT_NOTSEP; i = strlen(line); if (line[i - 1] == '\n') i--; /* strip off trailing whitespace */ while (line[i - 1] == ' ' || line[i - 1] == '\t') i--; savec = line[i]; line[i] = '\0'; if (tTd(43, 5)) printf("mimeboundary: line=\"%s\"... ", line); /* check for this as an intermediate boundary */ if (isboundary(&line[2], boundaries) >= 0) type = MBT_INTERMED; else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0) { /* check for a final boundary */ line[i - 2] = '\0'; if (isboundary(&line[2], boundaries) >= 0) type = MBT_FINAL; line[i - 2] = '-'; } line[i] = savec; if (tTd(43, 5)) printf("%s\n", MimeBoundaryNames[type]); return type; } /* ** DEFCHARSET -- return default character set for message ** ** The first choice for character set is for the mailer ** corresponding to the envelope sender. If neither that ** nor the global configuration file has a default character ** set defined, return "unknown-8bit" as recommended by ** RFC 1428 section 3. ** ** Parameters: ** e -- the envelope for this message. ** ** Returns: ** The default character set for that mailer. */ char * defcharset(e) register ENVELOPE *e; { if (e != NULL && e->e_from.q_mailer != NULL && e->e_from.q_mailer->m_defcharset != NULL) return e->e_from.q_mailer->m_defcharset; if (DefaultCharSet != NULL) return DefaultCharSet; return "unknown-8bit"; } /* ** ISBOUNDARY -- is a given string a currently valid boundary? ** ** Parameters: ** line -- the current input line. ** boundaries -- the list of valid boundaries. ** ** Returns: ** The index number in boundaries if the line is found. ** -1 -- otherwise. ** */ int isboundary(line, boundaries) char *line; char **boundaries; { register int i; for (i = 0; boundaries[i] != NULL; i++) { if (strcmp(line, boundaries[i]) == 0) return i; } return -1; } #endif /* MIME8TO7 */ #if MIME7TO8 /* ** MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format ** ** This is a hack. Supports translating the two 7-bit body-encodings ** (quoted-printable and base64) to 8-bit coded bodies. ** ** There is not much point in supporting multipart here, as the UA ** will be able to deal with encoded MIME bodies if it can parse MIME ** multipart messages. ** ** Note also that we wont be called unless it is a text/plain MIME ** message, encoded base64 or QP and mailer flag '9' has been defined ** on mailer. ** ** Contributed by Marius Olaffson . ** ** Parameters: ** mci -- mailer connection information. ** header -- the header for this body part. ** e -- envelope. ** ** Returns: ** none. */ extern int mime_fromqp __P((u_char *, u_char **, int, int)); static char index_64[128] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 }; #define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)]) void mime7to8(mci, header, e) register MCI *mci; HDR *header; register ENVELOPE *e; { register char *p; char *cte; char **pvp; u_char *obp; u_char ch, *fbufp, *obufp; char buf[MAXLINE]; u_char obuf[MAXLINE + 1]; u_char fbuf[MAXLINE + 1]; char pvpbuf[MAXLINE]; extern u_char MimeTokenTab[256]; p = hvalue("Content-Transfer-Encoding", header); if (p == NULL || (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, MimeTokenTab)) == NULL || pvp[0] == NULL) { /* "can't happen" -- upper level should have caught this */ syserr("mime7to8: unparsable CTE %s", p == NULL ? "" : p); /* avoid bounce loops */ e->e_flags |= EF_DONT_MIME; /* cheap failsafe algorithm -- should work on text/plain */ if (p != NULL) { snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %s", p); putline(buf, mci); } putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; while (fgets(buf, sizeof buf, e->e_dfp) != NULL) putline(buf, mci); return; } cataddr(pvp, NULL, buf, sizeof buf, '\0'); cte = newstr(buf); putline("Content-Transfer-Encoding: 8bit", mci); snprintf(buf, sizeof buf, "X-MIME-Autoconverted: from %.200s to 8bit by %s id %s", cte, MyHostName, e->e_id); putline(buf, mci); putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; /* ** Translate body encoding to 8-bit. Supports two types of ** encodings; "base64" and "quoted-printable". Assume qp if ** it is not base64. */ if (strcasecmp(cte, "base64") == 0) { int nchar = 0; int c1, c2, c3, c4; fbufp = fbuf; while ((c1 = fgetc(e->e_dfp)) != EOF) { if (isascii(c1) && isspace(c1)) continue; do { c2 = fgetc(e->e_dfp); } while (isascii(c2) && isspace(c2)); if (c2 == EOF) break; do { c3 = fgetc(e->e_dfp); } while (isascii(c3) && isspace(c3)); if (c3 == EOF) break; do { c4 = fgetc(e->e_dfp); } while (isascii(c4) && isspace(c4)); if (c4 == EOF) break; if (c1 == '=' || c2 == '=') continue; c1 = CHAR64(c1); c2 = CHAR64(c2); *fbufp = (c1 << 2) | ((c2 & 0x30) >> 4); if (*fbufp++ == '\n' || fbuf >= &fbuf[MAXLINE]) { if (*--fbufp != '\n' || *--fbufp != '\r') fbufp++; *fbufp = '\0'; putline((char *) fbuf, mci); fbufp = fbuf; } if (c3 == '=') continue; c3 = CHAR64(c3); *fbufp = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2); if (*fbufp++ == '\n' || fbuf >= &fbuf[MAXLINE]) { if (*--fbufp != '\n' || *--fbufp != '\r') fbufp++; *fbufp = '\0'; putline((char *) fbuf, mci); fbufp = fbuf; } if (c4 == '=') continue; c4 = CHAR64(c4); *fbufp = ((c3 & 0x03) << 6) | c4; if (*fbufp++ == '\n' || fbuf >= &fbuf[MAXLINE]) { if (*--fbufp != '\n' || *--fbufp != '\r') fbufp++; *fbufp = '\0'; putline((char *) fbuf, mci); fbufp = fbuf; } } /* force out partial last line */ if (fbufp > fbuf) { *fbufp = '\0'; putline((char *) fbuf, mci); } } else { /* quoted-printable */ obp = obuf; while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { if (mime_fromqp((u_char *) buf, &obp, 0, &obuf[MAXLINE] - obp) == 0) continue; putline((char *) obuf, mci); obp = obuf; } } if (tTd(43, 3)) printf("\t\t\tmime7to8 => %s to 8bit done\n", cte); } /* ** The following is based on Borenstein's "codes.c" module, with simplifying ** changes as we do not deal with multipart, and to do the translation in-core, ** with an attempt to prevent overrun of output buffers. ** ** What is needed here are changes to defned this code better against ** bad encodings. Questionable to always return 0xFF for bad mappings. */ static char index_hex[128] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 }; #define HEXCHAR(c) (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)]) int mime_fromqp(infile, outfile, state, maxlen) u_char *infile; u_char **outfile; int state; /* Decoding body (0) or header (1) */ int maxlen; /* Max # of chars allowed in outfile */ { int c1, c2; int nchar = 0; while ((c1 = *infile++) != '\0') { if (c1 == '=') { if ((c1 = *infile++) == 0) break; if (c1 == '\n') /* ignore it */ { if (state == 0) return 0; } else { if ((c2 = *infile++) == '\0') break; c1 = HEXCHAR(c1); c2 = HEXCHAR(c2); if (++nchar > maxlen) break; *(*outfile)++ = c1 << 4 | c2; } } else { if (state == 1 && c1 == '_') c1 = ' '; if (++nchar > maxlen) break; *(*outfile)++ = c1; if (c1 == '\n') break; } } *(*outfile)++ = '\0'; return 1; } #endif /* MIME7TO8 */ Index: head/usr.sbin/sendmail/src/readcf.c =================================================================== --- head/usr.sbin/sendmail/src/readcf.c (revision 19843) +++ head/usr.sbin/sendmail/src/readcf.c (revision 19844) @@ -1,2700 +1,2704 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)readcf.c 8.174 (Berkeley) 10/9/96"; +static char sccsid[] = "@(#)readcf.c 8.176 (Berkeley) 11/10/96"; #endif /* not lint */ # include "sendmail.h" # include #if NAMED_BIND # include #endif /* ** READCF -- read control file. ** ** This routine reads the control file and builds the internal ** form. ** ** The file is formatted as a sequence of lines, each taken ** atomically. The first character of each line describes how ** the line is to be interpreted. The lines are: ** Dxval Define macro x to have value val. ** Cxword Put word into class x. ** Fxfile [fmt] Read file for lines to put into ** class x. Use scanf string 'fmt' ** or "%s" if not present. Fmt should ** only produce one string-valued result. ** Hname: value Define header with field-name 'name' ** and value as specified; this will be ** macro expanded immediately before ** use. ** Sn Use rewriting set n. ** Rlhs rhs Rewrite addresses that match lhs to ** be rhs. ** Mn arg=val... Define mailer. n is the internal name. ** Args specify mailer parameters. ** Oxvalue Set option x to value. ** Pname=value Set precedence name to value. ** Vversioncode[/vendorcode] ** Version level/vendor name of ** configuration syntax. ** Kmapname mapclass arguments.... ** Define keyed lookup of a given class. ** Arguments are class dependent. ** Eenvar=value Set the environment value to the given value. ** ** Parameters: ** cfname -- control file name. ** safe -- TRUE if this is the system config file; ** FALSE otherwise. ** e -- the main envelope. ** ** Returns: ** none. ** ** Side Effects: ** Builds several internal tables. */ void readcf(cfname, safe, e) char *cfname; bool safe; register ENVELOPE *e; { FILE *cf; int ruleset = 0; char *q; struct rewrite *rwp = NULL; char *bp; auto char *ep; int nfuzzy; char *file; bool optional; int mid; char buf[MAXLINE]; register char *p; extern char **copyplist(); struct stat statb; char exbuf[MAXLINE]; char pvpbuf[MAXLINE + MAXATOM]; static char *null_list[1] = { NULL }; extern char *munchstring __P((char *, char **, int)); extern void fileclass __P((int, char *, char *, bool, bool)); extern void toomany __P((int, int)); extern void translate_dollars __P((char *)); extern void inithostmaps __P((void)); FileName = cfname; LineNumber = 0; cf = fopen(cfname, "r"); if (cf == NULL) { syserr("cannot open"); exit(EX_OSFILE); } if (fstat(fileno(cf), &statb) < 0) { syserr("cannot fstat"); exit(EX_OSFILE); } if (!S_ISREG(statb.st_mode)) { syserr("not a plain file"); exit(EX_OSFILE); } if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) { if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS) fprintf(stderr, "%s: WARNING: dangerous write permissions\n", FileName); #ifdef LOG if (LogLevel > 0) syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", FileName); #endif } #ifdef XLA xla_zero(); #endif while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) { if (bp[0] == '#') { if (bp != buf) free(bp); continue; } /* do macro expansion mappings */ translate_dollars(bp); /* interpret this line */ errno = 0; switch (bp[0]) { case '\0': case '#': /* comment */ break; case 'R': /* rewriting rule */ for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) continue; if (*p == '\0') { syserr("invalid rewrite line \"%s\" (tab expected)", bp); break; } /* allocate space for the rule header */ if (rwp == NULL) { RewriteRules[ruleset] = rwp = (struct rewrite *) xalloc(sizeof *rwp); } else { rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); rwp = rwp->r_next; } rwp->r_next = NULL; /* expand and save the LHS */ *p = '\0'; expand(&bp[1], exbuf, sizeof exbuf, e); rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, sizeof pvpbuf, NULL, NULL); nfuzzy = 0; if (rwp->r_lhs != NULL) { register char **ap; rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); /* count the number of fuzzy matches in LHS */ for (ap = rwp->r_lhs; *ap != NULL; ap++) { char *botch; botch = NULL; switch (**ap & 0377) { case MATCHZANY: case MATCHANY: case MATCHONE: case MATCHCLASS: case MATCHNCLASS: nfuzzy++; break; case MATCHREPL: botch = "$0-$9"; break; case CANONNET: botch = "$#"; break; case CANONUSER: botch = "$:"; break; case CALLSUBR: botch = "$>"; break; case CONDIF: botch = "$?"; break; case CONDFI: botch = "$."; break; case HOSTBEGIN: botch = "$["; break; case HOSTEND: botch = "$]"; break; case LOOKUPBEGIN: botch = "$("; break; case LOOKUPEND: botch = "$)"; break; } if (botch != NULL) syserr("Inappropriate use of %s on LHS", botch); } } else { syserr("R line: null LHS"); rwp->r_lhs = null_list; } /* expand and save the RHS */ while (*++p == '\t') continue; q = p; while (*p != '\0' && *p != '\t') p++; *p = '\0'; expand(q, exbuf, sizeof exbuf, e); rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, sizeof pvpbuf, NULL, NULL); if (rwp->r_rhs != NULL) { register char **ap; rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); /* check no out-of-bounds replacements */ nfuzzy += '0'; for (ap = rwp->r_rhs; *ap != NULL; ap++) { char *botch; botch = NULL; switch (**ap & 0377) { case MATCHREPL: if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) { syserr("replacement $%c out of bounds", (*ap)[1]); } break; case MATCHZANY: botch = "$*"; break; case MATCHANY: botch = "$+"; break; case MATCHONE: botch = "$-"; break; case MATCHCLASS: botch = "$="; break; case MATCHNCLASS: botch = "$~"; break; } if (botch != NULL) syserr("Inappropriate use of %s on RHS", botch); } } else { syserr("R line: null RHS"); rwp->r_rhs = null_list; } break; case 'S': /* select rewriting set */ expand(&bp[1], exbuf, sizeof exbuf, e); ruleset = strtorwset(exbuf, NULL, ST_ENTER); if (ruleset < 0) break; rwp = RewriteRules[ruleset]; if (rwp != NULL) { if (OpMode == MD_TEST || tTd(37, 1)) printf("WARNING: Ruleset %s has multiple definitions\n", &bp[1]); while (rwp->r_next != NULL) rwp = rwp->r_next; } break; case 'D': /* macro definition */ mid = macid(&bp[1], &ep); p = munchstring(ep, NULL, '\0'); define(mid, newstr(p), e); break; case 'H': /* required header line */ (void) chompheader(&bp[1], TRUE, NULL, e); break; case 'C': /* word class */ case 'T': /* trusted user (set class `t') */ if (bp[0] == 'C') { mid = macid(&bp[1], &ep); expand(ep, exbuf, sizeof exbuf, e); p = exbuf; } else { mid = 't'; p = &bp[1]; } while (*p != '\0') { register char *wd; char delim; while (*p != '\0' && isascii(*p) && isspace(*p)) p++; wd = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; delim = *p; *p = '\0'; if (wd[0] != '\0') setclass(mid, wd); *p = delim; } break; case 'F': /* word class from file */ mid = macid(&bp[1], &ep); for (p = ep; isascii(*p) && isspace(*p); ) p++; if (p[0] == '-' && p[1] == 'o') { optional = TRUE; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; while (isascii(*p) && isspace(*p)) p++; } else optional = FALSE; file = p; if (*file == '|') p = "%s"; else { while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p == '\0') p = "%s"; else { *p = '\0'; while (isascii(*++p) && isspace(*p)) continue; } } fileclass(mid, file, p, safe, optional); break; #ifdef XLA case 'L': /* extended load average description */ xla_init(&bp[1]); break; #endif #ifdef SUN_EXTENSIONS case 'L': /* lookup macro */ case 'G': /* lookup class */ /* reserved for Sun -- NIS+ database lookup */ goto badline; #endif case 'M': /* define mailer */ makemailer(&bp[1]); break; case 'O': /* set option */ setoption(bp[1], &bp[2], safe, FALSE, e); break; case 'P': /* set precedence */ if (NumPriorities >= MAXPRIORITIES) { toomany('P', MAXPRIORITIES); break; } for (p = &bp[1]; *p != '\0' && *p != '='; p++) continue; if (*p == '\0') goto badline; *p = '\0'; Priorities[NumPriorities].pri_name = newstr(&bp[1]); Priorities[NumPriorities].pri_val = atoi(++p); NumPriorities++; break; case 'V': /* configuration syntax version */ for (p = &bp[1]; isascii(*p) && isspace(*p); p++) continue; if (!isascii(*p) || !isdigit(*p)) { syserr("invalid argument to V line: \"%.20s\"", &bp[1]); break; } ConfigLevel = strtol(p, &ep, 10); /* ** Do heuristic tweaking for back compatibility. */ if (ConfigLevel >= 5) { /* level 5 configs have short name in $w */ p = macvalue('w', e); if (p != NULL && (p = strchr(p, '.')) != NULL) *p = '\0'; define('w', macvalue('w', e), e); } if (ConfigLevel >= 6) { ColonOkInAddr = FALSE; } /* ** Look for vendor code. */ if (*ep++ == '/') { extern bool setvendor __P((char *)); /* extract vendor code */ for (p = ep; isascii(*p) && isalpha(*p); ) p++; *p = '\0'; if (!setvendor(ep)) syserr("invalid V line vendor code: \"%s\"", ep); } break; case 'K': expand(&bp[1], exbuf, sizeof exbuf, e); (void) makemapentry(exbuf); break; case 'E': p = strchr(bp, '='); if (p != NULL) *p++ = '\0'; setuserenv(&bp[1], p); break; default: badline: syserr("unknown control line \"%s\"", bp); } if (bp != buf) free(bp); } if (ferror(cf)) { syserr("I/O read error"); exit(EX_OSFILE); } fclose(cf); FileName = NULL; /* initialize host maps from local service tables */ inithostmaps(); /* determine if we need to do special name-server frotz */ { int nmaps; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; nmaps = switch_map_find("hosts", maptype, mapreturn); UseNameServer = FALSE; if (nmaps > 0 && nmaps <= MAXMAPSTACK) { register int mapno; for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++) { if (strcmp(maptype[mapno], "dns") == 0) UseNameServer = TRUE; } } #ifdef HESIOD nmaps = switch_map_find("passwd", maptype, mapreturn); UseHesiod = FALSE; if (nmaps > 0 && nmaps <= MAXMAPSTACK) { register int mapno; for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++) { if (strcmp(maptype[mapno], "hesiod") == 0) UseHesiod = TRUE; } } #endif } } /* ** TRANSLATE_DOLLARS -- convert $x into internal form ** ** Actually does all appropriate pre-processing of a config line ** to turn it into internal form. ** ** Parameters: ** bp -- the buffer to translate. ** ** Returns: ** None. The buffer is translated in place. Since the ** translations always make the buffer shorter, this is ** safe without a size parameter. */ void translate_dollars(bp) char *bp; { register char *p; auto char *ep; for (p = bp; *p != '\0'; p++) { if (*p == '#' && p > bp && ConfigLevel >= 3) { /* this is an on-line comment */ register char *e; switch (*--p & 0377) { case MACROEXPAND: /* it's from $# -- let it go through */ p++; break; case '\\': /* it's backslash escaped */ (void) strcpy(p, p + 1); break; default: /* delete preceeding white space */ while (isascii(*p) && isspace(*p) && *p != '\n' && p > bp) p--; if ((e = strchr(++p, '\n')) != NULL) (void) strcpy(p, e); else *p-- = '\0'; break; } continue; } if (*p != '$' || p[1] == '\0') continue; if (p[1] == '$') { /* actual dollar sign.... */ (void) strcpy(p, p + 1); continue; } /* convert to macro expansion character */ *p++ = MACROEXPAND; /* special handling for $=, $~, $&, and $? */ if (*p == '=' || *p == '~' || *p == '&' || *p == '?') p++; /* convert macro name to code */ *p = macid(p, &ep); if (ep != p) strcpy(p + 1, ep); } /* strip trailing white space from the line */ while (--p > bp && isascii(*p) && isspace(*p)) *p = '\0'; } /* ** TOOMANY -- signal too many of some option ** ** Parameters: ** id -- the id of the error line ** maxcnt -- the maximum possible values ** ** Returns: ** none. ** ** Side Effects: ** gives a syserr. */ void toomany(id, maxcnt) int id; int maxcnt; { syserr("too many %c lines, %d max", id, maxcnt); } /* ** FILECLASS -- read members of a class from a file ** ** Parameters: ** class -- class to define. ** filename -- name of file to read. ** fmt -- scanf string to use for match. ** safe -- if set, this is a safe read. ** optional -- if set, it is not an error for the file to ** not exist. ** ** Returns: ** none ** ** Side Effects: ** ** puts all lines in filename that match a scanf into ** the named class. */ void fileclass(class, filename, fmt, safe, optional) int class; char *filename; char *fmt; bool safe; bool optional; { FILE *f; int sff; - int pid; + pid_t pid; register char *p; char buf[MAXLINE]; if (tTd(37, 2)) printf("fileclass(%s, fmt=%s)\n", filename, fmt); if (filename[0] == '|') { auto int fd; int i; char *argv[MAXPV + 1]; i = 0; for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t")) { if (i >= MAXPV) break; argv[i++] = p; } argv[i] = NULL; pid = prog_open(argv, &fd, CurEnv); if (pid < 0) f = NULL; else f = fdopen(fd, "r"); } else { pid = -1; sff = SFF_REGONLY; if (safe) sff |= SFF_OPENASROOT; f = safefopen(filename, O_RDONLY, 0, sff); } if (f == NULL) { if (!optional) syserr("fileclass: cannot open %s", filename); return; } while (fgets(buf, sizeof buf, f) != NULL) { register char *p; # if SCANF char wordbuf[MAXNAME+1]; # endif if (buf[0] == '#') continue; # if SCANF if (sscanf(buf, fmt, wordbuf) != 1) continue; p = wordbuf; # else /* SCANF */ p = buf; # endif /* SCANF */ /* ** Break up the match into words. */ while (*p != '\0') { register char *q; /* strip leading spaces */ while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; /* find the end of the word */ q = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p != '\0') *p++ = '\0'; /* enter the word in the symbol table */ setclass(class, q); } } (void) fclose(f); if (pid > 0) (void) waitfor(pid); } /* ** MAKEMAILER -- define a new mailer. ** ** Parameters: ** line -- description of mailer. This is in labeled ** fields. The fields are: ** A -- the argv for this mailer ** C -- the character set for MIME conversions ** D -- the directory to run in ** E -- the eol string ** F -- the flags associated with the mailer ** L -- the maximum line length ** M -- the maximum message size ** N -- the niceness at which to run ** P -- the path to the mailer ** R -- the recipient rewriting set ** S -- the sender rewriting set ** T -- the mailer type (for DSNs) ** U -- the uid to run as ** The first word is the canonical name of the mailer. ** ** Returns: ** none. ** ** Side Effects: ** enters the mailer into the mailer table. */ void makemailer(line) char *line; { register char *p; register struct mailer *m; register STAB *s; int i; char fcode; auto char *endp; extern int NextMailer; extern char **makeargv(); extern char *munchstring __P((char *, char **, int)); /* allocate a mailer and set up defaults */ m = (struct mailer *) xalloc(sizeof *m); bzero((char *) m, sizeof *m); /* collect the mailer name */ for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) continue; if (*p != '\0') *p++ = '\0'; if (line[0] == '\0') syserr("name required for mailer"); m->m_name = newstr(line); /* now scan through and assign info from the fields */ while (*p != '\0') { auto char *delimptr; while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) p++; /* p now points to field code */ fcode = *p; while (*p != '\0' && *p != '=' && *p != ',') p++; if (*p++ != '=') { syserr("mailer %s: `=' expected", m->m_name); return; } while (isascii(*p) && isspace(*p)) p++; /* p now points to the field body */ p = munchstring(p, &delimptr, ','); /* install the field into the mailer struct */ switch (fcode) { case 'P': /* pathname */ if (*p == '\0') syserr("mailer %s: empty path name", m->m_name); m->m_mailer = newstr(p); break; case 'F': /* flags */ for (; *p != '\0'; p++) if (!(isascii(*p) && isspace(*p))) setbitn(*p, m->m_flags); break; case 'S': /* sender rewriting ruleset */ case 'R': /* recipient rewriting ruleset */ i = strtorwset(p, &endp, ST_ENTER); if (i < 0) return; if (fcode == 'S') m->m_sh_rwset = m->m_se_rwset = i; else m->m_rh_rwset = m->m_re_rwset = i; p = endp; if (*p++ == '/') { i = strtorwset(p, NULL, ST_ENTER); if (i < 0) return; if (fcode == 'S') m->m_sh_rwset = i; else m->m_rh_rwset = i; } break; case 'E': /* end of line string */ if (*p == '\0') syserr("mailer %s: null end-of-line string", m->m_name); m->m_eol = newstr(p); break; case 'A': /* argument vector */ if (*p == '\0') syserr("mailer %s: null argument vector", m->m_name); m->m_argv = makeargv(p); break; case 'M': /* maximum message size */ m->m_maxsize = atol(p); break; case 'L': /* maximum line length */ m->m_linelimit = atoi(p); if (m->m_linelimit < 0) m->m_linelimit = 0; break; case 'N': /* run niceness */ m->m_nice = atoi(p); break; case 'D': /* working directory */ if (*p == '\0') syserr("mailer %s: null working directory", m->m_name); m->m_execdir = newstr(p); break; case 'C': /* default charset */ if (*p == '\0') syserr("mailer %s: null charset", m->m_name); m->m_defcharset = newstr(p); break; case 'T': /* MTA-Name/Address/Diagnostic types */ /* extract MTA name type; default to "dns" */ m->m_mtatype = newstr(p); p = strchr(m->m_mtatype, '/'); if (p != NULL) { *p++ = '\0'; if (*p == '\0') p = NULL; } if (*m->m_mtatype == '\0') m->m_mtatype = "dns"; /* extract address type; default to "rfc822" */ m->m_addrtype = p; if (p != NULL) p = strchr(p, '/'); if (p != NULL) { *p++ = '\0'; if (*p == '\0') p = NULL; } if (m->m_addrtype == NULL || *m->m_addrtype == '\0') m->m_addrtype = "rfc822"; /* extract diagnostic type; default to "smtp" */ m->m_diagtype = p; if (m->m_diagtype == NULL || *m->m_diagtype == '\0') m->m_diagtype = "smtp"; break; case 'U': /* user id */ if (isascii(*p) && !isdigit(*p)) { char *q = p; struct passwd *pw; while (*p != '\0' && isascii(*p) && (isalnum(*p) || strchr("-_", *p) != NULL)) p++; while (isascii(*p) && isspace(*p)) *p++ = '\0'; if (*p != '\0') *p++ = '\0'; if (*q == '\0') syserr("mailer %s: null user name", m->m_name); pw = sm_getpwnam(q); if (pw == NULL) syserr("readcf: mailer U= flag: unknown user %s", q); else { m->m_uid = pw->pw_uid; m->m_gid = pw->pw_gid; } } else { auto char *q; m->m_uid = strtol(p, &q, 0); p = q; + while (isascii(*p) && isspace(*p)) + p++; + if (*p != '\0') + p++; } while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; if (isascii(*p) && !isdigit(*p)) { char *q = p; struct group *gr; while (isascii(*p) && isalnum(*p)) p++; *p++ = '\0'; if (*q == '\0') syserr("mailer %s: null group name", m->m_name); gr = getgrnam(q); if (gr == NULL) syserr("readcf: mailer U= flag: unknown group %s", q); else m->m_gid = gr->gr_gid; } else { m->m_gid = strtol(p, NULL, 0); } break; } p = delimptr; } /* do some rationality checking */ if (m->m_argv == NULL) { syserr("M%s: A= argument required", m->m_name); return; } if (m->m_mailer == NULL) { syserr("M%s: P= argument required", m->m_name); return; } if (NextMailer >= MAXMAILERS) { syserr("too many mailers defined (%d max)", MAXMAILERS); return; } /* do some heuristic cleanup for back compatibility */ if (bitnset(M_LIMITS, m->m_flags)) { if (m->m_linelimit == 0) m->m_linelimit = SMTPLINELIM; if (ConfigLevel < 2) setbitn(M_7BITS, m->m_flags); } if (strcmp(m->m_mailer, "[IPC]") == 0 || strcmp(m->m_mailer, "[TCP]") == 0) { if (m->m_mtatype == NULL) m->m_mtatype = "dns"; if (m->m_addrtype == NULL) m->m_addrtype = "rfc822"; if (m->m_diagtype == NULL) m->m_diagtype = "smtp"; } if (m->m_eol == NULL) { char **pp; /* default for SMTP is \r\n; use \n for local delivery */ for (pp = m->m_argv; *pp != NULL; pp++) { char *p; for (p = *pp; *p != '\0'; ) { if ((*p++ & 0377) == MACROEXPAND && *p == 'u') break; } if (*p != '\0') break; } if (*pp == NULL) m->m_eol = "\r\n"; else m->m_eol = "\n"; } /* enter the mailer into the symbol table */ s = stab(m->m_name, ST_MAILER, ST_ENTER); if (s->s_mailer != NULL) { i = s->s_mailer->m_mno; free(s->s_mailer); } else { i = NextMailer++; } Mailer[i] = s->s_mailer = m; m->m_mno = i; } /* ** MUNCHSTRING -- translate a string into internal form. ** ** Parameters: ** p -- the string to munch. ** delimptr -- if non-NULL, set to the pointer of the ** field delimiter character. ** delim -- the delimiter for the field. ** ** Returns: ** the munched string. */ char * munchstring(p, delimptr, delim) register char *p; char **delimptr; int delim; { register char *q; bool backslash = FALSE; bool quotemode = FALSE; static char buf[MAXLINE]; for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++) { if (backslash) { /* everything is roughly literal */ backslash = FALSE; switch (*p) { case 'r': /* carriage return */ *q++ = '\r'; continue; case 'n': /* newline */ *q++ = '\n'; continue; case 'f': /* form feed */ *q++ = '\f'; continue; case 'b': /* backspace */ *q++ = '\b'; continue; } *q++ = *p; } else { if (*p == '\\') backslash = TRUE; else if (*p == '"') quotemode = !quotemode; else if (quotemode || *p != delim) *q++ = *p; else break; } } if (delimptr != NULL) *delimptr = p; *q++ = '\0'; return (buf); } /* ** MAKEARGV -- break up a string into words ** ** Parameters: ** p -- the string to break up. ** ** Returns: ** a char **argv (dynamically allocated) ** ** Side Effects: ** munges p. */ char ** makeargv(p) register char *p; { char *q; int i; char **avp; char *argv[MAXPV + 1]; /* take apart the words */ i = 0; while (*p != '\0' && i < MAXPV) { q = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; while (isascii(*p) && isspace(*p)) *p++ = '\0'; argv[i++] = newstr(q); } argv[i++] = NULL; /* now make a copy of the argv */ avp = (char **) xalloc(sizeof *avp * i); bcopy((char *) argv, (char *) avp, sizeof *avp * i); return (avp); } /* ** PRINTRULES -- print rewrite rules (for debugging) ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** prints rewrite rules. */ void printrules() { register struct rewrite *rwp; register int ruleset; for (ruleset = 0; ruleset < 10; ruleset++) { if (RewriteRules[ruleset] == NULL) continue; printf("\n----Rule Set %d:", ruleset); for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) { printf("\nLHS:"); printav(rwp->r_lhs); printf("RHS:"); printav(rwp->r_rhs); } } } /* ** PRINTMAILER -- print mailer structure (for debugging) ** ** Parameters: ** m -- the mailer to print ** ** Returns: ** none. */ void printmailer(m) register MAILER *m; { int j; printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=", m->m_mno, m->m_name, m->m_mailer, m->m_se_rwset, m->m_sh_rwset, m->m_re_rwset, m->m_rh_rwset, m->m_maxsize, m->m_uid, m->m_gid); for (j = '\0'; j <= '\177'; j++) if (bitnset(j, m->m_flags)) (void) putchar(j); printf(" L=%d E=", m->m_linelimit); xputs(m->m_eol); if (m->m_defcharset != NULL) printf(" C=%s", m->m_defcharset); printf(" T=%s/%s/%s", m->m_mtatype == NULL ? "" : m->m_mtatype, m->m_addrtype == NULL ? "" : m->m_addrtype, m->m_diagtype == NULL ? "" : m->m_diagtype); if (m->m_argv != NULL) { char **a = m->m_argv; printf(" A="); while (*a != NULL) { if (a != m->m_argv) printf(" "); xputs(*a++); } } printf("\n"); } /* ** SETOPTION -- set global processing option ** ** Parameters: ** opt -- option name. ** val -- option value (as a text string). ** safe -- set if this came from a configuration file. ** Some options (if set from the command line) will ** reset the user id to avoid security problems. ** sticky -- if set, don't let other setoptions override ** this value. ** e -- the main envelope. ** ** Returns: ** none. ** ** Side Effects: ** Sets options as implied by the arguments. */ static BITMAP StickyOpt; /* set if option is stuck */ extern void settimeout __P((char *, char *)); #if NAMED_BIND struct resolverflags { char *rf_name; /* name of the flag */ long rf_bits; /* bits to set/clear */ } ResolverFlags[] = { { "debug", RES_DEBUG }, { "aaonly", RES_AAONLY }, { "usevc", RES_USEVC }, { "primary", RES_PRIMARY }, { "igntc", RES_IGNTC }, { "recurse", RES_RECURSE }, { "defnames", RES_DEFNAMES }, { "stayopen", RES_STAYOPEN }, { "dnsrch", RES_DNSRCH }, { "true", 0 }, /* avoid error on old syntax */ { NULL, 0 } }; #endif struct optioninfo { char *o_name; /* long name of option */ u_char o_code; /* short name of option */ bool o_safe; /* safe for random people to use */ } OptionTab[] = { { "SevenBitInput", '7', TRUE }, #if MIME8TO7 { "EightBitMode", '8', TRUE }, #endif { "AliasFile", 'A', FALSE }, { "AliasWait", 'a', FALSE }, { "BlankSub", 'B', FALSE }, { "MinFreeBlocks", 'b', TRUE }, { "CheckpointInterval", 'C', TRUE }, { "HoldExpensive", 'c', FALSE }, { "AutoRebuildAliases", 'D', FALSE }, { "DeliveryMode", 'd', TRUE }, { "ErrorHeader", 'E', FALSE }, { "ErrorMode", 'e', TRUE }, { "TempFileMode", 'F', FALSE }, { "SaveFromLine", 'f', FALSE }, { "MatchGECOS", 'G', FALSE }, { "HelpFile", 'H', FALSE }, { "MaxHopCount", 'h', FALSE }, { "ResolverOptions", 'I', FALSE }, { "IgnoreDots", 'i', TRUE }, { "ForwardPath", 'J', FALSE }, { "SendMimeErrors", 'j', TRUE }, { "ConnectionCacheSize", 'k', FALSE }, { "ConnectionCacheTimeout", 'K', FALSE }, { "UseErrorsTo", 'l', FALSE }, { "LogLevel", 'L', TRUE }, { "MeToo", 'm', TRUE }, { "CheckAliases", 'n', FALSE }, { "OldStyleHeaders", 'o', TRUE }, { "DaemonPortOptions", 'O', FALSE }, { "PrivacyOptions", 'p', TRUE }, { "PostmasterCopy", 'P', FALSE }, { "QueueFactor", 'q', FALSE }, { "QueueDirectory", 'Q', FALSE }, { "DontPruneRoutes", 'R', FALSE }, { "Timeout", 'r', FALSE }, { "StatusFile", 'S', FALSE }, { "SuperSafe", 's', TRUE }, { "QueueTimeout", 'T', FALSE }, { "TimeZoneSpec", 't', FALSE }, { "UserDatabaseSpec", 'U', FALSE }, { "DefaultUser", 'u', FALSE }, { "FallbackMXhost", 'V', FALSE }, { "Verbose", 'v', TRUE }, { "TryNullMXList", 'w', TRUE }, { "QueueLA", 'x', FALSE }, { "RefuseLA", 'X', FALSE }, { "RecipientFactor", 'y', FALSE }, { "ForkEachJob", 'Y', FALSE }, { "ClassFactor", 'z', FALSE }, { "RetryFactor", 'Z', FALSE }, #define O_QUEUESORTORD 0x81 { "QueueSortOrder", O_QUEUESORTORD, TRUE }, #define O_HOSTSFILE 0x82 { "HostsFile", O_HOSTSFILE, FALSE }, #define O_MQA 0x83 { "MinQueueAge", O_MQA, TRUE }, #define O_DEFCHARSET 0x85 { "DefaultCharSet", O_DEFCHARSET, TRUE }, #define O_SSFILE 0x86 { "ServiceSwitchFile", O_SSFILE, FALSE }, #define O_DIALDELAY 0x87 { "DialDelay", O_DIALDELAY, TRUE }, #define O_NORCPTACTION 0x88 { "NoRecipientAction", O_NORCPTACTION, TRUE }, #define O_SAFEFILEENV 0x89 { "SafeFileEnvironment", O_SAFEFILEENV, FALSE }, #define O_MAXMSGSIZE 0x8a { "MaxMessageSize", O_MAXMSGSIZE, FALSE }, #define O_COLONOKINADDR 0x8b { "ColonOkInAddr", O_COLONOKINADDR, TRUE }, #define O_MAXQUEUERUN 0x8c { "MaxQueueRunSize", O_MAXQUEUERUN, TRUE }, #define O_MAXCHILDREN 0x8d { "MaxDaemonChildren", O_MAXCHILDREN, FALSE }, #define O_KEEPCNAMES 0x8e { "DontExpandCnames", O_KEEPCNAMES, FALSE }, #define O_MUSTQUOTE 0x8f { "MustQuoteChars", O_MUSTQUOTE, FALSE }, #define O_SMTPGREETING 0x90 { "SmtpGreetingMessage", O_SMTPGREETING, FALSE }, #define O_UNIXFROM 0x91 { "UnixFromLine", O_UNIXFROM, FALSE }, #define O_OPCHARS 0x92 { "OperatorChars", O_OPCHARS, FALSE }, #define O_DONTINITGRPS 0x93 { "DontInitGroups", O_DONTINITGRPS, TRUE }, #define O_SLFH 0x94 { "SingleLineFromHeader", O_SLFH, TRUE }, #define O_ABH 0x95 { "AllowBogusHELO", O_ABH, TRUE }, #define O_CONNTHROT 0x97 { "ConnectionRateThrottle", O_CONNTHROT, FALSE }, #define O_UGW 0x99 { "UnsafeGroupWrites", O_UGW, FALSE }, #define O_DBLBOUNCE 0x9a { "DoubleBounceAddress", O_DBLBOUNCE, FALSE }, #define O_HSDIR 0x9b { "HostStatusDirectory", O_HSDIR, FALSE }, #define O_SINGTHREAD 0x9c { "SingleThreadDelivery", O_SINGTHREAD, FALSE }, #define O_RUNASUSER 0x9d { "RunAsUser", O_RUNASUSER, FALSE }, { NULL, '\0', FALSE } }; void setoption(opt, val, safe, sticky, e) int opt; char *val; bool safe; bool sticky; register ENVELOPE *e; { register char *p; register struct optioninfo *o; char *subopt; int mid; auto char *ep; char buf[50]; extern bool atobool(); extern time_t convtime(); extern int QueueLA; extern int RefuseLA; extern bool Warn_Q_option; extern void setalias __P((char *)); extern int atooct __P((char *)); extern void setdefuser __P((void)); extern void setdaemonoptions __P((char *)); errno = 0; if (opt == ' ') { /* full word options */ struct optioninfo *sel; p = strchr(val, '='); if (p == NULL) p = &val[strlen(val)]; while (*--p == ' ') continue; while (*++p == ' ') *p = '\0'; if (p == val) { syserr("readcf: null option name"); return; } if (*p == '=') *p++ = '\0'; while (*p == ' ') p++; subopt = strchr(val, '.'); if (subopt != NULL) *subopt++ = '\0'; sel = NULL; for (o = OptionTab; o->o_name != NULL; o++) { if (strncasecmp(o->o_name, val, strlen(val)) != 0) continue; if (strlen(o->o_name) == strlen(val)) { /* completely specified -- this must be it */ sel = NULL; break; } if (sel != NULL) break; sel = o; } if (sel != NULL && o->o_name == NULL) o = sel; else if (o->o_name == NULL) { syserr("readcf: unknown option name %s", val); return; } else if (sel != NULL) { syserr("readcf: ambiguous option name %s (matches %s and %s)", val, sel->o_name, o->o_name); return; } if (strlen(val) != strlen(o->o_name)) { bool oldVerbose = Verbose; Verbose = TRUE; message("Option %s used as abbreviation for %s", val, o->o_name); Verbose = oldVerbose; } opt = o->o_code; val = p; } else { for (o = OptionTab; o->o_name != NULL; o++) { if (o->o_code == opt) break; } subopt = NULL; } if (tTd(37, 1)) { printf(isascii(opt) && isprint(opt) ? "setoption %s (%c).%s=" : "setoption %s (0x%x).%s=", o->o_name == NULL ? "" : o->o_name, opt, subopt == NULL ? "" : subopt); xputs(val); } /* ** See if this option is preset for us. */ if (!sticky && bitnset(opt, StickyOpt)) { if (tTd(37, 1)) printf(" (ignored)\n"); return; } /* ** Check to see if this option can be specified by this user. */ if (!safe && RealUid == 0) safe = TRUE; if (!safe && !o->o_safe) { if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) { if (tTd(37, 1)) printf(" (unsafe)"); if (RealUid != geteuid()) { if (tTd(37, 1)) printf("(Resetting uid)"); endpwent(); (void) setgid(RealGid); (void) setuid(RealUid); } } } if (tTd(37, 1)) printf("\n"); switch (opt & 0xff) { case '7': /* force seven-bit input */ SevenBitInput = atobool(val); break; #if MIME8TO7 case '8': /* handling of 8-bit input */ switch (*val) { case 'm': /* convert 8-bit, convert MIME */ MimeMode = MM_CVTMIME|MM_MIME8BIT; break; case 'p': /* pass 8 bit, convert MIME */ MimeMode = MM_CVTMIME|MM_PASS8BIT; break; case 's': /* strict adherence */ MimeMode = MM_CVTMIME; break; #if 0 case 'r': /* reject 8-bit, don't convert MIME */ MimeMode = 0; break; case 'j': /* "just send 8" */ MimeMode = MM_PASS8BIT; break; case 'a': /* encode 8 bit if available */ MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; break; case 'c': /* convert 8 bit to MIME, never 7 bit */ MimeMode = MM_MIME8BIT; break; #endif default: syserr("Unknown 8-bit mode %c", *val); exit(EX_USAGE); } break; #endif case 'A': /* set default alias file */ if (val[0] == '\0') setalias("aliases"); else setalias(val); break; case 'a': /* look N minutes for "@:@" in alias file */ if (val[0] == '\0') SafeAlias = 5 * 60; /* five minutes */ else SafeAlias = convtime(val, 'm'); break; case 'B': /* substitution for blank character */ SpaceSub = val[0]; if (SpaceSub == '\0') SpaceSub = ' '; break; case 'b': /* min blocks free on queue fs/max msg size */ p = strchr(val, '/'); if (p != NULL) { *p++ = '\0'; MaxMessageSize = atol(p); } MinBlocksFree = atol(val); break; case 'c': /* don't connect to "expensive" mailers */ NoConnect = atobool(val); break; case 'C': /* checkpoint every N addresses */ CheckpointInterval = atoi(val); break; case 'd': /* delivery mode */ switch (*val) { case '\0': e->e_sendmode = SM_DELIVER; break; case SM_QUEUE: /* queue only */ case SM_DEFER: /* queue only and defer map lookups */ #ifndef QUEUE syserr("need QUEUE to set -odqueue or -oddefer"); #endif /* QUEUE */ /* fall through..... */ case SM_DELIVER: /* do everything */ case SM_FORK: /* fork after verification */ e->e_sendmode = *val; break; default: syserr("Unknown delivery mode %c", *val); exit(EX_USAGE); } break; case 'D': /* rebuild alias database as needed */ AutoRebuild = atobool(val); break; case 'E': /* error message header/header file */ if (*val != '\0') ErrMsgFile = newstr(val); break; case 'e': /* set error processing mode */ switch (*val) { case EM_QUIET: /* be silent about it */ case EM_MAIL: /* mail back */ case EM_BERKNET: /* do berknet error processing */ case EM_WRITE: /* write back (or mail) */ case EM_PRINT: /* print errors normally (default) */ e->e_errormode = *val; break; } break; case 'F': /* file mode */ FileMode = atooct(val) & 0777; break; case 'f': /* save Unix-style From lines on front */ SaveFrom = atobool(val); break; case 'G': /* match recipients against GECOS field */ MatchGecos = atobool(val); break; case 'g': /* default gid */ g_opt: if (isascii(*val) && isdigit(*val)) DefGid = atoi(val); else { register struct group *gr; DefGid = -1; gr = getgrnam(val); if (gr == NULL) syserr("readcf: option %c: unknown group %s", opt, val); else DefGid = gr->gr_gid; } break; case 'H': /* help file */ if (val[0] == '\0') HelpFile = "sendmail.hf"; else HelpFile = newstr(val); break; case 'h': /* maximum hop count */ MaxHopCount = atoi(val); break; case 'I': /* use internet domain name server */ #if NAMED_BIND for (p = val; *p != 0; ) { bool clearmode; char *q; struct resolverflags *rfp; while (*p == ' ') p++; if (*p == '\0') break; clearmode = FALSE; if (*p == '-') clearmode = TRUE; else if (*p != '+') p--; p++; q = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p != '\0') *p++ = '\0'; if (strcasecmp(q, "HasWildcardMX") == 0) { HasWildcardMX = !clearmode; continue; } for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) { if (strcasecmp(q, rfp->rf_name) == 0) break; } if (rfp->rf_name == NULL) syserr("readcf: I option value %s unrecognized", q); else if (clearmode) _res.options &= ~rfp->rf_bits; else _res.options |= rfp->rf_bits; } if (tTd(8, 2)) printf("_res.options = %x, HasWildcardMX = %d\n", _res.options, HasWildcardMX); #else usrerr("name server (I option) specified but BIND not compiled in"); #endif break; case 'i': /* ignore dot lines in message */ IgnrDot = atobool(val); break; case 'j': /* send errors in MIME (RFC 1341) format */ SendMIMEErrors = atobool(val); break; case 'J': /* .forward search path */ ForwardPath = newstr(val); break; case 'k': /* connection cache size */ MaxMciCache = atoi(val); if (MaxMciCache < 0) MaxMciCache = 0; break; case 'K': /* connection cache timeout */ MciCacheTimeout = convtime(val, 'm'); break; case 'l': /* use Errors-To: header */ UseErrorsTo = atobool(val); break; case 'L': /* log level */ if (safe || LogLevel < atoi(val)) LogLevel = atoi(val); break; case 'M': /* define macro */ mid = macid(val, &ep); p = newstr(ep); if (!safe) cleanstrcpy(p, p, MAXNAME); define(mid, p, CurEnv); sticky = FALSE; break; case 'm': /* send to me too */ MeToo = atobool(val); break; case 'n': /* validate RHS in newaliases */ CheckAliases = atobool(val); break; /* 'N' available -- was "net name" */ case 'O': /* daemon options */ #ifdef DAEMON setdaemonoptions(val); #else syserr("DaemonPortOptions (O option) set but DAEMON not compiled in"); #endif break; case 'o': /* assume old style headers */ if (atobool(val)) CurEnv->e_flags |= EF_OLDSTYLE; else CurEnv->e_flags &= ~EF_OLDSTYLE; break; case 'p': /* select privacy level */ p = val; for (;;) { register struct prival *pv; extern struct prival PrivacyValues[]; while (isascii(*p) && (isspace(*p) || ispunct(*p))) p++; if (*p == '\0') break; val = p; while (isascii(*p) && isalnum(*p)) p++; if (*p != '\0') *p++ = '\0'; for (pv = PrivacyValues; pv->pv_name != NULL; pv++) { if (strcasecmp(val, pv->pv_name) == 0) break; } if (pv->pv_name == NULL) syserr("readcf: Op line: %s unrecognized", val); PrivacyFlags |= pv->pv_flag; } sticky = FALSE; break; case 'P': /* postmaster copy address for returned mail */ PostMasterCopy = newstr(val); break; case 'q': /* slope of queue only function */ QueueFactor = atoi(val); break; case 'Q': /* queue directory */ if (val[0] == '\0') QueueDir = "mqueue"; else QueueDir = newstr(val); if (RealUid != 0 && !safe) Warn_Q_option = TRUE; break; case 'R': /* don't prune routes */ DontPruneRoutes = atobool(val); break; case 'r': /* read timeout */ if (subopt == NULL) inittimeouts(val); else settimeout(subopt, val); break; case 'S': /* status file */ if (val[0] == '\0') StatFile = "sendmail.st"; else StatFile = newstr(val); break; case 's': /* be super safe, even if expensive */ SuperSafe = atobool(val); break; case 'T': /* queue timeout */ p = strchr(val, '/'); if (p != NULL) { *p++ = '\0'; settimeout("queuewarn", p); } settimeout("queuereturn", val); break; case 't': /* time zone name */ TimeZoneSpec = newstr(val); break; case 'U': /* location of user database */ UdbSpec = newstr(val); break; case 'u': /* set default uid */ for (p = val; *p != '\0'; p++) { if (*p == '.' || *p == '/' || *p == ':') { *p++ = '\0'; break; } } if (isascii(*val) && isdigit(*val)) DefUid = atoi(val); else { register struct passwd *pw; DefUid = -1; pw = sm_getpwnam(val); if (pw == NULL) syserr("readcf: option u: unknown user %s", val); else { DefUid = pw->pw_uid; DefGid = pw->pw_gid; } } #ifdef UID_MAX if (DefUid > UID_MAX) { syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored", DefUid, UID_MAX); } #endif setdefuser(); /* handle the group if it is there */ if (*p == '\0') break; val = p; goto g_opt; case 'V': /* fallback MX host */ if (val[0] != '\0') FallBackMX = newstr(val); break; case 'v': /* run in verbose mode */ Verbose = atobool(val); break; case 'w': /* if we are best MX, try host directly */ TryNullMXList = atobool(val); break; /* 'W' available -- was wizard password */ case 'x': /* load avg at which to auto-queue msgs */ QueueLA = atoi(val); break; case 'X': /* load avg at which to auto-reject connections */ RefuseLA = atoi(val); break; case 'y': /* work recipient factor */ WkRecipFact = atoi(val); break; case 'Y': /* fork jobs during queue runs */ ForkQueueRuns = atobool(val); break; case 'z': /* work message class factor */ WkClassFact = atoi(val); break; case 'Z': /* work time factor */ WkTimeFact = atoi(val); break; case O_QUEUESORTORD: /* queue sorting order */ switch (*val) { case 'h': /* Host first */ case 'H': QueueSortOrder = QS_BYHOST; break; case 'p': /* Priority order */ case 'P': QueueSortOrder = QS_BYPRIORITY; break; case 't': /* Submission time */ case 'T': QueueSortOrder = QS_BYTIME; break; default: syserr("Invalid queue sort order \"%s\"", val); } break; case O_HOSTSFILE: /* pathname of /etc/hosts file */ HostsFile = newstr(val); break; case O_MQA: /* minimum queue age between deliveries */ MinQueueAge = convtime(val, 'm'); break; case O_DEFCHARSET: /* default character set for mimefying */ DefaultCharSet = newstr(denlstring(val, TRUE, TRUE)); break; case O_SSFILE: /* service switch file */ ServiceSwitchFile = newstr(val); break; case O_DIALDELAY: /* delay for dial-on-demand operation */ DialDelay = convtime(val, 's'); break; case O_NORCPTACTION: /* what to do if no recipient */ if (strcasecmp(val, "none") == 0) NoRecipientAction = NRA_NO_ACTION; else if (strcasecmp(val, "add-to") == 0) NoRecipientAction = NRA_ADD_TO; else if (strcasecmp(val, "add-apparently-to") == 0) NoRecipientAction = NRA_ADD_APPARENTLY_TO; else if (strcasecmp(val, "add-bcc") == 0) NoRecipientAction = NRA_ADD_BCC; else if (strcasecmp(val, "add-to-undisclosed") == 0) NoRecipientAction = NRA_ADD_TO_UNDISCLOSED; else syserr("Invalid NoRecipientAction: %s", val); break; case O_SAFEFILEENV: /* chroot() environ for writing to files */ SafeFileEnv = newstr(val); break; case O_MAXMSGSIZE: /* maximum message size */ MaxMessageSize = atol(val); break; case O_COLONOKINADDR: /* old style handling of colon addresses */ ColonOkInAddr = atobool(val); break; case O_MAXQUEUERUN: /* max # of jobs in a single queue run */ MaxQueueRun = atol(val); break; case O_MAXCHILDREN: /* max # of children of daemon */ MaxChildren = atoi(val); break; case O_KEEPCNAMES: /* don't expand CNAME records */ DontExpandCnames = atobool(val); break; case O_MUSTQUOTE: /* must quote these characters in phrases */ strcpy(buf, "@,;:\\()[]"); if (strlen(val) < (SIZE_T) sizeof buf - 10) strcat(buf, val); MustQuoteChars = newstr(buf); break; case O_SMTPGREETING: /* SMTP greeting message (old $e macro) */ SmtpGreeting = newstr(munchstring(val, NULL, '\0')); break; case O_UNIXFROM: /* UNIX From_ line (old $l macro) */ UnixFromLine = newstr(munchstring(val, NULL, '\0')); break; case O_OPCHARS: /* operator characters (old $o macro) */ OperatorChars = newstr(munchstring(val, NULL, '\0')); break; case O_DONTINITGRPS: /* don't call initgroups(3) */ DontInitGroups = atobool(val); break; case O_SLFH: /* make sure from fits on one line */ SingleLineFromHeader = atobool(val); break; case O_ABH: /* allow HELO commands with syntax errors */ AllowBogusHELO = atobool(val); break; case O_CONNTHROT: /* connection rate throttle */ ConnRateThrottle = atoi(val); break; case O_UGW: /* group writable files are unsafe */ UnsafeGroupWrites = atobool(val); break; case O_DBLBOUNCE: /* address to which to send double bounces */ if (val[0] != '\0') DoubleBounceAddr = newstr(val); else syserr("readcf: option DoubleBounceAddress: value required"); break; case O_HSDIR: /* persistent host status directory */ if (val[0] != '\0') HostStatDir = newstr(val); break; case O_SINGTHREAD: /* single thread deliveries (requires hsdir) */ SingleThreadDelivery = atobool(val); break; case O_RUNASUSER: /* run bulk of code as this user */ for (p = val; *p != '\0'; p++) { if (*p == '.' || *p == '/' || *p == ':') { *p++ = '\0'; break; } } if (isascii(*val) && isdigit(*val)) RunAsUid = atoi(val); else { register struct passwd *pw; pw = sm_getpwnam(val); if (pw == NULL) syserr("readcf: option RunAsUser: unknown user %s", val); else { RunAsUid = pw->pw_uid; RunAsGid = pw->pw_gid; } } if (*p == '\0') break; if (isascii(*p) && isdigit(*p)) DefGid = atoi(p); else { register struct group *gr; gr = getgrnam(p); if (gr == NULL) syserr("readcf: option RunAsUser: unknown group %s", p); else RunAsGid = gr->gr_gid; } break; default: if (tTd(37, 1)) { if (isascii(opt) && isprint(opt)) printf("Warning: option %c unknown\n", opt); else printf("Warning: option 0x%x unknown\n", opt); } break; } if (sticky) setbitn(opt, StickyOpt); } /* ** SETCLASS -- set a string into a class ** ** Parameters: ** class -- the class to put the string in. ** str -- the string to enter ** ** Returns: ** none. ** ** Side Effects: ** puts the word into the symbol table. */ void setclass(class, str) int class; char *str; { register STAB *s; if (tTd(37, 8)) printf("setclass(%s, %s)\n", macname(class), str); s = stab(str, ST_CLASS, ST_ENTER); setbitn(class, s->s_class); } /* ** MAKEMAPENTRY -- create a map entry ** ** Parameters: ** line -- the config file line ** ** Returns: ** A pointer to the map that has been created. ** NULL if there was a syntax error. ** ** Side Effects: ** Enters the map into the dictionary. */ MAP * makemapentry(line) char *line; { register char *p; char *mapname; char *classname; register STAB *s; STAB *class; for (p = line; isascii(*p) && isspace(*p); p++) continue; if (!(isascii(*p) && isalnum(*p))) { syserr("readcf: config K line: no map name"); return NULL; } mapname = p; while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.') continue; if (*p != '\0') *p++ = '\0'; while (isascii(*p) && isspace(*p)) p++; if (!(isascii(*p) && isalnum(*p))) { syserr("readcf: config K line, map %s: no map class", mapname); return NULL; } classname = p; while (isascii(*++p) && isalnum(*p)) continue; if (*p != '\0') *p++ = '\0'; while (isascii(*p) && isspace(*p)) p++; /* look up the class */ class = stab(classname, ST_MAPCLASS, ST_FIND); if (class == NULL) { syserr("readcf: map %s: class %s not available", mapname, classname); return NULL; } /* enter the map */ s = stab(mapname, ST_MAP, ST_ENTER); s->s_map.map_class = &class->s_mapclass; s->s_map.map_mname = newstr(mapname); if (class->s_mapclass.map_parse(&s->s_map, p)) s->s_map.map_mflags |= MF_VALID; if (tTd(37, 5)) { printf("map %s, class %s, flags %lx, file %s,\n", s->s_map.map_mname, s->s_map.map_class->map_cname, s->s_map.map_mflags, s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); printf("\tapp %s, domain %s, rebuild %s\n", s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); } return &s->s_map; } /* ** STRTORWSET -- convert string to rewriting set number ** ** Parameters: ** p -- the pointer to the string to decode. ** endp -- if set, store the trailing delimiter here. ** stabmode -- ST_ENTER to create this entry, ST_FIND if ** it must already exist. ** ** Returns: ** The appropriate ruleset number. ** -1 if it is not valid (error already printed) */ int strtorwset(p, endp, stabmode) char *p; char **endp; int stabmode; { int ruleset; static int nextruleset = MAXRWSETS; while (isascii(*p) && isspace(*p)) p++; if (!isascii(*p)) { syserr("invalid ruleset name: \"%.20s\"", p); return -1; } if (isdigit(*p)) { ruleset = strtol(p, endp, 10); if (ruleset >= MAXRWSETS / 2 || ruleset < 0) { syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS / 2); ruleset = -1; } } else { STAB *s; char delim; char *q; q = p; while (*p != '\0' && isascii(*p) && (isalnum(*p) || *p == '_')) p++; if (q == p || !isalpha(*q)) { /* no valid characters */ syserr("invalid ruleset name: \"%.20s\"", q); return -1; } while (isascii(*p) && isspace(*p)) *p++ = '\0'; delim = *p; if (delim != '\0') *p = '\0'; s = stab(q, ST_RULESET, stabmode); if (delim != '\0') *p = delim; if (s == NULL) return -1; if (stabmode == ST_ENTER && delim == '=') { while (isascii(*++p) && isspace(*p)) continue; if (!isdigit(*p)) { syserr("bad ruleset definition \"%s\" (number required after `=')", q); ruleset = -1; } else { ruleset = strtol(p, endp, 10); if (ruleset >= MAXRWSETS / 2 || ruleset < 0) { syserr("bad ruleset number %d in \"%s\" (%d max)", ruleset, q, MAXRWSETS / 2); ruleset = -1; } } } else { if (endp != NULL) *endp = p; if (s->s_ruleset > 0) ruleset = s->s_ruleset; else if ((ruleset = --nextruleset) < MAXRWSETS / 2) { syserr("%s: too many named rulesets (%d max)", q, MAXRWSETS / 2); ruleset = -1; } } if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset) { syserr("%s: ruleset changed value (old %d, new %d)", q, s->s_ruleset, ruleset); ruleset = s->s_ruleset; } else if (ruleset > 0) { s->s_ruleset = ruleset; } } return ruleset; } /* ** INITTIMEOUTS -- parse and set timeout values ** ** Parameters: ** val -- a pointer to the values. If NULL, do initial ** settings. ** ** Returns: ** none. ** ** Side Effects: ** Initializes the TimeOuts structure */ #define SECONDS #define MINUTES * 60 #define HOUR * 3600 void inittimeouts(val) register char *val; { register char *p; extern time_t convtime(); if (tTd(37, 2)) printf("inittimeouts(%s)\n", val == NULL ? "" : val); if (val == NULL) { TimeOuts.to_connect = (time_t) 0 SECONDS; TimeOuts.to_initial = (time_t) 5 MINUTES; TimeOuts.to_helo = (time_t) 5 MINUTES; TimeOuts.to_mail = (time_t) 10 MINUTES; TimeOuts.to_rcpt = (time_t) 1 HOUR; TimeOuts.to_datainit = (time_t) 5 MINUTES; TimeOuts.to_datablock = (time_t) 1 HOUR; TimeOuts.to_datafinal = (time_t) 1 HOUR; TimeOuts.to_rset = (time_t) 5 MINUTES; TimeOuts.to_quit = (time_t) 2 MINUTES; TimeOuts.to_nextcommand = (time_t) 1 HOUR; TimeOuts.to_miscshort = (time_t) 2 MINUTES; #if IDENTPROTO TimeOuts.to_ident = (time_t) 30 SECONDS; #else TimeOuts.to_ident = (time_t) 0 SECONDS; #endif TimeOuts.to_fileopen = (time_t) 60 SECONDS; if (tTd(37, 5)) { printf("Timeouts:\n"); printf(" connect = %ld\n", TimeOuts.to_connect); printf(" initial = %ld\n", TimeOuts.to_initial); printf(" helo = %ld\n", TimeOuts.to_helo); printf(" mail = %ld\n", TimeOuts.to_mail); printf(" rcpt = %ld\n", TimeOuts.to_rcpt); printf(" datainit = %ld\n", TimeOuts.to_datainit); printf(" datablock = %ld\n", TimeOuts.to_datablock); printf(" datafinal = %ld\n", TimeOuts.to_datafinal); printf(" rset = %ld\n", TimeOuts.to_rset); printf(" quit = %ld\n", TimeOuts.to_quit); printf(" nextcommand = %ld\n", TimeOuts.to_nextcommand); printf(" miscshort = %ld\n", TimeOuts.to_miscshort); printf(" ident = %ld\n", TimeOuts.to_ident); printf(" fileopen = %ld\n", TimeOuts.to_fileopen); } return; } for (;; val = p) { while (isascii(*val) && isspace(*val)) val++; if (*val == '\0') break; for (p = val; *p != '\0' && *p != ','; p++) continue; if (*p != '\0') *p++ = '\0'; if (isascii(*val) && isdigit(*val)) { /* old syntax -- set everything */ TimeOuts.to_mail = convtime(val, 'm'); TimeOuts.to_rcpt = TimeOuts.to_mail; TimeOuts.to_datainit = TimeOuts.to_mail; TimeOuts.to_datablock = TimeOuts.to_mail; TimeOuts.to_datafinal = TimeOuts.to_mail; TimeOuts.to_nextcommand = TimeOuts.to_mail; continue; } else { register char *q = strchr(val, ':'); if (q == NULL && (q = strchr(val, '=')) == NULL) { /* syntax error */ continue; } *q++ = '\0'; settimeout(val, q); } } } /* ** SETTIMEOUT -- set an individual timeout ** ** Parameters: ** name -- the name of the timeout. ** val -- the value of the timeout. ** ** Returns: ** none. */ void settimeout(name, val) char *name; char *val; { register char *p; time_t to; extern time_t convtime(); if (tTd(37, 2)) printf("settimeout(%s = %s)\n", name, val); to = convtime(val, 'm'); p = strchr(name, '.'); if (p != NULL) *p++ = '\0'; if (strcasecmp(name, "initial") == 0) TimeOuts.to_initial = to; else if (strcasecmp(name, "mail") == 0) TimeOuts.to_mail = to; else if (strcasecmp(name, "rcpt") == 0) TimeOuts.to_rcpt = to; else if (strcasecmp(name, "datainit") == 0) TimeOuts.to_datainit = to; else if (strcasecmp(name, "datablock") == 0) TimeOuts.to_datablock = to; else if (strcasecmp(name, "datafinal") == 0) TimeOuts.to_datafinal = to; else if (strcasecmp(name, "command") == 0) TimeOuts.to_nextcommand = to; else if (strcasecmp(name, "rset") == 0) TimeOuts.to_rset = to; else if (strcasecmp(name, "helo") == 0) TimeOuts.to_helo = to; else if (strcasecmp(name, "quit") == 0) TimeOuts.to_quit = to; else if (strcasecmp(name, "misc") == 0) TimeOuts.to_miscshort = to; else if (strcasecmp(name, "ident") == 0) TimeOuts.to_ident = to; else if (strcasecmp(name, "fileopen") == 0) TimeOuts.to_fileopen = to; else if (strcasecmp(name, "connect") == 0) TimeOuts.to_connect = to; else if (strcasecmp(name, "iconnect") == 0) TimeOuts.to_iconnect = to; else if (strcasecmp(name, "queuewarn") == 0) { to = convtime(val, 'h'); if (p == NULL || strcmp(p, "*") == 0) { TimeOuts.to_q_warning[TOC_NORMAL] = to; TimeOuts.to_q_warning[TOC_URGENT] = to; TimeOuts.to_q_warning[TOC_NONURGENT] = to; } else if (strcasecmp(p, "normal") == 0) TimeOuts.to_q_warning[TOC_NORMAL] = to; else if (strcasecmp(p, "urgent") == 0) TimeOuts.to_q_warning[TOC_URGENT] = to; else if (strcasecmp(p, "non-urgent") == 0) TimeOuts.to_q_warning[TOC_NONURGENT] = to; else syserr("settimeout: invalid queuewarn subtimeout %s", p); } else if (strcasecmp(name, "queuereturn") == 0) { to = convtime(val, 'd'); if (p == NULL || strcmp(p, "*") == 0) { TimeOuts.to_q_return[TOC_NORMAL] = to; TimeOuts.to_q_return[TOC_URGENT] = to; TimeOuts.to_q_return[TOC_NONURGENT] = to; } else if (strcasecmp(p, "normal") == 0) TimeOuts.to_q_return[TOC_NORMAL] = to; else if (strcasecmp(p, "urgent") == 0) TimeOuts.to_q_return[TOC_URGENT] = to; else if (strcasecmp(p, "non-urgent") == 0) TimeOuts.to_q_return[TOC_NONURGENT] = to; else syserr("settimeout: invalid queuereturn subtimeout %s", p); } else if (strcasecmp(name, "hoststatus") == 0) MciInfoTimeout = convtime(val, 'm'); else syserr("settimeout: invalid timeout %s", name); } Index: head/usr.sbin/sendmail/src/sendmail.h =================================================================== --- head/usr.sbin/sendmail/src/sendmail.h (revision 19843) +++ head/usr.sbin/sendmail/src/sendmail.h (revision 19844) @@ -1,1394 +1,1395 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)sendmail.h 8.206 (Berkeley) 10/17/96 + * @(#)sendmail.h 8.209 (Berkeley) 11/8/96 */ /* ** SENDMAIL.H -- Global definitions for sendmail. */ # ifdef _DEFINE # define EXTERN # ifndef lint -static char SmailSccsId[] = "@(#)sendmail.h 8.206 10/17/96"; +static char SmailSccsId[] = "@(#)sendmail.h 8.209 11/8/96"; # endif # else /* _DEFINE */ # define EXTERN extern # endif /* _DEFINE */ # include # include # include # include # include # include # include # include # include # ifdef EX_OK # undef EX_OK /* for SVr4.2 SMP */ # endif # include # include "conf.h" # include "useful.h" # ifdef LOG # include # endif /* LOG */ # ifdef DAEMON # include # endif # if NETUNIX # include # endif # if NETINET # include # endif # if NETISO # include # endif # if NETNS # include # endif # if NETX25 # include # endif /* forward references for prototypes */ typedef struct envelope ENVELOPE; typedef struct mailer MAILER; /* ** Data structure for bit maps. ** ** Each bit in this map can be referenced by an ascii character. ** This is 256 possible bits, or 32 8-bit bytes. */ #define BITMAPBYTES 32 /* number of bytes in a bit map */ #define BYTEBITS 8 /* number of bits in a byte */ /* internal macros */ #define _BITWORD(bit) ((bit) / (BYTEBITS * sizeof (int))) #define _BITBIT(bit) (1 << ((bit) % (BYTEBITS * sizeof (int)))) typedef int BITMAP[BITMAPBYTES / sizeof (int)]; /* test bit number N */ #define bitnset(bit, map) ((map)[_BITWORD(bit)] & _BITBIT(bit)) /* set bit number N */ #define setbitn(bit, map) (map)[_BITWORD(bit)] |= _BITBIT(bit) /* clear bit number N */ #define clrbitn(bit, map) (map)[_BITWORD(bit)] &= ~_BITBIT(bit) /* clear an entire bit map */ #define clrbitmap(map) bzero((char *) map, BITMAPBYTES) /* ** Utility macros */ /* return number of bytes left in a buffer */ #define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf)) /* ** Address structure. ** Addresses are stored internally in this structure. */ struct address { char *q_paddr; /* the printname for the address */ char *q_user; /* user name */ char *q_ruser; /* real user name, or NULL if q_user */ char *q_host; /* host name */ struct mailer *q_mailer; /* mailer to use */ u_long q_flags; /* status flags, see below */ uid_t q_uid; /* user-id of receiver (if known) */ gid_t q_gid; /* group-id of receiver (if known) */ char *q_home; /* home dir (local mailer only) */ char *q_fullname; /* full name if known */ struct address *q_next; /* chain */ struct address *q_alias; /* address this results from */ char *q_owner; /* owner of q_alias */ struct address *q_tchain; /* temporary use chain */ char *q_orcpt; /* ORCPT parameter from RCPT TO: line */ char *q_status; /* status code for DSNs */ char *q_rstatus; /* remote status message for DSNs */ time_t q_statdate; /* date of status messages */ char *q_statmta; /* MTA generating q_rstatus */ short q_specificity; /* how "specific" this address is */ }; typedef struct address ADDRESS; # define QDONTSEND 0x00000001 /* don't send to this address */ # define QBADADDR 0x00000002 /* this address is verified bad */ # define QGOODUID 0x00000004 /* the q_uid q_gid fields are good */ # define QPRIMARY 0x00000008 /* set from RCPT or argv */ # define QQUEUEUP 0x00000010 /* queue for later transmission */ # define QSENT 0x00000020 /* has been successfully delivered */ # define QNOTREMOTE 0x00000040 /* address not for remote forwarding */ # define QSELFREF 0x00000080 /* this address references itself */ # define QVERIFIED 0x00000100 /* verified, but not expanded */ # define QBOGUSSHELL 0x00000400 /* user has no valid shell listed */ # define QUNSAFEADDR 0x00000800 /* address aquired via unsafe path */ # define QPINGONSUCCESS 0x00001000 /* give return on successful delivery */ # define QPINGONFAILURE 0x00002000 /* give return on failure */ # define QPINGONDELAY 0x00004000 /* give return on message delay */ # define QHASNOTIFY 0x00008000 /* propogate notify parameter */ # define QRELAYED 0x00010000 /* DSN: relayed to non-DSN aware sys */ # define QEXPANDED 0x00020000 /* DSN: undergone list expansion */ # define QDELIVERED 0x00040000 /* DSN: successful final delivery */ # define QDELAYED 0x00080000 /* DSN: message delayed */ # define QTHISPASS 0x40000000 /* temp: address set this pass */ # define Q_PINGFLAGS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY) # define NULLADDR ((ADDRESS *) NULL) /* functions */ extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **, ENVELOPE *)); extern ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *)); extern ADDRESS *recipient __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); extern char **prescan __P((char *, int, char[], int, char **, u_char *)); extern int rewrite __P((char **, int, int, ENVELOPE *)); extern char *remotename __P((char *, MAILER *, int, int *, ENVELOPE *)); extern ADDRESS *getctladdr __P((ADDRESS *)); extern bool sameaddr __P((ADDRESS *, ADDRESS *)); extern bool emptyaddr __P((ADDRESS *)); extern void printaddr __P((ADDRESS *, bool)); extern void cataddr __P((char **, char **, char *, int, int)); extern int sendtolist __P((char *, ADDRESS *, ADDRESS **, int, ENVELOPE *)); /* ** Mailer definition structure. ** Every mailer known to the system is declared in this ** structure. It defines the pathname of the mailer, some ** flags associated with it, and the argument vector to ** pass to it. The flags are defined in conf.c ** ** The argument vector is expanded before actual use. All ** words except the first are passed through the macro ** processor. */ struct mailer { char *m_name; /* symbolic name of this mailer */ char *m_mailer; /* pathname of the mailer to use */ char *m_mtatype; /* type of this MTA */ char *m_addrtype; /* type for addresses */ char *m_diagtype; /* type for diagnostics */ BITMAP m_flags; /* status flags, see below */ short m_mno; /* mailer number internally */ short m_nice; /* niceness to run at (mostly for prog) */ char **m_argv; /* template argument vector */ short m_sh_rwset; /* rewrite set: sender header addresses */ short m_se_rwset; /* rewrite set: sender envelope addresses */ short m_rh_rwset; /* rewrite set: recipient header addresses */ short m_re_rwset; /* rewrite set: recipient envelope addresses */ char *m_eol; /* end of line string */ long m_maxsize; /* size limit on message to this mailer */ int m_linelimit; /* max # characters per line */ char *m_execdir; /* directory to chdir to before execv */ uid_t m_uid; /* UID to run as */ gid_t m_gid; /* GID to run as */ char *m_defcharset; /* default character set */ }; /* bits for m_flags */ # define M_ESMTP 'a' /* run Extended SMTP protocol */ # define M_ALIASABLE 'A' /* user can be LHS of an alias */ # define M_BLANKEND 'b' /* ensure blank line at end of message */ # define M_NOCOMMENT 'c' /* don't include comment part of address */ # define M_CANONICAL 'C' /* make addresses canonical "u@dom" */ # define M_NOBRACKET 'd' /* never angle bracket envelope route-addrs */ /* 'D' CF: include Date: */ # define M_EXPENSIVE 'e' /* it costs to use this mailer.... */ # define M_ESCFROM 'E' /* escape From lines to >From */ # define M_FOPT 'f' /* mailer takes picky -f flag */ /* 'F' CF: include From: or Resent-From: */ # define M_NO_NULL_FROM 'g' /* sender of errors should be $g */ # define M_HST_UPPER 'h' /* preserve host case distinction */ # define M_PREHEAD 'H' /* MAIL11V3: preview headers */ # define M_UDBENVELOPE 'i' /* do udbsender rewriting on envelope */ # define M_INTERNAL 'I' /* SMTP to another sendmail site */ # define M_UDBRECIPIENT 'j' /* do udbsender rewriting on recipient lines */ # define M_NOLOOPCHECK 'k' /* don't check for loops in HELO command */ # define M_CHUNKING 'K' /* CHUNKING: reserved for future use */ # define M_LOCALMAILER 'l' /* delivery is to this host */ # define M_LIMITS 'L' /* must enforce SMTP line limits */ # define M_MUSER 'm' /* can handle multiple users at once */ /* 'M' CF: include Message-Id: */ # define M_NHDR 'n' /* don't insert From line */ # define M_MANYSTATUS 'N' /* MAIL11V3: DATA returns multi-status */ # define M_RUNASRCPT 'o' /* always run mailer as recipient */ # define M_FROMPATH 'p' /* use reverse-path in MAIL FROM: */ /* 'P' CF: include Return-Path: */ # define M_VRFY250 'q' /* VRFY command returns 250 instead of 252 */ # define M_ROPT 'r' /* mailer takes picky -r flag */ # define M_SECURE_PORT 'R' /* try to send on a reserved TCP port */ # define M_STRIPQ 's' /* strip quote chars from user/host */ # define M_SPECIFIC_UID 'S' /* run as specific uid/gid */ # define M_USR_UPPER 'u' /* preserve user case distinction */ # define M_UGLYUUCP 'U' /* this wants an ugly UUCP from line */ # define M_CONTENT_LEN 'v' /* add Content-Length: header (SVr4) */ /* 'V' UIUC: !-relativize all addresses */ # define M_HASPWENT 'w' /* check for /etc/passwd entry */ /* 'x' CF: include Full-Name: */ # define M_XDOT 'X' /* use hidden-dot algorithm */ +# define M_LMTP 'z' /* run Local Mail Transport Protocol */ # define M_NOMX '0' /* turn off MX lookups */ # define M_EBCDIC '3' /* extend Q-P encoding for EBCDIC */ # define M_TRYRULESET5 '5' /* use ruleset 5 after local aliasing */ # define M_7BITS '7' /* use 7-bit path */ # define M_8BITS '8' /* force "just send 8" behaviour */ # define M_MAKE8BIT '9' /* convert 7 -> 8 bit if appropriate */ # define M_CHECKINCLUDE ':' /* check for :include: files */ # define M_CHECKPROG '|' /* check for |program addresses */ # define M_CHECKFILE '/' /* check for /file addresses */ # define M_CHECKUDB '@' /* user can be user database key */ # define M_CHECKHDIR '~' /* SGI: check for valid home directory */ EXTERN MAILER *Mailer[MAXMAILERS+1]; EXTERN MAILER *LocalMailer; /* ptr to local mailer */ EXTERN MAILER *ProgMailer; /* ptr to program mailer */ EXTERN MAILER *FileMailer; /* ptr to *file* mailer */ EXTERN MAILER *InclMailer; /* ptr to *include* mailer */ /* ** Information about currently open connections to mailers, or to ** hosts that we have looked up recently. */ # define MCI struct mailer_con_info MCI { short mci_flags; /* flag bits, see below */ short mci_errno; /* error number on last connection */ short mci_herrno; /* h_errno from last DNS lookup */ short mci_exitstat; /* exit status from last connection */ short mci_state; /* SMTP state */ long mci_maxsize; /* max size this server will accept */ FILE *mci_in; /* input side of connection */ FILE *mci_out; /* output side of connection */ - int mci_pid; /* process id of subordinate proc */ + pid_t mci_pid; /* process id of subordinate proc */ char *mci_phase; /* SMTP phase string */ struct mailer *mci_mailer; /* ptr to the mailer for this conn */ char *mci_host; /* host name */ char *mci_status; /* DSN status to be copied to addrs */ char *mci_rstatus; /* SMTP status to be copied to addrs */ time_t mci_lastuse; /* last usage time */ FILE *mci_statfile; /* long term status file */ }; /* flag bits */ #define MCIF_VALID 0x0001 /* this entry is valid */ #define MCIF_TEMP 0x0002 /* don't cache this connection */ #define MCIF_CACHED 0x0004 /* currently in open cache */ #define MCIF_ESMTP 0x0008 /* this host speaks ESMTP */ #define MCIF_EXPN 0x0010 /* EXPN command supported */ #define MCIF_SIZE 0x0020 /* SIZE option supported */ #define MCIF_8BITMIME 0x0040 /* BODY=8BITMIME supported */ #define MCIF_7BIT 0x0080 /* strip this message to 7 bits */ #define MCIF_MULTSTAT 0x0100 /* MAIL11V3: handles MULT status */ #define MCIF_INHEADER 0x0200 /* currently outputing header */ #define MCIF_CVT8TO7 0x0400 /* convert from 8 to 7 bits */ #define MCIF_DSN 0x0800 /* DSN extension supported */ #define MCIF_8BITOK 0x1000 /* OK to send 8 bit characters */ #define MCIF_CVT7TO8 0x2000 /* convert from 7 to 8 bits */ #define MCIF_INMIME 0x4000 /* currently reading MIME header */ /* states */ #define MCIS_CLOSED 0 /* no traffic on this connection */ #define MCIS_OPENING 1 /* sending initial protocol */ #define MCIS_OPEN 2 /* open, initial protocol sent */ #define MCIS_ACTIVE 3 /* message being sent */ #define MCIS_QUITING 4 /* running quit protocol */ #define MCIS_SSD 5 /* service shutting down */ #define MCIS_ERROR 6 /* I/O error on connection */ /* functions */ extern MCI *mci_get __P((char *, MAILER *)); extern void mci_cache __P((MCI *)); extern void mci_flush __P((bool, MCI *)); extern void mci_dump __P((MCI *, bool)); extern void mci_dump_all __P((bool)); extern MCI **mci_scan __P((MCI *)); extern int mci_traverse_persistent __P((int (), char *)); extern int mci_print_persistent __P((char *, char *)); extern int mci_purge_persistent __P((char *, char *)); extern int mci_lock_host __P((MCI *)); extern void mci_unlock_host __P((MCI *)); extern int mci_lock_host_statfile __P((MCI *)); extern void mci_store_persistent __P((MCI *)); extern int mci_read_persistent __P((FILE *, MCI *)); /* ** Header structure. ** This structure is used internally to store header items. */ struct header { char *h_field; /* the name of the field */ char *h_value; /* the value of that field */ struct header *h_link; /* the next header */ u_short h_flags; /* status bits, see below */ BITMAP h_mflags; /* m_flags bits needed */ }; typedef struct header HDR; /* ** Header information structure. ** Defined in conf.c, this struct declares the header fields ** that have some magic meaning. */ struct hdrinfo { char *hi_field; /* the name of the field */ u_short hi_flags; /* status bits, see below */ }; extern struct hdrinfo HdrInfo[]; /* bits for h_flags and hi_flags */ # define H_EOH 0x0001 /* this field terminates header */ # define H_RCPT 0x0002 /* contains recipient addresses */ # define H_DEFAULT 0x0004 /* if another value is found, drop this */ # define H_RESENT 0x0008 /* this address is a "Resent-..." address */ # define H_CHECK 0x0010 /* check h_mflags against m_flags */ # define H_ACHECK 0x0020 /* ditto, but always (not just default) */ # define H_FORCE 0x0040 /* force this field, even if default */ # define H_TRACE 0x0080 /* this field contains trace information */ # define H_FROM 0x0100 /* this is a from-type field */ # define H_VALID 0x0200 /* this field has a validated value */ # define H_RECEIPTTO 0x0400 /* this field has return receipt info */ # define H_ERRORSTO 0x0800 /* this field has error address info */ # define H_CTE 0x1000 /* this field is a content-transfer-encoding */ # define H_CTYPE 0x2000 /* this is a content-type field */ # define H_BCC 0x4000 /* Bcc: header: strip value or delete */ # define H_ENCODABLE 0x8000 /* field can be RFC 1522 encoded */ /* functions */ extern void addheader __P((char *, char *, HDR **)); extern char *hvalue __P((char *, HDR *)); extern void commaize __P((HDR *, char *, bool, MCI *, ENVELOPE *)); extern void put_vanilla_header __P((HDR *, char *, MCI *)); extern void eatheader __P((ENVELOPE *e, bool)); extern int chompheader __P((char *, bool, HDR **, ENVELOPE *)); /* ** Envelope structure. ** This structure defines the message itself. There is usually ** only one of these -- for the message that we originally read ** and which is our primary interest -- but other envelopes can ** be generated during processing. For example, error messages ** will have their own envelope. */ struct envelope { HDR *e_header; /* head of header list */ long e_msgpriority; /* adjusted priority of this message */ time_t e_ctime; /* time message appeared in the queue */ char *e_to; /* the target person */ ADDRESS e_from; /* the person it is from */ char *e_sender; /* e_from.q_paddr w comments stripped */ char **e_fromdomain; /* the domain part of the sender */ ADDRESS *e_sendqueue; /* list of message recipients */ ADDRESS *e_errorqueue; /* the queue for error responses */ long e_msgsize; /* size of the message in bytes */ long e_flags; /* flags, see below */ int e_nrcpts; /* number of recipients */ short e_class; /* msg class (priority, junk, etc.) */ short e_hopcount; /* number of times processed */ short e_nsent; /* number of sends since checkpoint */ short e_sendmode; /* message send mode */ short e_errormode; /* error return mode */ short e_timeoutclass; /* message timeout class */ void (*e_puthdr)__P((MCI *, HDR *, ENVELOPE *)); /* function to put header of message */ void (*e_putbody)__P((MCI *, ENVELOPE *, char *)); /* function to put body of message */ struct envelope *e_parent; /* the message this one encloses */ struct envelope *e_sibling; /* the next envelope of interest */ char *e_bodytype; /* type of message body */ FILE *e_dfp; /* temporary file */ char *e_id; /* code for this entry in queue */ FILE *e_xfp; /* transcript file */ FILE *e_lockfp; /* the lock file for this message */ char *e_message; /* error message */ char *e_statmsg; /* stat msg (changes per delivery) */ char *e_msgboundary; /* MIME-style message part boundary */ char *e_origrcpt; /* original recipient (one only) */ char *e_envid; /* envelope id from MAIL FROM: line */ char *e_status; /* DSN status for this message */ time_t e_dtime; /* time of last delivery attempt */ int e_ntries; /* number of delivery attempts */ dev_t e_dfdev; /* df file's device, for crash recov */ ino_t e_dfino; /* df file's ino, for crash recovery */ char *e_macro[256]; /* macro definitions */ }; /* values for e_flags */ #define EF_OLDSTYLE 0x0000001 /* use spaces (not commas) in hdrs */ #define EF_INQUEUE 0x0000002 /* this message is fully queued */ #define EF_NO_BODY_RETN 0x0000004 /* omit message body on error */ #define EF_CLRQUEUE 0x0000008 /* disk copy is no longer needed */ #define EF_SENDRECEIPT 0x0000010 /* send a return receipt */ #define EF_FATALERRS 0x0000020 /* fatal errors occured */ #define EF_DELETE_BCC 0x0000040 /* delete Bcc: headers entirely */ #define EF_RESPONSE 0x0000080 /* this is an error or return receipt */ #define EF_RESENT 0x0000100 /* this message is being forwarded */ #define EF_VRFYONLY 0x0000200 /* verify only (don't expand aliases) */ #define EF_WARNING 0x0000400 /* warning message has been sent */ #define EF_QUEUERUN 0x0000800 /* this envelope is from queue */ #define EF_GLOBALERRS 0x0001000 /* treat errors as global */ #define EF_PM_NOTIFY 0x0002000 /* send return mail to postmaster */ #define EF_METOO 0x0004000 /* send to me too */ #define EF_LOGSENDER 0x0008000 /* need to log the sender */ #define EF_NORECEIPT 0x0010000 /* suppress all return-receipts */ #define EF_HAS8BIT 0x0020000 /* at least one 8-bit char in body */ #define EF_NL_NOT_EOL 0x0040000 /* don't accept raw NL as EOLine */ #define EF_CRLF_NOT_EOL 0x0080000 /* don't accept CR-LF as EOLine */ #define EF_RET_PARAM 0x0100000 /* RCPT command had RET argument */ #define EF_HAS_DF 0x0200000 /* set when df file is instantiated */ #define EF_IS_MIME 0x0400000 /* really is a MIME message */ #define EF_DONT_MIME 0x0800000 /* never MIME this message */ EXTERN ENVELOPE *CurEnv; /* envelope currently being processed */ /* functions */ extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *)); extern void dropenvelope __P((ENVELOPE *, bool)); extern void clearenvelope __P((ENVELOPE *, bool)); extern void putheader __P((MCI *, HDR *, ENVELOPE *)); extern void putbody __P((MCI *, ENVELOPE *, char *)); /* ** Message priority classes. ** ** The message class is read directly from the Priority: header ** field in the message. ** ** CurEnv->e_msgpriority is the number of bytes in the message plus ** the creation time (so that jobs ``tend'' to be ordered correctly), ** adjusted by the message class, the number of recipients, and the ** amount of time the message has been sitting around. This number ** is used to order the queue. Higher values mean LOWER priority. ** ** Each priority class point is worth WkClassFact priority points; ** each recipient is worth WkRecipFact priority points. Each time ** we reprocess a message the priority is adjusted by WkTimeFact. ** WkTimeFact should normally decrease the priority so that jobs ** that have historically failed will be run later; thanks go to ** Jay Lepreau at Utah for pointing out the error in my thinking. ** ** The "class" is this number, unadjusted by the age or size of ** this message. Classes with negative representations will have ** error messages thrown away if they are not local. */ struct priority { char *pri_name; /* external name of priority */ int pri_val; /* internal value for same */ }; EXTERN struct priority Priorities[MAXPRIORITIES]; EXTERN int NumPriorities; /* pointer into Priorities */ /* ** Rewrite rules. */ struct rewrite { char **r_lhs; /* pattern match */ char **r_rhs; /* substitution value */ struct rewrite *r_next;/* next in chain */ }; EXTERN struct rewrite *RewriteRules[MAXRWSETS]; /* ** Special characters in rewriting rules. ** These are used internally only. ** The COND* rules are actually used in macros rather than in ** rewriting rules, but are given here because they ** cannot conflict. */ /* left hand side items */ # define MATCHZANY ((u_char)0220) /* match zero or more tokens */ # define MATCHANY ((u_char)0221) /* match one or more tokens */ # define MATCHONE ((u_char)0222) /* match exactly one token */ # define MATCHCLASS ((u_char)0223) /* match one token in a class */ # define MATCHNCLASS ((u_char)0224) /* match anything not in class */ # define MATCHREPL ((u_char)0225) /* replacement on RHS for above */ /* right hand side items */ # define CANONNET ((u_char)0226) /* canonical net, next token */ # define CANONHOST ((u_char)0227) /* canonical host, next token */ # define CANONUSER ((u_char)0230) /* canonical user, next N tokens */ # define CALLSUBR ((u_char)0231) /* call another rewriting set */ /* conditionals in macros */ # define CONDIF ((u_char)0232) /* conditional if-then */ # define CONDELSE ((u_char)0233) /* conditional else */ # define CONDFI ((u_char)0234) /* conditional fi */ /* bracket characters for host name lookup */ # define HOSTBEGIN ((u_char)0235) /* hostname lookup begin */ # define HOSTEND ((u_char)0236) /* hostname lookup end */ /* bracket characters for generalized lookup */ # define LOOKUPBEGIN ((u_char)0205) /* generalized lookup begin */ # define LOOKUPEND ((u_char)0206) /* generalized lookup end */ /* macro substitution character */ # define MACROEXPAND ((u_char)0201) /* macro expansion */ # define MACRODEXPAND ((u_char)0202) /* deferred macro expansion */ /* to make the code clearer */ # define MATCHZERO CANONHOST /* external <==> internal mapping table */ struct metamac { char metaname; /* external code (after $) */ u_char metaval; /* internal code (as above) */ }; /* values for macros with external names only */ # define MID_OPMODE 0202 /* operation mode */ /* functions */ extern void expand __P((char *, char *, size_t, ENVELOPE *)); extern void define __P((int, char *, ENVELOPE *)); extern char *macvalue __P((int, ENVELOPE *)); extern char *macname __P((int)); extern int macid __P((char *, char **)); /* ** Name canonification short circuit. ** ** If the name server for a host is down, the process of trying to ** canonify the name can hang. This is similar to (but alas, not ** identical to) looking up the name for delivery. This stab type ** caches the result of the name server lookup so we don't hang ** multiple times. */ #define NAMECANON struct _namecanon NAMECANON { short nc_errno; /* cached errno */ short nc_herrno; /* cached h_errno */ short nc_stat; /* cached exit status code */ short nc_flags; /* flag bits */ char *nc_cname; /* the canonical name */ }; /* values for nc_flags */ #define NCF_VALID 0x0001 /* entry valid */ /* ** Mapping functions ** ** These allow arbitrary mappings in the config file. The idea ** (albeit not the implementation) comes from IDA sendmail. */ # define MAPCLASS struct _mapclass # define MAP struct _map # define MAXMAPACTIONS 3 /* size of map_actions array */ /* ** An actual map. */ MAP { MAPCLASS *map_class; /* the class of this map */ char *map_mname; /* name of this map */ long map_mflags; /* flags, see below */ char *map_file; /* the (nominal) filename */ ARBPTR_T map_db1; /* the open database ptr */ ARBPTR_T map_db2; /* an "extra" database pointer */ char *map_keycolnm; /* key column name */ char *map_valcolnm; /* value column name */ u_char map_keycolno; /* key column number */ u_char map_valcolno; /* value column number */ char map_coldelim; /* column delimiter */ char *map_app; /* to append to successful matches */ char *map_domain; /* the (nominal) NIS domain */ char *map_rebuild; /* program to run to do auto-rebuild */ time_t map_mtime; /* last database modification time */ int map_lockfd; /* auxiliary lock file descriptor */ short map_specificity; /* specificity of aliases */ MAP *map_stack[MAXMAPSTACK]; /* list for stacked maps */ short map_return[MAXMAPACTIONS]; /* return bitmaps for stacked maps */ }; /* bit values for map_mflags */ # define MF_VALID 0x00000001 /* this entry is valid */ # define MF_INCLNULL 0x00000002 /* include null byte in key */ # define MF_OPTIONAL 0x00000004 /* don't complain if map not found */ # define MF_NOFOLDCASE 0x00000008 /* don't fold case in keys */ # define MF_MATCHONLY 0x00000010 /* don't use the map value */ # define MF_OPEN 0x00000020 /* this entry is open */ # define MF_WRITABLE 0x00000040 /* open for writing */ # define MF_ALIAS 0x00000080 /* this is an alias file */ # define MF_TRY0NULL 0x00000100 /* try with no null byte */ # define MF_TRY1NULL 0x00000200 /* try with the null byte */ # define MF_LOCKED 0x00000400 /* this map is currently locked */ # define MF_ALIASWAIT 0x00000800 /* alias map in aliaswait state */ # define MF_IMPL_HASH 0x00001000 /* implicit: underlying hash database */ # define MF_IMPL_NDBM 0x00002000 /* implicit: underlying NDBM database */ # define MF_UNSAFEDB 0x00004000 /* this map is world writable */ # define MF_APPEND 0x00008000 /* append new entry on rebuiled */ # define MF_KEEPQUOTES 0x00010000 /* don't dequote key before lookup */ # define MF_NODEFER 0x00020000 /* don't defer if map lookup fails */ /* indices for map_actions */ # define MA_NOTFOUND 0 /* member map returned "not found" */ # define MA_UNAVAIL 1 /* member map is not available */ # define MA_TRYAGAIN 2 /* member map returns temp failure */ /* ** The class of a map -- essentially the functions to call */ MAPCLASS { char *map_cname; /* name of this map class */ char *map_ext; /* extension for database file */ short map_cflags; /* flag bits, see below */ bool (*map_parse)__P((MAP *, char *)); /* argument parsing function */ char *(*map_lookup)__P((MAP *, char *, char **, int *)); /* lookup function */ void (*map_store)__P((MAP *, char *, char *)); /* store function */ bool (*map_open)__P((MAP *, int)); /* open function */ void (*map_close)__P((MAP *)); /* close function */ }; /* bit values for map_cflags */ #define MCF_ALIASOK 0x0001 /* can be used for aliases */ #define MCF_ALIASONLY 0x0002 /* usable only for aliases */ #define MCF_REBUILDABLE 0x0004 /* can rebuild alias files */ #define MCF_OPTFILE 0x0008 /* file name is optional */ /* functions */ extern char *map_rewrite __P((MAP *, char *, int, char **)); extern MAP *makemapentry __P((char *)); extern void initmaps __P((bool, ENVELOPE *)); /* ** Symbol table definitions */ struct symtab { char *s_name; /* name to be entered */ short s_type; /* general type (see below) */ short s_len; /* length of this entry */ struct symtab *s_next; /* pointer to next in chain */ union { BITMAP sv_class; /* bit-map of word classes */ ADDRESS *sv_addr; /* pointer to address header */ MAILER *sv_mailer; /* pointer to mailer */ char *sv_alias; /* alias */ MAPCLASS sv_mapclass; /* mapping function class */ MAP sv_map; /* mapping function */ char *sv_hostsig; /* host signature */ MCI sv_mci; /* mailer connection info */ NAMECANON sv_namecanon; /* canonical name cache */ int sv_macro; /* macro name => id mapping */ int sv_ruleset; /* ruleset index */ char *sv_service[MAXMAPSTACK]; /* service switch */ } s_value; }; typedef struct symtab STAB; /* symbol types */ # define ST_UNDEF 0 /* undefined type */ # define ST_CLASS 1 /* class map */ # define ST_ADDRESS 2 /* an address in parsed format */ # define ST_MAILER 3 /* a mailer header */ # define ST_ALIAS 4 /* an alias */ # define ST_MAPCLASS 5 /* mapping function class */ # define ST_MAP 6 /* mapping function */ # define ST_HOSTSIG 7 /* host signature */ # define ST_NAMECANON 8 /* cached canonical name */ # define ST_MACRO 9 /* macro name to id mapping */ # define ST_RULESET 10 /* ruleset index */ # define ST_SERVICE 11 /* service switch entry */ # define ST_MCI 16 /* mailer connection info (offset) */ # define s_class s_value.sv_class # define s_address s_value.sv_addr # define s_mailer s_value.sv_mailer # define s_alias s_value.sv_alias # define s_mci s_value.sv_mci # define s_mapclass s_value.sv_mapclass # define s_hostsig s_value.sv_hostsig # define s_map s_value.sv_map # define s_namecanon s_value.sv_namecanon # define s_macro s_value.sv_macro # define s_ruleset s_value.sv_ruleset # define s_service s_value.sv_service extern STAB *stab __P((char *, int, int)); extern void stabapply __P((void (*)(STAB *, int), int)); /* opcodes to stab */ # define ST_FIND 0 /* find entry */ # define ST_ENTER 1 /* enter if not there */ /* ** STRUCT EVENT -- event queue. ** ** Maintained in sorted order. ** ** We store the pid of the process that set this event to insure ** that when we fork we will not take events intended for the parent. */ struct event { time_t ev_time; /* time of the function call */ void (*ev_func)__P((int)); /* function to call */ int ev_arg; /* argument to ev_func */ int ev_pid; /* pid that set this event */ struct event *ev_link; /* link to next item */ }; typedef struct event EVENT; EXTERN EVENT *EventQueue; /* head of event queue */ /* functions */ extern EVENT *setevent __P((time_t, void(*)(), int)); extern void clrevent __P((EVENT *)); /* ** Operation, send, error, and MIME modes ** ** The operation mode describes the basic operation of sendmail. ** This can be set from the command line, and is "send mail" by ** default. ** ** The send mode tells how to send mail. It can be set in the ** configuration file. It's setting determines how quickly the ** mail will be delivered versus the load on your system. If the ** -v (verbose) flag is given, it will be forced to SM_DELIVER ** mode. ** ** The error mode tells how to return errors. */ EXTERN char OpMode; /* operation mode, see below */ #define MD_DELIVER 'm' /* be a mail sender */ #define MD_SMTP 's' /* run SMTP on standard input */ #define MD_ARPAFTP 'a' /* obsolete ARPANET mode (Grey Book) */ #define MD_DAEMON 'd' /* run as a daemon */ #define MD_FGDAEMON 'D' /* run daemon in foreground */ #define MD_VERIFY 'v' /* verify: don't collect or deliver */ #define MD_TEST 't' /* test mode: resolve addrs only */ #define MD_INITALIAS 'i' /* initialize alias database */ #define MD_PRINT 'p' /* print the queue */ #define MD_FREEZE 'z' /* freeze the configuration file */ #define MD_HOSTSTAT 'h' /* print persistent host stat info */ #define MD_PURGESTAT 'H' /* purge persistent host stat info */ /* values for e_sendmode -- send modes */ #define SM_DELIVER 'i' /* interactive delivery */ #define SM_FORK 'b' /* deliver in background */ #define SM_QUEUE 'q' /* queue, don't deliver */ #define SM_DEFER 'd' /* defer map lookups as well as queue */ #define SM_VERIFY 'v' /* verify only (used internally) */ /* used only as a parameter to sendall */ #define SM_DEFAULT '\0' /* unspecified, use SendMode */ /* values for e_errormode -- error handling modes */ #define EM_PRINT 'p' /* print errors */ #define EM_MAIL 'm' /* mail back errors */ #define EM_WRITE 'w' /* write back errors */ #define EM_BERKNET 'e' /* special berknet processing */ #define EM_QUIET 'q' /* don't print messages (stat only) */ /* MIME processing mode */ EXTERN int MimeMode; /* bit values for MimeMode */ #define MM_CVTMIME 0x0001 /* convert 8 to 7 bit MIME */ #define MM_PASS8BIT 0x0002 /* just send 8 bit data blind */ #define MM_MIME8BIT 0x0004 /* convert 8-bit data to MIME */ /* queue sorting order algorithm */ EXTERN int QueueSortOrder; #define QS_BYPRIORITY 0 /* sort by message priority */ #define QS_BYHOST 1 /* sort by first host name */ #define QS_BYTIME 2 /* sort by submission time */ /* how to handle messages without any recipient addresses */ EXTERN int NoRecipientAction; #define NRA_NO_ACTION 0 /* just leave it as is */ #define NRA_ADD_TO 1 /* add To: header */ #define NRA_ADD_APPARENTLY_TO 2 /* add Apparently-To: header */ #define NRA_ADD_BCC 3 /* add empty Bcc: header */ #define NRA_ADD_TO_UNDISCLOSED 4 /* add To: undisclosed:; header */ /* flags to putxline */ #define PXLF_NOTHINGSPECIAL 0 /* no special mapping */ #define PXLF_MAPFROM 0x0001 /* map From_ to >From_ */ #define PXLF_STRIP8BIT 0x0002 /* strip 8th bit */ /* ** Additional definitions */ /* ** Privacy flags ** These are bit values for the PrivacyFlags word. */ #define PRIV_PUBLIC 0 /* what have I got to hide? */ #define PRIV_NEEDMAILHELO 0x0001 /* insist on HELO for MAIL, at least */ #define PRIV_NEEDEXPNHELO 0x0002 /* insist on HELO for EXPN */ #define PRIV_NEEDVRFYHELO 0x0004 /* insist on HELO for VRFY */ #define PRIV_NOEXPN 0x0008 /* disallow EXPN command entirely */ #define PRIV_NOVRFY 0x0010 /* disallow VRFY command entirely */ #define PRIV_AUTHWARNINGS 0x0020 /* flag possible authorization probs */ #define PRIV_NORECEIPTS 0x0040 /* disallow return receipts */ #define PRIV_RESTRICTMAILQ 0x1000 /* restrict mailq command */ #define PRIV_RESTRICTQRUN 0x2000 /* restrict queue run */ #define PRIV_GOAWAY 0x0fff /* don't give no info, anyway, anyhow */ /* struct defining such things */ struct prival { char *pv_name; /* name of privacy flag */ int pv_flag; /* numeric level */ }; /* ** Flags passed to remotename, parseaddr, allocaddr, and buildaddr. */ #define RF_SENDERADDR 0x001 /* this is a sender address */ #define RF_HEADERADDR 0x002 /* this is a header address */ #define RF_CANONICAL 0x004 /* strip comment information */ #define RF_ADDDOMAIN 0x008 /* OK to do domain extension */ #define RF_COPYPARSE 0x010 /* copy parsed user & host */ #define RF_COPYPADDR 0x020 /* copy print address */ #define RF_COPYALL (RF_COPYPARSE|RF_COPYPADDR) #define RF_COPYNONE 0 /* ** Flags passed to safefile. */ #define SFF_ANYFILE 0 /* no special restrictions */ #define SFF_MUSTOWN 0x0001 /* user must own this file */ #define SFF_NOSLINK 0x0002 /* file cannot be a symbolic link */ #define SFF_ROOTOK 0x0004 /* ok for root to own this file */ #define SFF_RUNASREALUID 0x0008 /* if no ctladdr, run as real uid */ #define SFF_NOPATHCHECK 0x0010 /* don't bother checking dir path */ #define SFF_SETUIDOK 0x0020 /* setuid files are ok */ #define SFF_CREAT 0x0040 /* ok to create file if necessary */ #define SFF_REGONLY 0x0080 /* regular files only */ /* flags that are actually specific to safefopen */ #define SFF_OPENASROOT 0x1000 /* open as root instead of real user */ /* functions */ extern int safefile __P((char *, UID_T, GID_T, char *, int, int, struct stat *)); /* ** Flags passed to mime8to7. */ #define M87F_OUTER 0 /* outer context */ #define M87F_NO8BIT 0x0001 /* can't have 8-bit in this section */ #define M87F_DIGEST 0x0002 /* processing multipart/digest */ /* ** Flags passed to returntosender. */ #define RTSF_NO_BODY 0 /* send headers only */ #define RTSF_SEND_BODY 0x0001 /* include body of message in return */ #define RTSF_PM_BOUNCE 0x0002 /* this is a postmaster bounce */ /* ** Regular UNIX sockaddrs are too small to handle ISO addresses, so ** we are forced to declare a supertype here. */ #ifdef DAEMON union bigsockaddr { struct sockaddr sa; /* general version */ #if NETUNIX struct sockaddr_un sunix; /* UNIX family */ #endif #if NETINET struct sockaddr_in sin; /* INET family */ #endif #if NETISO struct sockaddr_iso siso; /* ISO family */ #endif #if NETNS struct sockaddr_ns sns; /* XNS family */ #endif #if NETX25 struct sockaddr_x25 sx25; /* X.25 family */ #endif }; #define SOCKADDR union bigsockaddr EXTERN SOCKADDR RealHostAddr; /* address of host we are talking to */ extern char *hostnamebyanyaddr __P((SOCKADDR *)); extern char *anynet_ntoa __P((SOCKADDR *)); #endif /* ** Vendor codes ** ** Vendors can customize sendmail to add special behaviour, ** generally for back compatibility. Ideally, this should ** be set up in the .cf file using the "V" command. However, ** it's quite reasonable for some vendors to want the default ** be their old version; this can be set using ** -DVENDOR_DEFAULT=VENDOR_xxx ** in the Makefile. ** ** Vendors should apply to sendmail@CS.Berkeley.EDU for ** unique vendor codes. */ #define VENDOR_BERKELEY 1 /* Berkeley-native configuration file */ #define VENDOR_SUN 2 /* Sun-native configuration file */ #define VENDOR_HP 3 /* Hewlett-Packard specific config syntax */ #define VENDOR_IBM 4 /* IBM specific config syntax */ EXTERN int VendorCode; /* vendor-specific operation enhancements */ /* prototypes for vendor-specific hook routines */ extern void vendor_set_uid __P((UID_T)); extern void vendor_daemon_setup __P((ENVELOPE *)); /* ** Terminal escape codes. ** ** To make debugging output clearer. */ struct termescape { char *te_rv_on; /* turn reverse-video on */ char *te_rv_off; /* turn reverse-video off */ }; EXTERN struct termescape TermEscape; /* ** Error return from inet_addr(3), in case not defined in /usr/include. */ #ifndef INADDR_NONE # define INADDR_NONE 0xffffffff #endif /* ** Global variables. */ EXTERN bool FromFlag; /* if set, "From" person is explicit */ EXTERN bool MeToo; /* send to the sender also */ EXTERN bool IgnrDot; /* don't let dot end messages */ EXTERN bool SaveFrom; /* save leading "From" lines */ EXTERN bool Verbose; /* set if blow-by-blow desired */ EXTERN bool GrabTo; /* if set, get recipients from msg */ EXTERN bool SuprErrs; /* set if we are suppressing errors */ EXTERN bool HoldErrs; /* only output errors to transcript */ EXTERN bool NoConnect; /* don't connect to non-local mailers */ EXTERN bool SuperSafe; /* be extra careful, even if expensive */ EXTERN bool ForkQueueRuns; /* fork for each job when running the queue */ EXTERN bool AutoRebuild; /* auto-rebuild the alias database as needed */ EXTERN bool CheckAliases; /* parse addresses during newaliases */ EXTERN bool NoAlias; /* suppress aliasing */ EXTERN bool UseNameServer; /* using DNS -- interpret h_errno & MX RRs */ EXTERN bool UseHesiod; /* using Hesiod -- interpret Hesiod errors */ EXTERN bool SevenBitInput; /* force 7-bit data on input */ EXTERN bool HasEightBits; /* has at least one eight bit input byte */ EXTERN bool ConfigFileRead; /* configuration file has been read */ EXTERN time_t SafeAlias; /* interval to wait until @:@ in alias file */ EXTERN FILE *InChannel; /* input connection */ EXTERN FILE *OutChannel; /* output connection */ EXTERN char *RealUserName; /* real user name of caller */ EXTERN uid_t RealUid; /* real uid of caller */ EXTERN gid_t RealGid; /* real gid of caller */ EXTERN uid_t DefUid; /* default uid to run as */ EXTERN gid_t DefGid; /* default gid to run as */ EXTERN char *DefUser; /* default user to run as (from DefUid) */ -EXTERN int OldUmask; /* umask when sendmail starts up */ +EXTERN MODE_T OldUmask; /* umask when sendmail starts up */ EXTERN int Errors; /* set if errors (local to single pass) */ EXTERN int ExitStat; /* exit status code */ EXTERN int LineNumber; /* line number in current input */ EXTERN int LogLevel; /* level of logging to perform */ EXTERN int FileMode; /* mode on files */ EXTERN int QueueLA; /* load average starting forced queueing */ EXTERN int RefuseLA; /* load average refusing connections are */ EXTERN int CurrentLA; /* current load average */ EXTERN long QueueFactor; /* slope of queue function */ EXTERN time_t QueueIntvl; /* intervals between running the queue */ EXTERN char *HelpFile; /* location of SMTP help file */ EXTERN char *ErrMsgFile; /* file to prepend to all error messages */ EXTERN char *StatFile; /* location of statistics summary */ EXTERN char *QueueDir; /* location of queue directory */ EXTERN char *FileName; /* name to print on error messages */ EXTERN char *SmtpPhase; /* current phase in SMTP processing */ EXTERN char *MyHostName; /* name of this host for SMTP messages */ EXTERN char *RealHostName; /* name of host we are talking to */ EXTERN char *CurHostName; /* current host we are dealing with */ EXTERN jmp_buf TopFrame; /* branch-to-top-of-loop-on-error frame */ EXTERN bool QuickAbort; /* .... but only if we want a quick abort */ EXTERN bool LogUsrErrs; /* syslog user errors (e.g., SMTP RCPT cmd) */ EXTERN bool SendMIMEErrors; /* send error messages in MIME format */ EXTERN bool MatchGecos; /* look for user names in gecos field */ EXTERN bool UseErrorsTo; /* use Errors-To: header (back compat) */ EXTERN bool TryNullMXList; /* if we are the best MX, try host directly */ EXTERN bool InChild; /* true if running in an SMTP subprocess */ EXTERN bool DisConnected; /* running with OutChannel redirected to xf */ EXTERN bool ColonOkInAddr; /* single colon legal in address */ EXTERN bool HasWildcardMX; /* don't use MX records when canonifying */ EXTERN char SpaceSub; /* substitution for */ EXTERN int PrivacyFlags; /* privacy flags */ EXTERN char *ConfFile; /* location of configuration file [conf.c] */ extern char *PidFile; /* location of proc id file [conf.c] */ extern ADDRESS NullAddress; /* a null (template) address [main.c] */ EXTERN long WkClassFact; /* multiplier for message class -> priority */ EXTERN long WkRecipFact; /* multiplier for # of recipients -> priority */ EXTERN long WkTimeFact; /* priority offset each time this job is run */ EXTERN char *UdbSpec; /* user database source spec */ EXTERN int MaxHopCount; /* max # of hops until bounce */ EXTERN int ConfigLevel; /* config file level */ EXTERN char *TimeZoneSpec; /* override time zone specification */ EXTERN char *ForwardPath; /* path to search for .forward files */ EXTERN long MinBlocksFree; /* min # of blocks free on queue fs */ EXTERN char *FallBackMX; /* fall back MX host */ EXTERN long MaxMessageSize; /* advertised max size we will accept */ EXTERN time_t MinQueueAge; /* min delivery interval */ EXTERN time_t DialDelay; /* delay between dial-on-demand tries */ EXTERN char *SafeFileEnv; /* chroot location for file delivery */ EXTERN char *HostsFile; /* path to /etc/hosts file */ EXTERN char *HostStatDir; /* location of host status information */ EXTERN int MaxQueueRun; /* maximum number of jobs in one queue run */ EXTERN int MaxChildren; /* maximum number of daemonic children */ EXTERN int CurChildren; /* current number of daemonic children */ EXTERN char *SmtpGreeting; /* SMTP greeting message (old $e macro) */ EXTERN char *UnixFromLine; /* UNIX From_ line (old $l macro) */ EXTERN char *OperatorChars; /* operators (old $o macro) */ EXTERN bool DontInitGroups; /* avoid initgroups() because of NIS cost */ EXTERN int DefaultNotify; /* default DSN notification flags */ EXTERN bool AllowBogusHELO; /* allow syntax errors on HELO command */ EXTERN bool UserSubmission; /* initial (user) mail submission */ EXTERN uid_t RunAsUid; /* UID to become for bulk of run */ EXTERN gid_t RunAsGid; /* GID to become for bulk of run */ EXTERN bool SingleThreadDelivery; /* single thread hosts on delivery */ EXTERN bool UnsafeGroupWrites; /* group-writable files are unsafe */ EXTERN bool SingleLineFromHeader; /* force From: header to be one line */ EXTERN int ConnRateThrottle; /* throttle for SMTP connection rate */ EXTERN int MaxAliasRecursion; /* maximum depth of alias recursion */ EXTERN int MaxMacroRecursion; /* maximum depth of macro recursion */ EXTERN int MaxRuleRecursion; /* maximum depth of ruleset recursion */ EXTERN char *MustQuoteChars; /* quote these characters in phrases */ EXTERN char *ServiceSwitchFile; /* backup service switch */ EXTERN char *DefaultCharSet; /* default character set for MIME */ EXTERN int DeliveryNiceness; /* how nice to be during delivery */ EXTERN char *PostMasterCopy; /* address to get errs cc's */ EXTERN int CheckpointInterval; /* queue file checkpoint interval */ EXTERN bool DontPruneRoutes; /* don't prune source routes */ EXTERN bool DontExpandCnames; /* do not $[...$] expand CNAMEs */ EXTERN int MaxMciCache; /* maximum entries in MCI cache */ EXTERN time_t ServiceCacheTime; /* time service switch was cached */ EXTERN time_t ServiceCacheMaxAge; /* refresh interval for cache */ EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */ EXTERN time_t MciInfoTimeout; /* how long 'til we retry down hosts */ EXTERN char *QueueLimitRecipient; /* limit queue runs to this recipient */ EXTERN char *QueueLimitSender; /* limit queue runs to this sender */ EXTERN char *QueueLimitId; /* limit queue runs to this id */ EXTERN FILE *TrafficLogFile; /* file in which to log all traffic */ EXTERN char *DoubleBounceAddr; /* where to send double bounces */ EXTERN char **ExternalEnviron; /* input environment */ EXTERN char *UserEnviron[MAXUSERENVIRON + 1]; /* saved user environment */ extern int errno; /* ** Timeouts ** ** Indicated values are the MINIMUM per RFC 1123 section 5.3.2. */ EXTERN struct { /* RFC 1123-specified timeouts [minimum value] */ time_t to_initial; /* initial greeting timeout [5m] */ time_t to_mail; /* MAIL command [5m] */ time_t to_rcpt; /* RCPT command [5m] */ time_t to_datainit; /* DATA initiation [2m] */ time_t to_datablock; /* DATA block [3m] */ time_t to_datafinal; /* DATA completion [10m] */ time_t to_nextcommand; /* next command [5m] */ /* following timeouts are not mentioned in RFC 1123 */ time_t to_iconnect; /* initial connection timeout (first try) */ time_t to_connect; /* initial connection timeout (later tries) */ time_t to_rset; /* RSET command */ time_t to_helo; /* HELO command */ time_t to_quit; /* QUIT command */ time_t to_miscshort; /* misc short commands (NOOP, VERB, etc) */ time_t to_ident; /* IDENT protocol requests */ time_t to_fileopen; /* opening :include: and .forward files */ /* following are per message */ time_t to_q_return[MAXTOCLASS]; /* queue return timeouts */ time_t to_q_warning[MAXTOCLASS]; /* queue warning timeouts */ } TimeOuts; /* timeout classes for return and warning timeouts */ # define TOC_NORMAL 0 /* normal delivery */ # define TOC_URGENT 1 /* urgent delivery */ # define TOC_NONURGENT 2 /* non-urgent delivery */ /* ** Trace information */ /* trace vector and macros for debugging flags */ EXTERN u_char tTdvect[100]; # define tTd(flag, level) (tTdvect[flag] >= level) # define tTdlevel(flag) (tTdvect[flag]) /* ** Miscellaneous information. */ /* ** Some in-line functions */ /* set exit status */ #define setstat(s) { \ if (ExitStat == EX_OK || ExitStat == EX_TEMPFAIL) \ ExitStat = s; \ } /* make a copy of a string */ #define newstr(s) strcpy(xalloc(strlen(s) + 1), s) #define STRUCTCOPY(s, d) d = s /* ** Declarations of useful functions */ extern char *xalloc __P((int)); extern FILE *dfopen __P((char *, int, int)); extern char *sfgets __P((char *, int, FILE *, time_t, char *)); extern char *queuename __P((ENVELOPE *, int)); extern time_t curtime __P(()); extern bool transienterror __P((int)); extern char *fgetfolded __P((char *, int, FILE *)); extern char *username __P(()); extern char *pintvl __P((time_t, bool)); extern bool shouldqueue __P((long, time_t)); extern bool lockfile __P((int, char *, char *, int)); extern char *hostsignature __P((MAILER *, char *, ENVELOPE *)); extern void openxscript __P((ENVELOPE *)); extern void closexscript __P((ENVELOPE *)); extern char *shortenstring __P((const char *, int)); extern bool usershellok __P((char *, char *)); extern char *defcharset __P((ENVELOPE *)); extern bool wordinclass __P((char *, int)); extern char *denlstring __P((char *, bool, bool)); extern void makelower __P((char *)); extern void rebuildaliases __P((MAP *, bool)); extern void readaliases __P((MAP *, FILE *, bool, bool)); extern void finis __P(()); extern void setsender __P((char *, ENVELOPE *, char **, bool)); extern FILE *safefopen __P((char *, int, int, int)); extern void xputs __P((const char *)); extern void logsender __P((ENVELOPE *, char *)); extern void smtprset __P((MAILER *, MCI *, ENVELOPE *)); extern void smtpquit __P((MAILER *, MCI *, ENVELOPE *)); extern void setuserenv __P((const char *, const char *)); extern char *getextenv __P((const char *)); extern void disconnect __P((int, ENVELOPE *)); extern void putxline __P((char *, MCI *, int)); extern void dumpfd __P((int, bool, bool)); extern void makemailer __P((char *)); extern void putfromline __P((MCI *, ENVELOPE *)); extern void setoption __P((int, char *, bool, bool, ENVELOPE *)); extern void setclass __P((int, char *)); extern void inittimeouts __P((char *)); extern void logdelivery __P((MAILER *, MCI *, const char *, ADDRESS *, time_t, ENVELOPE *)); extern void giveresponse __P((int, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *)); extern void buildfname __P((char *, char *, char *, int)); -extern void mci_setstat __P((MCI *, char *, char *)); +extern void mci_setstat __P((MCI *, int, char *, char *)); extern char *smtptodsn __P((int)); extern int rscheck __P((char *, char *, char *, ENVELOPE *e)); extern void mime7to8 __P((MCI *, HDR *, ENVELOPE *)); extern int mime8to7 __P((MCI *, HDR *, ENVELOPE *, char **, int)); extern void xfclose __P((FILE *, char *, char *)); extern int switch_map_find __P((char *, char *[], short [])); extern void shorten_hostname __P((char [])); extern int waitfor __P((pid_t)); extern void proc_list_add __P((pid_t)); extern void proc_list_drop __P((pid_t)); extern void buffer_errors __P((void)); extern void flush_errors __P((bool)); extern void putline __P((char *, MCI *)); extern void putxline __P((char *, MCI *, int)); extern bool xtextok __P((char *)); extern char *xtextify __P((char *, char *)); extern char *xuntextify __P((char *)); extern void cleanstrcpy __P((char *, char *, int)); extern int getmxrr __P((char *, char **, bool, int *)); extern int strtorwset __P((char *, char **, int)); extern void printav __P((char **)); extern void printopenfds __P((bool)); extern int endmailer __P((MCI *, ENVELOPE *, char **)); extern void fixcrlf __P((char *, bool)); extern int dofork __P((void)); extern void initsys __P((ENVELOPE *)); extern void collect __P((FILE *, bool, bool, HDR **, ENVELOPE *)); extern void stripquotes __P((char *)); extern int include __P((char *, bool, ADDRESS *, ADDRESS **, int, ENVELOPE *)); extern void unlockqueue __P((ENVELOPE *)); extern void xunlink __P((char *)); extern void runqueue __P((bool)); extern int getla __P((void)); extern void sendall __P((ENVELOPE *, int)); extern void queueup __P((ENVELOPE *, bool)); extern void checkfds __P((char *)); extern int returntosender __P((char *, ADDRESS *, int, ENVELOPE *)); extern void markstats __P((ENVELOPE *, ADDRESS *)); extern void poststats __P((char *)); extern char *arpadate __P((char *)); extern int mailfile __P((char *, ADDRESS *, int, ENVELOPE *)); extern void loseqfile __P((ENVELOPE *, char *)); extern int prog_open __P((char **, int *, ENVELOPE *)); extern bool getcanonname __P((char *, int, bool)); extern bool validate_connection __P((SOCKADDR *, char *, ENVELOPE *)); extern bool path_is_dir __P((char *, bool)); extern pid_t dowork __P((char *, bool, bool, ENVELOPE *)); extern const char *errstring __P((int)); extern sigfunc_t setsignal __P((int, sigfunc_t)); extern int releasesignal __P((int)); extern struct hostent *sm_gethostbyname __P((char *)); extern struct hostent *sm_gethostbyaddr __P((char *, int, int)); extern struct passwd *sm_getpwnam __P((char *)); extern struct passwd *sm_getpwuid __P((UID_T)); extern struct passwd *finduser __P((char *, bool *)); #ifdef XDEBUG extern void checkfdopen __P((int, char *)); extern void checkfd012 __P((char *)); #endif /* ellipsis is a different case though */ #ifdef __STDC__ extern void auth_warning(ENVELOPE *, const char *, ...); extern void syserr(const char *, ...); extern void usrerr(const char *, ...); extern void message(const char *, ...); extern void nmessage(const char *, ...); extern void setproctitle(const char *fmt, ...); #else extern void auth_warning(); extern void syserr(); extern void usrerr(); extern void message(); extern void nmessage(); extern void setproctitle(); #endif #if !HASSNPRINTF # ifdef __STDC__ extern int snprintf(char *, size_t, const char *, ...); extern int vsnprintf(char *, size_t, const char *, va_list); # else extern int snprintf(); extern int vsnprintf(); # endif #endif Index: head/usr.sbin/sendmail/src/srvrsmtp.c =================================================================== --- head/usr.sbin/sendmail/src/srvrsmtp.c (revision 19843) +++ head/usr.sbin/sendmail/src/srvrsmtp.c (revision 19844) @@ -1,1389 +1,1404 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ # include "sendmail.h" #ifndef lint #ifdef SMTP -static char sccsid[] = "@(#)srvrsmtp.c 8.123 (Berkeley) 10/12/96 (with SMTP)"; +static char sccsid[] = "@(#)srvrsmtp.c 8.125 (Berkeley) 11/8/96 (with SMTP)"; #else -static char sccsid[] = "@(#)srvrsmtp.c 8.123 (Berkeley) 10/12/96 (without SMTP)"; +static char sccsid[] = "@(#)srvrsmtp.c 8.125 (Berkeley) 11/8/96 (without SMTP)"; #endif #endif /* not lint */ # include # ifdef SMTP /* ** SMTP -- run the SMTP protocol. ** ** Parameters: ** none. ** ** Returns: ** never. ** ** Side Effects: ** Reads commands from the input channel and processes ** them. */ struct cmd { char *cmdname; /* command name */ int cmdcode; /* internal code, see below */ }; /* values for cmdcode */ # define CMDERROR 0 /* bad command */ # define CMDMAIL 1 /* mail -- designate sender */ # define CMDRCPT 2 /* rcpt -- designate recipient */ # define CMDDATA 3 /* data -- send message text */ # define CMDRSET 4 /* rset -- reset state */ # define CMDVRFY 5 /* vrfy -- verify address */ # define CMDEXPN 6 /* expn -- expand address */ # define CMDNOOP 7 /* noop -- do nothing */ # define CMDQUIT 8 /* quit -- close connection and die */ # define CMDHELO 9 /* helo -- be polite */ # define CMDHELP 10 /* help -- give usage info */ # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ # define CMDETRN 12 /* etrn -- flush queue */ /* non-standard commands */ # define CMDONEX 16 /* onex -- sending one transaction only */ # define CMDVERB 17 /* verb -- go into verbose mode */ # define CMDXUSR 18 /* xusr -- initial (user) submission */ /* use this to catch and log "door handle" attempts on your system */ # define CMDLOGBOGUS 23 /* bogus command that should be logged */ /* debugging-only commands, only enabled if SMTPDEBUG is defined */ # define CMDDBGQSHOW 24 /* showq -- show send queue */ # define CMDDBGDEBUG 25 /* debug -- set debug mode */ static struct cmd CmdTab[] = { { "mail", CMDMAIL }, { "rcpt", CMDRCPT }, { "data", CMDDATA }, { "rset", CMDRSET }, { "vrfy", CMDVRFY }, { "expn", CMDEXPN }, { "help", CMDHELP }, { "noop", CMDNOOP }, { "quit", CMDQUIT }, { "helo", CMDHELO }, { "ehlo", CMDEHLO }, { "etrn", CMDETRN }, { "verb", CMDVERB }, { "onex", CMDONEX }, { "xusr", CMDXUSR }, /* remaining commands are here only to trap and log attempts to use them */ { "showq", CMDDBGQSHOW }, { "debug", CMDDBGDEBUG }, { "wiz", CMDLOGBOGUS }, { NULL, CMDERROR } }; bool OneXact = FALSE; /* one xaction only this run */ char *CurSmtpClient; /* who's at the other end of channel */ static char *skipword(); #define MAXBADCOMMANDS 25 /* maximum number of bad commands */ void smtp(nullserver, e) bool nullserver; register ENVELOPE *volatile e; { register char *p; register struct cmd *c; char *cmd; auto ADDRESS *vrfyqueue; ADDRESS *a; volatile bool gotmail; /* mail command received */ volatile bool gothello; /* helo command received */ bool vrfy; /* set if this is a vrfy command */ char *volatile protocol; /* sending protocol */ char *volatile sendinghost; /* sending hostname */ char *volatile peerhostname; /* name of SMTP peer or "localhost" */ auto char *delimptr; char *id; volatile int nrcpts = 0; /* number of RCPT commands */ bool doublequeue; volatile int badcommands = 0; /* count of bad commands */ volatile int nverifies = 0; /* count of VRFY/EXPN commands */ volatile int n_etrn = 0; /* count of ETRN commands */ char inp[MAXLINE]; char cmdbuf[MAXLINE]; extern ENVELOPE BlankEnvelope; extern void help __P((char *)); extern void settime __P((ENVELOPE *)); extern bool enoughdiskspace __P((long)); extern int runinchild __P((char *, ENVELOPE *)); if (fileno(OutChannel) != fileno(stdout)) { /* arrange for debugging output to go to remote host */ (void) dup2(fileno(OutChannel), fileno(stdout)); } settime(e); peerhostname = RealHostName; if (peerhostname == NULL) peerhostname = "localhost"; CurHostName = peerhostname; CurSmtpClient = macvalue('_', e); if (CurSmtpClient == NULL) CurSmtpClient = CurHostName; setproctitle("server %s startup", CurSmtpClient); #ifdef LOG if (LogLevel > 11) { /* log connection information */ syslog(LOG_INFO, "SMTP connect from %.100s (%.100s)", CurSmtpClient, anynet_ntoa(&RealHostAddr)); } #endif /* output the first line, inserting "ESMTP" as second word */ expand(SmtpGreeting, inp, sizeof inp, e); p = strchr(inp, '\n'); if (p != NULL) *p++ = '\0'; id = strchr(inp, ' '); if (id == NULL) id = &inp[strlen(inp)]; cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s"; message(cmd, id - inp, inp, id); /* output remaining lines */ while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) { *p++ = '\0'; if (isascii(*id) && isspace(*id)) id++; message("220-%s", id); } if (id != NULL) { if (isascii(*id) && isspace(*id)) id++; message("220 %s", id); } protocol = NULL; sendinghost = macvalue('s', e); gothello = FALSE; gotmail = FALSE; for (;;) { /* arrange for backout */ if (setjmp(TopFrame) > 0) { /* if() nesting is necessary for Cray UNICOS */ if (InChild) { QuickAbort = FALSE; SuprErrs = TRUE; finis(); } } QuickAbort = FALSE; HoldErrs = FALSE; LogUsrErrs = FALSE; e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); /* setup for the read */ e->e_to = NULL; Errors = 0; (void) fflush(stdout); /* read the input line */ SmtpPhase = "server cmd read"; setproctitle("server %s cmd read", CurSmtpClient); p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, SmtpPhase); /* handle errors */ if (p == NULL) { /* end of file, just die */ disconnect(1, e); message("421 %s Lost input channel from %s", MyHostName, CurSmtpClient); #ifdef LOG if (LogLevel > (gotmail ? 1 : 19)) syslog(LOG_NOTICE, "lost input channel from %.100s", CurSmtpClient); #endif if (InChild) ExitStat = EX_QUIT; finis(); } /* clean up end of line */ fixcrlf(inp, TRUE); /* echo command to transcript */ if (e->e_xfp != NULL) fprintf(e->e_xfp, "<<< %s\n", inp); #ifdef LOG if (LogLevel >= 15) syslog(LOG_INFO, "<-- %s", inp); #endif if (e->e_id == NULL) setproctitle("%s: %.80s", CurSmtpClient, inp); else setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); /* break off command */ for (p = inp; isascii(*p) && isspace(*p); p++) continue; cmd = cmdbuf; while (*p != '\0' && !(isascii(*p) && isspace(*p)) && cmd < &cmdbuf[sizeof cmdbuf - 2]) *cmd++ = *p++; *cmd = '\0'; /* throw away leading whitespace */ while (isascii(*p) && isspace(*p)) p++; /* decode command */ for (c = CmdTab; c->cmdname != NULL; c++) { if (!strcasecmp(c->cmdname, cmdbuf)) break; } /* reset errors */ errno = 0; /* ** Process command. ** ** If we are running as a null server, return 550 ** to everything. */ if (nullserver && c->cmdcode != CMDQUIT) { message("550 Access denied"); continue; } /* non-null server */ switch (c->cmdcode) { case CMDHELO: /* hello -- introduce yourself */ case CMDEHLO: /* extended hello */ if (c->cmdcode == CMDEHLO) { protocol = "ESMTP"; SmtpPhase = "server EHLO"; } else { protocol = "SMTP"; SmtpPhase = "server HELO"; } /* check for valid domain name (re 1123 5.2.5) */ if (*p == '\0' && !AllowBogusHELO) { message("501 %s requires domain address", cmdbuf); break; } else { register char *q; for (q = p; *q != '\0'; q++) { if (!isascii(*q)) break; if (isalnum(*q)) continue; if (isspace(*q)) { *q = '\0'; break; } if (strchr("[].-_#", *q) == NULL) break; } if (*q != '\0') { if (!AllowBogusHELO) message("501 Invalid domain name"); else message("250 %s Invalid domain name, accepting anyway", MyHostName); break; } } /* check for duplicate HELO/EHLO per RFC 1651 4.2 */ if (gothello) { message("503 %s Duplicate HELO/EHLO", MyHostName); break; } sendinghost = newstr(p); gothello = TRUE; if (c->cmdcode != CMDEHLO) { /* print old message and be done with it */ message("250 %s Hello %s, pleased to meet you", MyHostName, CurSmtpClient); break; } /* print extended message and brag */ message("250-%s Hello %s, pleased to meet you", MyHostName, CurSmtpClient); if (!bitset(PRIV_NOEXPN, PrivacyFlags)) { message("250-EXPN"); message("250-VERB"); } #if MIME8TO7 message("250-8BITMIME"); #endif if (MaxMessageSize > 0) message("250-SIZE %ld", MaxMessageSize); else message("250-SIZE"); #if DSN if (SendMIMEErrors) message("250-DSN"); #endif message("250-ONEX"); message("250-ETRN"); message("250-XUSR"); message("250 HELP"); break; case CMDMAIL: /* mail -- designate sender */ SmtpPhase = "server MAIL"; /* check for validity of this command */ if (!gothello) { /* set sending host to our known value */ if (sendinghost == NULL) sendinghost = peerhostname; if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) { message("503 Polite people say HELO first"); break; } } if (gotmail) { message("503 Sender already specified"); if (InChild) finis(); break; } if (InChild) { errno = 0; syserr("503 Nested MAIL command: MAIL %s", p); finis(); } p = skipword(p, "from"); if (p == NULL) break; /* fork a subprocess to process this command */ if (runinchild("SMTP-MAIL", e) > 0) break; if (!gothello) { auth_warning(e, "%s didn't use HELO protocol", CurSmtpClient); } #ifdef PICKY_HELO_CHECK if (strcasecmp(sendinghost, peerhostname) != 0 && (strcasecmp(peerhostname, "localhost") != 0 || strcasecmp(sendinghost, MyHostName) != 0)) { auth_warning(e, "Host %s claimed to be %s", CurSmtpClient, sendinghost); } #endif if (protocol == NULL) protocol = "SMTP"; define('r', protocol, e); define('s', sendinghost, e); initsys(e); nrcpts = 0; e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); /* child -- go do the processing */ if (setjmp(TopFrame) > 0) { /* this failed -- undo work */ undo_subproc: if (InChild) { QuickAbort = FALSE; SuprErrs = TRUE; e->e_flags &= ~EF_FATALERRS; finis(); } break; } QuickAbort = TRUE; /* must parse sender first */ delimptr = NULL; setsender(p, e, &delimptr, FALSE); if (delimptr != NULL && *delimptr != '\0') *delimptr++ = '\0'; /* do config file checking of the sender */ if (rscheck("check_mail", p, NULL, e) != EX_OK) goto undo_subproc; /* check for possible spoofing */ if (RealUid != 0 && OpMode == MD_SMTP && !wordinclass(RealUserName, 't') && !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && strcmp(e->e_from.q_user, RealUserName) != 0) { auth_warning(e, "%s owned process doing -bs", RealUserName); } /* now parse ESMTP arguments */ e->e_msgsize = 0; p = delimptr; while (p != NULL && *p != '\0') { char *kp; char *vp = NULL; extern void mail_esmtp_args __P((char *, char *, ENVELOPE *)); /* locate the beginning of the keyword */ while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; kp = p; /* skip to the value portion */ while ((isascii(*p) && isalnum(*p)) || *p == '-') p++; if (*p == '=') { *p++ = '\0'; vp = p; /* skip to the end of the value */ while (*p != '\0' && *p != ' ' && !(isascii(*p) && iscntrl(*p)) && *p != '=') p++; } if (*p != '\0') *p++ = '\0'; if (tTd(19, 1)) printf("MAIL: got arg %s=\"%s\"\n", kp, vp == NULL ? "" : vp); mail_esmtp_args(kp, vp, e); } if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) { usrerr("552 Message size exceeds fixed maximum message size (%ld)", MaxMessageSize); /* NOTREACHED */ } if (!enoughdiskspace(e->e_msgsize)) { message("452 Insufficient disk space; try again later"); break; } message("250 Sender ok"); gotmail = TRUE; break; case CMDRCPT: /* rcpt -- designate recipient */ if (!gotmail) { usrerr("503 Need MAIL before RCPT"); break; } SmtpPhase = "server RCPT"; if (setjmp(TopFrame) > 0) { e->e_flags &= ~EF_FATALERRS; break; } QuickAbort = TRUE; LogUsrErrs = TRUE; if (e->e_sendmode != SM_DELIVER) e->e_flags |= EF_VRFYONLY; p = skipword(p, "to"); if (p == NULL) break; a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); if (a == NULL) break; if (delimptr != NULL && *delimptr != '\0') *delimptr++ = '\0'; /* do config file checking of the recipient */ if (rscheck("check_rcpt", p, NULL, e) != EX_OK) break; /* now parse ESMTP arguments */ p = delimptr; while (p != NULL && *p != '\0') { char *kp; char *vp = NULL; extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); /* locate the beginning of the keyword */ while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; kp = p; /* skip to the value portion */ while ((isascii(*p) && isalnum(*p)) || *p == '-') p++; if (*p == '=') { *p++ = '\0'; vp = p; /* skip to the end of the value */ while (*p != '\0' && *p != ' ' && !(isascii(*p) && iscntrl(*p)) && *p != '=') p++; } if (*p != '\0') *p++ = '\0'; if (tTd(19, 1)) printf("RCPT: got arg %s=\"%s\"\n", kp, vp == NULL ? "" : vp); rcpt_esmtp_args(a, kp, vp, e); } /* save in recipient list after ESMTP mods */ a = recipient(a, &e->e_sendqueue, 0, e); if (Errors != 0) break; /* no errors during parsing, but might be a duplicate */ e->e_to = a->q_paddr; if (!bitset(QBADADDR, a->q_flags)) { message("250 Recipient ok%s", bitset(QQUEUEUP, a->q_flags) ? " (will queue)" : ""); nrcpts++; } else { /* punt -- should keep message in ADDRESS.... */ message("550 Addressee unknown"); } e->e_to = NULL; break; case CMDDATA: /* data -- text of mail */ SmtpPhase = "server DATA"; if (!gotmail) { message("503 Need MAIL command"); break; } else if (nrcpts <= 0) { message("503 Need RCPT (recipient)"); break; } /* check to see if we need to re-expand aliases */ /* also reset QBADADDR on already-diagnosted addrs */ doublequeue = FALSE; for (a = e->e_sendqueue; a != NULL; a = a->q_next) { if (bitset(QVERIFIED, a->q_flags)) { /* need to re-expand aliases */ doublequeue = TRUE; } if (bitset(QBADADDR, a->q_flags)) { /* make this "go away" */ a->q_flags |= QDONTSEND; a->q_flags &= ~QBADADDR; } } /* collect the text of the message */ SmtpPhase = "collect"; buffer_errors(); collect(InChannel, TRUE, doublequeue, NULL, e); flush_errors(TRUE); if (Errors != 0) goto abortmessage; /* make sure we actually do delivery */ e->e_flags &= ~EF_CLRQUEUE; /* from now on, we have to operate silently */ buffer_errors(); e->e_errormode = EM_MAIL; /* ** Arrange to send to everyone. ** If sending to multiple people, mail back ** errors rather than reporting directly. ** In any case, don't mail back errors for ** anything that has happened up to ** now (the other end will do this). ** Truncate our transcript -- the mail has gotten ** to us successfully, and if we have ** to mail this back, it will be easier ** on the reader. ** Then send to everyone. ** Finally give a reply code. If an error has ** already been given, don't mail a ** message back. ** We goose error returns by clearing error bit. */ SmtpPhase = "delivery"; e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); id = e->e_id; if (doublequeue) { /* make sure it is in the queue */ queueup(e, FALSE); } else { /* send to all recipients */ sendall(e, SM_DEFAULT); } e->e_to = NULL; /* issue success message */ message("250 %s Message accepted for delivery", id); /* if we just queued, poke it */ if (doublequeue && e->e_sendmode != SM_QUEUE && e->e_sendmode != SM_DEFER) { extern pid_t dowork(); unlockqueue(e); (void) dowork(id, TRUE, TRUE, e); } abortmessage: /* if in a child, pop back to our parent */ if (InChild) finis(); /* clean up a bit */ gotmail = FALSE; dropenvelope(e, TRUE); CurEnv = e = newenvelope(e, CurEnv); e->e_flags = BlankEnvelope.e_flags; break; case CMDRSET: /* rset -- reset state */ message("250 Reset state"); /* arrange to ignore any current send list */ e->e_sendqueue = NULL; e->e_flags |= EF_CLRQUEUE; if (InChild) finis(); /* clean up a bit */ gotmail = FALSE; dropenvelope(e, TRUE); CurEnv = e = newenvelope(e, CurEnv); break; case CMDVRFY: /* vrfy -- verify address */ case CMDEXPN: /* expn -- expand address */ if (++nverifies >= MAXBADCOMMANDS) { #ifdef LOG if (nverifies == MAXBADCOMMANDS && LogLevel > 5) { syslog(LOG_INFO, "%.100s: VRFY attack?", CurSmtpClient); } #endif sleep(1); } vrfy = c->cmdcode == CMDVRFY; if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, PrivacyFlags)) { if (vrfy) message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); else message("502 Sorry, we do not allow this operation"); #ifdef LOG if (LogLevel > 5) syslog(LOG_INFO, "%.100s: %s [rejected]", CurSmtpClient, shortenstring(inp, 203)); #endif break; } else if (!gothello && bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, PrivacyFlags)) { message("503 I demand that you introduce yourself first"); break; } if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) break; #ifdef LOG if (LogLevel > 5) syslog(LOG_INFO, "%.100s: %s", CurSmtpClient, shortenstring(inp, 203)); #endif vrfyqueue = NULL; QuickAbort = TRUE; if (vrfy) e->e_flags |= EF_VRFYONLY; while (*p != '\0' && isascii(*p) && isspace(*p)) p++; if (*p == '\0') { message("501 Argument required"); Errors++; } else { (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); } if (Errors != 0) { if (InChild) finis(); break; } if (vrfyqueue == NULL) { message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); } while (vrfyqueue != NULL) { extern void printvrfyaddr __P((ADDRESS *, bool, bool)); a = vrfyqueue; while ((a = a->q_next) != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) continue; if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) printvrfyaddr(vrfyqueue, a == NULL, vrfy); vrfyqueue = vrfyqueue->q_next; } if (InChild) finis(); break; case CMDETRN: /* etrn -- force queue flush */ if (strlen(p) <= 0) { message("500 Parameter required"); break; } /* crude way to avoid denial-of-service attacks */ if (n_etrn++ >= 3) sleep(3); id = p; if (*id == '@') id++; else *--id = '@'; #ifdef LOG if (LogLevel > 5) syslog(LOG_INFO, "%.100s: ETRN %s", CurSmtpClient, shortenstring(id, 203)); #endif QueueLimitRecipient = id; runqueue(TRUE); QueueLimitRecipient = NULL; message("250 Queuing for node %s started", p); break; case CMDHELP: /* help -- give user info */ help(p); break; case CMDNOOP: /* noop -- do nothing */ message("250 OK"); break; case CMDQUIT: /* quit -- leave mail */ message("221 %s closing connection", MyHostName); doquit: /* arrange to ignore any current send list */ e->e_sendqueue = NULL; /* avoid future 050 messages */ disconnect(1, e); if (InChild) ExitStat = EX_QUIT; finis(); case CMDVERB: /* set verbose mode */ if (bitset(PRIV_NOEXPN, PrivacyFlags)) { /* this would give out the same info */ message("502 Verbose unavailable"); break; } Verbose = TRUE; e->e_sendmode = SM_DELIVER; message("250 Verbose mode"); break; case CMDONEX: /* doing one transaction only */ OneXact = TRUE; message("250 Only one transaction"); break; case CMDXUSR: /* initial (user) submission */ UserSubmission = TRUE; message("250 Initial submission"); break; # ifdef SMTPDEBUG case CMDDBGQSHOW: /* show queues */ printf("Send Queue="); printaddr(e->e_sendqueue, TRUE); break; case CMDDBGDEBUG: /* set debug mode */ tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); tTflag(p); message("200 Debug set"); break; # else /* not SMTPDEBUG */ case CMDDBGQSHOW: /* show queues */ case CMDDBGDEBUG: /* set debug mode */ # endif /* SMTPDEBUG */ case CMDLOGBOGUS: /* bogus command */ # ifdef LOG if (LogLevel > 0) syslog(LOG_CRIT, "\"%s\" command from %.100s (%.100s)", c->cmdname, CurSmtpClient, anynet_ntoa(&RealHostAddr)); # endif /* FALL THROUGH */ case CMDERROR: /* unknown command */ if (++badcommands > MAXBADCOMMANDS) { message("421 %s Too many bad commands; closing connection", MyHostName); goto doquit; } message("500 Command unrecognized"); break; default: errno = 0; syserr("500 smtp: unknown code %d", c->cmdcode); break; } } } /* ** SKIPWORD -- skip a fixed word. ** ** Parameters: ** p -- place to start looking. ** w -- word to skip. ** ** Returns: ** p following w. ** NULL on error. ** ** Side Effects: ** clobbers the p data area. */ static char * skipword(p, w) register char *p; char *w; { register char *q; char *firstp = p; /* find beginning of word */ while (isascii(*p) && isspace(*p)) p++; q = p; /* find end of word */ while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) p++; while (isascii(*p) && isspace(*p)) *p++ = '\0'; if (*p != ':') { syntax: message("501 Syntax error in parameters scanning \"%s\"", shortenstring(firstp, 203)); Errors++; return (NULL); } *p++ = '\0'; while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') goto syntax; /* see if the input word matches desired word */ if (strcasecmp(q, w)) goto syntax; return (p); } /* ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line ** ** Parameters: ** kp -- the parameter key. ** vp -- the value of that parameter. ** e -- the envelope. ** ** Returns: ** none. */ void mail_esmtp_args(kp, vp, e) char *kp; char *vp; ENVELOPE *e; { if (strcasecmp(kp, "size") == 0) { if (vp == NULL) { usrerr("501 SIZE requires a value"); /* NOTREACHED */ } # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) e->e_msgsize = strtoul(vp, (char **) NULL, 10); # else e->e_msgsize = strtol(vp, (char **) NULL, 10); # endif } else if (strcasecmp(kp, "body") == 0) { if (vp == NULL) { usrerr("501 BODY requires a value"); /* NOTREACHED */ } else if (strcasecmp(vp, "8bitmime") == 0) { SevenBitInput = FALSE; } else if (strcasecmp(vp, "7bit") == 0) { SevenBitInput = TRUE; } else { usrerr("501 Unknown BODY type %s", vp); /* NOTREACHED */ } e->e_bodytype = newstr(vp); } else if (strcasecmp(kp, "envid") == 0) { if (vp == NULL) { usrerr("501 ENVID requires a value"); /* NOTREACHED */ } if (!xtextok(vp)) { usrerr("501 Syntax error in ENVID parameter value"); /* NOTREACHED */ } if (e->e_envid != NULL) { usrerr("501 Duplicate ENVID parameter"); /* NOTREACHED */ } e->e_envid = newstr(vp); } else if (strcasecmp(kp, "ret") == 0) { if (vp == NULL) { usrerr("501 RET requires a value"); /* NOTREACHED */ } if (bitset(EF_RET_PARAM, e->e_flags)) { usrerr("501 Duplicate RET parameter"); /* NOTREACHED */ } e->e_flags |= EF_RET_PARAM; if (strcasecmp(vp, "hdrs") == 0) e->e_flags |= EF_NO_BODY_RETN; else if (strcasecmp(vp, "full") != 0) { usrerr("501 Bad argument \"%s\" to RET", vp); /* NOTREACHED */ } } else { usrerr("501 %s parameter unrecognized", kp); /* NOTREACHED */ } } /* ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line ** ** Parameters: ** a -- the address corresponding to the To: parameter. ** kp -- the parameter key. ** vp -- the value of that parameter. ** e -- the envelope. ** ** Returns: ** none. */ void rcpt_esmtp_args(a, kp, vp, e) ADDRESS *a; char *kp; char *vp; ENVELOPE *e; { if (strcasecmp(kp, "notify") == 0) { char *p; if (vp == NULL) { usrerr("501 NOTIFY requires a value"); /* NOTREACHED */ } a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); a->q_flags |= QHASNOTIFY; if (strcasecmp(vp, "never") == 0) return; for (p = vp; p != NULL; vp = p) { p = strchr(p, ','); if (p != NULL) *p++ = '\0'; if (strcasecmp(vp, "success") == 0) a->q_flags |= QPINGONSUCCESS; else if (strcasecmp(vp, "failure") == 0) a->q_flags |= QPINGONFAILURE; else if (strcasecmp(vp, "delay") == 0) a->q_flags |= QPINGONDELAY; else { usrerr("501 Bad argument \"%s\" to NOTIFY", vp); /* NOTREACHED */ } } } else if (strcasecmp(kp, "orcpt") == 0) { if (vp == NULL) { usrerr("501 ORCPT requires a value"); /* NOTREACHED */ } if (strchr(vp, ';') == NULL || !xtextok(vp)) { usrerr("501 Syntax error in ORCPT parameter value"); /* NOTREACHED */ } if (a->q_orcpt != NULL) { usrerr("501 Duplicate ORCPT parameter"); /* NOTREACHED */ } a->q_orcpt = newstr(vp); } else { usrerr("501 %s parameter unrecognized", kp); /* NOTREACHED */ } } /* ** PRINTVRFYADDR -- print an entry in the verify queue ** ** Parameters: ** a -- the address to print ** last -- set if this is the last one. ** vrfy -- set if this is a VRFY command. ** ** Returns: ** none. ** ** Side Effects: ** Prints the appropriate 250 codes. */ void printvrfyaddr(a, last, vrfy) register ADDRESS *a; bool last; bool vrfy; { char fmtbuf[20]; if (vrfy && a->q_mailer != NULL && !bitnset(M_VRFY250, a->q_mailer->m_flags)) strcpy(fmtbuf, "252"); else strcpy(fmtbuf, "250"); fmtbuf[3] = last ? ' ' : '-'; if (a->q_fullname == NULL) { if (strchr(a->q_user, '@') == NULL) strcpy(&fmtbuf[4], "<%s@%s>"); else strcpy(&fmtbuf[4], "<%s>"); message(fmtbuf, a->q_user, MyHostName); } else { if (strchr(a->q_user, '@') == NULL) strcpy(&fmtbuf[4], "%s <%s@%s>"); else strcpy(&fmtbuf[4], "%s <%s>"); message(fmtbuf, a->q_fullname, a->q_user, MyHostName); } } /* ** RUNINCHILD -- return twice -- once in the child, then in the parent again ** ** Parameters: ** label -- a string used in error messages ** ** Returns: ** zero in the child ** one in the parent ** ** Side Effects: ** none. */ int runinchild(label, e) char *label; register ENVELOPE *e; { - int childpid; + pid_t childpid; + sigfunc_t chldsig; if (!OneXact) { + /* + ** Disable child process reaping, in case ETRN has preceeded + ** MAIL command. + */ + +#ifdef SIGCHLD + chldsig = setsignal(SIGCHLD, SIG_IGN); +#endif + childpid = dofork(); if (childpid < 0) { syserr("451 %s: cannot fork", label); return (1); } if (childpid > 0) { auto int st; /* parent -- wait for child to complete */ setproctitle("server %s child wait", CurSmtpClient); st = waitfor(childpid); if (st == -1) syserr("451 %s: lost child", label); else if (!WIFEXITED(st)) syserr("451 %s: died on signal %d", label, st & 0177); /* if we exited on a QUIT command, complete the process */ if (WEXITSTATUS(st) == EX_QUIT) { disconnect(1, e); finis(); } + +#ifdef SIGCHLD + /* restore the child signal */ + (void) setsignal(SIGCHLD, chldsig); +#endif return (1); } else { /* child */ InChild = TRUE; QuickAbort = FALSE; clearenvelope(e, FALSE); } } /* open alias database */ initmaps(FALSE, e); return (0); } # endif /* SMTP */ /* ** HELP -- implement the HELP command. ** ** Parameters: ** topic -- the topic we want help for. ** ** Returns: ** none. ** ** Side Effects: ** outputs the help file to message output. */ void help(topic) char *topic; { register FILE *hf; int len; bool noinfo; char buf[MAXLINE]; extern char Version[]; if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) { /* no help */ errno = 0; message("502 Sendmail %s -- HELP not implemented", Version); return; } if (topic == NULL || *topic == '\0') { topic = "smtp"; message("214-This is Sendmail version %s", Version); noinfo = FALSE; } else { makelower(topic); noinfo = TRUE; } len = strlen(topic); while (fgets(buf, sizeof buf, hf) != NULL) { if (strncmp(buf, topic, len) == 0) { register char *p; p = strchr(buf, '\t'); if (p == NULL) p = buf; else p++; fixcrlf(p, TRUE); message("214-%s", p); noinfo = FALSE; } } if (noinfo) message("504 HELP topic \"%.10s\" unknown", topic); else message("214 End of HELP info"); (void) fclose(hf); } Index: head/usr.sbin/sendmail/src/usersmtp.c =================================================================== --- head/usr.sbin/sendmail/src/usersmtp.c (revision 19843) +++ head/usr.sbin/sendmail/src/usersmtp.c (revision 19844) @@ -1,1090 +1,1198 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ # include "sendmail.h" #ifndef lint #ifdef SMTP -static char sccsid[] = "@(#)usersmtp.c 8.72 (Berkeley) 9/15/96 (with SMTP)"; +static char sccsid[] = "@(#)usersmtp.c 8.75 (Berkeley) 11/6/96 (with SMTP)"; #else -static char sccsid[] = "@(#)usersmtp.c 8.72 (Berkeley) 9/15/96 (without SMTP)"; +static char sccsid[] = "@(#)usersmtp.c 8.75 (Berkeley) 11/6/96 (without SMTP)"; #endif #endif /* not lint */ # include # include # ifdef SMTP /* ** USERSMTP -- run SMTP protocol from the user end. ** ** This protocol is described in RFC821. */ #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ #define SMTPCLOSING 421 /* "Service Shutting Down" */ char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ char SmtpError[MAXLINE] = ""; /* save failure error messages */ int SmtpPid; /* pid of mailer */ bool SmtpNeedIntro; /* need "while talking" in transcript */ extern void smtpmessage __P((char *f, MAILER *m, MCI *mci, ...)); extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)())); /* ** SMTPINIT -- initialize SMTP. ** ** Opens the connection and sends the initial protocol. ** ** Parameters: ** m -- mailer to create connection to. ** pvp -- pointer to parameter vector to pass to ** the mailer. ** ** Returns: ** none. ** ** Side Effects: ** creates connection and sends initial protocol. */ void smtpinit(m, mci, e) MAILER *m; register MCI *mci; ENVELOPE *e; { register int r; register char *p; extern void esmtp_check(); extern void helo_options(); if (tTd(18, 1)) { printf("smtpinit "); mci_dump(mci, FALSE); } /* ** Open the connection to the mailer. */ SmtpError[0] = '\0'; CurHostName = mci->mci_host; /* XXX UGLY XXX */ if (CurHostName == NULL) CurHostName = MyHostName; SmtpNeedIntro = TRUE; switch (mci->mci_state) { case MCIS_ACTIVE: /* need to clear old information */ smtprset(m, mci, e); /* fall through */ case MCIS_OPEN: return; case MCIS_ERROR: case MCIS_SSD: /* shouldn't happen */ smtpquit(m, mci, e); /* fall through */ case MCIS_CLOSED: syserr("451 smtpinit: state CLOSED"); return; case MCIS_OPENING: break; } mci->mci_state = MCIS_OPENING; /* ** Get the greeting message. ** This should appear spontaneously. Give it five minutes to ** happen. */ SmtpPhase = mci->mci_phase = "client greeting"; setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); - if (r < 0 || REPLYTYPE(r) == 4) + if (r < 0) goto tempfail1; + if (REPLYTYPE(r) == 4) + goto tempfail2; if (REPLYTYPE(r) != 2) goto unavailable; /* ** Send the HELO command. ** My mother taught me to always introduce myself. */ +#if FFR_LMTP + if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags)) +#else if (bitnset(M_ESMTP, m->m_flags)) +#endif mci->mci_flags |= MCIF_ESMTP; tryhelo: +#if FFR_LMTP + if (bitnset(M_LMTP, m->m_flags)) + { + smtpmessage("LHLO %s", m, mci, MyHostName); + SmtpPhase = mci->mci_phase = "client LHLO"; + } + else if (bitset(MCIF_ESMTP, mci->mci_flags)) +#else if (bitset(MCIF_ESMTP, mci->mci_flags)) +#endif { smtpmessage("EHLO %s", m, mci, MyHostName); SmtpPhase = mci->mci_phase = "client EHLO"; } else { smtpmessage("HELO %s", m, mci, MyHostName); SmtpPhase = mci->mci_phase = "client HELO"; } setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_helo, helo_options); if (r < 0) goto tempfail1; else if (REPLYTYPE(r) == 5) { +#if FFR_LMTP + if (bitset(MCIF_ESMTP, mci->mci_flags) && + !bitnset(M_LMTP, m->m_flags)) +#else if (bitset(MCIF_ESMTP, mci->mci_flags)) +#endif { /* try old SMTP instead */ mci->mci_flags &= ~MCIF_ESMTP; goto tryhelo; } goto unavailable; } else if (REPLYTYPE(r) != 2) - goto tempfail1; + goto tempfail2; /* ** Check to see if we actually ended up talking to ourself. ** This means we didn't know about an alias or MX, or we managed ** to connect to an echo server. */ p = strchr(&SmtpReplyBuffer[4], ' '); if (p != NULL) *p = '\0'; if (!bitnset(M_NOLOOPCHECK, m->m_flags) && +#if FFR_LMTP + !bitnset(M_LMTP, m->m_flags) && +#endif strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) { syserr("553 %s config error: mail loops back to me (MX problem?)", mci->mci_host); - mci->mci_exitstat = EX_CONFIG; + mci_setstat(mci, EX_CONFIG, NULL, NULL); mci->mci_errno = 0; smtpquit(m, mci, e); return; } /* ** If this is expected to be another sendmail, send some internal ** commands. */ if (bitnset(M_INTERNAL, m->m_flags)) { /* tell it to be verbose */ smtpmessage("VERB", m, mci); r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); if (r < 0) - goto tempfail2; + goto tempfail1; } if (mci->mci_state != MCIS_CLOSED) { mci->mci_state = MCIS_OPEN; return; } /* got a 421 error code during startup */ tempfail1: + if (mci->mci_errno == 0) + mci->mci_errno = errno; + mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); + if (mci->mci_state != MCIS_CLOSED) + smtpquit(m, mci, e); + return; + tempfail2: - mci->mci_exitstat = EX_TEMPFAIL; if (mci->mci_errno == 0) mci->mci_errno = errno; + /* XXX should use code from other end iff ENHANCEDSTATUSCODES */ + mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer); if (mci->mci_state != MCIS_CLOSED) smtpquit(m, mci, e); return; unavailable: - mci->mci_exitstat = EX_UNAVAILABLE; mci->mci_errno = errno; + mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer); smtpquit(m, mci, e); return; } /* ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol ** ** Parameters: ** line -- the response line. ** firstline -- set if this is the first line of the reply. ** m -- the mailer. ** mci -- the mailer connection info. ** e -- the envelope. ** ** Returns: ** none. */ void esmtp_check(line, firstline, m, mci, e) char *line; bool firstline; MAILER *m; register MCI *mci; ENVELOPE *e; { if (strstr(line, "ESMTP") != NULL) mci->mci_flags |= MCIF_ESMTP; if (strstr(line, "8BIT-OK") != NULL) mci->mci_flags |= MCIF_8BITOK; } /* ** HELO_OPTIONS -- process the options on a HELO line. ** ** Parameters: ** line -- the response line. ** firstline -- set if this is the first line of the reply. ** m -- the mailer. ** mci -- the mailer connection info. ** e -- the envelope. ** ** Returns: ** none. */ void helo_options(line, firstline, m, mci, e) char *line; bool firstline; MAILER *m; register MCI *mci; ENVELOPE *e; { register char *p; if (firstline) return; if (strlen(line) < (SIZE_T) 5) return; line += 4; p = strchr(line, ' '); if (p != NULL) *p++ = '\0'; if (strcasecmp(line, "size") == 0) { mci->mci_flags |= MCIF_SIZE; if (p != NULL) mci->mci_maxsize = atol(p); } else if (strcasecmp(line, "8bitmime") == 0) { mci->mci_flags |= MCIF_8BITMIME; mci->mci_flags &= ~MCIF_7BIT; } else if (strcasecmp(line, "expn") == 0) mci->mci_flags |= MCIF_EXPN; else if (strcasecmp(line, "dsn") == 0) mci->mci_flags |= MCIF_DSN; } /* ** SMTPMAILFROM -- send MAIL command ** ** Parameters: ** m -- the mailer. ** mci -- the mailer connection structure. ** e -- the envelope (including the sender to specify). */ int smtpmailfrom(m, mci, e) MAILER *m; MCI *mci; ENVELOPE *e; { int r; int l; char *bufp; char *bodytype; char buf[MAXNAME + 1]; char optbuf[MAXLINE]; if (tTd(18, 2)) printf("smtpmailfrom: CurHost=%s\n", CurHostName); /* set up appropriate options to include */ if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize); else strcpy(optbuf, ""); l = sizeof optbuf - strlen(optbuf) - 1; bodytype = e->e_bodytype; if (bitset(MCIF_8BITMIME, mci->mci_flags)) { if (bodytype == NULL && bitset(MM_MIME8BIT, MimeMode) && bitset(EF_HAS8BIT, e->e_flags) && !bitset(EF_DONT_MIME, e->e_flags) && !bitnset(M_8BITS, m->m_flags)) bodytype = "8BITMIME"; if (bodytype != NULL) { strcat(optbuf, " BODY="); strcat(optbuf, bodytype); l -= strlen(optbuf); } } else if (bitnset(M_8BITS, m->m_flags) || !bitset(EF_HAS8BIT, e->e_flags) || bitset(MCIF_8BITOK, mci->mci_flags)) { /* just pass it through */ } #if MIME8TO7 else if (bitset(MM_CVTMIME, MimeMode) && !bitset(EF_DONT_MIME, e->e_flags) && (!bitset(MM_PASS8BIT, MimeMode) || bitset(EF_IS_MIME, e->e_flags))) { /* must convert from 8bit MIME format to 7bit encoded */ mci->mci_flags |= MCIF_CVT8TO7; } #endif else if (!bitset(MM_PASS8BIT, MimeMode)) { /* cannot just send a 8-bit version */ extern char MsgBuf[]; usrerr("%s does not support 8BITMIME", mci->mci_host); - mci_setstat(mci, "5.6.3", MsgBuf); + mci_setstat(mci, EX_DATAERR, "5.6.3", MsgBuf); return EX_DATAERR; } if (bitset(MCIF_DSN, mci->mci_flags)) { if (e->e_envid != NULL && strlen(e->e_envid) < (SIZE_T) (l - 7)) { strcat(optbuf, " ENVID="); strcat(optbuf, e->e_envid); l -= strlen(optbuf); } /* RET= parameter */ if (bitset(EF_RET_PARAM, e->e_flags) && l >= 9) { strcat(optbuf, " RET="); if (bitset(EF_NO_BODY_RETN, e->e_flags)) strcat(optbuf, "HDRS"); else strcat(optbuf, "FULL"); l -= 9; } } /* ** Send the MAIL command. ** Designates the sender. */ mci->mci_state = MCIS_ACTIVE; if (bitset(EF_RESPONSE, e->e_flags) && !bitnset(M_NO_NULL_FROM, m->m_flags)) (void) strcpy(buf, ""); else expand("\201g", buf, sizeof buf, e); if (buf[0] == '<') { /* strip off (put back on below) */ bufp = &buf[strlen(buf) - 1]; if (*bufp == '>') *bufp = '\0'; bufp = &buf[1]; } else bufp = buf; if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) || !bitnset(M_FROMPATH, m->m_flags)) { smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); } else { smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, *bufp == '@' ? ',' : ':', bufp, optbuf); } SmtpPhase = mci->mci_phase = "client MAIL"; setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_mail, NULL); - if (r < 0 || r == 421) + if (r < 0) { - /* communications failure/service shutting down */ - mci->mci_exitstat = EX_TEMPFAIL; + /* communications failure */ mci->mci_errno = errno; + mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); smtpquit(m, mci, e); return EX_TEMPFAIL; } + else if (r == 421) + { + /* service shutting down */ + mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer); + smtpquit(m, mci, e); + return EX_TEMPFAIL; + } else if (REPLYTYPE(r) == 4) { - mci_setstat(mci, smtptodsn(r), SmtpReplyBuffer); + mci_setstat(mci, EX_TEMPFAIL, smtptodsn(r), SmtpReplyBuffer); return EX_TEMPFAIL; } else if (REPLYTYPE(r) == 2) { return EX_OK; } else if (r == 501) { /* syntax error in arguments */ - mci_setstat(mci, "5.5.2", SmtpReplyBuffer); + mci_setstat(mci, EX_DATAERR, "5.5.2", SmtpReplyBuffer); return EX_DATAERR; } else if (r == 553) { /* mailbox name not allowed */ - mci_setstat(mci, "5.1.3", SmtpReplyBuffer); + mci_setstat(mci, EX_DATAERR, "5.1.3", SmtpReplyBuffer); return EX_DATAERR; } else if (r == 552) { /* exceeded storage allocation */ - mci_setstat(mci, "5.2.2", SmtpReplyBuffer); + mci_setstat(mci, EX_UNAVAILABLE, "5.2.2", SmtpReplyBuffer); return EX_UNAVAILABLE; } else if (REPLYTYPE(r) == 5) { /* unknown error */ - mci_setstat(mci, "5.0.0", SmtpReplyBuffer); + mci_setstat(mci, EX_UNAVAILABLE, "5.0.0", SmtpReplyBuffer); return EX_UNAVAILABLE; } #ifdef LOG if (LogLevel > 1) { syslog(LOG_CRIT, "%s: %.100s: SMTP MAIL protocol error: %s", e->e_id, mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } #endif /* protocol error -- close up */ - mci_setstat(mci, "5.5.1", SmtpReplyBuffer); + mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer); smtpquit(m, mci, e); return EX_PROTOCOL; } /* ** SMTPRCPT -- designate recipient. ** ** Parameters: ** to -- address of recipient. ** m -- the mailer we are sending to. ** mci -- the connection info for this transaction. ** e -- the envelope for this transaction. ** ** Returns: ** exit status corresponding to recipient status. ** ** Side Effects: ** Sends the mail via SMTP. */ int smtprcpt(to, m, mci, e) ADDRESS *to; register MAILER *m; MCI *mci; ENVELOPE *e; { register int r; int l; char optbuf[MAXLINE]; strcpy(optbuf, ""); l = sizeof optbuf - 1; if (bitset(MCIF_DSN, mci->mci_flags)) { /* NOTIFY= parameter */ if (bitset(QHASNOTIFY, to->q_flags) && - bitset(QPRIMARY, to->q_flags)) + bitset(QPRIMARY, to->q_flags) && + !bitnset(M_LOCALMAILER, m->m_flags)) { bool firstone = TRUE; strcat(optbuf, " NOTIFY="); if (bitset(QPINGONSUCCESS, to->q_flags)) { strcat(optbuf, "SUCCESS"); firstone = FALSE; } if (bitset(QPINGONFAILURE, to->q_flags)) { if (!firstone) strcat(optbuf, ","); strcat(optbuf, "FAILURE"); firstone = FALSE; } if (bitset(QPINGONDELAY, to->q_flags)) { if (!firstone) strcat(optbuf, ","); strcat(optbuf, "DELAY"); firstone = FALSE; } if (firstone) strcat(optbuf, "NEVER"); l -= strlen(optbuf); } /* ORCPT= parameter */ if (to->q_orcpt != NULL && strlen(to->q_orcpt) + 7 < l) { strcat(optbuf, " ORCPT="); strcat(optbuf, to->q_orcpt); l -= strlen(optbuf); } } smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf); SmtpPhase = mci->mci_phase = "client RCPT"; setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); to->q_rstatus = newstr(SmtpReplyBuffer); to->q_status = smtptodsn(r); to->q_statmta = mci->mci_host; if (r < 0 || REPLYTYPE(r) == 4) return EX_TEMPFAIL; else if (REPLYTYPE(r) == 2) return EX_OK; else if (r == 550) { to->q_status = "5.1.1"; return EX_NOUSER; } else if (r == 551) { to->q_status = "5.1.6"; return EX_NOUSER; } else if (r == 553) { to->q_status = "5.1.3"; return EX_NOUSER; } else if (REPLYTYPE(r) == 5) { return EX_UNAVAILABLE; } #ifdef LOG if (LogLevel > 1) { syslog(LOG_CRIT, "%s: %.100s: SMTP RCPT protocol error: %s", e->e_id, mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } #endif return EX_PROTOCOL; } /* ** SMTPDATA -- send the data and clean up the transaction. ** ** Parameters: ** m -- mailer being sent to. +** mci -- the mailer connection information. ** e -- the envelope for this message. ** ** Returns: ** exit status corresponding to DATA command. ** ** Side Effects: ** none. */ static jmp_buf CtxDataTimeout; static void datatimeout(); int smtpdata(m, mci, e) MAILER *m; register MCI *mci; register ENVELOPE *e; { register int r; register EVENT *ev; + int rstat; time_t timeout; /* ** Send the data. ** First send the command and check that it is ok. ** Then send the data. ** Follow it up with a dot to terminate. ** Finally get the results of the transaction. */ /* send the command and check ok to proceed */ smtpmessage("DATA", m, mci); SmtpPhase = mci->mci_phase = "client DATA 354"; setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_datainit, NULL); if (r < 0 || REPLYTYPE(r) == 4) { smtpquit(m, mci, e); return EX_TEMPFAIL; } else if (REPLYTYPE(r) == 5) { smtprset(m, mci, e); return EX_UNAVAILABLE; } else if (r != 354) { #ifdef LOG if (LogLevel > 1) { syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-1 protocol error: %s", e->e_id, mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } #endif smtprset(m, mci, e); return (EX_PROTOCOL); } /* ** Set timeout around data writes. Make it at least large ** enough for DNS timeouts on all recipients plus some fudge ** factor. The main thing is that it should not be infinite. */ if (setjmp(CtxDataTimeout) != 0) { mci->mci_errno = errno; - mci->mci_exitstat = EX_TEMPFAIL; mci->mci_state = MCIS_ERROR; + mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); syserr("451 timeout writing message to %s", mci->mci_host); smtpquit(m, mci, e); return EX_TEMPFAIL; } timeout = e->e_msgsize / 16; if (timeout < (time_t) 600) timeout = (time_t) 600; timeout += e->e_nrcpts * 300; ev = setevent(timeout, datatimeout, 0); /* ** Output the actual message. */ (*e->e_puthdr)(mci, e->e_header, e); (*e->e_putbody)(mci, e, NULL); /* ** Cleanup after sending message. */ clrevent(ev); if (ferror(mci->mci_out)) { /* error during processing -- don't send the dot */ mci->mci_errno = EIO; - mci->mci_exitstat = EX_IOERR; mci->mci_state = MCIS_ERROR; + mci_setstat(mci, EX_IOERR, "4.4.2", NULL); smtpquit(m, mci, e); return EX_IOERR; } /* terminate the message */ fprintf(mci->mci_out, ".%s", m->m_eol); if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); if (Verbose) nmessage(">>> ."); /* check for the results of the transaction */ - SmtpPhase = mci->mci_phase = "client DATA 250"; + SmtpPhase = mci->mci_phase = "client DATA status"; setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); +#if FFR_LMTP + if (bitnset(M_LMTP, m->m_flags)) + return EX_OK; +#endif r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); if (r < 0) { smtpquit(m, mci, e); return EX_TEMPFAIL; } mci->mci_state = MCIS_OPEN; - mci_setstat(mci, smtptodsn(r), SmtpReplyBuffer); - e->e_statmsg = newstr(&SmtpReplyBuffer[4]); if (REPLYTYPE(r) == 4) - return EX_TEMPFAIL; + rstat = EX_TEMPFAIL; else if (REPLYCLASS(r) != 5) - /* fall through */ ; + rstat = EX_PROTOCOL; else if (REPLYTYPE(r) == 2) - return EX_OK; + rstat = EX_OK; else if (REPLYTYPE(r) == 5) - return EX_UNAVAILABLE; + rstat = EX_UNAVAILABLE; + else + rstat = EX_PROTOCOL; + mci_setstat(mci, rstat, smtptodsn(r), SmtpReplyBuffer); + if (e->e_statmsg != NULL) + free(e->e_statmsg); + e->e_statmsg = newstr(&SmtpReplyBuffer[4]); + if (rstat != EX_PROTOCOL) + return rstat; #ifdef LOG if (LogLevel > 1) { syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-2 protocol error: %s", e->e_id, mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } #endif - return EX_PROTOCOL; + return rstat; } static void datatimeout() { longjmp(CtxDataTimeout, 1); } /* +** SMTPGETSTAT -- get status code from DATA in LMTP +** +** Parameters: +** m -- the mailer to which we are sending the message. +** mci -- the mailer connection structure. +** e -- the current envelope. +** +** Returns: +** The exit status corresponding to the reply code. +*/ + +#if FFR_LMTP + +int +smtpgetstat(m, mci, e) + MAILER *m; + MCI *mci; + ENVELOPE *e; +{ + int r; + int stat; + + /* check for the results of the transaction */ + r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); + if (r < 0) + { + smtpquit(m, mci, e); + return EX_TEMPFAIL; + } + if (e->e_statmsg != NULL) + free(e->e_statmsg); + e->e_statmsg = newstr(&SmtpReplyBuffer[4]); + if (REPLYTYPE(r) == 4) + stat = EX_TEMPFAIL; + else if (REPLYCLASS(r) != 5) + stat = EX_PROTOCOL; + else if (REPLYTYPE(r) == 2) + stat = EX_OK; + else if (REPLYTYPE(r) == 5) + stat = EX_UNAVAILABLE; + mci_setstat(mci, stat, smtptodsn(r), SmtpReplyBuffer); +#ifdef LOG + if (LogLevel > 1 && stat == EX_PROTOCOL) + { + syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-3 protocol error: %s", + e->e_id, mci->mci_host, + shortenstring(SmtpReplyBuffer, 403)); + } +#endif + return stat; +} + +#endif + /* ** SMTPQUIT -- close the SMTP connection. ** ** Parameters: ** m -- a pointer to the mailer. +** mci -- the mailer connection information. +** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** sends the final protocol and closes the connection. */ void smtpquit(m, mci, e) register MAILER *m; register MCI *mci; ENVELOPE *e; { bool oldSuprErrs = SuprErrs; /* ** Suppress errors here -- we may be processing a different ** job when we do the quit connection, and we don't want the ** new job to be penalized for something that isn't it's ** problem. */ SuprErrs = TRUE; /* send the quit message if we haven't gotten I/O error */ if (mci->mci_state != MCIS_ERROR) { SmtpPhase = "client QUIT"; smtpmessage("QUIT", m, mci); (void) reply(m, mci, e, TimeOuts.to_quit, NULL); SuprErrs = oldSuprErrs; if (mci->mci_state == MCIS_CLOSED) { SuprErrs = oldSuprErrs; return; } } /* now actually close the connection and pick up the zombie */ (void) endmailer(mci, e, NULL); SuprErrs = oldSuprErrs; } /* ** SMTPRSET -- send a RSET (reset) command */ void smtprset(m, mci, e) register MAILER *m; register MCI *mci; ENVELOPE *e; { int r; SmtpPhase = "client RSET"; smtpmessage("RSET", m, mci); r = reply(m, mci, e, TimeOuts.to_rset, NULL); if (r < 0) mci->mci_state = MCIS_ERROR; else if (REPLYTYPE(r) == 2) { mci->mci_state = MCIS_OPEN; return; } smtpquit(m, mci, e); } /* ** SMTPPROBE -- check the connection state */ int smtpprobe(mci) register MCI *mci; { int r; MAILER *m = mci->mci_mailer; extern ENVELOPE BlankEnvelope; ENVELOPE *e = &BlankEnvelope; SmtpPhase = "client probe"; smtpmessage("RSET", m, mci); r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); if (r < 0 || REPLYTYPE(r) != 2) smtpquit(m, mci, e); return r; } /* ** REPLY -- read arpanet reply ** ** Parameters: ** m -- the mailer we are reading the reply from. ** mci -- the mailer connection info structure. ** e -- the current envelope. ** timeout -- the timeout for reads. ** pfunc -- processing function called on each line of response. ** If null, no special processing is done. ** ** Returns: ** reply code it reads. ** ** Side Effects: ** flushes the mail file. */ int reply(m, mci, e, timeout, pfunc) MAILER *m; MCI *mci; ENVELOPE *e; time_t timeout; void (*pfunc)(); { register char *bufp; register int r; bool firstline = TRUE; char junkbuf[MAXLINE]; if (mci->mci_out != NULL) (void) fflush(mci->mci_out); if (tTd(18, 1)) printf("reply\n"); /* ** Read the input line, being careful not to hang. */ for (bufp = SmtpReplyBuffer;; bufp = junkbuf) { register char *p; extern time_t curtime(); /* actually do the read */ if (e->e_xfp != NULL) (void) fflush(e->e_xfp); /* for debugging */ /* if we are in the process of closing just give the code */ if (mci->mci_state == MCIS_CLOSED) return (SMTPCLOSING); if (mci->mci_out != NULL) fflush(mci->mci_out); /* get the line from the other side */ p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); mci->mci_lastuse = curtime(); if (p == NULL) { bool oldholderrs; + extern char MsgBuf[]; /* if the remote end closed early, fake an error */ if (errno == 0) # ifdef ECONNRESET errno = ECONNRESET; # else /* ECONNRESET */ errno = EPIPE; # endif /* ECONNRESET */ mci->mci_errno = errno; - mci->mci_exitstat = EX_TEMPFAIL; oldholderrs = HoldErrs; HoldErrs = TRUE; usrerr("451 reply: read error from %s", mci->mci_host); + mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf); /* if debugging, pause so we can see state */ if (tTd(18, 100)) pause(); mci->mci_state = MCIS_ERROR; smtpquit(m, mci, e); #if XDEBUG { char wbuf[MAXLINE]; char *p = wbuf; int wbufleft = sizeof wbuf; if (e->e_to != NULL) { int plen; snprintf(p, wbufleft, "%s... ", shortenstring(e->e_to, 203)); plen = strlen(p); p += plen; wbufleft -= plen; } snprintf(p, wbufleft, "reply(%.100s) during %s", mci->mci_host, SmtpPhase); checkfd012(wbuf); } #endif HoldErrs = oldholderrs; return (-1); } fixcrlf(bufp, TRUE); /* EHLO failure is not a real error */ if (e->e_xfp != NULL && (bufp[0] == '4' || (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) { /* serious error -- log the previous command */ if (SmtpNeedIntro) { /* inform user who we are chatting with */ fprintf(CurEnv->e_xfp, "... while talking to %s:\n", CurHostName); SmtpNeedIntro = FALSE; } if (SmtpMsgBuffer[0] != '\0') fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); SmtpMsgBuffer[0] = '\0'; /* now log the message as from the other side */ fprintf(e->e_xfp, "<<< %s\n", bufp); } /* display the input for verbose mode */ if (Verbose) nmessage("050 %s", bufp); /* process the line */ if (pfunc != NULL) (*pfunc)(bufp, firstline, m, mci, e); firstline = FALSE; /* if continuation is required, we can go on */ if (bufp[3] == '-') continue; /* ignore improperly formated input */ if (!(isascii(bufp[0]) && isdigit(bufp[0]))) continue; /* decode the reply code */ r = atoi(bufp); /* extra semantics: 0xx codes are "informational" */ if (r >= 100) break; } /* ** Now look at SmtpReplyBuffer -- only care about the first ** line of the response from here on out. */ /* save temporary failure messages for posterity */ if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer); /* reply code 421 is "Service Shutting Down" */ if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) { /* send the quit protocol */ mci->mci_state = MCIS_SSD; smtpquit(m, mci, e); } return (r); } /* ** SMTPMESSAGE -- send message to server ** ** Parameters: ** f -- format ** m -- the mailer to control formatting. ** a, b, c -- parameters ** ** Returns: ** none. ** ** Side Effects: ** writes message to mci->mci_out. */ /*VARARGS1*/ void #ifdef __STDC__ smtpmessage(char *f, MAILER *m, MCI *mci, ...) #else smtpmessage(f, m, mci, va_alist) char *f; MAILER *m; MCI *mci; va_dcl #endif { VA_LOCAL_DECL VA_START(mci); (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap); VA_END; if (tTd(18, 1) || Verbose) nmessage(">>> %s", SmtpMsgBuffer); if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); if (mci->mci_out != NULL) { fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, m == NULL ? "\r\n" : m->m_eol); } else if (tTd(18, 1)) { printf("smtpmessage: NULL mci_out\n"); } } # endif /* SMTP */ Index: head/usr.sbin/sendmail/src/util.c =================================================================== --- head/usr.sbin/sendmail/src/util.c (revision 19843) +++ head/usr.sbin/sendmail/src/util.c (revision 19844) @@ -1,2251 +1,2307 @@ /* * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)util.c 8.105 (Berkeley) 10/12/96"; +static char sccsid[] = "@(#)util.c 8.109 (Berkeley) 11/16/96"; #endif /* not lint */ # include "sendmail.h" # include /* ** STRIPQUOTES -- Strip quotes & quote bits from a string. ** ** Runs through a string and strips off unquoted quote ** characters and quote bits. This is done in place. ** ** Parameters: ** s -- the string to strip. ** ** Returns: ** none. ** ** Side Effects: ** none. ** ** Called By: ** deliver */ void stripquotes(s) char *s; { register char *p; register char *q; register char c; if (s == NULL) return; p = q = s; do { c = *p++; if (c == '\\') c = *p++; else if (c == '"') continue; *q++ = c; } while (c != '\0'); } /* ** XALLOC -- Allocate memory and bitch wildly on failure. ** ** THIS IS A CLUDGE. This should be made to give a proper ** error -- but after all, what can we do? ** ** Parameters: ** sz -- size of area to allocate. ** ** Returns: ** pointer to data region. ** ** Side Effects: ** Memory is allocated. */ char * xalloc(sz) register int sz; { register char *p; /* some systems can't handle size zero mallocs */ if (sz <= 0) sz = 1; p = malloc((unsigned) sz); if (p == NULL) { syserr("!Out of memory!!"); /* exit(EX_UNAVAILABLE); */ } return (p); } /* ** COPYPLIST -- copy list of pointers. ** ** This routine is the equivalent of newstr for lists of ** pointers. ** ** Parameters: ** list -- list of pointers to copy. ** Must be NULL terminated. ** copycont -- if TRUE, copy the contents of the vector ** (which must be a string) also. ** ** Returns: ** a copy of 'list'. ** ** Side Effects: ** none. */ char ** copyplist(list, copycont) char **list; bool copycont; { register char **vp; register char **newvp; for (vp = list; *vp != NULL; vp++) continue; vp++; newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); if (copycont) { for (vp = newvp; *vp != NULL; vp++) *vp = newstr(*vp); } return (newvp); } /* ** COPYQUEUE -- copy address queue. ** ** This routine is the equivalent of newstr for address queues ** addresses marked with QDONTSEND aren't copied ** ** Parameters: ** addr -- list of address structures to copy. ** ** Returns: ** a copy of 'addr'. ** ** Side Effects: ** none. */ ADDRESS * copyqueue(addr) ADDRESS *addr; { register ADDRESS *newaddr; ADDRESS *ret; register ADDRESS **tail = &ret; while (addr != NULL) { if (!bitset(QDONTSEND, addr->q_flags)) { newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS)); STRUCTCOPY(*addr, *newaddr); *tail = newaddr; tail = &newaddr->q_next; } addr = addr->q_next; } *tail = NULL; return ret; } /* ** PRINTAV -- print argument vector. ** ** Parameters: ** av -- argument vector. ** ** Returns: ** none. ** ** Side Effects: ** prints av. */ void printav(av) register char **av; { while (*av != NULL) { if (tTd(0, 44)) printf("\n\t%08x=", *av); else (void) putchar(' '); xputs(*av++); } (void) putchar('\n'); } /* ** LOWER -- turn letter into lower case. ** ** Parameters: ** c -- character to turn into lower case. ** ** Returns: ** c, in lower case. ** ** Side Effects: ** none. */ char lower(c) register char c; { return((isascii(c) && isupper(c)) ? tolower(c) : c); } /* ** XPUTS -- put string doing control escapes. ** ** Parameters: ** s -- string to put. ** ** Returns: ** none. ** ** Side Effects: ** output to stdout */ void xputs(s) register const char *s; { register int c; register struct metamac *mp; bool shiftout = FALSE; extern struct metamac MetaMacros[]; if (s == NULL) { printf("%s%s", TermEscape.te_rv_on, TermEscape.te_rv_off); return; } while ((c = (*s++ & 0377)) != '\0') { if (shiftout) { printf("%s", TermEscape.te_rv_off); shiftout = FALSE; } if (!isascii(c)) { if (c == MATCHREPL) { printf("%s$", TermEscape.te_rv_on); shiftout = TRUE; if (*s == '\0') continue; c = *s++ & 0377; goto printchar; } if (c == MACROEXPAND) { printf("%s$", TermEscape.te_rv_on); shiftout = TRUE; if (strchr("=~&?", *s) != NULL) putchar(*s++); if (bitset(0200, *s)) printf("{%s}", macname(*s++ & 0377)); else printf("%c", *s++); continue; } for (mp = MetaMacros; mp->metaname != '\0'; mp++) { if ((mp->metaval & 0377) == c) { printf("%s$%c", TermEscape.te_rv_on, mp->metaname); shiftout = TRUE; break; } } if (c == MATCHCLASS || c == MATCHNCLASS) { if (bitset(0200, *s)) printf("{%s}", macname(*s++ & 0377)); else printf("%c", *s++); } if (mp->metaname != '\0') continue; /* unrecognized meta character */ printf("%sM-", TermEscape.te_rv_on); shiftout = TRUE; c &= 0177; } printchar: if (isprint(c)) { putchar(c); continue; } /* wasn't a meta-macro -- find another way to print it */ switch (c) { case '\n': c = 'n'; break; case '\r': c = 'r'; break; case '\t': c = 't'; break; } if (!shiftout) { printf("%s", TermEscape.te_rv_on); shiftout = TRUE; } if (isprint(c)) { (void) putchar('\\'); (void) putchar(c); } else { (void) putchar('^'); (void) putchar(c ^ 0100); } } if (shiftout) printf("%s", TermEscape.te_rv_off); (void) fflush(stdout); } /* ** MAKELOWER -- Translate a line into lower case ** ** Parameters: ** p -- the string to translate. If NULL, return is ** immediate. ** ** Returns: ** none. ** ** Side Effects: ** String pointed to by p is translated to lower case. ** ** Called By: ** parse */ void makelower(p) register char *p; { register char c; if (p == NULL) return; for (; (c = *p) != '\0'; p++) if (isascii(c) && isupper(c)) *p = tolower(c); } /* ** BUILDFNAME -- build full name from gecos style entry. ** ** This routine interprets the strange entry that would appear ** in the GECOS field of the password file. ** ** Parameters: ** p -- name to build. ** login -- the login name of this user (for &). ** buf -- place to put the result. ** buflen -- length of buf. ** ** Returns: ** none. ** ** Side Effects: ** none. */ void buildfname(gecos, login, buf, buflen) register char *gecos; char *login; char *buf; int buflen; { register char *p; register char *bp = buf; int l; if (*gecos == '*') gecos++; /* copy gecos, interpolating & to be full name */ for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) { if (bp >= &buf[buflen - 1]) { /* buffer overflow -- just use login name */ snprintf(buf, buflen, "%s", login); return; } if (*p == '&') { /* interpolate full name */ snprintf(bp, buflen - (bp - buf), "%s", login); *bp = toupper(*bp); bp += strlen(bp); } else *bp++ = *p; } *bp = '\0'; } /* ** SAFEFILE -- return true if a file exists and is safe for a user. ** ** Parameters: ** fn -- filename to check. ** uid -- user id to compare against. ** gid -- group id to compare against. ** uname -- user name to compare against (used for group ** sets). ** flags -- modifiers: ** SFF_MUSTOWN -- "uid" must own this file. ** SFF_NOSLINK -- file cannot be a symbolic link. ** mode -- mode bits that must match. ** st -- if set, points to a stat structure that will ** get the stat info for the file. ** ** Returns: ** 0 if fn exists, is owned by uid, and matches mode. ** An errno otherwise. The actual errno is cleared. ** ** Side Effects: ** none. */ #include #ifndef S_IXOTH # define S_IXOTH (S_IEXEC >> 6) #endif #ifndef S_IXGRP # define S_IXGRP (S_IEXEC >> 3) #endif #ifndef S_IXUSR # define S_IXUSR (S_IEXEC) #endif #define ST_MODE_NOFILE 0171147 /* unlikely to occur */ int safefile(fn, uid, gid, uname, flags, mode, st) char *fn; UID_T uid; GID_T gid; char *uname; int flags; int mode; struct stat *st; { register char *p; register struct group *gr = NULL; int file_errno = 0; struct stat stbuf; struct stat fstbuf; if (tTd(44, 4)) printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n", fn, uid, gid, flags, mode); errno = 0; if (st == NULL) st = &fstbuf; /* first check to see if the file exists at all */ #ifdef HASLSTAT if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) : stat(fn, st)) < 0) #else if (stat(fn, st) < 0) #endif { file_errno = errno; } else if (bitset(SFF_SETUIDOK, flags) && !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) && S_ISREG(st->st_mode)) { /* ** If final file is setuid, run as the owner of that ** file. Gotta be careful not to reveal anything too ** soon here! */ #ifdef SUID_ROOT_FILES_OK if (bitset(S_ISUID, st->st_mode)) #else if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0) #endif { uid = st->st_uid; uname = NULL; } #ifdef SUID_ROOT_FILES_OK if (bitset(S_ISGID, st->st_mode)) #else if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) #endif gid = st->st_gid; } if (!bitset(SFF_NOPATHCHECK, flags) || (uid == 0 && !bitset(SFF_ROOTOK, flags))) { /* check the path to the file for acceptability */ for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/') { *p = '\0'; if (stat(fn, &stbuf) < 0) break; if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode)) message("051 WARNING: writable directory %s", fn); if (uid == 0 && !bitset(SFF_ROOTOK, flags)) { if (bitset(S_IXOTH, stbuf.st_mode)) continue; break; } if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode)) continue; if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode)) continue; #ifndef NO_GROUP_SET if (uname != NULL && !DontInitGroups && ((gr != NULL && gr->gr_gid == stbuf.st_gid) || (gr = getgrgid(stbuf.st_gid)) != NULL)) { register char **gp; for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) if (strcmp(*gp, uname) == 0) break; if (gp != NULL && *gp != NULL && bitset(S_IXGRP, stbuf.st_mode)) continue; } #endif if (!bitset(S_IXOTH, stbuf.st_mode)) break; } if (p != NULL) { int ret = errno; if (ret == 0) ret = EACCES; if (tTd(44, 4)) printf("\t[dir %s] %s\n", fn, errstring(ret)); *p = '/'; return ret; } } /* ** If the target file doesn't exist, check the directory to ** ensure that it is writable by this user. */ if (file_errno != 0) { int ret = file_errno; if (tTd(44, 4)) printf("\t%s\n", errstring(ret)); errno = 0; if (!bitset(SFF_CREAT, flags)) return ret; /* check to see if legal to create the file */ p = strrchr(fn, '/'); if (p == NULL) return ENOTDIR; *p = '\0'; if (stat(fn, &stbuf) >= 0) { int md = S_IWRITE|S_IEXEC; if (stbuf.st_uid != uid) md >>= 6; if ((stbuf.st_mode & md) != md) errno = EACCES; } ret = errno; if (tTd(44, 4)) printf("\t[final dir %s uid %d mode %o] %s\n", fn, stbuf.st_uid, stbuf.st_mode, errstring(ret)); *p = '/'; st->st_mode = ST_MODE_NOFILE; return ret; } #ifdef S_ISLNK if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) { if (tTd(44, 4)) printf("\t[slink mode %o]\tEPERM\n", st->st_mode); return EPERM; } #endif if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) { if (tTd(44, 4)) printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode); return EPERM; } if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) { if (tTd(44, 4)) printf("\t[exec bits %o]\tEPERM]\n", st->st_mode); return EPERM; } if (uid == 0 && !bitset(SFF_ROOTOK, flags)) mode >>= 6; else if (st->st_uid != uid) { mode >>= 3; if (st->st_gid == gid) ; #ifndef NO_GROUP_SET else if (uname != NULL && !DontInitGroups && ((gr != NULL && gr->gr_gid == st->st_gid) || (gr = getgrgid(st->st_gid)) != NULL)) { register char **gp; for (gp = gr->gr_mem; *gp != NULL; gp++) if (strcmp(*gp, uname) == 0) break; if (*gp == NULL) mode >>= 3; } #endif else mode >>= 3; } if (tTd(44, 4)) printf("\t[uid %d, stat %o, mode %o] ", st->st_uid, st->st_mode, mode); if ((st->st_uid == uid || st->st_uid == 0 || !bitset(SFF_MUSTOWN, flags)) && (st->st_mode & mode) == mode) { if (tTd(44, 4)) printf("\tOK\n"); return 0; } if (tTd(44, 4)) printf("\tEACCES\n"); return EACCES; } /* ** SAFEFOPEN -- do a file open with extra checking ** ** Parameters: ** fn -- the file name to open. ** omode -- the open-style mode flags. ** cmode -- the create-style mode flags. ** sff -- safefile flags. ** ** Returns: ** Same as fopen. */ #ifndef O_ACCMODE # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) #endif FILE * safefopen(fn, omode, cmode, sff) char *fn; int omode; int cmode; int sff; { int rval; FILE *fp; int smode; struct stat stb, sta; if (bitset(O_CREAT, omode)) sff |= SFF_CREAT; smode = 0; switch (omode & O_ACCMODE) { case O_RDONLY: smode = S_IREAD; break; case O_WRONLY: smode = S_IWRITE; break; case O_RDWR: smode = S_IREAD|S_IWRITE; break; default: smode = 0; break; } if (bitset(SFF_OPENASROOT, sff)) rval = safefile(fn, 0, 0, NULL, sff, smode, &stb); else rval = safefile(fn, RealUid, RealGid, RealUserName, sff, smode, &stb); if (rval != 0) { errno = rval; return NULL; } if (stb.st_mode == ST_MODE_NOFILE) omode |= O_EXCL; fp = dfopen(fn, omode, cmode); if (fp == NULL) return NULL; if (bitset(O_EXCL, omode)) return fp; if (fstat(fileno(fp), &sta) < 0 || sta.st_nlink != stb.st_nlink || sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino || sta.st_uid != stb.st_uid || sta.st_gid != stb.st_gid) { syserr("554 cannot open: file %s changed after open", fn); fclose(fp); errno = EPERM; return NULL; } return fp; } /* ** FIXCRLF -- fix in line. ** ** Looks for the combination and turns it into the ** UNIX canonical character. It only takes one line, ** i.e., it is assumed that the first found is the end ** of the line. ** ** Parameters: ** line -- the line to fix. ** stripnl -- if true, strip the newline also. ** ** Returns: ** none. ** ** Side Effects: ** line is changed in place. */ void fixcrlf(line, stripnl) char *line; bool stripnl; { register char *p; p = strchr(line, '\n'); if (p == NULL) return; if (p > line && p[-1] == '\r') p--; if (!stripnl) *p++ = '\n'; *p = '\0'; } /* ** DFOPEN -- determined file open ** ** This routine has the semantics of fopen, except that it will ** keep trying a few times to make this happen. The idea is that ** on very loaded systems, we may run out of resources (inodes, ** whatever), so this tries to get around it. */ struct omodes { int mask; int mode; char *farg; } OpenModes[] = { { O_ACCMODE, O_RDONLY, "r" }, { O_ACCMODE|O_APPEND, O_WRONLY, "w" }, { O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a" }, { O_TRUNC, 0, "w+" }, { O_APPEND, O_APPEND, "a+" }, { 0, 0, "r+" }, }; FILE * dfopen(filename, omode, cmode) char *filename; int omode; int cmode; { register int tries; int fd; register struct omodes *om; struct stat st; for (om = OpenModes; om->mask != 0; om++) if ((omode & om->mask) == om->mode) break; for (tries = 0; tries < 10; tries++) { sleep((unsigned) (10 * tries)); errno = 0; fd = open(filename, omode, cmode); if (fd >= 0) break; switch (errno) { case ENFILE: /* system file table full */ case EINTR: /* interrupted syscall */ #ifdef ETXTBSY case ETXTBSY: /* Apollo: net file locked */ #endif continue; } break; } if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) { int locktype; /* lock the file to avoid accidental conflicts */ if ((omode & O_ACCMODE) != O_RDONLY) locktype = LOCK_EX; else locktype = LOCK_SH; (void) lockfile(fd, filename, NULL, locktype); errno = 0; } if (fd < 0) return NULL; else return fdopen(fd, om->farg); } /* ** PUTLINE -- put a line like fputs obeying SMTP conventions ** ** This routine always guarantees outputing a newline (or CRLF, ** as appropriate) at the end of the string. ** ** Parameters: ** l -- line to put. ** mci -- the mailer connection information. ** ** Returns: ** none ** ** Side Effects: ** output of l to fp. */ void putline(l, mci) register char *l; register MCI *mci; { putxline(l, mci, PXLF_MAPFROM); } /* ** PUTXLINE -- putline with flags bits. ** ** This routine always guarantees outputing a newline (or CRLF, ** as appropriate) at the end of the string. ** ** Parameters: ** l -- line to put. ** mci -- the mailer connection information. ** pxflags -- flag bits: ** PXLF_MAPFROM -- map From_ to >From_. ** PXLF_STRIP8BIT -- strip 8th bit. ** ** Returns: ** none ** ** Side Effects: ** output of l to fp. */ void putxline(l, mci, pxflags) register char *l; register MCI *mci; int pxflags; { register char *p; register char svchar; int slop = 0; /* strip out 0200 bits -- these can look like TELNET protocol */ if (bitset(MCIF_7BIT, mci->mci_flags) || bitset(PXLF_STRIP8BIT, pxflags)) { for (p = l; (svchar = *p) != '\0'; ++p) if (bitset(0200, svchar)) *p = svchar &~ 0200; } do { /* find the end of the line */ p = strchr(l, '\n'); if (p == NULL) p = &l[strlen(l)]; if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d >>> ", getpid()); /* check for line overflow */ while (mci->mci_mailer->m_linelimit > 0 && (p - l + slop) > mci->mci_mailer->m_linelimit) { register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; svchar = *q; *q = '\0'; if (l[0] == '.' && slop == 0 && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { (void) putc('.', mci->mci_out); if (TrafficLogFile != NULL) (void) putc('.', TrafficLogFile); } else if (l[0] == 'F' && slop == 0 && bitset(PXLF_MAPFROM, pxflags) && strncmp(l, "From ", 5) == 0 && bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) { (void) putc('>', mci->mci_out); if (TrafficLogFile != NULL) (void) putc('>', TrafficLogFile); } fputs(l, mci->mci_out); (void) putc('!', mci->mci_out); fputs(mci->mci_mailer->m_eol, mci->mci_out); (void) putc(' ', mci->mci_out); if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%s!\n%05d >>> ", l, getpid()); *q = svchar; l = q; slop = 1; } /* output last part */ if (l[0] == '.' && slop == 0 && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { (void) putc('.', mci->mci_out); if (TrafficLogFile != NULL) (void) putc('.', TrafficLogFile); } + else if (l[0] == 'F' && slop == 0 && + bitset(PXLF_MAPFROM, pxflags) && + strncmp(l, "From ", 5) == 0 && + bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) + { + (void) putc('>', mci->mci_out); + if (TrafficLogFile != NULL) + (void) putc('>', TrafficLogFile); + } if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%.*s\n", p - l, l); for ( ; l < p; ++l) (void) putc(*l, mci->mci_out); fputs(mci->mci_mailer->m_eol, mci->mci_out); if (*l == '\n') { if (*++l != ' ' && *l != '\t' && *l != '\0') { (void) putc(' ', mci->mci_out); if (TrafficLogFile != NULL) (void) putc(' ', TrafficLogFile); } } } while (l[0] != '\0'); } /* ** XUNLINK -- unlink a file, doing logging as appropriate. ** ** Parameters: ** f -- name of file to unlink. ** ** Returns: ** none. ** ** Side Effects: ** f is unlinked. */ void xunlink(f) char *f; { register int i; # ifdef LOG if (LogLevel > 98) syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f); # endif /* LOG */ i = unlink(f); # ifdef LOG if (i < 0 && LogLevel > 97) syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); # endif /* LOG */ } /* ** XFCLOSE -- close a file, doing logging as appropriate. ** ** Parameters: ** fp -- file pointer for the file to close ** a, b -- miscellaneous crud to print for debugging ** ** Returns: ** none. ** ** Side Effects: ** fp is closed. */ void xfclose(fp, a, b) FILE *fp; char *a, *b; { if (tTd(53, 99)) printf("xfclose(%x) %s %s\n", fp, a, b); #if XDEBUG if (fileno(fp) == 1) syserr("xfclose(%s %s): fd = 1", a, b); #endif if (fclose(fp) < 0 && tTd(53, 99)) printf("xfclose FAILURE: %s\n", errstring(errno)); } /* ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. ** ** Parameters: ** buf -- place to put the input line. ** siz -- size of buf. ** fp -- file to read from. ** timeout -- the timeout before error occurs. ** during -- what we are trying to read (for error messages). ** ** Returns: ** NULL on error (including timeout). This will also leave ** buf containing a null string. ** buf otherwise. ** ** Side Effects: ** none. */ static jmp_buf CtxReadTimeout; static void readtimeout(); char * sfgets(buf, siz, fp, timeout, during) char *buf; int siz; FILE *fp; time_t timeout; char *during; { register EVENT *ev = NULL; register char *p; if (fp == NULL) { buf[0] = '\0'; return NULL; } /* set the timeout */ if (timeout != 0) { if (setjmp(CtxReadTimeout) != 0) { # ifdef LOG if (LogLevel > 1) syslog(LOG_NOTICE, "timeout waiting for input from %.100s during %s", CurHostName ? CurHostName : "local", during); # endif errno = 0; usrerr("451 timeout waiting for input during %s", during); buf[0] = '\0'; #if XDEBUG checkfd012(during); #endif return (NULL); } ev = setevent(timeout, readtimeout, 0); } /* try to read */ p = NULL; while (!feof(fp) && !ferror(fp)) { errno = 0; p = fgets(buf, siz, fp); if (p != NULL || errno != EINTR) break; clearerr(fp); } /* clear the event if it has not sprung */ clrevent(ev); /* clean up the books and exit */ LineNumber++; if (p == NULL) { buf[0] = '\0'; if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid()); return (NULL); } if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf); if (SevenBitInput) { for (p = buf; *p != '\0'; p++) *p &= ~0200; } else if (!HasEightBits) { for (p = buf; *p != '\0'; p++) { if (bitset(0200, *p)) { HasEightBits = TRUE; break; } } } return (buf); } static void readtimeout(timeout) time_t timeout; { longjmp(CtxReadTimeout, 1); } /* ** FGETFOLDED -- like fgets, but know about folded lines. ** ** Parameters: ** buf -- place to put result. ** n -- bytes available. ** f -- file to read from. ** ** Returns: ** input line(s) on success, NULL on error or EOF. ** This will normally be buf -- unless the line is too ** long, when it will be xalloc()ed. ** ** Side Effects: ** buf gets lines from f, with continuation lines (lines ** with leading white space) appended. CRLF's are mapped ** into single newlines. Any trailing NL is stripped. */ char * fgetfolded(buf, n, f) char *buf; register int n; FILE *f; { register char *p = buf; char *bp = buf; register int i; n--; while ((i = getc(f)) != EOF) { if (i == '\r') { i = getc(f); if (i != '\n') { if (i != EOF) (void) ungetc(i, f); i = '\r'; } } if (--n <= 0) { /* allocate new space */ char *nbp; int nn; nn = (p - bp); if (nn < MEMCHUNKSIZE) nn *= 2; else nn += MEMCHUNKSIZE; nbp = xalloc(nn); bcopy(bp, nbp, p - bp); p = &nbp[p - bp]; if (bp != buf) free(bp); bp = nbp; n = nn - (p - bp); } *p++ = i; if (i == '\n') { LineNumber++; i = getc(f); if (i != EOF) (void) ungetc(i, f); if (i != ' ' && i != '\t') break; } } if (p == bp) return (NULL); if (p[-1] == '\n') p--; *p = '\0'; return (bp); } /* ** CURTIME -- return current time. ** ** Parameters: ** none. ** ** Returns: ** the current time. ** ** Side Effects: ** none. */ time_t curtime() { auto time_t t; (void) time(&t); return (t); } /* ** ATOBOOL -- convert a string representation to boolean. ** ** Defaults to "TRUE" ** ** Parameters: ** s -- string to convert. Takes "tTyY" as true, ** others as false. ** ** Returns: ** A boolean representation of the string. ** ** Side Effects: ** none. */ bool atobool(s) register char *s; { if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) return (TRUE); return (FALSE); } /* ** ATOOCT -- convert a string representation to octal. ** ** Parameters: ** s -- string to convert. ** ** Returns: ** An integer representing the string interpreted as an ** octal number. ** ** Side Effects: ** none. */ int atooct(s) register char *s; { register int i = 0; while (*s >= '0' && *s <= '7') i = (i << 3) | (*s++ - '0'); return (i); } /* ** WAITFOR -- wait for a particular process id. ** ** Parameters: ** pid -- process id to wait for. ** ** Returns: ** status of pid. ** -1 if pid never shows up. ** ** Side Effects: ** none. */ int waitfor(pid) pid_t pid; { #ifdef WAITUNION union wait st; #else auto int st; #endif pid_t i; do { errno = 0; i = wait(&st); if (i > 0) proc_list_drop(i); } while ((i >= 0 || errno == EINTR) && i != pid); if (i < 0) return -1; #ifdef WAITUNION return st.w_status; #else return st; #endif } /* ** BITINTERSECT -- tell if two bitmaps intersect ** ** Parameters: ** a, b -- the bitmaps in question ** ** Returns: ** TRUE if they have a non-null intersection ** FALSE otherwise ** ** Side Effects: ** none. */ bool bitintersect(a, b) BITMAP a; BITMAP b; { int i; for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) if ((a[i] & b[i]) != 0) return (TRUE); return (FALSE); } /* ** BITZEROP -- tell if a bitmap is all zero ** ** Parameters: ** map -- the bit map to check ** ** Returns: ** TRUE if map is all zero. ** FALSE if there are any bits set in map. ** ** Side Effects: ** none. */ bool bitzerop(map) BITMAP map; { int i; for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) if (map[i] != 0) return (FALSE); return (TRUE); } /* ** STRCONTAINEDIN -- tell if one string is contained in another ** ** Parameters: ** a -- possible substring. ** b -- possible superstring. ** ** Returns: ** TRUE if a is contained in b. ** FALSE otherwise. */ bool strcontainedin(a, b) register char *a; register char *b; { int la; int lb; int c; la = strlen(a); lb = strlen(b); c = *a; if (isascii(c) && isupper(c)) c = tolower(c); for (; lb-- >= la; b++) { if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) continue; if (strncasecmp(a, b, la) == 0) return TRUE; } return FALSE; } /* ** CHECKFD012 -- check low numbered file descriptors ** ** File descriptors 0, 1, and 2 should be open at all times. ** This routine verifies that, and fixes it if not true. ** ** Parameters: ** where -- a tag printed if the assertion failed ** ** Returns: ** none */ void checkfd012(where) char *where; { #if XDEBUG register int i; struct stat stbuf; for (i = 0; i < 3; i++) { if (fstat(i, &stbuf) < 0 && errno == EBADF) { /* oops.... */ int fd; syserr("%s: fd %d not open", where, i); fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666); if (fd != i) { (void) dup2(fd, i); (void) close(fd); } } } #endif /* XDEBUG */ } /* ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging ** ** Parameters: ** fd -- file descriptor to check. ** where -- tag to print on failure. ** ** Returns: ** none. */ void checkfdopen(fd, where) int fd; char *where; { #if XDEBUG struct stat st; if (fstat(fd, &st) < 0 && errno == EBADF) { syserr("checkfdopen(%d): %s not open as expected!", fd, where); printopenfds(TRUE); } #endif } /* ** CHECKFDS -- check for new or missing file descriptors ** ** Parameters: ** where -- tag for printing. If null, take a base line. ** ** Returns: ** none ** ** Side Effects: ** If where is set, shows changes since the last call. */ void checkfds(where) char *where; { int maxfd; register int fd; bool printhdr = TRUE; int save_errno = errno; static BITMAP baseline; extern int DtableSize; if (DtableSize > 256) maxfd = 256; else maxfd = DtableSize; if (where == NULL) clrbitmap(baseline); for (fd = 0; fd < maxfd; fd++) { struct stat stbuf; if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) { if (!bitnset(fd, baseline)) continue; clrbitn(fd, baseline); } else if (!bitnset(fd, baseline)) setbitn(fd, baseline); else continue; /* file state has changed */ if (where == NULL) continue; if (printhdr) { syslog(LOG_DEBUG, "%s: changed fds:", where); printhdr = FALSE; } dumpfd(fd, TRUE, TRUE); } errno = save_errno; } /* ** PRINTOPENFDS -- print the open file descriptors (for debugging) ** ** Parameters: ** logit -- if set, send output to syslog; otherwise ** print for debugging. ** ** Returns: ** none. */ #include void printopenfds(logit) bool logit; { register int fd; extern int DtableSize; for (fd = 0; fd < DtableSize; fd++) dumpfd(fd, FALSE, logit); } /* ** DUMPFD -- dump a file descriptor ** ** Parameters: ** fd -- the file descriptor to dump. ** printclosed -- if set, print a notification even if ** it is closed; otherwise print nothing. ** logit -- if set, send output to syslog instead of stdout. */ void dumpfd(fd, printclosed, logit) int fd; bool printclosed; bool logit; { register char *p; char *hp; char *fmtstr; #ifdef S_IFSOCK SOCKADDR sa; #endif auto int slen; struct stat st; char buf[200]; extern char *hostnamebyanyaddr(); p = buf; snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); p += strlen(p); if (fstat(fd, &st) < 0) { if (errno != EBADF) { snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)", errstring(errno)); goto printit; } else if (printclosed) { snprintf(p, SPACELEFT(buf, p), "CLOSED"); goto printit; } return; } slen = fcntl(fd, F_GETFL, NULL); if (slen != -1) { snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", slen); p += strlen(p); } snprintf(p, SPACELEFT(buf, p), "mode=%o: ", st.st_mode); p += strlen(p); switch (st.st_mode & S_IFMT) { #ifdef S_IFSOCK case S_IFSOCK: snprintf(p, SPACELEFT(buf, p), "SOCK "); p += strlen(p); slen = sizeof sa; if (getsockname(fd, &sa.sa, &slen) < 0) snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); else { hp = hostnamebyanyaddr(&sa); if (sa.sa.sa_family == AF_INET) snprintf(p, SPACELEFT(buf, p), "%s/%d", hp, ntohs(sa.sin.sin_port)); else snprintf(p, SPACELEFT(buf, p), "%s", hp); } p += strlen(p); snprintf(p, SPACELEFT(buf, p), "->"); p += strlen(p); slen = sizeof sa; if (getpeername(fd, &sa.sa, &slen) < 0) snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); else { hp = hostnamebyanyaddr(&sa); if (sa.sa.sa_family == AF_INET) snprintf(p, SPACELEFT(buf, p), "%s/%d", hp, ntohs(sa.sin.sin_port)); else snprintf(p, SPACELEFT(buf, p), "%s", hp); } break; #endif case S_IFCHR: snprintf(p, SPACELEFT(buf, p), "CHR: "); p += strlen(p); goto defprint; case S_IFBLK: snprintf(p, SPACELEFT(buf, p), "BLK: "); p += strlen(p); goto defprint; #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) case S_IFIFO: snprintf(p, SPACELEFT(buf, p), "FIFO: "); p += strlen(p); goto defprint; #endif #ifdef S_IFDIR case S_IFDIR: snprintf(p, SPACELEFT(buf, p), "DIR: "); p += strlen(p); goto defprint; #endif #ifdef S_IFLNK case S_IFLNK: snprintf(p, SPACELEFT(buf, p), "LNK: "); p += strlen(p); goto defprint; #endif default: defprint: if (sizeof st.st_size > sizeof (long)) fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd"; else fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld"; snprintf(p, SPACELEFT(buf, p), fmtstr, major(st.st_dev), minor(st.st_dev), st.st_ino, st.st_nlink, st.st_uid, st.st_gid, st.st_size); break; } printit: #ifdef LOG if (logit) syslog(LOG_DEBUG, "%.800s", buf); else #endif printf("%s\n", buf); } /* ** SHORTENSTRING -- return short version of a string ** ** If the string is already short, just return it. If it is too ** long, return the head and tail of the string. ** ** Parameters: ** s -- the string to shorten. ** m -- the max length of the string. ** ** Returns: ** Either s or a short version of s. */ #ifndef MAXSHORTSTR # define MAXSHORTSTR 203 #endif char * shortenstring(s, m) register const char *s; int m; { int l; static char buf[MAXSHORTSTR + 1]; l = strlen(s); if (l < m) return (char *) s; if (m > MAXSHORTSTR) m = MAXSHORTSTR; else if (m < 10) { if (m < 5) { strncpy(buf, s, m); buf[m] = '\0'; return buf; } strncpy(buf, s, m - 3); strcpy(buf + m - 3, "..."); return buf; } m = (m - 3) / 2; strncpy(buf, s, m); strcpy(buf + m, "..."); strcpy(buf + m + 3, s + l - m); return buf; } /* ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. ** ** Parameters: ** host -- the host to shorten (stripped in place). ** ** Returns: ** none. */ void shorten_hostname(host) char host[]; { register char *p; char *mydom; int i; bool canon = FALSE; /* strip off final dot */ p = &host[strlen(host) - 1]; if (*p == '.') { *p = '\0'; canon = TRUE; } /* see if there is any domain at all -- if not, we are done */ p = strchr(host, '.'); if (p == NULL) return; /* yes, we have a domain -- see if it looks like us */ mydom = macvalue('m', CurEnv); if (mydom == NULL) mydom = ""; i = strlen(++p); if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 && (mydom[i] == '.' || mydom[i] == '\0')) *--p = '\0'; } /* ** PROG_OPEN -- open a program for reading ** ** Parameters: ** argv -- the argument list. ** pfd -- pointer to a place to store the file descriptor. ** e -- the current envelope. ** ** Returns: ** pid of the process -- -1 if it failed. */ int prog_open(argv, pfd, e) char **argv; int *pfd; ENVELOPE *e; { int pid; int i; int saveerrno; int fdv[2]; char *p, *q; char buf[MAXLINE + 1]; extern int DtableSize; if (pipe(fdv) < 0) { syserr("%s: cannot create pipe for stdout", argv[0]); return -1; } pid = fork(); if (pid < 0) { syserr("%s: cannot fork", argv[0]); close(fdv[0]); close(fdv[1]); return -1; } if (pid > 0) { /* parent */ close(fdv[1]); *pfd = fdv[0]; return pid; } /* child -- close stdin */ close(0); /* stdout goes back to parent */ close(fdv[0]); if (dup2(fdv[1], 1) < 0) { syserr("%s: cannot dup2 for stdout", argv[0]); _exit(EX_OSERR); } close(fdv[1]); /* stderr goes to transcript if available */ if (e->e_xfp != NULL) { if (dup2(fileno(e->e_xfp), 2) < 0) { syserr("%s: cannot dup2 for stderr", argv[0]); _exit(EX_OSERR); } } /* this process has no right to the queue file */ if (e->e_lockfp != NULL) close(fileno(e->e_lockfp)); /* run as default user */ endpwent(); if (setgid(DefGid) < 0) syserr("prog_open: setgid(%ld) failed", (long) DefGid); if (setuid(DefUid) < 0) syserr("prog_open: setuid(%ld) failed", (long) DefUid); /* run in some directory */ if (ProgMailer != NULL) p = ProgMailer->m_execdir; else p = NULL; for (; p != NULL; p = q) { q = strchr(p, ':'); if (q != NULL) *q = '\0'; expand(p, buf, sizeof buf, e); if (q != NULL) *q++ = ':'; if (buf[0] != '\0' && chdir(buf) >= 0) break; } if (p == NULL) { /* backup directories */ if (chdir("/tmp") < 0) (void) chdir("/"); } /* arrange for all the files to be closed */ for (i = 3; i < DtableSize; i++) { register int j; if ((j = fcntl(i, F_GETFD, 0)) != -1) (void) fcntl(i, F_SETFD, j | 1); } /* now exec the process */ execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); /* woops! failed */ saveerrno = errno; syserr("%s: cannot exec", argv[0]); if (transienterror(saveerrno)) _exit(EX_OSERR); _exit(EX_CONFIG); return -1; /* avoid compiler warning on IRIX */ } /* ** GET_COLUMN -- look up a Column in a line buffer ** ** Parameters: ** line -- the raw text line to search. ** col -- the column number to fetch. ** delim -- the delimiter between columns. If null, ** use white space. ** buf -- the output buffer. ** buflen -- the length of buf. ** ** Returns: ** buf if successful. ** NULL otherwise. */ char * get_column(line, col, delim, buf, buflen) char line[]; int col; char delim; char buf[]; int buflen; { char *p; char *begin, *end; int i; char delimbuf[4]; if (delim == '\0') strcpy(delimbuf, "\n\t "); else { delimbuf[0] = delim; delimbuf[1] = '\0'; } p = line; if (*p == '\0') return NULL; /* line empty */ if (*p == delim && col == 0) return NULL; /* first column empty */ begin = line; if (col == 0 && delim == '\0') { while (*begin != '\0' && isascii(*begin) && isspace(*begin)) begin++; } for (i = 0; i < col; i++) { if ((begin = strpbrk(begin, delimbuf)) == NULL) return NULL; /* no such column */ begin++; if (delim == '\0') { while (*begin != '\0' && isascii(*begin) && isspace(*begin)) begin++; } } end = strpbrk(begin, delimbuf); if (end == NULL) i = strlen(begin); else i = end - begin; if (i >= buflen) i = buflen - 1; strncpy(buf, begin, i); buf[i] = '\0'; return buf; } /* ** CLEANSTRCPY -- copy string keeping out bogus characters ** ** Parameters: ** t -- "to" string. ** f -- "from" string. ** l -- length of space available in "to" string. ** ** Returns: ** none. */ void cleanstrcpy(t, f, l) register char *t; register char *f; int l; { #ifdef LOG /* check for newlines and log if necessary */ (void) denlstring(f, TRUE, TRUE); #endif l--; while (l > 0 && *f != '\0') { if (isascii(*f) && (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) { l--; *t++ = *f; } f++; } *t = '\0'; } /* ** DENLSTRING -- convert newlines in a string to spaces ** ** Parameters: ** s -- the input string ** strict -- if set, don't permit continuation lines. ** logattacks -- if set, log attempted attacks. ** ** Returns: ** A pointer to a version of the string with newlines ** mapped to spaces. This should be copied. */ char * denlstring(s, strict, logattacks) char *s; bool strict; bool logattacks; { register char *p; int l; static char *bp = NULL; static int bl = 0; p = s; while ((p = strchr(p, '\n')) != NULL) if (strict || (*++p != ' ' && *p != '\t')) break; if (p == NULL) return s; l = strlen(s) + 1; if (bl < l) { /* allocate more space */ if (bp != NULL) free(bp); bp = xalloc(l); bl = l; } strcpy(bp, s); for (p = bp; (p = strchr(p, '\n')) != NULL; ) *p++ = ' '; #ifdef LOG if (logattacks) { syslog(LOG_NOTICE, "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", RealHostName == NULL ? "[UNKNOWN]" : RealHostName, shortenstring(bp, 203)); } #endif return bp; } /* ** PATH_IS_DIR -- check to see if file exists and is a directory. ** ** Parameters: ** pathname -- pathname to check for directory-ness. ** createflag -- if set, create directory if needed. ** ** Returns: ** TRUE -- if the indicated pathname is a directory ** FALSE -- otherwise */ int path_is_dir(pathname, createflag) char *pathname; bool createflag; { struct stat statbuf; if (stat(pathname, &statbuf) < 0) { if (errno != ENOENT || !createflag) return FALSE; if (mkdir(pathname, 0755) < 0) return FALSE; return TRUE; } if (!S_ISDIR(statbuf.st_mode)) { errno = ENOTDIR; return FALSE; } return TRUE; } /* ** PROC_LIST_ADD -- add process id to list of our children ** ** Parameters: ** pid -- pid to add to list. ** ** Returns: ** none */ static pid_t *ProcListVec = NULL; static int ProcListSize = 0; #define NO_PID ((pid_t) 0) #ifndef PROC_LIST_SEG # define PROC_LIST_SEG 32 /* number of pids to alloc at a time */ #endif void proc_list_add(pid) pid_t pid; { int i; + extern void proc_list_probe __P((void)); for (i = 0; i < ProcListSize; i++) { if (ProcListVec[i] == NO_PID) break; } if (i >= ProcListSize) { + /* probe the existing vector to avoid growing infinitely */ + proc_list_probe(); + + /* now scan again */ + for (i = 0; i < ProcListSize; i++) + { + if (ProcListVec[i] == NO_PID) + break; + } + } + if (i >= ProcListSize) + { /* grow process list */ pid_t *npv; npv = (pid_t *) xalloc(sizeof (pid_t) * (ProcListSize + PROC_LIST_SEG)); if (ProcListSize > 0) { bcopy(ProcListVec, npv, ProcListSize * sizeof (pid_t)); free(ProcListVec); } for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) npv[i] = NO_PID; i = ProcListSize; ProcListSize += PROC_LIST_SEG; ProcListVec = npv; } ProcListVec[i] = pid; CurChildren++; } /* ** PROC_LIST_DROP -- drop pid from process list ** ** Parameters: ** pid -- pid to drop ** ** Returns: ** none. */ void proc_list_drop(pid) pid_t pid; { int i; for (i = 0; i < ProcListSize; i++) { if (ProcListVec[i] == pid) { ProcListVec[i] = NO_PID; - CurChildren--; break; } } + if (CurChildren > 0) + CurChildren--; +} + /* +** PROC_LIST_PROBE -- probe processes in the list to see if they still exist +** +** Parameters: +** none +** +** Returns: +** none +*/ + +void +proc_list_probe() +{ + int i; + + for (i = 0; i < ProcListSize; i++) + { + if (ProcListVec[i] == NO_PID) + continue; + if (kill(ProcListVec[i], 0) < 0) + { +#ifdef LOG + if (LogLevel > 3) + syslog(LOG_DEBUG, "proc_list_probe: lost pid %d", + ProcListVec[i]); +#endif + ProcListVec[i] = NO_PID; + CurChildren--; + } + } + if (CurChildren < 0) + CurChildren = 0; }