aboutsummaryrefslogtreecommitdiff
path: root/mockmta.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2021-06-17 16:46:13 +0300
committerSergey Poznyakoff <gray@gnu.org>2021-06-17 16:46:13 +0300
commit6fa210d16195ba4448648e171bb053d3b49fa095 (patch)
tree0700b62bfd5e5716ebf6e1313ec01b4cfb0c1d3b /mockmta.c
parentc3a3d811d674b7fac0355253a5bd4c481e687a83 (diff)
downloadmockmta-6fa210d16195ba4448648e171bb053d3b49fa095.tar.gz
mockmta-6fa210d16195ba4448648e171bb053d3b49fa095.tar.bz2
Listen on arbitrary IP address/port. Use tcp-wrappers.HEADmaster
* README: Update. * configure.ac: New option --with-tcp-wrappers * mockmta.1: Document changes. * mockmta.c (port): Replace with portstr. (sockaddr_str, address_parse): New functions. (tcpwrap_access): New function. (mta_open): Take struct addrinfo * as argument. (thr_mta_listener): Verify connection using TCP wrappers and log the peer's IP address. (main): Likewise (for inetd mode). New option -s. Use syslog if it is given. (version): Report whether libwrap is used.
Diffstat (limited to 'mockmta.c')
-rw-r--r--mockmta.c249
1 files changed, 220 insertions, 29 deletions
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.