dma(8) : update to version 0.9
authorJoris Giovannangeli <joris@giovannangeli.fr>
Fri, 21 Feb 2014 17:17:22 +0000 (18:17 +0100)
committerJoris Giovannangeli <joris@giovannangeli.fr>
Fri, 21 Feb 2014 17:17:22 +0000 (18:17 +0100)
 * Sources from upstream commit 89702b7f148445240a308a5be65f79891e865a4b

 * (minor) local changes are kept.

14 files changed:
libexec/dma/README.markdown
libexec/dma/VERSION
libexec/dma/aliases_parse.y
libexec/dma/conf.c
libexec/dma/crypto.c
libexec/dma/dma-mbox-create.c [moved from libexec/dma/dma-mbox-create/dma-mbox-create.c with 100% similarity]
libexec/dma/dma.8
libexec/dma/dma.c
libexec/dma/dma.conf
libexec/dma/dma.h
libexec/dma/dma/Makefile
libexec/dma/get-version.sh [new file with mode: 0644]
libexec/dma/net.c
libexec/dma/spool.c

index 528a4d6..13ff20c 100644 (file)
@@ -23,6 +23,8 @@ Installation
 
        make install sendmail-link mailq-link install-spool-dirs install-etc
 
+See INSTALL for requirements and configuration options.
+
 
 Contact
 -------
index 03776fb..490a0cd 100644 (file)
@@ -1 +1 @@
-v0.7
+v0.9
index dd8b88a..a5a9e7b 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <err.h>
 #include <string.h>
+#include <syslog.h>
 #include "dma.h"
 
 extern int yylineno;
@@ -12,7 +13,12 @@ int yylex(void);
 static void
 yyerror(const char *msg)
 {
-       warnx("aliases line %d: %s", yylineno, msg);
+       /**
+        * Because we do error '\n' below, we need to report the error
+        * one line above of what yylineno points to.
+        */
+       syslog(LOG_CRIT, "aliases line %d: %s", yylineno - 1, msg);
+       fprintf(stderr, "aliases line %d: %s\n", yylineno - 1, msg);
 }
 
 int
@@ -72,8 +78,7 @@ alias : T_IDENT ':' dests '\n'
                }
        | error '\n'
                {
-                       yyerrok;
-                       $$ = NULL;
+                       YYABORT;
                }
        ;
 
index e8cb463..919ab7c 100644 (file)
@@ -211,7 +211,7 @@ parse_conf(const char *config_path)
                        } else {
                                host = data;
                        }
-                       if (host && *host == 0)
+                       if (host && *host == 0)
                                host = NULL;
                         if (user && *user == 0)
                                 user = NULL;
@@ -229,11 +229,18 @@ parse_conf(const char *config_path)
                        config.features |= INSECURE;
                else if (strcmp(word, "FULLBOUNCE") == 0 && data == NULL)
                        config.features |= FULLBOUNCE;
+               else if (strcmp(word, "NULLCLIENT") == 0 && data == NULL)
+                       config.features |= NULLCLIENT;
                else {
                        errlogx(1, "syntax error in %s:%d", config_path, lineno);
                        /* NOTREACHED */
                }
        }
 
+       if ((config.features & NULLCLIENT) && config.smarthost == NULL) {
+               errlogx(1, "%s: NULLCLIENT requires SMARTHOST", config_path);
+               /* NOTREACHED */
+       }
+
        fclose(conf);
 }
index 7e8865c..897b55b 100644 (file)
@@ -80,7 +80,11 @@ int
 smtp_init_crypto(int fd, int feature)
 {
        SSL_CTX *ctx = NULL;
+#if (OPENSSL_VERSION_NUMBER >= 0x00909000L)
        const SSL_METHOD *meth = NULL;
+#else
+       SSL_METHOD *meth = NULL;
+#endif
        X509 *cert;
        int error;
 
index e32fbfc..792ec42 100644 (file)
@@ -29,7 +29,7 @@
 .\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd January 9, 2012
+.Dd February 13, 2014
 .Dt DMA 8
 .Os
 .Sh NAME
@@ -304,6 +304,15 @@ setting it to
 will send all mails as
 .Ql Sm off Va username @percolator .
 .Sm on
+.It Ic NULLCLIENT Xo
+.Xc
+Bypass aliases and local delivery, and instead forward all mails to
+the defined
+.Sq SMARTHOST .
+.Sq NULLCLIENT
+requires
+.Sq SMARTHOST
+to be set.
 .El
 .Ss Environment variables
 The behavior of
@@ -349,4 +358,4 @@ utility first appeared in
 was written by
 .An Matthias Schmidt Aq Mt matthias@dragonflybsd.org
 and
-.An Simon Schubert Aq Mt 2@0x2c .
+.An Simon Schubert Aq Mt 2@0x2c.org .
index b6040ec..9a67b63 100644 (file)
@@ -70,6 +70,7 @@ const char *logident_base;
 char errmsg[ERRMSG_SIZE];
 
 static int daemonize = 1;
+static int doqueue = 0;
 
 struct config config = {
        .smarthost      = NULL,
@@ -94,25 +95,31 @@ sighup_handler(int signo)
 static char *
 set_from(struct queue *queue, const char *osender)
 {
+       const char *addr;
        char *sender;
 
        if (osender) {
-               sender = strdup(osender);
-               if (sender == NULL)
-                       return (NULL);
+               addr = osender;
        } else if (getenv("EMAIL") != NULL) {
-               sender = strdup(getenv("EMAIL"));
-               if (sender == NULL)
-                       return (NULL);
+               addr = getenv("EMAIL");
        } else {
-               const char *from_user = username;
+               if (config.masquerade_user)
+                       addr = config.masquerade_user;
+               else
+                       addr = username;
+       }
+
+       if (!strchr(addr, '@')) {
                const char *from_host = hostname();
 
-               if (config.masquerade_user)
-                       from_user = config.masquerade_user;
                if (config.masquerade_host)
                        from_host = config.masquerade_host;
-               if (asprintf(&sender, "%s@%s", from_user, from_host) <= 0)
+
+               if (asprintf(&sender, "%s@%s", addr, from_host) <= 0)
+                       return (NULL);
+       } else {
+               sender = strdup(addr);
+               if (sender == NULL)
                        return (NULL);
        }
 
@@ -195,7 +202,12 @@ add_recp(struct queue *queue, const char *str, int expand)
                }
        }
        LIST_INSERT_HEAD(&queue->queue, it, next);
-       if (strrchr(it->addr, '@') == NULL) {
+
+       /**
+        * Do local delivery if there is no @.
+        * Do not do local delivery when NULLCLIENT is set.
+        */
+       if (strrchr(it->addr, '@') == NULL && (config.features & NULLCLIENT) == 0) {
                it->remote = 0;
                if (expand) {
                        aliased = do_alias(queue, it->addr);
@@ -265,11 +277,21 @@ retit:
                        /*
                         * If necessary, acquire the queue and * mail files.
                         * If this fails, we probably were raced by another
-                        * process.
+                        * process.  It is okay to be raced if we're supposed
+                        * to flush the queue.
                         */
                        setlogident("%s", it->queueid);
-                       if (acquirespool(it) < 0)
+                       switch (acquirespool(it)) {
+                       case 0:
+                               break;
+                       case 1:
+                               if (doqueue)
+                                       exit(0);
+                               syslog(LOG_WARNING, "could not lock queue file");
                                exit(1);
+                       default:
+                               exit(1);
+                       }
                        dropspool(queue, it);
                        return (it);
 
@@ -291,7 +313,7 @@ static void
 deliver(struct qitem *it)
 {
        int error;
-       unsigned int backoff = MIN_RETRY;
+       unsigned int backoff = MIN_RETRY, slept;
        struct timeval now;
        struct stat st;
 
@@ -323,7 +345,14 @@ retry:
                                 MAX_TIMEOUT);
                        goto bounce;
                }
-               if (sleep(backoff) == 0) {
+               for (slept = 0; slept < backoff;) {
+                       slept += SLEEP_TIMEOUT - sleep(SLEEP_TIMEOUT);
+                       if (flushqueue_since(slept)) {
+                               backoff = MIN_RETRY;
+                               goto retry;
+                       }
+               }
+               if (slept >= backoff) {
                        /* pick the next backoff between [1.5, 2.5) times backoff */
                        backoff = backoff + backoff / 2 + random() % backoff;
                        if (backoff > MAX_RETRY)
@@ -393,7 +422,7 @@ main(int argc, char **argv)
        char *sender = NULL;
        struct queue queue;
        int i, ch;
-       int nodot = 0, doqueue = 0, showq = 0, queue_only = 0;
+       int nodot = 0, showq = 0, queue_only = 0;
        int recp_from_header = 0;
 
        set_username();
@@ -405,9 +434,14 @@ main(int argc, char **argv)
        if (geteuid() == 0 || getuid() == 0) {
                struct passwd *pw;
 
+               errno = 0;
                pw = getpwnam(DMA_ROOT_USER);
-               if (pw == NULL)
-                       err(1, "cannot drop root privileges");
+               if (pw == NULL) {
+                       if (errno == 0)
+                               errx(1, "user '%s' not found", DMA_ROOT_USER);
+                       else
+                               err(1, "cannot drop root privileges");
+               }
 
                if (setuid(pw->pw_uid) != 0)
                        err(1, "cannot drop root privileges");
@@ -428,6 +462,13 @@ main(int argc, char **argv)
                if (argc != 0)
                        errx(1, "invalid arguments");
                goto skipopts;
+       } else if (strcmp(argv[0], "newaliases") == 0) {
+               logident_base = "dma";
+               setlogident(NULL);
+
+               if (read_aliases() != 0)
+                       errx(1, "could not parse aliases file `%s'", config.aliases);
+               exit(0);
        }
 
        opterr = 0;
@@ -476,6 +517,9 @@ main(int argc, char **argv)
                        break;
 
                case 'q':
+                       /* Don't let getopt slup up other arguments */
+                       if (optarg && *optarg == '-')
+                               optind--;
                        doqueue = 1;
                        break;
 
@@ -540,6 +584,7 @@ skipopts:
        }
 
        if (doqueue) {
+               flushqueue_signal();
                if (load_queue(&queue) < 0)
                        errlog(1, "can not load queue");
                run_queue(&queue);
@@ -547,13 +592,13 @@ skipopts:
        }
 
        if (read_aliases() != 0)
-               errlog(1, "can not read aliases file `%s'", config.aliases);
+               errlog(1, "could not parse aliases file `%s'", config.aliases);
 
        if ((sender = set_from(&queue, sender)) == NULL)
                errlog(1, NULL);
 
        if (newspoolf(&queue) != 0)
-               errlog(1, "can not create temp file");
+               errlog(1, "can not create temp file in `%s'", config.spooldir);
 
        setlogident("%s", queue.id);
 
index 56fa104..1cc2bf5 100644 (file)
@@ -61,3 +61,6 @@
 # MASQUERADE john@  on host "hamlet" will send all mails as john@hamlet
 # MASQUERADE percolator  will send mails as $username@percolator, e.g. fish@percolator
 # MASQUERADE herb@ert  will send all mails as herb@ert
+
+# Directly forward the mail to the SMARTHOST bypassing aliases and local delivery
+#NULLCLIENT
index d413ca5..baacb0e 100644 (file)
@@ -52,6 +52,7 @@
 #define MIN_RETRY      300             /* 5 minutes */
 #define MAX_RETRY      (3*60*60)       /* retry at least every 3 hours */
 #define MAX_TIMEOUT    (5*24*60*60)    /* give up after 5 days */
+#define SLEEP_TIMEOUT  30              /* check for queue flush every 30 seconds */
 #ifndef PATH_MAX
 #define PATH_MAX       1024            /* Max path len */
 #endif
@@ -65,6 +66,7 @@
 #define INSECURE       0x020           /* Allow plain login w/o encryption */
 #define FULLBOUNCE     0x040           /* Bounce the full message */
 #define TLS_OPP                0x080           /* Opportunistic STARTTLS */
+#define NULLCLIENT     0x100           /* Nullclient support */
 
 #ifndef CONF_PATH
 #error Please define CONF_PATH
 #error Please define LIBEXEC_PATH
 #endif
 
+#define SPOOL_FLUSHFILE        "flush"
+
+#ifndef DMA_ROOT_USER
 #define DMA_ROOT_USER  "mail"
+#endif
+#ifndef DMA_GROUP
 #define DMA_GROUP      "mail"
+#endif
 
 #ifndef MBOX_STRICT
 #define MBOX_STRICT    0
@@ -182,7 +190,7 @@ int dns_get_mx_list(const char *, int, struct mx_hostentry **, int);
 /* net.c */
 char *ssl_errstr(void);
 int read_remote(int, int, char *);
-ssize_t send_remote_command(int, const char*, ...) __printflike(2, 3);
+ssize_t send_remote_command(int, const char*, ...)  __attribute__((__nonnull__(2), __format__ (__printf__, 2, 3)));
 int deliver_remote(struct qitem *);
 
 /* base64.c */
@@ -202,6 +210,8 @@ int load_queue(struct queue *);
 void delqueue(struct qitem *);
 int acquirespool(struct qitem *);
 void dropspool(struct queue *, struct qitem *);
+int flushqueue_since(unsigned int);
+int flushqueue_signal(void);
 
 /* local.c */
 int deliver_local(struct qitem *);
@@ -212,9 +222,9 @@ int readmail(struct queue *, int, int);
 
 /* util.c */
 const char *hostname(void);
-void setlogident(const char *, ...) __printf0like(1, 2);
-void errlog(int, const char *, ...) __printf0like(2, 3);
-void errlogx(int, const char *, ...) __printf0like(2, 3);
+void setlogident(const char *, ...) __attribute__((__format__ (__printf__, 1, 2)));
+void errlog(int, const char *, ...) __attribute__((__format__ (__printf__, 2, 3)));
+void errlogx(int, const char *, ...) __attribute__((__format__ (__printf__, 2, 3)));
 void set_username(void);
 void deltmp(void);
 int do_timeout(int, int);
index 2d89c53..ecd9c57 100644 (file)
@@ -1,9 +1,11 @@
+version!=       sh get-version.sh
+
 .PATH: ${.CURDIR}/..
 
 CFLAGS+=-I${.CURDIR}/..
 CFLAGS+= -DHAVE_REALLOCF -DHAVE_STRLCPY -DHAVE_GETPROGNAME
 CFLAGS+=-DCONF_PATH='"/etc/dma"'
-CFLAGS+=-DLIBEXEC_PATH='"/usr/libexec"' -DDMA_VERSION='"v0.7"'
+CFLAGS+=-DLIBEXEC_PATH='"/usr/libexec"' -DDMA_VERSION='"${version}"'
 
 DPADD=  ${LIBSSL} ${LIBCRYPTO}
 LDADD=  -lssl -lcrypto
diff --git a/libexec/dma/get-version.sh b/libexec/dma/get-version.sh
new file mode 100644 (file)
index 0000000..aecc333
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+gitver=$(git describe 2>/dev/null | tr - .)
+filever=$(cat VERSION)
+
+version=${gitver}
+: ${version:=$filever}
+
+echo "$version"
index 985c539..d95a788 100644 (file)
@@ -256,7 +256,7 @@ smtp_login(int fd, char *login, char* password)
                return (0);
        } else if (res == -2) {
        /*
-        * If the return code is -2, then then the login attempt failed, 
+        * If the return code is -2, then then the login attempt failed,
         * do not try other login mechanisms
         */
                return (1);
@@ -493,17 +493,6 @@ deliver_remote(struct qitem *it)
        int port;
        int error = 1, smarthost = 0;
 
-       host = strrchr(it->addr, '@');
-       /* Should not happen */
-       if (host == NULL) {
-               snprintf(errmsg, sizeof(errmsg), "Internal error: badly formed address %s",
-                   it->addr);
-               return(-1);
-       } else {
-               /* Step over the @ */
-               host++;
-       }
-
        port = SMTP_PORT;
 
        /* Smarthost support? */
@@ -512,11 +501,23 @@ deliver_remote(struct qitem *it)
                port = config.port;
                syslog(LOG_INFO, "using smarthost (%s:%i)", host, port);
                smarthost = 1;
+       } else {
+               host = strrchr(it->addr, '@');
+               /* Should not happen */
+               if (host == NULL) {
+                       snprintf(errmsg, sizeof(errmsg), "Internal error: badly formed address %s",
+                                it->addr);
+                       return(-1);
+               } else {
+                       /* Step over the @ */
+                       host++;
+               }
        }
 
        error = dns_get_mx_list(host, port, &hosts, smarthost);
        if (error) {
-               syslog(LOG_NOTICE, "remote delivery %s: DNS failure (%s)",
+               snprintf(errmsg, sizeof(errmsg), "DNS lookup failure: host %s not found", host);
+               syslog(LOG_NOTICE, "remote delivery %s: DNS lookup failure: host %s not found",
                       error < 0 ? "failed" : "deferred",
                       host);
                return (error);
index 9c4d79e..416b5fa 100644 (file)
@@ -374,6 +374,8 @@ acquirespool(struct qitem *it)
        return (0);
 
 fail:
+       if (errno == EWOULDBLOCK)
+               return (1);
        syslog(LOG_INFO, "could not acquire queue file: %m");
        return (-1);
 }
@@ -393,3 +395,46 @@ dropspool(struct queue *queue, struct qitem *keep)
                        fclose(it->mailf);
        }
 }
+
+int
+flushqueue_since(unsigned int period)
+{
+        struct stat st;
+       struct timeval now;
+        char *flushfn = NULL;
+
+       if (asprintf(&flushfn, "%s/%s", config.spooldir, SPOOL_FLUSHFILE) < 0)
+               return (0);
+       if (stat(flushfn, &st) < 0) {
+               free(flushfn);
+               return (0);
+       }
+       free(flushfn);
+       flushfn = NULL;
+       if (gettimeofday(&now, 0) != 0)
+               return (0);
+
+       /* Did the flush file get touched within the last period seconds? */
+       if (st.st_mtim.tv_sec + period >= now.tv_sec)
+               return (1);
+       else
+               return (0);
+}
+
+int
+flushqueue_signal(void)
+{
+        char *flushfn = NULL;
+       int fd;
+
+        if (asprintf(&flushfn, "%s/%s", config.spooldir, SPOOL_FLUSHFILE) < 0)
+               return (-1);
+       fd = open(flushfn, O_CREAT|O_WRONLY|O_TRUNC, 0660);
+       free(flushfn);
+       if (fd < 0) {
+               syslog(LOG_ERR, "could not open flush file: %m");
+               return (-1);
+       }
+        close(fd);
+       return (0);
+}