summaryrefslogtreecommitdiff
path: root/usr/src/cmd/sendmail/libmilter
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/sendmail/libmilter
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/sendmail/libmilter')
-rw-r--r--usr/src/cmd/sendmail/libmilter/Makefile60
-rw-r--r--usr/src/cmd/sendmail/libmilter/Makefile.com66
-rw-r--r--usr/src/cmd/sendmail/libmilter/README420
-rw-r--r--usr/src/cmd/sendmail/libmilter/comm.c362
-rw-r--r--usr/src/cmd/sendmail/libmilter/engine.c1252
-rw-r--r--usr/src/cmd/sendmail/libmilter/handler.c68
-rw-r--r--usr/src/cmd/sendmail/libmilter/i386/Makefile29
-rw-r--r--usr/src/cmd/sendmail/libmilter/libmilter.h189
-rw-r--r--usr/src/cmd/sendmail/libmilter/listener.c955
-rw-r--r--usr/src/cmd/sendmail/libmilter/llib-lmilter32
-rw-r--r--usr/src/cmd/sendmail/libmilter/main.c245
-rw-r--r--usr/src/cmd/sendmail/libmilter/signal.c223
-rw-r--r--usr/src/cmd/sendmail/libmilter/sm_gethost.c150
-rw-r--r--usr/src/cmd/sendmail/libmilter/smfi.c650
-rw-r--r--usr/src/cmd/sendmail/libmilter/sparc/Makefile29
15 files changed, 4730 insertions, 0 deletions
diff --git a/usr/src/cmd/sendmail/libmilter/Makefile b/usr/src/cmd/sendmail/libmilter/Makefile
new file mode 100644
index 0000000000..cf41698dbe
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/Makefile
@@ -0,0 +1,60 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include $(SRC)/lib/Makefile.lib
+
+HDRS = mfapi.h mfdef.h
+HDRDIR = ../include/libmilter
+ROOTHDRDIR = $(ROOT)/usr/include/libmilter
+
+SUBDIRS = $(MACH)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber lint: $(SUBDIRS)
+install: $(SUBDIRS) $(ROOTHDRDIR)/README
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(ROOTHDRDIR)/README%: README%
+ $(INS.file)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/cmd/sendmail/libmilter/Makefile.com b/usr/src/cmd/sendmail/libmilter/Makefile.com
new file mode 100644
index 0000000000..c4ac692bfc
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/Makefile.com
@@ -0,0 +1,66 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+LIBRARY= libmilter.a
+VERS= .1
+LOCOBJS= main.o engine.o listener.o handler.o comm.o smfi.o signal.o \
+ sm_gethost.o
+REMOBJS= errstring.o strl.o
+OBJECTS= $(LOCOBJS) $(REMOBJS)
+SENDMAIL= $(SRC)/cmd/sendmail
+
+include $(SENDMAIL)/Makefile.cmd
+include $(SRC)/lib/Makefile.lib
+
+REMDIR= $(SENDMAIL)/libsm
+SRCDIR= $(SENDMAIL)/libmilter
+
+SRCS= $(LOCOBJS:%.o=$(SRCDIR)/%.c) $(REMOBJS:%.o=$(REMDIR)/%.c)
+
+INCPATH= -I$(SENDMAIL)/src -I$(SENDMAIL)/include
+ENVDEF= $(RLS_DEF) -DMILTER -DNETINET6 -DNOT_SENDMAIL \
+ -Dsm_snprintf=snprintf -D_REENTRANT -D_FFR_MILTER_ROOT_UNSAFE
+CPPFLAGS += $(INCPATH) $(ENVDEF)
+
+LIBS= $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc -lsocket -lnsl
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all .WAIT $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
+
+lint: lintcheck
+
+include $(SRC)/lib/Makefile.targ
+
+pics/%.o: $(REMDIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/cmd/sendmail/libmilter/README b/usr/src/cmd/sendmail/libmilter/README
new file mode 100644
index 0000000000..4079c4f9ef
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/README
@@ -0,0 +1,420 @@
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+The sendmail Mail Filter API (Milter) is designed to allow third-party
+programs access to mail messages as they are being processed in order to
+filter meta-information and content.
+
+This README file describes the steps needed to compile and run a filter,
+through reference to a sample filter which is attached at the end of this
+file.
+
++----------------+
+| SECURITY HINTS |
++----------------+
+
+Note: we strongly recommend not to run any milter as root. Libmilter
+does not need root access to communicate with sendmail. It is a
+good security practice to run a program only with root privileges
+if really necessary. A milter should probably check first whether
+it runs as root and refuse to start in that case. libmilter will
+not unlink a socket when running as root.
+
++-------------------+
+| BUILDING A FILTER |
++-------------------+
+
+The following command presumes that the sample code from the end of this
+README is saved to a file named 'sample.c'.
+
+ cc -D_REENTRANT -o sample sample.c -lmilter
+
+Filters must be thread-safe!
+
+Note that since filters use threads, it may be necessary to alter per
+process limits in your filter. For example, you might look at using
+setrlimit() to increase the number of open file descriptors if your filter
+is going to be busy.
+
+
++----------------------------------------+
+| SPECIFYING FILTERS IN SENDMAIL CONFIGS |
++----------------------------------------+
+
+Filters are specified with a key letter ``X'' (for ``eXternal'').
+
+For example:
+
+ Xfilter1, S=local:/var/run/f1.sock, F=R
+ Xfilter2, S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m
+ Xfilter3, S=inet:3333@localhost
+
+specifies three filters. Filters can be specified in your .mc file using
+the following:
+
+ INPUT_MAIL_FILTER(`filter1', `S=local:/var/run/f1.sock, F=R')
+ INPUT_MAIL_FILTER(`filter2', `S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m')
+ INPUT_MAIL_FILTER(`filter3', `S=inet:3333@localhost')
+
+The first attaches to a Unix-domain socket in the /var/run directory; the
+second uses an IPv6 socket on port 999 of localhost, and the third uses an
+IPv4 socket on port 3333 of localhost. The current flags (F=) are:
+
+ R Reject connection if filter unavailable
+ T Temporary fail connection if filter unavailable
+
+If neither F=R nor F=T is specified, the message is passed through sendmail
+in case of filter errors as if the failing filters were not present.
+
+Finally, you can override the default timeouts used by sendmail when
+talking to the filters using the T= equate. There are four fields inside
+of the T= equate:
+
+Letter Meaning
+ C Timeout for connecting to a filter (if 0, use system timeout)
+ S Timeout for sending information from the MTA to a filter
+ R Timeout for reading reply from the filter
+ E Overall timeout between sending end-of-message to filter
+ and waiting for the final acknowledgment
+
+Note the separator between each is a ';' as a ',' already separates equates
+and therefore can't separate timeouts. The default values (if not set in
+the config) are:
+
+T=C:5m;S:10s;R:10s;E:5m
+
+where 's' is seconds and 'm' is minutes.
+
+Which filters are invoked and their sequencing is handled by the
+InputMailFilters option. Note: if InputMailFilters is not defined no filters
+will be used.
+
+ O InputMailFilters=filter1, filter2, filter3
+
+This is is set automatically according to the order of the
+INPUT_MAIL_FILTER commands in your .mc file. Alternatively, you can
+reset its value by setting confINPUT_MAIL_FILTERS in your .mc file.
+This options causes the three filters to be called in the same order
+they were specified. It allows for possible future filtering on output
+(although this is not intended for this release).
+
+Also note that a filter can be defined without adding it to the input
+filter list by using MAIL_FILTER() instead of INPUT_MAIL_FILTER() in your
+.mc file.
+
+To test sendmail with the sample filter, the following might be added (in
+the appropriate locations) to your .mc file:
+
+ INPUT_MAIL_FILTER(`sample', `S=local:/var/run/f1.sock')
+
+
++------------------+
+| TESTING A FILTER |
++------------------+
+
+Once you have compiled a filter, modified your .mc file and restarted
+the sendmail process, you will want to test that the filter performs as
+intended.
+
+The sample filter takes one argument -p, which indicates the local port
+on which to create a listening socket for the filter. Maintaining
+consistency with the suggested options for sendmail.cf, this would be the
+UNIX domain socket located in /var/run/f1.sock.
+
+ % ./sample -p local:/var/run/f1.sock
+
+If the sample filter returns immediately to a command line, there was either
+an error with your command or a problem creating the specified socket.
+Further logging can be captured through the syslogd daemon. Using the
+'netstat -a' command can ensure that your filter process is listening on
+the appropriate local socket.
+
+Email messages must be injected via SMTP to be filtered. There are two
+simple means of doing this; either using the 'sendmail -bs' command, or
+by telnetting to port 25 of the machine configured for milter. Once
+connected via one of these options, the session can be continued through
+the use of standard SMTP commands.
+
+% sendmail -bs
+220 test.sendmail.com ESMTP Sendmail 8.11.0/8.11.0; Tue, 10 Nov 1970 13:05:23 -0500 (EST)
+HELO localhost
+250 test.sendmail.com Hello testy@localhost, pleased to meet you
+MAIL From:<testy>
+250 2.1.0 <testy>... Sender ok
+RCPT To:<root>
+250 2.1.5 <root>... Recipient ok
+DATA
+354 Enter mail, end with "." on a line by itself
+From: testy@test.sendmail.com
+To: root@test.sendmail.com
+Subject: testing sample filter
+
+Sample body
+.
+250 2.0.0 dB73Zxi25236 Message accepted for delivery
+QUIT
+221 2.0.0 test.sendmail.com closing connection
+
+In the above example, the lines beginning with numbers are output by the
+mail server, and those without are your input. If everything is working
+properly, you will find a file in /tmp by the name of msg.XXXXXXXX (where
+the Xs represent any combination of letters and numbers). This file should
+contain the message body and headers from the test email entered above.
+
+If the sample filter did not log your test email, there are a number of
+methods to narrow down the source of the problem. Check your system
+logs written by syslogd and see if there are any pertinent lines. You
+may need to reconfigure syslogd to capture all relevant data. Additionally,
+the logging level of sendmail can be raised with the LogLevel option.
+See the sendmail(8) manual page for more information.
+
+
++--------------------------+
+| SOURCE FOR SAMPLE FILTER |
++--------------------------+
+
+/* A trivial filter that logs all email to a file. */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "libmilter/mfapi.h"
+
+#ifndef true
+typedef int bool;
+# define false 0
+# define true 1
+#endif /* ! true */
+
+struct mlfiPriv
+{
+ char *mlfi_fname;
+ FILE *mlfi_fp;
+};
+
+#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
+
+extern sfsistat mlfi_cleanup(SMFICTX *, bool);
+
+sfsistat
+mlfi_envfrom(ctx, envfrom)
+ SMFICTX *ctx;
+ char **envfrom;
+{
+ struct mlfiPriv *priv;
+ int fd = -1;
+
+ /* allocate some private memory */
+ priv = malloc(sizeof *priv);
+ if (priv == NULL)
+ {
+ /* can't accept this message right now */
+ return SMFIS_TEMPFAIL;
+ }
+ memset(priv, '\0', sizeof *priv);
+
+ /* open a file to store this message */
+ priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
+ if (priv->mlfi_fname == NULL)
+ {
+ free(priv);
+ return SMFIS_TEMPFAIL;
+ }
+ if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
+ (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
+ {
+ if (fd >= 0)
+ (void) close(fd);
+ free(priv->mlfi_fname);
+ free(priv);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* save the private data */
+ smfi_setpriv(ctx, priv);
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+mlfi_header(ctx, headerf, headerv)
+ SMFICTX *ctx;
+ char *headerf;
+ char *headerv;
+{
+ /* write the header to the log file */
+ fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+mlfi_eoh(ctx)
+ SMFICTX *ctx;
+{
+ /* output the blank line between the header and the body */
+ fprintf(MLFIPRIV->mlfi_fp, "\r\n");
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+mlfi_body(ctx, bodyp, bodylen)
+ SMFICTX *ctx;
+ u_char *bodyp;
+ size_t bodylen;
+{
+ /* output body block to log file */
+ if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) == 0)
+ {
+ /* write failed */
+ (void) mlfi_cleanup(ctx, false);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+mlfi_eom(ctx)
+ SMFICTX *ctx;
+{
+ return mlfi_cleanup(ctx, true);
+}
+
+sfsistat
+mlfi_close(ctx)
+ SMFICTX *ctx;
+{
+ return SMFIS_ACCEPT;
+}
+
+sfsistat
+mlfi_abort(ctx)
+ SMFICTX *ctx;
+{
+ return mlfi_cleanup(ctx, false);
+}
+
+sfsistat
+mlfi_cleanup(ctx, ok)
+ SMFICTX *ctx;
+ bool ok;
+{
+ sfsistat rstat = SMFIS_CONTINUE;
+ struct mlfiPriv *priv = MLFIPRIV;
+ char *p;
+ char host[512];
+ char hbuf[1024];
+
+ if (priv == NULL)
+ return rstat;
+
+ /* close the archive file */
+ if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
+ {
+ /* failed; we have to wait until later */
+ rstat = SMFIS_TEMPFAIL;
+ (void) unlink(priv->mlfi_fname);
+ }
+ else if (ok)
+ {
+ /* add a header to the message announcing our presence */
+ if (gethostname(host, sizeof host) < 0)
+ snprintf(host, sizeof host, "localhost");
+ p = strrchr(priv->mlfi_fname, '/');
+ if (p == NULL)
+ p = priv->mlfi_fname;
+ else
+ p++;
+ snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
+ smfi_addheader(ctx, "X-Archived", hbuf);
+ }
+ else
+ {
+ /* message was aborted -- delete the archive file */
+ (void) unlink(priv->mlfi_fname);
+ }
+
+ /* release private memory */
+ free(priv->mlfi_fname);
+ free(priv);
+ smfi_setpriv(ctx, NULL);
+
+ /* return status */
+ return rstat;
+}
+
+struct smfiDesc smfilter =
+{
+ "SampleFilter", /* filter name */
+ SMFI_VERSION, /* version code -- do not change */
+ SMFIF_ADDHDRS, /* flags */
+ NULL, /* connection info filter */
+ NULL, /* SMTP HELO command filter */
+ mlfi_envfrom, /* envelope sender filter */
+ NULL, /* envelope recipient filter */
+ mlfi_header, /* header filter */
+ mlfi_eoh, /* end of header */
+ mlfi_body, /* body block filter */
+ mlfi_eom, /* end of message */
+ mlfi_abort, /* message aborted */
+ mlfi_close /* connection cleanup */
+};
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ bool setconn = false;
+ int c;
+ const char *args = "p:";
+
+ /* Process command line options */
+ while ((c = getopt(argc, argv, args)) != -1)
+ {
+ switch (c)
+ {
+ case 'p':
+ if (optarg == NULL || *optarg == '\0')
+ {
+ (void) fprintf(stderr, "Illegal conn: %s\n",
+ optarg);
+ exit(EX_USAGE);
+ }
+ (void) smfi_setconn(optarg);
+ setconn = true;
+ break;
+
+ }
+ }
+ if (!setconn)
+ {
+ fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
+ exit(EX_USAGE);
+ }
+ if (smfi_register(smfilter) == MI_FAILURE)
+ {
+ fprintf(stderr, "smfi_register failed\n");
+ exit(EX_UNAVAILABLE);
+ }
+ return smfi_main();
+}
+
+/* eof */
+
+$Revision: 8.35.2.2 $, Last updated $Date: 2003/05/26 04:10:06 $
diff --git a/usr/src/cmd/sendmail/libmilter/comm.c b/usr/src/cmd/sendmail/libmilter/comm.c
new file mode 100644
index 0000000000..fd64922d37
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/comm.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 1999-2004 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.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: comm.c,v 8.66 2004/08/20 20:38:35 ca Exp $")
+
+#include "libmilter.h"
+#include <sm/errstring.h>
+#include <sys/uio.h>
+
+static ssize_t retry_writev __P((socket_t, struct iovec *, int, struct timeval *));
+static size_t Maxdatasize = MILTER_MAX_DATA_SIZE;
+
+#if _FFR_MAXDATASIZE
+/*
+** SMFI_SETMAXDATASIZE -- set limit for milter data read/write.
+**
+** Parameters:
+** sz -- new limit.
+**
+** Returns:
+** old limit
+*/
+
+size_t
+smfi_setmaxdatasize(sz)
+ size_t sz;
+{
+ size_t old;
+
+ old = Maxdatasize;
+ Maxdatasize = sz;
+ return old;
+}
+#endif /* _FFR_MAXDATASIZE */
+
+/*
+** MI_RD_CMD -- read a command
+**
+** Parameters:
+** sd -- socket descriptor
+** timeout -- maximum time to wait
+** cmd -- single character command read from sd
+** rlen -- pointer to length of result
+** name -- name of milter
+**
+** Returns:
+** buffer with rest of command
+** (malloc()ed here, should be free()d)
+** hack: encode error in cmd
+*/
+
+char *
+mi_rd_cmd(sd, timeout, cmd, rlen, name)
+ socket_t sd;
+ struct timeval *timeout;
+ char *cmd;
+ size_t *rlen;
+ char *name;
+{
+ ssize_t len;
+ mi_int32 expl;
+ ssize_t i;
+ FD_RD_VAR(rds, excs);
+ int ret;
+ int save_errno;
+ char *buf;
+ char data[MILTER_LEN_BYTES + 1];
+
+ *cmd = '\0';
+ *rlen = 0;
+
+ i = 0;
+ for (;;)
+ {
+ FD_RD_INIT(sd, rds, excs);
+ ret = FD_RD_READY(sd, rds, excs, timeout);
+ if (ret == 0)
+ break;
+ else if (ret < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (FD_IS_RD_EXC(sd, rds, excs))
+ {
+ *cmd = SMFIC_SELECT;
+ return NULL;
+ }
+
+ len = MI_SOCK_READ(sd, data + i, sizeof data - i);
+ if (MI_SOCK_READ_FAIL(len))
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s, mi_rd_cmd: read returned %d: %s",
+ name, (int) len, sm_errstring(errno));
+ *cmd = SMFIC_RECVERR;
+ return NULL;
+ }
+ if (len == 0)
+ {
+ *cmd = SMFIC_EOF;
+ return NULL;
+ }
+ if (len >= (ssize_t) sizeof data - i)
+ break;
+ i += len;
+ }
+ if (ret == 0)
+ {
+ *cmd = SMFIC_TIMEOUT;
+ return NULL;
+ }
+ else if (ret < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: mi_rd_cmd: select returned %d: %s",
+ name, ret, sm_errstring(errno));
+ *cmd = SMFIC_RECVERR;
+ return NULL;
+ }
+
+ *cmd = data[MILTER_LEN_BYTES];
+ data[MILTER_LEN_BYTES] = '\0';
+ (void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES);
+ expl = ntohl(expl) - 1;
+ if (expl <= 0)
+ return NULL;
+ if (expl > Maxdatasize)
+ {
+ *cmd = SMFIC_TOOBIG;
+ return NULL;
+ }
+#if _FFR_ADD_NULL
+ buf = malloc(expl + 1);
+#else /* _FFR_ADD_NULL */
+ buf = malloc(expl);
+#endif /* _FFR_ADD_NULL */
+ if (buf == NULL)
+ {
+ *cmd = SMFIC_MALLOC;
+ return NULL;
+ }
+
+ i = 0;
+ for (;;)
+ {
+ FD_RD_INIT(sd, rds, excs);
+ ret = FD_RD_READY(sd, rds, excs, timeout);
+ if (ret == 0)
+ break;
+ else if (ret < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (FD_IS_RD_EXC(sd, rds, excs))
+ {
+ *cmd = SMFIC_SELECT;
+ free(buf);
+ return NULL;
+ }
+ len = MI_SOCK_READ(sd, buf + i, expl - i);
+ if (MI_SOCK_READ_FAIL(len))
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: mi_rd_cmd: read returned %d: %s",
+ name, (int) len, sm_errstring(errno));
+ ret = -1;
+ break;
+ }
+ if (len == 0)
+ {
+ *cmd = SMFIC_EOF;
+ free(buf);
+ return NULL;
+ }
+ if (len > expl - i)
+ {
+ *cmd = SMFIC_RECVERR;
+ free(buf);
+ return NULL;
+ }
+ if (len >= expl - i)
+ {
+ *rlen = expl;
+#if _FFR_ADD_NULL
+ /* makes life simpler for common string routines */
+ buf[expl] = '\0';
+#endif /* _FFR_ADD_NULL */
+ return buf;
+ }
+ i += len;
+ }
+
+ save_errno = errno;
+ free(buf);
+
+ /* select returned 0 (timeout) or < 0 (error) */
+ if (ret == 0)
+ {
+ *cmd = SMFIC_TIMEOUT;
+ return NULL;
+ }
+ if (ret < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: mi_rd_cmd: select returned %d: %s",
+ name, ret, sm_errstring(save_errno));
+ *cmd = SMFIC_RECVERR;
+ return NULL;
+ }
+ *cmd = SMFIC_UNKNERR;
+ return NULL;
+}
+
+/*
+** RETRY_WRITEV -- Keep calling the writev() system call
+** until all the data is written out or an error occurs.
+**
+** Parameters:
+** fd -- socket descriptor
+** iov -- io vector
+** iovcnt -- number of elements in io vector
+** must NOT exceed UIO_MAXIOV.
+** timeout -- maximum time to wait
+**
+** Returns:
+** success: number of bytes written
+** otherwise: MI_FAILURE
+*/
+
+static ssize_t
+retry_writev(fd, iov, iovcnt, timeout)
+ socket_t fd;
+ struct iovec *iov;
+ int iovcnt;
+ struct timeval *timeout;
+{
+ int i;
+ ssize_t n, written;
+ FD_WR_VAR(wrs);
+
+ written = 0;
+ for (;;)
+ {
+ while (iovcnt > 0 && iov[0].iov_len == 0)
+ {
+ iov++;
+ iovcnt--;
+ }
+ if (iovcnt <= 0)
+ return written;
+
+ /*
+ ** We don't care much about the timeout here,
+ ** it's very long anyway; correct solution would be
+ ** to take the time before the loop and reduce the
+ ** timeout after each invocation.
+ ** FD_SETSIZE is checked when socket is created.
+ */
+
+ FD_WR_INIT(fd, wrs);
+ i = FD_WR_READY(fd, wrs, timeout);
+ if (i == 0)
+ return MI_FAILURE;
+ if (i < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return MI_FAILURE;
+ }
+ n = writev(fd, iov, iovcnt);
+ if (n == -1)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return MI_FAILURE;
+ }
+
+ written += n;
+ for (i = 0; i < iovcnt; i++)
+ {
+ if (iov[i].iov_len > (unsigned int) n)
+ {
+ iov[i].iov_base = (char *)iov[i].iov_base + n;
+ iov[i].iov_len -= (unsigned int) n;
+ break;
+ }
+ n -= (int) iov[i].iov_len;
+ iov[i].iov_len = 0;
+ }
+ if (i == iovcnt)
+ return written;
+ }
+}
+
+/*
+** MI_WR_CMD -- write a cmd to sd
+**
+** Parameters:
+** sd -- socket descriptor
+** timeout -- maximum time to wait
+** cmd -- single character command to write
+** buf -- buffer with further data
+** len -- length of buffer (without cmd!)
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_wr_cmd(sd, timeout, cmd, buf, len)
+ socket_t sd;
+ struct timeval *timeout;
+ int cmd;
+ char *buf;
+ size_t len;
+{
+ size_t sl, i;
+ ssize_t l;
+ mi_int32 nl;
+ int iovcnt;
+ struct iovec iov[2];
+ char data[MILTER_LEN_BYTES + 1];
+
+ if (len > Maxdatasize || (len > 0 && buf == NULL))
+ return MI_FAILURE;
+
+ nl = htonl(len + 1); /* add 1 for the cmd char */
+ (void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES);
+ data[MILTER_LEN_BYTES] = (char) cmd;
+ i = 0;
+ sl = MILTER_LEN_BYTES + 1;
+
+ /* set up the vector for the size / command */
+ iov[0].iov_base = (void *) data;
+ iov[0].iov_len = sl;
+ iovcnt = 1;
+ if (len >= 0 && buf != NULL)
+ {
+ iov[1].iov_base = (void *) buf;
+ iov[1].iov_len = len;
+ iovcnt = 2;
+ }
+
+ l = retry_writev(sd, iov, iovcnt, timeout);
+ if (l == MI_FAILURE)
+ return MI_FAILURE;
+ return MI_SUCCESS;
+}
diff --git a/usr/src/cmd/sendmail/libmilter/engine.c b/usr/src/cmd/sendmail/libmilter/engine.c
new file mode 100644
index 0000000000..19f8218bc9
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/engine.c
@@ -0,0 +1,1252 @@
+/*
+ * Copyright (c) 1999-2003 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.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: engine.c,v 8.120 2004/10/20 21:09:00 ca Exp $")
+
+#include "libmilter.h"
+
+#if NETINET || NETINET6
+# include <arpa/inet.h>
+#endif /* NETINET || NETINET6 */
+
+/* generic argument for functions in the command table */
+struct arg_struct
+{
+ size_t a_len; /* length of buffer */
+ char *a_buf; /* argument string */
+ int a_idx; /* index for macro array */
+ SMFICTX_PTR a_ctx; /* context */
+};
+
+typedef struct arg_struct genarg;
+
+/* structure for commands received from MTA */
+struct cmdfct_t
+{
+ char cm_cmd; /* command */
+ int cm_argt; /* type of arguments expected */
+ int cm_next; /* next state */
+ int cm_todo; /* what to do next */
+ int cm_macros; /* index for macros */
+ int (*cm_fct) __P((genarg *)); /* function to execute */
+};
+
+typedef struct cmdfct_t cmdfct;
+
+/* possible values for cm_argt */
+#define CM_ARG0 0 /* no args */
+#define CM_ARG1 1 /* one arg (string) */
+#define CM_ARG2 2 /* two args (strings) */
+#define CM_ARGA 4 /* one string and _SOCK_ADDR */
+#define CM_ARGO 5 /* two integers */
+#define CM_ARGV 8 /* \0 separated list of args, NULL-terminated */
+#define CM_ARGN 9 /* \0 separated list of args (strings) */
+
+/* possible values for cm_todo */
+#define CT_CONT 0x0000 /* continue reading commands */
+#define CT_IGNO 0x0001 /* continue even when error */
+
+/* not needed right now, done via return code instead */
+#define CT_KEEP 0x0004 /* keep buffer (contains symbols) */
+#define CT_END 0x0008 /* start replying */
+
+/* index in macro array: macros only for these commands */
+#define CI_NONE (-1)
+#define CI_CONN 0
+#define CI_HELO 1
+#define CI_MAIL 2
+#define CI_RCPT 3
+#define CI_EOM 4
+#if CI_EOM >= MAX_MACROS_ENTRIES
+ERROR: do not compile with CI_EOM >= MAX_MACROS_ENTRIES
+#endif
+
+/* function prototypes */
+static int st_abortfct __P((genarg *));
+static int st_macros __P((genarg *));
+static int st_optionneg __P((genarg *));
+static int st_bodychunk __P((genarg *));
+static int st_connectinfo __P((genarg *));
+static int st_bodyend __P((genarg *));
+static int st_helo __P((genarg *));
+static int st_header __P((genarg *));
+static int st_sender __P((genarg *));
+static int st_rcpt __P((genarg *));
+#if SMFI_VERSION > 2
+static int st_unknown __P((genarg *));
+#endif /* SMFI_VERSION > 2 */
+#if SMFI_VERSION > 3
+static int st_data __P((genarg *));
+#endif /* SMFI_VERSION > 3 */
+static int st_eoh __P((genarg *));
+static int st_quit __P((genarg *));
+static int sendreply __P((sfsistat, socket_t, struct timeval *, SMFICTX_PTR));
+static void fix_stm __P((SMFICTX_PTR));
+static bool trans_ok __P((int, int));
+static char **dec_argv __P((char *, size_t));
+static int dec_arg2 __P((char *, size_t, char **, char **));
+
+/* states */
+#define ST_NONE (-1)
+#define ST_INIT 0 /* initial state */
+#define ST_OPTS 1 /* option negotiation */
+#define ST_CONN 2 /* connection info */
+#define ST_HELO 3 /* helo */
+#define ST_MAIL 4 /* mail from */
+#define ST_RCPT 5 /* rcpt to */
+#define ST_DATA 6 /* data */
+#define ST_HDRS 7 /* headers */
+#define ST_EOHS 8 /* end of headers */
+#define ST_BODY 9 /* body */
+#define ST_ENDM 10 /* end of message */
+#define ST_QUIT 11 /* quit */
+#define ST_ABRT 12 /* abort */
+#define ST_UNKN 13 /* unknown SMTP command */
+#define ST_LAST ST_UNKN /* last valid state */
+#define ST_SKIP 15 /* not a state but required for the state table */
+
+/* in a mail transaction? must be before eom according to spec. */
+#define ST_IN_MAIL(st) ((st) >= ST_MAIL && (st) < ST_ENDM)
+
+/*
+** set of next states
+** each state (ST_*) corresponds to bit in an int value (1 << state)
+** each state has a set of allowed transitions ('or' of bits of states)
+** so a state transition is valid if the mask of the next state
+** is set in the NX_* value
+** this function is coded in trans_ok(), see below.
+*/
+
+#define MI_MASK(x) (0x0001 << (x)) /* generate a bit "mask" for a state */
+#define NX_INIT (MI_MASK(ST_OPTS))
+#define NX_OPTS (MI_MASK(ST_CONN) | MI_MASK(ST_UNKN))
+#define NX_CONN (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN))
+#define NX_HELO (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN))
+#define NX_MAIL (MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | MI_MASK(ST_UNKN))
+#define NX_RCPT (MI_MASK(ST_HDRS) | MI_MASK(ST_EOHS) | MI_MASK(ST_DATA) | \
+ MI_MASK(ST_BODY) | MI_MASK(ST_ENDM) | \
+ MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | MI_MASK(ST_UNKN))
+#define NX_DATA (MI_MASK(ST_EOHS) | MI_MASK(ST_HDRS) | MI_MASK(ST_ABRT))
+#define NX_HDRS (MI_MASK(ST_EOHS) | MI_MASK(ST_HDRS) | MI_MASK(ST_ABRT))
+#define NX_EOHS (MI_MASK(ST_BODY) | MI_MASK(ST_ENDM) | MI_MASK(ST_ABRT))
+#define NX_BODY (MI_MASK(ST_ENDM) | MI_MASK(ST_BODY) | MI_MASK(ST_ABRT))
+#define NX_ENDM (MI_MASK(ST_QUIT) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN))
+#define NX_QUIT 0
+#define NX_ABRT 0
+#define NX_UNKN (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | \
+ MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | \
+ MI_MASK(ST_DATA) | \
+ MI_MASK(ST_BODY) | MI_MASK(ST_UNKN) | \
+ MI_MASK(ST_ABRT) | MI_MASK(ST_QUIT))
+#define NX_SKIP MI_MASK(ST_SKIP)
+
+static int next_states[] =
+{
+ NX_INIT,
+ NX_OPTS,
+ NX_CONN,
+ NX_HELO,
+ NX_MAIL,
+ NX_RCPT,
+ NX_DATA,
+ NX_HDRS,
+ NX_EOHS,
+ NX_BODY,
+ NX_ENDM,
+ NX_QUIT,
+ NX_ABRT,
+ NX_UNKN
+};
+
+/* commands received by milter */
+static cmdfct cmds[] =
+{
+{SMFIC_ABORT, CM_ARG0, ST_ABRT, CT_CONT, CI_NONE, st_abortfct },
+{SMFIC_MACRO, CM_ARGV, ST_NONE, CT_KEEP, CI_NONE, st_macros },
+{SMFIC_BODY, CM_ARG1, ST_BODY, CT_CONT, CI_NONE, st_bodychunk },
+{SMFIC_CONNECT, CM_ARG2, ST_CONN, CT_CONT, CI_CONN, st_connectinfo },
+{SMFIC_BODYEOB, CM_ARG1, ST_ENDM, CT_CONT, CI_EOM, st_bodyend },
+{SMFIC_HELO, CM_ARG1, ST_HELO, CT_CONT, CI_HELO, st_helo },
+{SMFIC_HEADER, CM_ARG2, ST_HDRS, CT_CONT, CI_NONE, st_header },
+{SMFIC_MAIL, CM_ARGV, ST_MAIL, CT_CONT, CI_MAIL, st_sender },
+{SMFIC_OPTNEG, CM_ARGO, ST_OPTS, CT_CONT, CI_NONE, st_optionneg },
+{SMFIC_EOH, CM_ARG0, ST_EOHS, CT_CONT, CI_NONE, st_eoh },
+{SMFIC_QUIT, CM_ARG0, ST_QUIT, CT_END, CI_NONE, st_quit },
+#if SMFI_VERSION > 3
+{SMFIC_DATA, CM_ARG0, ST_DATA, CT_CONT, CI_NONE, st_data },
+#endif /* SMFI_VERSION > 3 */
+{SMFIC_RCPT, CM_ARGV, ST_RCPT, CT_IGNO, CI_RCPT, st_rcpt }
+#if SMFI_VERSION > 2
+,{SMFIC_UNKNOWN,CM_ARG1, ST_UNKN, CT_IGNO, CI_NONE, st_unknown }
+#endif /* SMFI_VERSION > 2 */
+};
+
+/* additional (internal) reply codes */
+#define _SMFIS_KEEP 20
+#define _SMFIS_ABORT 21
+#define _SMFIS_OPTIONS 22
+#define _SMFIS_NOREPLY 23
+#define _SMFIS_FAIL (-1)
+#define _SMFIS_NONE (-2)
+
+/*
+** MI_ENGINE -- receive commands and process them
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** MI_FAILURE/MI_SUCCESS
+*/
+int
+mi_engine(ctx)
+ SMFICTX_PTR ctx;
+{
+ size_t len;
+ int i;
+ socket_t sd;
+ int ret = MI_SUCCESS;
+ int ncmds = sizeof(cmds) / sizeof(cmdfct);
+ int curstate = ST_INIT;
+ int newstate;
+ bool call_abort;
+ sfsistat r;
+ char cmd;
+ char *buf = NULL;
+ genarg arg;
+ struct timeval timeout;
+ int (*f) __P((genarg *));
+ sfsistat (*fi_abort) __P((SMFICTX *));
+ sfsistat (*fi_close) __P((SMFICTX *));
+
+ arg.a_ctx = ctx;
+ sd = ctx->ctx_sd;
+ fi_abort = ctx->ctx_smfi->xxfi_abort;
+ mi_clr_macros(ctx, 0);
+ fix_stm(ctx);
+ r = _SMFIS_NONE;
+ do
+ {
+ /* call abort only if in a mail transaction */
+ call_abort = ST_IN_MAIL(curstate);
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ if (mi_stop() == MILTER_ABRT)
+ {
+ if (ctx->ctx_dbg > 3)
+ sm_dprintf("[%d] milter_abort\n",
+ (int) ctx->ctx_id);
+ ret = MI_FAILURE;
+ break;
+ }
+
+ /*
+ ** Notice: buf is allocated by mi_rd_cmd() and it will
+ ** usually be free()d after it has been used in f().
+ ** However, if the function returns _SMFIS_KEEP then buf
+ ** contains macros and will not be free()d.
+ ** Hence r must be set to _SMFIS_NONE if a new buf is
+ ** allocated to avoid problem with housekeeping, esp.
+ ** if the code "break"s out of the loop.
+ */
+
+ r = _SMFIS_NONE;
+ if ((buf = mi_rd_cmd(sd, &timeout, &cmd, &len,
+ ctx->ctx_smfi->xxfi_name)) == NULL &&
+ cmd < SMFIC_VALIDCMD)
+ {
+ if (ctx->ctx_dbg > 5)
+ sm_dprintf("[%d] mi_engine: mi_rd_cmd error (%x)\n",
+ (int) ctx->ctx_id, (int) cmd);
+
+ /*
+ ** eof is currently treated as failure ->
+ ** abort() instead of close(), otherwise use:
+ ** if (cmd != SMFIC_EOF)
+ */
+
+ ret = MI_FAILURE;
+ break;
+ }
+ if (ctx->ctx_dbg > 4)
+ sm_dprintf("[%d] got cmd '%c' len %d\n",
+ (int) ctx->ctx_id, cmd, (int) len);
+ for (i = 0; i < ncmds; i++)
+ {
+ if (cmd == cmds[i].cm_cmd)
+ break;
+ }
+ if (i >= ncmds)
+ {
+ /* unknown command */
+ if (ctx->ctx_dbg > 1)
+ sm_dprintf("[%d] cmd '%c' unknown\n",
+ (int) ctx->ctx_id, cmd);
+ ret = MI_FAILURE;
+ break;
+ }
+ if ((f = cmds[i].cm_fct) == NULL)
+ {
+ /* stop for now */
+ if (ctx->ctx_dbg > 1)
+ sm_dprintf("[%d] cmd '%c' not impl\n",
+ (int) ctx->ctx_id, cmd);
+ ret = MI_FAILURE;
+ break;
+ }
+
+ /* is new state ok? */
+ newstate = cmds[i].cm_next;
+ if (ctx->ctx_dbg > 5)
+ sm_dprintf("[%d] cur %x new %x nextmask %x\n",
+ (int) ctx->ctx_id,
+ curstate, newstate, next_states[curstate]);
+
+ if (newstate != ST_NONE && !trans_ok(curstate, newstate))
+ {
+ if (ctx->ctx_dbg > 1)
+ sm_dprintf("[%d] abort: cur %d (%x) new %d (%x) next %x\n",
+ (int) ctx->ctx_id,
+ curstate, MI_MASK(curstate),
+ newstate, MI_MASK(newstate),
+ next_states[curstate]);
+
+ /* call abort only if in a mail transaction */
+ if (fi_abort != NULL && call_abort)
+ (void) (*fi_abort)(ctx);
+
+ /*
+ ** try to reach the new state from HELO
+ ** if it can't be reached, ignore the command.
+ */
+
+ curstate = ST_HELO;
+ if (!trans_ok(curstate, newstate))
+ {
+ if (buf != NULL)
+ {
+ free(buf);
+ buf = NULL;
+ }
+ continue;
+ }
+ }
+ arg.a_len = len;
+ arg.a_buf = buf;
+ if (newstate != ST_NONE)
+ {
+ curstate = newstate;
+ ctx->ctx_state = curstate;
+ }
+ arg.a_idx = cmds[i].cm_macros;
+ call_abort = ST_IN_MAIL(curstate);
+
+ /* call function to deal with command */
+ r = (*f)(&arg);
+ if (r != _SMFIS_KEEP && buf != NULL)
+ {
+ free(buf);
+ buf = NULL;
+ }
+ if (sendreply(r, sd, &timeout, ctx) != MI_SUCCESS)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+
+ if (r == SMFIS_ACCEPT)
+ {
+ /* accept mail, no further actions taken */
+ curstate = ST_HELO;
+ }
+ else if (r == SMFIS_REJECT || r == SMFIS_DISCARD ||
+ r == SMFIS_TEMPFAIL)
+ {
+ /*
+ ** further actions depend on current state
+ ** if the IGNO bit is set: "ignore" the error,
+ ** i.e., stay in the current state
+ */
+ if (!bitset(CT_IGNO, cmds[i].cm_todo))
+ curstate = ST_HELO;
+ }
+ else if (r == _SMFIS_ABORT)
+ {
+ if (ctx->ctx_dbg > 5)
+ sm_dprintf("[%d] function returned abort\n",
+ (int) ctx->ctx_id);
+ ret = MI_FAILURE;
+ break;
+ }
+ } while (!bitset(CT_END, cmds[i].cm_todo));
+
+ if (ret != MI_SUCCESS)
+ {
+ /* call abort only if in a mail transaction */
+ if (fi_abort != NULL && call_abort)
+ (void) (*fi_abort)(ctx);
+ }
+
+ /* close must always be called */
+ if ((fi_close = ctx->ctx_smfi->xxfi_close) != NULL)
+ (void) (*fi_close)(ctx);
+ if (r != _SMFIS_KEEP && buf != NULL)
+ free(buf);
+ mi_clr_macros(ctx, 0);
+ return ret;
+}
+/*
+** SENDREPLY -- send a reply to the MTA
+**
+** Parameters:
+** r -- reply code
+** sd -- socket descriptor
+** timeout_ptr -- (ptr to) timeout to use for sending
+** ctx -- context structure
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+static int
+sendreply(r, sd, timeout_ptr, ctx)
+ sfsistat r;
+ socket_t sd;
+ struct timeval *timeout_ptr;
+ SMFICTX_PTR ctx;
+{
+ int ret = MI_SUCCESS;
+
+ switch (r)
+ {
+ case SMFIS_CONTINUE:
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_CONTINUE, NULL, 0);
+ break;
+ case SMFIS_TEMPFAIL:
+ case SMFIS_REJECT:
+ if (ctx->ctx_reply != NULL &&
+ ((r == SMFIS_TEMPFAIL && *ctx->ctx_reply == '4') ||
+ (r == SMFIS_REJECT && *ctx->ctx_reply == '5')))
+ {
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_REPLYCODE,
+ ctx->ctx_reply,
+ strlen(ctx->ctx_reply) + 1);
+ free(ctx->ctx_reply);
+ ctx->ctx_reply = NULL;
+ }
+ else
+ {
+ ret = mi_wr_cmd(sd, timeout_ptr, r == SMFIS_REJECT ?
+ SMFIR_REJECT : SMFIR_TEMPFAIL, NULL, 0);
+ }
+ break;
+ case SMFIS_DISCARD:
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_DISCARD, NULL, 0);
+ break;
+ case SMFIS_ACCEPT:
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_ACCEPT, NULL, 0);
+ break;
+ case _SMFIS_OPTIONS:
+ {
+ char buf[MILTER_OPTLEN];
+ mi_int32 v;
+
+ v = htonl(ctx->ctx_smfi->xxfi_version);
+ (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
+ v = htonl(ctx->ctx_smfi->xxfi_flags);
+ (void) memcpy(&(buf[MILTER_LEN_BYTES]), (void *) &v,
+ MILTER_LEN_BYTES);
+ v = htonl(ctx->ctx_pflags);
+ (void) memcpy(&(buf[MILTER_LEN_BYTES * 2]), (void *) &v,
+ MILTER_LEN_BYTES);
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIC_OPTNEG, buf,
+ MILTER_OPTLEN);
+ }
+ break;
+ default: /* don't send a reply */
+ break;
+ }
+ return ret;
+}
+
+/*
+** CLR_MACROS -- clear set of macros starting from a given index
+**
+** Parameters:
+** ctx -- context structure
+** m -- index from which to clear all macros
+**
+** Returns:
+** None.
+*/
+void
+mi_clr_macros(ctx, m)
+ SMFICTX_PTR ctx;
+ int m;
+{
+ int i;
+
+ for (i = m; i < MAX_MACROS_ENTRIES; i++)
+ {
+ if (ctx->ctx_mac_ptr[i] != NULL)
+ {
+ free(ctx->ctx_mac_ptr[i]);
+ ctx->ctx_mac_ptr[i] = NULL;
+ }
+ if (ctx->ctx_mac_buf[i] != NULL)
+ {
+ free(ctx->ctx_mac_buf[i]);
+ ctx->ctx_mac_buf[i] = NULL;
+ }
+ }
+}
+/*
+** ST_OPTIONNEG -- negotiate options
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** abort/send options/continue
+*/
+
+static int
+st_optionneg(g)
+ genarg *g;
+{
+ mi_int32 i, v;
+
+ if (g == NULL || g->a_ctx->ctx_smfi == NULL)
+ return SMFIS_CONTINUE;
+ mi_clr_macros(g->a_ctx, g->a_idx + 1);
+
+ /* check for minimum length */
+ if (g->a_len < MILTER_OPTLEN)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: st_optionneg[%d]: len too short %d < %d",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (int) g->a_ctx->ctx_id, (int) g->a_len,
+ MILTER_OPTLEN);
+ return _SMFIS_ABORT;
+ }
+
+ (void) memcpy((void *) &i, (void *) &(g->a_buf[0]),
+ MILTER_LEN_BYTES);
+ v = ntohl(i);
+ if (v < g->a_ctx->ctx_smfi->xxfi_version)
+ {
+ /* hard failure for now! */
+ smi_log(SMI_LOG_ERR,
+ "%s: st_optionneg[%d]: version mismatch MTA: %d < milter: %d",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (int) g->a_ctx->ctx_id, (int) v,
+ g->a_ctx->ctx_smfi->xxfi_version);
+ return _SMFIS_ABORT;
+ }
+
+ (void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES]),
+ MILTER_LEN_BYTES);
+ v = ntohl(i);
+
+ /* no flags? set to default value for V1 actions */
+ if (v == 0)
+ v = SMFI_V1_ACTS;
+ i = g->a_ctx->ctx_smfi->xxfi_flags;
+ if ((v & i) != i)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: st_optionneg[%d]: 0x%x does not fulfill action requirements 0x%x",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (int) g->a_ctx->ctx_id, v, i);
+ return _SMFIS_ABORT;
+ }
+
+ (void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES * 2]),
+ MILTER_LEN_BYTES);
+ v = ntohl(i);
+
+ /* no flags? set to default value for V1 protocol */
+ if (v == 0)
+ v = SMFI_V1_PROT;
+ i = g->a_ctx->ctx_pflags;
+ if ((v & i) != i)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: st_optionneg[%d]: 0x%x does not fulfill protocol requirements 0x%x",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (int) g->a_ctx->ctx_id, v, i);
+ return _SMFIS_ABORT;
+ }
+
+ return _SMFIS_OPTIONS;
+}
+/*
+** ST_CONNECTINFO -- receive connection information
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_connectinfo(g)
+ genarg *g;
+{
+ size_t l;
+ size_t i;
+ char *s, family;
+ unsigned short port = 0;
+ _SOCK_ADDR sockaddr;
+ sfsistat (*fi_connect) __P((SMFICTX *, char *, _SOCK_ADDR *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ mi_clr_macros(g->a_ctx, g->a_idx + 1);
+ if (g->a_ctx->ctx_smfi == NULL ||
+ (fi_connect = g->a_ctx->ctx_smfi->xxfi_connect) == NULL)
+ return SMFIS_CONTINUE;
+
+ s = g->a_buf;
+ i = 0;
+ l = g->a_len;
+ while (s[i] != '\0' && i <= l)
+ ++i;
+ if (i + 1 >= l)
+ return _SMFIS_ABORT;
+
+ /* Move past trailing \0 in host string */
+ i++;
+ family = s[i++];
+ (void) memset(&sockaddr, '\0', sizeof sockaddr);
+ if (family != SMFIA_UNKNOWN)
+ {
+ if (i + sizeof port >= l)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%d]: wrong len %d >= %d",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (int) g->a_ctx->ctx_id, (int) i, (int) l);
+ return _SMFIS_ABORT;
+ }
+ (void) memcpy((void *) &port, (void *) (s + i),
+ sizeof port);
+ i += sizeof port;
+
+ /* make sure string is terminated */
+ if (s[l - 1] != '\0')
+ return _SMFIS_ABORT;
+# if NETINET
+ if (family == SMFIA_INET)
+ {
+ if (inet_aton(s + i, (struct in_addr *) &sockaddr.sin.sin_addr)
+ != 1)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%d]: inet_aton failed",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (int) g->a_ctx->ctx_id);
+ return _SMFIS_ABORT;
+ }
+ sockaddr.sa.sa_family = AF_INET;
+ if (port > 0)
+ sockaddr.sin.sin_port = port;
+ }
+ else
+# endif /* NETINET */
+# if NETINET6
+ if (family == SMFIA_INET6)
+ {
+ if (mi_inet_pton(AF_INET6, s + i,
+ &sockaddr.sin6.sin6_addr) != 1)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%d]: mi_inet_pton failed",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (int) g->a_ctx->ctx_id);
+ return _SMFIS_ABORT;
+ }
+ sockaddr.sa.sa_family = AF_INET6;
+ if (port > 0)
+ sockaddr.sin6.sin6_port = port;
+ }
+ else
+# endif /* NETINET6 */
+# if NETUNIX
+ if (family == SMFIA_UNIX)
+ {
+ if (sm_strlcpy(sockaddr.sunix.sun_path, s + i,
+ sizeof sockaddr.sunix.sun_path) >=
+ sizeof sockaddr.sunix.sun_path)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%d]: path too long",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (int) g->a_ctx->ctx_id);
+ return _SMFIS_ABORT;
+ }
+ sockaddr.sunix.sun_family = AF_UNIX;
+ }
+ else
+# endif /* NETUNIX */
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%d]: unknown family %d",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (int) g->a_ctx->ctx_id, family);
+ return _SMFIS_ABORT;
+ }
+ }
+ return (*fi_connect)(g->a_ctx, g->a_buf,
+ family != SMFIA_UNKNOWN ? &sockaddr : NULL);
+}
+
+/*
+** ST_EOH -- end of headers
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_eoh(g)
+ genarg *g;
+{
+ sfsistat (*fi_eoh) __P((SMFICTX *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g->a_ctx->ctx_smfi != NULL &&
+ (fi_eoh = g->a_ctx->ctx_smfi->xxfi_eoh) != NULL)
+ return (*fi_eoh)(g->a_ctx);
+ return SMFIS_CONTINUE;
+}
+
+#if SMFI_VERSION > 3
+/*
+** ST_DATA -- DATA command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_data(g)
+ genarg *g;
+{
+ sfsistat (*fi_data) __P((SMFICTX *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g->a_ctx->ctx_smfi != NULL &&
+ (fi_data = g->a_ctx->ctx_smfi->xxfi_data) != NULL)
+ return (*fi_data)(g->a_ctx);
+ return SMFIS_CONTINUE;
+}
+#endif /* SMFI_VERSION > 3 */
+
+/*
+** ST_HELO -- helo/ehlo command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+static int
+st_helo(g)
+ genarg *g;
+{
+ sfsistat (*fi_helo) __P((SMFICTX *, char *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ mi_clr_macros(g->a_ctx, g->a_idx + 1);
+ if (g->a_ctx->ctx_smfi != NULL &&
+ (fi_helo = g->a_ctx->ctx_smfi->xxfi_helo) != NULL)
+ {
+ /* paranoia: check for terminating '\0' */
+ if (g->a_len == 0 || g->a_buf[g->a_len - 1] != '\0')
+ return MI_FAILURE;
+ return (*fi_helo)(g->a_ctx, g->a_buf);
+ }
+ return SMFIS_CONTINUE;
+}
+/*
+** ST_HEADER -- header line
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_header(g)
+ genarg *g;
+{
+ char *hf, *hv;
+ sfsistat (*fi_header) __P((SMFICTX *, char *, char *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g->a_ctx->ctx_smfi == NULL ||
+ (fi_header = g->a_ctx->ctx_smfi->xxfi_header) == NULL)
+ return SMFIS_CONTINUE;
+ if (dec_arg2(g->a_buf, g->a_len, &hf, &hv) == MI_SUCCESS)
+ return (*fi_header)(g->a_ctx, hf, hv);
+ else
+ return _SMFIS_ABORT;
+}
+
+#define ARGV_FCT(lf, rf, idx) \
+ char **argv; \
+ sfsistat (*lf) __P((SMFICTX *, char **)); \
+ int r; \
+ \
+ if (g == NULL) \
+ return _SMFIS_ABORT; \
+ mi_clr_macros(g->a_ctx, g->a_idx + 1); \
+ if (g->a_ctx->ctx_smfi == NULL || \
+ (lf = g->a_ctx->ctx_smfi->rf) == NULL) \
+ return SMFIS_CONTINUE; \
+ if ((argv = dec_argv(g->a_buf, g->a_len)) == NULL) \
+ return _SMFIS_ABORT; \
+ r = (*lf)(g->a_ctx, argv); \
+ free(argv); \
+ return r;
+
+/*
+** ST_SENDER -- MAIL FROM command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_sender(g)
+ genarg *g;
+{
+ ARGV_FCT(fi_envfrom, xxfi_envfrom, CI_MAIL)
+}
+/*
+** ST_RCPT -- RCPT TO command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_rcpt(g)
+ genarg *g;
+{
+ ARGV_FCT(fi_envrcpt, xxfi_envrcpt, CI_RCPT)
+}
+
+#if SMFI_VERSION > 2
+/*
+** ST_UNKNOWN -- unrecognized or unimplemented command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_unknown(g)
+ genarg *g;
+{
+ sfsistat (*fi_unknown) __P((SMFICTX *, char *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ mi_clr_macros(g->a_ctx, g->a_idx + 1);
+ if (g->a_ctx->ctx_smfi != NULL &&
+ (fi_unknown = g->a_ctx->ctx_smfi->xxfi_unknown) != NULL)
+ return (*fi_unknown)(g->a_ctx, g->a_buf);
+ return SMFIS_CONTINUE;
+}
+#endif /* SMFI_VERSION > 2 */
+
+/*
+** ST_MACROS -- deal with macros received from the MTA
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue/keep
+**
+** Side effects:
+** set pointer in macro array to current values.
+*/
+
+static int
+st_macros(g)
+ genarg *g;
+{
+ int i;
+ char **argv;
+
+ if (g == NULL || g->a_len < 1)
+ return _SMFIS_FAIL;
+ if ((argv = dec_argv(g->a_buf + 1, g->a_len - 1)) == NULL)
+ return _SMFIS_FAIL;
+ switch (g->a_buf[0])
+ {
+ case SMFIC_CONNECT:
+ i = CI_CONN;
+ break;
+ case SMFIC_HELO:
+ i = CI_HELO;
+ break;
+ case SMFIC_MAIL:
+ i = CI_MAIL;
+ break;
+ case SMFIC_RCPT:
+ i = CI_RCPT;
+ break;
+ case SMFIC_BODYEOB:
+ i = CI_EOM;
+ break;
+ default:
+ free(argv);
+ return _SMFIS_FAIL;
+ }
+ if (g->a_ctx->ctx_mac_ptr[i] != NULL)
+ free(g->a_ctx->ctx_mac_ptr[i]);
+ if (g->a_ctx->ctx_mac_buf[i] != NULL)
+ free(g->a_ctx->ctx_mac_buf[i]);
+ g->a_ctx->ctx_mac_ptr[i] = argv;
+ g->a_ctx->ctx_mac_buf[i] = g->a_buf;
+ return _SMFIS_KEEP;
+}
+/*
+** ST_QUIT -- quit command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** noreply
+*/
+
+/* ARGSUSED */
+static int
+st_quit(g)
+ genarg *g;
+{
+ return _SMFIS_NOREPLY;
+}
+/*
+** ST_BODYCHUNK -- deal with a piece of the mail body
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_bodychunk(g)
+ genarg *g;
+{
+ sfsistat (*fi_body) __P((SMFICTX *, unsigned char *, size_t));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g->a_ctx->ctx_smfi != NULL &&
+ (fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL)
+ return (*fi_body)(g->a_ctx, (unsigned char *)g->a_buf,
+ g->a_len);
+ return SMFIS_CONTINUE;
+}
+/*
+** ST_BODYEND -- deal with the last piece of the mail body
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+**
+** Side effects:
+** sends a reply for the body part (if non-empty).
+*/
+
+static int
+st_bodyend(g)
+ genarg *g;
+{
+ sfsistat r;
+ sfsistat (*fi_body) __P((SMFICTX *, unsigned char *, size_t));
+ sfsistat (*fi_eom) __P((SMFICTX *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ r = SMFIS_CONTINUE;
+ if (g->a_ctx->ctx_smfi != NULL)
+ {
+ if ((fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL &&
+ g->a_len > 0)
+ {
+ socket_t sd;
+ struct timeval timeout;
+
+ timeout.tv_sec = g->a_ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ sd = g->a_ctx->ctx_sd;
+ r = (*fi_body)(g->a_ctx, (unsigned char *)g->a_buf,
+ g->a_len);
+ if (r != SMFIS_CONTINUE &&
+ sendreply(r, sd, &timeout, g->a_ctx) != MI_SUCCESS)
+ return _SMFIS_ABORT;
+ }
+ }
+ if (r == SMFIS_CONTINUE &&
+ (fi_eom = g->a_ctx->ctx_smfi->xxfi_eom) != NULL)
+ return (*fi_eom)(g->a_ctx);
+ return r;
+}
+/*
+** ST_ABORTFCT -- deal with aborts
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** abort or filter-specified value
+*/
+
+static int
+st_abortfct(g)
+ genarg *g;
+{
+ sfsistat (*fi_abort) __P((SMFICTX *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g != NULL && g->a_ctx->ctx_smfi != NULL &&
+ (fi_abort = g->a_ctx->ctx_smfi->xxfi_abort) != NULL)
+ (void) (*fi_abort)(g->a_ctx);
+ return _SMFIS_NOREPLY;
+}
+/*
+** TRANS_OK -- is the state transition ok?
+**
+** Parameters:
+** old -- old state
+** new -- new state
+**
+** Returns:
+** state transition ok
+*/
+
+static bool
+trans_ok(old, new)
+ int old, new;
+{
+ int s, n;
+
+ s = old;
+ do
+ {
+ /* is this state transition allowed? */
+ if ((MI_MASK(new) & next_states[s]) != 0)
+ return true;
+
+ /*
+ ** no: try next state;
+ ** this works since the relevant states are ordered
+ ** strict sequentially
+ */
+
+ n = s + 1;
+
+ /*
+ ** can we actually "skip" this state?
+ ** see fix_stm() which sets this bit for those
+ ** states which the filter program is not interested in
+ */
+
+ if (bitset(NX_SKIP, next_states[n]))
+ s = n;
+ else
+ return false;
+ } while (s <= ST_LAST);
+ return false;
+}
+/*
+** FIX_STM -- add "skip" bits to the state transition table
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** None.
+**
+** Side effects:
+** may change state transition table.
+*/
+
+static void
+fix_stm(ctx)
+ SMFICTX_PTR ctx;
+{
+ unsigned long fl;
+
+ if (ctx == NULL || ctx->ctx_smfi == NULL)
+ return;
+ fl = ctx->ctx_pflags;
+ if (bitset(SMFIP_NOCONNECT, fl))
+ next_states[ST_CONN] |= NX_SKIP;
+ if (bitset(SMFIP_NOHELO, fl))
+ next_states[ST_HELO] |= NX_SKIP;
+ if (bitset(SMFIP_NOMAIL, fl))
+ next_states[ST_MAIL] |= NX_SKIP;
+ if (bitset(SMFIP_NORCPT, fl))
+ next_states[ST_RCPT] |= NX_SKIP;
+ if (bitset(SMFIP_NOHDRS, fl))
+ next_states[ST_HDRS] |= NX_SKIP;
+ if (bitset(SMFIP_NOEOH, fl))
+ next_states[ST_EOHS] |= NX_SKIP;
+ if (bitset(SMFIP_NOBODY, fl))
+ next_states[ST_BODY] |= NX_SKIP;
+}
+/*
+** DEC_ARGV -- split a buffer into a list of strings, NULL terminated
+**
+** Parameters:
+** buf -- buffer with several strings
+** len -- length of buffer
+**
+** Returns:
+** array of pointers to the individual strings
+*/
+
+static char **
+dec_argv(buf, len)
+ char *buf;
+ size_t len;
+{
+ char **s;
+ size_t i;
+ int elem, nelem;
+
+ nelem = 0;
+ for (i = 0; i < len; i++)
+ {
+ if (buf[i] == '\0')
+ ++nelem;
+ }
+ if (nelem == 0)
+ return NULL;
+
+ /* last entry is only for the name */
+ s = (char **)malloc((nelem + 1) * (sizeof *s));
+ if (s == NULL)
+ return NULL;
+ s[0] = buf;
+ for (i = 0, elem = 0; i < len && elem < nelem; i++)
+ {
+ if (buf[i] == '\0')
+ {
+ ++elem;
+ if (i + 1 >= len)
+ s[elem] = NULL;
+ else
+ s[elem] = &(buf[i + 1]);
+ }
+ }
+
+ /* overwrite last entry (already done above, just paranoia) */
+ s[elem] = NULL;
+ return s;
+}
+/*
+** DEC_ARG2 -- split a buffer into two strings
+**
+** Parameters:
+** buf -- buffer with two strings
+** len -- length of buffer
+** s1,s2 -- pointer to result strings
+**
+** Returns:
+** MI_FAILURE/MI_SUCCESS
+*/
+
+static int
+dec_arg2(buf, len, s1, s2)
+ char *buf;
+ size_t len;
+ char **s1;
+ char **s2;
+{
+ size_t i;
+
+ /* paranoia: check for terminating '\0' */
+ if (len == 0 || buf[len - 1] != '\0')
+ return MI_FAILURE;
+ *s1 = buf;
+ for (i = 1; i < len && buf[i] != '\0'; i++)
+ continue;
+ if (i >= len - 1)
+ return MI_FAILURE;
+ *s2 = buf + i + 1;
+ return MI_SUCCESS;
+}
+/*
+** SENDOK -- is it ok for the filter to send stuff to the MTA?
+**
+** Parameters:
+** ctx -- context structure
+** flag -- flag to check
+**
+** Returns:
+** sending allowed (in current state)
+*/
+
+bool
+mi_sendok(ctx, flag)
+ SMFICTX_PTR ctx;
+ int flag;
+{
+ if (ctx == NULL || ctx->ctx_smfi == NULL)
+ return false;
+
+ /* did the milter request this operation? */
+ if (flag != 0 && !bitset(flag, ctx->ctx_smfi->xxfi_flags))
+ return false;
+
+ /* are we in the correct state? It must be "End of Message". */
+ return ctx->ctx_state == ST_ENDM;
+}
diff --git a/usr/src/cmd/sendmail/libmilter/handler.c b/usr/src/cmd/sendmail/libmilter/handler.c
new file mode 100644
index 0000000000..4823b378b2
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/handler.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1999-2003 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.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: handler.c,v 8.30.2.4 2003/01/23 22:28:36 ca Exp $")
+
+#include "libmilter.h"
+
+
+/*
+** HANDLE_SESSION -- Handle a connected session in its own context
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_handle_session(ctx)
+ SMFICTX_PTR ctx;
+{
+ int ret;
+
+ if (ctx == NULL)
+ return MI_FAILURE;
+ ctx->ctx_id = (sthread_t) sthread_get_id();
+
+ /*
+ ** Detach so resources are free when the thread returns.
+ ** If we ever "wait" for threads, this call must be removed.
+ */
+
+ if (pthread_detach(ctx->ctx_id) != 0)
+ ret = MI_FAILURE;
+ else
+ ret = mi_engine(ctx);
+ if (ValidSocket(ctx->ctx_sd))
+ {
+ (void) closesocket(ctx->ctx_sd);
+ ctx->ctx_sd = INVALID_SOCKET;
+ }
+ if (ctx->ctx_reply != NULL)
+ {
+ free(ctx->ctx_reply);
+ ctx->ctx_reply = NULL;
+ }
+ if (ctx->ctx_privdata != NULL)
+ {
+ smi_log(SMI_LOG_WARN,
+ "%s: private data not NULL",
+ ctx->ctx_smfi->xxfi_name);
+ }
+ mi_clr_macros(ctx, 0);
+ free(ctx);
+ ctx = NULL;
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libmilter/i386/Makefile b/usr/src/cmd/sendmail/libmilter/i386/Makefile
new file mode 100644
index 0000000000..e86ae6a9f7
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/i386/Makefile
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/cmd/sendmail/libmilter/libmilter.h b/usr/src/cmd/sendmail/libmilter/libmilter.h
new file mode 100644
index 0000000000..da8a72ba5d
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/libmilter.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1999-2003 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.
+ */
+
+/*
+** LIBMILTER.H -- include file for mail filter library functions
+*/
+
+#ifndef _LIBMILTER_H
+# define _LIBMILTER_H 1
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+
+#ifdef _DEFINE
+# define EXTERN
+# define INIT(x) = x
+SM_IDSTR(MilterlId, "@(#)$Id: libmilter.h,v 8.50 2003/12/11 18:14:34 ca Exp $")
+#else /* _DEFINE */
+# define EXTERN extern
+# define INIT(x)
+#endif /* _DEFINE */
+
+
+#define NOT_SENDMAIL 1
+#define _SOCK_ADDR union bigsockaddr
+#include "sendmail.h"
+
+#include "libmilter/milter.h"
+
+# define ValidSocket(sd) ((sd) >= 0)
+# define INVALID_SOCKET (-1)
+# define closesocket close
+# define MI_SOCK_READ(s, b, l) read(s, b, l)
+# define MI_SOCK_READ_FAIL(x) ((x) < 0)
+# define MI_SOCK_WRITE(s, b, l) write(s, b, l)
+
+# define thread_create(ptid,wr,arg) pthread_create(ptid, NULL, wr, arg)
+# define sthread_get_id() pthread_self()
+
+typedef pthread_mutex_t smutex_t;
+# define smutex_init(mp) (pthread_mutex_init(mp, NULL) == 0)
+# define smutex_destroy(mp) (pthread_mutex_destroy(mp) == 0)
+# define smutex_lock(mp) (pthread_mutex_lock(mp) == 0)
+# define smutex_unlock(mp) (pthread_mutex_unlock(mp) == 0)
+# define smutex_trylock(mp) (pthread_mutex_trylock(mp) == 0)
+
+#if SM_CONF_POLL
+
+# include <poll.h>
+# define MI_POLLSELECT "poll"
+
+# define MI_POLL_RD_FLAGS (POLLIN | POLLPRI)
+# define MI_POLL_WR_FLAGS (POLLOUT)
+# define MI_MS(timeout) (((timeout)->tv_sec * 1000) + (timeout)->tv_usec)
+
+# define FD_RD_VAR(rds, excs) struct pollfd rds
+# define FD_WR_VAR(wrs) struct pollfd wrs
+
+# define FD_RD_INIT(sd, rds, excs) \
+ (rds).fd = (sd); \
+ (rds).events = MI_POLL_RD_FLAGS; \
+ (rds).revents = 0
+
+# define FD_WR_INIT(sd, wrs) \
+ (wrs).fd = (sd); \
+ (wrs).events = MI_POLL_WR_FLAGS; \
+ (wrs).revents = 0
+
+# define FD_IS_RD_EXC(sd, rds, excs) \
+ (((rds).revents & (POLLERR | POLLHUP | POLLNVAL)) != 0)
+
+# define FD_IS_WR_RDY(sd, wrs) \
+ (((wrs).revents & MI_POLL_WR_FLAGS) != 0)
+
+# define FD_IS_RD_RDY(sd, rds, excs) \
+ (((rds).revents & MI_POLL_RD_FLAGS) != 0)
+
+# define FD_WR_READY(sd, excs, timeout) \
+ poll(&(wrs), 1, MI_MS(timeout))
+
+# define FD_RD_READY(sd, rds, excs, timeout) \
+ poll(&(rds), 1, MI_MS(timeout))
+
+#else /* SM_CONF_POLL */
+
+# include <sm/fdset.h>
+# define MI_POLLSELECT "select"
+
+# define FD_RD_VAR(rds, excs) fd_set rds, excs
+# define FD_WR_VAR(wrs) fd_set wrs
+
+# define FD_RD_INIT(sd, rds, excs) \
+ FD_ZERO(&(rds)); \
+ FD_SET((unsigned int) (sd), &(rds)); \
+ FD_ZERO(&(excs)); \
+ FD_SET((unsigned int) (sd), &(excs))
+
+# define FD_WR_INIT(sd, wrs) \
+ FD_ZERO(&(wrs)); \
+ FD_SET((unsigned int) (sd), &(wrs)); \
+
+# define FD_IS_RD_EXC(sd, rds, excs) FD_ISSET(sd, &(excs))
+# define FD_IS_WR_RDY(sd, wrs) FD_ISSET((sd), &(wrs))
+# define FD_IS_RD_RDY(sd, rds, excs) FD_ISSET((sd), &(rds))
+
+# define FD_WR_READY(sd, wrs, timeout) \
+ select((sd) + 1, NULL, &(wrs), NULL, (timeout))
+# define FD_RD_READY(sd, rds, excs, timeout) \
+ select((sd) + 1, &(rds), NULL, &(excs), (timeout))
+
+#endif /* SM_CONF_POLL */
+
+#include <sys/time.h>
+
+/* version info */
+#define MILTER_PRODUCT_NAME "libmilter"
+#define MILTER_VERSION 100
+
+/* some defaults */
+#define MI_TIMEOUT 7210 /* default timeout for read/write */
+#define MI_CHK_TIME 5 /* checking whether to terminate */
+
+#ifndef MI_SOMAXCONN
+# if SOMAXCONN > 20
+# define MI_SOMAXCONN SOMAXCONN
+# else /* SOMAXCONN */
+# define MI_SOMAXCONN 20
+# endif /* SOMAXCONN */
+#endif /* ! MI_SOMAXCONN */
+
+/* maximum number of repeated failures in mi_listener() */
+#define MAX_FAILS_M 16 /* malloc() */
+#define MAX_FAILS_T 16 /* thread creation */
+#define MAX_FAILS_A 16 /* accept() */
+#define MAX_FAILS_S 16 /* select() */
+
+/* internal "commands", i.e., error codes */
+#define SMFIC_TIMEOUT ((char) 1) /* timeout */
+#define SMFIC_SELECT ((char) 2) /* select error */
+#define SMFIC_MALLOC ((char) 3) /* malloc error */
+#define SMFIC_RECVERR ((char) 4) /* recv() error */
+#define SMFIC_EOF ((char) 5) /* eof */
+#define SMFIC_UNKNERR ((char) 6) /* unknown error */
+#define SMFIC_TOOBIG ((char) 7) /* body chunk too big */
+#define SMFIC_VALIDCMD ' ' /* first valid command */
+
+/* hack */
+#define smi_log syslog
+#define sm_dprintf (void) printf
+#define milter_ret int
+#define SMI_LOG_ERR LOG_ERR
+#define SMI_LOG_FATAL LOG_ERR
+#define SMI_LOG_WARN LOG_WARNING
+#define SMI_LOG_INFO LOG_INFO
+#define SMI_LOG_DEBUG LOG_DEBUG
+
+/* stop? */
+#define MILTER_CONT 0
+#define MILTER_STOP 1
+#define MILTER_ABRT 2
+
+/* functions */
+extern int mi_handle_session __P((SMFICTX_PTR));
+extern int mi_engine __P((SMFICTX_PTR));
+extern int mi_listener __P((char *, int, smfiDesc_ptr, time_t, int));
+extern void mi_clr_macros __P((SMFICTX_PTR, int));
+extern int mi_stop __P((void));
+extern int mi_control_startup __P((char *));
+extern void mi_stop_milters __P((int));
+extern void mi_clean_signals __P((void));
+extern struct hostent *mi_gethostbyname __P((char *, int));
+extern int mi_inet_pton __P((int, const char *, void *));
+extern void mi_closener __P((void));
+extern int mi_opensocket __P((char *, int, int, bool, smfiDesc_ptr));
+
+/* communication functions */
+extern char *mi_rd_cmd __P((socket_t, struct timeval *, char *, size_t *, char *));
+extern int mi_wr_cmd __P((socket_t, struct timeval *, int, char *, size_t));
+extern bool mi_sendok __P((SMFICTX_PTR, int));
+
+
+#endif /* ! _LIBMILTER_H */
diff --git a/usr/src/cmd/sendmail/libmilter/listener.c b/usr/src/cmd/sendmail/libmilter/listener.c
new file mode 100644
index 0000000000..1a30004ac2
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/listener.c
@@ -0,0 +1,955 @@
+/*
+ * Copyright (c) 1999-2004 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.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: listener.c,v 8.111 2004/09/20 21:11:15 msk Exp $")
+
+/*
+** listener.c -- threaded network listener
+*/
+
+#include "libmilter.h"
+#include <sm/errstring.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+# if NETINET || NETINET6
+# include <arpa/inet.h>
+# endif /* NETINET || NETINET6 */
+
+static smutex_t L_Mutex;
+static int L_family;
+static SOCKADDR_LEN_T L_socksize;
+static socket_t listenfd = INVALID_SOCKET;
+
+static socket_t mi_milteropen __P((char *, int, bool, char *));
+static void *mi_thread_handle_wrapper __P((void *));
+
+/*
+** MI_OPENSOCKET -- create the socket where this filter and the MTA will meet
+**
+** Parameters:
+** conn -- connection description
+** backlog -- listen backlog
+** dbg -- debug level
+** rmsocket -- if true, try to unlink() the socket first
+** (UNIX domain sockets only)
+** smfi -- filter structure to use
+**
+** Return value:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_opensocket(conn, backlog, dbg, rmsocket, smfi)
+ char *conn;
+ int backlog;
+ int dbg;
+ bool rmsocket;
+ smfiDesc_ptr smfi;
+{
+ if (smfi == NULL || conn == NULL)
+ return MI_FAILURE;
+
+ if (ValidSocket(listenfd))
+ return MI_SUCCESS;
+
+ if (dbg > 0)
+ {
+ smi_log(SMI_LOG_DEBUG,
+ "%s: Opening listen socket on conn %s",
+ smfi->xxfi_name, conn);
+ }
+ (void) smutex_init(&L_Mutex);
+ (void) smutex_lock(&L_Mutex);
+ listenfd = mi_milteropen(conn, backlog, rmsocket, smfi->xxfi_name);
+ if (!ValidSocket(listenfd))
+ {
+ smi_log(SMI_LOG_FATAL,
+ "%s: Unable to create listening socket on conn %s",
+ smfi->xxfi_name, conn);
+ (void) smutex_unlock(&L_Mutex);
+ return MI_FAILURE;
+ }
+#if !SM_CONF_POLL
+ if (!SM_FD_OK_SELECT(listenfd))
+ {
+ smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
+ smfi->xxfi_name, listenfd, FD_SETSIZE);
+ (void) smutex_unlock(&L_Mutex);
+ return MI_FAILURE;
+ }
+#endif /* !SM_CONF_POLL */
+ (void) smutex_unlock(&L_Mutex);
+ return MI_SUCCESS;
+}
+
+/*
+** MI_MILTEROPEN -- setup socket to listen on
+**
+** Parameters:
+** conn -- connection description
+** backlog -- listen backlog
+** rmsocket -- if true, try to unlink() the socket first
+** (UNIX domain sockets only)
+** name -- name for logging
+**
+** Returns:
+** socket upon success, error code otherwise.
+**
+** Side effect:
+** sets sockpath if UNIX socket.
+*/
+
+#if NETUNIX
+static char *sockpath = NULL;
+#endif /* NETUNIX */
+
+static socket_t
+mi_milteropen(conn, backlog, rmsocket, name)
+ char *conn;
+ int backlog;
+ bool rmsocket;
+ char *name;
+{
+ socket_t sock;
+ int sockopt = 1;
+ int fdflags;
+ size_t len = 0;
+ char *p;
+ char *colon;
+ char *at;
+ SOCKADDR addr;
+
+ if (conn == NULL || conn[0] == '\0')
+ {
+ smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
+ name);
+ return INVALID_SOCKET;
+ }
+ (void) memset(&addr, '\0', sizeof addr);
+
+ /* protocol:filename or protocol:port@host */
+ p = conn;
+ colon = strchr(p, ':');
+ if (colon != NULL)
+ {
+ *colon = '\0';
+
+ if (*p == '\0')
+ {
+#if NETUNIX
+ /* default to AF_UNIX */
+ addr.sa.sa_family = AF_UNIX;
+ L_socksize = sizeof (struct sockaddr_un);
+#else /* NETUNIX */
+# if NETINET
+ /* default to AF_INET */
+ addr.sa.sa_family = AF_INET;
+ L_socksize = sizeof addr.sin;
+# else /* NETINET */
+# if NETINET6
+ /* default to AF_INET6 */
+ addr.sa.sa_family = AF_INET6;
+ L_socksize = sizeof addr.sin6;
+# else /* NETINET6 */
+ /* no protocols available */
+ smi_log(SMI_LOG_ERR,
+ "%s: no valid socket protocols available",
+ name);
+ return INVALID_SOCKET;
+# endif /* NETINET6 */
+# endif /* NETINET */
+#endif /* NETUNIX */
+ }
+#if NETUNIX
+ else if (strcasecmp(p, "unix") == 0 ||
+ strcasecmp(p, "local") == 0)
+ {
+ addr.sa.sa_family = AF_UNIX;
+ L_socksize = sizeof (struct sockaddr_un);
+ }
+#endif /* NETUNIX */
+#if NETINET
+ else if (strcasecmp(p, "inet") == 0)
+ {
+ addr.sa.sa_family = AF_INET;
+ L_socksize = sizeof addr.sin;
+ }
+#endif /* NETINET */
+#if NETINET6
+ else if (strcasecmp(p, "inet6") == 0)
+ {
+ addr.sa.sa_family = AF_INET6;
+ L_socksize = sizeof addr.sin6;
+ }
+#endif /* NETINET6 */
+ else
+ {
+ smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
+ name, p);
+ return INVALID_SOCKET;
+ }
+ *colon++ = ':';
+ }
+ else
+ {
+ colon = p;
+#if NETUNIX
+ /* default to AF_UNIX */
+ addr.sa.sa_family = AF_UNIX;
+ L_socksize = sizeof (struct sockaddr_un);
+#else /* NETUNIX */
+# if NETINET
+ /* default to AF_INET */
+ addr.sa.sa_family = AF_INET;
+ L_socksize = sizeof addr.sin;
+# else /* NETINET */
+# if NETINET6
+ /* default to AF_INET6 */
+ addr.sa.sa_family = AF_INET6;
+ L_socksize = sizeof addr.sin6;
+# else /* NETINET6 */
+ smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
+ name, p);
+ return INVALID_SOCKET;
+# endif /* NETINET6 */
+# endif /* NETINET */
+#endif /* NETUNIX */
+ }
+
+#if NETUNIX
+ if (addr.sa.sa_family == AF_UNIX)
+ {
+# if 0
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
+# endif /* 0 */
+
+ at = colon;
+ len = strlen(colon) + 1;
+ if (len >= sizeof addr.sunix.sun_path)
+ {
+ errno = EINVAL;
+ smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
+ name, colon);
+ return INVALID_SOCKET;
+ }
+ (void) sm_strlcpy(addr.sunix.sun_path, colon,
+ sizeof addr.sunix.sun_path);
+# if 0
+ errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
+ S_IRUSR|S_IWUSR, NULL);
+
+ /* if not safe, don't create */
+ if (errno != 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: UNIX socket name %s unsafe",
+ name, colon);
+ return INVALID_SOCKET;
+ }
+# endif /* 0 */
+ }
+#endif /* NETUNIX */
+
+#if NETINET || NETINET6
+ if (
+# if NETINET
+ addr.sa.sa_family == AF_INET
+# endif /* NETINET */
+# if NETINET && NETINET6
+ ||
+# endif /* NETINET && NETINET6 */
+# if NETINET6
+ addr.sa.sa_family == AF_INET6
+# endif /* NETINET6 */
+ )
+ {
+ unsigned short port;
+
+ /* Parse port@host */
+ at = strchr(colon, '@');
+ if (at == NULL)
+ {
+ switch (addr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ addr.sin.sin_addr.s_addr = INADDR_ANY;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ addr.sin6.sin6_addr = in6addr_any;
+ break;
+# endif /* NETINET6 */
+ }
+ }
+ else
+ *at = '\0';
+
+ if (isascii(*colon) && isdigit(*colon))
+ port = htons((unsigned short) atoi(colon));
+ else
+ {
+# ifdef NO_GETSERVBYNAME
+ smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
+ name, colon);
+ return INVALID_SOCKET;
+# else /* NO_GETSERVBYNAME */
+ register struct servent *sp;
+
+ sp = getservbyname(colon, "tcp");
+ if (sp == NULL)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: unknown port name %s",
+ name, colon);
+ return INVALID_SOCKET;
+ }
+ port = sp->s_port;
+# endif /* NO_GETSERVBYNAME */
+ }
+ if (at != NULL)
+ {
+ *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 &&
+ mi_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)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Invalid numeric domain spec \"%s\"",
+ name, at);
+ return INVALID_SOCKET;
+ }
+ }
+ else
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Invalid numeric domain spec \"%s\"",
+ name, at);
+ return INVALID_SOCKET;
+ }
+ }
+ else
+ {
+ struct hostent *hp = NULL;
+
+ hp = mi_gethostbyname(at, addr.sa.sa_family);
+ if (hp == NULL)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unknown host name %s",
+ name, at);
+ return INVALID_SOCKET;
+ }
+ addr.sa.sa_family = hp->h_addrtype;
+ switch (hp->h_addrtype)
+ {
+# if NETINET
+ case AF_INET:
+ (void) memmove(&addr.sin.sin_addr,
+ hp->h_addr,
+ INADDRSZ);
+ addr.sin.sin_port = port;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ (void) memmove(&addr.sin6.sin6_addr,
+ hp->h_addr,
+ IN6ADDRSZ);
+ addr.sin6.sin6_port = port;
+ break;
+# endif /* NETINET6 */
+
+ default:
+ smi_log(SMI_LOG_ERR,
+ "%s: Unknown protocol for %s (%d)",
+ name, at, hp->h_addrtype);
+ return INVALID_SOCKET;
+ }
+# if NETINET6
+ freehostent(hp);
+# endif /* NETINET6 */
+ }
+ }
+ else
+ {
+ switch (addr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ addr.sin.sin_port = port;
+ break;
+# endif /* NETINET */
+# if NETINET6
+ case AF_INET6:
+ addr.sin6.sin6_port = port;
+ break;
+# endif /* NETINET6 */
+ }
+ }
+ }
+#endif /* NETINET || NETINET6 */
+
+ sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
+ if (!ValidSocket(sock))
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to create new socket: %s",
+ name, sm_errstring(errno));
+ return INVALID_SOCKET;
+ }
+
+ if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 ||
+ fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to set close-on-exec: %s", name,
+ sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
+ sizeof(sockopt)) == -1)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to setsockopt: %s", name,
+ sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+#if NETUNIX
+ if (addr.sa.sa_family == AF_UNIX && rmsocket)
+ {
+ struct stat s;
+
+ if (stat(colon, &s) != 0)
+ {
+ if (errno != ENOENT)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to stat() %s: %s",
+ name, colon, sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+ }
+ else if (!S_ISSOCK(s.st_mode))
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: %s is not a UNIX domain socket",
+ name, colon);
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+ else if (unlink(colon) != 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to remove %s: %s",
+ name, colon, sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+ }
+#endif /* NETUNIX */
+
+ if (bind(sock, &addr.sa, L_socksize) < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to bind to port %s: %s",
+ name, conn, sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+ if (listen(sock, backlog) < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: listen call failed: %s", name,
+ sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+#if NETUNIX
+ if (addr.sa.sa_family == AF_UNIX && len > 0)
+ {
+ /*
+ ** Set global variable sockpath so the UNIX socket can be
+ ** unlink()ed at exit.
+ */
+
+ sockpath = (char *) malloc(len);
+ if (sockpath != NULL)
+ (void) sm_strlcpy(sockpath, colon, len);
+ else
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: can't malloc(%d) for sockpath: %s",
+ name, (int) len, sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+ }
+#endif /* NETUNIX */
+ L_family = addr.sa.sa_family;
+ return sock;
+}
+/*
+** MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
+**
+** Parameters:
+** arg -- argument to pass to mi_handle_session()
+**
+** Returns:
+** results from mi_handle_session()
+*/
+
+static void *
+mi_thread_handle_wrapper(arg)
+ void *arg;
+{
+ return (void *) mi_handle_session(arg);
+}
+
+/*
+** MI_CLOSENER -- close listen socket
+**
+** NOTE: It is assumed that this function is called from a
+** function that has a mutex lock (currently mi_stop_milters()).
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+*/
+
+void
+mi_closener()
+{
+ (void) smutex_lock(&L_Mutex);
+ if (ValidSocket(listenfd))
+ {
+#if NETUNIX
+ bool removable;
+ struct stat sockinfo;
+ struct stat fileinfo;
+
+ removable = sockpath != NULL &&
+ geteuid() != 0 &&
+ fstat(listenfd, &sockinfo) == 0 &&
+ (S_ISFIFO(sockinfo.st_mode)
+# ifdef S_ISSOCK
+ || S_ISSOCK(sockinfo.st_mode)
+# endif /* S_ISSOCK */
+ );
+#endif /* NETUNIX */
+
+ (void) closesocket(listenfd);
+ listenfd = INVALID_SOCKET;
+
+#if NETUNIX
+ /* XXX sleep() some time before doing this? */
+ if (sockpath != NULL)
+ {
+ if (removable &&
+ stat(sockpath, &fileinfo) == 0 &&
+ ((fileinfo.st_dev == sockinfo.st_dev &&
+ fileinfo.st_ino == sockinfo.st_ino)
+# ifdef S_ISSOCK
+ || S_ISSOCK(fileinfo.st_mode)
+# endif /* S_ISSOCK */
+ )
+ &&
+ (S_ISFIFO(fileinfo.st_mode)
+# ifdef S_ISSOCK
+ || S_ISSOCK(fileinfo.st_mode)
+# endif /* S_ISSOCK */
+ ))
+ (void) unlink(sockpath);
+ free(sockpath);
+ sockpath = NULL;
+ }
+#endif /* NETUNIX */
+ }
+ (void) smutex_unlock(&L_Mutex);
+}
+
+/*
+** MI_LISTENER -- Generic listener harness
+**
+** Open up listen port
+** Wait for connections
+**
+** Parameters:
+** conn -- connection description
+** dbg -- debug level
+** smfi -- filter structure to use
+** timeout -- timeout for reads/writes
+** backlog -- listen queue backlog size
+**
+** Returns:
+** MI_SUCCESS -- Exited normally
+** (session finished or we were told to exit)
+** MI_FAILURE -- Network initialization failed.
+*/
+
+#if BROKEN_PTHREAD_SLEEP
+
+/*
+** Solaris 2.6, perhaps others, gets an internal threads library panic
+** when sleep() is used:
+**
+** thread_create() failed, returned 11 (EINVAL)
+** co_enable, thr_create() returned error = 24
+** libthread panic: co_enable failed (PID: 17793 LWP 1)
+** stacktrace:
+** ef526b10
+** ef52646c
+** ef534cbc
+** 156a4
+** 14644
+** 1413c
+** 135e0
+** 0
+*/
+
+# define MI_SLEEP(s) \
+{ \
+ int rs = 0; \
+ struct timeval st; \
+ \
+ st.tv_sec = (s); \
+ st.tv_usec = 0; \
+ if (st.tv_sec > 0) \
+ { \
+ for (;;) \
+ { \
+ rs = select(0, NULL, NULL, NULL, &st); \
+ if (rs < 0 && errno == EINTR) \
+ continue; \
+ if (rs != 0) \
+ { \
+ smi_log(SMI_LOG_ERR, \
+ "MI_SLEEP(): select() returned non-zero result %d, errno = %d", \
+ rs, errno); \
+ } \
+ break; \
+ } \
+ } \
+}
+#else /* BROKEN_PTHREAD_SLEEP */
+# define MI_SLEEP(s) sleep((s))
+#endif /* BROKEN_PTHREAD_SLEEP */
+
+int
+mi_listener(conn, dbg, smfi, timeout, backlog)
+ char *conn;
+ int dbg;
+ smfiDesc_ptr smfi;
+ time_t timeout;
+ int backlog;
+{
+ socket_t connfd = INVALID_SOCKET;
+#if _FFR_DUP_FD
+ socket_t dupfd = INVALID_SOCKET;
+#endif /* _FFR_DUP_FD */
+ int sockopt = 1;
+ int r, mistop;
+ int ret = MI_SUCCESS;
+ int mcnt = 0; /* error count for malloc() failures */
+ int tcnt = 0; /* error count for thread_create() failures */
+ int acnt = 0; /* error count for accept() failures */
+ int scnt = 0; /* error count for select() failures */
+ int save_errno = 0;
+ sthread_t thread_id;
+ _SOCK_ADDR cliaddr;
+ SOCKADDR_LEN_T clilen;
+ SMFICTX_PTR ctx;
+ FD_RD_VAR(rds, excs);
+ struct timeval chktime;
+
+ if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE)
+ return MI_FAILURE;
+
+ clilen = L_socksize;
+ while ((mistop = mi_stop()) == MILTER_CONT)
+ {
+ (void) smutex_lock(&L_Mutex);
+ if (!ValidSocket(listenfd))
+ {
+ ret = MI_FAILURE;
+ smi_log(SMI_LOG_ERR,
+ "%s: listenfd=%d corrupted, terminating, errno=%d",
+ smfi->xxfi_name, listenfd, errno);
+ (void) smutex_unlock(&L_Mutex);
+ break;
+ }
+
+ /* select on interface ports */
+ FD_RD_INIT(listenfd, rds, excs);
+ chktime.tv_sec = MI_CHK_TIME;
+ chktime.tv_usec = 0;
+ r = FD_RD_READY(listenfd, rds, excs, &chktime);
+ if (r == 0) /* timeout */
+ {
+ (void) smutex_unlock(&L_Mutex);
+ continue; /* just check mi_stop() */
+ }
+ if (r < 0)
+ {
+ save_errno = errno;
+ (void) smutex_unlock(&L_Mutex);
+ if (save_errno == EINTR)
+ continue;
+ scnt++;
+ smi_log(SMI_LOG_ERR,
+ "%s: select() failed (%s), %s",
+ smfi->xxfi_name, sm_errstring(save_errno),
+ scnt >= MAX_FAILS_S ? "abort" : "try again");
+ MI_SLEEP(scnt);
+ if (scnt >= MAX_FAILS_S)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+ continue;
+ }
+ if (!FD_IS_RD_RDY(listenfd, rds, excs))
+ {
+ /* some error: just stop for now... */
+ ret = MI_FAILURE;
+ (void) smutex_unlock(&L_Mutex);
+ smi_log(SMI_LOG_ERR,
+ "%s: %s() returned exception for socket, abort",
+ smfi->xxfi_name, MI_POLLSELECT);
+ break;
+ }
+ scnt = 0; /* reset error counter for select() */
+
+ (void) memset(&cliaddr, '\0', sizeof cliaddr);
+ connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
+ &clilen);
+ save_errno = errno;
+ (void) smutex_unlock(&L_Mutex);
+
+ /*
+ ** If remote side closes before
+ ** accept() finishes, sockaddr
+ ** might not be fully filled in.
+ */
+
+ if (ValidSocket(connfd) &&
+ (clilen == 0 ||
+# ifdef BSD4_4_SOCKADDR
+ cliaddr.sa.sa_len == 0 ||
+# endif /* BSD4_4_SOCKADDR */
+ cliaddr.sa.sa_family != L_family))
+ {
+ (void) closesocket(connfd);
+ connfd = INVALID_SOCKET;
+ save_errno = EINVAL;
+ }
+
+#if !SM_CONF_POLL
+ /* check if acceptable for select() */
+ if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd))
+ {
+ (void) closesocket(connfd);
+ connfd = INVALID_SOCKET;
+ save_errno = ERANGE;
+ }
+#endif /* !SM_CONF_POLL */
+
+ if (!ValidSocket(connfd))
+ {
+ if (save_errno == EINTR
+#ifdef EAGAIN
+ || save_errno == EAGAIN
+#endif /* EAGAIN */
+#ifdef ECONNABORTED
+ || save_errno == ECONNABORTED
+#endif /* ECONNABORTED */
+#ifdef EMFILE
+ || save_errno == EMFILE
+#endif /* EMFILE */
+#ifdef ENFILE
+ || save_errno == ENFILE
+#endif /* ENFILE */
+#ifdef ENOBUFS
+ || save_errno == ENOBUFS
+#endif /* ENOBUFS */
+#ifdef ENOMEM
+ || save_errno == ENOMEM
+#endif /* ENOMEM */
+#ifdef ENOSR
+ || save_errno == ENOSR
+#endif /* ENOSR */
+#ifdef EWOULDBLOCK
+ || save_errno == EWOULDBLOCK
+#endif /* EWOULDBLOCK */
+ )
+ continue;
+ acnt++;
+ smi_log(SMI_LOG_ERR,
+ "%s: accept() returned invalid socket (%s), %s",
+ smfi->xxfi_name, sm_errstring(save_errno),
+ acnt >= MAX_FAILS_A ? "abort" : "try again");
+ MI_SLEEP(acnt);
+ if (acnt >= MAX_FAILS_A)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+ continue;
+ }
+ acnt = 0; /* reset error counter for accept() */
+#if _FFR_DUP_FD
+ dupfd = fcntl(connfd, F_DUPFD, 256);
+ if (ValidSocket(dupfd)
+# if !SM_CONF_POLL
+ && SM_FD_OK_SELECT(dupfd)
+# endif /* !SM_CONF_POLL */
+ )
+ {
+ close(connfd);
+ connfd = dupfd;
+ dupfd = INVALID_SOCKET;
+ }
+#endif /* _FFR_DUP_FD */
+
+ if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
+ (void *) &sockopt, sizeof sockopt) < 0)
+ {
+ smi_log(SMI_LOG_WARN, "%s: setsockopt() failed (%s)",
+ smfi->xxfi_name, sm_errstring(errno));
+ /* XXX: continue? */
+ }
+ if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
+ {
+ (void) closesocket(connfd);
+ mcnt++;
+ smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s",
+ smfi->xxfi_name, sm_errstring(save_errno),
+ mcnt >= MAX_FAILS_M ? "abort" : "try again");
+ MI_SLEEP(mcnt);
+ if (mcnt >= MAX_FAILS_M)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+ continue;
+ }
+ mcnt = 0; /* reset error counter for malloc() */
+ (void) memset(ctx, '\0', sizeof *ctx);
+ ctx->ctx_sd = connfd;
+ ctx->ctx_dbg = dbg;
+ ctx->ctx_timeout = timeout;
+ ctx->ctx_smfi = smfi;
+#if 0
+ if (smfi->xxfi_eoh == NULL)
+ if (smfi->xxfi_eom == NULL)
+ if (smfi->xxfi_abort == NULL)
+ if (smfi->xxfi_close == NULL)
+#endif /* 0 */
+ if (smfi->xxfi_connect == NULL)
+ ctx->ctx_pflags |= SMFIP_NOCONNECT;
+ if (smfi->xxfi_helo == NULL)
+ ctx->ctx_pflags |= SMFIP_NOHELO;
+ if (smfi->xxfi_envfrom == NULL)
+ ctx->ctx_pflags |= SMFIP_NOMAIL;
+ if (smfi->xxfi_envrcpt == NULL)
+ ctx->ctx_pflags |= SMFIP_NORCPT;
+ if (smfi->xxfi_header == NULL)
+ ctx->ctx_pflags |= SMFIP_NOHDRS;
+ if (smfi->xxfi_eoh == NULL)
+ ctx->ctx_pflags |= SMFIP_NOEOH;
+ if (smfi->xxfi_body == NULL)
+ ctx->ctx_pflags |= SMFIP_NOBODY;
+
+ if ((r = thread_create(&thread_id,
+ mi_thread_handle_wrapper,
+ (void *) ctx)) != 0)
+ {
+ tcnt++;
+ smi_log(SMI_LOG_ERR,
+ "%s: thread_create() failed: %d, %s",
+ smfi->xxfi_name, r,
+ tcnt >= MAX_FAILS_T ? "abort" : "try again");
+ MI_SLEEP(tcnt);
+ (void) closesocket(connfd);
+ free(ctx);
+ if (tcnt >= MAX_FAILS_T)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+ continue;
+ }
+ tcnt = 0;
+ }
+ if (ret != MI_SUCCESS)
+ mi_stop_milters(MILTER_ABRT);
+ else
+ {
+ if (mistop != MILTER_CONT)
+ smi_log(SMI_LOG_INFO, "%s: mi_stop=%d",
+ smfi->xxfi_name, mistop);
+ mi_closener();
+ }
+ (void) smutex_destroy(&L_Mutex);
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libmilter/llib-lmilter b/usr/src/cmd/sendmail/libmilter/llib-lmilter
new file mode 100644
index 0000000000..0d56627c3e
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/llib-lmilter
@@ -0,0 +1,32 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include "libmilter.h"
diff --git a/usr/src/cmd/sendmail/libmilter/main.c b/usr/src/cmd/sendmail/libmilter/main.c
new file mode 100644
index 0000000000..4115951fee
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/main.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 1999-2003 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.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: main.c,v 8.79 2003/10/20 22:25:09 ca Exp $")
+
+#define _DEFINE 1
+#include "libmilter.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+
+
+static smfiDesc_ptr smfi = NULL;
+
+/*
+** SMFI_REGISTER -- register a filter description
+**
+** Parameters:
+** smfilter -- description of filter to register
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_register(smfilter)
+ smfiDesc_str smfilter;
+{
+ size_t len;
+
+ if (smfi == NULL)
+ {
+ smfi = (smfiDesc_ptr) malloc(sizeof *smfi);
+ if (smfi == NULL)
+ return MI_FAILURE;
+ }
+ (void) memcpy(smfi, &smfilter, sizeof *smfi);
+ if (smfilter.xxfi_name == NULL)
+ smfilter.xxfi_name = "Unknown";
+
+ len = strlen(smfilter.xxfi_name) + 1;
+ smfi->xxfi_name = (char *) malloc(len);
+ if (smfi->xxfi_name == NULL)
+ return MI_FAILURE;
+ (void) sm_strlcpy(smfi->xxfi_name, smfilter.xxfi_name, len);
+
+ /* compare milter version with hard coded version */
+ if (smfi->xxfi_version != SMFI_VERSION)
+ {
+ /* hard failure for now! */
+ smi_log(SMI_LOG_ERR,
+ "%s: smfi_register: version mismatch application: %d != milter: %d",
+ smfi->xxfi_name, smfi->xxfi_version,
+ (int) SMFI_VERSION);
+
+ /* XXX how about smfi? */
+ free(smfi->xxfi_name);
+ return MI_FAILURE;
+ }
+
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_STOP -- stop milter
+**
+** Parameters:
+** none.
+**
+** Returns:
+** success.
+*/
+
+int
+smfi_stop()
+{
+ mi_stop_milters(MILTER_STOP);
+ return MI_SUCCESS;
+}
+
+/*
+** Default values for some variables.
+** Most of these can be changed with the functions below.
+*/
+
+static int dbg = 0;
+static char *conn = NULL;
+static int timeout = MI_TIMEOUT;
+static int backlog = MI_SOMAXCONN;
+
+/*
+** SMFI_OPENSOCKET -- try the socket setup to make sure we'll be
+** able to start up
+**
+** Parameters:
+** rmsocket -- if true, instructs libmilter to attempt
+** to remove the socket before creating it;
+** only applies for "local:" or "unix:" sockets
+**
+** Return:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_opensocket(rmsocket)
+ bool rmsocket;
+{
+ if (smfi == NULL || conn == NULL)
+ return MI_FAILURE;
+
+ return mi_opensocket(conn, backlog, dbg, rmsocket, smfi);
+}
+
+/*
+** SMFI_SETDBG -- set debug level.
+**
+** Parameters:
+** odbg -- new debug level.
+**
+** Returns:
+** MI_SUCCESS
+*/
+
+int
+smfi_setdbg(odbg)
+ int odbg;
+{
+ dbg = odbg;
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_SETTIMEOUT -- set timeout (for read/write).
+**
+** Parameters:
+** otimeout -- new timeout.
+**
+** Returns:
+** MI_SUCCESS
+*/
+
+int
+smfi_settimeout(otimeout)
+ int otimeout;
+{
+ timeout = otimeout;
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_SETCONN -- set connection information (socket description)
+**
+** Parameters:
+** oconn -- new connection information.
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_setconn(oconn)
+ char *oconn;
+{
+ size_t l;
+
+ if (oconn == NULL || *oconn == '\0')
+ return MI_FAILURE;
+ l = strlen(oconn) + 1;
+ if ((conn = (char *) malloc(l)) == NULL)
+ return MI_FAILURE;
+ if (sm_strlcpy(conn, oconn, l) >= l)
+ return MI_FAILURE;
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_SETBACKLOG -- set backlog
+**
+** Parameters:
+** obacklog -- new backlog.
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_setbacklog(obacklog)
+ int obacklog;
+{
+ if (obacklog <= 0)
+ return MI_FAILURE;
+ backlog = obacklog;
+ return MI_SUCCESS;
+}
+
+
+/*
+** SMFI_MAIN -- setup milter connnection and start listener.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_main()
+{
+ int r;
+
+ (void) signal(SIGPIPE, SIG_IGN);
+ if (conn == NULL)
+ {
+ smi_log(SMI_LOG_FATAL, "%s: missing connection information",
+ smfi->xxfi_name);
+ return MI_FAILURE;
+ }
+
+ (void) atexit(mi_clean_signals);
+ if (mi_control_startup(smfi->xxfi_name) != MI_SUCCESS)
+ {
+ smi_log(SMI_LOG_FATAL,
+ "%s: Couldn't start signal thread",
+ smfi->xxfi_name);
+ return MI_FAILURE;
+ }
+ r = MI_SUCCESS;
+
+ /* Startup the listener */
+ if (mi_listener(conn, dbg, smfi, timeout, backlog) != MI_SUCCESS)
+ r = MI_FAILURE;
+
+ return r;
+}
+
diff --git a/usr/src/cmd/sendmail/libmilter/signal.c b/usr/src/cmd/sendmail/libmilter/signal.c
new file mode 100644
index 0000000000..e3744cdc21
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/signal.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 1999-2004 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.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: signal.c,v 8.42 2004/08/20 21:10:30 ca Exp $")
+
+#include "libmilter.h"
+
+/*
+** thread to handle signals
+*/
+
+static smutex_t M_Mutex;
+
+static int MilterStop = MILTER_CONT;
+
+static void *mi_signal_thread __P((void *));
+static int mi_spawn_signal_thread __P((char *));
+
+/*
+** MI_STOP -- return value of MilterStop
+**
+** Parameters:
+** none.
+**
+** Returns:
+** value of MilterStop
+*/
+
+int
+mi_stop()
+{
+ return MilterStop;
+}
+/*
+** MI_STOP_MILTERS -- set value of MilterStop
+**
+** Parameters:
+** v -- new value for MilterStop.
+**
+** Returns:
+** none.
+*/
+
+void
+mi_stop_milters(v)
+ int v;
+{
+ (void) smutex_lock(&M_Mutex);
+ if (MilterStop < v)
+ MilterStop = v;
+
+ /* close listen socket */
+ mi_closener();
+ (void) smutex_unlock(&M_Mutex);
+}
+/*
+** MI_CLEAN_SIGNALS -- clean up signal handler thread
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+*/
+
+void
+mi_clean_signals()
+{
+ (void) smutex_destroy(&M_Mutex);
+}
+/*
+** MI_SIGNAL_THREAD -- thread to deal with signals
+**
+** Parameters:
+** name -- name of milter
+**
+** Returns:
+** NULL
+*/
+
+static void *
+mi_signal_thread(name)
+ void *name;
+{
+ int sig, errs;
+ sigset_t set;
+
+ (void) sigemptyset(&set);
+ (void) sigaddset(&set, SIGHUP);
+ (void) sigaddset(&set, SIGTERM);
+
+ /* Handle Ctrl-C gracefully for debugging */
+ (void) sigaddset(&set, SIGINT);
+ errs = 0;
+
+ for (;;)
+ {
+ sig = 0;
+#if defined(SOLARIS) || defined(__svr5__)
+ if ((sig = sigwait(&set)) < 0)
+#else /* defined(SOLARIS) || defined(__svr5__) */
+ if (sigwait(&set, &sig) != 0)
+#endif /* defined(SOLARIS) || defined(__svr5__) */
+ {
+ /* this can happen on OSF/1 (at least) */
+ if (errno == EINTR)
+ continue;
+ smi_log(SMI_LOG_ERR,
+ "%s: sigwait returned error: %d",
+ (char *)name, errno);
+ if (++errs > MAX_FAILS_T)
+ {
+ mi_stop_milters(MILTER_ABRT);
+ return NULL;
+ }
+ continue;
+ }
+ errs = 0;
+
+ switch (sig)
+ {
+ case SIGHUP:
+ case SIGTERM:
+ mi_stop_milters(MILTER_STOP);
+ return NULL;
+ case SIGINT:
+ mi_stop_milters(MILTER_ABRT);
+ return NULL;
+ default:
+ smi_log(SMI_LOG_ERR,
+ "%s: sigwait returned unmasked signal: %d",
+ (char *)name, sig);
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
+/*
+** MI_SPAWN_SIGNAL_THREAD -- spawn thread to handle signals
+**
+** Parameters:
+** name -- name of milter
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+static int
+mi_spawn_signal_thread(name)
+ char *name;
+{
+ sthread_t tid;
+ int r;
+ sigset_t set;
+
+ /* Mask HUP and KILL signals */
+ (void) sigemptyset(&set);
+ (void) sigaddset(&set, SIGHUP);
+ (void) sigaddset(&set, SIGTERM);
+ (void) sigaddset(&set, SIGINT);
+
+ if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Couldn't mask HUP and KILL signals", name);
+ return MI_FAILURE;
+ }
+ r = thread_create(&tid, mi_signal_thread, (void *)name);
+ if (r != 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Couldn't start signal thread: %d",
+ name, r);
+ return MI_FAILURE;
+ }
+ return MI_SUCCESS;
+}
+/*
+** MI_CONTROL_STARTUP -- startup for thread to handle signals
+**
+** Parameters:
+** name -- name of milter
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_control_startup(name)
+ char *name;
+{
+
+ if (!smutex_init(&M_Mutex))
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Couldn't initialize control pipe mutex", name);
+ return MI_FAILURE;
+ }
+
+ /*
+ ** spawn_signal_thread must happen before other threads are spawned
+ ** off so that it can mask the right signals and other threads
+ ** will inherit that mask.
+ */
+ if (mi_spawn_signal_thread(name) == MI_FAILURE)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Couldn't spawn signal thread", name);
+ (void) smutex_destroy(&M_Mutex);
+ return MI_FAILURE;
+ }
+ return MI_SUCCESS;
+}
diff --git a/usr/src/cmd/sendmail/libmilter/sm_gethost.c b/usr/src/cmd/sendmail/libmilter/sm_gethost.c
new file mode 100644
index 0000000000..504260ce50
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/sm_gethost.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1999-2001, 2004 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.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: sm_gethost.c,v 8.27 2004/08/20 21:12:37 ca Exp $")
+
+#include <sendmail.h>
+#if NETINET || NETINET6
+# include <arpa/inet.h>
+#endif /* NETINET || NETINET6 */
+#include "libmilter.h"
+
+/*
+** MI_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
+**
+** Some operating systems have wierd problems with the gethostbyXXX
+** routines. For example, Solaris versions at least through 2.3
+** don't properly deliver a canonical h_name field. This tries to
+** work around these problems.
+**
+** Support IPv6 as well as IPv4.
+*/
+
+#if NETINET6 && NEEDSGETIPNODE
+
+static struct hostent *getipnodebyname __P((char *, int, int, int *));
+
+# ifndef AI_ADDRCONFIG
+# define AI_ADDRCONFIG 0 /* dummy */
+# endif /* ! AI_ADDRCONFIG */
+# ifndef AI_ALL
+# define AI_ALL 0 /* dummy */
+# endif /* ! AI_ALL */
+# ifndef AI_DEFAULT
+# define AI_DEFAULT 0 /* dummy */
+# endif /* ! AI_DEFAULT */
+
+static struct hostent *
+getipnodebyname(name, family, flags, err)
+ char *name;
+ int family;
+ int flags;
+ int *err;
+{
+ bool resv6 = true;
+ struct hostent *h;
+
+ if (family == AF_INET6)
+ {
+ /* From RFC2133, section 6.1 */
+ resv6 = bitset(RES_USE_INET6, _res.options);
+ _res.options |= RES_USE_INET6;
+ }
+ SM_SET_H_ERRNO(0);
+ h = gethostbyname(name);
+ if (family == AF_INET6 && !resv6)
+ _res.options &= ~RES_USE_INET6;
+ *err = h_errno;
+ return h;
+}
+
+void
+freehostent(h)
+ struct hostent *h;
+{
+ /*
+ ** Stub routine -- if they don't have getipnodeby*(),
+ ** they probably don't have the free routine either.
+ */
+
+ return;
+}
+#endif /* NEEDSGETIPNODE && NETINET6 */
+
+struct hostent *
+mi_gethostbyname(name, family)
+ char *name;
+ int family;
+{
+ struct hostent *h = NULL;
+#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4))
+# if SOLARIS == 20300 || SOLARIS == 203
+ static struct hostent hp;
+ static char buf[1000];
+ extern struct hostent *_switch_gethostbyname_r();
+
+ h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
+# else /* SOLARIS == 20300 || SOLARIS == 203 */
+ extern struct hostent *__switch_gethostbyname();
+
+ h = __switch_gethostbyname(name);
+# endif /* SOLARIS == 20300 || SOLARIS == 203 */
+#else /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */
+# if NETINET6
+ int flags = AI_DEFAULT|AI_ALL;
+ int err;
+# endif /* NETINET6 */
+
+# if NETINET6
+# if ADDRCONFIG_IS_BROKEN
+ flags &= ~AI_ADDRCONFIG;
+# endif /* ADDRCONFIG_IS_BROKEN */
+ h = getipnodebyname(name, family, flags, &err);
+ SM_SET_H_ERRNO(err);
+# else /* NETINET6 */
+ h = gethostbyname(name);
+# endif /* NETINET6 */
+
+#endif /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */
+ return h;
+}
+
+#if NETINET6
+/*
+** MI_INET_PTON -- convert printed form to network address.
+**
+** Wrapper for inet_pton() which handles IPv6: labels.
+**
+** Parameters:
+** family -- address family
+** src -- string
+** dst -- destination address structure
+**
+** Returns:
+** 1 if the address was valid
+** 0 if the address wasn't parseable
+** -1 if error
+*/
+
+int
+mi_inet_pton(family, src, dst)
+ int family;
+ const char *src;
+ void *dst;
+{
+ if (family == AF_INET6 &&
+ strncasecmp(src, "IPv6:", 5) == 0)
+ src += 5;
+ return inet_pton(family, src, dst);
+}
+#endif /* NETINET6 */
diff --git a/usr/src/cmd/sendmail/libmilter/smfi.c b/usr/src/cmd/sendmail/libmilter/smfi.c
new file mode 100644
index 0000000000..a04811b370
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/smfi.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 1999-2004 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.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: smfi.c,v 8.73 2004/09/20 21:26:57 ca Exp $")
+#include <sm/varargs.h>
+#include "libmilter.h"
+
+static int smfi_header __P((SMFICTX *, int, int, char *, char *));
+static int myisenhsc __P((const char *, int));
+
+/* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
+#define MAXREPLYLEN 980 /* max. length of a reply string */
+#define MAXREPLIES 32 /* max. number of reply strings */
+
+/*
+** SMFI_HEADER -- send a header to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** cmd -- Header modification command
+** hdridx -- Header index
+** headerf -- Header field name
+** headerv -- Header field value
+**
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+static int
+smfi_header(ctx, cmd, hdridx, headerf, headerv)
+ SMFICTX *ctx;
+ int cmd;
+ int hdridx;
+ char *headerf;
+ char *headerv;
+{
+ size_t len, l1, l2, offset;
+ int r;
+ mi_int32 v;
+ char *buf;
+ struct timeval timeout;
+
+ if (headerf == NULL || *headerf == '\0' || headerv == NULL)
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ l1 = strlen(headerf) + 1;
+ l2 = strlen(headerv) + 1;
+ len = l1 + l2;
+ if (hdridx >= 0)
+ len += MILTER_LEN_BYTES;
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE;
+ offset = 0;
+ if (hdridx >= 0)
+ {
+ v = htonl(hdridx);
+ (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
+ offset += MILTER_LEN_BYTES;
+ }
+ (void) memcpy(buf + offset, headerf, l1);
+ (void) memcpy(buf + offset + l1, headerv, l2);
+ r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
+ free(buf);
+ return r;
+}
+
+/*
+** SMFI_ADDHEADER -- send a new header to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** headerf -- Header field name
+** headerv -- Header field value
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_addheader(ctx, headerf, headerv)
+ SMFICTX *ctx;
+ char *headerf;
+ char *headerv;
+{
+ if (!mi_sendok(ctx, SMFIF_ADDHDRS))
+ return MI_FAILURE;
+
+ return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
+}
+
+/*
+** SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
+**
+** Parameters:
+** ctx -- Opaque context structure
+** hdridx -- index into header list where insertion should occur
+** headerf -- Header field name
+** headerv -- Header field value
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_insheader(ctx, hdridx, headerf, headerv)
+ SMFICTX *ctx;
+ int hdridx;
+ char *headerf;
+ char *headerv;
+{
+ if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
+ return MI_FAILURE;
+
+ return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
+}
+
+/*
+** SMFI_CHGHEADER -- send a changed header to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** headerf -- Header field name
+** hdridx -- Header index value
+** headerv -- Header field value
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_chgheader(ctx, headerf, hdridx, headerv)
+ SMFICTX *ctx;
+ char *headerf;
+ mi_int32 hdridx;
+ char *headerv;
+{
+ if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
+ return MI_FAILURE;
+ if (headerv == NULL)
+ headerv = "";
+
+ return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
+}
+
+/*
+** SMFI_ADDRCPT -- send an additional recipient to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** rcpt -- recipient address
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_addrcpt(ctx, rcpt)
+ SMFICTX *ctx;
+ char *rcpt;
+{
+ size_t len;
+ struct timeval timeout;
+
+ if (rcpt == NULL || *rcpt == '\0')
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_ADDRCPT))
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ len = strlen(rcpt) + 1;
+ return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
+}
+
+/*
+** SMFI_DELRCPT -- send a recipient to be removed to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** rcpt -- recipient address
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_delrcpt(ctx, rcpt)
+ SMFICTX *ctx;
+ char *rcpt;
+{
+ size_t len;
+ struct timeval timeout;
+
+ if (rcpt == NULL || *rcpt == '\0')
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_DELRCPT))
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ len = strlen(rcpt) + 1;
+ return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
+}
+
+/*
+** SMFI_REPLACEBODY -- send a body chunk to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** bodyp -- body chunk
+** bodylen -- length of body chunk
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_replacebody(ctx, bodyp, bodylen)
+ SMFICTX *ctx;
+ unsigned char *bodyp;
+ int bodylen;
+{
+ int len, off, r;
+ struct timeval timeout;
+
+ if (bodylen < 0 ||
+ (bodyp == NULL && bodylen > 0))
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_CHGBODY))
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+
+ /* split body chunk if necessary */
+ off = 0;
+ while (bodylen > 0)
+ {
+ len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
+ bodylen;
+ if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
+ (char *) (bodyp + off), len)) != MI_SUCCESS)
+ return r;
+ off += len;
+ bodylen -= len;
+ }
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_QUARANTINE -- quarantine an envelope
+**
+** Parameters:
+** ctx -- Opaque context structure
+** reason -- why?
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_quarantine(ctx, reason)
+ SMFICTX *ctx;
+ char *reason;
+{
+ size_t len;
+ int r;
+ char *buf;
+ struct timeval timeout;
+
+ if (reason == NULL || *reason == '\0')
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_QUARANTINE))
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ len = strlen(reason) + 1;
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE;
+ (void) memcpy(buf, reason, len);
+ r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
+ free(buf);
+ return r;
+}
+
+/*
+** MYISENHSC -- check whether a string contains an enhanced status code
+**
+** Parameters:
+** s -- string with possible enhanced status code.
+** delim -- delim for enhanced status code.
+**
+** Returns:
+** 0 -- no enhanced status code.
+** >4 -- length of enhanced status code.
+**
+** Side Effects:
+** none.
+*/
+
+static int
+myisenhsc(s, delim)
+ const char *s;
+ int delim;
+{
+ int l, h;
+
+ if (s == NULL)
+ return 0;
+ if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
+ return 0;
+ h = 0;
+ l = 2;
+ while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
+ ++h;
+ if (h == 0 || s[l + h] != '.')
+ return 0;
+ l += h + 1;
+ h = 0;
+ while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
+ ++h;
+ if (h == 0 || s[l + h] != delim)
+ return 0;
+ return l + h;
+}
+
+/*
+** SMFI_SETREPLY -- set the reply code for the next reply to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** rcode -- The three-digit (RFC 821) SMTP reply code.
+** xcode -- The extended (RFC 2034) reply code.
+** message -- The text part of the SMTP reply.
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_setreply(ctx, rcode, xcode, message)
+ SMFICTX *ctx;
+ char *rcode;
+ char *xcode;
+ char *message;
+{
+ size_t len;
+ char *buf;
+
+ if (rcode == NULL || ctx == NULL)
+ return MI_FAILURE;
+
+ /* ### <sp> \0 */
+ len = strlen(rcode) + 2;
+ if (len != 5)
+ return MI_FAILURE;
+ if ((rcode[0] != '4' && rcode[0] != '5') ||
+ !isascii(rcode[1]) || !isdigit(rcode[1]) ||
+ !isascii(rcode[2]) || !isdigit(rcode[2]))
+ return MI_FAILURE;
+ if (xcode != NULL)
+ {
+ if (!myisenhsc(xcode, '\0'))
+ return MI_FAILURE;
+ len += strlen(xcode) + 1;
+ }
+ if (message != NULL)
+ {
+ size_t ml;
+
+ /* XXX check also for unprintable chars? */
+ if (strpbrk(message, "\r\n") != NULL)
+ return MI_FAILURE;
+ ml = strlen(message);
+ if (ml > MAXREPLYLEN)
+ return MI_FAILURE;
+ len += ml + 1;
+ }
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE; /* oops */
+ (void) sm_strlcpy(buf, rcode, len);
+ (void) sm_strlcat(buf, " ", len);
+ if (xcode != NULL)
+ (void) sm_strlcat(buf, xcode, len);
+ if (message != NULL)
+ {
+ if (xcode != NULL)
+ (void) sm_strlcat(buf, " ", len);
+ (void) sm_strlcat(buf, message, len);
+ }
+ if (ctx->ctx_reply != NULL)
+ free(ctx->ctx_reply);
+ ctx->ctx_reply = buf;
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** rcode -- The three-digit (RFC 821) SMTP reply code.
+** xcode -- The extended (RFC 2034) reply code.
+** txt, ... -- The text part of the SMTP reply,
+** MUST be terminated with NULL.
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+#if SM_VA_STD
+smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
+#else /* SM_VA_STD */
+smfi_setmlreply(ctx, rcode, xcode, va_alist)
+ SMFICTX *ctx;
+ const char *rcode;
+ const char *xcode;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ size_t len;
+ size_t rlen;
+ int args;
+ char *buf, *txt;
+ const char *xc;
+ char repl[16];
+ SM_VA_LOCAL_DECL
+
+ if (rcode == NULL || ctx == NULL)
+ return MI_FAILURE;
+
+ /* ### <sp> */
+ len = strlen(rcode) + 1;
+ if (len != 4)
+ return MI_FAILURE;
+ if ((rcode[0] != '4' && rcode[0] != '5') ||
+ !isascii(rcode[1]) || !isdigit(rcode[1]) ||
+ !isascii(rcode[2]) || !isdigit(rcode[2]))
+ return MI_FAILURE;
+ if (xcode != NULL)
+ {
+ if (!myisenhsc(xcode, '\0'))
+ return MI_FAILURE;
+ xc = xcode;
+ }
+ else
+ {
+ if (rcode[0] == '4')
+ xc = "4.0.0";
+ else
+ xc = "5.0.0";
+ }
+
+ /* add trailing space */
+ len += strlen(xc) + 1;
+ rlen = len;
+ args = 0;
+ SM_VA_START(ap, xcode);
+ while ((txt = SM_VA_ARG(ap, char *)) != NULL)
+ {
+ size_t tl;
+
+ tl = strlen(txt);
+ if (tl > MAXREPLYLEN)
+ break;
+
+ /* this text, reply codes, \r\n */
+ len += tl + 2 + rlen;
+ if (++args > MAXREPLIES)
+ break;
+
+ /* XXX check also for unprintable chars? */
+ if (strpbrk(txt, "\r\n") != NULL)
+ break;
+ }
+ SM_VA_END(ap);
+ if (txt != NULL)
+ return MI_FAILURE;
+
+ /* trailing '\0' */
+ ++len;
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE; /* oops */
+ (void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
+ (void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
+ xc, " ");
+ SM_VA_START(ap, xcode);
+ txt = SM_VA_ARG(ap, char *);
+ if (txt != NULL)
+ {
+ (void) sm_strlcat2(buf, " ", txt, len);
+ while ((txt = SM_VA_ARG(ap, char *)) != NULL)
+ {
+ if (--args <= 1)
+ repl[3] = ' ';
+ (void) sm_strlcat2(buf, "\r\n", repl, len);
+ (void) sm_strlcat(buf, txt, len);
+ }
+ }
+ if (ctx->ctx_reply != NULL)
+ free(ctx->ctx_reply);
+ ctx->ctx_reply = buf;
+ SM_VA_END(ap);
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_SETPRIV -- set private data
+**
+** Parameters:
+** ctx -- Opaque context structure
+** privatedata -- pointer to private data
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_setpriv(ctx, privatedata)
+ SMFICTX *ctx;
+ void *privatedata;
+{
+ if (ctx == NULL)
+ return MI_FAILURE;
+ ctx->ctx_privdata = privatedata;
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_GETPRIV -- get private data
+**
+** Parameters:
+** ctx -- Opaque context structure
+**
+** Returns:
+** pointer to private data
+*/
+
+void *
+smfi_getpriv(ctx)
+ SMFICTX *ctx;
+{
+ if (ctx == NULL)
+ return NULL;
+ return ctx->ctx_privdata;
+}
+
+/*
+** SMFI_GETSYMVAL -- get the value of a macro
+**
+** See explanation in mfapi.h about layout of the structures.
+**
+** Parameters:
+** ctx -- Opaque context structure
+** symname -- name of macro
+**
+** Returns:
+** value of macro (NULL in case of failure)
+*/
+
+char *
+smfi_getsymval(ctx, symname)
+ SMFICTX *ctx;
+ char *symname;
+{
+ int i;
+ char **s;
+ char one[2];
+ char braces[4];
+
+ if (ctx == NULL || symname == NULL || *symname == '\0')
+ return NULL;
+
+ if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
+ {
+ one[0] = symname[1];
+ one[1] = '\0';
+ }
+ else
+ one[0] = '\0';
+ if (strlen(symname) == 1)
+ {
+ braces[0] = '{';
+ braces[1] = *symname;
+ braces[2] = '}';
+ braces[3] = '\0';
+ }
+ else
+ braces[0] = '\0';
+
+ /* search backwards through the macro array */
+ for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
+ {
+ if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
+ ctx->ctx_mac_buf[i] == NULL)
+ continue;
+ while (s != NULL && *s != NULL)
+ {
+ if (strcmp(*s, symname) == 0)
+ return *++s;
+ if (one[0] != '\0' && strcmp(*s, one) == 0)
+ return *++s;
+ if (braces[0] != '\0' && strcmp(*s, braces) == 0)
+ return *++s;
+ ++s; /* skip over macro value */
+ ++s; /* points to next macro name */
+ }
+ }
+ return NULL;
+}
+
+/*
+** SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
+** timeouts during long milter-side operations
+**
+** Parameters:
+** ctx -- Opaque context structure
+**
+** Return value:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_progress(ctx)
+ SMFICTX *ctx;
+{
+ struct timeval timeout;
+
+ if (ctx == NULL)
+ return MI_FAILURE;
+
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+
+ return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
+}
diff --git a/usr/src/cmd/sendmail/libmilter/sparc/Makefile b/usr/src/cmd/sendmail/libmilter/sparc/Makefile
new file mode 100644
index 0000000000..e86ae6a9f7
--- /dev/null
+++ b/usr/src/cmd/sendmail/libmilter/sparc/Makefile
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)