--- /dev/null
+# $DragonFly: src/libexec/dma/Makefile,v 1.1 2008/02/02 18:20:51 matthias Exp $
+#
+
+CFLAGS+= -DHAVE_CRYPTO -DHAVE_INET6
+
+DPADD= ${LIBSSL} ${LIBCRYPTO}
+LDADD= -lssl -lcrypto
+
+PROG= dma
+SRCS= base64.c conf.c crypto.c net.c dma.c aliases_scan.l aliases_parse.y
+MAN= dma.8
+
+BINOWN= root
+BINGRP= mail
+BINMODE=2555
+WARNS?= 1
+
+.include <bsd.prog.mk>
--- /dev/null
+%{
+/* $DragonFly: src/libexec/dma/aliases_parse.y,v 1.1 2008/02/02 18:20:51 matthias Exp $ */
+
+#include <err.h>
+#include <string.h>
+#include "dma.h"
+
+extern int yylineno;
+void yyerror(const char *);
+int yywrap(void);
+
+void
+yyerror(const char *msg)
+{
+ warnx("aliases line %d: %s", yylineno, msg);
+}
+
+int
+yywrap(void)
+{
+ return (1);
+}
+
+%}
+
+%union {
+ char *ident;
+ struct stritem *strit;
+ struct alias *alias;
+}
+
+%token <ident> T_IDENT
+%token T_ERROR
+%token T_EOF 0
+
+%type <strit> dests
+%type <alias> alias aliases
+
+%%
+
+start : aliases T_EOF
+ {
+ LIST_FIRST(&aliases) = $1;
+ }
+
+aliases : /* EMPTY */
+ {
+ $$ = NULL;
+ }
+ | alias aliases
+ {
+ if ($2 != NULL && $1 != NULL)
+ LIST_INSERT_AFTER($2, $1, next);
+ else if ($2 == NULL)
+ $2 = $1;
+ $$ = $2;
+ }
+ ;
+
+alias : T_IDENT ':' dests '\n'
+ {
+ struct alias *al;
+
+ if ($1 == NULL)
+ YYABORT;
+ al = calloc(1, sizeof(*al));
+ if (al == NULL)
+ YYABORT;
+ al->alias = $1;
+ SLIST_FIRST(&al->dests) = $3;
+ $$ = al;
+ }
+ | error '\n'
+ {
+ yyerrok;
+ $$ = NULL;
+ }
+ ;
+
+dests : T_IDENT
+ {
+ struct stritem *it;
+
+ if ($1 == NULL)
+ YYABORT;
+ it = calloc(1, sizeof(*it));
+ if (it == NULL)
+ YYABORT;
+ it->str = $1;
+ $$ = it;
+ }
+ | T_IDENT ',' dests
+ {
+ struct stritem *it;
+
+ if ($1 == NULL)
+ YYABORT;
+ it = calloc(1, sizeof(*it));
+ if (it == NULL)
+ YYABORT;
+ it->str = $1;
+ SLIST_NEXT(it, next) = $3;
+ $$ = it;
+ }
+ ;
+
+%%
--- /dev/null
+%{
+/* $DragonFly: src/libexec/dma/aliases_scan.l,v 1.1 2008/02/02 18:20:51 matthias Exp $ */
+
+#include <string.h>
+#include "aliases_parse.h"
+%}
+
+%option yylineno
+
+%%
+
+[^:,#[:space:][:cntrl:]]+ {yylval.ident = strdup(yytext); return T_IDENT;}
+[:,\n] return yytext[0];
+^([[:blank:]]*(#.*)?\n)+ ;/* ignore empty lines */
+(\n?[[:blank:]]+|#.*)+ ;/* ignore whitespace and continuation */
+\\\n ;/* ignore continuation. not allowed in comments */
+. return T_ERROR;
+<<EOF>> return T_EOF;
+
+%%
--- /dev/null
+/*
+ * Copyright (c) 1995-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly: src/libexec/dma/base64.c,v 1.1 2008/02/02 18:20:51 matthias Exp $
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+int base64_encode(const void *data, int size, char **str);
+
+static char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int
+base64_encode(const void *data, int size, char **str)
+{
+ char *s, *p;
+ int i;
+ int c;
+ const unsigned char *q;
+
+ p = s = (char *) malloc(size * 4 / 3 + 4);
+ if (p == NULL)
+ return -1;
+ q = (const unsigned char *) data;
+ i = 0;
+ for (i = 0; i < size;) {
+ c = q[i++];
+ c *= 256;
+ if (i < size)
+ c += q[i];
+ i++;
+ c *= 256;
+ if (i < size)
+ c += q[i];
+ i++;
+ p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+ p[1] = base64_chars[(c & 0x0003f000) >> 12];
+ p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+ p[3] = base64_chars[(c & 0x0000003f) >> 0];
+ if (i > size)
+ p[3] = '=';
+ if (i > size + 1)
+ p[2] = '=';
+ p += 4;
+ }
+ *p = 0;
+ *str = s;
+ return strlen(s);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2008 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
+ * Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly: src/libexec/dma/conf.c,v 1.1 2008/02/02 18:20:51 matthias Exp $
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+#include "dma.h"
+
+#define DP ": \t\n"
+#define EQS " \t\n"
+
+extern struct virtusers virtusers;
+extern struct authusers authusers;
+
+/*
+ * Remove trailing \n's
+ */
+void
+trim_line(char *line)
+{
+ size_t linelen;
+ char *p;
+
+ p = line;
+
+ if ((p = strchr(line, '\n')))
+ *p = (char)0;
+
+ /* Escape leading dot in every case */
+ linelen = strlen(line);
+ if (line[0] == '.') {
+ if ((linelen + 2) > 1000) {
+ syslog(LOG_CRIT, "Cannot escape leading dot. Buffer overflow");
+ exit(1);
+ }
+ memmove((line + 1), line, (linelen + 1));
+ line[0] = '.';
+ }
+}
+
+/*
+ * Add a virtual user entry to the list of virtual users
+ */
+static void
+add_virtuser(char *login, char *address)
+{
+ struct virtuser *v;
+
+ v = malloc(sizeof(struct virtuser));
+ v->login = strdup(login);
+ v->address = strdup(address);
+ SLIST_INSERT_HEAD(&virtusers, v, next);
+}
+
+/*
+ * Read the virtual user table
+ */
+int
+parse_virtuser(const char *path)
+{
+ FILE *v;
+ char *word;
+ char *data;
+ char line[2048];
+
+ v = fopen(path, "r");
+ if (v == NULL)
+ return (-1);
+
+ while (!feof(v)) {
+ fgets(line, sizeof(line), v);
+ /* We hit a comment */
+ if (strchr(line, '#'))
+ *strchr(line, '#') = 0;
+ if ((word = strtok(line, DP)) != NULL) {
+ data = strtok(NULL, DP);
+ if (data != NULL) {
+ add_virtuser(word, data);
+ }
+ }
+ }
+
+ fclose(v);
+ return (0);
+}
+
+/*
+ * Add entry to the SMTP auth user list
+ */
+static void
+add_smtp_auth_user(char *userstring, char *password)
+{
+ struct authuser *a;
+ char *temp;
+
+ a = malloc(sizeof(struct virtuser));
+ a->password= strdup(password);
+
+ temp = strrchr(userstring, '|');
+ if (temp == NULL)
+ errx(1, "auth.conf file in wrong format");
+
+ a->host = strdup(temp+1);
+ a->login = strdup(strtok(userstring, "|"));
+ if (a->login == NULL)
+ errx(1, "auth.conf file in wrong format");
+
+ SLIST_INSERT_HEAD(&authusers, a, next);
+}
+
+/*
+ * Read the SMTP authentication config file
+ */
+int
+parse_authfile(const char *path)
+{
+ FILE *a;
+ char *word;
+ char *data;
+ char line[2048];
+
+ a = fopen(path, "r");
+ if (a == NULL)
+ return (1);
+
+ while (!feof(a)) {
+ fgets(line, sizeof(line), a);
+ /* We hit a comment */
+ if (strchr(line, '#'))
+ *strchr(line, '#') = 0;
+ if ((word = strtok(line, DP)) != NULL) {
+ data = strtok(NULL, DP);
+ if (data != NULL) {
+ add_smtp_auth_user(word, data);
+ }
+ }
+ }
+
+ fclose(a);
+ return (0);
+}
+
+/*
+ * XXX TODO
+ * Check if the user supplied a value. If not, fill in default
+ * Check for bad things[TM]
+ */
+int
+parse_conf(const char *config_path, struct config *config)
+{
+ char *word;
+ char *data;
+ FILE *conf;
+ char line[2048];
+
+ conf = fopen(config_path, "r");
+ if (conf == NULL)
+ return (-1);
+
+ /* Reset features */
+ config->features = 0;
+
+ while (!feof(conf)) {
+ fgets(line, sizeof(line), conf);
+ /* We hit a comment */
+ if (strchr(line, '#'))
+ *strchr(line, '#') = 0;
+ if ((word = strtok(line, EQS)) != NULL) {
+ data = strtok(NULL, EQS);
+ if (strcmp(word, "SMARTHOST") == 0) {
+ if (data != NULL)
+ config->smarthost = strdup(data);
+ }
+ else if (strcmp(word, "PORT") == 0) {
+ if (data != NULL)
+ config->port = atoi(strdup(data));
+ }
+ else if (strcmp(word, "ALIASES") == 0) {
+ if (data != NULL)
+ config->aliases = strdup(data);
+ }
+ else if (strcmp(word, "SPOOLDIR") == 0) {
+ if (data != NULL)
+ config->spooldir = strdup(data);
+ }
+ else if (strcmp(word, "VIRTPATH") == 0) {
+ if (data != NULL)
+ config->virtualpath = strdup(data);
+ }
+ else if (strcmp(word, "AUTHPATH") == 0) {
+ if (data != NULL)
+ config->authpath= strdup(data);
+ }
+ else if (strcmp(word, "CERTFILE") == 0) {
+ if (data != NULL)
+ config->certfile = strdup(data);
+ }
+ else if (strcmp(word, "VIRTUAL") == 0)
+ config->features |= VIRTUAL;
+ else if (strcmp(word, "STARTTLS") == 0)
+ config->features |= STARTTLS;
+ else if (strcmp(word, "SECURETRANSFER") == 0)
+ config->features |= SECURETRANS;
+ else if (strcmp(word, "DEFER") == 0)
+ config->features |= DEFER;
+ }
+ }
+
+ fclose(conf);
+ return (0);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2008 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
+ * Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly: src/libexec/dma/crypto.c,v 1.1 2008/02/02 18:20:51 matthias Exp $
+ */
+
+#ifdef HAVE_CRYPTO
+
+#include <openssl/x509.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+
+#include <syslog.h>
+
+#include "dma.h"
+
+extern struct config *config;
+
+static int
+init_cert_file(struct qitem *it, SSL_CTX *ctx, const char *path)
+{
+ int error;
+
+ /* Load certificate into ctx */
+ error = SSL_CTX_use_certificate_chain_file(ctx, path);
+ if (error < 1) {
+ syslog(LOG_ERR, "%s: SSL: Cannot load certificate: %s",
+ it->queueid, path);
+ return (-1);
+ }
+
+ /* Add private key to ctx */
+ error = SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM);
+ if (error < 1) {
+ syslog(LOG_ERR, "%s: SSL: Cannot load private key: %s",
+ it->queueid, path);
+ return (-1);
+ }
+
+ /*
+ * Check the consistency of a private key with the corresponding
+ * certificate
+ */
+ error = SSL_CTX_check_private_key(ctx);
+ if (error < 1) {
+ syslog(LOG_ERR, "%s: SSL: Cannot check private key: %s",
+ it->queueid, path);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+smtp_init_crypto(struct qitem *it, int fd, int feature)
+{
+ SSL_CTX *ctx = NULL;
+ SSL_METHOD *meth = NULL;
+ X509 *cert;
+ char buf[2048];
+ int error;
+
+ /* Init SSL library */
+ SSL_library_init();
+
+ meth = TLSv1_client_method();
+
+ ctx = SSL_CTX_new(meth);
+ if (ctx == NULL) {
+ syslog(LOG_ERR, "%s: remote delivery deferred:"
+ " SSL init failed: %m", it->queueid);
+ return (2);
+ }
+
+ /* User supplied a certificate */
+ if (config->certfile != NULL)
+ init_cert_file(it, ctx, config->certfile);
+
+ /*
+ * If the user wants STARTTLS, we have to send EHLO here
+ */
+ if (((feature & SECURETRANS) != 0) &&
+ (feature & STARTTLS) != 0) {
+ /* TLS init phase, disable SSL_write */
+ config->features |= TLSINIT;
+
+ send_remote_command(fd, "EHLO %s", hostname());
+ if (check_for_smtp_error(fd, buf) == 0) {
+ send_remote_command(fd, "STARTTLS");
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery failed:"
+ " STARTTLS not available: %m", it->queueid);
+ config->features &= ~TLSINIT;
+ return (-1);
+ }
+ }
+ /* End of TLS init phase, enable SSL_write/read */
+ config->features &= ~TLSINIT;
+ }
+
+ config->ssl = SSL_new(ctx);
+ if (config->ssl == NULL) {
+ syslog(LOG_ERR, "%s: remote delivery deferred:"
+ " SSL struct creation failed:", it->queueid);
+ return (2);
+ }
+
+ /* Set ssl to work in client mode */
+ SSL_set_connect_state(config->ssl);
+
+ /* Set fd for SSL in/output */
+ error = SSL_set_fd(config->ssl, fd);
+ if (error == 0) {
+ error = SSL_get_error(config->ssl, error);
+ syslog(LOG_ERR, "%s: remote delivery deferred:"
+ " SSL set fd failed (%d): %m", it->queueid, error);
+ return (2);
+ }
+
+ /* Open SSL connection */
+ error = SSL_connect(config->ssl);
+ if (error < 0) {
+ syslog(LOG_ERR, "%s: remote delivery failed:"
+ " SSL handshake fataly failed: %m", it->queueid);
+ return (-1);
+ }
+
+ /* Get peer certificate */
+ cert = SSL_get_peer_certificate(config->ssl);
+ if (cert == NULL) {
+ syslog(LOG_ERR, "%s: remote delivery deferred:"
+ " Peer did not provied certificate: %m", it->queueid);
+ }
+ X509_free(cert);
+
+ return (0);
+}
+
+#if 0
+/*
+ * CRAM-MD5 authentication
+ *
+ * XXX TODO implement me, I don't have a mail server with CRAM-MD5 available
+ */
+int
+smtp_auth_md5(int fd, char *login, char *password)
+{
+}
+#endif /* 0 */
+
+#endif /* HAVE_CRYPTO */
--- /dev/null
+.\"
+.\" Copyright (c) 2008
+.\" The DragonFly Project. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\" 3. Neither the name of The DragonFly Project nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific, prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $DragonFly: src/libexec/dma/dma.8,v 1.1 2008/02/02 18:20:51 matthias Exp $
+.\"
+.Dd February 02, 2008
+.Dt DMA 8
+.Os
+.Sh NAME
+.Nm dma
+.Nd DragonFly Mail Agent
+.Sh SYNOPSIS
+.Nm
+.Op Fl Diq
+.Op Fl A Ar mode
+.Op Fl b Ar mode
+.Op Fl f Ar sender
+.Op Fl L Ar tag
+.Op Fl o Ar option
+.Op Fl r Ar sender
+.Op Ar recipient ...
+.Sh DESCRIPTION
+.Nm
+is a small Mail Transport Agent (MTA), designed for home and office use.
+It accepts mails from locally installed Mail User Agents (MUA) and
+delivers the mails either locally or to a remote destination.
+Remote delivery includes several features like TLS/SSL support and SMTP
+authentication (AUTH LOGIN only).
+.Pp
+.Nm
+is not intended as a replacement for real, big MTAs like
+.Xr sendmail 8
+or
+.Xr postfix 8 .
+Consequently,
+.Nm
+does not listen on port 25 for incoming connections.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl A Ar mode
+.Fl A Ns Ar c
+acts as a compatibility option for sendmail.
+.It Fl b Ar mode
+Specifying
+.Fl b Ns Ar p
+will list all mails currently stored in the mail queue.
+All other modes are are ignored.
+.It Fl D
+Don't run in the background.
+Useful for debugging.
+.It Fl f Ar sender
+Set sender address to
+.Ar sender .
+.It Fl i
+Ignore dots alone on lines by themselves in incoming messages.
+This should be set if you are reading data from a file.
+.It Fl L Ar tag
+Set the identifier used in syslog messages to the supplied
+.Ar tag .
+This is a compatibility option for sendmail.
+.It Fl o Ar option
+Specifying
+.Fl o Ns Ar i
+is synonymous to
+.Fl i .
+All other options are ignored.
+.It Fl q
+Process saved messages in the queue.
+.It Fl r Ar sender
+Same as
+.Fl f .
+.El
+.Sh CONFIGURATION
+.Nm
+can be configured with three config files:
+.Pp
+.Bl -bullet -compact
+.It
+auth.conf
+.It
+dma.conf
+.It
+virtusertable
+.El
+.Pp
+These three files are stored per default in
+.Pa /etc/dma .
+However every user can install it's own config files in
+.Pa $HOME/.dma .
+.Sh FILE FORMAT
+Every file contains parameters of the form
+.Sq name value .
+Lines containing boolean values are set to
+.Sq NO
+if the line is commented and to
+.Sq YES
+if the line is uncommented.
+Empty lines or lines beginning with a
+.Sq #
+are ignored.
+Parameter names and their values are case sensitive.
+.Sh PARAMETERS
+.Ss auth.conf
+SMTP authentication can be configured in
+.Pa auth.conf .
+Each line has the format
+.Dq Li user|smarthost:password .
+.Ss dma.conf
+Most of the behaviour of
+.Nm
+can be configured in
+.Pa dma.conf .
+.Pp
+.Bl -tag -width 4n
+.It Ic SMARTHOST Xo
+(string, default=empty)
+.Xc
+If you want to send outgoing mails via a smarthost, set this variable to
+your smarthosts address.
+.It Ic PORT Xo
+(numeric, default=25)
+.Xc
+Use this port to deliver remote emails.
+Only useful together with the
+.Sq SMARTHOST
+option, because
+.Nm
+will deliver all mails to this port, regardless if a smarthost is set or not.
+.It Ic ALIASES Xo
+(string, default=/etc/mail/aliases)
+.Xc
+Path to the local aliases file.
+Just stick with the default.
+.It Ic SPOOLDIR Xo
+(string, default=/var/spool/dma)
+.Xc
+Path to
+.Nm Ap s
+spool directory.
+Just stick with the default.
+.It Ic VIRTPATH Xo
+(string, default=/etc/dma/virtusertable)
+.Xc
+Path to the
+.Sq virtusertable
+file.
+If you have your config in
+.Pa $HOME/.dma
+be sure to change this path accordingly.
+.It Ic AUTHPATH Xo
+(string, default=/etc/dma/auth.conf)
+.Xc
+Path to the
+.Sq auth.conf
+file.
+If you have your config in
+.Pa $HOME/.dma
+be sure to change this path accordingly.
+.It Ic VIRTUAL Xo
+(boolean, default=commented)
+.Xc
+Comment if you want virtual user support.
+.It Ic SECURETRANS Xo
+(boolean, default=commented)
+.Xc
+Comment if you want TLS/SSL secured transfer.
+.It Ic STARTTLS Xo
+(boolean, default=commented)
+.Xc
+Comment if you want to use STARTTLS.
+Only useful together with
+.Sq SECURETRANS .
+.It Ic CERTFILE Xo
+(string, default=empty)
+.Xc
+Path to your SSL certificate file.
+.It Ic DEFER Xo
+(boolean, default=commented)
+.Xc
+Comment if you want that
+.Nm
+defers your mail.
+You have to flush your mail queue manually with the
+.Fl q
+option.
+This option is handy if you are behind a dialup line.
+.El
+.Ss virtusertable
+The
+.Pa virtusertable
+file specifies a virtual user table.
+Each line has the format
+.Dq Li localuser:mail-address .
+Some smarthosts do not accept mails from unresolvable email address
+(e.g. user@localhost) so you have to rewrite your outgoing email
+address to a valid address.
+.Sh SEE ALSO
+.Xr mailaddr 7 ,
+.Xr mailwrapper 8 ,
+.Xr sendmail 8
+.Rs
+.%A "J. B. Postel"
+.%T "Simple Mail Transfer Protocol"
+.%O RFC 821
+.Re
+.Rs
+.%A "J. Myers"
+.%T "SMTP Service Extension for Authentication"
+.%O RFC 2554
+.Re
+.Rs
+.%A "P. Hoffman"
+.%T "SMTP Service Extension for Secure SMTP over TLS"
+.%O RFC 2487
+.Re
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Dx 1.11 .
+.Sh AUTHORS
+.Nm
+was written by
+.An Matthias Schmidt Aq matthias@dragonflybsd.org
+and
+.An Simon Schubert Aq corecode@dragonflybsd.org .
--- /dev/null
+/*
+ * Copyright (c) 2008 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly: src/libexec/dma/dma.c,v 1.1 2008/02/02 18:20:51 matthias Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef HAVE_CRYPTO
+#include <openssl/ssl.h>
+#endif /* HAVE_CRYPTO */
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "dma.h"
+
+
+
+static void deliver(struct qitem *);
+static int add_recp(struct queue *, const char *, const char *, int);
+
+struct aliases aliases = LIST_HEAD_INITIALIZER(aliases);
+static struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs);
+struct virtusers virtusers = LIST_HEAD_INITIALIZER(virtusers);
+struct authusers authusers = LIST_HEAD_INITIALIZER(authusers);
+static int daemonize = 1;
+struct config *config;
+
+char *
+hostname(void)
+{
+ static char name[MAXHOSTNAMELEN+1];
+
+ if (gethostname(name, sizeof(name)) != 0)
+ strcpy(name, "(unknown hostname)");
+
+ return name;
+}
+
+static char *
+set_from(const char *osender)
+{
+ struct virtuser *v;
+ char *sender;
+
+ if ((config->features & VIRTUAL) != 0) {
+ SLIST_FOREACH(v, &virtusers, next) {
+ if (strcmp(v->login, getlogin()) == 0) {
+ sender = strdup(v->address);
+ if (sender == NULL)
+ return(NULL);
+ goto out;
+ }
+ }
+ }
+
+ if (osender) {
+ sender = strdup(osender);
+ if (sender == NULL)
+ return (NULL);
+ } else {
+ if (asprintf(&sender, "%s@%s", getlogin(), hostname()) <= 0)
+ return (NULL);
+ }
+
+ if (strchr(sender, '\n') != NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+out:
+ return (sender);
+}
+
+static int
+read_aliases(void)
+{
+ yyin = fopen(config->aliases, "r");
+ if (yyin == NULL)
+ return (0); /* not fatal */
+ if (yyparse())
+ return (-1); /* fatal error, probably malloc() */
+ fclose(yyin);
+ return (0);
+}
+
+static int
+add_recp(struct queue *queue, const char *str, const char *sender, int expand)
+{
+ struct qitem *it, *tit;
+ struct stritem *sit;
+ struct alias *al;
+ struct passwd *pw;
+ char *host;
+ int aliased = 0;
+
+ it = calloc(1, sizeof(*it));
+ if (it == NULL)
+ return (-1);
+ it->addr = strdup(str);
+ if (it->addr == NULL)
+ return (-1);
+
+ it->sender = sender;
+ host = strrchr(it->addr, '@');
+ if (host != NULL &&
+ (strcmp(host + 1, hostname()) == 0 ||
+ strcmp(host + 1, "localhost") == 0)) {
+ *host = 0;
+ }
+ LIST_FOREACH(tit, &queue->queue, next) {
+ /* weed out duplicate dests */
+ if (strcmp(tit->addr, it->addr) == 0) {
+ free(it->addr);
+ free(it);
+ return (0);
+ }
+ }
+ LIST_INSERT_HEAD(&queue->queue, it, next);
+ if (strrchr(it->addr, '@') == NULL) {
+ it->remote = 0;
+ if (expand) {
+ LIST_FOREACH(al, &aliases, next) {
+ if (strcmp(al->alias, it->addr) != 0)
+ continue;
+ SLIST_FOREACH(sit, &al->dests, next) {
+ if (add_recp(queue, sit->str, sender, 1) != 0)
+ return (-1);
+ }
+ aliased = 1;
+ }
+ if (aliased) {
+ LIST_REMOVE(it, next);
+ } else {
+ /* Local destination, check */
+ pw = getpwnam(it->addr);
+ if (pw == NULL)
+ goto out;
+ endpwent();
+ }
+ }
+ } else {
+ it->remote = 1;
+ }
+
+ return (0);
+
+out:
+ free(it->addr);
+ free(it);
+ return (-1);
+}
+
+static void
+deltmp(void)
+{
+ struct stritem *t;
+
+ SLIST_FOREACH(t, &tmpfs, next) {
+ unlink(t->str);
+ }
+}
+
+static int
+gentempf(struct queue *queue)
+{
+ char fn[PATH_MAX+1];
+ struct stritem *t;
+ int fd;
+
+ if (snprintf(fn, sizeof(fn), "%s/%s", config->spooldir, "tmp_XXXXXXXXXX") <= 0)
+ return (-1);
+ fd = mkstemp(fn);
+ if (fd < 0)
+ return (-1);
+ queue->mailfd = fd;
+ queue->tmpf = strdup(fn);
+ if (queue->tmpf == NULL) {
+ unlink(fn);
+ return (-1);
+ }
+ t = malloc(sizeof(*t));
+ if (t != NULL) {
+ t->str = queue->tmpf;
+ SLIST_INSERT_HEAD(&tmpfs, t, next);
+ }
+ return (0);
+}
+
+/*
+ * spool file format:
+ *
+ * envelope-from
+ * queue-id1 envelope-to1
+ * queue-id2 envelope-to2
+ * ...
+ * <empty line>
+ * mail data
+ *
+ * queue ids are unique, formed from the inode of the spool file
+ * and a unique identifier.
+ */
+static int
+preparespool(struct queue *queue, const char *sender)
+{
+ char line[1000]; /* by RFC2822 */
+ struct stat st;
+ int error;
+ struct qitem *it;
+ FILE *queuef;
+ off_t hdrlen;
+
+ error = snprintf(line, sizeof(line), "%s\n", sender);
+ if (error < 0 || (size_t)error >= sizeof(line)) {
+ errno = E2BIG;
+ return (-1);
+ }
+ if (write(queue->mailfd, line, error) != error)
+ return (-1);
+
+ queuef = fdopen(queue->mailfd, "r+");
+ if (queuef == NULL)
+ return (-1);
+
+ /*
+ * Assign queue id to each dest.
+ */
+ if (fstat(queue->mailfd, &st) != 0)
+ return (-1);
+ queue->id = st.st_ino;
+ LIST_FOREACH(it, &queue->queue, next) {
+ if (asprintf(&it->queueid, "%"PRIxMAX".%"PRIxPTR,
+ queue->id, (uintptr_t)it) <= 0)
+ return (-1);
+ if (asprintf(&it->queuefn, "%s/%s",
+ config->spooldir, it->queueid) <= 0)
+ return (-1);
+ /* File may not exist yet */
+ if (stat(it->queuefn, &st) == 0)
+ return (-1);
+ it->queuef = queuef;
+ error = snprintf(line, sizeof(line), "%s %s\n",
+ it->queueid, it->addr);
+ if (error < 0 || (size_t)error >= sizeof(line))
+ return (-1);
+ if (write(queue->mailfd, line, error) != error)
+ return (-1);
+ }
+ line[0] = '\n';
+ if (write(queue->mailfd, line, 1) != 1)
+ return (-1);
+
+ hdrlen = lseek(queue->mailfd, 0, SEEK_CUR);
+ LIST_FOREACH(it, &queue->queue, next) {
+ it->hdrlen = hdrlen;
+ }
+ return (0);
+}
+
+static char *
+rfc822date(void)
+{
+ static char str[50];
+ size_t error;
+ time_t now;
+
+ now = time(NULL);
+ error = strftime(str, sizeof(str), "%a, %d %b %Y %T %z",
+ localtime(&now));
+ if (error == 0)
+ strcpy(str, "(date fail)");
+ return (str);
+}
+
+static int
+readmail(struct queue *queue, const char *sender, int nodot)
+{
+ char line[1000]; /* by RFC2822 */
+ size_t linelen;
+ int error;
+
+ error = snprintf(line, sizeof(line), "\
+Received: from %s (uid %d)\n\
+\t(envelope-from %s)\n\
+\tid %"PRIxMAX"\n\
+\tby %s (%s)\n\
+\t%s\n",
+ getlogin(), getuid(),
+ sender,
+ queue->id,
+ hostname(), VERSION,
+ rfc822date());
+ if (error < 0 || (size_t)error >= sizeof(line))
+ return (-1);
+ if (write(queue->mailfd, line, error) != error)
+ return (-1);
+
+ while (!feof(stdin)) {
+ if (fgets(line, sizeof(line), stdin) == NULL)
+ break;
+ linelen = strlen(line);
+ if (linelen == 0 || line[linelen - 1] != '\n') {
+ errno = EINVAL; /* XXX mark permanent errors */
+ return (-1);
+ }
+ if (!nodot && linelen == 2 && line[0] == '.')
+ break;
+ if ((size_t)write(queue->mailfd, line, linelen) != linelen)
+ return (-1);
+ }
+ if (fsync(queue->mailfd) != 0)
+ return (-1);
+ return (0);
+}
+
+static int
+linkspool(struct queue *queue)
+{
+ struct qitem *it;
+
+ LIST_FOREACH(it, &queue->queue, next) {
+ if (link(queue->tmpf, it->queuefn) != 0)
+ goto delfiles;
+ }
+ unlink(queue->tmpf);
+ return (0);
+
+delfiles:
+ LIST_FOREACH(it, &queue->queue, next) {
+ unlink(it->queuefn);
+ }
+ return (-1);
+}
+
+static struct qitem *
+go_background(struct queue *queue)
+{
+ struct sigaction sa;
+ struct qitem *it;
+ pid_t pid;
+
+ if (daemonize && daemon(0, 0) != 0) {
+ syslog(LOG_ERR, "can not daemonize: %m");
+ exit(1);
+ }
+ daemonize = 0;
+
+ bzero(&sa, sizeof(sa));
+ sa.sa_flags = SA_NOCLDWAIT;
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+
+ LIST_FOREACH(it, &queue->queue, next) {
+ /* No need to fork for the last dest */
+ if (LIST_NEXT(it, next) == NULL)
+ return (it);
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ syslog(LOG_ERR, "can not fork: %m");
+ exit(1);
+ break;
+
+ case 0:
+ /*
+ * Child:
+ *
+ * return and deliver mail
+ */
+ return (it);
+
+ default:
+ /*
+ * Parent:
+ *
+ * fork next child
+ */
+ break;
+ }
+ }
+
+ syslog(LOG_CRIT, "reached dead code");
+ exit(1);
+}
+
+static void
+bounce(struct qitem *it, const char *reason)
+{
+ struct queue bounceq;
+ struct qitem *bit;
+ char line[1000];
+ int error;
+
+ /* Don't bounce bounced mails */
+ if (it->sender[0] == 0) {
+ syslog(LOG_CRIT, "%s: delivery panic: can't bounce a bounce",
+ it->queueid);
+ exit(1);
+ }
+
+ syslog(LOG_ERR, "%s: delivery failed, bouncing",
+ it->queueid);
+
+ LIST_INIT(&bounceq.queue);
+ if (add_recp(&bounceq, it->sender, "", 1) != 0)
+ goto fail;
+ if (gentempf(&bounceq) != 0)
+ goto fail;
+ if (preparespool(&bounceq, "") != 0)
+ goto fail;
+
+ bit = LIST_FIRST(&bounceq.queue);
+ error = fprintf(bit->queuef, "\
+Received: from MAILER-DAEMON\n\
+\tid %"PRIxMAX"\n\
+\tby %s (%s)\n\
+\t%s\n\
+X-Original-To: <%s>\n\
+From: MAILER-DAEMON <>\n\
+To: %s\n\
+Subject: Mail delivery failed\n\
+Message-Id: <%"PRIxMAX"@%s>\n\
+Date: %s\n\
+\n\
+This is the %s at %s.\n\
+\n\
+There was an error delivering your mail to <%s>.\n\
+\n\
+%s\n\
+\n\
+Message headers follow.\n\
+\n\
+",
+ bounceq.id,
+ hostname(), VERSION,
+ rfc822date(),
+ it->addr,
+ it->sender,
+ bounceq.id, hostname(),
+ rfc822date(),
+ VERSION, hostname(),
+ it->addr,
+ reason);
+ if (error < 0)
+ goto fail;
+ if (fflush(bit->queuef) != 0)
+ goto fail;
+
+ if (fseek(it->queuef, it->hdrlen, SEEK_SET) != 0)
+ goto fail;
+ while (!feof(it->queuef)) {
+ if (fgets(line, sizeof(line), it->queuef) == NULL)
+ break;
+ if (line[0] == '\n')
+ break;
+ write(bounceq.mailfd, line, strlen(line));
+ }
+ if (fsync(bounceq.mailfd) != 0)
+ goto fail;
+ if (linkspool(&bounceq) != 0)
+ goto fail;
+ /* bounce is safe */
+
+ unlink(it->queuefn);
+ fclose(it->queuef);
+
+ bit = go_background(&bounceq);
+ deliver(bit);
+ /* NOTREACHED */
+
+fail:
+ syslog(LOG_CRIT, "%s: error creating bounce: %m", it->queueid);
+ unlink(it->queuefn);
+ exit(1);
+}
+
+static int
+deliver_local(struct qitem *it, const char **errmsg)
+{
+ char fn[PATH_MAX+1];
+ char line[1000];
+ size_t linelen;
+ int mbox;
+ int error;
+ off_t mboxlen;
+ time_t now = time(NULL);
+
+ error = snprintf(fn, sizeof(fn), "%s/%s", _PATH_MAILDIR, it->addr);
+ if (error < 0 || (size_t)error >= sizeof(fn)) {
+ syslog(LOG_ERR, "%s: local delivery deferred: %m",
+ it->queueid);
+ return (1);
+ }
+
+ /* mailx removes users mailspool file if empty, so open with O_CREAT */
+ mbox = open(fn, O_WRONLY | O_EXLOCK | O_APPEND | O_CREAT);
+ if (mbox < 0) {
+ syslog(LOG_ERR, "%s: local delivery deferred: can not open `%s': %m",
+ it->queueid, fn);
+ return (1);
+ }
+ mboxlen = lseek(mbox, 0, SEEK_CUR);
+
+ if (fseek(it->queuef, it->hdrlen, SEEK_SET) != 0) {
+ syslog(LOG_ERR, "%s: local delivery deferred: can not seek: %m",
+ it->queueid);
+ return (1);
+ }
+
+ error = snprintf(line, sizeof(line), "From %s\t%s", it->sender, ctime(&now));
+ if (error < 0 || (size_t)error >= sizeof(line)) {
+ syslog(LOG_ERR, "%s: local delivery deferred: can not write header: %m",
+ it->queueid);
+ return (1);
+ }
+ if (write(mbox, line, error) != error)
+ goto wrerror;
+
+ while (!feof(it->queuef)) {
+ if (fgets(line, sizeof(line), it->queuef) == NULL)
+ break;
+ linelen = strlen(line);
+ if (linelen == 0 || line[linelen - 1] != '\n') {
+ syslog(LOG_CRIT, "%s: local delivery failed: corrupted queue file",
+ it->queueid);
+ *errmsg = "corrupted queue file";
+ error = -1;
+ goto chop;
+ }
+
+ if (strncmp(line, "From ", 5) == 0) {
+ const char *gt = ">";
+
+ if (write(mbox, gt, 1) != 1)
+ goto wrerror;
+ }
+ if ((size_t)write(mbox, line, linelen) != linelen)
+ goto wrerror;
+ }
+ line[0] = '\n';
+ if (write(mbox, line, 1) != 1)
+ goto wrerror;
+ close(mbox);
+ return (0);
+
+wrerror:
+ syslog(LOG_ERR, "%s: local delivery failed: write error: %m",
+ it->queueid);
+ error = 1;
+chop:
+ if (ftruncate(mbox, mboxlen) != 0)
+ syslog(LOG_WARNING, "%s: error recovering mbox `%s': %m",
+ it->queueid, fn);
+ close(mbox);
+ return (error);
+}
+
+static void
+deliver(struct qitem *it)
+{
+ int error;
+ unsigned int backoff = MIN_RETRY;
+ const char *errmsg = "unknown bounce reason";
+ struct timeval now;
+ struct stat st;
+
+ syslog(LOG_INFO, "%s: mail from=<%s> to=<%s>",
+ it->queueid, it->sender, it->addr);
+
+retry:
+ syslog(LOG_INFO, "%s: trying delivery",
+ it->queueid);
+
+ if (it->remote)
+ error = deliver_remote(it, &errmsg);
+ else
+ error = deliver_local(it, &errmsg);
+
+ switch (error) {
+ case 0:
+ unlink(it->queuefn);
+ syslog(LOG_INFO, "%s: delivery successful",
+ it->queueid);
+ exit(0);
+
+ case 1:
+ if (stat(it->queuefn, &st) != 0) {
+ syslog(LOG_ERR, "%s: lost queue file `%s'",
+ it->queueid, it->queuefn);
+ exit(1);
+ }
+ if (gettimeofday(&now, NULL) == 0 &&
+ (now.tv_sec - st.st_mtimespec.tv_sec > MAX_TIMEOUT)) {
+ char *msg;
+
+ if (asprintf(&msg,
+ "Could not deliver for the last %d seconds. Giving up.",
+ MAX_TIMEOUT) > 0)
+ errmsg = msg;
+ goto bounce;
+ }
+ sleep(backoff);
+ backoff *= 2;
+ if (backoff > MAX_RETRY)
+ backoff = MAX_RETRY;
+ goto retry;
+
+ case -1:
+ default:
+ break;
+ }
+
+bounce:
+ bounce(it, errmsg);
+ /* NOTREACHED */
+}
+
+static void
+load_queue(struct queue *queue)
+{
+ struct stat st;
+ struct qitem *it;
+ //struct queue queue, itmqueue;
+ struct queue itmqueue;
+ DIR *spooldir;
+ struct dirent *de;
+ char line[1000];
+ char *fn;
+ FILE *queuef;
+ char *sender;
+ char *addr;
+ char *queueid;
+ char *queuefn;
+ off_t hdrlen;
+ int fd;
+
+ LIST_INIT(&queue->queue);
+
+ spooldir = opendir(config->spooldir);
+ if (spooldir == NULL)
+ err(1, "reading queue");
+
+ while ((de = readdir(spooldir)) != NULL) {
+ sender = NULL;
+ queuef = NULL;
+ queueid = NULL;
+ queuefn = NULL;
+ fn = NULL;
+ LIST_INIT(&itmqueue.queue);
+
+ /* ignore temp files */
+ if (strncmp(de->d_name, "tmp_", 4) == 0 ||
+ de->d_type != DT_REG)
+ continue;
+ if (asprintf(&queuefn, "%s/%s", config->spooldir, de->d_name) < 0)
+ goto fail;
+ fd = open(queuefn, O_RDONLY|O_EXLOCK|O_NONBLOCK);
+ if (fd < 0) {
+ /* Ignore locked files */
+ if (errno == EWOULDBLOCK)
+ continue;
+ goto skip_item;
+ }
+
+ queuef = fdopen(fd, "r");
+ if (queuef == NULL)
+ goto skip_item;
+ if (fgets(line, sizeof(line), queuef) == NULL ||
+ line[0] == 0)
+ goto skip_item;
+ line[strlen(line) - 1] = 0; /* chop newline */
+ sender = strdup(line);
+ if (sender == NULL)
+ goto skip_item;
+
+ for (;;) {
+ if (fgets(line, sizeof(line), queuef) == NULL ||
+ line[0] == 0)
+ goto skip_item;
+ if (line[0] == '\n')
+ break;
+ line[strlen(line) - 1] = 0;
+ queueid = strdup(line);
+ if (queueid == NULL)
+ goto skip_item;
+ addr = strchr(queueid, ' ');
+ if (addr == NULL)
+ goto skip_item;
+ *addr++ = 0;
+ if (fn != NULL)
+ free(fn);
+ if (asprintf(&fn, "%s/%s", config->spooldir, queueid) < 0)
+ goto skip_item;
+ /* Item has already been delivered? */
+ if (stat(fn, &st) != 0)
+ continue;
+ if (add_recp(&itmqueue, addr, sender, 0) != 0)
+ goto skip_item;
+ it = LIST_FIRST(&itmqueue.queue);
+ it->queuef = queuef;
+ it->queueid = queueid;
+ it->queuefn = fn;
+ fn = NULL;
+ }
+ if (LIST_EMPTY(&itmqueue.queue)) {
+ warnx("queue file without items: `%s'", queuefn);
+ goto skip_item2;
+ }
+ hdrlen = ftell(queuef);
+ while ((it = LIST_FIRST(&itmqueue.queue)) != NULL) {
+ it->hdrlen = hdrlen;
+ LIST_REMOVE(it, next);
+ LIST_INSERT_HEAD(&queue->queue, it, next);
+ }
+ continue;
+
+skip_item:
+ warn("reading queue: `%s'", queuefn);
+skip_item2:
+ if (sender != NULL)
+ free(sender);
+ if (queuefn != NULL)
+ free(queuefn);
+ if (fn != NULL)
+ free(fn);
+ if (queueid != NULL)
+ free(queueid);
+ close(fd);
+ }
+ closedir(spooldir);
+ return;
+
+fail:
+ err(1, "reading queue");
+}
+
+static void
+run_queue(struct queue *queue)
+{
+ struct qitem *it;
+ if (LIST_EMPTY(&queue->queue))
+ return;
+
+ it = go_background(queue);
+ deliver(it);
+ /* NOTREACHED */
+}
+
+static void
+show_queue(struct queue *queue)
+{
+ struct qitem *it;
+
+ if (LIST_EMPTY(&queue->queue)) {
+ printf("Mail queue is empty\n");
+ return;
+ }
+
+ LIST_FOREACH(it, &queue->queue, next) {
+ printf("\
+ID\t: %s\n\
+From\t: %s\n\
+To\t: %s\n--\n", it->queueid, it->sender, it->addr);
+ }
+}
+
+/*
+ * TODO:
+ *
+ * - alias processing
+ * - use group permissions
+ * - proper sysexit codes
+ */
+
+int
+main(int argc, char **argv)
+{
+ char *sender = NULL;
+ char tag[255];
+ char confpath[PATH_MAX];
+ struct qitem *it;
+ struct queue queue;
+ struct queue lqueue;
+ struct stat sb;
+ int i, ch;
+ int nodot = 0, doqueue = 0, showq = 0;
+
+ atexit(deltmp);
+ LIST_INIT(&queue.queue);
+ snprintf(tag, 254, "dma");
+
+ while ((ch = getopt(argc, argv, "A:b:Df:iL:o:qr:")) != -1) {
+ switch (ch) {
+ case 'A':
+ /* -AX is being ignored, except for -A{c,m} */
+ if (optarg[0] == 'c' || optarg[0] == 'm') {
+ break;
+ }
+ /* else FALLTRHOUGH */
+ case 'b':
+ /* -bX is being ignored, except for -bp */
+ if (optarg[0] == 'p') {
+ showq = 1;
+ break;
+ }
+ /* else FALLTRHOUGH */
+ case 'D':
+ daemonize = 0;
+ break;
+ case 'L':
+ if (optarg != NULL)
+ snprintf(tag, 254, "%s", optarg);
+ break;
+ case 'f':
+ case 'r':
+ sender = optarg;
+ break;
+
+ case 'o':
+ /* -oX is being ignored, except for -oi */
+ if (optarg[0] != 'i')
+ break;
+ /* else FALLTRHOUGH */
+ case 'i':
+ nodot = 1;
+ break;
+
+ case 'q':
+ doqueue = 1;
+ break;
+
+ default:
+ exit(1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ openlog(tag, LOG_PID | LOG_PERROR, LOG_MAIL);
+
+ config = malloc(sizeof(struct config));
+ if (config == NULL)
+ errx(1, "Cannot allocate enough memory");
+
+ memset(config, 0, sizeof(struct config));
+
+ /* Check if the user has its own config files */
+ snprintf(confpath, PATH_MAX, "%s/.dma/dma.conf", getenv("HOME"));
+ if (stat(confpath, &sb) < 0)
+ snprintf(confpath, PATH_MAX, "%s", CONF_PATH);
+
+ if (parse_conf(confpath, config) < 0) {
+ free(config);
+ errx(1, "reading config file");
+ }
+
+ if (config->features & VIRTUAL)
+ if (parse_virtuser(config->virtualpath) < 0)
+ errx(1, "error reading virtual user file: %s",
+ config->virtualpath);
+
+ if (parse_authfile(config->authpath) < 0)
+ err(1, "reading SMTP authentication file");
+
+ if (showq) {
+ if (argc != 0)
+ errx(1, "sending mail and displaying queue is"
+ " mutually exclusive");
+ load_queue(&lqueue);
+ show_queue(&lqueue);
+ return (0);
+ }
+
+ if (doqueue) {
+ if (argc != 0)
+ errx(1, "sending mail and queue pickup is mutually exclusive");
+ load_queue(&lqueue);
+ run_queue(&lqueue);
+ return (0);
+ }
+
+ if (read_aliases() != 0)
+ err(1, "reading aliases");
+
+ if ((sender = set_from(sender)) == NULL)
+ err(1, "setting from address");
+
+ for (i = 0; i < argc; i++) {
+ if (add_recp(&queue, argv[i], sender, 1) != 0)
+ errx(1, "invalid recipient `%s'\n", argv[i]);
+ }
+
+ if (LIST_EMPTY(&queue.queue))
+ errx(1, "no recipients");
+
+ if (gentempf(&queue) != 0)
+ err(1, "create temp file");
+
+ if (preparespool(&queue, sender) != 0)
+ err(1, "creating spools (1)");
+
+ if (readmail(&queue, sender, nodot) != 0)
+ err(1, "reading mail");
+
+ if (linkspool(&queue) != 0)
+ err(1, "creating spools (2)");
+
+ /* From here on the mail is safe. */
+
+ if (config->features & DEFER)
+ return (0);
+
+ it = go_background(&queue);
+ deliver(it);
+
+ /* NOTREACHED */
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de> and
+ * Matthias Schmidt <matthias@dragonflybsd.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly: src/libexec/dma/dma.h,v 1.1 2008/02/02 18:20:51 matthias Exp $
+ */
+
+#ifndef DMA_H
+#define DMA_H
+
+#ifdef HAVE_CRYPTO
+#include <openssl/ssl.h>
+#endif /* HAVE_CRYPTO */
+
+#include <sys/queue.h>
+#include <stdio.h>
+
+
+#define VERSION "DragonFly Mail Agent 1.0"
+
+#define BUF_SIZE 2048
+#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 PATH_MAX 1024 /* Max path len */
+#define CONF_PATH "/etc/dma/dma.conf" /* /etc/dma/dma.conf */
+#define SMTP_PORT 25 /* default SMTP port */
+#define CON_TIMEOUT 120 /* Connection timeout */
+
+#define VIRTUAL 0x1 /* Support for address rewrites */
+#define STARTTLS 0x2 /* StartTLS support */
+#define SECURETRANS 0x4 /* SSL/TLS in general */
+#define TLSINIT 0x8 /* Flag for TLS init phase */
+#define DEFER 0x10 /* Defer mails */
+
+struct stritem {
+ SLIST_ENTRY(stritem) next;
+ char *str;
+};
+SLIST_HEAD(strlist, stritem);
+
+struct alias {
+ LIST_ENTRY(alias) next;
+ char *alias;
+ struct strlist dests;
+};
+LIST_HEAD(aliases, alias);
+
+struct qitem {
+ LIST_ENTRY(qitem) next;
+ const char *sender;
+ char *addr;
+ char *queuefn;
+ char *queueid;
+ FILE *queuef;
+ off_t hdrlen;
+ int remote;
+};
+LIST_HEAD(queueh, qitem);
+
+struct queue {
+ struct queueh queue;
+ uintmax_t id;
+ int mailfd;
+ char *tmpf;
+};
+
+struct config {
+ char *smarthost;
+ int port;
+ char *aliases;
+ char *spooldir;
+ char *virtualpath;
+ char *authpath;
+ char *certfile;
+ int features;
+#ifdef HAVE_CRYPTO
+ SSL *ssl;
+#endif /* HAVE_CRYPTO */
+};
+
+
+struct virtuser {
+ SLIST_ENTRY(virtuser) next;
+ char *login;
+ char *address;
+};
+SLIST_HEAD(virtusers, virtuser);
+
+struct authuser {
+ SLIST_ENTRY(authuser) next;
+ char *login;
+ char *password;
+ char *host;
+};
+SLIST_HEAD(authusers, authuser);
+
+extern struct aliases aliases;
+
+/* aliases_parse.y */
+extern int yyparse(void);
+extern FILE *yyin;
+
+/* conf.c */
+extern void trim_line(char *);
+extern int parse_conf(const char *, struct config *);
+extern int parse_virtuser(const char *);
+extern int parse_authfile(const char *);
+
+/* crypto.c */
+#ifdef HAVE_CRYPTO
+extern int smtp_init_crypto(struct qitem *, int, int);
+#endif /* HAVE_CRYPTO */
+
+/* net.c */
+extern int check_for_smtp_error(int, char *);
+extern ssize_t send_remote_command(int, const char*, ...);
+extern int deliver_remote(struct qitem *, const char **);
+
+/* base64.c */
+extern int base64_encode(const void *, int, char **);
+
+/* dma.c */
+extern char * hostname(void);
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2008 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
+ * Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly: src/libexec/dma/net.c,v 1.1 2008/02/02 18:20:51 matthias Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef HAVE_CRYPTO
+#include <openssl/ssl.h>
+#endif /* HAVE_CRYPTO */
+
+#include <netdb.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "dma.h"
+
+extern struct config *config;
+extern struct authusers authusers;
+static jmp_buf timeout_alarm;
+
+static void
+sig_alarm(int signo)
+{
+ longjmp(timeout_alarm, 1);
+}
+
+ssize_t
+send_remote_command(int fd, const char* fmt, ...)
+{
+ va_list va;
+ char cmd[4096];
+ ssize_t len = 0;
+
+ va_start(va, fmt);
+ vsprintf(cmd, fmt, va);
+
+ if (((config->features & SECURETRANS) != 0) &&
+ ((config->features & TLSINIT) == 0)) {
+ len = SSL_write(config->ssl, (const char*)cmd, strlen(cmd));
+ SSL_write(config->ssl, "\r\n", 2);
+ }
+ else {
+ len = write(fd, cmd, strlen(cmd));
+ write(fd, "\r\n", 2);
+ }
+ va_end(va);
+
+ return (len+2);
+}
+
+static int
+read_remote_command(int fd, char *buff)
+{
+ ssize_t len;
+
+ if (signal(SIGALRM, sig_alarm) == SIG_ERR) {
+ syslog(LOG_ERR, "SIGALRM error: %m");
+ }
+ if (setjmp(timeout_alarm) != 0) {
+ syslog(LOG_ERR, "Timeout reached");
+ return (1);
+ }
+ alarm(CON_TIMEOUT);
+
+ /*
+ * According to RFC 821 a reply can consists of multiple lines, so
+ * so read until the 4th char of the reply code is != '-'
+ */
+ if (((config->features & SECURETRANS) != 0) &&
+ ((config->features & TLSINIT) == 0))
+ do {
+ len = SSL_read(config->ssl, buff, BUF_SIZE);
+ } while (len > 3 && buff[3] == '-');
+ else
+ do {
+ len = read(fd, buff, BUF_SIZE);
+ } while (len > 3 && buff[3] == '-');
+
+ alarm(0);
+
+ return (0);
+}
+
+int
+check_for_smtp_error(int fd, char *buff)
+{
+ if (read_remote_command(fd, buff) < 0)
+ return (-1);
+
+ /* We received a 5XX reply thus an error happend */
+ if (strncmp(buff, "5", 1) == 0) {
+ syslog(LOG_ERR, "SMTP error : %s", buff);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Handle SMTP authentication
+ *
+ * XXX TODO: give me AUTH CRAM-MD5
+ */
+static int
+smtp_login(struct qitem *it, int fd, char *login, char* password)
+{
+ char buf[2048];
+ char *temp;
+ int len;
+
+ /* Send AUTH command according to RFC 2554 */
+ send_remote_command(fd, "AUTH LOGIN");
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred:"
+ " AUTH login not available: %m", it->queueid);
+ return (1);
+ }
+
+ len = base64_encode(login, strlen(login), &temp);
+ if (len <= 0)
+ return (-1);
+
+ send_remote_command(fd, "%s", temp);
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred:"
+ " AUTH login failed: %m", it->queueid);
+ return (-1);
+ }
+
+ len = base64_encode(password, strlen(password), &temp);
+ if (len <= 0)
+ return (-1);
+
+ send_remote_command(fd, "%s", temp);
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred:"
+ " AUTH password failed: %m", it->queueid);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+open_connection(struct qitem *it, const char *host)
+{
+#ifdef HAVE_INET6
+ struct addrinfo hints, *res, *res0;
+ char servname[128];
+ const char *errmsg = NULL;
+#else
+ struct hostent *hn;
+ struct sockaddr_in addr;
+#endif
+ int fd, error = 0, port;
+
+ if (config->port != NULL)
+ port = config->port;
+ else
+ port = SMTP_PORT;
+
+#ifdef HAVE_INET6
+ /* Shamelessly taken from getaddrinfo(3) */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ snprintf(servname, sizeof(servname), "%d", port);
+ error = getaddrinfo(host, servname, &hints, &res0);
+ if (error) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: "
+ "%s: %m", it->queueid, gai_strerror(error));
+ return (-1);
+ }
+ fd = -1;
+ for (res = res0; res; res = res->ai_next) {
+ fd=socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (fd < 0) {
+ errmsg = "socket failed";
+ continue;
+ }
+ if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
+ errmsg = "connect failed";
+ close(fd);
+ fd = -1;
+ continue;
+ }
+ break;
+ }
+ if (fd < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: %s (%s:%s)",
+ it->queueid, errmsg, host, servname);
+ freeaddrinfo(res0);
+ return (-1);
+ }
+ freeaddrinfo(res0);
+#else
+ memset(&addr, 0, sizeof(addr));
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ addr.sin_family = AF_INET;
+
+ addr.sin_port = htons(port);
+ error = inet_pton(AF_INET, host, &addr.sin_addr);
+ if (error < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: "
+ "address conversion failed: %m", it->queueid);
+ return (1);
+ }
+ hn = gethostbyname(host);
+ if (hn == NULL) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: cannot resolve "
+ "hostname (%s) %m", it->queueid, host);
+ return (-1);
+ } else {
+ memcpy(&addr.sin_addr, hn->h_addr, sizeof(struct in_addr));
+ if (hn->h_length != 4)
+ return (-1);
+ }
+
+ error = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
+ if (error < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: "
+ "connection failed : %m", it->queueid);
+ return (-1);
+ }
+#endif
+ return (fd);
+}
+
+int
+deliver_remote(struct qitem *it, const char **errmsg)
+{
+ struct authuser *a;
+ char *host, buf[2048], line[1000];
+ int fd, error = 0, do_auth = 0;
+ size_t linelen;
+
+ host = strrchr(it->addr, '@');
+ /* Should not happen */
+ if (host == NULL)
+ return(-1);
+ else
+ /* Step over the @ */
+ host++;
+
+ /* Smarthost support? */
+ if (config->smarthost != NULL && strlen(config->smarthost) > 0) {
+ syslog(LOG_INFO, "%s: using smarthost (%s)",
+ it->queueid, config->smarthost);
+ host = config->smarthost;
+ }
+
+ fd = open_connection(it, host);
+ if (fd < 0)
+ return (1);
+
+#ifdef HAVE_CRYPTO
+ if ((config->features & SECURETRANS) != 0) {
+ error = smtp_init_crypto(it, fd, config->features);
+ if (error >= 0)
+ syslog(LOG_INFO, "%s: SSL initialization sucessful",
+ it->queueid);
+ else
+ goto out;
+ }
+
+ /*
+ * If the user doesn't want STARTTLS, but SSL encryption, we
+ * have to enable SSL first, then send EHLO
+ */
+ if (((config->features & STARTTLS) == 0) &&
+ ((config->features & SECURETRANS) != 0)) {
+ send_remote_command(fd, "EHLO %s", hostname());
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: "
+ " EHLO failed: %m", it->queueid);
+ return (-1);
+ }
+ }
+#endif /* HAVE_CRYPTO */
+ if (((config->features & SECURETRANS) == 0)) {
+ send_remote_command(fd, "EHLO %s", hostname());
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: "
+ " EHLO failed: %m", it->queueid);
+ return (-1);
+ }
+ }
+
+ /*
+ * Use SMTP authentication if the user defined an entry for the remote
+ * or smarthost
+ */
+ SLIST_FOREACH(a, &authusers, next) {
+ if (strcmp(a->host, host) == 0) {
+ do_auth = 1;
+ break;
+ }
+ }
+
+ if (do_auth == 1) {
+ syslog(LOG_INFO, "%s: Use SMTP authentication", it->queueid);
+ error = smtp_login(it, fd, a->login, a->password);
+ if (error < 0) {
+ syslog(LOG_ERR, "%s: remote delivery failed:"
+ " SMTP login failed: %m", it->queueid);
+ return (-1);
+ }
+ /* SMTP login is not available, so try without */
+ else if (error > 0)
+ syslog(LOG_ERR, "%s: SMTP login not available. Try without",
+ it->queueid);
+ }
+
+ send_remote_command(fd, "MAIL FROM:<%s>", it->sender);
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred:"
+ " MAIL FROM failed: %m", it->queueid);
+ return (1);
+ }
+
+ /* XXX TODO:
+ * Iterate over all recepients and open only one connection
+ */
+ send_remote_command(fd, "RCPT TO:<%s>", it->addr);
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred:"
+ " RCPT TO failed: %m", it->queueid);
+ return (1);
+ }
+
+ send_remote_command(fd, "DATA");
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred:"
+ " DATA failed: %m", it->queueid);
+ return (1);
+ }
+
+ if (fseek(it->queuef, it->hdrlen, SEEK_SET) != 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: cannot seek: %m",
+ it->queueid);
+ return (1);
+ }
+
+ while (!feof(it->queuef)) {
+ if (fgets(line, sizeof(line), it->queuef) == NULL)
+ break;
+ linelen = strlen(line);
+ if (linelen == 0 || line[linelen - 1] != '\n') {
+ syslog(LOG_CRIT, "%s: remote delivery failed:"
+ "corrupted queue file", it->queueid);
+ *errmsg = "corrupted queue file";
+ error = -1;
+ goto out;
+ }
+
+ /* Remove trailing \n's and escape leading dots */
+ trim_line(line);
+
+ /*
+ * If the first character is a dot, we escape it so the line
+ * length increases
+ */
+ if (line[0] == '.')
+ linelen++;
+
+ if (send_remote_command(fd, "%s", line) != (ssize_t)linelen+1) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: "
+ "write error", it->queueid);
+ error = 1;
+ goto out;
+ }
+ }
+
+ send_remote_command(fd, ".");
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: %m",
+ it->queueid);
+ return (1);
+ }
+
+ send_remote_command(fd, "QUIT");
+ if (check_for_smtp_error(fd, buf) < 0) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: "
+ "QUIT failed: %m", it->queueid);
+ return (1);
+ }
+out:
+
+ close(fd);
+ return (error);
+}
+