diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2021-06-17 16:46:13 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2021-06-17 16:46:13 +0300 |
commit | 6fa210d16195ba4448648e171bb053d3b49fa095 (patch) | |
tree | 0700b62bfd5e5716ebf6e1313ec01b4cfb0c1d3b /mockmta.c | |
parent | c3a3d811d674b7fac0355253a5bd4c481e687a83 (diff) | |
download | mockmta-master.tar.gz mockmta-master.tar.bz2 |
* 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.c | 249 |
1 files changed, 220 insertions, 29 deletions
@@ -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); } |