aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2007-03-01 23:08:32 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2007-03-01 23:08:32 +0000
commit7128853094a3df29948dc949ff04b43cccd400f7 (patch)
tree69db0978e6b835b7a736490a06a4307b1e32aa0c /src
parentc6eecf2842218ed97a7084fbecae06388ec358f3 (diff)
downloadmailfromd-7128853094a3df29948dc949ff04b43cccd400f7.tar.gz
mailfromd-7128853094a3df29948dc949ff04b43cccd400f7.tar.bz2
Start implementing test MTA simulator and Gacopyz server code
git-svn-id: file:///svnroot/mailfromd/trunk@1269 7a8a7f39-df28-0410-adc6-e0d955640f24
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am3
-rw-r--r--src/gram.y2
-rw-r--r--src/main.c10
-rw-r--r--src/mtasim.c1130
4 files changed, 1143 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 274ed70a..6c905735 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,6 +17,7 @@
# MA 02110-1301 USA
sbin_PROGRAMS = mailfromd
+bin_PROGRAMS = mtasim
incdir=$(pkgdatadir)/$(VERSION)/include
inc_DATA = status.mfh
@@ -54,6 +55,8 @@ mailfromd_SOURCES = \
rate.c\
$(M4_FILES:.m4=.c)
+mtasim_SOURCES = mtasim.c
+
noinst_HEADERS = mailfromd.h mu_dbm.h builtin.h dns.h spf.h
EXTRA_DIST = gram.h snarf.m4 init.m4 $(M4_FILES) builtin.h builtin.def mf-status.mfi status.mfi mfstat.awk status.mfh optab.opc optab.oph opcode.awk opcodes
BUILT_SOURCES=$(M4_FILES:.m4=.c) builtin.h mf-status.c optab.c optab.h
diff --git a/src/gram.y b/src/gram.y
index 52f548f4..e6bc98fb 100644
--- a/src/gram.y
+++ b/src/gram.y
@@ -876,7 +876,7 @@ value : STRING
$$ = *value_ptr;
else {
if (!allow_unquoted_strings)
- parse_warning("unquoted identifier `%s'",
+ parse_warning("unquoted identifier `%s'",
$1->text);
$$.type = dtype_string;
$$.v.literal = $1;
diff --git a/src/main.c b/src/main.c
index ed566d9d..f146b126 100644
--- a/src/main.c
+++ b/src/main.c
@@ -895,6 +895,7 @@ enum mailfromd_option {
OPTION_DUMP_XREF,
OPTION_EXPIRE,
OPTION_FOREGROUND,
+ OPTION_DEBUG_GACOPYZ,
OPTION_IGNORE_FAILED_READS,
OPTION_LIST,
OPTION_LOCK_RETRY_COUNT,
@@ -1032,6 +1033,8 @@ static struct argp_option options[] = {
N_("Show used Sendmail macros"), GRP+1 },
{ "xref", OPTION_DUMP_XREF, NULL, 0,
N_("Produce a cross-reference listing"), GRP+1 },
+ { "gacopyz-debug", OPTION_DEBUG_GACOPYZ, NULL, 0,
+ N_("Milter protocol trace"), GRP+1 },
{ "dump-xref", 0, NULL, OPTION_ALIAS, NULL, GRP+1 },
{ "stderr", 's', NULL, 0,
N_("Log to stderr"), GRP+1 },
@@ -1268,6 +1271,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
foreground = 1;
break;
+ case OPTION_DEBUG_GACOPYZ:
+ smfi_setdbg(1);
+ break;
+
case OPTION_LIST:
need_config = 0;
log_to_stderr = 1;
@@ -1715,7 +1722,8 @@ main(int argc, char **argv)
if (!log_to_stderr) {
openlog(syslog_tag, LOG_PID, log_facility);
mu_error_set_print(syslog_error_printer);
- }
+ } else
+ gacopyz_set_logger (gacopyz_stderr_log_printer);
if (script_dump_macros)
print_used_macros();
diff --git a/src/mtasim.c b/src/mtasim.c
new file mode 100644
index 00000000..8f40ff48
--- /dev/null
+++ b/src/mtasim.c
@@ -0,0 +1,1130 @@
+/*
+ This file is part of GNU Anubis testsuite.
+ Copyright (C) 2003, 2004, 2007 The Anubis Team.
+
+ GNU Anubis is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ GNU Anubis is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Anubis; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ GNU Anubis is released under the GPL with the additional exemption that
+ compiling, linking, and/or using OpenSSL is allowed.
+*/
+
+/* This is a "fake" mta designed for testing purposes. It imitates
+ sendmail SMTP modes (daemon and stdin). It does not actually send anything,
+ instead it just outputs the transcript of the SMTP session.
+
+ Invocation:
+
+ 1. mta -bs [-d FILE]
+
+ Use the SMTP protocol on standard input and output.
+
+ 2. mta -bd [-p port] [-d FILE]
+
+ Operates as daemon. If port is given, mta will listen on that port.
+ Otherwise, it will use the first free port in the range 1024-65535.
+ In this case, mta prints the port number on the stdout, prior to
+ starting operation. Notice, that in this mode mta does not disconnect
+ itself from the controlling terminal, it always stays on the foreground.
+
+ Option -d in both cases sets the name of the output diagnostics file.
+
+ Environment variables:
+
+ MTA_DIAG Sets the name of the output diagnostic file. By default,
+ the diagnostics goes to stderr.
+ MTA_APPEND When set to any non-empty value, directs mta to append
+ to the diagnostics file, not to overwrite it.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef HAVE_GETOPT_H
+# include <getopt.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <mailutils/mailutils.h>
+#include <gacopyz.h>
+
+#if defined(USE_GNUTLS) && defined(HAVE_GNUTLS_GNUTLS_H)
+# include <gnutls/gnutls.h>
+# define HAVE_TLS
+#endif /* USE_GNUTLS and HAVE_GNUTLS_GNUTLS_H */
+
+FILE *diag = NULL; /* diagnostic output */
+int port = 0; /* Port number (for smtp mode) */
+
+const char *milter_port;
+int verbose;
+#ifdef HAVE_TLS
+char *tls_cert; /* TLS sertificate */
+char *tls_key; /* TLS key */
+char *tls_cafile;
+
+#define DH_BITS 768
+#define enable_tls() (tls_cafile != NULL || (tls_cert != NULL && tls_key != NULL))
+void tls_init (void);
+
+gnutls_dh_params dh_params;
+static gnutls_certificate_server_credentials x509_cred;
+#endif /* HAVE_TLS */
+
+char *progname;
+
+int mta_daemon (int, char **);
+int mta_stdio (int, char **);
+void error (const char *, ...);
+void smtp_reply (int, char *, ...);
+void reset_capa (char *);
+
+void shell (int argc, char **argv);
+int define_macro (char *arg);
+
+#define R_CONT 0x8000
+#define R_CODEMASK 0xfff
+
+
+gacopyz_srv_t gsrv;
+
+void
+update_nrcpts (unsigned n)
+{
+ char buf[128];
+ snprintf (buf, sizeof buf, "%u", n);
+ gacopyz_srv_define_macro (gsrv, "nrcpts", buf);
+}
+
+
+static mu_list_t defnlist;
+
+int
+defer_define_macro (char *arg)
+{
+ char *p;
+ p = strchr (arg, '=');
+ if (!p)
+ return 1;
+ *p++ = 0;
+ if (!defnlist)
+ mu_list_create (&defnlist);
+ mu_list_append (defnlist, arg);
+ return 0;
+}
+
+static int
+do_define (void *item, void *data)
+{
+ char *name = item;
+ char *value = name + strlen (name) + 1;
+ gacopyz_srv_t srv = data;
+ gacopyz_srv_define_macro (srv, name, value);
+ return 0;
+}
+
+void
+flush_deferred_defns (gacopyz_srv_t srv)
+{
+ if (srv)
+ mu_list_do (defnlist, do_define, srv);
+ mu_list_destroy (&defnlist);
+}
+
+
+
+int
+main (int argc, char **argv)
+{
+ int c, status;
+ int (*mta_mode) (int argc, char **argv) = NULL;
+ char *diag_name = NULL;
+ int append = getenv ("MTA_APPEND") != NULL;
+
+ progname = strrchr (argv[0], '/');
+ if (!progname)
+ progname = argv[0];
+ else
+ progname++;
+
+ while ((c = getopt (argc, argv, "ac:C:b:d:D:k:m:p:v")) != EOF)
+ {
+ switch (c) {
+ case 'a':
+ append = 1;
+ break;
+
+ case 'b':
+ switch (optarg[0]) {
+ case 'd':
+ mta_mode = mta_daemon;
+ break;
+
+ case 's':
+ mta_mode = mta_stdio;
+ break;
+
+ default:
+ error ("unsupported mode");
+ exit (1);
+ }
+ break;
+
+#ifdef HAVE_TLS
+ case 'c':
+ tls_cert = optarg;
+ break;
+
+ case 'C':
+ tls_cafile = optarg;
+ break;
+
+ case 'k':
+ tls_key = optarg;
+ break;
+#endif
+
+ case 'D':
+ if (defer_define_macro (optarg))
+ fprintf (stderr, "wrong assignement format: %s\n", optarg);
+ break;
+
+ case 'd':
+ diag_name = optarg;
+ break;
+
+ case 'm':
+ milter_port = optarg;
+ break;
+
+ case 'p':
+ port = strtoul (optarg, NULL, 0);
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ default:
+ error ("unknown option");
+ exit (1);
+ }
+ }
+
+ if (!diag_name)
+ diag_name = getenv ("MTA_DIAG");
+
+ if (diag_name)
+ {
+ char *mode = append ? "a" : "w";
+ diag = fopen (diag_name, mode);
+ if (!diag)
+ {
+ error ("can't open diagnostic output: %s", diag_name);
+ return 1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (!mta_mode)
+ {
+ error ("use either -bs or -bd");
+ exit (1);
+ }
+
+ if (milter_port)
+ {
+ int mask = SMI_DEFAULT_LOG_MASK;
+ if (verbose)
+ mask |= SMI_LOG_MASK (SMI_LOG_DEBUG);
+
+ gacopyz_set_logger (gacopyz_stderr_log_printer);
+
+ if (gacopyz_srv_create (&gsrv, mask) != MI_SUCCESS)
+ {
+ error ("cannot create gacopyz server");
+ exit (1);
+ }
+ if (gacopyz_srv_connect (gsrv, milter_port) != MI_SUCCESS)
+ {
+ error ("cannot connect to the milter using %s", milter_port);
+ exit (1);
+ }
+ gacopyz_srv_negotiate (gsrv);
+ }
+
+ flush_deferred_defns (gsrv);
+
+#ifdef HAVE_TLS
+ tls_init ();
+#endif
+ status = mta_mode (argc, argv);
+
+ if (diag)
+ fclose (diag);
+ smtp_reply (221, "Done");
+ return status;
+}
+
+void
+error (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ fprintf (stderr, "%s: ", progname);
+ vfprintf (stderr, fmt, ap);
+ fprintf (stderr, "\n");
+ va_end (ap);
+}
+
+static void *in, *out;
+
+static const char *
+_def_strerror (int rc)
+{
+ return rc == -1 ? "end of file reached" : strerror (rc);
+}
+
+static int
+_def_write (void *sd, char *data, size_t size, size_t * nbytes)
+{
+ int n = write ((int) sd, data, size);
+ if (n != size)
+ return errno;
+ if (nbytes)
+ *nbytes = n;
+ return 0;
+}
+
+static int
+_def_read (void *sd, char *data, size_t size, size_t * nbytes)
+{
+ int n = read ((int) sd, data, size);
+ if (n != size)
+ return errno ? errno : -1;
+ if (nbytes)
+ *nbytes = n;
+ return 0;
+}
+
+static int
+_def_close (void *sd)
+{
+ return close ((int) sd);
+}
+
+int (*_mta_read) (void *, char *, size_t, size_t *) = _def_read;
+int (*_mta_write) (void *, char *, size_t, size_t *) = _def_write;
+int (*_mta_close) (void *) = _def_close;
+const char *(*_mta_strerror) (int) = _def_strerror;
+
+#ifdef HAVE_TLS
+
+static void
+_tls_cleanup_x509 (void)
+{
+ if (x509_cred)
+ gnutls_certificate_free_credentials (x509_cred);
+}
+
+static void
+generate_dh_params (void)
+{
+ gnutls_dh_params_init (&dh_params);
+ gnutls_dh_params_generate2 (dh_params, DH_BITS);
+}
+
+void
+tls_init (void)
+{
+ if (!enable_tls ())
+ return;
+
+ gnutls_global_init ();
+ atexit (gnutls_global_deinit);
+ gnutls_certificate_allocate_credentials (&x509_cred);
+ atexit (_tls_cleanup_x509);
+ if (tls_cafile)
+ {
+ int rc = gnutls_certificate_set_x509_trust_file (x509_cred,
+ tls_cafile,
+ GNUTLS_X509_FMT_PEM);
+ if (rc < 0)
+ {
+ gnutls_perror (rc);
+ return;
+ }
+ }
+ if (tls_cert && tls_key)
+ gnutls_certificate_set_x509_key_file (x509_cred,
+ tls_cert, tls_key,
+ GNUTLS_X509_FMT_PEM);
+
+ generate_dh_params ();
+ gnutls_certificate_set_dh_params (x509_cred, dh_params);
+}
+
+static ssize_t
+_tls_fd_pull (gnutls_transport_ptr fd, void *buf, size_t size)
+{
+ int rc;
+ do
+ {
+ rc = read ((int) fd, buf, size);
+ }
+ while (rc == -1 && errno == EAGAIN);
+ return rc;
+}
+
+static ssize_t
+_tls_fd_push (gnutls_transport_ptr fd, const void *buf, size_t size)
+{
+ int rc;
+ do
+ {
+ rc = write ((int) fd, buf, size);
+ }
+ while (rc == -1 && errno == EAGAIN);
+ return rc;
+}
+
+static const char *
+_tls_strerror (int rc)
+{
+ return gnutls_strerror (rc);
+}
+
+static int
+_tls_write (void *sd, char *data, size_t size, size_t * nbytes)
+{
+ int rc;
+
+ do
+ rc = gnutls_record_send (sd, data, size);
+ while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN);
+ if (rc >= 0)
+ {
+ if (nbytes)
+ *nbytes = rc;
+ return 0;
+ }
+ return rc;
+}
+
+static int
+_tls_read (void *sd, char *data, size_t size, size_t * nbytes)
+{
+ int rc = gnutls_record_recv (sd, data, size);
+ if (rc >= 0)
+ {
+ if (nbytes)
+ *nbytes = rc;
+ return 0;
+ }
+ return rc;
+}
+
+static int
+_tls_close (void *sd)
+{
+ if (sd)
+ {
+ gnutls_bye (sd, GNUTLS_SHUT_RDWR);
+ gnutls_deinit (sd);
+ }
+ return 0;
+}
+
+static gnutls_session
+tls_session_init (void)
+{
+ gnutls_session session = 0;
+ int rc;
+
+ gnutls_init (&session, GNUTLS_SERVER);
+ gnutls_set_default_priority (session);
+ gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+ gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
+ gnutls_dh_set_prime_bits (session, DH_BITS);
+
+ gnutls_transport_set_pull_function (session, _tls_fd_pull);
+ gnutls_transport_set_push_function (session, _tls_fd_push);
+
+ gnutls_transport_set_ptr2 (session,
+ (gnutls_transport_ptr) in,
+ (gnutls_transport_ptr) out);
+ rc = gnutls_handshake (session);
+ if (rc < 0)
+ {
+ gnutls_deinit (session);
+ gnutls_perror (rc);
+ return 0;
+ }
+
+ return (gnutls_session) session;
+}
+
+void
+smtp_starttls (void)
+{
+ gnutls_session session;
+
+ smtp_reply (220, "Ready to start TLS");
+
+ session = tls_session_init ();
+ if (session)
+ {
+ in = out = session;
+ _mta_read = _tls_read;
+ _mta_write = _tls_write;
+ _mta_close = _tls_close;
+ _mta_strerror = _tls_strerror;
+ reset_capa ("STARTTLS");
+ }
+ else
+ smtp_reply (530, "TLS negotiation failed");
+}
+
+#endif /* HAVE_TLS */
+
+void
+smtp_reply (int code, char *fmt, ...)
+{
+ va_list ap;
+ int cont = code & R_CONT ? '-' : ' ';
+ static char obuf[512];
+ int n, rc;
+
+ va_start (ap, fmt);
+ n = snprintf (obuf, sizeof obuf, "%d%c", code & R_CODEMASK, cont);
+ n += vsnprintf (obuf + n, sizeof obuf - n, fmt, ap);
+ va_end (ap);
+ n += snprintf (obuf + n, sizeof obuf - n, "\r\n");
+ rc = _mta_write (out, obuf, n, NULL);
+ if (rc)
+ {
+ fprintf (stderr, "Write failed: %s", _mta_strerror (rc));
+ abort ();
+ }
+}
+
+int
+get_input_line (char *buf, size_t bufsize)
+{
+ int i, rc;
+
+ for (i = 0; i < bufsize - 1; i++)
+ {
+ size_t n;
+ rc = _mta_read (in, buf + i, 1, &n);
+ if (rc)
+ {
+ fprintf (stderr, "Read failed: %s\n", _mta_strerror (rc));
+ exit (1);
+ }
+ if (n == 0)
+ break;
+ if (buf[i] == '\n')
+ break;
+ }
+ buf[++i] = 0;
+ return i;
+}
+
+#define STATE_INIT 0
+#define STATE_EHLO 1
+#define STATE_MAIL 2
+#define STATE_RCPT 3
+#define STATE_DATA 4
+#define STATE_QUIT 5
+#define STATE_DOT 6
+
+#define KW_EHLO 0
+#define KW_HELO 1
+#define KW_MAIL 2
+#define KW_RCPT 3
+#define KW_DATA 4
+#define KW_HELP 5
+#define KW_QUIT 6
+#define KW_STARTTLS 7
+#define KW_RSET 8
+
+int
+smtp_kw (const char *name)
+{
+ static struct kw
+ {
+ char *name;
+ int code;
+ }
+ kw[] =
+ {
+ { "ehlo", KW_EHLO },
+ { "helo", KW_HELO },
+ { "mail", KW_MAIL },
+ { "rcpt", KW_RCPT },
+ { "data", KW_DATA },
+ { "help", KW_HELP },
+ { "quit", KW_QUIT },
+ { "help", KW_HELP },
+ { "starttls", KW_STARTTLS },
+ { "rset", KW_RSET },
+ { NULL },
+ };
+ int i;
+
+ for (i = 0; kw[i].name != NULL; i++)
+ if (strcasecmp (name, kw[i].name) == 0)
+ return kw[i].code;
+ return -1;
+}
+
+char *
+skipws (char *str)
+{
+ while (*str && isspace (*(u_char *) str))
+ str++;
+ return str;
+}
+
+char *
+skipword (char *str)
+{
+ while (*str && !isspace (*(u_char *) str))
+ str++;
+ return str;
+}
+
+int
+argcv_split (char *buf, int *pargc, char ***pargv)
+{
+ char *t;
+ int i, argc = 0;
+ char **argv;
+
+ t = buf;
+ do
+ {
+ argc++;
+ t = skipws (t);
+ }
+ while (*t && (t = skipword (t)));
+
+ argv = calloc (argc, sizeof (*argv));
+ for (i = 0, t = strtok (buf, " \t"); t; i++, t = strtok (NULL, " \t"))
+ argv[i] = strdup (t);
+ argv[i] = NULL;
+ *pargc = argc - 1;
+ *pargv = argv;
+ return 0;
+}
+
+int
+argcv_free (int argc, char **argv)
+{
+ while (--argc >= 0)
+ if (argv[argc])
+ free (argv[argc]);
+ free (argv);
+ return 1;
+}
+
+char *mta_capa[] = {
+#ifdef HAVE_TLS
+ "STARTTLS",
+#endif
+ NULL
+};
+
+void
+reset_capa (char *name)
+{
+ int i;
+ for (i = 0; mta_capa[i]; i++)
+ if (strcmp (mta_capa[i], name) == 0)
+ {
+ mta_capa[i] = NULL;
+ break;
+ }
+}
+
+void
+smtp_ehlo (int extended, char *domain)
+{
+ int i;
+
+ if (gsrv)
+ {
+ gacopyz_srv_define_macro (gsrv, "s", domain);
+ gacopyz_srv_helo (gsrv, domain);
+ }
+
+ if (!extended)
+ {
+ smtp_reply (250, "pleased to meet you");
+ return;
+ }
+
+ smtp_reply (R_CONT | 250, "pleased to meet you");
+ for (i = 0; mta_capa[i]; i++)
+ smtp_reply (R_CONT | 250, "%s", mta_capa[i]);
+ smtp_reply (250, "HELP");
+}
+
+void
+smtp_help (void)
+{
+ smtp_reply (502, "HELP not implemented");
+}
+
+void
+smtp (void)
+{
+ int state;
+ char buf[128];
+ unsigned nrcpt = 0;
+
+ smtp_reply (220, "localhost bitbucket ready");
+ for (state = STATE_INIT; state != STATE_QUIT;)
+ {
+ int argc;
+ char **argv;
+ int kw, len;
+
+ if (get_input_line (buf, sizeof buf) <= 0)
+ exit (1);
+
+ len = strlen (buf);
+ while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
+ len--;
+ buf[len] = 0;
+
+ if (diag)
+ fprintf (diag, "%s\n", buf);
+
+ argcv_split (buf, &argc, &argv);
+ if (argc == 0)
+ continue;
+
+ if (argv[0][0] == '\\')
+ {
+ shell (argc, argv);
+ argcv_free (argc, argv);
+ continue;
+ }
+
+ kw = smtp_kw (argv[0]);
+ if (kw == KW_QUIT)
+ {
+ state = STATE_QUIT;
+ argcv_free (argc, argv);
+ continue;
+ }
+ else if (kw == KW_HELP)
+ {
+ smtp_help ();
+ continue;
+ }
+
+ switch (state) {
+ case STATE_INIT:
+ switch (kw) {
+ case KW_EHLO:
+ case KW_HELO:
+ if (argc == 2)
+ {
+ smtp_ehlo (kw == KW_EHLO, argv[1]);
+ state = STATE_EHLO;
+ }
+ else
+ smtp_reply (501, "%s requires domain address", argv[0]);
+ break;
+
+ default:
+ smtp_reply (503, "Polite people say HELO first");
+ break;
+ }
+ break;
+
+ case STATE_EHLO:
+ switch (kw) {
+ case KW_EHLO:
+ if (argc == 2)
+ {
+ smtp_ehlo (1, argv[1]);
+ }
+ else
+ smtp_reply (501, "%s requires domain address", argv[0]);
+ break;
+
+ case KW_MAIL:
+ if (argc == 3 && strcasecmp (argv[1], "from:") == 0)
+ {
+ if (gsrv)
+ {
+ gacopyz_srv_define_macro (gsrv, "f", argv[2]);
+ gacopyz_srv_envfrom (gsrv, argv + 2);
+ }
+ smtp_reply (250, "Sender OK");
+ state = STATE_MAIL;
+ }
+ else
+ smtp_reply (501, "Syntax error");
+ break;
+
+#ifdef HAVE_TLS
+ case KW_STARTTLS:
+ smtp_starttls ();
+ break;
+#endif
+ default:
+ smtp_reply (503, "Need MAIL command");
+ }
+ break;
+
+ case STATE_MAIL:
+ switch (kw) {
+ case KW_RCPT:
+ nrcpt++;
+ if (argc == 3 && strcasecmp (argv[1], "to:") == 0)
+ {
+ if (gsrv)
+ {
+ update_nrcpts (nrcpt);
+ gacopyz_srv_envfrom (gsrv, argv + 2);
+ }
+ smtp_reply (250, "Recipient OK");
+ state = STATE_RCPT;
+ }
+ else
+ smtp_reply (501, "Syntax error");
+ break;
+
+ default:
+ smtp_reply (503, "Need RCPT command");
+ }
+ break;
+
+ case STATE_RCPT:
+ switch (kw) {
+ case KW_RCPT:
+ if (argc == 3 && strcasecmp (argv[1], "to:") == 0)
+ {
+ if (gsrv)
+ {
+ update_nrcpts (nrcpt);
+ gacopyz_srv_envfrom (gsrv, argv + 2);
+ }
+ smtp_reply (250, "Recipient OK");
+ }
+ else
+ smtp_reply (501, "Syntax error");
+ break;
+
+ case KW_DATA:
+ if (gsrv)
+ gacopyz_srv_data (gsrv);
+
+ smtp_reply (354,
+ "Enter mail, end with \".\" on a line by itself");
+ state = STATE_DATA;
+ break;
+
+ default:
+ smtp_reply (501, "Syntax error");
+ }
+
+ case STATE_DATA:
+ if (strcmp (buf, ".") == 0)
+ {
+ if (gsrv)
+ gacopyz_srv_eom (gsrv);
+ /* FIXME: Clear macro table, except for the entires from
+ command line */
+ smtp_reply (250, "Mail accepted for delivery");
+ state = STATE_EHLO;
+ }
+ else
+ {
+ /* FIXME: Collect headers and body lines and feed them
+ to gacopyz */
+ }
+ break;
+ }
+ }
+ if (gsrv)
+ {
+ gacopyz_srv_close (gsrv);
+ gacopyz_srv_destroy (&gsrv);
+ }
+}
+
+int
+mta_daemon (int argc, char **argv)
+{
+ int on = 1;
+ struct sockaddr_in address;
+ int fd;
+
+ fd = socket (PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ {
+ perror ("socket");
+ return 1;
+ }
+
+ setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on));
+
+ memset (&address, 0, sizeof (address));
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+
+ if (port)
+ {
+ address.sin_port = htons (port);
+ if (bind (fd, (struct sockaddr *) &address, sizeof (address)) < 0)
+ {
+ close (fd);
+ perror ("bind");
+ return 1;
+ }
+ }
+ else
+ {
+ int status;
+
+ port = 1023;
+ do
+ {
+ if (++port >= 65535)
+ {
+ error ("can't bind socket: all ports in use?");
+ return 1;
+ }
+ address.sin_port = htons (port);
+ status = bind (fd, (struct sockaddr *) &address, sizeof (address));
+ }
+ while (status < 0);
+ printf ("%d\n", port);
+ fclose (stdout);
+ }
+
+ listen (fd, 5);
+ while (1)
+ {
+ fd_set rfds;
+ struct sockaddr_in his_addr;
+ int sfd, len, status;
+
+ FD_ZERO (&rfds);
+ FD_SET (fd, &rfds);
+
+ status = select (fd + 1, &rfds, NULL, NULL, NULL);
+ if (status == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ perror ("select");
+ return 1;
+ }
+
+ len = sizeof (his_addr);
+ if ((sfd = accept (fd, (struct sockaddr *) &his_addr, &len)) < 0)
+ {
+ perror ("accept");
+ return 1;
+ }
+
+ in = out = (void *) fd;
+ smtp ();
+ break;
+ }
+
+ return 0;
+}
+
+int
+mta_stdio (int argc, char **argv)
+{
+ in = (void *) fileno (stdin);
+ out = (void *) fileno (stdout);
+ smtp ();
+ return 0;
+}
+
+
+
+int
+define_macro (char *arg)
+{
+ char *p;
+ p = strchr (arg, '=');
+ if (!p)
+ return 1;
+ *p++ = 0;
+ gacopyz_srv_define_macro (gsrv, arg, p);
+ return 0;
+}
+
+void
+undefine_macro (char *arg)
+{
+ gacopyz_srv_del_macro (gsrv, arg);
+}
+
+void
+undefine_all_macros ()
+{
+ gacopyz_srv_clear_macros (gsrv);
+}
+
+void
+list_macro (char *name)
+{
+ const char *val;
+ if (gacopyz_srv_find_macro (gsrv, name, &val) == MI_SUCCESS)
+ smtp_reply (220, "%s=%s", name, val);
+ else
+ smtp_reply (220, "%s undefined", name);
+}
+
+struct macro_itr
+{
+ size_t num;
+ size_t count;
+};
+
+int
+macprint (const char *name, const char *value, void *data)
+{
+ struct macro_itr *itr = data;
+ size_t i;
+ int code = 220;
+
+ itr->num++;
+ if (itr->num < itr->count)
+ code |= R_CONT;
+ smtp_reply (code, "%s=%s", name, value);
+ return 0;
+}
+
+void
+list_all_macros ()
+{
+ struct macro_itr itr;
+
+ itr.num = itr.count = 0;
+ gacopyz_srv_count_macros (gsrv, &itr.count);
+
+ if (itr.count == 0)
+ smtp_reply (220, "No macros defined");
+ else
+ gacopyz_srv_iterate_macros (gsrv, macprint, &itr);
+}
+
+void
+shell_help ()
+{
+ static char *hstr[] = {
+ "\\Dname=value [name=value...] Define Sendmail macros",
+ "\\Uname [name...] Undefine Sendmail macros",
+ "\\L[name] [name...] List macros"
+ };
+ #define hcount (sizeof (hstr) / sizeof (hstr[0]))
+ int i;
+
+ for (i = 0; i < hcount; i++)
+ smtp_reply (((i < hcount-1) ? R_CONT : 0) | 250, "%s", hstr[i]);
+}
+
+void
+shell (int argc, char **argv)
+{
+ switch (argv[0][1])
+ {
+ case 'D':
+ {
+ int rc = 0;
+ if (argv[0][2])
+ rc = define_macro (&argv[0][2]);
+ while (rc == 0 && --argc)
+ rc = define_macro (*++argv);
+
+ if (rc)
+ smtp_reply (502, "Malformed administrative command");
+ }
+ break;
+
+ case 'U':
+ if (argv[0][2])
+ {
+ undefine_macro (&argv[0][2]);
+ while (--argc)
+ undefine_macro (*++argv);
+ }
+ else if (argc > 1)
+ while (--argc)
+ undefine_macro (*++argv);
+ else
+ undefine_all_macros ();
+ break;
+
+ case 'L':
+ if (argv[0][2])
+ {
+ list_macro (&argv[0][2]);
+ while (--argc)
+ list_macro (*++argv);
+ }
+ else if (argc > 1)
+ while (--argc)
+ list_macro (*++argv);
+ else
+ list_all_macros ();
+ break;
+
+ case '?':
+ shell_help ();
+ break;
+
+ default:
+ smtp_reply (502, "Unknown administrative command");
+ break;
+ }
+}
+
+/*
+ Local Variables:
+ c-file-style: "gnu"
+ End:
+*/
+/* EOF */

Return to:

Send suggestions and report system problems to the System administrator.