summaryrefslogtreecommitdiff
path: root/contrib/sendmail/src
diff options
context:
space:
mode:
authorcvs2svn <cvs2svn@FreeBSD.org>2000-08-13 19:20:03 +0000
committercvs2svn <cvs2svn@FreeBSD.org>2000-08-13 19:20:03 +0000
commit5f4053c8ecd89d0ca5fd58b7a1ef7ce6ccf0e0b2 (patch)
treec78c6bf436e65ec80276e773565bd712940a05c0 /contrib/sendmail/src
parentdfd9ce69af41851def892f5a6b931c99f3e99c31 (diff)
Notes
Diffstat (limited to 'contrib/sendmail/src')
-rw-r--r--contrib/sendmail/src/Makefile17
-rw-r--r--contrib/sendmail/src/bf.h26
-rw-r--r--contrib/sendmail/src/bf_portable.c482
-rw-r--r--contrib/sendmail/src/bf_portable.h46
-rw-r--r--contrib/sendmail/src/bf_torek.c784
-rw-r--r--contrib/sendmail/src/bf_torek.h42
-rw-r--r--contrib/sendmail/src/helpfile136
-rw-r--r--contrib/sendmail/src/milter.c3402
-rw-r--r--contrib/sendmail/src/sfsasl.c367
-rw-r--r--contrib/sendmail/src/sfsasl.h60
-rw-r--r--contrib/sendmail/src/shmticklib.c84
-rw-r--r--contrib/sendmail/src/statusd_shm.h43
-rw-r--r--contrib/sendmail/src/timers.c229
-rw-r--r--contrib/sendmail/src/timers.h33
14 files changed, 5751 insertions, 0 deletions
diff --git a/contrib/sendmail/src/Makefile b/contrib/sendmail/src/Makefile
new file mode 100644
index 0000000000000..c86bbf5e22041
--- /dev/null
+++ b/contrib/sendmail/src/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 8.11 1999/09/23 22:36:42 ca Exp $
+
+SHELL= /bin/sh
+BUILD= ./Build
+OPTIONS= $(CONFIG) $(FLAGS)
+
+all: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+clean: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+install: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+
+fresh: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) -c
+
+FRC:
diff --git a/contrib/sendmail/src/bf.h b/contrib/sendmail/src/bf.h
new file mode 100644
index 0000000000000..d7d6303fb4125
--- /dev/null
+++ b/contrib/sendmail/src/bf.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * $Id: bf.h,v 8.5 1999/11/04 19:31:25 ca Exp $
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef BF_H
+#define BF_H 1
+
+extern FILE *bfopen __P((char *, int, size_t, long));
+extern FILE *bfdup __P((FILE *));
+extern int bfcommit __P((FILE *));
+extern int bfrewind __P((FILE *));
+extern int bftruncate __P((FILE *));
+extern int bfclose __P((FILE *));
+extern bool bftest __P((FILE *));
+
+#endif /* BF_H */
diff --git a/contrib/sendmail/src/bf_portable.c b/contrib/sendmail/src/bf_portable.c
new file mode 100644
index 0000000000000..4de0af3cd3f36
--- /dev/null
+++ b/contrib/sendmail/src/bf_portable.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: bf_portable.c,v 8.25.4.3 2000/06/29 21:21:58 gshapiro Exp $";
+#endif /* ! lint */
+
+#if SFIO
+# include <sfio/stdio.h>
+#endif /* SFIO */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <errno.h>
+#if !SFIO
+# include <stdio.h>
+#endif /* !SFIO */
+#ifndef BF_STANDALONE
+# include "sendmail.h"
+#endif /* ! BF_STANDALONE */
+#include "bf_portable.h"
+#include "bf.h"
+
+ /*
+** BFOPEN -- create a new buffered file
+**
+** Parameters:
+** filename -- the file's name
+** fmode -- what mode the file should be created as
+** bsize -- amount of buffer space to allocate (may be 0)
+** flags -- if running under sendmail, passed directly to safeopen
+**
+** Returns:
+** a FILE * which may then be used with stdio functions, or NULL
+** on failure. FILE * is opened for writing (mode "w+").
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** ENOMEM -- out of memory
+** ENOENT -- illegal empty filename specified
+** any value of errno specified by open()
+** any value of errno specified by fdopen()
+** any value of errno specified by funopen()
+*/
+
+#ifdef BF_STANDALONE
+# define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode)
+#else /* BF_STANDALONE */
+# define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
+#endif /* BF_STANDALONE */
+
+/* List of currently-open buffered files */
+struct bf *bflist = NULL;
+
+FILE *
+bfopen(filename, fmode, bsize, flags)
+ char *filename;
+ int fmode;
+ size_t bsize;
+ long flags;
+{
+ struct bf *bfp;
+ FILE *retval;
+ int fd, l;
+
+ fd = OPEN(filename, O_RDWR | O_CREAT | O_TRUNC, fmode, flags);
+ if (fd == -1)
+ {
+ /* errno is set implicitly by open */
+ return NULL;
+ }
+
+ retval = fdopen(fd, "w+");
+
+ /* If failure, return immediately */
+ if (retval == NULL)
+ {
+ /* errno is set implicitly by fdopen */
+ return NULL;
+ }
+
+ /* Allocate memory */
+ bfp = (struct bf *)malloc(sizeof(struct bf));
+ if (bfp == NULL)
+ {
+ (void) fclose(retval);
+
+ /* don't care about errors */
+ (void) unlink(filename);
+ errno = ENOMEM;
+ return NULL;
+ }
+ if (tTd(58, 8))
+ dprintf("bfopen(%s): malloced %ld\n",
+ filename, (long) sizeof(struct bf));
+
+ l = strlen(filename) + 1;
+ bfp->bf_filename = (char *)malloc(l);
+ if (bfp->bf_filename == NULL)
+ {
+ free(bfp);
+ (void) fclose(retval);
+
+ /* don't care about errors */
+ (void) unlink(filename);
+ errno = ENOMEM;
+ return NULL;
+ }
+ (void) strlcpy(bfp->bf_filename, filename, l);
+
+ /* Fill in the other fields, then add it to the list */
+ bfp->bf_key = retval;
+ bfp->bf_committed = FALSE;
+ bfp->bf_refcount = 1;
+
+ bfinsert(bfp);
+
+ /* Whew. Nothing bad happened. We're okay. */
+ return retval;
+}
+ /*
+** BFDUP -- increase refcount on buffered file
+**
+** Parameters:
+** fp -- FILE * to "duplicate"
+**
+** Returns:
+** fp with increased refcount
+*/
+
+FILE *
+bfdup(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+
+ /* Get associated bf structure */
+ bfp = bflookup(fp);
+
+ if (bfp == NULL)
+ return NULL;
+
+ /* Increase the refcount */
+ bfp->bf_refcount++;
+
+ return fp;
+}
+
+ /*
+** BFCOMMIT -- "commits" the buffered file
+**
+** Parameters:
+** fp -- FILE * to commit to disk
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** Forces the given FILE * to be written to disk if it is not
+** already, and ensures that it will be kept after closing. If
+** fp is not a buffered file, this is a no-op.
+**
+** Sets errno:
+** any value of errno specified by open()
+** any value of errno specified by write()
+** any value of errno specified by lseek()
+*/
+
+int
+bfcommit(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+
+ /* Get associated bf structure */
+ bfp = bflookup(fp);
+
+ /* If called on a normal FILE *, noop */
+ if (bfp != NULL)
+ bfp->bf_committed = TRUE;
+
+ return 0;
+}
+
+ /*
+** BFREWIND -- rewinds the FILE *
+**
+** Parameters:
+** fp -- FILE * to rewind
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the FILE * and puts it into read mode. Normally one
+** would bfopen() a file, write to it, then bfrewind() and
+** fread(). If fp is not a buffered file, this is equivalent to
+** rewind().
+**
+** Sets errno:
+** any value of errno specified by fseek()
+*/
+
+int
+bfrewind(fp)
+ FILE *fp;
+{
+ int err;
+
+ /* check to see if there is an error on the stream */
+ err = ferror(fp);
+
+ (void) fflush(fp);
+
+ /*
+ ** Clear error if tried to fflush()
+ ** a read-only file pointer and
+ ** there wasn't a previous error.
+ */
+
+ if (err == 0)
+ clearerr(fp);
+
+ /* errno is set implicitly by fseek() before return */
+ return fseek(fp, 0, SEEK_SET);
+}
+
+ /*
+** BFTRUNCATE -- rewinds and truncates the FILE *
+**
+** Parameters:
+** fp -- FILE * to truncate
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the FILE *, truncates it to zero length, and puts it
+** into write mode. If fp is not a buffered file, this is
+** equivalent to a rewind() and then an ftruncate(fileno(fp), 0).
+**
+** Sets errno:
+** any value of errno specified by fseek()
+** any value of errno specified by ftruncate()
+*/
+
+int
+bftruncate(fp)
+ FILE *fp;
+{
+ int ret;
+
+ if (bfrewind(fp) == -1)
+ {
+ /* errno is set implicitly by bfrewind() */
+ return -1;
+ }
+
+#if NOFTRUNCATE
+ /* XXX */
+ errno = EINVAL;
+ ret = -1;
+#else /* NOFTRUNCATE */
+ /* errno is set implicitly by ftruncate() before return */
+ ret = ftruncate(fileno(fp), 0);
+#endif /* NOFTRUNCATE */
+ return ret;
+}
+
+ /*
+** BFCLOSE -- close a buffered file
+**
+** Parameters:
+** fp -- FILE * to close
+**
+** Returns:
+** 0 on success, EOF on failure
+**
+** Side Effects:
+** Closes fp. If fp is a buffered file, unlink it if it has not
+** already been committed. If fp is not a buffered file, this is
+** equivalent to fclose().
+**
+** Sets errno:
+** any value of errno specified by fclose()
+*/
+
+int
+bfclose(fp)
+ FILE *fp;
+{
+ int retval;
+ struct bf *bfp = NULL;
+
+ /* Get associated bf structure */
+ bfp = bflookup(fp);
+
+ /* Decrement and check refcount */
+ if (bfp != NULL && --bfp->bf_refcount > 0)
+ return 0;
+
+ /* If bf, get bf structure and remove from list */
+ if (bfp != NULL)
+ bfp = bfdelete(fp);
+
+ if (fclose(fp) == EOF)
+ {
+ if (tTd(58, 8))
+ dprintf("bfclose: fclose failed\n");
+ /* errno is set implicitly by fclose() */
+ return -1;
+ }
+
+ if (bfp == NULL)
+ return 0;
+
+ /* Success unless we determine otherwise in next block */
+ retval = 0;
+
+ if (bfp != NULL)
+ {
+ /* Might have to unlink; certainly will have to deallocate */
+ if (!bfp->bf_committed)
+ retval = unlink(bfp->bf_filename);
+
+ free(bfp->bf_filename);
+ free(bfp);
+ if (tTd(58, 8))
+ dprintf("bfclose: freed %ld\n",
+ (long) sizeof(struct bf));
+ }
+ else
+ {
+ if (tTd(58, 8))
+ dprintf("bfclose: bfp was NULL\n");
+ }
+
+ return retval;
+}
+
+ /*
+** BFTEST -- test if a FILE * is a buffered file
+**
+** Parameters:
+** fp -- FILE * to test
+**
+** Returns:
+** TRUE if fp is a buffered file, FALSE otherwise.
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** never.
+*/
+
+bool
+bftest(fp)
+ FILE *fp;
+{
+ return (bflookup(fp) != NULL);
+}
+
+ /*
+** BFINSERT -- insert item in linking list
+**
+** Parameters:
+** datum -- item to insert
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** never.
+*/
+
+void
+bfinsert(datum)
+ struct bf *datum;
+{
+ datum->bf_cdr = bflist;
+ bflist = datum;
+}
+
+ /*
+** BFLOOKUP -- lookup FILE * in list
+**
+** Parameters:
+** fp -- FILE * to lookup
+**
+** Returns:
+** bf struct for the FILE *, NULL if not found
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** never.
+*/
+
+struct bf *
+bflookup(key)
+ FILE *key;
+{
+ struct bf *t;
+
+ for (t = bflist; t != NULL; t = t->bf_cdr)
+ {
+ if (t->bf_key == key)
+ {
+ return t;
+ }
+ }
+
+ /* If we got this far, we didn't find it */
+ return NULL;
+}
+
+ /*
+** BFDELETE -- delete a FILE * in list
+**
+** Parameters:
+** fp -- FILE * to delete
+**
+** Returns:
+** bf struct for deleted FILE *, NULL if not found,
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** never.
+*/
+
+struct bf *
+bfdelete(key)
+ FILE *key;
+{
+ struct bf *t, *u;
+
+ if (bflist == NULL)
+ return NULL;
+
+ /* if first element, special case */
+ if (bflist->bf_key == key)
+ {
+ u = bflist;
+ bflist = bflist->bf_cdr;
+ return u;
+ }
+
+ for (t = bflist; t->bf_cdr != NULL; t = t->bf_cdr)
+ {
+ if (t->bf_cdr->bf_key == key)
+ {
+ u = t->bf_cdr;
+ t->bf_cdr = u->bf_cdr;
+ return u;
+ }
+ }
+
+ /* If we got this far, we didn't find it */
+ return NULL;
+}
diff --git a/contrib/sendmail/src/bf_portable.h b/contrib/sendmail/src/bf_portable.h
new file mode 100644
index 0000000000000..e319a740399cb
--- /dev/null
+++ b/contrib/sendmail/src/bf_portable.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * $Id: bf_portable.h,v 8.6 1999/11/04 19:31:25 ca Exp $
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef BF_PORTABLE_H
+#define BF_PORTABLE_H 1
+/*
+** This implementation will behave differently from the Torek-based code in
+** the following major ways:
+** - The buffer size argument to bfopen() will be sent in, sent back,
+** queried, lost, found, subjected to public inquiry, lost again, and
+** finally buried in soft peat and recycled as firelighters.
+** - Errors in creating the file (but not necessarily writing to it) will
+** always be detected and reported synchronously with the bfopen()
+*/
+
+/* Linked structure for storing information about each buffered file */
+struct bf
+{
+ FILE *bf_key; /* Unused except as a key for lookup */
+ bool bf_committed; /* buffered file is on disk */
+ char *bf_filename; /* Name of disk file */
+ int bf_refcount; /* Reference count */
+ struct bf *bf_cdr;
+};
+
+/*
+** Access routines for looking up bf structures
+**
+** maybe replace with a faster data structure later
+*/
+
+extern void bfinsert __P((struct bf *));
+extern struct bf *bflookup __P((FILE *));
+extern struct bf *bfdelete __P((FILE *));
+#endif /* BF_PORTABLE_H */
diff --git a/contrib/sendmail/src/bf_torek.c b/contrib/sendmail/src/bf_torek.c
new file mode 100644
index 0000000000000..fb0536860d4c4
--- /dev/null
+++ b/contrib/sendmail/src/bf_torek.c
@@ -0,0 +1,784 @@
+/*
+ * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: bf_torek.c,v 8.19.18.1 2000/07/18 16:52:26 gshapiro Exp $";
+#endif /* ! lint */
+
+#if SFIO
+ ERROR README: Can not use bf_torek.c with SFIO.
+#endif /* SFIO */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#ifndef BF_STANDALONE
+# include "sendmail.h"
+#endif /* ! BF_STANDALONE */
+#include "bf_torek.h"
+#include "bf.h"
+
+ /*
+** BFOPEN -- create a new buffered file
+**
+** Parameters:
+** filename -- the file's name
+** fmode -- what mode the file should be created as
+** bsize -- amount of buffer space to allocate (may be 0)
+** flags -- if running under sendmail, passed directly to safeopen
+**
+** Returns:
+** a FILE * which may then be used with stdio functions, or NULL
+** on failure. FILE * is opened for writing (mode "w+").
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** ENOMEM -- out of memory
+** ENOENT -- illegal empty filename specified
+** any value of errno specified by open()
+** any value of errno specified by fdopen()
+** any value of errno specified by funopen()
+*/
+
+#ifdef BF_STANDALONE
+# define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode)
+#else /* BF_STANDALONE */
+# define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
+#endif /* BF_STANDALONE */
+
+FILE *
+bfopen(filename, fmode, bsize, flags)
+ char *filename;
+ int fmode;
+ size_t bsize;
+ long flags;
+{
+ struct bf *bfp;
+ FILE *retval;
+ int save_errno, l;
+ struct stat st;
+
+ /* Sanity checks */
+ /* Empty filename string */
+ if (*filename == '\0')
+ {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (stat(filename, &st) == 0)
+ {
+ /* file already exists on disk */
+ errno = EEXIST;
+ return NULL;
+ }
+
+ /* Allocate memory */
+ bfp = (struct bf *)malloc(sizeof(struct bf));
+ if (bfp == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* A zero bsize is valid, just don't allocate memory */
+ if (bsize > 0)
+ {
+ bfp->bf_buf = (char *)malloc(bsize);
+ if (bfp->bf_buf == NULL)
+ {
+ free(bfp);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+ else
+ bfp->bf_buf = NULL;
+
+ /* Nearly home free, just set all the parameters now */
+ bfp->bf_committed = FALSE;
+ bfp->bf_ondisk = FALSE;
+ bfp->bf_refcount = 1;
+ bfp->bf_flags = flags;
+ bfp->bf_bufsize = bsize;
+ bfp->bf_buffilled = 0;
+ l = strlen(filename) + 1;
+ bfp->bf_filename = (char *)malloc(l);
+ if (bfp->bf_filename == NULL)
+ {
+ free(bfp);
+ if (bfp->bf_buf != NULL)
+ free(bfp->bf_buf);
+ errno = ENOMEM;
+ return NULL;
+ }
+ (void) strlcpy(bfp->bf_filename, filename, l);
+ bfp->bf_filemode = fmode;
+ bfp->bf_offset = 0;
+ bfp->bf_size = 0;
+
+ if (tTd(58, 8))
+ dprintf("bfopen(%s, %d)\n", filename, bsize);
+
+ /* The big test: will funopen accept it? */
+ retval = funopen((void *)bfp, _bfread, _bfwrite, _bfseek, _bfclose);
+ if (retval == NULL)
+ {
+ /* Just in case free() sets errno */
+ save_errno = errno;
+ free(bfp);
+ free(bfp->bf_filename);
+ if (bfp->bf_buf != NULL)
+ free(bfp->bf_buf);
+ errno = save_errno;
+ return NULL;
+ }
+ else
+ {
+ /* Success */
+ return retval;
+ }
+}
+ /*
+** BFDUP -- increase refcount on buffered file
+**
+** Parameters:
+** fp -- FILE * to "duplicate"
+**
+** Returns:
+** If file is memory buffered, fp with increased refcount
+** If file is on disk, NULL (need to use link())
+*/
+
+FILE *
+bfdup(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+
+ /* If called on a normal FILE *, noop */
+ if (!bftest(fp))
+ return NULL;
+
+ /* Get associated bf structure */
+ bfp = (struct bf *)fp->_cookie;
+
+ /* Increase ref count */
+ bfp->bf_refcount++;
+
+ return fp;
+}
+
+ /*
+** BFCOMMIT -- "commits" the buffered file
+**
+** Parameters:
+** fp -- FILE * to commit to disk
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** Forces the given FILE * to be written to disk if it is not
+** already, and ensures that it will be kept after closing. If
+** fp is not a buffered file, this is a no-op.
+**
+** Sets errno:
+** any value of errno specified by open()
+** any value of errno specified by write()
+** any value of errno specified by lseek()
+*/
+
+int
+bfcommit(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+ int retval;
+ int byteswritten;
+
+ /* If called on a normal FILE *, noop */
+ if (!bftest(fp))
+ return 0;
+
+ /* Get associated bf structure */
+ bfp = (struct bf *)fp->_cookie;
+
+ /* If already committed, noop */
+ if (bfp->bf_committed)
+ return 0;
+
+ /* Do we need to open a file? */
+ if (!bfp->bf_ondisk)
+ {
+ struct stat st;
+
+ if (tTd(58, 8))
+ dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
+
+ if (stat(bfp->bf_filename, &st) == 0)
+ {
+ errno = EEXIST;
+ return -1;
+ }
+
+ retval = OPEN(bfp->bf_filename, O_RDWR | O_CREAT | O_TRUNC,
+ bfp->bf_filemode, bfp->bf_flags);
+
+ /* Couldn't create file: failure */
+ if (retval < 0)
+ {
+ /* errno is set implicitly by open() */
+ return -1;
+ }
+
+ bfp->bf_disk_fd = retval;
+ bfp->bf_ondisk = TRUE;
+ }
+
+ /* Write out the contents of our buffer, if we have any */
+ if (bfp->bf_buffilled > 0)
+ {
+ byteswritten = 0;
+
+ if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0)
+ {
+ /* errno is set implicitly by lseek() */
+ return -1;
+ }
+
+ while (byteswritten < bfp->bf_buffilled)
+ {
+ retval = write(bfp->bf_disk_fd,
+ bfp->bf_buf + byteswritten,
+ bfp->bf_buffilled - byteswritten);
+ if (retval < 0)
+ {
+ /* errno is set implicitly by write() */
+ return -1;
+ }
+ else
+ byteswritten += retval;
+ }
+ }
+ bfp->bf_committed = TRUE;
+
+ /* Invalidate buf; all goes to file now */
+ bfp->bf_buffilled = 0;
+ if (bfp->bf_bufsize > 0)
+ {
+ /* Don't need buffer anymore; free it */
+ bfp->bf_bufsize = 0;
+ free(bfp->bf_buf);
+ }
+ return 0;
+}
+
+ /*
+** BFREWIND -- rewinds the FILE *
+**
+** Parameters:
+** fp -- FILE * to rewind
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the FILE * and puts it into read mode. Normally one
+** would bfopen() a file, write to it, then bfrewind() and
+** fread(). If fp is not a buffered file, this is equivalent to
+** rewind().
+**
+** Sets errno:
+** any value of errno specified by fseek()
+*/
+
+int
+bfrewind(fp)
+ FILE *fp;
+{
+ int err;
+
+ /* check to see if there is an error on the stream */
+ err = ferror(fp);
+
+ (void) fflush(fp);
+
+ /*
+ ** Clear error if tried to fflush()
+ ** a read-only file pointer and
+ ** there wasn't a previous error.
+ */
+
+ if (err == 0)
+ clearerr(fp);
+
+ /* errno is set implicitly by fseek() before return */
+ return fseek(fp, 0, SEEK_SET);
+}
+
+ /*
+** BFTRUNCATE -- rewinds and truncates the FILE *
+**
+** Parameters:
+** fp -- FILE * to truncate
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the FILE *, truncates it to zero length, and puts it
+** into write mode. If fp is not a buffered file, this is
+** equivalent to a rewind() and then an ftruncate(fileno(fp), 0).
+**
+** Sets errno:
+** any value of errno specified by fseek()
+** any value of errno specified by ftruncate()
+*/
+
+int
+bftruncate(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+
+ if (bfrewind(fp) < 0)
+ return -1;
+
+ if (bftest(fp))
+ {
+ /* Get bf structure */
+ bfp = (struct bf *)fp->_cookie;
+ bfp->bf_buffilled = 0;
+ bfp->bf_size = 0;
+
+ /* Need to zero the buffer */
+ if (bfp->bf_bufsize > 0)
+ memset(bfp->bf_buf, '\0', bfp->bf_bufsize);
+ if (bfp->bf_ondisk)
+ return ftruncate(bfp->bf_disk_fd, 0);
+ else
+ return 0;
+ }
+ else
+ return ftruncate(fileno(fp), 0);
+}
+
+ /*
+** BFCLOSE -- close a buffered file
+**
+** Parameters:
+** fp -- FILE * to close
+**
+** Returns:
+** 0 on success, EOF on failure
+**
+** Side Effects:
+** Closes fp. If fp is a buffered file, unlink it if it has not
+** already been committed. If fp is not a buffered file, this is
+** equivalent to fclose().
+**
+** Sets errno:
+** any value of errno specified by fclose()
+*/
+
+int
+bfclose(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+
+ /* If called on a normal FILE *, call fclose() on it */
+ if (!bftest(fp))
+ return fclose(fp);
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *)fp->_cookie;
+
+ /* Check reference count to see if we actually want to close */
+ if (bfp != NULL && --bfp->bf_refcount > 0)
+ return 0;
+
+ /*
+ ** In this implementation, just call fclose--the _bfclose
+ ** routine will be called by that
+ */
+
+ return fclose(fp);
+}
+
+ /*
+** BFTEST -- test if a FILE * is a buffered file
+**
+** Parameters:
+** fp -- FILE * to test
+**
+** Returns:
+** TRUE if fp is a buffered file, FALSE otherwise.
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** never.
+*/
+
+bool
+bftest(fp)
+ FILE *fp;
+{
+ /*
+ ** Check to see if our special I/O routines are installed
+ ** in this file structure
+ */
+
+ return ((fp->_close == _bfclose) &&
+ (fp->_read == _bfread) &&
+ (fp->_seek == _bfseek) &&
+ (fp->_write == _bfwrite));
+}
+
+ /*
+** _BFCLOSE -- close a buffered file
+**
+** Parameters:
+** cookie -- cookie of file to close
+**
+** Returns:
+** 0 to indicate success
+**
+** Side Effects:
+** deletes backing file, frees memory.
+**
+** Sets errno:
+** never.
+*/
+
+int
+_bfclose(cookie)
+ void *cookie;
+{
+ struct bf *bfp;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *)cookie;
+
+ /* Need to clean up the file */
+ if (bfp->bf_ondisk && !bfp->bf_committed)
+ unlink(bfp->bf_filename);
+
+ /* Need to free the buffer */
+ if (bfp->bf_bufsize > 0)
+ free(bfp->bf_buf);
+
+ /* Finally, free the structure */
+ free(bfp);
+
+ return 0;
+}
+
+ /*
+** _BFREAD -- read a buffered file
+**
+** Parameters:
+** cookie -- cookie of file to read
+** buf -- buffer to fill
+** nbytes -- how many bytes to read
+**
+** Returns:
+** number of bytes read or -1 indicate failure
+**
+** Side Effects:
+** none.
+**
+*/
+
+int
+_bfread(cookie, buf, nbytes)
+ void *cookie;
+ char *buf;
+ int nbytes;
+{
+ struct bf *bfp;
+ int count = 0; /* Number of bytes put in buf so far */
+ int retval;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *)cookie;
+
+ if (bfp->bf_offset < bfp->bf_buffilled)
+ {
+ /* Need to grab some from buffer */
+ count = nbytes;
+ if ((bfp->bf_offset + count) > bfp->bf_buffilled)
+ count = bfp->bf_buffilled - bfp->bf_offset;
+
+ memcpy(buf, bfp->bf_buf + bfp->bf_offset, count);
+ }
+
+ if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled)
+ {
+ /* Need to grab some from file */
+
+ if (!bfp->bf_ondisk)
+ {
+ /* Oops, the file doesn't exist. EOF. */
+ goto finished;
+ }
+
+ /* Catch a read() on an earlier failed write to disk */
+ if (bfp->bf_disk_fd < 0)
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ if (lseek(bfp->bf_disk_fd,
+ bfp->bf_offset + count, SEEK_SET) < 0)
+ {
+ if ((errno == EINVAL) || (errno == ESPIPE))
+ {
+ /*
+ ** stdio won't be expecting these
+ ** errnos from read()! Change them
+ ** into something it can understand.
+ */
+
+ errno = EIO;
+ }
+ return -1;
+ }
+
+ while (count < nbytes)
+ {
+ retval = read(bfp->bf_disk_fd,
+ buf + count,
+ nbytes - count);
+ if (retval < 0)
+ {
+ /* errno is set implicitly by read() */
+ return -1;
+ }
+ else if (retval == 0)
+ goto finished;
+ else
+ count += retval;
+ }
+ }
+
+finished:
+ bfp->bf_offset += count;
+ return count;
+}
+
+ /*
+** _BFSEEK -- seek to a position in a buffered file
+**
+** Parameters:
+** cookie -- cookie of file to seek
+** offset -- position to seek to
+** whence -- how to seek
+**
+** Returns:
+** new file offset or -1 indicate failure
+**
+** Side Effects:
+** none.
+**
+*/
+
+fpos_t
+_bfseek(cookie, offset, whence)
+ void *cookie;
+ fpos_t offset;
+ int whence;
+
+{
+ struct bf *bfp;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *)cookie;
+
+ switch (whence)
+ {
+ case SEEK_SET:
+ bfp->bf_offset = offset;
+ break;
+
+ case SEEK_CUR:
+ bfp->bf_offset += offset;
+ break;
+
+ case SEEK_END:
+ bfp->bf_offset = bfp->bf_size + offset;
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return bfp->bf_offset;
+}
+
+ /*
+** _BFWRITE -- write to a buffered file
+**
+** Parameters:
+** cookie -- cookie of file to write
+** buf -- data buffer
+** nbytes -- how many bytes to write
+**
+** Returns:
+** number of bytes written or -1 indicate failure
+**
+** Side Effects:
+** may create backing file if over memory limit for file.
+**
+*/
+
+int
+_bfwrite(cookie, buf, nbytes)
+ void *cookie;
+ const char *buf;
+ int nbytes;
+{
+ struct bf *bfp;
+ int count = 0; /* Number of bytes written so far */
+ int retval;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *)cookie;
+
+ /* If committed, go straight to disk */
+ if (bfp->bf_committed)
+ {
+ if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0)
+ {
+ if ((errno == EINVAL) || (errno == ESPIPE))
+ {
+ /*
+ ** stdio won't be expecting these
+ ** errnos from write()! Change them
+ ** into something it can understand.
+ */
+
+ errno = EIO;
+ }
+ return -1;
+ }
+
+ count = write(bfp->bf_disk_fd, buf, nbytes);
+ if (count < 0)
+ {
+ /* errno is set implicitly by write() */
+ return -1;
+ }
+ goto finished;
+ }
+
+ if (bfp->bf_offset < bfp->bf_bufsize)
+ {
+ /* Need to put some in buffer */
+ count = nbytes;
+ if ((bfp->bf_offset + count) > bfp->bf_bufsize)
+ count = bfp->bf_bufsize - bfp->bf_offset;
+
+ memcpy(bfp->bf_buf + bfp->bf_offset, buf, count);
+ if ((bfp->bf_offset + count) > bfp->bf_buffilled)
+ bfp->bf_buffilled = bfp->bf_offset + count;
+ }
+
+ if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize)
+ {
+ /* Need to put some in file */
+ if (!bfp->bf_ondisk)
+ {
+ /* Oops, the file doesn't exist. */
+ if (tTd(58, 8))
+ dprintf("_bfwrite(%s): to disk\n",
+ bfp->bf_filename);
+
+ retval = OPEN(bfp->bf_filename,
+ O_RDWR | O_CREAT | O_TRUNC,
+ bfp->bf_filemode, bfp->bf_flags);
+
+ /* Couldn't create file: failure */
+ if (retval < 0)
+ {
+ /*
+ ** stdio may not be expecting these
+ ** errnos from write()! Change to
+ ** something which it can understand.
+ ** Note that ENOSPC and EDQUOT are saved
+ ** because they are actually valid for
+ ** write().
+ */
+
+ if (!((errno == ENOSPC) || (errno == EDQUOT)))
+ errno = EIO;
+
+ return -1;
+ }
+ bfp->bf_disk_fd = retval;
+ bfp->bf_ondisk = TRUE;
+ }
+
+ /* Catch a write() on an earlier failed write to disk */
+ if (bfp->bf_ondisk && bfp->bf_disk_fd < 0)
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ if (lseek(bfp->bf_disk_fd,
+ bfp->bf_offset + count, SEEK_SET) < 0)
+ {
+ if ((errno == EINVAL) || (errno == ESPIPE))
+ {
+ /*
+ ** stdio won't be expecting these
+ ** errnos from write()! Change them into
+ ** something which it can understand.
+ */
+
+ errno = EIO;
+ }
+ return -1;
+ }
+
+ while (count < nbytes)
+ {
+ retval = write(bfp->bf_disk_fd, buf + count,
+ nbytes - count);
+ if (retval < 0)
+ {
+ /* errno is set implicitly by write() */
+ return -1;
+ }
+ else
+ count += retval;
+ }
+ }
+
+finished:
+ bfp->bf_offset += count;
+ if (bfp->bf_offset > bfp->bf_size)
+ bfp->bf_size = bfp->bf_offset;
+ return count;
+}
diff --git a/contrib/sendmail/src/bf_torek.h b/contrib/sendmail/src/bf_torek.h
new file mode 100644
index 0000000000000..48c3995b0d1f9
--- /dev/null
+++ b/contrib/sendmail/src/bf_torek.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * $Id: bf_torek.h,v 8.6 1999/11/04 19:31:25 ca Exp $
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef BF_TOREK_H
+#define BF_TOREK_H 1
+/*
+** Data structure for storing information about each buffered file
+*/
+
+struct bf
+{
+ bool bf_committed; /* Has this buffered file been committed? */
+ bool bf_ondisk; /* On disk: committed or buffer overflow */
+ int bf_flags;
+ int bf_disk_fd; /* If on disk, associated file descriptor */
+ char *bf_buf; /* Memory buffer */
+ int bf_bufsize; /* Length of above buffer */
+ int bf_buffilled; /* Bytes of buffer actually filled */
+ char *bf_filename; /* Name of buffered file, if ever committed */
+ mode_t bf_filemode; /* Mode of buffered file, if ever committed */
+ fpos_t bf_offset; /* Currect file offset */
+ int bf_size; /* Total current size of file */
+ int bf_refcount; /* Reference count */
+};
+
+/* Our lower-level I/O routines */
+extern int _bfclose __P((void *));
+extern int _bfread __P((void *, char *, int));
+extern fpos_t _bfseek __P((void *, fpos_t, int));
+extern int _bfwrite __P((void *, const char *, int));
+#endif /* BF_TOREK_H */
diff --git a/contrib/sendmail/src/helpfile b/contrib/sendmail/src/helpfile
new file mode 100644
index 0000000000000..2563d2e37a591
--- /dev/null
+++ b/contrib/sendmail/src/helpfile
@@ -0,0 +1,136 @@
+#vers 2
+cpyr
+cpyr Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+cpyr All rights reserved.
+cpyr Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+cpyr Copyright (c) 1988, 1993
+cpyr The Regents of the University of California. All rights reserved.
+cpyr
+cpyr
+cpyr By using this file, you agree to the terms and conditions set
+cpyr forth in the LICENSE file which can be found at the top level of
+cpyr the sendmail distribution.
+cpyr
+cpyr $$Id: helpfile,v 8.31.16.3 2000/07/19 18:54:55 gshapiro Exp $$
+cpyr
+smtp This is sendmail version $v
+smtp Topics:
+smtp HELO EHLO MAIL RCPT DATA
+smtp RSET NOOP QUIT HELP VRFY
+smtp EXPN VERB ETRN DSN AUTH
+smtp STARTTLS
+smtp For more info use "HELP <topic>".
+smtp To report bugs in the implementation send email to
+smtp sendmail-bugs@sendmail.org.
+smtp For local information send email to Postmaster at your site.
+help HELP [ <topic> ]
+help The HELP command gives help info.
+helo HELO <hostname>
+helo Introduce yourself.
+ehlo EHLO <hostname>
+ehlo Introduce yourself, and request extended SMTP mode.
+ehlo Possible replies include:
+ehlo SEND Send as mail [RFC821]
+ehlo SOML Send as mail or terminal [RFC821]
+ehlo SAML Send as mail and terminal [RFC821]
+ehlo EXPN Expand the mailing list [RFC821]
+ehlo HELP Supply helpful information [RFC821]
+ehlo TURN Turn the operation around [RFC821]
+ehlo 8BITMIME Use 8-bit data [RFC1652]
+ehlo SIZE Message size declaration [RFC1870]
+ehlo VERB Verbose [Allman]
+ehlo ONEX One message transaction only [Allman]
+ehlo CHUNKING Chunking [RFC1830]
+ehlo BINARYMIME Binary MIME [RFC1830]
+ehlo PIPELINING Command Pipelining [RFC1854]
+ehlo DSN Delivery Status Notification [RFC1891]
+ehlo ETRN Remote Message Queue Starting [RFC1985]
+ehlo STARTTLS Secure SMTP [RFC2487]
+ehlo AUTH Authentication [RFC2554]
+ehlo XUSR Initial (user) submission [Allman]
+ehlo ENHANCEDSTATUSCODES Enhanced status codes [RFC2034]
+mail MAIL FROM: <sender> [ <parameters> ]
+mail Specifies the sender. Parameters are ESMTP extensions.
+mail See "HELP DSN" for details.
+rcpt RCPT TO: <recipient> [ <parameters> ]
+rcpt Specifies the recipient. Can be used any number of times.
+rcpt Parameters are ESMTP extensions. See "HELP DSN" for details.
+data DATA
+data Following text is collected as the message.
+data End with a single dot.
+rset RSET
+rset Resets the system.
+quit QUIT
+quit Exit sendmail (SMTP).
+auth AUTH mechanism [initial-response]
+auth Start authentication.
+starttls STARTTLS
+starttls Start TLS negotiation.
+verb VERB
+verb Go into verbose mode. This sends 0xy responses that are
+verb not RFC821 standard (but should be) They are recognized
+verb by humans and other sendmail implementations.
+vrfy VRFY <recipient>
+vrfy Verify an address. If you want to see what it aliases
+vrfy to, use EXPN instead.
+expn EXPN <recipient>
+expn Expand an address. If the address indicates a mailing
+expn list, return the contents of that list.
+noop NOOP
+noop Do nothing.
+send SEND FROM: <sender>
+send replaces the MAIL command, and can be used to send
+send directly to a users terminal. Not supported in this
+send implementation.
+soml SOML FROM: <sender>
+soml Send or mail. If the user is logged in, send directly,
+soml otherwise mail. Not supported in this implementation.
+saml SAML FROM: <sender>
+saml Send and mail. Send directly to the user's terminal,
+saml and also mail a letter. Not supported in this
+saml implementation.
+turn TURN
+turn Reverses the direction of the connection. Not currently
+turn implemented.
+etrn ETRN [ <hostname> | @<domain> | #<queuename> ]
+etrn Run the queue for the specified <hostname>, or
+etrn all hosts within a given <domain>, or a specially-named
+etrn <queuename> (implementation-specific).
+dsn MAIL FROM: <sender> [ RET={ FULL | HDRS} ] [ ENVID=<envid> ]
+dsn RCPT TO: <recipient> [ NOTIFY={NEVER,SUCCESS,FAILURE,DELAY} ]
+dsn [ ORCPT=<recipient> ]
+dsn SMTP Delivery Status Notifications.
+dsn Descriptions:
+dsn RET Return either the full message or only headers.
+dsn ENVID Sender's "envelope identifier" for tracking.
+dsn NOTIFY When to send a DSN. Multiple options are OK, comma-
+dsn delimited. NEVER must appear by itself.
+dsn ORCPT Original recipient.
+-bt Help for test mode:
+-bt ? :this help message.
+-bt .Dmvalue :define macro `m' to `value'.
+-bt .Ccvalue :add `value' to class `c'.
+-bt =Sruleset :dump the contents of the indicated ruleset.
+-bt =M :display the known mailers.
+-bt -ddebug-spec :equivalent to the command-line -d debug flag.
+-bt $$m :print the value of macro $$m.
+-bt $$=c :print the contents of class $$=c.
+-bt /mx host :returns the MX records for `host'.
+-bt /parse address :parse address, returning the value of crackaddr, and
+-bt the parsed address.
+-bt /try mailer addr :rewrite address into the form it will have when
+-bt presented to the indicated mailer.
+-bt /tryflags flags :set flags used by parsing. The flags can be `H' for
+-bt Header or `E' for Envelope, and `S' for Sender or `R'
+-bt for Recipient. These can be combined, `HR' sets
+-bt flags for header recipients.
+-bt /canon hostname :try to canonify hostname.
+-bt /map mapname key :look up `key' in the indicated `mapname'.
+-bt /quit :quit address test mode.
+-bt rules addr :run the indicated address through the named rules.
+-bt Rules can be a comma separated list of rules.
+control Help for smcontrol:
+control help This message.
+control restart Restart sendmail.
+control shutdown Shutdown sendmail.
+control status Show sendmail status.
diff --git a/contrib/sendmail/src/milter.c b/contrib/sendmail/src/milter.c
new file mode 100644
index 0000000000000..82f1574c4fb30
--- /dev/null
+++ b/contrib/sendmail/src/milter.c
@@ -0,0 +1,3402 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: milter.c,v 8.50.4.30 2000/07/18 07:24:51 gshapiro Exp $";
+#endif /* ! lint */
+
+#if _FFR_MILTER
+
+# include <sendmail.h>
+# include <errno.h>
+# include <sys/time.h>
+
+# if NETINET || NETINET6
+# include <arpa/inet.h>
+# endif /* NETINET || NETINET6 */
+
+
+static void milter_error __P((struct milter *));
+static int milter_open __P((struct milter *, bool, ENVELOPE *));
+static void milter_parse_timeouts __P((char *, struct milter *));
+
+static char *MilterConnectMacros[MAXFILTERMACROS + 1];
+static char *MilterHeloMacros[MAXFILTERMACROS + 1];
+static char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
+static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
+
+# define MILTER_CHECK_DONE_MSG() \
+ if (*state == SMFIR_REPLYCODE || \
+ *state == SMFIR_REJECT || \
+ *state == SMFIR_DISCARD || \
+ *state == SMFIR_TEMPFAIL) \
+ { \
+ /* Abort the filters to let them know we are done with msg */ \
+ milter_abort(e); \
+ }
+
+# define MILTER_CHECK_ERROR(action) \
+ if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
+ *state = SMFIR_TEMPFAIL; \
+ else if (bitnset(SMF_REJECT, m->mf_flags)) \
+ *state = SMFIR_REJECT; \
+ else \
+ action;
+
+# define MILTER_CHECK_REPLYCODE(default) \
+ if (response == NULL || \
+ strlen(response) + 1 != (size_t) rlen || \
+ rlen < 3 || \
+ (response[0] != '4' && response[0] != '5') || \
+ !isascii(response[1]) || !isdigit(response[1]) || \
+ !isascii(response[2]) || !isdigit(response[2])) \
+ { \
+ if (response != NULL) \
+ free(response); \
+ response = newstr(default); \
+ } \
+ else \
+ { \
+ char *ptr = response; \
+ \
+ /* Check for unprotected %'s in the string */ \
+ while (*ptr != '\0') \
+ { \
+ if (*ptr == '%' && *++ptr != '%') \
+ { \
+ free(response); \
+ response = newstr(default); \
+ break; \
+ } \
+ ptr++; \
+ } \
+ }
+
+# define MILTER_DF_ERROR(msg) \
+{ \
+ int save_errno = errno; \
+ \
+ if (tTd(64, 5)) \
+ { \
+ dprintf(msg, dfname, errstring(save_errno)); \
+ dprintf("\n"); \
+ } \
+ if (LogLevel > 0) \
+ sm_syslog(LOG_ERR, e->e_id, msg, dfname, errstring(save_errno)); \
+ if (SuperSafe) \
+ { \
+ if (e->e_dfp != NULL) \
+ { \
+ (void) fclose(e->e_dfp); \
+ e->e_dfp = NULL; \
+ } \
+ e->e_flags &= ~EF_HAS_DF; \
+ } \
+ errno = save_errno; \
+}
+
+/*
+** MILTER_TIMEOUT -- make sure socket is ready in time
+**
+** Parameters:
+** routine -- routine name for debug/logging
+** secs -- number of seconds in timeout
+** write -- waiting to read or write?
+**
+** Assumes 'm' is a milter structure for the current socket.
+*/
+
+
+# define MILTER_TIMEOUT(routine, secs, write) \
+{ \
+ int ret; \
+ int save_errno; \
+ fd_set fds; \
+ struct timeval tv; \
+ \
+ if (m->mf_sock >= FD_SETSIZE) \
+ { \
+ if (tTd(64, 5)) \
+ dprintf("%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
+ routine, m->mf_name, m->mf_sock, FD_SETSIZE); \
+ if (LogLevel > 0) \
+ sm_syslog(LOG_ERR, e->e_id, \
+ "%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
+ routine, m->mf_name, m->mf_sock, FD_SETSIZE); \
+ milter_error(m); \
+ return NULL; \
+ } \
+ \
+ FD_ZERO(&fds); \
+ FD_SET(m->mf_sock, &fds); \
+ tv.tv_sec = secs; \
+ tv.tv_usec = 0; \
+ ret = select(m->mf_sock + 1, \
+ write ? NULL : &fds, \
+ write ? &fds : NULL, \
+ NULL, &tv); \
+ \
+ switch (ret) \
+ { \
+ case 0: \
+ if (tTd(64, 5)) \
+ dprintf("%s(%s): timeout\n", routine, m->mf_name); \
+ if (LogLevel > 0) \
+ sm_syslog(LOG_ERR, e->e_id, "%s(%s): timeout\n", \
+ routine, m->mf_name); \
+ milter_error(m); \
+ return NULL; \
+ \
+ case -1: \
+ save_errno = errno; \
+ if (tTd(64, 5)) \
+ dprintf("%s(%s): select: %s\n", \
+ routine, m->mf_name, errstring(save_errno)); \
+ if (LogLevel > 0) \
+ sm_syslog(LOG_ERR, e->e_id, \
+ "%s(%s): select: %s\n", \
+ routine, m->mf_name, errstring(save_errno)); \
+ milter_error(m); \
+ return NULL; \
+ \
+ default: \
+ if (FD_ISSET(m->mf_sock, &fds)) \
+ break; \
+ if (tTd(64, 5)) \
+ dprintf("%s(%s): socket not ready\n", \
+ routine, m->mf_name); \
+ if (LogLevel > 0) \
+ sm_syslog(LOG_ERR, e->e_id, \
+ "%s(%s): socket not ready\n", \
+ m->mf_name, routine); \
+ milter_error(m); \
+ return NULL; \
+ } \
+}
+
+
+/*
+** Low level functions
+*/
+
+ /*
+** MILTER_READ -- read from a remote milter filter
+**
+** Parameters:
+** m -- milter to read from.
+** cmd -- return param for command read.
+** rlen -- return length of response string.
+** to -- timeout in seconds.
+** e -- current envelope.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+static char *
+milter_sysread(m, buf, sz, to, e)
+ struct milter *m;
+ char *buf;
+ ssize_t sz;
+ time_t to;
+ ENVELOPE *e;
+{
+ time_t readstart;
+ ssize_t len, curl;
+
+ curl = 0;
+
+ if (to > 0)
+ readstart = curtime();
+
+ for (;;)
+ {
+ if (to > 0)
+ {
+ time_t now;
+
+ now = curtime();
+ if (now - readstart >= to)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_read(%s): timeout before data read\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_read(%s): timeout before data read\n",
+ m->mf_name);
+ milter_error(m);
+ return NULL;
+ }
+ to -= now - readstart;
+ readstart = now;
+ MILTER_TIMEOUT("milter_read", to, FALSE);
+ }
+
+ len = read(m->mf_sock, buf + curl, sz - curl);
+
+ if (len < 0)
+ {
+ int save_errno = errno;
+
+ if (tTd(64, 5))
+ dprintf("milter_read(%s): read returned %ld: %s\n",
+ m->mf_name, (long) len,
+ errstring(save_errno));
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_read(%s): read returned %ld: %s",
+ m->mf_name, (long) len,
+ errstring(save_errno));
+ milter_error(m);
+ return NULL;
+ }
+
+ curl += len;
+ if (len == 0 || len >= sz)
+ break;
+
+ }
+
+ if (curl != sz)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_read(%s): read returned %ld, expecting %ld\n",
+ m->mf_name, (long) curl, (long) sz);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_read(%s): read returned %ld, expecting %ld",
+ m->mf_name, (long) curl, (long) sz);
+ milter_error(m);
+ return NULL;
+ }
+ return buf;
+}
+
+static char *
+milter_read(m, cmd, rlen, to, e)
+ struct milter *m;
+ char *cmd;
+ ssize_t *rlen;
+ time_t to;
+ ENVELOPE *e;
+{
+ time_t readstart;
+ ssize_t expl;
+ mi_int32 i;
+ char *buf;
+ char data[MILTER_LEN_BYTES + 1];
+
+ *rlen = 0;
+ *cmd = '\0';
+
+ if (to > 0)
+ readstart = curtime();
+
+ if (milter_sysread(m, data, sizeof data, to, e) == NULL)
+ return NULL;
+
+ /* reset timeout */
+ if (to > 0)
+ {
+ time_t now;
+
+ now = curtime();
+ if (now - readstart >= to)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_read(%s): timeout before data read\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_read(%s): timeout before data read\n",
+ m->mf_name);
+ milter_error(m);
+ return NULL;
+ }
+ to -= now - readstart;
+ }
+
+ *cmd = data[MILTER_LEN_BYTES];
+ data[MILTER_LEN_BYTES] = '\0';
+ (void) memcpy(&i, data, MILTER_LEN_BYTES);
+ expl = ntohl(i) - 1;
+
+ if (tTd(64, 25))
+ dprintf("milter_read(%s): expecting %ld bytes\n",
+ m->mf_name, (long) expl);
+
+ if (expl < 0)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_read(%s): read size %ld out of range\n",
+ m->mf_name, (long) expl);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_read(%s): read size %ld out of range",
+ m->mf_name, (long) expl);
+ milter_error(m);
+ return NULL;
+ }
+
+ if (expl == 0)
+ return NULL;
+
+ buf = (char *)xalloc(expl);
+
+ if (milter_sysread(m, buf, expl, to, e) == NULL)
+ {
+ free(buf);
+ return NULL;
+ }
+
+ if (tTd(64, 50))
+ dprintf("milter_read(%s): Returning %*s\n",
+ m->mf_name, (int) expl, buf);
+ *rlen = expl;
+ return buf;
+}
+ /*
+** MILTER_WRITE -- write to a remote milter filter
+**
+** Parameters:
+** m -- milter to read from.
+** cmd -- command to send.
+** buf -- optional command data.
+** len -- length of buf.
+** to -- timeout in seconds.
+** e -- current envelope.
+**
+** Returns:
+** buf if successful, NULL otherwise
+** Not actually used anywhere but function prototype
+** must match milter_read()
+*/
+
+static char *
+milter_write(m, cmd, buf, len, to, e)
+ struct milter *m;
+ char cmd;
+ char *buf;
+ ssize_t len;
+ time_t to;
+ ENVELOPE *e;
+{
+ time_t writestart = (time_t) 0;
+ ssize_t sl, i;
+ mi_int32 nl;
+ char data[MILTER_LEN_BYTES + 1];
+
+ if (len < 0 || len > MILTER_CHUNK_SIZE)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_write(%s): length %ld out of range\n",
+ m->mf_name, (long) len);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_write(%s): length %ld out of range",
+ m->mf_name, (long) len);
+ milter_error(m);
+ return NULL;
+ }
+
+ if (tTd(64, 20))
+ dprintf("milter_write(%s): cmd %c, len %ld\n",
+ m->mf_name, cmd, (long) len);
+
+ nl = htonl(len + 1); /* add 1 for the cmd char */
+ (void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
+ data[MILTER_LEN_BYTES] = cmd;
+ sl = MILTER_LEN_BYTES + 1;
+
+ if (to > 0)
+ {
+ writestart = curtime();
+ MILTER_TIMEOUT("milter_write", to, TRUE);
+ }
+
+ /* use writev() instead to send the whole stuff at once? */
+ i = write(m->mf_sock, (void *) data, sl);
+ if (i != sl)
+ {
+ int save_errno = errno;
+
+ if (tTd(64, 5))
+ dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
+ m->mf_name, cmd, (long) i, (long) sl,
+ errstring(save_errno));
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_write(%s): write(%c) returned %ld, expected %ld: %s",
+ m->mf_name, cmd, (long) i, (long) sl,
+ errstring(save_errno));
+ milter_error(m);
+ return buf;
+ }
+
+ if (len <= 0 || buf == NULL)
+ return buf;
+
+ if (tTd(64, 50))
+ dprintf("milter_write(%s): Sending %*s\n",
+ m->mf_name, (int) len, buf);
+
+ if (to > 0)
+ {
+ time_t now;
+
+ now = curtime();
+ if (now - writestart >= to)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_write(%s): timeout before data send\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_write(%s): timeout before data send\n",
+ m->mf_name);
+ milter_error(m);
+ return NULL;
+ }
+ else
+ {
+ to -= now - writestart;
+ MILTER_TIMEOUT("milter_write", to, TRUE);
+ }
+ }
+
+ i = write(m->mf_sock, (void *) buf, len);
+ if (i != len)
+ {
+ int save_errno = errno;
+
+ if (tTd(64, 5))
+ dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
+ m->mf_name, cmd, (long) i, (long) sl,
+ errstring(save_errno));
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_write(%s): write(%c) returned %ld, expected %ld: %s",
+ m->mf_name, cmd, (long) i, (long) len,
+ errstring(save_errno));
+ milter_error(m);
+ return NULL;
+ }
+ return buf;
+}
+
+/*
+** Utility functions
+*/
+
+ /*
+** MILTER_OPEN -- connect to remote milter filter
+**
+** Parameters:
+** m -- milter to connect to.
+** parseonly -- parse but don't connect.
+** e -- current envelope.
+**
+** Returns:
+** connected socket if sucessful && !parseonly,
+** 0 upon parse success if parseonly,
+** -1 otherwise.
+*/
+
+static int
+milter_open(m, parseonly, e)
+ struct milter *m;
+ bool parseonly;
+ ENVELOPE *e;
+{
+ int sock = 0;
+ SOCKADDR_LEN_T addrlen = 0;
+ int addrno = 0;
+ int save_errno;
+ char *p;
+ char *colon;
+ char *at;
+ struct hostent *hp = NULL;
+ SOCKADDR addr;
+
+ if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
+ {
+ if (tTd(64, 5))
+ dprintf("X%s: empty or missing socket information\n",
+ m->mf_name);
+ if (parseonly)
+ syserr("X%s: empty or missing socket information",
+ m->mf_name);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: empty or missing socket information",
+ m->mf_name);
+ milter_error(m);
+ return -1;
+ }
+
+ /* protocol:filename or protocol:port@host */
+ p = m->mf_conn;
+ colon = strchr(p, ':');
+ if (colon != NULL)
+ {
+ *colon = '\0';
+
+ if (*p == '\0')
+ {
+# if NETUNIX
+ /* default to AF_UNIX */
+ addr.sa.sa_family = AF_UNIX;
+# else /* NETUNIX */
+# if NETINET
+ /* default to AF_INET */
+ addr.sa.sa_family = AF_INET;
+# else /* NETINET */
+# if NETINET6
+ /* default to AF_INET6 */
+ addr.sa.sa_family = AF_INET6;
+# else /* NETINET6 */
+ /* no protocols available */
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: no valid socket protocols available",
+ m->mf_name);
+ milter_error(m);
+ return -1;
+# endif /* NETINET6 */
+# endif /* NETINET */
+# endif /* NETUNIX */
+ }
+# if NETUNIX
+ else if (strcasecmp(p, "unix") == 0 ||
+ strcasecmp(p, "local") == 0)
+ addr.sa.sa_family = AF_UNIX;
+# endif /* NETUNIX */
+# if NETINET
+ else if (strcasecmp(p, "inet") == 0)
+ addr.sa.sa_family = AF_INET;
+# endif /* NETINET */
+# if NETINET6
+ else if (strcasecmp(p, "inet6") == 0)
+ addr.sa.sa_family = AF_INET6;
+# endif /* NETINET6 */
+ else
+ {
+# ifdef EPROTONOSUPPORT
+ errno = EPROTONOSUPPORT;
+# else /* EPROTONOSUPPORT */
+ errno = EINVAL;
+# endif /* EPROTONOSUPPORT */
+ if (tTd(64, 5))
+ dprintf("X%s: unknown socket type %s\n",
+ m->mf_name, p);
+ if (parseonly)
+ syserr("X%s: unknown socket type %s",
+ m->mf_name, p);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: unknown socket type %s",
+ m->mf_name, p);
+ milter_error(m);
+ return -1;
+ }
+ *colon++ = ':';
+ }
+ else
+ {
+ /* default to AF_UNIX */
+ addr.sa.sa_family = AF_UNIX;
+ colon = p;
+ }
+
+# if NETUNIX
+ if (addr.sa.sa_family == AF_UNIX)
+ {
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
+
+ at = colon;
+ if (strlen(colon) >= sizeof addr.sunix.sun_path)
+ {
+ if (tTd(64, 5))
+ dprintf("X%s: local socket name %s too long\n",
+ m->mf_name, colon);
+ errno = EINVAL;
+ if (parseonly)
+ syserr("X%s: local socket name %s too long",
+ m->mf_name, colon);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: local socket name %s too long",
+ m->mf_name, colon);
+ milter_error(m);
+ return -1;
+ }
+ errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
+ S_IRUSR|S_IWUSR, NULL);
+
+ /* if just parsing .cf file, socket doesn't need to exist */
+ if (parseonly && errno == ENOENT)
+ {
+ if (OpMode == MD_DAEMON ||
+ OpMode == MD_FGDAEMON)
+ fprintf(stderr,
+ "WARNING: X%s: local socket name %s missing\n",
+ m->mf_name, colon);
+ }
+ else if (errno != 0)
+ {
+ /* if not safe, don't create */
+ save_errno = errno;
+ if (tTd(64, 5))
+ dprintf("X%s: local socket name %s unsafe\n",
+ m->mf_name, colon);
+ errno = save_errno;
+ if (parseonly)
+ {
+ if (OpMode == MD_DAEMON ||
+ OpMode == MD_FGDAEMON ||
+ OpMode == MD_SMTP)
+ syserr("X%s: local socket name %s unsafe",
+ m->mf_name, colon);
+ }
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: local socket name %s unsafe",
+ m->mf_name, colon);
+ milter_error(m);
+ return -1;
+ }
+
+ (void) strlcpy(addr.sunix.sun_path, colon,
+ sizeof addr.sunix.sun_path);
+ addrlen = sizeof (struct sockaddr_un);
+ }
+ else
+# endif /* NETUNIX */
+# if NETINET || NETINET6
+ if (FALSE
+# if NETINET
+ || addr.sa.sa_family == AF_INET
+# endif /* NETINET */
+# if NETINET6
+ || addr.sa.sa_family == AF_INET6
+# endif /* NETINET6 */
+ )
+ {
+ u_short port;
+
+ /* Parse port@host */
+ at = strchr(colon, '@');
+ if (at == NULL)
+ {
+ if (tTd(64, 5))
+ dprintf("X%s: bad address %s (expected port@host)\n",
+ m->mf_name, colon);
+ if (parseonly)
+ syserr("X%s: bad address %s (expected port@host)",
+ m->mf_name, colon);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: bad address %s (expected port@host)",
+ m->mf_name, colon);
+ milter_error(m);
+ return -1;
+ }
+ *at = '\0';
+ if (isascii(*colon) && isdigit(*colon))
+ port = htons((u_short) atoi(colon));
+ else
+ {
+# ifdef NO_GETSERVBYNAME
+ if (tTd(64, 5))
+ dprintf("X%s: invalid port number %s\n",
+ m->mf_name, colon);
+ if (parseonly)
+ syserr("X%s: invalid port number %s",
+ m->mf_name, colon);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: invalid port number %s",
+ m->mf_name, colon);
+ milter_error(m);
+ return -1;
+# else /* NO_GETSERVBYNAME */
+ register struct servent *sp;
+
+ sp = getservbyname(colon, "tcp");
+ if (sp == NULL)
+ {
+ save_errno = errno;
+ if (tTd(64, 5))
+ dprintf("X%s: unknown port name %s\n",
+ m->mf_name, colon);
+ errno = save_errno;
+ if (parseonly)
+ syserr("X%s: unknown port name %s",
+ m->mf_name, colon);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: unknown port name %s",
+ m->mf_name, colon);
+ milter_error(m);
+ return -1;
+ }
+ port = sp->s_port;
+# endif /* NO_GETSERVBYNAME */
+ }
+ *at++ = '@';
+ if (*at == '[')
+ {
+ char *end;
+
+ end = strchr(at, ']');
+ if (end != NULL)
+ {
+ bool found = FALSE;
+# if NETINET
+ unsigned long hid = INADDR_NONE;
+# endif /* NETINET */
+# if NETINET6
+ struct sockaddr_in6 hid6;
+# endif /* NETINET6 */
+
+ *end = '\0';
+# if NETINET
+ if (addr.sa.sa_family == AF_INET &&
+ (hid = inet_addr(&at[1])) != INADDR_NONE)
+ {
+ addr.sin.sin_addr.s_addr = hid;
+ addr.sin.sin_port = port;
+ found = TRUE;
+ }
+# endif /* NETINET */
+# if NETINET6
+ (void) memset(&hid6, '\0', sizeof hid6);
+ if (addr.sa.sa_family == AF_INET6 &&
+ inet_pton(AF_INET6, &at[1],
+ &hid6.sin6_addr) == 1)
+ {
+ addr.sin6.sin6_addr = hid6.sin6_addr;
+ addr.sin6.sin6_port = port;
+ found = TRUE;
+ }
+# endif /* NETINET6 */
+ *end = ']';
+ if (!found)
+ {
+ if (tTd(64, 5))
+ dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
+ m->mf_name, at);
+ if (parseonly)
+ syserr("X%s: Invalid numeric domain spec \"%s\"",
+ m->mf_name, at);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: Invalid numeric domain spec \"%s\"",
+ m->mf_name, at);
+ milter_error(m);
+ return -1;
+ }
+ }
+ else
+ {
+ if (tTd(64, 5))
+ dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
+ m->mf_name, at);
+ if (parseonly)
+ syserr("X%s: Invalid numeric domain spec \"%s\"",
+ m->mf_name, at);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: Invalid numeric domain spec \"%s\"",
+ m->mf_name, at);
+ milter_error(m);
+ return -1;
+ }
+ }
+ else
+ {
+ hp = sm_gethostbyname(at, addr.sa.sa_family);
+ if (hp == NULL)
+ {
+ save_errno = errno;
+ if (tTd(64, 5))
+ dprintf("X%s: Unknown host name %s\n",
+ m->mf_name, at);
+ errno = save_errno;
+ if (parseonly)
+ syserr("X%s: Unknown host name %s",
+ m->mf_name, at);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: Unknown host name %s",
+ m->mf_name, at);
+ milter_error(m);
+ return -1;
+ }
+ addr.sa.sa_family = hp->h_addrtype;
+ switch (hp->h_addrtype)
+ {
+# if NETINET
+ case AF_INET:
+ memmove(&addr.sin.sin_addr,
+ hp->h_addr,
+ INADDRSZ);
+ addr.sin.sin_port = port;
+ addrlen = sizeof (struct sockaddr_in);
+ addrno = 1;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ memmove(&addr.sin6.sin6_addr,
+ hp->h_addr,
+ IN6ADDRSZ);
+ addr.sin6.sin6_port = port;
+ addrlen = sizeof (struct sockaddr_in6);
+ addrno = 1;
+ break;
+# endif /* NETINET6 */
+
+ default:
+ if (tTd(64, 5))
+ dprintf("X%s: Unknown protocol for %s (%d)\n",
+ m->mf_name, at,
+ hp->h_addrtype);
+ if (parseonly)
+ syserr("X%s: Unknown protocol for %s (%d)",
+ m->mf_name, at, hp->h_addrtype);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: Unknown protocol for %s (%d)",
+ m->mf_name, at,
+ hp->h_addrtype);
+ milter_error(m);
+ return -1;
+ }
+ }
+ }
+ else
+# endif /* NETINET || NETINET6 */
+ {
+ if (tTd(64, 5))
+ dprintf("X%s: unknown socket protocol\n", m->mf_name);
+ if (parseonly)
+ syserr("X%s: unknown socket protocol", m->mf_name);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: unknown socket protocol", m->mf_name);
+ milter_error(m);
+ return -1;
+ }
+
+ /* just parsing through? */
+ if (parseonly)
+ {
+ m->mf_state = SMFS_READY;
+ return 0;
+ }
+
+ /* sanity check */
+ if (m->mf_state != SMFS_READY &&
+ m->mf_state != SMFS_CLOSED)
+ {
+ /* shouldn't happen */
+ if (tTd(64, 1))
+ dprintf("milter_open(%s): Trying to open filter in state %c\n",
+ m->mf_name, (char) m->mf_state);
+ milter_error(m);
+ return -1;
+ }
+
+ /* nope, actually connecting */
+ for (;;)
+ {
+ sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
+ if (sock < 0)
+ {
+ save_errno = errno;
+ if (tTd(64, 5))
+ dprintf("X%s: error creating socket: %s\n",
+ m->mf_name, errstring(save_errno));
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: error creating socket: %s",
+ m->mf_name, errstring(save_errno));
+ milter_error(m);
+ return -1;
+ }
+
+ if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
+ break;
+
+ /* couldn't connect.... try next address */
+ save_errno = errno;
+ if (tTd(64, 5))
+ dprintf("milter_open(%s): %s failed: %s\n",
+ m->mf_name, at, errstring(save_errno));
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, e->e_id,
+ "milter_open(%s): %s failed: %s",
+ m->mf_name, at, errstring(save_errno));
+ (void) close(sock);
+
+ /* try next address */
+ if (hp != NULL && hp->h_addr_list[addrno] != NULL)
+ {
+ switch (addr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ memmove(&addr.sin.sin_addr,
+ hp->h_addr_list[addrno++],
+ INADDRSZ);
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ memmove(&addr.sin6.sin6_addr,
+ hp->h_addr_list[addrno++],
+ IN6ADDRSZ);
+ break;
+# endif /* NETINET6 */
+
+ default:
+ if (tTd(64, 5))
+ dprintf("X%s: Unknown protocol for %s (%d)\n",
+ m->mf_name, at,
+ hp->h_addrtype);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: Unknown protocol for %s (%d)",
+ m->mf_name, at,
+ hp->h_addrtype);
+ milter_error(m);
+ return -1;
+ }
+ continue;
+ }
+ if (tTd(64, 5))
+ dprintf("X%s: error connecting to filter\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: error connecting to filter",
+ m->mf_name);
+ milter_error(m);
+ return -1;
+ }
+ m->mf_state = SMFS_OPEN;
+ return sock;
+}
+ /*
+** MILTER_SETUP -- setup structure for a mail filter
+**
+** Parameters:
+** line -- the options line.
+**
+** Returns:
+** none
+*/
+
+void
+milter_setup(line)
+ char *line;
+{
+ char fcode;
+ register char *p;
+ register struct milter *m;
+ STAB *s;
+
+ /* 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 mail filter");
+ return;
+ }
+ m = (struct milter *)xalloc(sizeof *m);
+ memset((char *) m, '\0', sizeof *m);
+ m->mf_name = newstr(line);
+ m->mf_state = SMFS_READY;
+ m->mf_sock = -1;
+ m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
+ m->mf_timeout[SMFTO_READ] = (time_t) 10;
+ m->mf_timeout[SMFTO_EOM] = (time_t) 300;
+
+ /* now scan through and assign info from the fields */
+ while (*p != '\0')
+ {
+ 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("X%s: `=' expected", m->mf_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 'S': /* socket */
+ if (p == NULL)
+ m->mf_conn = NULL;
+ else
+ m->mf_conn = newstr(p);
+ break;
+
+ case 'F': /* Milter flags configured on MTA */
+ for (; *p != '\0'; p++)
+ {
+ if (!(isascii(*p) && isspace(*p)))
+ setbitn(*p, m->mf_flags);
+ }
+ break;
+
+ case 'T': /* timeouts */
+ milter_parse_timeouts(p, m);
+ break;
+
+ default:
+ syserr("X%s: unknown filter equate %c=",
+ m->mf_name, fcode);
+ break;
+ }
+ p = delimptr;
+ }
+
+ /* early check for errors */
+ (void) milter_open(m, TRUE, CurEnv);
+
+ /* enter the mailer into the symbol table */
+ s = stab(m->mf_name, ST_MILTER, ST_ENTER);
+ if (s->s_milter != NULL)
+ syserr("X%s: duplicate filter definition", m->mf_name);
+ else
+ s->s_milter = m;
+}
+ /*
+** MILTER_PARSE_LIST -- parse option list into an array
+**
+** Called when reading configuration file.
+**
+** Parameters:
+** spec -- the filter list.
+** list -- the array to fill in.
+** max -- the maximum number of entries in list.
+**
+** Returns:
+** none
+*/
+
+void
+milter_parse_list(spec, list, max)
+ char *spec;
+ struct milter **list;
+ int max;
+{
+ int numitems = 0;
+ register char *p;
+
+ /* leave one for the NULL signifying the end of the list */
+ max--;
+
+ for (p = spec; p != NULL; )
+ {
+ STAB *s;
+
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ spec = p;
+
+ if (numitems >= max)
+ {
+ syserr("Too many filters defined, %d max", max);
+ if (max > 0)
+ list[0] = NULL;
+ return;
+ }
+ p = strpbrk(p, ",");
+ if (p != NULL)
+ *p++ = '\0';
+
+ s = stab(spec, ST_MILTER, ST_FIND);
+ if (s == NULL)
+ {
+ syserr("InputFilter %s not defined", spec);
+ ExitStat = EX_CONFIG;
+ return;
+ }
+ list[numitems++] = s->s_milter;
+ }
+ list[numitems] = NULL;
+}
+ /*
+** MILTER_PARSE_TIMEOUTS -- parse timeout list
+**
+** Called when reading configuration file.
+**
+** Parameters:
+** spec -- the timeout list.
+** m -- milter to set.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_parse_timeouts(spec, m)
+ char *spec;
+ struct milter *m;
+{
+ char fcode;
+ register char *p;
+
+ p = spec;
+
+ /* now scan through and assign info from the fields */
+ while (*p != '\0')
+ {
+ char *delimptr;
+
+ while (*p != '\0' &&
+ (*p == ';' || (isascii(*p) && isspace(*p))))
+ p++;
+
+ /* p now points to field code */
+ fcode = *p;
+ while (*p != '\0' && *p != ':')
+ p++;
+ if (*p++ != ':')
+ {
+ syserr("X%s, T=: `:' expected", m->mf_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 'S':
+ m->mf_timeout[SMFTO_WRITE] = convtime(p, 's');
+ if (tTd(64, 5))
+ printf("X%s: %c=%ld\n",
+ m->mf_name, fcode,
+ (u_long) m->mf_timeout[SMFTO_WRITE]);
+ break;
+
+ case 'R':
+ m->mf_timeout[SMFTO_READ] = convtime(p, 's');
+ if (tTd(64, 5))
+ printf("X%s: %c=%ld\n",
+ m->mf_name, fcode,
+ (u_long) m->mf_timeout[SMFTO_READ]);
+ break;
+
+ case 'E':
+ m->mf_timeout[SMFTO_EOM] = convtime(p, 's');
+ if (tTd(64, 5))
+ printf("X%s: %c=%ld\n",
+ m->mf_name, fcode,
+ (u_long) m->mf_timeout[SMFTO_EOM]);
+ break;
+
+ default:
+ if (tTd(64, 5))
+ printf("X%s: %c unknown\n",
+ m->mf_name, fcode);
+ syserr("X%s: unknown filter timeout %c",
+ m->mf_name, fcode);
+ break;
+ }
+ p = delimptr;
+ }
+}
+ /*
+** MILTER_SET_OPTION -- set an individual milter option
+**
+** Parameters:
+** name -- the name of the option.
+** val -- the value of the option.
+** sticky -- if set, don't let other setoptions override
+** this value.
+**
+** Returns:
+** none.
+*/
+
+/* set if Milter sub-option is stuck */
+static BITMAP256 StickyMilterOpt;
+
+static struct milteropt
+{
+ char *mo_name; /* long name of milter option */
+ u_char mo_code; /* code for option */
+} MilterOptTab[] =
+{
+# define MO_MACROS_CONNECT 0x01
+ { "macros.connect", MO_MACROS_CONNECT },
+# define MO_MACROS_HELO 0x02
+ { "macros.helo", MO_MACROS_HELO },
+# define MO_MACROS_ENVFROM 0x03
+ { "macros.envfrom", MO_MACROS_ENVFROM },
+# define MO_MACROS_ENVRCPT 0x04
+ { "macros.envrcpt", MO_MACROS_ENVRCPT },
+ { NULL, 0 },
+};
+
+void
+milter_set_option(name, val, sticky)
+ char *name;
+ char *val;
+ bool sticky;
+{
+ int nummac = 0;
+ register struct milteropt *mo;
+ char *p;
+ char **macros = NULL;
+
+ if (tTd(37, 2) || tTd(64, 5))
+ dprintf("milter_set_option(%s = %s)", name, val);
+
+ for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
+ {
+ if (strcasecmp(mo->mo_name, name) == 0)
+ break;
+ }
+
+ if (mo->mo_name == NULL)
+ syserr("milter_set_option: invalid Milter option %s", name);
+
+ /*
+ ** See if this option is preset for us.
+ */
+
+ if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
+ {
+ if (tTd(37, 2) || tTd(64,5))
+ dprintf(" (ignored)\n");
+ return;
+ }
+
+ if (tTd(37, 2) || tTd(64,5))
+ dprintf("\n");
+
+ switch (mo->mo_code)
+ {
+ case MO_MACROS_CONNECT:
+ if (macros == NULL)
+ macros = MilterConnectMacros;
+ /* FALLTHROUGH */
+
+ case MO_MACROS_HELO:
+ if (macros == NULL)
+ macros = MilterHeloMacros;
+ /* FALLTHROUGH */
+
+ case MO_MACROS_ENVFROM:
+ if (macros == NULL)
+ macros = MilterEnvFromMacros;
+ /* FALLTHROUGH */
+
+ case MO_MACROS_ENVRCPT:
+ if (macros == NULL)
+ macros = MilterEnvRcptMacros;
+
+ p = newstr(val);
+ while (*p != '\0')
+ {
+ char *macro;
+
+ /* Skip leading commas, spaces */
+ while (*p != '\0' &&
+ (*p == ',' || (isascii(*p) && isspace(*p))))
+ p++;
+
+ if (*p == '\0')
+ break;
+
+ /* Find end of macro */
+ macro = p;
+ while (*p != '\0' && *p != ',' &&
+ isascii(*p) && !isspace(*p))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+
+ if (nummac >= MAXFILTERMACROS)
+ {
+ syserr("milter_set_option: too many macros in Milter.%s (max %d)",
+ name, MAXFILTERMACROS);
+ macros[nummac] = NULL;
+ break;
+ }
+ macros[nummac++] = macro;
+ }
+ macros[nummac] = NULL;
+ break;
+
+ default:
+ syserr("milter_set_option: invalid Milter option %s", name);
+ break;
+ }
+
+ if (sticky)
+ setbitn(mo->mo_code, StickyMilterOpt);
+}
+ /*
+** MILTER_REOPEN_DF -- open & truncate the df file (for replbody)
+**
+** Parameters:
+** e -- current envelope.
+**
+** Returns:
+** 0 if succesful, -1 otherwise
+*/
+
+static int
+milter_reopen_df(e)
+ ENVELOPE *e;
+{
+ char dfname[MAXPATHLEN];
+
+ (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+
+ /*
+ ** In SuperSafe mode, e->e_dfp is a read-only FP so
+ ** close and reopen writable (later close and reopen
+ ** read only again).
+ **
+ ** In !SuperSafe mode, e->e_dfp still points at the
+ ** buffered file I/O descriptor, still open for writing
+ ** so there isn't as much work to do, just truncate it
+ ** and go.
+ */
+
+ if (SuperSafe)
+ {
+ /* close read-only df */
+ if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
+ {
+ (void) fclose(e->e_dfp);
+ e->e_flags &= ~EF_HAS_DF;
+ }
+
+ /* open writable */
+ if ((e->e_dfp = fopen(dfname, "w+")) == NULL)
+ {
+ MILTER_DF_ERROR("milter_reopen_df: fopen %s: %s");
+ return -1;
+ }
+ }
+ else if (e->e_dfp == NULL)
+ {
+ /* shouldn't happen */
+ errno = ENOENT;
+ MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
+ return -1;
+ }
+ return 0;
+}
+ /*
+** MILTER_RESET_DF -- re-open read-only the df file (for replbody)
+**
+** Parameters:
+** e -- current envelope.
+**
+** Returns:
+** 0 if succesful, -1 otherwise
+*/
+
+static int
+milter_reset_df(e)
+ ENVELOPE *e;
+{
+ int afd;
+ char dfname[MAXPATHLEN];
+
+ (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+
+ if (fflush(e->e_dfp) != 0 || ferror(e->e_dfp))
+ {
+ MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
+ return -1;
+ }
+ else if (!SuperSafe)
+ {
+ /* skip next few clauses */
+ /* EMPTY */
+ }
+ else if ((afd = fileno(e->e_dfp)) >= 0 && fsync(afd) < 0)
+ {
+ MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
+ return -1;
+ }
+ else if (fclose(e->e_dfp) < 0)
+ {
+ MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
+ return -1;
+ }
+ else if ((e->e_dfp = fopen(dfname, "r")) == NULL)
+ {
+ MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
+ return -1;
+ }
+ else
+ e->e_flags |= EF_HAS_DF;
+ return 0;
+}
+ /*
+** MILTER_CAN_DELRCPTS -- can any milter filters delete recipients?
+**
+** Parameters:
+** none
+**
+** Returns:
+** TRUE if any filter deletes recipients, FALSE otherwise
+*/
+
+bool
+milter_can_delrcpts()
+{
+ bool can = FALSE;
+ int i;
+
+ if (tTd(64, 10))
+ dprintf("milter_can_delrcpts:");
+
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ if (bitset(SMFIF_DELRCPT, m->mf_fflags))
+ {
+ can = TRUE;
+ break;
+ }
+ }
+ if (tTd(64, 10))
+ dprintf("%s\n", can ? "TRUE" : "FALSE");
+
+ return can;
+}
+ /*
+** MILTER_QUIT_FILTER -- close down a single filter
+**
+** Parameters:
+** m -- milter structure of filter to close down.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_quit_filter(m, e)
+ struct milter *m;
+ ENVELOPE *e;
+{
+ if (tTd(64, 10))
+ dprintf("milter_quit_filter(%s)\n", m->mf_name);
+
+ /* Never replace error state */
+ if (m->mf_state == SMFS_ERROR)
+ return;
+
+ if (m->mf_sock < 0 ||
+ m->mf_state == SMFS_CLOSED ||
+ m->mf_state == SMFS_READY)
+ {
+ m->mf_sock = -1;
+ m->mf_state = SMFS_CLOSED;
+ return;
+ }
+
+ (void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
+ m->mf_timeout[SMFTO_WRITE], e);
+ (void) close(m->mf_sock);
+ m->mf_sock = -1;
+ if (m->mf_state != SMFS_ERROR)
+ m->mf_state = SMFS_CLOSED;
+}
+ /*
+** MILTER_ABORT_FILTER -- tell filter to abort current message
+**
+** Parameters:
+** m -- milter structure of filter to abort.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_abort_filter(m, e)
+ struct milter *m;
+ ENVELOPE *e;
+{
+ if (tTd(64, 10))
+ dprintf("milter_abort_filter(%s)\n", m->mf_name);
+
+ if (m->mf_sock < 0 ||
+ m->mf_state != SMFS_INMSG)
+ return;
+
+ (void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
+ m->mf_timeout[SMFTO_WRITE], e);
+ if (m->mf_state != SMFS_ERROR)
+ m->mf_state = SMFS_DONE;
+}
+ /*
+** MILTER_SEND_MACROS -- provide macros to the filters
+**
+** Parameters:
+** m -- milter to send macros to.
+** macros -- macros to send for filter smfi_getsymval().
+** cmd -- which command the macros are associated with.
+** e -- current envelope (for macro access).
+**
+** Returns:
+** none
+*/
+
+static void
+milter_send_macros(m, macros, cmd, e)
+ struct milter *m;
+ char **macros;
+ char cmd;
+ ENVELOPE *e;
+{
+ int i;
+ int mid;
+ char *v;
+ char *buf, *bp;
+ ssize_t s;
+
+ /* sanity check */
+ if (macros == NULL || macros[0] == NULL)
+ return;
+
+ /* put together data */
+ s = 1; /* for the command character */
+ for (i = 0; macros[i] != NULL; i++)
+ {
+ mid = macid(macros[i], NULL);
+ if (mid == '\0')
+ continue;
+ v = macvalue(mid, e);
+ if (v == NULL)
+ continue;
+ s += strlen(macros[i]) + 1 + strlen(v) + 1;
+ }
+
+ buf = (char *)xalloc(s);
+ bp = buf;
+ *bp++ = cmd;
+ for (i = 0; macros[i] != NULL; i++)
+ {
+ mid = macid(macros[i], NULL);
+ if (mid == '\0')
+ continue;
+ v = macvalue(mid, e);
+ if (v == NULL)
+ continue;
+
+ if (tTd(64, 10))
+ dprintf("milter_send_macros(%s, %c): %s=%s\n",
+ m->mf_name, cmd, macros[i], v);
+
+ (void) strlcpy(bp, macros[i], s - (bp - buf));
+ bp += strlen(bp) + 1;
+ (void) strlcpy(bp, v, s - (bp - buf));
+ bp += strlen(bp) + 1;
+ }
+ (void) milter_write(m, SMFIC_MACRO, buf, s,
+ m->mf_timeout[SMFTO_WRITE], e);
+ free(buf);
+}
+
+ /*
+** MILTER_SEND_COMMAND -- send a command and return the response for a filter
+**
+** Parameters:
+** m -- current milter filter
+** command -- command to send.
+** data -- optional command data.
+** sz -- length of buf.
+** e -- current envelope (for e->e_id).
+** state -- return state word.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+static char *
+milter_send_command(m, command, data, sz, e, state)
+ struct milter *m;
+ char command;
+ void *data;
+ ssize_t sz;
+ ENVELOPE *e;
+ char *state;
+{
+ char rcmd;
+ ssize_t rlen;
+ u_long skipflag;
+ char *defresponse;
+ char *response;
+
+ if (tTd(64, 10))
+ dprintf("milter_send_command(%s): cmd %c len %ld\n",
+ m->mf_name, (char) command, (long) sz);
+
+ /* find skip flag and default failure */
+ switch (command)
+ {
+ case SMFIC_CONNECT:
+ skipflag = SMFIP_NOCONNECT;
+ defresponse = "554 Command rejected";
+ break;
+
+ case SMFIC_HELO:
+ skipflag = SMFIP_NOHELO;
+ defresponse = "550 Command rejected";
+ break;
+
+ case SMFIC_MAIL:
+ skipflag = SMFIP_NOMAIL;
+ defresponse = "550 5.7.1 Command rejected";
+ break;
+
+ case SMFIC_RCPT:
+ skipflag = SMFIP_NORCPT;
+ defresponse = "550 5.7.1 Command rejected";
+ break;
+
+ case SMFIC_HEADER:
+ skipflag = SMFIP_NOHDRS;
+ defresponse = "550 5.7.1 Command rejected";
+ break;
+
+ case SMFIC_BODY:
+ skipflag = SMFIP_NOBODY;
+ defresponse = "554 5.7.1 Command rejected";
+ break;
+
+ case SMFIC_EOH:
+ skipflag = SMFIP_NOEOH;
+ defresponse = "550 5.7.1 Command rejected";
+ break;
+
+ case SMFIC_BODYEOB:
+ case SMFIC_OPTNEG:
+ case SMFIC_MACRO:
+ case SMFIC_ABORT:
+ case SMFIC_QUIT:
+ /* NOTE: not handled by milter_send_command() */
+ /* FALLTHROUGH */
+
+ default:
+ skipflag = 0;
+ defresponse = "550 5.7.1 Command rejected";
+ break;
+ }
+
+ /* check if filter wants this command */
+ if (skipflag != 0 &&
+ bitset(skipflag, m->mf_pflags))
+ return NULL;
+
+
+ (void) milter_write(m, command, data, sz,
+ m->mf_timeout[SMFTO_WRITE], e);
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(/* EMPTY */;);
+ return NULL;
+ }
+
+ response = milter_read(m, &rcmd, &rlen,
+ m->mf_timeout[SMFTO_READ], e);
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(/* EMPTY */;);
+ return NULL;
+ }
+
+ if (tTd(64, 10))
+ dprintf("milter_send_command(%s): returned %c\n",
+ m->mf_name, (char) rcmd);
+
+ switch (rcmd)
+ {
+ case SMFIR_REPLYCODE:
+ MILTER_CHECK_REPLYCODE(defresponse);
+ /* FALLTHROUGH */
+
+ case SMFIR_REJECT:
+ case SMFIR_DISCARD:
+ case SMFIR_TEMPFAIL:
+ *state = rcmd;
+ break;
+
+ case SMFIR_ACCEPT:
+ /* this filter is done with message/connection */
+ m->mf_state = SMFS_DONE;
+ break;
+
+ case SMFIR_CONTINUE:
+ /* if MAIL command is ok, filter is in message state */
+ if (command == SMFIC_MAIL)
+ m->mf_state = SMFS_INMSG;
+ break;
+
+ default:
+ /* Invalid response to command */
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_send_command(%s): returned bogus response %c",
+ m->mf_name, rcmd);
+ milter_error(m);
+ break;
+ }
+
+ if (*state != SMFIR_REPLYCODE &&
+ response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+ return response;
+}
+
+ /*
+** MILTER_COMMAND -- send a command and return the response for each filter
+**
+** Parameters:
+** command -- command to send.
+** data -- optional command data.
+** sz -- length of buf.
+** macros -- macros to send for filter smfi_getsymval().
+** e -- current envelope (for macro access).
+** state -- return state word.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+static char *
+milter_command(command, data, sz, macros, e, state)
+ char command;
+ void *data;
+ ssize_t sz;
+ char **macros;
+ ENVELOPE *e;
+ char *state;
+{
+ int i;
+ char *response = NULL;
+
+ if (tTd(64, 10))
+ dprintf("milter_command: cmd %c len %ld\n",
+ (char) command, (long) sz);
+
+ *state = SMFIR_CONTINUE;
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ /* sanity check */
+ if (m->mf_sock < 0 ||
+ (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
+ continue;
+
+ /* send macros (regardless of whether we send command) */
+ if (macros != NULL && macros[0] != NULL)
+ {
+ milter_send_macros(m, macros, command, e);
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(continue);
+ break;
+ }
+ }
+
+ response = milter_send_command(m, command, data, sz, e, state);
+ if (*state != SMFIR_CONTINUE)
+ break;
+ }
+ return response;
+}
+ /*
+** MILTER_NEGOTIATE -- get version and flags from filter
+**
+** Parameters:
+** m -- milter filter structure.
+** e -- current envelope.
+**
+** Returns:
+** 0 on success, -1 otherwise
+*/
+
+static int
+milter_negotiate(m, e)
+ struct milter *m;
+ ENVELOPE *e;
+{
+ char rcmd;
+ mi_int32 fvers;
+ mi_int32 fflags;
+ mi_int32 pflags;
+ char *response;
+ ssize_t rlen;
+ char data[MILTER_OPTLEN];
+
+ /* sanity check */
+ if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
+ {
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): impossible state",
+ m->mf_name);
+ milter_error(m);
+ return -1;
+ }
+
+ fvers = htonl(SMFI_VERSION);
+ fflags = htonl(SMFI_CURR_ACTS);
+ pflags = htonl(SMFI_CURR_PROT);
+ (void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
+ (void) memcpy(data + MILTER_LEN_BYTES,
+ (char *) &fflags, MILTER_LEN_BYTES);
+ (void) memcpy(data + (MILTER_LEN_BYTES * 2),
+ (char *) &pflags, MILTER_LEN_BYTES);
+ (void) milter_write(m, SMFIC_OPTNEG, data, sizeof data,
+ m->mf_timeout[SMFTO_WRITE], e);
+
+ if (m->mf_state == SMFS_ERROR)
+ return -1;
+
+ response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e);
+ if (m->mf_state == SMFS_ERROR)
+ return -1;
+
+ if (rcmd != SMFIC_OPTNEG)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): returned %c instead of %c\n",
+ m->mf_name, rcmd, SMFIC_OPTNEG);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): returned %c instead of %c",
+ m->mf_name, rcmd, SMFIC_OPTNEG);
+ if (response != NULL)
+ free(response);
+ milter_error(m);
+ return -1;
+ }
+
+ /* Make sure we have enough bytes for the version */
+ if (response == NULL || rlen < MILTER_LEN_BYTES)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): did not return valid info\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): did not return valid info",
+ m->mf_name);
+ if (response != NULL)
+ free(response);
+ milter_error(m);
+ return -1;
+ }
+
+ /* extract information */
+ (void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
+
+ /* Now make sure we have enough for the feature bitmap */
+ if (rlen != MILTER_OPTLEN)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): did not return enough info\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): did not return enough info",
+ m->mf_name);
+ if (response != NULL)
+ free(response);
+ milter_error(m);
+ return -1;
+ }
+
+ (void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
+ MILTER_LEN_BYTES);
+ (void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
+ MILTER_LEN_BYTES);
+ free(response);
+ response = NULL;
+
+ m->mf_fvers = ntohl(fvers);
+ m->mf_fflags = ntohl(fflags);
+ m->mf_pflags = ntohl(pflags);
+
+ /* check for version compatibility */
+ if (m->mf_fvers == 1 ||
+ m->mf_fvers > SMFI_VERSION)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): version %lu != MTA milter version %d\n",
+ m->mf_name, m->mf_fvers, SMFI_VERSION);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): version %ld != MTA milter version %d",
+ m->mf_name, m->mf_fvers, SMFI_VERSION);
+ milter_error(m);
+ return -1;
+ }
+
+ /* check for filter feature mismatch */
+ if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ m->mf_name, m->mf_fflags,
+ (u_long) SMFI_CURR_ACTS);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ m->mf_name, m->mf_fflags,
+ (u_long) SMFI_CURR_ACTS);
+ milter_error(m);
+ return -1;
+ }
+
+ /* check for protocol feature mismatch */
+ if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ m->mf_name, m->mf_pflags,
+ (u_long) SMFI_CURR_PROT);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ m->mf_name, m->mf_pflags,
+ (u_long) SMFI_CURR_PROT);
+ milter_error(m);
+ return -1;
+ }
+
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): version %lu, fflags 0x%lx, pflags 0x%lx\n",
+ m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
+ return 0;
+}
+ /*
+** MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
+**
+** Reduce code duplication by putting these checks in one place
+**
+** Parameters:
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_per_connection_check(e)
+ ENVELOPE *e;
+{
+ int i;
+
+ /* see if we are done with any of the filters */
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ if (m->mf_state == SMFS_DONE)
+ milter_quit_filter(m, e);
+ }
+}
+ /*
+** MILTER_ERROR -- Put a milter filter into error state
+**
+** Parameters:
+** m -- the broken filter.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_error(m)
+ struct milter *m;
+{
+ /*
+ ** We could send a quit here but
+ ** we may have gotten here due to
+ ** an I/O error so we don't want
+ ** to try to make things worse.
+ */
+
+ if (m->mf_sock >= 0)
+ {
+ (void) close(m->mf_sock);
+ m->mf_sock = -1;
+ }
+ m->mf_state = SMFS_ERROR;
+}
+ /*
+** MILTER_HEADERS -- send headers to a single milter filter
+**
+** Parameters:
+** m -- current filter.
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+static char *
+milter_headers(m, e, state)
+ struct milter *m;
+ ENVELOPE *e;
+ char *state;
+{
+ char *response = NULL;
+ HDR *h;
+
+ for (h = e->e_header; h != NULL; h = h->h_link)
+ {
+ char *buf;
+ ssize_t s;
+
+ /* don't send over deleted headers */
+ if (h->h_value == NULL)
+ {
+ /* strip H_USER so not counted in milter_chgheader() */
+ h->h_flags &= ~H_USER;
+ continue;
+ }
+
+ /* skip auto-generated */
+ if (!bitset(H_USER, h->h_flags))
+ continue;
+
+ if (tTd(64, 10))
+ dprintf("milter_headers: %s: %s\n",
+ h->h_field, h->h_value);
+
+ s = strlen(h->h_field) + 1 +
+ strlen(h->h_value) + 1;
+ buf = (char *) xalloc(s);
+ snprintf(buf, s, "%s%c%s", h->h_field, '\0', h->h_value);
+
+ /* send it over */
+ response = milter_send_command(m, SMFIC_HEADER, buf,
+ s, e, state);
+ free(buf);
+ if (m->mf_state == SMFS_ERROR ||
+ m->mf_state == SMFS_DONE ||
+ *state != SMFIR_CONTINUE)
+ break;
+ }
+ return response;
+}
+ /*
+** MILTER_BODY -- send the body to a filter
+**
+** Parameters:
+** m -- current filter.
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+static char *
+milter_body(m, e, state)
+ struct milter *m;
+ ENVELOPE *e;
+ char *state;
+{
+ char bufchar = '\0';
+ char prevchar = '\0';
+ int c;
+ char *response = NULL;
+ char *bp;
+ char buf[MILTER_CHUNK_SIZE];
+
+ if (tTd(64, 10))
+ dprintf("milter_body\n");
+
+ if (bfrewind(e->e_dfp) < 0)
+ {
+ ExitStat = EX_IOERR;
+ *state = SMFIR_TEMPFAIL;
+ syserr("milter_body: %s/df%s: rewind error",
+ qid_printqueue(e->e_queuedir), e->e_id);
+ return NULL;
+ }
+
+ bp = buf;
+ while ((c = getc(e->e_dfp)) != EOF)
+ {
+ /* Change LF to CRLF */
+ if (c == '\n')
+ {
+ /* Not a CRLF already? */
+ if (prevchar != '\r')
+ {
+ /* Room for CR now? */
+ if (bp + 2 > &buf[sizeof buf])
+ {
+ /* No room, buffer LF */
+ bufchar = c;
+
+ /* and send CR now */
+ c = '\r';
+ }
+ else
+ {
+ /* Room to do it now */
+ *bp++ = '\r';
+ prevchar = '\r';
+ }
+ }
+ }
+ *bp++ = (char) c;
+ prevchar = c;
+ if (bp >= &buf[sizeof buf])
+ {
+ /* send chunk */
+ response = milter_send_command(m, SMFIC_BODY, buf,
+ bp - buf, e, state);
+ bp = buf;
+ if (bufchar != '\0')
+ {
+ *bp++ = bufchar;
+ bufchar = '\0';
+ prevchar = bufchar;
+ }
+ }
+ if (m->mf_state == SMFS_ERROR ||
+ m->mf_state == SMFS_DONE ||
+ *state != SMFIR_CONTINUE)
+ break;
+ }
+
+ /* check for read errors */
+ if (ferror(e->e_dfp))
+ {
+ ExitStat = EX_IOERR;
+ if (*state == SMFIR_CONTINUE ||
+ *state == SMFIR_ACCEPT)
+ {
+ *state = SMFIR_TEMPFAIL;
+ if (response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+ }
+ syserr("milter_body: %s/df%s: read error",
+ qid_printqueue(e->e_queuedir), e->e_id);
+ return response;
+ }
+
+ /* send last body chunk */
+ if (bp > buf &&
+ m->mf_state != SMFS_ERROR &&
+ m->mf_state != SMFS_DONE &&
+ *state == SMFIR_CONTINUE)
+ {
+ /* send chunk */
+ response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
+ e, state);
+ bp = buf;
+ }
+ return response;
+}
+
+/*
+** Actions
+*/
+
+ /*
+** MILTER_ADDHEADER -- Add the supplied header to the message
+**
+** Parameters:
+** response -- encoded form of header/value.
+** rlen -- length of response.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_addheader(response, rlen, e)
+ char *response;
+ ssize_t rlen;
+ ENVELOPE *e;
+{
+ char *val;
+
+ if (tTd(64, 10))
+ dprintf("milter_addheader: ");
+
+ /* sanity checks */
+ if (response == NULL)
+ {
+ if (tTd(64, 10))
+ dprintf("NULL response\n");
+ return;
+ }
+
+ if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
+ {
+ if (tTd(64, 10))
+ dprintf("didn't follow protocol (total len)\n");
+ return;
+ }
+
+ /* Find separating NUL */
+ val = response + strlen(response) + 1;
+
+ /* another sanity check */
+ if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
+ {
+ if (tTd(64, 10))
+ dprintf("didn't follow protocol (part len)\n");
+ return;
+ }
+
+ if (*response == '\0')
+ {
+ if (tTd(64, 10))
+ dprintf("empty field name\n");
+ return;
+ }
+
+ /* add to e_msgsize */
+ e->e_msgsize += strlen(response) + 2 + strlen(val);
+
+ if (tTd(64, 10))
+ dprintf("Add %s: %s\n", response, val);
+
+ addheader(newstr(response), val, H_USER, &e->e_header);
+}
+ /*
+** MILTER_CHANGEHEADER -- Change the supplied header in the message
+**
+** Parameters:
+** response -- encoded form of header/index/value.
+** rlen -- length of response.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_changeheader(response, rlen, e)
+ char *response;
+ ssize_t rlen;
+ ENVELOPE *e;
+{
+ mi_int32 i, index;
+ char *field, *val;
+ HDR *h;
+
+ if (tTd(64, 10))
+ dprintf("milter_changeheader: ");
+
+ /* sanity checks */
+ if (response == NULL)
+ {
+ if (tTd(64, 10))
+ dprintf("NULL response\n");
+ return;
+ }
+
+ if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
+ {
+ if (tTd(64, 10))
+ dprintf("didn't follow protocol (total len)\n");
+ return;
+ }
+
+ /* Find separating NUL */
+ (void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
+ index = ntohl(i);
+ field = response + MILTER_LEN_BYTES;
+ val = field + strlen(field) + 1;
+
+ /* another sanity check */
+ if (MILTER_LEN_BYTES + strlen(field) + 1 +
+ strlen(val) + 1 != (size_t) rlen)
+ {
+ if (tTd(64, 10))
+ dprintf("didn't follow protocol (part len)\n");
+ return;
+ }
+
+ if (*field == '\0')
+ {
+ if (tTd(64, 10))
+ dprintf("empty field name\n");
+ return;
+ }
+
+ for (h = e->e_header; h != NULL; h = h->h_link)
+ {
+ if (bitset(H_USER, h->h_flags) &&
+ strcasecmp(h->h_field, field) == 0 &&
+ --index <= 0)
+ break;
+ }
+
+ if (h == NULL)
+ {
+ if (*val == '\0')
+ {
+ if (tTd(64, 10))
+ dprintf("Delete (noop) %s:\n", field);
+ }
+ else
+ {
+ /* treat modify value with no existing header as add */
+ if (tTd(64, 10))
+ dprintf("Add %s: %s\n", field, val);
+
+ addheader(newstr(field), val, H_USER, &e->e_header);
+ }
+ return;
+ }
+
+ if (tTd(64, 10))
+ {
+ if (*val == '\0')
+ {
+ dprintf("Delete %s: %s\n", field,
+ h->h_value == NULL ? "<NULL>" : h->h_value);
+ }
+ else
+ {
+ dprintf("Change %s: from %s to %s\n",
+ field,
+ h->h_value == NULL ? "<NULL>" : h->h_value,
+ val);
+ }
+ }
+
+ if (h->h_value != NULL)
+ {
+ e->e_msgsize -= strlen(h->h_value);
+ free(h->h_value);
+ }
+
+ if (*val == '\0')
+ {
+ /* Remove "Field: " from message size */
+ e->e_msgsize -= strlen(h->h_field) + 2;
+ h->h_value = NULL;
+ }
+ else
+ {
+ h->h_value = newstr(val);
+ e->e_msgsize += strlen(h->h_value);
+ }
+}
+ /*
+** MILTER_ADDRCPT -- Add the supplied recipient to the message
+**
+** Parameters:
+** response -- encoded form of recipient address.
+** rlen -- length of response.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_addrcpt(response, rlen, e)
+ char *response;
+ ssize_t rlen;
+ ENVELOPE *e;
+{
+ if (tTd(64, 10))
+ dprintf("milter_addrcpt: ");
+
+ /* sanity checks */
+ if (response == NULL)
+ {
+ if (tTd(64, 10))
+ dprintf("NULL response\n");
+ return;
+ }
+
+ if (*response == '\0' ||
+ strlen(response) + 1 != (size_t) rlen)
+ {
+ if (tTd(64, 10))
+ dprintf("didn't follow protocol (total len %d != rlen %d)\n",
+ strlen(response), rlen -1);
+ return;
+ }
+
+ if (tTd(64, 10))
+ dprintf("%s\n", response);
+ (void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
+ return;
+}
+ /*
+** MILTER_DELRCPT -- Delete the supplied recipient from the message
+**
+** Parameters:
+** response -- encoded form of recipient address.
+** rlen -- length of response.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_delrcpt(response, rlen, e)
+ char *response;
+ ssize_t rlen;
+ ENVELOPE *e;
+{
+ if (tTd(64, 10))
+ dprintf("milter_delrcpt: ");
+
+ /* sanity checks */
+ if (response == NULL)
+ {
+ if (tTd(64, 10))
+ dprintf("NULL response\n");
+ return;
+ }
+
+ if (*response == '\0' ||
+ strlen(response) + 1 != (size_t) rlen)
+ {
+ if (tTd(64, 10))
+ dprintf("didn't follow protocol (total len)\n");
+ return;
+ }
+
+ if (tTd(64, 10))
+ dprintf("%s\n", response);
+ (void) removefromlist(response, &e->e_sendqueue, e);
+ return;
+}
+ /*
+** MILTER_REPLBODY -- Replace the current df file with new body
+**
+** Parameters:
+** response -- encoded form of new body.
+** rlen -- length of response.
+** newfilter -- if first time called by a new filter
+** e -- current envelope.
+**
+** Returns:
+** 0 upon success, -1 upon failure
+*/
+
+static int
+milter_replbody(response, rlen, newfilter, e)
+ char *response;
+ ssize_t rlen;
+ bool newfilter;
+ ENVELOPE *e;
+{
+ static char prevchar;
+ int i;
+
+ if (tTd(64, 10))
+ dprintf("milter_replbody\n");
+
+ /* If a new filter, reset previous character and truncate df */
+ if (newfilter)
+ {
+ off_t prevsize = 0;
+ char dfname[MAXPATHLEN];
+
+ (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+
+ /* Reset prevchar */
+ prevchar = '\0';
+
+ /* Get the current df information */
+ if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
+ {
+ int afd;
+ struct stat st;
+
+ afd = fileno(e->e_dfp);
+ if (afd > 0 && fstat(afd, &st) == 0)
+ prevsize = st.st_size;
+ }
+
+ /* truncate current df file */
+ if (bftruncate(e->e_dfp) < 0)
+ {
+ MILTER_DF_ERROR("milter_reopen_df: bftruncate %s: %s");
+ return -1;
+ }
+ else
+ {
+ if (prevsize > e->e_msgsize)
+ e->e_msgsize = 0;
+ else
+ e->e_msgsize -= prevsize;
+ }
+ }
+
+ if (response == NULL)
+ {
+ /* Flush the buffered '\r' */
+ if (prevchar == '\r')
+ {
+ (void) putc(prevchar, e->e_dfp);
+ e->e_msgsize++;
+ }
+ return 0;
+ }
+
+ for (i = 0; i < rlen; i++)
+ {
+ /* Buffered char from last chunk */
+ if (i == 0 && prevchar == '\r')
+ {
+ /* Not CRLF, output prevchar */
+ if (response[i] != '\n')
+ {
+ (void) putc(prevchar, e->e_dfp);
+ e->e_msgsize++;
+ }
+ prevchar = '\0';
+ }
+
+ /* Turn CRLF into LF */
+ if (response[i] == '\r')
+ {
+ /* check if at end of chunk */
+ if (i + 1 < rlen)
+ {
+ /* If LF, strip CR */
+ if (response[i + 1] == '\n')
+ i++;
+ }
+ else
+ {
+ /* check next chunk */
+ prevchar = '\r';
+ continue;
+ }
+ }
+ (void) putc(response[i], e->e_dfp);
+ e->e_msgsize++;
+ }
+ return 0;
+}
+
+/*
+** MTA callouts
+*/
+
+ /*
+** MILTER_INIT -- open and negotiate with all of the filters
+**
+** Parameters:
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** none
+*/
+
+/* ARGSUSED */
+void
+milter_init(e, state)
+ ENVELOPE *e;
+ char *state;
+{
+ int i;
+
+ if (tTd(64, 10))
+ dprintf("milter_init\n");
+
+ *state = SMFIR_CONTINUE;
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ m->mf_sock = milter_open(m, FALSE, e);
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(continue);
+ break;
+ }
+
+ if (m->mf_sock < 0 ||
+ milter_negotiate(m, e) < 0 ||
+ m->mf_state == SMFS_ERROR)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_init(%s): failed to %s\n",
+ m->mf_name,
+ m->mf_sock < 0 ? "open" : "negotiate");
+
+ /* if negotation failure, close socket */
+ if (m->mf_sock >= 0)
+ {
+ (void) close(m->mf_sock);
+ m->mf_sock = -1;
+ }
+ milter_error(m);
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(continue);
+ break;
+ }
+ }
+ }
+
+ /*
+ ** If something temp/perm failed with one of the filters,
+ ** we won't be using any of them, so clear any existing
+ ** connections.
+ */
+
+ if (*state != SMFIR_CONTINUE)
+ milter_quit(e);
+}
+ /*
+** MILTER_CONNECT -- send connection info to milter filters
+**
+** Parameters:
+** hostname -- hostname of remote machine.
+** addr -- address of remote machine.
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+char *
+milter_connect(hostname, addr, e, state)
+ char *hostname;
+ SOCKADDR addr;
+ ENVELOPE *e;
+ char *state;
+{
+ char family;
+ u_short port;
+ char *buf, *bp;
+ char *response;
+ char *sockinfo = NULL;
+ ssize_t s;
+# if NETINET6
+ char buf6[INET6_ADDRSTRLEN];
+# endif /* NETINET6 */
+
+ if (tTd(64, 10))
+ dprintf("milter_connect(%s)\n", hostname);
+
+ /* gather data */
+ switch (addr.sa.sa_family)
+ {
+# if NETUNIX
+ case AF_UNIX:
+ family = SMFIA_UNIX;
+ port = htons(0);
+ sockinfo = addr.sunix.sun_path;
+ break;
+# endif /* NETUNIX */
+
+# if NETINET
+ case AF_INET:
+ family = SMFIA_INET;
+ port = htons(addr.sin.sin_port);
+ sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ family = SMFIA_INET6;
+ port = htons(addr.sin6.sin6_port);
+ sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6,
+ sizeof buf6);
+ if (sockinfo == NULL)
+ sockinfo = "";
+ break;
+# endif /* NETINET6 */
+
+ default:
+ family = SMFIA_UNKNOWN;
+ break;
+ }
+
+ s = strlen(hostname) + 1 + sizeof(family);
+ if (family != SMFIA_UNKNOWN)
+ s += sizeof(port) + strlen(sockinfo) + 1;
+
+ buf = (char *)xalloc(s);
+ bp = buf;
+
+ /* put together data */
+ (void) memcpy(bp, hostname, strlen(hostname));
+ bp += strlen(hostname);
+ *bp++ = '\0';
+ (void) memcpy(bp, &family, sizeof family);
+ bp += sizeof family;
+ if (family != SMFIA_UNKNOWN)
+ {
+ (void) memcpy(bp, &port, sizeof port);
+ bp += sizeof port;
+
+ /* include trailing '\0' */
+ (void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
+ }
+
+ response = milter_command(SMFIC_CONNECT, buf, s,
+ MilterConnectMacros, e, state);
+ free(buf);
+
+ /*
+ ** If this message connection is done for,
+ ** close the filters.
+ */
+
+ if (*state != SMFIR_CONTINUE)
+ milter_quit(e);
+ else
+ milter_per_connection_check(e);
+
+ /*
+ ** SMFIR_REPLYCODE can't work with connect due to
+ ** the requirements of SMTP. Therefore, ignore the
+ ** reply code text but keep the state it would reflect.
+ */
+
+ if (*state == SMFIR_REPLYCODE)
+ {
+ if (response != NULL &&
+ *response == '4')
+ *state = SMFIR_TEMPFAIL;
+ else
+ *state = SMFIR_REJECT;
+ if (response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+ }
+ return response;
+}
+ /*
+** MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
+**
+** Parameters:
+** helo -- argument to SMTP HELO/EHLO command.
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+char *
+milter_helo(helo, e, state)
+ char *helo;
+ ENVELOPE *e;
+ char *state;
+{
+ char *response;
+
+ if (tTd(64, 10))
+ dprintf("milter_helo(%s)\n", helo);
+
+ response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
+ MilterHeloMacros, e, state);
+ milter_per_connection_check(e);
+ return response;
+}
+ /*
+** MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
+**
+** Parameters:
+** args -- SMTP MAIL command args (args[0] == sender).
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+char *
+milter_envfrom(args, e, state)
+ char **args;
+ ENVELOPE *e;
+ char *state;
+{
+ int i;
+ char *buf, *bp;
+ char *response;
+ ssize_t s;
+
+ if (tTd(64, 10))
+ {
+ dprintf("milter_envfrom:");
+ for (i = 0; args[i] != NULL; i++)
+ dprintf(" %s", args[i]);
+ dprintf("\n");
+ }
+
+ /* sanity check */
+ if (args[0] == NULL)
+ {
+ *state = SMFIR_REJECT;
+ return NULL;
+ }
+
+ /* new message, so ... */
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ switch (m->mf_state)
+ {
+ case SMFS_INMSG:
+ /* abort in message filters */
+ milter_abort_filter(m, e);
+ /* FALLTHROUGH */
+
+ case SMFS_DONE:
+ /* reset done filters */
+ m->mf_state = SMFS_OPEN;
+ break;
+ }
+ }
+
+ /* put together data */
+ s = 0;
+ for (i = 0; args[i] != NULL; i++)
+ s += strlen(args[i]) + 1;
+ buf = (char *)xalloc(s);
+ bp = buf;
+ for (i = 0; args[i] != NULL; i++)
+ {
+ (void) strlcpy(bp, args[i], s - (bp - buf));
+ bp += strlen(bp) + 1;
+ }
+
+ /* send it over */
+ response = milter_command(SMFIC_MAIL, buf, s,
+ MilterEnvFromMacros, e, state);
+ free(buf);
+
+ /*
+ ** If filter rejects/discards a per message command,
+ ** abort the other filters since we are done with the
+ ** current message.
+ */
+
+ MILTER_CHECK_DONE_MSG();
+ return response;
+}
+ /*
+** MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
+**
+** Parameters:
+** args -- SMTP MAIL command args (args[0] == recipient).
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+char *
+milter_envrcpt(args, e, state)
+ char **args;
+ ENVELOPE *e;
+ char *state;
+{
+ int i;
+ char *buf, *bp;
+ char *response;
+ ssize_t s;
+
+ if (tTd(64, 10))
+ {
+ dprintf("milter_envrcpt:");
+ for (i = 0; args[i] != NULL; i++)
+ dprintf(" %s", args[i]);
+ dprintf("\n");
+ }
+
+ /* sanity check */
+ if (args[0] == NULL)
+ {
+ *state = SMFIR_REJECT;
+ return NULL;
+ }
+
+ /* put together data */
+ s = 0;
+ for (i = 0; args[i] != NULL; i++)
+ s += strlen(args[i]) + 1;
+ buf = (char *)xalloc(s);
+ bp = buf;
+ for (i = 0; args[i] != NULL; i++)
+ {
+ (void) strlcpy(bp, args[i], s - (bp - buf));
+ bp += strlen(bp) + 1;
+ }
+
+ /* send it over */
+ response = milter_command(SMFIC_RCPT, buf, s,
+ MilterEnvRcptMacros, e, state);
+ free(buf);
+ return response;
+}
+ /*
+** MILTER_DATA -- send message headers/body and gather final message results
+**
+** Parameters:
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+**
+** Side effects:
+** - Uses e->e_dfp for access to the body
+** - Can call the various milter action routines to
+** modify the envelope or message.
+*/
+
+# define MILTER_CHECK_RESULTS() \
+ if (*state == SMFIR_ACCEPT || \
+ m->mf_state == SMFS_DONE || \
+ m->mf_state == SMFS_ERROR) \
+ { \
+ if (m->mf_state != SMFS_ERROR) \
+ m->mf_state = SMFS_DONE; \
+ continue; /* to next filter */ \
+ } \
+ if (*state != SMFIR_CONTINUE) \
+ { \
+ m->mf_state = SMFS_DONE; \
+ goto finishup; \
+ }
+
+char *
+milter_data(e, state)
+ ENVELOPE *e;
+ char *state;
+{
+ bool replbody = FALSE; /* milter_replbody() called? */
+ bool replfailed = FALSE; /* milter_replbody() failed? */
+ bool rewind = FALSE; /* rewind df file? */
+ bool dfopen = FALSE; /* df open for writing? */
+ bool newfilter; /* reset on each new filter */
+ char rcmd;
+ int i;
+ int save_errno;
+ char *response = NULL;
+ time_t eomsent;
+ ssize_t rlen;
+
+ if (tTd(64, 10))
+ dprintf("milter_data\n");
+
+ *state = SMFIR_CONTINUE;
+
+ /*
+ ** XXX: Should actually send body chunks to each filter
+ ** a chunk at a time instead of sending the whole body to
+ ** each filter in turn. However, only if the filters don't
+ ** change the body.
+ */
+
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ if (*state != SMFIR_CONTINUE &&
+ *state != SMFIR_ACCEPT)
+ {
+ /*
+ ** A previous filter has dealt with the message,
+ ** safe to stop processing the filters.
+ */
+
+ break;
+ }
+
+ /* Now reset state for later evaluation */
+ *state = SMFIR_CONTINUE;
+ newfilter = TRUE;
+
+ /* sanity checks */
+ if (m->mf_sock < 0 ||
+ (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
+ continue;
+
+ m->mf_state = SMFS_INMSG;
+
+ /* check if filter wants the headers */
+ if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
+ {
+ response = milter_headers(m, e, state);
+ MILTER_CHECK_RESULTS();
+ }
+
+ /* check if filter wants EOH */
+ if (!bitset(SMFIP_NOEOH, m->mf_pflags))
+ {
+ if (tTd(64, 10))
+ dprintf("milter_data: eoh\n");
+
+ /* send it over */
+ response = milter_send_command(m, SMFIC_EOH, NULL, 0,
+ e, state);
+ MILTER_CHECK_RESULTS();
+ }
+
+ /* check if filter wants the body */
+ if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
+ e->e_dfp != NULL)
+ {
+ rewind = TRUE;
+ response = milter_body(m, e, state);
+ MILTER_CHECK_RESULTS();
+ }
+
+ /* send the final body chunk */
+ (void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
+ m->mf_timeout[SMFTO_WRITE], e);
+
+ /* Get time EOM sent for timeout */
+ eomsent = curtime();
+
+ /* deal with the possibility of multiple responses */
+ while (*state == SMFIR_CONTINUE)
+ {
+ /* Check total timeout from EOM to final ACK/NAK */
+ if (m->mf_timeout[SMFTO_EOM] > 0 &&
+ curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
+ {
+ if (tTd(64, 5))
+ dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_data(%s): EOM ACK/NAK timeout\n",
+ m->mf_name);
+ milter_error(m);
+ MILTER_CHECK_ERROR(continue);
+ break;
+ }
+
+ response = milter_read(m, &rcmd, &rlen,
+ m->mf_timeout[SMFTO_READ], e);
+ if (m->mf_state == SMFS_ERROR)
+ break;
+
+ if (tTd(64, 10))
+ dprintf("milter_data(%s): state %c\n",
+ m->mf_name, (char) rcmd);
+
+ switch (rcmd)
+ {
+ case SMFIR_REPLYCODE:
+ MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
+ *state = rcmd;
+ m->mf_state = SMFS_DONE;
+ break;
+
+ case SMFIR_REJECT:
+ case SMFIR_DISCARD:
+ case SMFIR_TEMPFAIL:
+ *state = rcmd;
+ m->mf_state = SMFS_DONE;
+ break;
+
+ case SMFIR_CONTINUE:
+ case SMFIR_ACCEPT:
+ /* this filter is done with message */
+ if (replfailed)
+ *state = SMFIR_TEMPFAIL;
+ else
+ *state = SMFIR_ACCEPT;
+ m->mf_state = SMFS_DONE;
+ break;
+
+ case SMFIR_PROGRESS:
+ break;
+
+ case SMFIR_ADDHEADER:
+ if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "milter_data(%s): lied about adding headers, honoring request anyway",
+ m->mf_name);
+ }
+ milter_addheader(response, rlen, e);
+ break;
+
+ case SMFIR_CHGHEADER:
+ if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "milter_data(%s): lied about changing headers, honoring request anyway",
+ m->mf_name);
+ }
+ milter_changeheader(response, rlen, e);
+ break;
+
+ case SMFIR_ADDRCPT:
+ if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "milter_data(%s) lied about adding recipients, honoring request anyway",
+ m->mf_name);
+ }
+ milter_addrcpt(response, rlen, e);
+ break;
+
+ case SMFIR_DELRCPT:
+ if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "milter_data(%s): lied about removing recipients, honoring request anyway",
+ m->mf_name);
+ }
+ milter_delrcpt(response, rlen, e);
+ break;
+
+ case SMFIR_REPLBODY:
+ if (!bitset(SMFIF_MODBODY, m->mf_fflags))
+ {
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
+ m->mf_name);
+ replfailed = TRUE;
+ break;
+ }
+
+ /* already failed in attempt */
+ if (replfailed)
+ break;
+
+ if (!dfopen)
+ {
+ if (milter_reopen_df(e) < 0)
+ {
+ replfailed = TRUE;
+ break;
+ }
+ dfopen = TRUE;
+ rewind = TRUE;
+ }
+
+ if (milter_replbody(response, rlen,
+ newfilter, e) < 0)
+ replfailed = TRUE;
+ newfilter = FALSE;
+ replbody = TRUE;
+ break;
+
+ default:
+ /* Invalid response to command */
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_data(%s): returned bogus response %c",
+ m->mf_name, rcmd);
+ milter_error(m);
+ break;
+ }
+ if (rcmd != SMFIR_REPLYCODE &&
+ response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+
+ if (m->mf_state == SMFS_ERROR)
+ break;
+ }
+
+ if (replbody && !replfailed)
+ {
+ /* flush possible buffered character */
+ milter_replbody(NULL, 0, !replbody, e);
+ replbody = FALSE;
+ }
+
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(continue);
+ goto finishup;
+ }
+ }
+
+finishup:
+ /* leave things in the expected state if we touched it */
+ if (replfailed)
+ {
+ if (*state == SMFIR_CONTINUE ||
+ *state == SMFIR_ACCEPT)
+ {
+ *state = SMFIR_TEMPFAIL;
+ if (response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+ }
+
+ if (dfopen)
+ {
+ (void) fclose(e->e_dfp);
+ e->e_dfp = NULL;
+ e->e_flags &= ~EF_HAS_DF;
+ dfopen = FALSE;
+ }
+ rewind = FALSE;
+ }
+
+ if ((dfopen && milter_reset_df(e) < 0) ||
+ (rewind && bfrewind(e->e_dfp) < 0))
+ {
+ save_errno = errno;
+ ExitStat = EX_IOERR;
+
+ /*
+ ** If filter told us to keep message but we had
+ ** an error, we can't really keep it, tempfail it.
+ */
+
+ if (*state == SMFIR_CONTINUE ||
+ *state == SMFIR_ACCEPT)
+ {
+ *state = SMFIR_TEMPFAIL;
+ if (response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+ }
+
+ errno = save_errno;
+ syserr("milter_data: %s/df%s: read error",
+ qid_printqueue(e->e_queuedir), e->e_id);
+ }
+ MILTER_CHECK_DONE_MSG();
+ return response;
+}
+ /*
+** MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
+**
+** Parameters:
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+void
+milter_quit(e)
+ ENVELOPE *e;
+{
+ int i;
+
+ if (tTd(64, 10))
+ dprintf("milter_quit\n");
+
+ for (i = 0; InputFilters[i] != NULL; i++)
+ milter_quit_filter(InputFilters[i], e);
+}
+ /*
+** MILTER_ABORT -- informs the filter(s) that we are aborting current message
+**
+** Parameters:
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+void
+milter_abort(e)
+ ENVELOPE *e;
+{
+ int i;
+
+ if (tTd(64, 10))
+ dprintf("milter_abort\n");
+
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ /* sanity checks */
+ if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG)
+ continue;
+
+ milter_abort_filter(m, e);
+ }
+}
+#endif /* _FFR_MILTER */
diff --git a/contrib/sendmail/src/sfsasl.c b/contrib/sendmail/src/sfsasl.c
new file mode 100644
index 0000000000000..43a65d198b0dd
--- /dev/null
+++ b/contrib/sendmail/src/sfsasl.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: sfsasl.c,v 8.17.4.7 2000/07/18 18:44:51 gshapiro Exp $";
+#endif /* ! lint */
+
+#if SFIO
+# include <sfio/stdio.h>
+#endif /* SFIO */
+
+#include <stdlib.h>
+#include <sendmail.h>
+
+#if SASL && SFIO
+/*
+** SASL
+*/
+
+# include <sasl.h>
+# include "sfsasl.h"
+
+static ssize_t
+sasl_read(f, buf, size, disc)
+ Sfio_t *f;
+ Void_t *buf;
+ size_t size;
+ Sfdisc_t *disc;
+{
+ int len, result;
+ char *outbuf;
+ unsigned int outlen;
+ Sasldisc_t *sd = (Sasldisc_t *) disc;
+
+ len = sfrd(f, buf, size, disc);
+
+ if (len <= 0)
+ return len;
+
+ result = sasl_decode(sd->conn, buf, len, &outbuf, &outlen);
+
+ if (result != SASL_OK)
+ {
+ /* eventually, we'll want an exception here */
+ return -1;
+ }
+
+ if (outbuf != NULL)
+ {
+ (void)memcpy(buf, outbuf, outlen);
+ free(outbuf);
+ }
+ return outlen;
+}
+
+static ssize_t
+sasl_write(f, buf, size, disc)
+ Sfio_t *f;
+ const Void_t *buf;
+ size_t size;
+ Sfdisc_t *disc;
+{
+ int result;
+ char *outbuf;
+ unsigned int outlen;
+ Sasldisc_t *sd = (Sasldisc_t *) disc;
+
+ result = sasl_encode(sd->conn, buf, size, &outbuf, &outlen);
+
+ if (result != SASL_OK)
+ {
+ /* eventually, we'll want an exception here */
+ return -1;
+ }
+
+ if (outbuf != NULL)
+ {
+ sfwr(f, outbuf, outlen, disc);
+ free(outbuf);
+ }
+ return size;
+}
+
+int
+sfdcsasl(fin, fout, conn)
+ Sfio_t *fin;
+ Sfio_t *fout;
+ sasl_conn_t *conn;
+{
+ Sasldisc_t *saslin, *saslout;
+
+ if (conn == NULL)
+ {
+ /* no need to do anything */
+ return 0;
+ }
+
+ if ((saslin = (Sasldisc_t *) malloc(sizeof(Sasldisc_t))) == NULL)
+ return -1;
+ if ((saslout = (Sasldisc_t *) malloc(sizeof(Sasldisc_t))) == NULL)
+ {
+ free(saslin);
+ return -1;
+ }
+
+ saslin->disc.readf = sasl_read;
+ saslin->disc.writef = sasl_write;
+ saslin->disc.seekf = NULL;
+ saslin->disc.exceptf = NULL;
+
+ saslout->disc.readf = sasl_read;
+ saslout->disc.writef = sasl_write;
+ saslout->disc.seekf = NULL;
+ saslout->disc.exceptf = NULL;
+
+ saslin->conn = conn;
+ saslout->conn = conn;
+
+ if (sfdisc(fin, (Sfdisc_t *) saslin) != (Sfdisc_t *) saslin ||
+ sfdisc(fout, (Sfdisc_t *) saslout) != (Sfdisc_t *) saslout)
+ {
+ free(saslin);
+ free(saslout);
+ return -1;
+ }
+ return 0;
+}
+#endif /* SASL && SFIO */
+
+#if STARTTLS && (SFIO || _FFR_TLS_TOREK)
+/*
+** STARTTLS
+*/
+
+# include "sfsasl.h"
+# include <openssl/err.h>
+
+static ssize_t
+# if SFIO
+tls_read(f, buf, size, disc)
+ Sfio_t *f;
+ Void_t *buf;
+ size_t size;
+ Sfdisc_t *disc;
+# else /* SFIO */
+tls_read(disc, buf, size)
+ void *disc;
+ void *buf;
+ size_t size;
+# endif /* SFIO */
+{
+ int r;
+ Tlsdisc_t *sd;
+
+ /* Cast back to correct type */
+ sd = (Tlsdisc_t *) disc;
+
+ r = SSL_read(sd->con, (char *) buf, size);
+ if (r < 0 && LogLevel > 7)
+ {
+ char *err;
+
+ err = NULL;
+ switch (SSL_get_error(sd->con, r))
+ {
+ case SSL_ERROR_NONE:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ err = "write W BLOCK";
+ break;
+ case SSL_ERROR_WANT_READ:
+ err = "write R BLOCK";
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ err = "write X BLOCK";
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ break;
+ case SSL_ERROR_SYSCALL:
+ err = "syscall error";
+/*
+ get_last_socket_error());
+*/
+ break;
+ case SSL_ERROR_SSL:
+ err = "generic SSL error";
+ break;
+ }
+ if (err != NULL)
+ sm_syslog(LOG_WARNING, NOQID, "TLS: read error: %s",
+ err);
+ }
+ return r;
+}
+
+static ssize_t
+# if SFIO
+tls_write(f, buf, size, disc)
+ Sfio_t *f;
+ const Void_t *buf;
+ size_t size;
+ Sfdisc_t *disc;
+# else /* SFIO */
+tls_write(disc, buf, size)
+ void *disc;
+ const void *buf;
+ size_t size;
+# endif /* SFIO */
+{
+ int r;
+ Tlsdisc_t *sd;
+
+ /* Cast back to correct type */
+ sd = (Tlsdisc_t *) disc;
+
+ r = SSL_write(sd->con, (char *)buf, size);
+ if (r < 0 && LogLevel > 7)
+ {
+ char *err;
+
+ err = NULL;
+ switch (SSL_get_error(sd->con, r))
+ {
+ case SSL_ERROR_NONE:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ err = "write W BLOCK";
+ break;
+ case SSL_ERROR_WANT_READ:
+ err = "write R BLOCK";
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ err = "write X BLOCK";
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ break;
+ case SSL_ERROR_SYSCALL:
+ err = "syscall error";
+/*
+ get_last_socket_error());
+*/
+ break;
+ case SSL_ERROR_SSL:
+ err = "generic SSL error";
+/*
+ ERR_GET_REASON(ERR_peek_error()));
+*/
+ break;
+ }
+ if (err != NULL)
+ sm_syslog(LOG_WARNING, NOQID, "TLS: write error: %s",
+ err);
+ }
+ return r;
+}
+
+# if !SFIO
+static int
+tls_close(cookie)
+ void *cookie;
+{
+ int retval = 0;
+ Tlsdisc_t *tc;
+
+ /* Cast back to correct type */
+ tc = (Tlsdisc_t *)cookie;
+
+ if (tc->fp != NULL)
+ {
+ retval = fclose(tc->fp);
+ tc->fp = NULL;
+ }
+
+ free(tc);
+ return retval;
+}
+# endif /* !SFIO */
+
+int
+sfdctls(fin, fout, con)
+# if SFIO
+ Sfio_t *fin;
+ Sfio_t *fout;
+# else /* SFIO */
+ FILE **fin;
+ FILE **fout;
+# endif /* SFIO */
+ SSL *con;
+{
+ Tlsdisc_t *tlsin, *tlsout;
+# if !SFIO
+ FILE *fp;
+# endif /* !SFIO */
+
+ if (con == NULL)
+ return 0;
+
+ if ((tlsin = (Tlsdisc_t *) malloc(sizeof(Tlsdisc_t))) == NULL)
+ return -1;
+ if ((tlsout = (Tlsdisc_t *) malloc(sizeof(Tlsdisc_t))) == NULL)
+ {
+ free(tlsin);
+ return -1;
+ }
+
+# if SFIO
+ tlsin->disc.readf = tls_read;
+ tlsin->disc.writef = tls_write;
+ tlsin->disc.seekf = NULL;
+ tlsin->disc.exceptf = NULL;
+ tlsin->con = con;
+
+ tlsout->disc.readf = tls_read;
+ tlsout->disc.writef = tls_write;
+ tlsout->disc.seekf = NULL;
+ tlsout->disc.exceptf = NULL;
+ tlsout->con = con;
+
+ SSL_set_rfd(con, fileno(fin)); /* fileno or sffileno? XXX */
+ SSL_set_wfd(con, fileno(fout));
+ if (sfdisc(fin, (Sfdisc_t *) tlsin) != (Sfdisc_t *) tlsin ||
+ sfdisc(fout, (Sfdisc_t *) tlsout) != (Sfdisc_t *) tlsout)
+ {
+ free(tlsin);
+ free(tlsout);
+ return -1;
+ }
+# else /* SFIO */
+ tlsin->fp = *fin;
+ tlsin->con = con;
+ fp = funopen(tlsin, tls_read, tls_write, NULL, tls_close);
+ if (fp == NULL)
+ {
+ free(tlsin);
+ return -1;
+ }
+ *fin = fp;
+
+ tlsout->fp = *fout;
+ tlsout->con = con;
+ fp = funopen(tlsout, tls_read, tls_write, NULL, tls_close);
+ if (fp == NULL)
+ {
+ FILE *save;
+
+ /* Hack: Don't close underlying fp */
+ save = tlsin->fp;
+ tlsin->fp = NULL;
+ fclose(*fin);
+ *fin = save;
+ free(tlsout);
+ return -1;
+ }
+ *fout = fp;
+ SSL_set_rfd(con, fileno(tlsin->fp));
+ SSL_set_wfd(con, fileno(tlsout->fp));
+# endif /* SFIO */
+ return 0;
+}
+#endif /* STARTTLS && (SFIO || _FFR_TLS_TOREK) */
diff --git a/contrib/sendmail/src/sfsasl.h b/contrib/sendmail/src/sfsasl.h
new file mode 100644
index 0000000000000..b276e27c03bd7
--- /dev/null
+++ b/contrib/sendmail/src/sfsasl.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1999, 2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * $Id: sfsasl.h,v 8.13.4.4 2000/07/18 18:44:51 gshapiro Exp $"
+ */
+
+#ifndef SFSASL_H
+# define SFSASL_H
+
+# if SFIO
+# include <sfio.h>
+# endif /* SFIO */
+
+# if SASL
+# if SFIO
+
+/* sf discipline to add sasl */
+typedef struct _sasldisc
+{
+ Sfdisc_t disc;
+ sasl_conn_t *conn;
+} Sasldisc_t;
+
+extern int sfdcsasl __P((Sfio_t *, Sfio_t *, sasl_conn_t *));
+
+# endif /* SFIO */
+# endif /* SASL */
+
+# if STARTTLS
+# if SFIO
+
+/* sf discipline to add tls */
+typedef struct _tlsdisc
+{
+ Sfdisc_t disc;
+ SSL *con;
+} Tlsdisc_t;
+
+extern int sfdctls __P((Sfio_t *, Sfio_t *, SSL *));
+
+# else /* SFIO */
+# if _FFR_TLS_TOREK
+
+typedef struct tls_conn
+{
+ FILE *fp; /* original FILE * */
+ SSL *con; /* SSL context */
+} Tlsdisc_t;
+
+extern int sfdctls __P((FILE **, FILE **, SSL *));
+
+# endif /* _FFR_TLS_TOREK */
+# endif /* SFIO */
+# endif /* STARTTLS */
+#endif /* ! SFSASL_H */
diff --git a/contrib/sendmail/src/shmticklib.c b/contrib/sendmail/src/shmticklib.c
new file mode 100644
index 0000000000000..a3e1ad5602671
--- /dev/null
+++ b/contrib/sendmail/src/shmticklib.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: shmticklib.c,v 8.6 2000/02/26 01:32:27 gshapiro Exp $";
+#endif /* ! lint */
+
+#if _FFR_SHM_STATUS
+# if SFIO
+# include <sfio/stdio.h>
+# else /* !SFIO */
+# include <stdio.h>
+# endif /* SFIO */
+# include <sys/types.h>
+# include <sys/ipc.h>
+# include <sys/shm.h>
+
+# include "statusd_shm.h"
+
+ /*
+** SHMTICK -- increment a shared memory variable
+**
+** Parameters:
+** inc_me -- identity of shared memory segment
+** what -- which variable to increment
+**
+** Returns:
+** none
+*/
+
+void
+shmtick(inc_me, what)
+ int inc_me;
+ int what;
+{
+ static int shmid = -1;
+ static STATUSD_SHM *sp = (STATUSD_SHM *)-1;
+ static unsigned int cookie = 0;
+
+ if (shmid < 0)
+ {
+ int size = sizeof(STATUSD_SHM);
+
+ shmid = shmget(STATUSD_SHM_KEY, size, 0);
+ if (shmid < 0)
+ return;
+ }
+ if ((unsigned long *)sp == (unsigned long *)-1)
+ {
+ sp = (STATUSD_SHM *)shmat(shmid, NULL, 0);
+ if ((unsigned long *)sp == (unsigned long *)-1)
+ return;
+ }
+ if (sp->magic != STATUSD_MAGIC)
+ {
+ /*
+ ** possible race condition, wait for
+ ** statusd to initialize.
+ */
+
+ return;
+ }
+ if (what >= STATUSD_LONGS)
+ what = STATUSD_LONGS - 1;
+ if (inc_me >= STATUSD_LONGS)
+ inc_me = STATUSD_LONGS - 1;
+
+ if (sp->ul[STATUSD_COOKIE] != cookie)
+ {
+ cookie = sp->ul[STATUSD_COOKIE];
+ ++(sp->ul[inc_me]);
+ }
+ ++(sp->ul[what]);
+}
+#endif /* _FFR_SHM_STATUS */
diff --git a/contrib/sendmail/src/statusd_shm.h b/contrib/sendmail/src/statusd_shm.h
new file mode 100644
index 0000000000000..c48b2fd782c84
--- /dev/null
+++ b/contrib/sendmail/src/statusd_shm.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * $Id: statusd_shm.h,v 8.4 1999/05/18 08:00:04 gshapiro Exp $
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+/*
+** The shared memory part of statusd.
+**
+** Attach to STATUSD_SHM_KEY and update the counter appropriate
+** for your type of service.
+**
+*/
+
+#define STATUSD_MAGIC 110946
+#define STATUSD_SHM_KEY (key_t)(13)
+#define STATUSD_LONGS (2)
+
+typedef struct {
+ unsigned long magic;
+ unsigned long ul[STATUSD_LONGS];
+} STATUSD_SHM;
+
+/*
+** Offsets into ul[]. The appropriate program
+** increments these as appropriate.
+*/
+
+#define STATUSD_COOKIE (0) /* reregister cookie */
+
+/* sendmail */
+#define STATUSD_SM_NSENDMAIL (1) /* how many running */
+
+extern void shmtick __P((int, int));
+
diff --git a/contrib/sendmail/src/timers.c b/contrib/sendmail/src/timers.c
new file mode 100644
index 0000000000000..74d0ccf78ee01
--- /dev/null
+++ b/contrib/sendmail/src/timers.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: timers.c,v 8.13 1999/11/23 07:22:28 gshapiro Exp $";
+#endif /* ! lint */
+
+#if _FFR_TIMERS
+# include <sys/types.h>
+# include <sys/time.h>
+# include "sendmail.h"
+# include <sys/resource.h> /* Must be after sendmail.h for NCR MP-RAS */
+
+static TIMER BaseTimer; /* current baseline */
+static int NTimers; /* current pointer into stack */
+static TIMER *TimerStack[MAXTIMERSTACK];
+
+static void
+# ifdef __STDC__
+warntimer(const char *msg, ...)
+# else /* __STDC__ */
+warntimer(msg, va_alist)
+ const char *msg;
+ va_dcl
+# endif /* __STDC__ */
+{
+ char buf[MAXLINE];
+ VA_LOCAL_DECL
+
+# if 0
+ if (!tTd(98, 30))
+ return;
+# endif /* 0 */
+ VA_START(msg);
+ vsnprintf(buf, sizeof buf, msg, ap);
+ VA_END;
+ sm_syslog(LOG_NOTICE, CurEnv->e_id, "%s; e_timers=0x%lx",
+ buf, (u_long) &CurEnv->e_timers);
+}
+
+static void
+zerotimer(ptimer)
+ TIMER *ptimer;
+{
+ memset(ptimer, '\0', sizeof *ptimer);
+}
+
+static void
+addtimer(ta, tb)
+ TIMER *ta;
+ TIMER *tb;
+{
+ tb->ti_wall_sec += ta->ti_wall_sec;
+ tb->ti_wall_usec += ta->ti_wall_usec;
+ if (tb->ti_wall_usec > 1000000)
+ {
+ tb->ti_wall_sec++;
+ tb->ti_wall_usec -= 1000000;
+ }
+ tb->ti_cpu_sec += ta->ti_cpu_sec;
+ tb->ti_cpu_usec += ta->ti_cpu_usec;
+ if (tb->ti_cpu_usec > 1000000)
+ {
+ tb->ti_cpu_sec++;
+ tb->ti_cpu_usec -= 1000000;
+ }
+}
+
+static void
+subtimer(ta, tb)
+ TIMER *ta;
+ TIMER *tb;
+{
+ tb->ti_wall_sec -= ta->ti_wall_sec;
+ tb->ti_wall_usec -= ta->ti_wall_usec;
+ if (tb->ti_wall_usec < 0)
+ {
+ tb->ti_wall_sec--;
+ tb->ti_wall_usec += 1000000;
+ }
+ tb->ti_cpu_sec -= ta->ti_cpu_sec;
+ tb->ti_cpu_usec -= ta->ti_cpu_usec;
+ if (tb->ti_cpu_usec < 0)
+ {
+ tb->ti_cpu_sec--;
+ tb->ti_cpu_usec += 1000000;
+ }
+}
+
+static int
+getcurtimer(ptimer)
+ TIMER *ptimer;
+{
+ struct rusage ru;
+ struct timeval now;
+
+ if (getrusage(RUSAGE_SELF, &ru) < 0 || gettimeofday(&now, NULL) < 0)
+ return -1;
+ ptimer->ti_wall_sec = now.tv_sec;
+ ptimer->ti_wall_usec = now.tv_usec;
+ ptimer->ti_cpu_sec = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+ ptimer->ti_cpu_usec = ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+ if (ptimer->ti_cpu_usec > 1000000)
+ {
+ ptimer->ti_cpu_sec++;
+ ptimer->ti_cpu_usec -= 1000000;
+ }
+ return 0;
+}
+
+static void
+getinctimer(ptimer)
+ TIMER *ptimer;
+{
+ TIMER cur;
+
+ if (getcurtimer(&cur) < 0)
+ {
+ zerotimer(ptimer);
+ return;
+ }
+ if (BaseTimer.ti_wall_sec == 0)
+ {
+ /* first call */
+ memset(ptimer, '\0', sizeof *ptimer);
+ }
+ else
+ {
+ *ptimer = cur;
+ subtimer(&BaseTimer, ptimer);
+ }
+ BaseTimer = cur;
+}
+
+void
+flushtimers()
+{
+ NTimers = 0;
+ (void) getcurtimer(&BaseTimer);
+}
+
+void
+pushtimer(ptimer)
+ TIMER *ptimer;
+{
+ int i;
+ int save_errno = errno;
+ TIMER incr;
+
+ /* find how much time has changed since last call */
+ getinctimer(&incr);
+
+ /* add that into the old timers */
+ i = NTimers;
+ if (i > MAXTIMERSTACK)
+ i = MAXTIMERSTACK;
+ while (--i >= 0)
+ {
+ addtimer(&incr, TimerStack[i]);
+ if (TimerStack[i] == ptimer)
+ {
+ warntimer("Timer@0x%lx already on stack, index=%d, NTimers=%d",
+ (u_long) ptimer, i, NTimers);
+ errno = save_errno;
+ return;
+ }
+ }
+ errno = save_errno;
+
+ /* handle stack overflow */
+ if (NTimers >= MAXTIMERSTACK)
+ return;
+
+ /* now add the timer to the stack */
+ TimerStack[NTimers++] = ptimer;
+}
+
+void
+poptimer(ptimer)
+ TIMER *ptimer;
+{
+ int i;
+ int save_errno = errno;
+ TIMER incr;
+
+ /* find how much time has changed since last call */
+ getinctimer(&incr);
+
+ /* add that into the old timers */
+ i = NTimers;
+ if (i > MAXTIMERSTACK)
+ i = MAXTIMERSTACK;
+ while (--i >= 0)
+ addtimer(&incr, TimerStack[i]);
+
+ /* pop back to this timer */
+ for (i = 0; i < NTimers; i++)
+ if (TimerStack[i] == ptimer)
+ break;
+ if (i != NTimers - 1)
+ warntimer("poptimer: odd pop (timer=0x%lx, index=%d, NTimers=%d)",
+ (u_long) ptimer, i, NTimers);
+ NTimers = i;
+
+ /* clean up and return */
+ errno = save_errno;
+}
+
+char *
+strtimer(ptimer)
+ TIMER *ptimer;
+{
+ static char buf[40];
+
+ snprintf(buf, sizeof buf, "%ld.%06ldr/%ld.%06ldc",
+ ptimer->ti_wall_sec, ptimer->ti_wall_usec,
+ ptimer->ti_cpu_sec, ptimer->ti_cpu_usec);
+ return buf;
+}
+#endif /* _FFR_TIMERS */
diff --git a/contrib/sendmail/src/timers.h b/contrib/sendmail/src/timers.h
new file mode 100644
index 0000000000000..e86b6ec3d1327
--- /dev/null
+++ b/contrib/sendmail/src/timers.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * $Id: timers.h,v 8.4 1999/11/04 19:31:26 ca Exp $
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef TIMERS_H
+#define TIMERS_H 1
+
+#define MAXTIMERSTACK 20 /* maximum timer depth */
+
+#define TIMER struct _timer
+
+TIMER
+{
+ long ti_wall_sec; /* wall clock seconds */
+ long ti_wall_usec; /* ... microseconds */
+ long ti_cpu_sec; /* cpu time seconds */
+ long ti_cpu_usec; /* ... microseconds */
+};
+
+extern void pushtimer __P((TIMER *));
+extern void poptimer __P((TIMER *));
+extern char *strtimer __P((TIMER *));
+#endif /* TIMERS_H */