summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/libmilter
diff options
context:
space:
mode:
authorgshapiro <gshapiro@FreeBSD.org>2000-08-12 21:55:49 +0000
committergshapiro <gshapiro@FreeBSD.org>2000-08-12 21:55:49 +0000
commit4332139a9a11f773ffe5109bed871561e3c290a1 (patch)
tree6d207932926718f38869bd08959330c09f4f3e0d /contrib/sendmail/libmilter
parenta392fe0bdb7081117c445f5dcc98d5ed4013dc17 (diff)
downloadFreeBSD-src-4332139a9a11f773ffe5109bed871561e3c290a1.zip
FreeBSD-src-4332139a9a11f773ffe5109bed871561e3c290a1.tar.gz
Import of sendmail version 8.11.0 into vendor branch SENDMAIL with
release tag v8_11_0. Obtained from: ftp://ftp.sendmail.org/pub/sendmail/
Diffstat (limited to 'contrib/sendmail/libmilter')
-rwxr-xr-xcontrib/sendmail/libmilter/Build13
-rw-r--r--contrib/sendmail/libmilter/Makefile17
-rw-r--r--contrib/sendmail/libmilter/Makefile.m415
-rw-r--r--contrib/sendmail/libmilter/README408
-rw-r--r--contrib/sendmail/libmilter/comm.c261
-rw-r--r--contrib/sendmail/libmilter/engine.c1117
-rw-r--r--contrib/sendmail/libmilter/handler.c61
-rw-r--r--contrib/sendmail/libmilter/libmilter.h100
-rw-r--r--contrib/sendmail/libmilter/listener.c581
-rw-r--r--contrib/sendmail/libmilter/main.c132
-rw-r--r--contrib/sendmail/libmilter/signal.c219
-rw-r--r--contrib/sendmail/libmilter/sm_gethost.c99
-rw-r--r--contrib/sendmail/libmilter/smfi.c400
13 files changed, 3423 insertions, 0 deletions
diff --git a/contrib/sendmail/libmilter/Build b/contrib/sendmail/libmilter/Build
new file mode 100755
index 0000000..09dd262
--- /dev/null
+++ b/contrib/sendmail/libmilter/Build
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+# All rights reserved.
+#
+# By using this file, you agree to the terms and conditions set
+# forth in the LICENSE file which can be found at the top level of
+# the sendmail distribution.
+#
+#
+# $Id: Build,v 8.3 2000/01/20 21:51:50 geir Exp $
+
+exec ../devtools/bin/Build $*
diff --git a/contrib/sendmail/libmilter/Makefile b/contrib/sendmail/libmilter/Makefile
new file mode 100644
index 0000000..04277eb
--- /dev/null
+++ b/contrib/sendmail/libmilter/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 8.1 1999/11/04 00:03:40 ca Exp $
+
+SHELL= /bin/sh
+BUILD= ./Build
+OPTIONS= $(CONFIG) $(FLAGS)
+
+all: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+clean: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+install: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+
+fresh: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) -c
+
+FRC:
diff --git a/contrib/sendmail/libmilter/Makefile.m4 b/contrib/sendmail/libmilter/Makefile.m4
new file mode 100644
index 0000000..9ac7760
--- /dev/null
+++ b/contrib/sendmail/libmilter/Makefile.m4
@@ -0,0 +1,15 @@
+include(confBUILDTOOLSDIR`/M4/switch.m4')
+
+define(`confMT', `true')
+
+# sendmail dir
+SMSRCDIR= ifdef(`confSMSRCDIR', `confSMSRCDIR', `${SRCDIR}/sendmail')
+PREPENDDEF(`confINCDIRS', `-I${SMSRCDIR} ')
+
+bldPRODUCT_START(`library', `libmilter')
+define(`bldSOURCES', `main.c engine.c listener.c handler.c comm.c smfi.c signal.c sm_gethost.c ')
+bldPUSH_SMLIB(`smutil')
+bldPRODUCT_END
+APPENDDEF(`confENVDEF', `-DNOT_SENDMAIL')
+
+bldFINISH
diff --git a/contrib/sendmail/libmilter/README b/contrib/sendmail/libmilter/README
new file mode 100644
index 0000000..7166b40
--- /dev/null
+++ b/contrib/sendmail/libmilter/README
@@ -0,0 +1,408 @@
+This directory contains the source files for libmilter.
+
+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. It is necessary to first build libmilter.a, which can be done by
+issuing the './Build' command in SRCDIR/libmilter .
+
+NOTE: Both libmilter and the callouts in sendmail are marked as an FFR (For
+Future Release). If you intend to use them in 8.10.X, you must compiled
+both libmilter and sendmail with -D_FFR_MILTER defined. You can do this by
+adding the following to your devtools/Site/site.config.m4 file:
+
+ dnl Milter
+ APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_MILTER=1')
+ APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER=1')
+
+You will also need to define _FFR_MILTER when building your .cf file using
+m4.
+
++-------------------+
+| 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' and built in the local platform-
+specific build subdirectory (SRCDIR/obj.*/libmilter).
+
+ cc -I../../sendmail -I../../include -o sample sample.c libmilter.a ../libsmutil/libsmutil.a -pthread
+
+It is recommended that you build your filters in a location outside of
+the sendmail source tree. Modify the compiler include references (-I)
+and the library locations accordingly. Also, some operating systems may
+require additional libraries. For example, SunOS 5.X requires '-lresolv
+-lsocket -lnsl'. Depending on your OS you may need a library instead
+of the option -pthread, e.g., -lpthread.
+
+Filters must be thread-safe! Many operating systems now provide support for
+POSIX threads in the standard C libraries. The compiler flag to link with
+threading support differs according to the compiler and linker used. Check
+the Makefile in your appropriate obj.*/libmilter build subdirectory if you
+are unsure of the local flag used.
+
+
++----------------------------------------+
+| 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=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=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
+
+Finally, you can override the default timeouts used by sendmail when
+talking to the filters using the T= equate. There are three fields inside
+of the T= equate:
+
+Letter Meaning
+ 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=S:10s;R:10s;E:5m
+
+where 's' is seconds and 'm' is minutes.
+
+Actual sequencing is handled by the InputMailFilters option which 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.10.0.Beta8/8.10.0.Beta8; Mon, 6 Dec 1999 19:34:23 -0800 (PST)
+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"
+
+typedef int bool;
+
+#ifndef FALSE
+# define FALSE 0
+#endif /* ! FALSE*/
+#ifndef TRUE
+# 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;
+
+ /* 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)
+ {
+ 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)
+ strlcpy(host, "localhost", sizeof host);
+ 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[];
+{
+ 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);
+ break;
+
+ }
+ }
+ if (smfi_register(smfilter) == MI_FAILURE)
+ {
+ fprintf(stderr, "smfi_register failed\n");
+ exit(EX_UNAVAILABLE);
+ }
+ return smfi_main();
+}
+
+/* eof */
+
+$Revision: 8.9.2.1.2.8 $, Last updated $Date: 2000/07/18 15:43:26 $
diff --git a/contrib/sendmail/libmilter/comm.c b/contrib/sendmail/libmilter/comm.c
new file mode 100644
index 0000000..765b024
--- /dev/null
+++ b/contrib/sendmail/libmilter/comm.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: comm.c,v 8.30.4.3 2000/06/12 14:53:01 ca Exp $";
+#endif /* ! lint */
+
+#if _FFR_MILTER
+#include "libmilter.h"
+
+#define FD_Z FD_ZERO(&readset); \
+ FD_SET((u_int) sd, &readset); \
+ FD_ZERO(&excset); \
+ FD_SET((u_int) sd, &excset)
+
+/*
+** 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_set readset, excset;
+ int ret;
+ int save_errno;
+ char *buf;
+ char data[MILTER_LEN_BYTES + 1];
+
+ *cmd = '\0';
+ *rlen = 0;
+ if (sd >= FD_SETSIZE)
+ {
+ smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
+ name, sd, FD_SETSIZE);
+ *cmd = SMFIC_SELECT;
+ return NULL;
+ }
+ FD_Z;
+ i = 0;
+ while ((ret = select(sd + 1, &readset, NULL, &excset, timeout)) >= 1)
+ {
+ if (FD_ISSET(sd, &excset))
+ {
+ *cmd = SMFIC_SELECT;
+ return NULL;
+ }
+ if ((len = read(sd, data + i, sizeof data - i)) < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s, mi_rd_cmd: read returned %d: %s",
+ name, len, strerror(errno));
+ *cmd = SMFIC_RECVERR;
+ return NULL;
+ }
+ if (len == 0)
+ {
+ *cmd = SMFIC_EOF;
+ return NULL;
+ }
+ if (len >= (ssize_t) sizeof data - i)
+ break;
+ i += len;
+ FD_Z;
+ }
+ 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, strerror(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 > MILTER_CHUNK_SIZE)
+ {
+ *cmd = SMFIC_TOOBIG;
+ return NULL;
+ }
+ buf = malloc(expl);
+ if (buf == NULL)
+ {
+ *cmd = SMFIC_MALLOC;
+ return NULL;
+ }
+
+ i = 0;
+ FD_Z;
+ while ((ret = select(sd + 1, &readset, NULL, &excset, timeout)) == 1)
+ {
+ if (FD_ISSET(sd, &excset))
+ {
+ *cmd = SMFIC_SELECT;
+ free(buf);
+ return NULL;
+ }
+ if ((len = read(sd, buf + i, expl - i)) < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: mi_rd_cmd: read returned %d: %s",
+ name, len, strerror(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;
+ return buf;
+ }
+ i += len;
+ FD_Z;
+ }
+
+ 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, strerror(save_errno));
+ *cmd = SMFIC_RECVERR;
+ return NULL;
+ }
+ *cmd = SMFIC_UNKNERR;
+ return NULL;
+}
+ /*
+** MI_WR_CMD -- write a cmd to sd
+**
+** Parameters:
+** sd -- socket descriptor
+** timeout -- maximum time to wait (currently unused)
+** 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 ret;
+ fd_set wrtset;
+ char data[MILTER_LEN_BYTES + 1];
+
+ if (len > MILTER_CHUNK_SIZE)
+ 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;
+
+ do {
+ FD_ZERO(&wrtset);
+ FD_SET((u_int) sd, &wrtset);
+ if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0)
+ return MI_FAILURE;
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0)
+ return MI_FAILURE;
+
+ /* use writev() instead to send the whole stuff at once? */
+ while ((l = write(sd, (void *) (data + i), sl - i)) < (ssize_t) sl)
+ {
+ if (l < 0)
+ return MI_FAILURE;
+ i += l;
+ sl -= l;
+ }
+
+ if (len > 0 && buf == NULL)
+ return MI_FAILURE;
+ if (len == 0 || buf == NULL)
+ return MI_SUCCESS;
+ i = 0;
+ sl = len;
+ do {
+ FD_ZERO(&wrtset);
+ FD_SET((u_int) sd, &wrtset);
+ if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0)
+ return MI_FAILURE;
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0)
+ return MI_FAILURE;
+ while ((l = write(sd, (void *) (buf + i), sl - i)) < (ssize_t) sl)
+ {
+ if (l < 0)
+ return MI_FAILURE;
+ i += l;
+ sl -= l;
+ }
+ return MI_SUCCESS;
+}
+#endif /* _FFR_MILTER */
diff --git a/contrib/sendmail/libmilter/engine.c b/contrib/sendmail/libmilter/engine.c
new file mode 100644
index 0000000..ca2da4c
--- /dev/null
+++ b/contrib/sendmail/libmilter/engine.c
@@ -0,0 +1,1117 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: engine.c,v 8.67.4.12 2000/07/14 06:16:57 msk Exp $";
+#endif /* ! lint */
+
+#if _FFR_MILTER
+#include "libmilter.h"
+#include "sendmail/useful.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
+#if CI_RCPT >= MAX_MACROS_ENTRIES
+ERROR: do not compile with CI_RCPT >= 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 *));
+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_HDRS 6 /* headers */
+#define ST_EOHS 7 /* end of headers */
+#define ST_BODY 8 /* body */
+#define ST_ENDM 9 /* end of message */
+#define ST_QUIT 10 /* quit */
+#define ST_ABRT 11 /* abort */
+#define ST_LAST ST_ABRT
+#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 MASK(x) (0x0001 << (x)) /* generate a bit "mask" for a state */
+#define NX_INIT (MASK(ST_OPTS))
+#define NX_OPTS (MASK(ST_CONN))
+#define NX_CONN (MASK(ST_HELO) | MASK(ST_MAIL))
+#define NX_HELO (MASK(ST_MAIL))
+#define NX_MAIL (MASK(ST_RCPT) | MASK(ST_ABRT))
+#define NX_RCPT (MASK(ST_HDRS) | MASK(ST_EOHS) | MASK(ST_RCPT) | MASK(ST_ABRT))
+#define NX_HDRS (MASK(ST_EOHS) | MASK(ST_HDRS) | MASK(ST_ABRT))
+#define NX_EOHS (MASK(ST_BODY) | MASK(ST_ENDM) | MASK(ST_ABRT))
+#define NX_BODY (MASK(ST_ENDM) | MASK(ST_BODY) | MASK(ST_ABRT))
+#define NX_ENDM (MASK(ST_QUIT) | MASK(ST_MAIL))
+#define NX_QUIT 0
+#define NX_ABRT 0
+#define NX_SKIP MASK(ST_SKIP)
+
+static int next_states[] =
+{
+ NX_INIT,
+ NX_OPTS,
+ NX_CONN,
+ NX_HELO,
+ NX_MAIL,
+ NX_RCPT,
+ NX_HDRS,
+ NX_EOHS,
+ NX_BODY,
+ NX_ENDM,
+ NX_QUIT,
+ NX_ABRT
+};
+
+/* 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_NONE, 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 },
+{SMFIC_RCPT, CM_ARGV, ST_RCPT, CT_IGNO, CI_RCPT, st_rcpt }
+};
+
+/* additional (internal) reply codes */
+#define _SMFIS_KEEP 20
+#define _SMFIS_ABORT 21
+#define _SMFIS_OPTIONS 22
+#define _SMFIS_NOREPLY 23
+#define _SMFIS_FAIL (-1)
+
+ /*
+** 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);
+ 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)
+ dprintf("[%d] milter_abort\n",
+ (int) ctx->ctx_id);
+ ret = MI_FAILURE;
+ break;
+ }
+ if ((buf = mi_rd_cmd(sd, &timeout, &cmd, &len,
+ ctx->ctx_smfi->xxfi_name)) == NULL &&
+ cmd < SMFIC_VALIDCMD)
+ {
+ if (ctx->ctx_dbg > 5)
+ dprintf("[%d] 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)
+ dprintf("[%d] got cmd '%c' len %d\n",
+ (int) ctx->ctx_id, cmd, len);
+ for (i = 0; i < ncmds; i++)
+ {
+ if (cmd == cmds[i].cm_cmd)
+ break;
+ }
+ if (i >= ncmds)
+ {
+ /* unknown command */
+ if (ctx->ctx_dbg > 1)
+ 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)
+ 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)
+ 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)
+ dprintf("[%d] abort: cur %d (%x) new %d (%x) next %x\n",
+ (int) ctx->ctx_id,
+ curstate, MASK(curstate),
+ newstate, 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))
+ 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 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;
+ }
+
+ call_abort = ST_IN_MAIL(curstate);
+ 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)
+ 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 (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)
+ {
+ 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, 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;
+ u_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 >= l)
+ return _SMFIS_ABORT;
+
+ /* Move past trailing \0 in host string */
+ i++;
+ family = s[i++];
+ memset(&sockaddr, '\0', sizeof sockaddr);
+ if (family != SMFIA_UNKNOWN)
+ {
+ (void) memcpy((void *) &port, (void *) (s + i),
+ sizeof port);
+ port = ntohs(port);
+ 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, i, l);
+ return _SMFIS_ABORT;
+ }
+# if NETINET
+ if (family == SMFIA_INET)
+ {
+ if (inet_aton(s + i, (struct in_addr *) &sockaddr.sin.sin_addr)
+ == INADDR_NONE)
+ {
+ 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 (inet_pton(AF_INET6, s + i,
+ &sockaddr.sin6.sin6_addr) != 1)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%d]: 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 (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;
+}
+ /*
+** 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)
+ 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)
+}
+ /*
+** 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;
+ 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
+*/
+
+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 *, u_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, (u_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 *, u_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, (u_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 ((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;
+{
+ u_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')
+ s[++elem] = &(buf[i + 1]);
+ }
+
+ /* overwrite last entry */
+ 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;
+
+ *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;
+ if (flag != 0 && !bitset(flag, ctx->ctx_smfi->xxfi_flags))
+ return FALSE;
+ return ctx->ctx_state == ST_ENDM;
+}
+#endif /* _FFR_MILTER */
diff --git a/contrib/sendmail/libmilter/handler.c b/contrib/sendmail/libmilter/handler.c
new file mode 100644
index 0000000..f6e75e9
--- /dev/null
+++ b/contrib/sendmail/libmilter/handler.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: handler.c,v 8.19.4.2 2000/07/14 06:16:57 msk Exp $";
+#endif /* ! lint */
+
+#if _FFR_MILTER
+#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)
+ return MI_FAILURE;
+ ret = mi_engine(ctx);
+ if (ValidSocket(ctx->ctx_sd))
+ (void) close(ctx->ctx_sd);
+ if (ctx->ctx_reply != NULL)
+ free(ctx->ctx_reply);
+ 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;
+}
+#endif /* _FFR_MILTER */
diff --git a/contrib/sendmail/libmilter/libmilter.h b/contrib/sendmail/libmilter/libmilter.h
new file mode 100644
index 0000000..a675ac6
--- /dev/null
+++ b/contrib/sendmail/libmilter/libmilter.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+/*
+** LIBMILTER.H -- include file for mail filter library functions
+*/
+
+#ifndef _LIBMILTER_H
+# define _LIBMILTER_H 1
+#ifdef _DEFINE
+# define EXTERN
+# define INIT(x) = x
+# ifndef lint
+static char MilterlId[] = "@(#)$Id: libmilter.h,v 8.3.6.4 2000/06/09 07:12:13 gshapiro Exp $";
+# endif /* ! lint */
+#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"
+
+#ifndef __P
+# include "sendmail/cdefs.h"
+#endif /* ! __P */
+#include "sendmail/useful.h"
+
+# define ValidSocket(sd) ((sd) >= 0)
+
+# define thread_create(ptid,wr,arg) pthread_create(ptid, NULL, wr, arg)
+# define sthread_get_id() pthread_self()
+
+#include <sys/time.h>
+
+/* version info */
+#define MILTER_PRODUCT_NAME "libmilter"
+#define MILTER_VERSION 100
+
+/* some defaults */
+#define MI_TIMEOUT 1800 /* default timeout for read/write */
+#define MI_CHK_TIME 5 /* checking whether to terminate */
+
+/* maximum number of repeated failures in mi_listener() */
+#define MAX_FAILS_M 16 /* malloc() */
+#define MAX_FAILS_T 16 /* thread creation */
+
+/* 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 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
+
+#define MI_INVALID_SOCKET (-1)
+
+/* 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));
+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));
+
+/* 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/contrib/sendmail/libmilter/listener.c b/contrib/sendmail/libmilter/listener.c
new file mode 100644
index 0000000..a88258c
--- /dev/null
+++ b/contrib/sendmail/libmilter/listener.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: listener.c,v 8.38.2.1.2.7 2000/05/25 21:44:26 gshapiro Exp $";
+#endif /* ! lint */
+
+#if _FFR_MILTER
+/*
+** listener.c -- threaded network listener
+*/
+
+#include "libmilter.h"
+
+
+# if NETINET || NETINET6
+# include <arpa/inet.h>
+# endif /* NETINET || NETINET6 */
+ /*
+** MI_MILTEROPEN -- setup socket to listen on
+**
+** Parameters:
+** conn -- connection description
+** backlog -- listen backlog
+** socksize -- socksize of created socket
+**
+** Returns:
+** socket upon success, error code otherwise.
+*/
+
+static int
+mi_milteropen(conn, backlog, socksize, name)
+ char *conn;
+ int backlog;
+ SOCKADDR_LEN_T *socksize;
+ char *name;
+{
+ int sock = 0;
+ int sockopt = 1;
+ char *p;
+ char *colon;
+ char *at;
+ struct hostent *hp = NULL;
+ SOCKADDR addr;
+
+ if (conn == NULL || conn[0] == '\0')
+ {
+ smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
+ name);
+ return MI_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;
+ *socksize = sizeof (struct sockaddr_un);
+#else /* NETUNIX */
+# if NETINET
+ /* default to AF_INET */
+ addr.sa.sa_family = AF_INET;
+ *socksize = sizeof addr.sin;
+# else /* NETINET */
+# if NETINET6
+ /* default to AF_INET6 */
+ addr.sa.sa_family = AF_INET6;
+ *socksize = sizeof addr.sin6;
+# else /* NETINET6 */
+ /* no protocols available */
+ smi_log(SMI_LOG_ERR,
+ "%s: no valid socket protocols available",
+ name);
+ return MI_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;
+ *socksize = sizeof (struct sockaddr_un);
+ }
+#endif /* NETUNIX */
+#if NETINET
+ else if (strcasecmp(p, "inet") == 0)
+ {
+ addr.sa.sa_family = AF_INET;
+ *socksize = sizeof addr.sin;
+ }
+#endif /* NETINET */
+#if NETINET6
+ else if (strcasecmp(p, "inet6") == 0)
+ {
+ addr.sa.sa_family = AF_INET6;
+ *socksize = sizeof addr.sin6;
+ }
+#endif /* NETINET6 */
+ else
+ {
+ smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
+ name, p);
+ return MI_INVALID_SOCKET;
+ }
+ *colon++ = ':';
+ }
+ else
+ {
+ colon = p;
+#if NETUNIX
+ /* default to AF_UNIX */
+ addr.sa.sa_family = AF_UNIX;
+ *socksize = sizeof (struct sockaddr_un);
+#else /* NETUNIX */
+# if NETINET
+ /* default to AF_INET */
+ addr.sa.sa_family = AF_INET;
+ *socksize = sizeof addr.sin;
+# else /* NETINET */
+# if NETINET6
+ /* default to AF_INET6 */
+ addr.sa.sa_family = AF_INET6;
+ *socksize = sizeof addr.sin6;
+# else /* NETINET6 */
+ smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
+ name, p);
+ return MI_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;
+ if (strlcpy(addr.sunix.sun_path, colon,
+ sizeof addr.sunix.sun_path) >=
+ sizeof addr.sunix.sun_path)
+ {
+ errno = EINVAL;
+ smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
+ name, colon);
+ return MI_INVALID_SOCKET;
+ }
+# 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 MI_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 */
+ )
+ {
+ u_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((u_short) atoi(colon));
+ else
+ {
+# ifdef NO_GETSERVBYNAME
+ smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
+ name, colon);
+ return MI_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 MI_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 &&
+ 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 MI_INVALID_SOCKET;
+ }
+ }
+ else
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Invalid numeric domain spec \"%s\"",
+ name, at);
+ return MI_INVALID_SOCKET;
+ }
+ }
+ else
+ {
+ hp = mi_gethostbyname(at, addr.sa.sa_family);
+ if (hp == NULL)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unknown host name %s",
+ name, at);
+ return MI_INVALID_SOCKET;
+ }
+ addr.sa.sa_family = hp->h_addrtype;
+ switch (hp->h_addrtype)
+ {
+# if NETINET
+ case AF_INET:
+ memmove(&addr.sin.sin_addr,
+ hp->h_addr,
+ INADDRSZ);
+ addr.sin.sin_port = port;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ 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 MI_INVALID_SOCKET;
+ }
+ }
+ }
+ 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, strerror(errno));
+ return MI_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, strerror(errno));
+ (void) close(sock);
+ return MI_INVALID_SOCKET;
+ }
+
+ if (bind(sock, &addr.sa, *socksize) < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to bind to port %s: %s",
+ name, conn, strerror(errno));
+ (void) close(sock);
+ return MI_INVALID_SOCKET;
+ }
+
+ if (listen(sock, backlog) < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: listen call failed: %s", name, strerror(errno));
+ (void) close(sock);
+ return MI_INVALID_SOCKET;
+ }
+
+ 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()
+*/
+
+void *
+mi_thread_handle_wrapper(arg)
+ void *arg;
+{
+ return (void *) mi_handle_session(arg);
+}
+
+ /*
+** MI_MILTER_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
+**
+** Returns:
+** MI_SUCCESS -- Exited normally
+** (session finished or we were told to exit)
+** MI_FAILURE -- Network initialization failed.
+*/
+
+int
+mi_listener(conn, dbg, smfi, timeout)
+ char *conn;
+ int dbg;
+ smfiDesc_ptr smfi;
+ time_t timeout;
+{
+ int connfd = -1;
+ int listenfd = -1;
+ int sockopt = 1;
+ int r;
+ int ret = MI_SUCCESS;
+ int cnt_m = 0;
+ int cnt_t = 0;
+ sthread_t thread_id;
+ _SOCK_ADDR cliaddr;
+ SOCKADDR_LEN_T socksize;
+ SOCKADDR_LEN_T clilen;
+ SMFICTX_PTR ctx;
+ fd_set readset, excset;
+ struct timeval chktime;
+
+ if (dbg > 0)
+ smi_log(SMI_LOG_DEBUG,
+ "%s: Opening listen socket on conn %s",
+ smfi->xxfi_name, conn);
+ if ((listenfd = mi_milteropen(conn, SOMAXCONN, &socksize,
+ smfi->xxfi_name)) < 0)
+ {
+ smi_log(SMI_LOG_FATAL,
+ "%s: Unable to create listening socket on conn %s",
+ smfi->xxfi_name, conn);
+ return MI_FAILURE;
+ }
+ clilen = socksize;
+ if (listenfd >= FD_SETSIZE)
+ {
+ smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
+ smfi->xxfi_name, listenfd, FD_SETSIZE);
+ return MI_FAILURE;
+ }
+
+ while (mi_stop() == MILTER_CONT)
+ {
+ /* select on interface ports */
+ FD_ZERO(&readset);
+ FD_SET((u_int) listenfd, &readset);
+ FD_ZERO(&excset);
+ FD_SET((u_int) listenfd, &excset);
+ chktime.tv_sec = MI_CHK_TIME;
+ chktime.tv_usec = 0;
+ r = select(listenfd + 1, &readset, NULL, &excset, &chktime);
+ if (r == 0) /* timeout */
+ continue; /* just check mi_stop() */
+ if (r < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ ret = MI_FAILURE;
+ break;
+ }
+ if (!FD_ISSET(listenfd, &readset))
+ {
+ /* some error: just stop for now... */
+ ret = MI_FAILURE;
+ break;
+ }
+
+ connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
+ &clilen);
+
+ if (connfd < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: accept() returned invalid socket",
+ smfi->xxfi_name);
+ continue;
+ }
+
+ if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
+ (void *) &sockopt, sizeof sockopt) < 0)
+ {
+ smi_log(SMI_LOG_WARN, "%s: setsockopt() failed",
+ smfi->xxfi_name);
+ /* XXX: continue? */
+ }
+ if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
+ {
+ (void) close(connfd);
+ smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed",
+ smfi->xxfi_name);
+ sleep(++cnt_m);
+ if (cnt_m >= MAX_FAILS_M)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+ continue;
+ }
+ cnt_m = 0;
+ 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)) != MI_SUCCESS)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: thread_create() failed: %d",
+ smfi->xxfi_name, r);
+ sleep(++cnt_t);
+ (void) close(connfd);
+ free(ctx);
+ if (cnt_t >= MAX_FAILS_T)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+ continue;
+ }
+ cnt_t = 0;
+ }
+ if (ret != MI_SUCCESS)
+ mi_stop_milters(MILTER_ABRT);
+ if (listenfd >= 0)
+ (void) close(listenfd);
+ return ret;
+}
+#endif /* _FFR_MILTER */
diff --git a/contrib/sendmail/libmilter/main.c b/contrib/sendmail/libmilter/main.c
new file mode 100644
index 0000000..3233ba1
--- /dev/null
+++ b/contrib/sendmail/libmilter/main.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: main.c,v 8.34.4.5 2000/07/14 06:16:57 msk Exp $";
+#endif /* ! lint */
+
+#if _FFR_MILTER
+#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) 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);
+ return MI_FAILURE;
+ }
+
+ return MI_SUCCESS;
+}
+
+static int dbg = 0;
+static char *conn = NULL;
+static int timeout = MI_TIMEOUT;
+
+int
+smfi_setdbg(odbg)
+ int odbg;
+{
+ dbg = odbg;
+ return MI_SUCCESS;
+}
+
+int
+smfi_settimeout(otimeout)
+ int otimeout;
+{
+ timeout = otimeout;
+ return MI_SUCCESS;
+}
+
+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 (strlcpy(conn, oconn, l) >= l)
+ return MI_FAILURE;
+ return MI_SUCCESS;
+}
+
+int
+smfi_main()
+{
+ 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;
+ }
+
+ /* Startup the listener */
+ if (mi_listener(conn, dbg, smfi, timeout) != MI_SUCCESS)
+ return MI_FAILURE;
+
+ return MI_SUCCESS;
+}
+#endif /* _FFR_MILTER */
diff --git a/contrib/sendmail/libmilter/signal.c b/contrib/sendmail/libmilter/signal.c
new file mode 100644
index 0000000..c57c313
--- /dev/null
+++ b/contrib/sendmail/libmilter/signal.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: signal.c,v 8.10.4.4 2000/07/14 06:16:57 msk Exp $";
+#endif /* ! lint */
+
+#if _FFR_MILTER
+#include "libmilter.h"
+
+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)
+
+/*
+** thread to handle signals
+*/
+
+static smutex_t M_Mutex;
+
+static int MilterStop = MILTER_CONT;
+
+ /*
+** 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;
+ (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;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGHUP);
+ sigaddset(&set, SIGTERM);
+
+ /* Handle Ctrl-C gracefully for debugging */
+ sigaddset(&set, SIGINT);
+ errs = 0;
+
+ while (TRUE)
+ {
+ sig = 0;
+#ifdef SOLARIS
+ if ((sig = sigwait(&set)) < 0)
+#else /* SOLARIS */
+ if (sigwait(&set, &sig) != 0)
+#endif /* SOLARIS */
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: sigwait returned error: %s",
+ (char *)name, strerror(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;
+ }
+ }
+}
+ /*
+** 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;
+ sigset_t set;
+
+ /* Mask HUP and KILL signals */
+ sigemptyset(&set);
+ sigaddset(&set, SIGHUP);
+ sigaddset(&set, SIGTERM);
+ 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;
+ }
+ if (thread_create(&tid, mi_signal_thread,
+ (void *)name) != MI_SUCCESS)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Couldn't start signal thread", name);
+ 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;
+}
+#endif /* _FFR_MILTER */
diff --git a/contrib/sendmail/libmilter/sm_gethost.c b/contrib/sendmail/libmilter/sm_gethost.c
new file mode 100644
index 0000000..fc45d28
--- /dev/null
+++ b/contrib/sendmail/libmilter/sm_gethost.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: sm_gethost.c,v 8.7 2000/01/20 21:51:52 geir Exp $";
+#endif /* ! lint */
+
+#if _FFR_MILTER
+#include <sendmail.h>
+#if NETINET || NETINET6
+# include <arpa/inet.h>
+#endif /* NETINET || NETINET6 */
+
+ /*
+** 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 && __RES < 19990909
+
+# ifndef AI_V4MAPPED
+# define AI_V4MAPPED 0 /* dummy */
+# endif /* ! AI_V4MAPPED */
+# ifndef AI_ALL
+# define AI_ALL 0 /* dummy */
+# endif /* ! AI_ALL */
+
+static struct hostent *
+mi_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;
+ }
+ h_errno = 0;
+ h = gethostbyname(name);
+ *err = h_errno;
+ if (family == AF_INET6 && !resv6)
+ _res.options &= ~RES_USE_INET6;
+ return h;
+}
+#endif /* NEEDSGETIPNODE && NETINET6 && __RES < 19990909 */
+
+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 err;
+# endif /* NETINET6 */
+
+# if NETINET6
+ h = mi_getipnodebyname(name, family, AI_V4MAPPED|AI_ALL, &err);
+ 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;
+}
+#endif /* _FFR_MILTER */
diff --git a/contrib/sendmail/libmilter/smfi.c b/contrib/sendmail/libmilter/smfi.c
new file mode 100644
index 0000000..e034aa8
--- /dev/null
+++ b/contrib/sendmail/libmilter/smfi.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: smfi.c,v 8.28.4.6 2000/06/28 23:48:56 gshapiro Exp $";
+#endif /* ! lint */
+
+#if _FFR_MILTER
+#include "libmilter.h"
+#include "sendmail/useful.h"
+
+/*
+** 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;
+{
+ /* do we want to copy the stuff or have a special mi_wr_cmd call? */
+ size_t len, l1, l2;
+ int r;
+ char *buf;
+ struct timeval timeout;
+
+ if (headerf == NULL || *headerf == '\0' || headerv == NULL)
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_ADDHDRS))
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ l1 = strlen(headerf);
+ l2 = strlen(headerv);
+ len = l1 + l2 + 2;
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE;
+ (void) memcpy(buf, headerf, l1 + 1);
+ (void) memcpy(buf + l1 + 1, headerv, l2 + 1);
+ r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDHEADER, buf, len);
+ free(buf);
+ return r;
+}
+
+/*
+** 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;
+{
+ /* do we want to copy the stuff or have a special mi_wr_cmd call? */
+ size_t len, l1, l2;
+ int r;
+ mi_int32 v;
+ char *buf;
+ struct timeval timeout;
+
+ if (headerf == NULL || *headerf == '\0')
+ return MI_FAILURE;
+ if (hdridx < 0)
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_CHGHDRS))
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ if (headerv == NULL)
+ headerv = "";
+ l1 = strlen(headerf);
+ l2 = strlen(headerv);
+ len = l1 + l2 + 2 + MILTER_LEN_BYTES;
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE;
+ v = htonl(hdridx);
+ (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
+ (void) memcpy(buf + MILTER_LEN_BYTES, headerf, l1 + 1);
+ (void) memcpy(buf + MILTER_LEN_BYTES + l1 + 1, headerv, l2 + 1);
+ r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_CHGHEADER, buf, len);
+ free(buf);
+ return r;
+}
+ /*
+** 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;
+ u_char *bodyp;
+ int bodylen;
+{
+ int len, off, r;
+ struct timeval timeout;
+
+ if (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;
+}
+ /*
+** 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, l1, l2, l3;
+ char *buf;
+
+ if (rcode == NULL || ctx == NULL)
+ return MI_FAILURE;
+ l1 = strlen(rcode) + 1;
+ if (l1 != 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;
+ l2 = xcode == NULL ? 1 : strlen(xcode) + 1;
+ if (xcode != NULL && !myisenhsc(xcode, '\0'))
+ return MI_FAILURE;
+ l3 = message == NULL ? 1 : strlen(message) + 1;
+ len = l1 + l2 + l3;
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE; /* oops */
+ (void) snprintf(buf, len, "%s %s %s", rcode,
+ xcode == NULL ? "" : xcode,
+ message == NULL ? "" : message);
+ if (ctx->ctx_reply != NULL)
+ free(ctx->ctx_reply);
+ ctx->ctx_reply = buf;
+ 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;
+}
+#endif /* _FFR_MILTER */
OpenPOWER on IntegriCloud