aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README11
-rw-r--r--configure.ac30
-rw-r--r--mockmta.133
-rw-r--r--mockmta.c249
4 files changed, 284 insertions, 39 deletions
diff --git a/README b/README
index da80bc8..f3915b6 100644
--- a/README
+++ b/README
@@ -28,6 +28,17 @@ If you want mockmta to support STARTTLS command, make sure the GnuTLS
library version 3.3.0 or newer is installed on your system and is
reported correctly by pkg-config.
+The configure tool takes a number of options, most of which are described
+in the INSTALL file. Two options are specific to mockmta:
+
+ --without-gnutls
+ Do not build with the GnuTLS library, even if it is available.
+
+ --without-tcp-wrappers
+ Do not build support for TCP wrappers. The use of TCP wrappers is
+ recommended if you plan to use mockmta on IP addresses. other than
+ localhost.
+
* Usage
Mockmta can work both as an inetd-style process (default) and as a
diff --git a/configure.ac b/configure.ac
index d7d5700..1d79f64 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,10 +13,13 @@ AC_CHECK_LIB(rt, clock_gettime)
AX_PTHREAD([],
[AC_MSG_ERROR([POSIX threads support is required, but not available])])
+#
+# GnuTLS
+#
gnutls_status=probe
AC_ARG_WITH([gnutls],
- [AC_HELP_STRING([--without-gnutls],
- [do not use the GNU TLS library])],
+ [AC_HELP_STRING([--with-gnutls],
+ [use the GNU TLS library (default)])],
[case "$withval" in
yes|no) gnutls_status=$withval;;
*) AC_MSG_ERROR([bad value for --with-gnutls])
@@ -34,5 +37,28 @@ fi
AC_SUBST([LIBGNUTLS_CFLAGS])
AC_SUBST([LIBGNUTLS_LIBS])
+#
+# TCP wrappers
+#
+# **********************
+# TCP wrappers
+# **********************
+AC_ARG_WITH(tcp-wrappers,
+ AC_HELP_STRING([--with-tcp-wrappers],
+ [compile with TCP wrappers support (default)]),
+ [status_tcpwrap=${withval}],
+ [status_tcpwrap=yes])
+
+if test "$status_tcpwrap" = yes; then
+ AC_CHECK_LIB(nsl, main)
+ AC_CHECK_LIB(wrap, main,, [status_tcpwrap=no])
+ if test "$status_tcpwrap" = yes; then
+ AC_CHECK_HEADERS(tcpd.h,,[status_tcpwrap=no])
+ fi
+fi
+if test "$status_tcpwrap" = yes; then
+ AC_DEFINE([WITH_LIBWRAP],1,[Defined if compiling with libwrap])
+fi
+
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
diff --git a/mockmta.1 b/mockmta.1
index 66de1c0..02036eb 100644
--- a/mockmta.1
+++ b/mockmta.1
@@ -1,21 +1,21 @@
-.TH MOCKMTA 1 "June 16, 2021" "MOCKMTA" "General Commands Manual"
+.TH MOCKMTA 1 "June 17, 2021" "MOCKMTA" "General Commands Manual"
.SH NAME
mockmta \- mock MTA server for experimental purposes
.SH SYNOPSIS
\fBmockmta\fR\
- [\fB\-dv?\fR]\
+ [\fB\-dsv?\fR]\
[\fB\-c \fICERT\fR]\
[\fB\-a \fICA\fR]\
[\fB\-k \fIKEY\fR]\
[\fB\-l \fILEVEL\fR]\
[\fB\-P \fIFILE\fR]\
- [\fB\-p \fIPORT\fR]\
+ [\fB\-p [\fIIP\fB:\fR]\fIPORT\fR]\
[\fB\-t \fISEC\fR]\
\fIMAILBOX\fR
.SH DESCRIPTION
Starts a mock MTA, which behaves almost identically to the real one,
-except that it listens on localhost only and delivers all messages, no
-matter their actual recipients, to the given MAILBOX file.
+except that it delivers all messages, no matter their actual
+recipients, to the given MAILBOX file.
.PP
No attempts are made to interpret the data supplied during the STMP
transaction, such as domain names, email addresses, etc, neither is
@@ -35,7 +35,16 @@ daemon. The inetd mode can be used with \fBGNU pies\fR as follows:
.EE
.PP
When run as a daemon, \fBmockmta\fR starts listening on localhost port
-\fIPORT\fR (default 25).
+25 by default. Alternative port and/or IP address can be requested
+using the \fB\-p\fR option.
+.PP
+Each incoming connection is verified using
+.B /etc/hosts.allow
+and
+.B /etc/hosts.deny
+files (daemon name \fBmockmta\fR). See
+.BR hosts_access (5),
+for a detailed description of these.
.PP
To support TLS, the program must be compiled with the \fBGnuTLS\fR library.
.PP
@@ -75,8 +84,14 @@ Default is \fBinfo\fR.
\fB\-P \fIFILE\fR
Write PID of the daemon process to \fIFILE\fR (with \fB\-d\fR or \fB\-f\fR).
.TP
-\fB\-p \fIPORT\fR
-Listen on this port.
+\fB\-p [\fIIP\fB:\fR]\fIPORT\fR
+Listen on this IP address and port. If \fIIP\fR is not given, listen
+on localhost. To listen on all available interfaces, use
+\fB\-p :\fIPORT\fR.
+.TP
+.B \-s
+Use syslog (facility \fBmail\fR) for logging. This is the default if
+\fB\-d\fR or \fB\-f\fR option is given.
.TP
\fB\-t \fISEC\fR
Set SMTP timeout.
@@ -101,6 +116,8 @@ Timed out waiting for I/O.
Command line usage error.
.SH BUGS
At most 32 \fBRCPT\fR commands are allowed.
+.SH SEE ALSO
+.BR hosts_access (5).
.SH AUTHOR
Sergey Poznyakoff <gray@gnu.org>
.SH LICENSE
diff --git a/mockmta.c b/mockmta.c
index 51a3250..e498700 100644
--- a/mockmta.c
+++ b/mockmta.c
@@ -20,6 +20,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
+#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
@@ -29,20 +30,25 @@
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <netdb.h>
#include <pthread.h>
#include <signal.h>
+#include <ctype.h>
#ifdef HAVE_PATHS_H
# include <paths.h>
#endif
#ifndef _PATH_DEVNULL
# define _PATH_DEVNULL "/dev/null"
#endif
+#ifdef WITH_LIBWRAP
+# include <tcpd.h>
+#endif
char *progname;
char *mailbox_name;
int daemon_opt;
int smtp_timeout = 5*60;
-int port = 25;
+static char *portstr = "25";
int log_level = LOG_INFO;
char *pidfile;
@@ -253,6 +259,127 @@ nomemory (void)
exit (EX_FAILURE);
}
+static char *
+sockaddr_str (struct sockaddr *sa, int salen)
+{
+ char buf[512];
+
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ case AF_INET6:
+ {
+ char host[NI_MAXHOST];
+ char srv[NI_MAXSERV];
+
+ if (getnameinfo (sa, salen,
+ host, sizeof (host), srv, sizeof (srv),
+ NI_NUMERICHOST|NI_NUMERICSERV) == 0)
+ snprintf (buf, sizeof buf, "%s://%s:%s",
+ sa->sa_family == AF_INET ? "inet" : "inet6",
+ host, srv);
+ else
+ snprintf (buf, sizeof buf, "%s://[getnameinfo failed]",
+ sa->sa_family == AF_INET ? "inet" : "inet6");
+ break;
+ }
+
+ default:
+ /* Should not happen */
+ snprintf (buf, sizeof buf, "family:%d", sa->sa_family);
+ }
+ return strdup (buf);
+}
+
+static int
+is_numstr (const char *p)
+{
+ if (!*p)
+ return 0;
+ for (; *p && isascii (*p) && isdigit (*p);p++)
+ ;
+ return *p == 0;
+}
+
+struct addrinfo *
+address_parse (char const *addrstr)
+{
+ struct addrinfo hints;
+ struct addrinfo *res;
+ int rc;
+ char const *node;
+ char const *service;
+ char *nodebuf = NULL;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+
+ service = strrchr (addrstr, ':');
+ if (service)
+ {
+ size_t len = service - addrstr;
+
+ if (len == 0)
+ node = NULL;
+ else
+ {
+ nodebuf = malloc (len + 1);
+ if (!nodebuf)
+ nomemory ();
+ memcpy (nodebuf, addrstr, len);
+ nodebuf[len] = 0;
+ node = nodebuf;
+ }
+ service++;
+ if (!*service)
+ {
+ service = "25";
+ hints.ai_flags |= AI_NUMERICSERV;
+ }
+ }
+ else if (is_numstr (addrstr))
+ {
+ node = "127.0.0.1";
+ service = addrstr;
+ hints.ai_flags |= AI_NUMERICSERV;
+ }
+ else
+ {
+ node = addrstr;
+ service = "25";
+ hints.ai_flags |= AI_NUMERICSERV;
+ }
+
+ rc = getaddrinfo (node, service, &hints, &res);
+ free (nodebuf);
+
+ switch (rc)
+ {
+ case 0:
+ break;
+
+ case EAI_SYSTEM:
+ logger (LOG_CRIT, "cannot parse address: %m");
+ exit (EX_USAGE);
+
+ case EAI_BADFLAGS:
+ case EAI_SOCKTYPE:
+ logger (LOG_CRIT, "%s:%d: internal error converting address",
+ __FILE__,__LINE__);
+ exit (EX_FAILURE);
+
+ case EAI_MEMORY:
+ nomemory ();
+
+ default:
+ logger (LOG_ERR, "getaddrinfo: %s", gai_strerror (rc));
+ exit (EX_FAILURE);
+ }
+ return res;
+}
+
struct iodrv
{
int (*drv_read) (void *, char *, size_t, size_t *);
@@ -1721,11 +1848,24 @@ thr_watcher (void *ptr)
return NULL;
}
+int
+tcpwrap_access(int fd)
+{
+#ifdef WITH_LIBWRAP
+ struct request_info req;
+
+ request_init (&req, RQ_DAEMON, PACKAGE_NAME, RQ_FILE, fd, NULL);
+ fromhost (&req);
+ return hosts_access (&req);
+#else
+ return 1;
+#endif
+}
+
static int
-mta_open (int port)
+mta_open (struct addrinfo *ap)
{
int on = 1;
- struct sockaddr_in address;
int fd;
fd = socket (PF_INET, SOCK_STREAM, 0);
@@ -1737,12 +1877,7 @@ mta_open (int port)
setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on));
- memset (&address, 0, sizeof (address));
- address.sin_family = AF_INET;
- address.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
- address.sin_port = htons (port);
-
- if (bind (fd, (struct sockaddr *) &address, sizeof (address)) < 0)
+ if (bind (fd, ap->ai_addr, ap->ai_addrlen) < 0)
{
close (fd);
logger (LOG_ERR, "bind: %m");
@@ -1805,10 +1940,9 @@ thr_mta_listener (void *ptr)
while (1)
{
int sfd;
- struct sockaddr_in remote_addr;
+ struct sockaddr_storage remote_addr;
socklen_t len = sizeof (remote_addr);
- pthread_t tid;
- struct smtp *smtp;
+ char *s;
if ((sfd = accept (fd, (struct sockaddr *) &remote_addr, &len)) < 0)
{
@@ -1816,8 +1950,21 @@ thr_mta_listener (void *ptr)
exit (EX_FAILURE);
}
- smtp = smtp_create (sfd, sfd);
- pthread_create (&tid, &attr, thr_smtp, smtp);
+ s = sockaddr_str ((struct sockaddr *) &remote_addr, len);
+ if (!tcpwrap_access (sfd))
+ {
+ logger (LOG_NOTICE, "denied access from %s", s);
+ close (sfd);
+ }
+ else
+ {
+ pthread_t tid;
+ struct smtp *smtp = smtp_create (sfd, sfd);
+ logger (LOG_INFO, "%s: connect from %s", smtp->sid, s);
+ pthread_create (&tid, &attr, thr_smtp, smtp);
+ }
+
+ free (s);
}
return NULL;
}
@@ -1866,20 +2013,22 @@ static char usage_help[] = "Usage: mockmta [OPTIONS] MAILBOX";
static char prog_descr[] = "Simple MTA for testing purposes";
static char *opthelp[] = {
#ifdef WITH_TLS
- "-a CA name of certificate authority file",
- "-c CERT name of the certificate file",
+ "-a CA name of certificate authority file",
+ "-c CERT name of the certificate file",
#endif
- "-d daemon mode",
- "-f remain in foreground (implies -d)",
+ "-d daemon mode",
+ "-f remain in foreground (implies -d)",
#ifdef WITH_TLS
- "-k KEY name of the certificate key file",
+ "-k KEY name of the certificate key file",
#endif
- "-l LEVEL log events at the specified, or more important, loglevels",
- "-P FILE write PID to FILE (with -d or -f)",
- "-p PORT listen on this port",
- "-t SEC set SMTP timeout",
- "-v print program version and exit",
- "-? print this help summary and exit",
+ "-l LEVEL log events at the specified, or more important,",
+ " loglevels",
+ "-P FILE write PID to FILE (with -d or -f)",
+ "-p [ADDRESS:]PORT listen on this port",
+ "-s always log via syslog",
+ "-t SEC set SMTP timeout",
+ "-v print program version and exit",
+ "-? print this help summary and exit",
NULL
};
@@ -1913,6 +2062,9 @@ There is NO WARRANTY, to the extent permitted by law.\n\
printf ("\n");
printf ("Using GnuTLS %s\n", gnutls_check_version (NULL));
#endif
+#ifdef WITH_LIBWRAP
+ printf ("Using libwrap\n");
+#endif
}
int
@@ -1921,6 +2073,7 @@ main (int argc, char **argv)
int c;
int fd;
int foreground = 0;
+ int syslog_opt = 0;
struct sigaction act;
sigset_t sigs;
int i;
@@ -1928,7 +2081,7 @@ main (int argc, char **argv)
progname = argv[0];
- while ((c = getopt (argc, argv, "a:dc:fk:l:P:p:t:v?")) != EOF)
+ while ((c = getopt (argc, argv, "a:dc:fk:l:P:p:st:v?")) != EOF)
{
switch (c)
{
@@ -1950,9 +2103,13 @@ main (int argc, char **argv)
break;
case 'p':
- port = atoi (optarg);
+ portstr = optarg;
break;
+ case 's':
+ syslog_opt = 1;
+ break;
+
case 't':
smtp_timeout = atoi (optarg);
if (smtp_timeout <= 0)
@@ -2015,7 +2172,9 @@ main (int argc, char **argv)
if (daemon_opt)
{
- fd = mta_open (port);
+ struct addrinfo *addr = address_parse (portstr);
+ fd = mta_open (addr);
+ freeaddrinfo (addr);
if (!foreground)
{
@@ -2067,14 +2226,46 @@ main (int argc, char **argv)
}
}
}
+ else if (syslog_opt)
+ {
+ openlog (progname, LOG_PID, LOG_MAIL);
+ logger = log_syslog;
+ }
pthread_create (&tid, NULL, thr_watcher, NULL);
pthread_create (&tid, NULL, thr_mta_listener, &fd);
}
else
{
+ struct sockaddr_storage remote_addr;
+ socklen_t len = sizeof (remote_addr);
+ struct smtp *smtp = smtp_create (0, 1);
+ char *s = NULL;
+
+ if (syslog_opt)
+ {
+ openlog (progname, LOG_PID, LOG_MAIL);
+ logger = log_syslog;
+ }
+
+ if (getpeername (0, (struct sockaddr *) &remote_addr, &len) == 0)
+ {
+ s = sockaddr_str ((struct sockaddr *) &remote_addr, len);
+
+ if (!tcpwrap_access (fd))
+ {
+ logger (LOG_NOTICE, "denied access from %s", s);
+ exit (EX_FAILURE);
+ }
+ }
+
+ if (s)
+ {
+ logger (LOG_INFO, "%s: connect from %s", smtp->sid, s);
+ free (s);
+ }
pthread_create (&tid, NULL, thr_watcher, NULL);
- pthread_create (&tid, NULL, thr_smtp_stdio, smtp_create (0, 1));
+ pthread_create (&tid, NULL, thr_smtp_stdio, smtp);
pthread_detach (tid);
}

Return to:

Send suggestions and report system problems to the System administrator.