Import Dragonfly Mail Agent into base system

It is a small and lightweight Mail Transport Agent.
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, SMTP authentication and NULLCLIENT.

Make dma conditional to new WITHOUT_DMA option and make it respect WITHOUT_MAIL

Reviewed by:	peter
Discussed with:	emaste, bz, peter
This commit is contained in:
Baptiste Daroussin 2014-02-21 07:26:49 +00:00
commit a9e8641da9
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=262282
55 changed files with 6233 additions and 0 deletions

28
contrib/dma/BSDmakefile Normal file
View File

@ -0,0 +1,28 @@
# $DragonFly: src/libexec/dma/Makefile,v 1.5 2008/09/19 00:36:57 corecode Exp $
#
version!= sh get-version.sh
CFLAGS+= -I${.CURDIR}
CFLAGS+= -DHAVE_REALLOCF -DHAVE_STRLCPY -DHAVE_GETPROGNAME
CFLAGS+= -DLIBEXEC_PATH='"${LIBEXEC}"' -DDMA_VERSION='"${version}"'
CFLAGS+= -DCONF_PATH='"${CONFDIR}"'
DPADD= ${LIBSSL} ${LIBCRYPTO}
LDADD= -lssl -lcrypto
PROG= dma
SRCS= aliases_parse.y aliases_scan.l base64.c conf.c crypto.c
SRCS+= dma.c dns.c local.c mail.c net.c spool.c util.c
MAN= dma.8
PREFIX?= /usr/local
LIBEXEC?= ${PREFIX}/libexec
CONFDIR?= ${PREFIX}/etc/dma
BINOWN= root
BINGRP= mail
BINMODE=2555
WARNS?= 6
.include <bsd.prog.mk>

28
contrib/dma/INSTALL Normal file
View File

@ -0,0 +1,28 @@
Installing DMA:
===============
On most systems (with a development environment installed) you should be able to compile DMA with:
make
Once it have compiled it successfully, you can install it with:
make install sendmail-link mailq-link install-spool-dirs install-etc
Troubleshooting:
----------------
On systems that do not default to a compatible "make" version, try using "gmake" or "pmake" instead of "make". Some known examples of this:
* Solaris 9
* Solaris 10
Check that you have the following commands installed:
* cc - gcc is known to work
* lex - flex is known to work
* yacc - bison is kjnown to work
* make - BSD make and GNU make is knwon to work
* sh - Need to be POSIX compliant, dash, bash known to work
* install - GNU and BSD versions known to work
* openssl - Add the header location to C_INCLUDE_PATH if you get errors about "err.h"
If you have all of these tools installed, set the CC, YACC, INSTALL, LEX and SH variable to point to the relevant location and command.
Example:
make CC=gcc YACC=bison LEX=/usr/bin/flex SH=/bin/bash INSTALL=/usr/bin/install

109
contrib/dma/LICENSE Normal file
View File

@ -0,0 +1,109 @@
Copyright (c) 2008 The DragonFly Project.
Copyright (c) 2008-2011, Simon Schubert <2@0x2c.org>.
All rights reserved.
This code is derived from software contributed to The DragonFly Project
by Simon Schubert <2@0x2c.org>.
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.
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.
Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Copyright (c) 1998, M. Warner Losh <imp@freebsd.org>
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.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.

111
contrib/dma/Makefile Normal file
View File

@ -0,0 +1,111 @@
#
# Depending on your operating system, you might want to influence
# the conditional inclusion of some helper functions:
#
# Define HAVE_* (in caps) if your system already provides:
# reallocf
# strlcpy
# getprogname
#
SH?= sh
version= $(shell ${SH} get-version.sh)
debversion= $(shell ${SH} get-version.sh | sed -Ee 's/^v//;s/[.]([[:digit:]]+)[.](g[[:xdigit:]]+)$$/+\1+\2/')
CC?= gcc
CFLAGS?= -O -pipe
LDADD?= -lssl -lcrypto -lresolv
CFLAGS+= -Wall -DDMA_VERSION='"${version}"' -DLIBEXEC_PATH='"${LIBEXEC}"' -DCONF_PATH='"${CONFDIR}"'
INSTALL?= install -p
CHGRP?= chgrp
CHMOD?= chmod
PREFIX?= /usr/local
SBIN?= ${PREFIX}/sbin
LIBEXEC?= ${PREFIX}/lib
CONFDIR?= /etc/dma
MAN?= ${PREFIX}/share/man
VAR?= /var
DMASPOOL?= ${VAR}/spool/dma
VARMAIL?= ${VAR}/mail
SYMLINK?= -s # or empty to create hard link
YACC?= yacc
LEX?= lex
LN?= ln
OBJS= aliases_parse.o aliases_scan.o base64.o conf.o crypto.o
OBJS+= dma.o dns.o local.o mail.o net.o spool.o util.o
OBJS+= dfcompat.o
all: dma dma-mbox-create
clean:
-rm -f .depend dma dma-mbox-create *.[do]
-rm -f aliases_parse.[ch] aliases_scan.c
install: all
${INSTALL} -d ${DESTDIR}${SBIN}
${INSTALL} -d ${DESTDIR}${MAN}/man8 ${DESTDIR}${LIBEXEC}
${INSTALL} -m 2755 -o root -g mail dma ${DESTDIR}${SBIN}
${INSTALL} -m 4754 -o root -g mail dma-mbox-create ${DESTDIR}${LIBEXEC}
${INSTALL} -m 0644 dma.8 ${DESTDIR}${MAN}/man8/
sendmail-link:
cd ${DESTDIR}${SBIN} && ${LN} ${SYMLINK} dma sendmail
mailq-link:
cd ${DESTDIR}${SBIN} && ${LN} ${SYMLINK} dma mailq
install-spool-dirs:
${INSTALL} -d -m 2775 -o root -g mail ${DESTDIR}${DMASPOOL}
${INSTALL} -d -m 2775 -o root -g mail ${DESTDIR}${VARMAIL}
permissions:
-${CHGRP} mail ${DESTDIR}${VARMAIL}/*
-${CHMOD} g+w ${DESTDIR}${VARMAIL}/*
-${CHMOD} 660 ${DESTDIR}${DMASPOOL}/flush
install-etc:
${INSTALL} -d ${DESTDIR}${CONFDIR}
@if [ -e ${DESTDIR}${CONFDIR}/dma.conf ]; then \
echo "Not overwriting ${DESTDIR}${CONFDIR}/dma.conf."; \
else \
echo ${INSTALL} -m 644 -o root -g mail dma.conf ${DESTDIR}${CONFDIR}; \
${INSTALL} -m 644 -o root -g mail dma.conf ${DESTDIR}${CONFDIR}; \
fi
@if [ -e ${DESTDIR}${CONFDIR}/auth.conf ]; then \
echo "Not overwriting ${DESTDIR}${CONFDIR}/auth.conf."; \
else \
echo ${INSTALL} -m 640 -o root -g mail auth.conf ${DESTDIR}${CONFDIR}; \
${INSTALL} -m 640 -o root -g mail auth.conf ${DESTDIR}${CONFDIR}; \
fi
aliases_parse.c: aliases_parse.y
${YACC} -d -o aliases_parse.c aliases_parse.y
aliases_scan.c: aliases_scan.l
${LEX} -t aliases_scan.l > aliases_scan.c
.SUFFIXES: .c .o
.c.o:
${CC} ${CFLAGS} ${CPPFLAGS} -include dfcompat.h -o $@ -c $<
dma: ${OBJS}
${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
dch:
dch --release-heuristic changelog -v ${debversion}
ppa:
@if [ -z '${DEB_DIST}' ]; then echo "please set DEB_DIST to build"; exit 1; fi
dch -v "${debversion}~${DEB_DIST}" -D ${DEB_DIST} "${DEB_DIST} build" -b
debuild -S -sa
ver=$$(dpkg-parsechangelog -n1 | awk '$$1 == "Version:" { print $$2 }'); \
dput ppa:corecode/dma ../dma_$${ver}_source.changes

15
contrib/dma/Makefile.etc Normal file
View File

@ -0,0 +1,15 @@
# $DragonFly: src/etc/dma/Makefile,v 1.3 2008/02/12 22:10:20 matthias Exp $
FILESDIR= /etc/dma
SHAREOWN= root
SHAREGRP= mail
FILESMODE= 640
.if !exists(${DESTDIR}/etc/dma/auth.conf)
FILES+= auth.conf
.endif
.if !exists(${DESTDIR}/etc/dma/dma.conf)
FILES+= dma.conf
.endif
.include <bsd.prog.mk>

View File

@ -0,0 +1,32 @@
dma -- DragonFly Mail Agent
===========================
dma 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.
dma is not intended as a replacement for real, big MTAs like sendmail(8)
or postfix(1). Consequently, dma does not listen on port 25 for
incoming connections.
Building
--------
make
Installation
------------
make install sendmail-link mailq-link install-spool-dirs install-etc
See INSTALL for requirements and configuration options.
Contact
-------
Simon Schubert <2@0x2c.org>

5
contrib/dma/TODO Normal file
View File

@ -0,0 +1,5 @@
- unquote/handle quoted local recipients
- use proper sysexit codes
- handle/use ESMTP extensions
- .forward support
- suggest way to run a queue flush on boot

1
contrib/dma/VERSION Normal file
View File

@ -0,0 +1 @@
v0.9

112
contrib/dma/aliases_parse.y Normal file
View File

@ -0,0 +1,112 @@
%{
#include <err.h>
#include <string.h>
#include <syslog.h>
#include "dma.h"
extern int yylineno;
static void yyerror(const char *);
int yywrap(void);
int yylex(void);
static void
yyerror(const char *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
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'
{
YYABORT;
}
;
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;
}
;
%%

View File

@ -0,0 +1,24 @@
%{
#include <string.h>
#include "aliases_parse.h"
#define YY_NO_INPUT
int yylex(void);
%}
%option yylineno
%option nounput
%%
[^:,#[:space:][:cntrl:]]+ {yylval.ident = strdup(yytext); return T_IDENT;}
^([[:blank:]]*(#.*)?\n)+ ;/* ignore empty lines */
[:,\n] return yytext[0];
(\n?[[:blank:]]+|#.*)+ ;/* ignore whitespace and continuation */
\\\n ;/* ignore continuation. not allowed in comments */
. return T_ERROR;
<<EOF>> return T_EOF;
%%

4
contrib/dma/auth.conf Normal file
View File

@ -0,0 +1,4 @@
# $DragonFly: src/etc/dma/auth.conf,v 1.1 2008/02/02 18:24:00 matthias Exp $
#
# SMTP authentication entries (currently AUTH LOGIN only)
# Format: user|my.smarthost.example.com:password

135
contrib/dma/base64.c Normal file
View File

@ -0,0 +1,135 @@
/*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include "dma.h"
static char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static int
pos(char c)
{
char *p;
for (p = base64_chars; *p; p++)
if (*p == c)
return p - base64_chars;
return -1;
}
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);
}
#define DECODE_ERROR 0xffffffff
static unsigned int
token_decode(const char *token)
{
int i;
unsigned int val = 0;
int marker = 0;
if (strlen(token) < 4)
return DECODE_ERROR;
for (i = 0; i < 4; i++) {
val *= 64;
if (token[i] == '=')
marker++;
else if (marker > 0)
return DECODE_ERROR;
else
val += pos(token[i]);
}
if (marker > 2)
return DECODE_ERROR;
return (marker << 24) | val;
}
int
base64_decode(const char *str, void *data)
{
const char *p;
unsigned char *q;
q = data;
for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
unsigned int val = token_decode(p);
unsigned int marker = (val >> 24) & 0xff;
if (val == DECODE_ERROR)
return -1;
*q++ = (val >> 16) & 0xff;
if (marker < 2)
*q++ = (val >> 8) & 0xff;
if (marker < 1)
*q++ = val & 0xff;
}
return q - (unsigned char *) data;
}

246
contrib/dma/conf.c Normal file
View File

@ -0,0 +1,246 @@
/*
* 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.
*/
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <stdarg.h>
#include "dma.h"
#define DP ": \t"
#define EQS " \t"
/*
* Remove trailing \n's
*/
void
trim_line(char *line)
{
size_t linelen;
char *p;
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] = '.';
}
}
static void
chomp(char *str)
{
size_t len = strlen(str);
if (len == 0)
return;
if (str[len - 1] == '\n')
str[len - 1] = 0;
}
/*
* Read the SMTP authentication config file
*
* file format is:
* user|host:password
*
* A line starting with # is treated as comment and ignored.
*/
void
parse_authfile(const char *path)
{
char line[2048];
struct authuser *au;
FILE *a;
char *data;
int lineno = 0;
a = fopen(path, "r");
if (a == NULL) {
errlog(1, "can not open auth file `%s'", path);
/* NOTREACHED */
}
while (!feof(a)) {
if (fgets(line, sizeof(line), a) == NULL)
break;
lineno++;
chomp(line);
/* We hit a comment */
if (*line == '#')
continue;
/* Ignore empty lines */
if (*line == 0)
continue;
au = calloc(1, sizeof(*au));
if (au == NULL)
errlog(1, NULL);
data = strdup(line);
au->login = strsep(&data, "|");
au->host = strsep(&data, DP);
au->password = data;
if (au->login == NULL ||
au->host == NULL ||
au->password == NULL) {
errlogx(1, "syntax error in authfile %s:%d",
path, lineno);
/* NOTREACHED */
}
SLIST_INSERT_HEAD(&authusers, au, next);
}
fclose(a);
}
/*
* XXX TODO
* Check for bad things[TM]
*/
void
parse_conf(const char *config_path)
{
char *word;
char *data;
FILE *conf;
char line[2048];
int lineno = 0;
conf = fopen(config_path, "r");
if (conf == NULL) {
/* Don't treat a non-existing config file as error */
if (errno == ENOENT)
return;
errlog(1, "can not open config `%s'", config_path);
/* NOTREACHED */
}
while (!feof(conf)) {
if (fgets(line, sizeof(line), conf) == NULL)
break;
lineno++;
chomp(line);
/* We hit a comment */
if (strchr(line, '#'))
*strchr(line, '#') = 0;
data = line;
word = strsep(&data, EQS);
/* Ignore empty lines */
if (word == NULL || *word == 0)
continue;
if (data != NULL && *data != 0)
data = strdup(data);
else
data = NULL;
if (strcmp(word, "SMARTHOST") == 0 && data != NULL)
config.smarthost = data;
else if (strcmp(word, "PORT") == 0 && data != NULL)
config.port = atoi(data);
else if (strcmp(word, "ALIASES") == 0 && data != NULL)
config.aliases = data;
else if (strcmp(word, "SPOOLDIR") == 0 && data != NULL)
config.spooldir = data;
else if (strcmp(word, "AUTHPATH") == 0 && data != NULL)
config.authpath= data;
else if (strcmp(word, "CERTFILE") == 0 && data != NULL)
config.certfile = data;
else if (strcmp(word, "MAILNAME") == 0 && data != NULL)
config.mailname = data;
else if (strcmp(word, "MASQUERADE") == 0 && data != NULL) {
char *user = NULL, *host = NULL;
if (strrchr(data, '@')) {
host = strrchr(data, '@');
*host = 0;
host++;
user = data;
} else {
host = data;
}
if (host && *host == 0)
host = NULL;
if (user && *user == 0)
user = NULL;
config.masquerade_host = host;
config.masquerade_user = user;
} else if (strcmp(word, "STARTTLS") == 0 && data == NULL)
config.features |= STARTTLS;
else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL)
config.features |= TLS_OPP;
else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL)
config.features |= SECURETRANS;
else if (strcmp(word, "DEFER") == 0 && data == NULL)
config.features |= DEFER;
else if (strcmp(word, "INSECURE") == 0 && data == NULL)
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);
}

312
contrib/dma/crypto.c Normal file
View File

@ -0,0 +1,312 @@
/*
* 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.
*/
#include <openssl/x509.h>
#include <openssl/md5.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <syslog.h>
#include "dma.h"
static int
init_cert_file(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, "SSL: Cannot load certificate `%s': %s", path, ssl_errstr());
return (-1);
}
/* Add private key to ctx */
error = SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM);
if (error < 1) {
syslog(LOG_ERR, "SSL: Cannot load private key `%s': %s", path, ssl_errstr());
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, "SSL: Cannot check private key: %s", ssl_errstr());
return (-1);
}
return (0);
}
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;
/* XXX clean up on error/close */
/* Init SSL library */
SSL_library_init();
SSL_load_error_strings();
meth = TLSv1_client_method();
ctx = SSL_CTX_new(meth);
if (ctx == NULL) {
syslog(LOG_WARNING, "remote delivery deferred: SSL init failed: %s", ssl_errstr());
return (1);
}
/* User supplied a certificate */
if (config.certfile != NULL) {
error = init_cert_file(ctx, config.certfile);
if (error) {
syslog(LOG_WARNING, "remote delivery deferred");
return (1);
}
}
/*
* 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 |= NOSSL;
send_remote_command(fd, "EHLO %s", hostname());
if (read_remote(fd, 0, NULL) == 2) {
send_remote_command(fd, "STARTTLS");
if (read_remote(fd, 0, NULL) != 2) {
if ((feature & TLS_OPP) == 0) {
syslog(LOG_ERR, "remote delivery deferred: STARTTLS not available: %s", neterr);
return (1);
} else {
syslog(LOG_INFO, "in opportunistic TLS mode, STARTTLS not available: %s", neterr);
return (0);
}
}
}
/* End of TLS init phase, enable SSL_write/read */
config.features &= ~NOSSL;
}
config.ssl = SSL_new(ctx);
if (config.ssl == NULL) {
syslog(LOG_NOTICE, "remote delivery deferred: SSL struct creation failed: %s",
ssl_errstr());
return (1);
}
/* 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) {
syslog(LOG_NOTICE, "remote delivery deferred: SSL set fd failed: %s",
ssl_errstr());
return (1);
}
/* Open SSL connection */
error = SSL_connect(config.ssl);
if (error < 0) {
syslog(LOG_ERR, "remote delivery deferred: SSL handshake failed fatally: %s",
ssl_errstr());
return (1);
}
/* Get peer certificate */
cert = SSL_get_peer_certificate(config.ssl);
if (cert == NULL) {
syslog(LOG_WARNING, "remote delivery deferred: Peer did not provide certificate: %s",
ssl_errstr());
}
X509_free(cert);
return (0);
}
/*
* hmac_md5() taken out of RFC 2104. This RFC was written by H. Krawczyk,
* M. Bellare and R. Canetti.
*
* text pointer to data stream
* text_len length of data stream
* key pointer to authentication key
* key_len length of authentication key
* digest caller digest to be filled int
*/
void
hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
unsigned char* digest)
{
MD5_CTX context;
unsigned char k_ipad[65]; /* inner padding -
* key XORd with ipad
*/
unsigned char k_opad[65]; /* outer padding -
* key XORd with opad
*/
unsigned char tk[16];
int i;
/* if key is longer than 64 bytes reset it to key=MD5(key) */
if (key_len > 64) {
MD5_CTX tctx;
MD5_Init(&tctx);
MD5_Update(&tctx, key, key_len);
MD5_Final(tk, &tctx);
key = tk;
key_len = 16;
}
/*
* the HMAC_MD5 transform looks like:
*
* MD5(K XOR opad, MD5(K XOR ipad, text))
*
* where K is an n byte key
* ipad is the byte 0x36 repeated 64 times
*
* opad is the byte 0x5c repeated 64 times
* and text is the data being protected
*/
/* start out by storing key in pads */
bzero( k_ipad, sizeof k_ipad);
bzero( k_opad, sizeof k_opad);
bcopy( key, k_ipad, key_len);
bcopy( key, k_opad, key_len);
/* XOR key with ipad and opad values */
for (i=0; i<64; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
/*
* perform inner MD5
*/
MD5_Init(&context); /* init context for 1st
* pass */
MD5_Update(&context, k_ipad, 64); /* start with inner pad */
MD5_Update(&context, text, text_len); /* then text of datagram */
MD5_Final(digest, &context); /* finish up 1st pass */
/*
* perform outer MD5
*/
MD5_Init(&context); /* init context for 2nd
* pass */
MD5_Update(&context, k_opad, 64); /* start with outer pad */
MD5_Update(&context, digest, 16); /* then results of 1st
* hash */
MD5_Final(digest, &context); /* finish up 2nd pass */
}
/*
* CRAM-MD5 authentication
*/
int
smtp_auth_md5(int fd, char *login, char *password)
{
unsigned char digest[BUF_SIZE];
char buffer[BUF_SIZE], ascii_digest[33];
char *temp;
int len, i;
static char hextab[] = "0123456789abcdef";
temp = calloc(BUF_SIZE, 1);
memset(buffer, 0, sizeof(buffer));
memset(digest, 0, sizeof(digest));
memset(ascii_digest, 0, sizeof(ascii_digest));
/* Send AUTH command according to RFC 2554 */
send_remote_command(fd, "AUTH CRAM-MD5");
if (read_remote(fd, sizeof(buffer), buffer) != 3) {
syslog(LOG_DEBUG, "smarthost authentication:"
" AUTH cram-md5 not available: %s", neterr);
/* if cram-md5 is not available */
free(temp);
return (-1);
}
/* skip 3 char status + 1 char space */
base64_decode(buffer + 4, temp);
hmac_md5((unsigned char *)temp, strlen(temp),
(unsigned char *)password, strlen(password), digest);
free(temp);
ascii_digest[32] = 0;
for (i = 0; i < 16; i++) {
ascii_digest[2*i] = hextab[digest[i] >> 4];
ascii_digest[2*i+1] = hextab[digest[i] & 15];
}
/* prepare answer */
snprintf(buffer, BUF_SIZE, "%s %s", login, ascii_digest);
/* encode answer */
len = base64_encode(buffer, strlen(buffer), &temp);
if (len < 0) {
syslog(LOG_ERR, "can not encode auth reply: %m");
return (-1);
}
/* send answer */
send_remote_command(fd, "%s", temp);
free(temp);
if (read_remote(fd, 0, NULL) != 2) {
syslog(LOG_WARNING, "remote delivery deferred:"
" AUTH cram-md5 failed: %s", neterr);
return (-2);
}
return (0);
}

29
contrib/dma/debian/NEWS Normal file
View File

@ -0,0 +1,29 @@
dma (0.0.2010.06.17-3) unstable; urgency=low
The default delivery mode has been changed to immediate, as it is in
the upstream version of dma; the DEFER keyword is now disabled by default
in dma.conf.
-- Peter Pentchev <roam@ringlet.net> Tue, 27 Jul 2010 13:26:48 +0300
dma (0.0.2010.06.17-1) unstable; urgency=low
The dma spool directory format has changed. The Debian package of dma now
recommends a separate package containing the dma-migrate utility; if it is
present, it will be invoked at each periodic dma queue flush and attempt to
convert the existing old-style queued messages to the new format. In most
cases, this should not incur any performance penalties in normal operation,
since dma-migrate will scan the spool directory and ignore any new messages
(they should already be in the new format); however, if it appears that
the periodic queue flush runs take longer than usual to start up, you may
remove the dma-migrate package once you have ascertained that your queue
directory (/var/spool/dma) only contains files with names beginning with
the letters M or Q.
This version of dma knows how to perform MX lookups, so remote delivery is
now possible directly, not through a smarthost. However, a smarthost setup
might still be preferred on many systems for various reasons - e.g. dynamic
address assignment, a central outgoing mailserver, a roaming laptop, etc.
-- Peter Pentchev <roam@ringlet.net> Mon, 21 Jun 2010 11:03:57 +0300

View File

@ -0,0 +1,12 @@
dma for Debian
--------------
Smarthost operation by default - needs to be configured!
After first installing dma, you need to configure it for proper operation -
whether it should deliver all outgoing e-mail messages through a single
smarthost or attempt to contact the remote mail servers directly. This should
be configured through the debconf questions, but you may change the setting
using the SMARTHOST directive in the /etc/dma/dma.conf file.
-- Simon Schubert <2@0x2c.org> Fri, 29 Oct 2010 00:25:48 +0200

View File

@ -0,0 +1,297 @@
dma (0.9) unstable; urgency=low
[ Ed Maste ]
* Be explicit about missing user.
* Allow DMA_ROOT_USER & DMA_GROUP to be overridden.
* Add compat #ifdef for older OpenSSL
* Add CONF_DIR, as in Makefile
* More detailed error message for tmp file failure.
[ Simon Schubert ]
* spool.c: bzero contents of pointer
-- Simon Schubert <2@0x2c.org> Mon, 03 Jun 2013 15:58:44 +0200
dma (0.8) unstable; urgency=low
[ Gert van den Berg ]
* Added some more documentation on compiling
* Make Makefile and README consistent with INSTALL
[ Sascha Wildner ]
* dma.8: Fix a few small issues.
[ Simon Schubert ]
* dma.8: we only have 2 config files at the moment
* Merge pull request #2 from mohag/master
* don't treat -options following -q as argument to it
* deliver_remote: propagate back DNS errors
* don't complain when we can't lock a queue file during flush
* implement queue flushing prod
-- Simon Schubert <2@0x2c.org> Fri, 30 Mar 2012 12:03:54 +0200
dma (0.7) unstable; urgency=low
[ Simon Schubert ]
* add semicolon before date in Received: header
* parse_conf: fix bug with masqueraded domains
* clear up warnings found by clang static analysis
* mark printf-alike functions
-- Simon Schubert <2@0x2c.org> Tue, 03 Jan 2012 14:53:43 +0100
dma (0.6) unstable; urgency=low
[ Simon Schubert ]
* deliver_local: quote "From " more liberally
-- Simon Schubert <2@0x2c.org> Wed, 07 Dec 2011 12:42:22 +0100
dma (0.5) unstable; urgency=low
[ Simon Schubert ]
* implement masquerading using the MASQUERADE config option
* access config files at CONF_PATH, add makefile target to install conf files
* implement the "*" catch-all alias
-- Simon Schubert <2@0x2c.org> Wed, 16 Nov 2011 13:34:43 +0100
dma (0.4) unstable; urgency=low
[ Simon Schubert ]
* Makefile: put libraries at the end when linking
* LICENSE: add
* Merge commit 'refs/merge-requests/3' of git://gitorious.org/dma/dma
* Merge commit 'refs/merge-requests/4' of git://gitorious.org/dma/dma
* Add symlink for sendmail which is expected by many packages
* Makefile: create spool directories in a separate target
* Makefile: add symlink for mailq
* README: elaborate, use markdown
[ Peter Pentchev ]
* Fix straight SSL/TLS delivery to remote MX's.
* Fix a minor memory leak discovered by cppcheck.
-- Simon Schubert <2@0x2c.org> Wed, 16 Nov 2011 00:08:28 +0100
dma (0.3) unstable; urgency=low
[ Simon Schubert ]
* todo: we create mboxes properly now
* dma-mbox-create: group mail only needs to write to mboxes
* errlog: preserve errno
* dma-mbox-create: add error/status logging
* dns_get_mx_list: handle errors properly
[ Peter Pentchev ]
* Make add_host() really return an error code.
[ Simon Schubert ]
* readmail: accept mail without newline at the end
[ Peter Pentchev ]
* In OpenSSL 1.0, TLSv1_client_method() returns a const pointer.
[ Simon Schubert ]
* make dma compile again on DragonFly
* parse_addrs: fix parsing for multiple <addresses>
-- Simon Schubert <2@0x2c.org> Sat, 09 Jul 2011 02:38:05 +0200
dma (0.2) unstable; urgency=low
* Update to dma 0.2
- debian: better mark as UNRELEASED
- add ppa makefile recipe
- TODO: suggest way to run a queue flush on boot
- partially adopt 34-manpage-defaults.patch: AUTHPATH is not set by default
- Revert "debian: better mark as UNRELEASED"
- setlogident: openlog() wants a static variable
- writequeuef: create files with g+rw
- drop privileges when run by root
- implement mbox creation via setuid helper
- debian: build with consistent flags
- debian: remove unused files
- debian: fix lintian warnings and errors
- make ppa: force lower version number
- make ppa: proper name
-- Simon Schubert <2@0x2c.org> Sun, 31 Oct 2010 23:57:50 +0100
dma (0.1) unstable; urgency=low
* Update dma to 0.1
-- Simon Schubert <2@0x2c.org> Fri, 29 Oct 2010 00:57:26 +0200
dma (0.0.2010.06.17-6) unstable; urgency=low
* Add the 37-gnu-hurd patch to really fix the FTBFS on GNU/Hurd.
* Convert several shell output assignments from = to :=
* Switch to bzip2 compression for the Debian tarball.
-- Peter Pentchev <roam@ringlet.net> Sun, 17 Oct 2010 00:08:33 +0300
dma (0.0.2010.06.17-5) unstable; urgency=low
* Only use SA_NOCLDWAIT if available to fix the Hurd FTBFS.
-- Peter Pentchev <roam@ringlet.net> Thu, 07 Oct 2010 11:42:23 +0300
dma (0.0.2010.06.17-4) unstable; urgency=low
* Fix an infinite loop in dma-migrate if char is unsigned.
Closes: #599172
* Switch to Git and point the Vcs-* fields to Gitorious.
-- Peter Pentchev <roam@ringlet.net> Wed, 06 Oct 2010 17:30:29 +0300
dma (0.0.2010.06.17-3) unstable; urgency=low
* Update the debconf translations:
- French by Steve Petruzzello; Closes: #587883
* Bump Standards-Version to 3.9.1 with no changes.
* Disable deferred delivery by default, as in the upstream version:
- in the 03-debian-locations patch, comment the DEFER keyword in dma.conf,
as it is upstream
- refresh the 11-double-bounce.patch
- reword the README.Debian section on deferred delivery a bit
- add a news blurb
-- Peter Pentchev <roam@ringlet.net> Tue, 27 Jul 2010 13:34:27 +0300
dma (0.0.2010.06.17-2) unstable; urgency=low
* Quick on the heels of -1 to fix a momentary lapse of reason on my part:
in the 03-debian-locations patch, revert part of the 0.0.2010.06.17-1
change: do not set a un-overrideable default for the deferred delivery!
Closes: #587593
* Update the debconf translations:
- Japanese by Hideki Yamane; Closes: #587543
-- Peter Pentchev <roam@ringlet.net> Wed, 30 Jun 2010 11:59:46 +0300
dma (0.0.2010.06.17-1) unstable; urgency=low
* New upstream version:
- no longer reports the remote port number; Closes: #544820
- fixes some queue locking problems; Closes: #582593
- adapt the rules file to use the GNU Makefile instead of the BSD one
- drop pmake from Build-Depends
- remove the 01-debian-build patch, overtaken by upstream changes
- in the 03-debian-locations patch, make the Debian defaults actual
defaults for the dma binary, not just in the dma.conf file
- adapt the 04-debian-setgid patch for the GNU Makefile
- in the 10-liblockfile patch, change the GNU Makefile, too
- enhance the 11-double-bounce patch a bit:
- use dma's own delqueue() function instead of a naive unlink() so
all the queue files are cleaned up
- document the Debian default for DBOUNCEPROG in the manual page
- resurrect the 13-hardening patch, correcting a couple of
unchecked asprintf(3) and vasprintf(3) invocations
- the functionality of the 20-parse-recipient patch was implemented
upstream in a different way, so replace it with a small bugfix
- remove the 22-openfiles patch, overtaken by upstream changes
- in the 24-random-message-id patch, change the GNU Makefile, too
- add the 27-int-size patch to cast a variable to the correct type
- add the 28-valid-recipient patch to fix parsing recipients out of
the message body
- add the 29-double-free patch to fix a double-free error
- add the 30-ldflags patch to honor LDFLAGS if specified
- refresh the 09-typos, 17-mailname, 23-dirent-d_type, and
25-unsupported-starttls patches
- teach the dbounce-simple-safecat handler about the M*/Q* spool
files scheme
* Bump Standards-Version to 3.9.0 with no changes.
* Update the copyright file:
- bring it up to the latest revision of the DEP 5
- update the upstream copyright notices
- bump the year on my copyright notice
* Remove the diffsrc rules target which was intended for my own
internal use, but has outlived its time.
* Use dpkg-buildflags from dpkg-dev >= 1.15.7~ to get the default
values for CFLAGS, CPPFLAGS, and LDFLAGS; no longer rely on
dpkg-buildpackage to set them by default.
* Add the dma-migrate utility (in a separate binary package) to convert
spool files to the new queue format (M* and Q* files for each message)
* Add a dma-migrate invocation to dma.cron.d
* Shorten the Vcs-Browser URL.
* Add the 31-sigalrm-backoff patch to force a delivery attempt on SIGALRM.
* Properly substitute the debconf-supplied values for DBOUNCEPROG and
SMARTHOST into dma.conf even if they are empty without generating
an invalid dma.conf file.
* Remove the smarthost default; dma does MX lookups now, so it doesn't
really *need* one anymore.
* Reword the debconf relayhost question - dma does not really need
a smarthost anymore.
* Update the debconf translations:
- Bulgarian
- Portuguese by Américo Monteiro
- German by Helge Kreutzmann; Closes: #586531
- Russian by Yuri Kozlov; Closes: #586579
- Czech by Michal Simunek; Closes: #586791
- Swedish by Martin Bagge; Closes: #586825
- Spanish by Francisco Javier Cuadrado; Closes: #587088
* Update the smarthost configuration information in README.Debian.
* Add the 32-comment-uncomment patch to correct the manual page wording.
* Add the 33-opportunistic-tls patch to allow remote connections to proceed
even if the STARTTLS negotiation fails.
* Fix the 25-unsupported-starttls patch to actually error out if the SSL
negotiation fails.
* Forward all the non-Debian-specific patches upstream to Simon Schubert.
* Add the 34-manpage-defaults patch to properly document what dma will use
as default values if not specified in the config file instead of what
the default config file specifies. Closes: #544748
* Add the 35-delivery-retry patch to try local deliveries a bit more often
and to randomize the delivery timeout to avoid locking issues.
-- Peter Pentchev <roam@ringlet.net> Mon, 28 Jun 2010 23:26:36 +0300
dma (0.0.2009.07.17-3) unstable; urgency=low
* Really install the files in /etc/dma/ as root/mail/640 and
change the ownership of the existing files in the postinst script
when upgrading from older versions. Closes: #544664
* Install the /usr/bin/mailq and /usr/bin/newaliases symlinks.
Closes: #558421
* Switch to the 3.0 (quilt) source format.
* Update the debconf translations:
- add German. Closes: #552754
- add Japanese. Closes: #554515
- remove a double space and unfuzzy the translations. Closes: #552586
* Fix a crash when the SMTP server does not support STARTTLS.
Closes: #547594
* Always use the user-supplied value from the debconf query for
the smarthost and the double-bounce program. This may result in
debconf overriding a manually-edited config file, so add a note to
dma.conf stating that these values are handled via debconf.
Closes: #544663
* Fix a misspelling of dma/dbounceprog as dma/defer in the debconf
configuration script. Oops.
-- Peter Pentchev <roam@ringlet.net> Sat, 19 Dec 2009 14:35:10 +0200
dma (0.0.2009.07.17-2) unstable; urgency=low
* Allow the spool directory to live on a filesystem that does not
set the d_type member of the dirent structure, like XFS.
Closes: #544357
* Randomize the Message-Id a bit more. Closes: #544475
* Bump Standards-Version to 3.8.3 with no changes.
* Only enable the build hardening wrapper if the "hardening" build
option is specified.
* Switch the copyright file header from the Wiki to DEP 5.
* Remove the manual page ".Dx" patch - the groff version in Squeeze
knows about the .Dx mdoc macro. Add a lintian override for
the "Unknown DragonFly version" error.
* Convert the patch file headers to the DEP 3 format.
-- Peter Pentchev <roam@ringlet.net> Tue, 01 Sep 2009 13:36:33 +0300
dma (0.0.2009.07.17-1) unstable; urgency=low
* Initial release
(Closes: #511410, #533458, #533614, #533890, #534101, #534860)
-- Peter Pentchev <roam@ringlet.net> Tue, 11 Aug 2009 16:08:41 +0300

View File

@ -0,0 +1 @@
7

View File

@ -0,0 +1,37 @@
Source: dma
Section: mail
Priority: optional
Maintainer: Peter Pentchev <roam@ringlet.net>
DM-Upload-Allowed: yes
Build-Depends: debhelper (>= 7.0.50), byacc, dpkg-dev (>= 1.15.7~), flex, hardening-wrapper, libssl-dev, po-debconf
Standards-Version: 3.9.1
Homepage: http://devel.ringlet.net/mail/dma/
Vcs-Git: git://gitorious.org/dma-roam/pkg-debian.git
Vcs-Browser: http://gitorious.org/dma-roam/pkg-debian
Package: dma
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Provides: mail-transport-agent
Conflicts: mail-transport-agent
Replaces: mail-transport-agent
Recommends: dma-migrate
Description: lightweight mail transport agent
The DragonFly Mail Agent is a small Mail Transport Agent (MTA),
designed for home and office use. It accepts mails from local Mail
User Agents (MUA) and delivers them either to local mailboxes or
remote SMTP servers. Remote delivery includes support for features
such as TLS/SSL and SMTP authentication.
.
dma is not intended as a replacement for full-featured MTAs like
Sendmail, Postfix, or Exim. Consequently, dma does not listen on
port 25 for incoming connections.
Package: dma-migrate
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Enhances: dma
Description: migration utility for the DragonFly Mail Agent's spool files
The dma-migrate utility examines the DragonFly Mail Agent's mail queue
and performs any conversions from old message file formats to the most
recent one as needed.

View File

@ -0,0 +1,91 @@
Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
Name: dma
Files: base64.c
Copyright: Copyright (c) 1995-2001 Kungliga Tekniska Högskolan
(Royal Institute of Technology, Stockholm, Sweden).
All rights reserved.
License: BSD-3
Files: conf.c crypto.c net.c
Copyright: 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.
License: BSD-3
Files: dfcompat.c
Copyright: Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
Copyright (c) 1998, M. Warner Losh <imp@freebsd.org> All rights reserved.
License: BSD-1 BSD-2
Files: dma.8
Copyright: Copyright (c) 2008 The DragonFly Project. All rights reserved.
License: BSD-3
Files: dma.c dns.c mail.c spool.c util.c
Copyright: 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>.
License: BSD-3
Files: dma.h
Copyright: 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>.
License: BSD-3
Files: debian/*
Copyright: Copyright (c) 2009, 2010 Peter Pentchev.
License: BSD-3
License: BSD-3
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.
License: BSD-1
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
License: BSD-2
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.
.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.

View File

@ -0,0 +1,2 @@
usr/sbin
usr/share/man/man8

View File

@ -0,0 +1 @@
debian/migrate/dma-migrate usr/sbin

View File

@ -0,0 +1 @@
debian/migrate/dma-migrate.8

View File

@ -0,0 +1,4 @@
etc/dma
usr/sbin
usr/share/lintian/overrides
usr/share/man/man8

View File

@ -0,0 +1,6 @@
usr/sbin/dma usr/bin/mailq
usr/sbin/dma usr/bin/newaliases
usr/sbin/dma usr/sbin/sendmail
usr/share/man/man8/dma.8 usr/share/man/man8/mailq.8
usr/share/man/man8/dma.8 usr/share/man/man8/newaliases.8
usr/share/man/man8/dma.8 usr/share/man/man8/sendmail.8

View File

@ -0,0 +1,8 @@
dma: manpage-has-errors-from-man *Unknown DragonFly version*
dma: non-standard-dir-in-var var/mail/
dma: non-standard-dir-perm var/spool/dma/ 0770 != 0755
dma: non-standard-dir-perm var/spool/dma/ 2775 != 0755
dma: non-standard-file-perm etc/dma/auth.conf 0640 != 0644
dma: no-upstream-changelog
dma: setgid-binary usr/sbin/dma 2755 root/mail
dma: setuid-binary usr/lib/dma-mbox-create 4754 root/mail

View File

@ -0,0 +1,4 @@
all: dma-migrate
clean:
rm -f dma-migrate dma-migrate.o

View File

@ -0,0 +1,6 @@
Change log for dma-migrate, the DragonFly Mail Agent queue migration utility.
0.01 not yet ;)
- first public release
Comments: Peter Pentchev <roam@ringlet.net>

View File

@ -0,0 +1,98 @@
.\" Copyright (c) 2010 Peter Pentchev
.\" 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.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
.\"
.Dd May 11, 2009
.Dt dma-migrate 8
.Os
.Sh NAME
.Nm dma-migrate
.Nd convert the DragonFly Mail Agent's queue files
.Sh SYNOPSIS
.Nm
.Op Fl v
.Op Fl d Ar spooldir
.Nm
.Op Fl h | Fl V
.Sh DESCRIPTION
The
.Nm
utility is used to convert the mail queue files in the
.Xr dma 8
spool directory to the latest spool directory format supported by
the installed version of
.Xr dma 8 .
Currently it only handles the conversion from a single file containing
both message and delivery metadata to the M/Q format.
.Pp
The following command-line options are available:
.Bl -tag -width indent
.It Fl d
Specify the location of the
.Xr dma 8
spool directory, default
.Pa /var/spool/dma .
.It Fl h
Display usage information and exit.
.It Fl V
Display program version information and exit.
.It Fl v
Verbose output - display diagnostic messages.
.El
.Sh ENVIRONMENT
The operation of the
.Nm
utility is currently not influenced by environment variables.
.Sh FILES
The
.Nm
utility looks for the
.Xr dma 8
mail queue files in the
.Pa /var/spool/dma
directory, unless another location is specified by the
.Fl d
command-line option.
.Sh EXIT STATUS
The
.Nm
utility will scan the whole spool directory and attempt to convert all
suitable files there.
If there are no files to be converted, or if all the conversions are
successful, it will complete with an exit code of zero.
If any conversion errors are encountered, a message will be displayed
to the standard error stream and
.Nm
will exit with an exit code of 1 after attempting to convert the rest of
the suitable files in the spool directory.
.Sh SEE ALSO
.Xr dma 8
.Sh STANDARDS
No standards documentation was harmed in the process of creating
.Nm .
.Sh BUGS
Please report any bugs in
.Nm
to the author.
.Sh AUTHOR
.An Peter Pentchev Aq roam@ringlet.net

View File

@ -0,0 +1,413 @@
/*-
* Copyright (c) 2010 Peter Pentchev
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <regex.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifndef __printflike
#ifdef __GNUC__
#define __printflike(fmtarg, firstvararg) \
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
#else
#define __printflike(fmtarg, firstvararg)
#endif
#endif
#define DEFAULT_SPOOLDIR "/var/spool/dma"
static int verbose = 0;
static char copybuf[BUFSIZ];
static int dma_migrate(int, const char *);
static int open_locked(const char *, int, ...);
static void cleanup_file(int, char *);
static void usage(int);
static void version(void);
static void debug(const char *, ...) __printflike(1, 2);
int
main(int argc, char **argv)
{
const char *spooldir;
int hflag, Vflag, errs, fd, res;
int ch;
DIR *d;
struct dirent *e;
struct stat sb;
srandom((unsigned long)((time(NULL) ^ getpid()) + ((uintptr_t)argv)));
hflag = Vflag = 0;
spooldir = DEFAULT_SPOOLDIR;
while (ch = getopt(argc, argv, "d:hVv"), ch != -1)
switch (ch) {
case 'd':
spooldir = optarg;
break;
case 'h':
hflag = 1;
break;
case 'V':
Vflag = 1;
break;
case 'v':
verbose = 1;
break;
case '?':
default:
usage(1);
/* NOTREACHED */
}
if (Vflag)
version();
if (hflag)
usage(0);
if (hflag || Vflag)
exit(0);
argc -= optind;
argv += optind;
/* Let's roll! */
if (chdir(spooldir) == -1)
err(1, "Could not change into spool directory %s", spooldir);
if (d = opendir("."), d == NULL)
err(1, "Could not read spool directory %s", spooldir);
errs = 0;
while (e = readdir(d), e != NULL) {
/* Do we care about this entry? */
debug("Read a directory entry: %s\n", e->d_name);
if (strncmp(e->d_name, "tmp_", 4) == 0 ||
e->d_name[0] == 'M' || e->d_name[0] == 'Q' ||
(e->d_type != DT_REG && e->d_type != DT_UNKNOWN))
continue;
if (e->d_type == DT_UNKNOWN)
if (stat(e->d_name, &sb) == -1 || !S_ISREG(sb.st_mode))
continue;
debug("- want to process it\n");
/* Try to lock it - skip it if dma is delivering the message */
if (fd = open_locked(e->d_name, O_RDONLY|O_NDELAY), fd == -1) {
debug("- seems to be locked, skipping\n");
continue;
}
/* Okay, convert it to the M/Q schema */
res = dma_migrate(fd, e->d_name);
close(fd);
if (res == -1)
errs++;
}
if (errs)
debug("Finished, %d conversion errors\n", errs);
else
debug("Everything seems to be all right\n");
return (errs && 1);
}
static int
dma_migrate(int fd, const char *fname)
{
const char *id;
char *mname, *qname, *tempname, *sender, *recp, *line, *recpline;
int mfd, qfd, tempfd;
struct stat sb;
FILE *fp, *qfp, *mfp;
size_t sz, len;
static regex_t *qidreg = NULL;
mfd = tempfd = qfd = -1;
mname = qname = sender = recp = line = NULL;
fp = qfp = NULL;
if (fstat(fd, &sb) == -1) {
warn("Could not fstat(%s)", fname);
return (-1);
}
/*
* Let's just blithely assume that the queue ID *is* the filename,
* since that's the way dma did things so far.
* Well, okay, let's check it.
*/
if (qidreg == NULL) {
regex_t *nreg;
if ((nreg = malloc(sizeof(*qidreg))) == NULL) {
warn("Could not allocate memory for a regex");
return (-1);
}
if (regcomp(nreg, "^[a-fA-F0-9]\\+\\.[a-fA-F0-9]\\+$", 0)
!= 0) {
warnx("Could not compile a dma queue ID regex");
free(nreg);
return (-1);
}
qidreg = nreg;
}
if (regexec(qidreg, fname, 0, NULL, 0) != 0) {
warnx("The name '%s' is not a valid dma queue ID", fname);
return (-1);
}
id = fname;
debug(" - queue ID %s\n", id);
if (asprintf(&mname, "M%s", id) == -1 ||
asprintf(&tempname, "tmp_%s", id) == -1 ||
asprintf(&qname, "Q%s", id) == -1 ||
mname == NULL || tempname == NULL || qname == NULL)
goto fail;
/* Create the message placeholder early to avoid races */
mfd = open_locked(mname, O_CREAT | O_EXCL | O_RDWR, 0600);
if (mfd == -1) {
warn("Could not create temporary file %s", mname);
goto fail;
}
if (stat(qname, &sb) != -1 || errno != ENOENT ||
stat(tempname, &sb) != -1 || errno != ENOENT) {
warnx("Some of the queue files for %s already exist", fname);
goto fail;
}
debug(" - mfd %d names %s, %s, %s\n", mfd, mname, tempname, qname);
fp = fdopen(fd, "r");
if (fp == NULL) {
warn("Could not reopen the descriptor for %s", fname);
goto fail;
}
/* Parse the header of the old-format message file */
/* ...sender... */
if (getline(&sender, &sz, fp) == -1) {
warn("Could not read the initial line from %s", fname);
goto fail;
}
sz = strlen(sender);
while (sz > 0 && (sender[sz - 1] == '\n' || sender[sz - 1] == '\r'))
sender[--sz] = '\0';
if (sz == 0) {
warnx("Empty sender line in %s", fname);
goto fail;
}
debug(" - sender %s\n", sender);
/* ...recipient(s)... */
len = strlen(fname);
recpline = NULL;
while (1) {
if (getline(&line, &sz, fp) == -1) {
warn("Could not read a recipient line from %s", fname);
goto fail;
}
sz = strlen(line);
while (sz > 0 &&
(line[sz - 1] == '\n' || line[sz - 1] == '\r'))
line[--sz] = '\0';
if (sz == 0) {
free(line);
line = NULL;
break;
}
if (recp == NULL &&
strncmp(line, fname, len) == 0 && line[len] == ' ') {
recp = line + len + 1;
recpline = line;
} else {
free(line);
}
line = NULL;
}
if (recp == NULL) {
warnx("Could not find its own recipient line in %s", fname);
goto fail;
}
/* ..phew, finished with the header. */
tempfd = open_locked(tempname, O_CREAT | O_EXCL | O_RDWR, 0600);
if (tempfd == -1) {
warn("Could not create a queue file for %s", fname);
goto fail;
}
qfp = fdopen(tempfd, "w");
if (qfp == NULL) {
warn("Could not fdopen(%s) for %s", tempname, fname);
goto fail;
}
mfp = fdopen(mfd, "w");
if (mfp == NULL) {
warn("Could not fdopen(%s) for %s", mname, fname);
goto fail;
}
fprintf(qfp, "ID: %s\nSender: %s\nRecipient: %s\n", id, sender, recp);
fflush(qfp);
fsync(tempfd);
/* Copy the message file over to mname */
while ((sz = fread(copybuf, 1, sizeof(copybuf), fp)) > 0)
if (fwrite(copybuf, 1, sz, mfp) != sz) {
warn("Could not copy the message from %s to %s",
fname, mname);
goto fail;
}
if (ferror(fp)) {
warn("Could not read the full message from %s", fname);
goto fail;
}
fflush(mfp);
fsync(mfd);
if (rename(tempname, qname) == -1) {
warn("Could not rename the queue file for %s", fname);
goto fail;
}
qfd = tempfd;
tempfd = -1;
if (unlink(fname) == -1) {
warn("Could not remove the old converted file %s", fname);
goto fail;
}
fclose(fp);
fclose(qfp);
free(sender);
free(line);
free(recpline);
free(mname);
free(qname);
free(tempname);
return (0);
fail:
if (fp != NULL)
fclose(fp);
if (qfp != NULL)
fclose(qfp);
if (sender != NULL)
free(sender);
if (line != NULL)
free(line);
if (recpline != NULL)
free(recpline);
cleanup_file(mfd, mname);
cleanup_file(qfd, qname);
cleanup_file(tempfd, tempname);
return (-1);
}
static void
cleanup_file(int fd, char *fname)
{
if (fd != -1) {
close(fd);
unlink(fname);
}
if (fname != NULL)
free(fname);
}
static void
usage(int ferr)
{
const char *s =
"Usage:\tdma-migrate [-hVv] [-d spooldir]\n"
"\t-d\tspecify the spool directory (" DEFAULT_SPOOLDIR ")\n"
"\t-h\tdisplay program usage information and exit\n"
"\t-V\tdisplay program version information and exit\n"
"\t-v\tverbose operation - display diagnostic messages";
if (ferr)
errx(1, "%s", s);
puts(s);
}
static void
version(void)
{
printf("dma-migrate 0.01 (dma 0.0.2010.06.17)\n");
}
static void
debug(const char *fmt, ...)
{
va_list v;
if (verbose < 1)
return;
va_start(v, fmt);
vfprintf(stderr, fmt, v);
va_end(v);
}
static int
open_locked(const char *fname, int flags, ...)
{
int mode = 0;
#ifndef O_EXLOCK
int fd, save_errno;
#endif
if (flags & O_CREAT) {
va_list ap;
va_start(ap, flags);
mode = va_arg(ap, int);
va_end(ap);
}
#ifndef O_EXLOCK
fd = open(fname, flags, mode);
if (fd < 0)
return(fd);
if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) {
save_errno = errno;
close(fd);
errno = save_errno;
return(-1);
}
return(fd);
#else
return(open(fname, flags|O_EXLOCK, mode));
#endif
}

49
contrib/dma/debian/rules Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Debian build rules for dma, the DragonFly mail agent
DDIR= $(CURDIR)/debian
D= $(DDIR)/dma
BUILDDEFS= DESTDIR=$D PREFIX=/usr
CFLAGS:= $(shell dpkg-buildflags --get CFLAGS)
CPPFLAGS:= $(shell dpkg-buildflags --get CPPFLAGS)
LDFLAGS:= $(shell dpkg-buildflags --get LDFLAGS)
CONFFILES= dma.conf auth.conf
ifneq (,$(filter werror,$(DEB_BUILD_OPTIONS)))
CFLAGS+= -Werror
endif
ifneq (,$(filter nostrip,$(DEB_BUILD_OPTIONS)))
export STRIPFLAG=
endif
ifneq (,$(filter hardening,$(DEB_BUILD_OPTIONS)))
export DEB_BUILD_HARDENING=1
else
export DEB_BUILD_HARDENING=0
endif
export CFLAGS CPPFLAGS LDFLAGS
override_dh_auto_build:
$(MAKE) -f Makefile ${BUILDDEFS}
$(MAKE) -C $(DDIR)/migrate
override_dh_auto_clean:
$(MAKE) -f Makefile clean
$(MAKE) -C $(DDIR)/migrate clean
override_dh_auto_install:
$(MAKE) -f Makefile ${BUILDDEFS} install sendmail-link mailq-link install-spool-dirs install-etc
override_dh_fixperms:
dh_fixperms -Xusr/sbin/dma -Xusr/lib/dma-mbox-create -Xvar/spool/dma -Xetc/dma
override_dh_installchangelogs:
dh_installchangelogs -p dma
dh_installchangelogs -p dma-migrate debian/migrate/NEWS
%:
dh $@

View File

@ -0,0 +1 @@
3.0 (native)

View File

@ -0,0 +1,2 @@
compression = "bzip2"
compression-level = 9

122
contrib/dma/dfcompat.c Normal file
View File

@ -0,0 +1,122 @@
#ifndef HAVE_STRLCPY
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $
* $FreeBSD$
* $DragonFly: src/lib/libc/string/strlcpy.c,v 1.4 2005/09/18 16:32:34 asmodai Exp $
*/
#include "dfcompat.h"
#include <sys/types.h>
#include <string.h>
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
#endif /* !HAVE_STRLCPY */
#ifndef HAVE_REALLOCF
/*-
* Copyright (c) 1998, M. Warner Losh <imp@freebsd.org>
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $FreeBSD$
* $DragonFly: src/lib/libc/stdlib/reallocf.c,v 1.2 2003/06/17 04:26:46 dillon Exp $
*/
#include <stdlib.h>
void *
reallocf(void *ptr, size_t size)
{
void *nptr;
nptr = realloc(ptr, size);
if (!nptr && ptr)
free(ptr);
return (nptr);
}
#endif /* !HAVE_REALLOCF */
#ifndef HAVE_GETPROGNAME
#ifdef __GLIBC__
#include <errno.h>
const char *
getprogname(void)
{
return (program_invocation_short_name);
}
#else /* __GLIBC__ */
#error "no getprogname implementation available"
#endif
#endif /* !HAVE_GETPROGNAME */

24
contrib/dma/dfcompat.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef DFCOMPAT_H
#define DFCOMPAT_H
#define _GNU_SOURCE
#include <sys/types.h>
#ifndef __DECONST
#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char *, const char *, size_t);
#endif
#ifndef HAVE_REALLOCF
void *reallocf(void *, size_t);
#endif
#ifndef HAVE_GETPROGNAME
const char *getprogname(void);
#endif
#endif /* DFCOMPAT_H */

View File

@ -0,0 +1,160 @@
/*
* Copyright (c) 2010 Simon Schubert <2@0x2c.org>
* 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.
*/
/*
* This binary is setuid root. Use extreme caution when touching
* user-supplied information. Keep the root window as small as possible.
*/
#include <sys/param.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <paths.h>
#include <pwd.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include "dma.h"
static void
logfail(const char *fmt, ...)
{
int oerrno = errno;
va_list ap;
char outs[1024];
outs[0] = 0;
if (fmt != NULL) {
va_start(ap, fmt);
vsnprintf(outs, sizeof(outs), fmt, ap);
va_end(ap);
}
errno = oerrno;
if (*outs != 0)
syslog(LOG_ERR, errno ? "%s: %m" : "%s", outs);
else
syslog(LOG_ERR, errno ? "%m" : "unknown error");
exit(1);
}
/*
* Create a mbox in /var/mail for a given user, or make sure
* the permissions are correct for dma.
*/
int
main(int argc, char **argv)
{
const char *user;
struct passwd *pw;
struct group *gr;
uid_t user_uid;
gid_t mail_gid;
int error;
char fn[PATH_MAX+1];
int f;
openlog("dma-mbox-create", 0, LOG_MAIL);
errno = 0;
gr = getgrnam(DMA_GROUP);
if (!gr)
logfail("cannot find dma group `%s'", DMA_GROUP);
mail_gid = gr->gr_gid;
if (setgid(mail_gid) != 0)
logfail("cannot set gid to %d (%s)", mail_gid, DMA_GROUP);
if (getegid() != mail_gid)
logfail("cannot set gid to %d (%s), still at %d", mail_gid, DMA_GROUP, getegid());
/*
* We take exactly one argument: the username.
*/
if (argc != 2) {
errno = 0;
logfail("no arguments");
}
user = argv[1];
syslog(LOG_NOTICE, "creating mbox for `%s'", user);
/* the username may not contain a pathname separator */
if (strchr(user, '/')) {
errno = 0;
logfail("path separator in username `%s'", user);
exit(1);
}
/* verify the user exists */
errno = 0;
pw = getpwnam(user);
if (!pw)
logfail("cannot find user `%s'", user);
user_uid = pw->pw_uid;
error = snprintf(fn, sizeof(fn), "%s/%s", _PATH_MAILDIR, user);
if (error < 0 || (size_t)error >= sizeof(fn)) {
if (error >= 0) {
errno = 0;
logfail("mbox path too long");
}
logfail("cannot build mbox path for `%s/%s'", _PATH_MAILDIR, user);
}
f = open(fn, O_RDONLY|O_CREAT, 0600);
if (f < 0)
logfail("cannot open mbox `%s'", fn);
if (fchown(f, user_uid, mail_gid))
logfail("cannot change owner of mbox `%s'", fn);
if (fchmod(f, 0620))
logfail("cannot change permissions of mbox `%s'", fn);
/* file should be present with the right owner and permissions */
syslog(LOG_NOTICE, "successfully created mbox for `%s'", user);
return (0);
}

363
contrib/dma/dma.8 Normal file
View File

@ -0,0 +1,363 @@
.\"
.\" 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.
.\"
.Dd February 13, 2014
.Dt DMA 8
.Os
.Sh NAME
.Nm dma
.Nd DragonFly Mail Agent
.Sh SYNOPSIS
.Nm
.Op Fl DiOt
.Op Fl A Ns Ar mode
.Op Fl b Ns Ar mode
.Op Fl f Ar sender
.Op Fl L Ar tag
.Op Fl o Ns Ar option
.Op Fl r Ar sender
.Op Fl q Ns Op Ar arg
.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.
.Pp
.Nm
is not intended as a replacement for real, big MTAs like
.Xr sendmail 8
or
.Xr postfix 1 .
Consequently,
.Nm
does not listen on port 25 for incoming connections.
.Pp
The options are as follows:
.Bl -tag -width indent
.It Fl A Ns Ar mode
.Fl \&Ac
acts as a compatibility option for sendmail.
.It Fl b Ns Ar mode
.Bl -tag -width indent
.It Fl bp
List all mails currently stored in the mail queue.
.It Fl bq
Queue the mail, but don't attempt to deliver it.
See also the
.Sq DEFER
config file setting below.
.El
.Pp
All other
.Ar mode Ns
s are are ignored.
.It Fl D
Don't run in the background.
Useful for debugging.
.It Fl f Ar sender
Set sender address (envelope-from) to
.Ar sender .
This overrides the value of the environment variable
.Ev EMAIL .
.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
This is a compatibility option for sendmail.
.It Fl o Ns Ar option
Specifying
.Fl oi
is synonymous to
.Fl i .
All other options are ignored.
.It Fl q Ns Op Ar arg
Process saved messages in the queue.
The argument is optional and ignored.
.It Fl r Ar sender
Same as
.Fl f .
.It Fl t
Obtain recipient addresses from the message header.
.Nm
will parse the
.Li To: ,
.Li Cc: ,
and
.Li Bcc:
headers.
The
.Li Bcc:
header will be removed independent of whether
.Fl t
is specified or not.
.El
.Sh CONFIGURATION
.Nm
can be configured with two config files:
.Pp
.Bl -bullet -compact
.It
auth.conf
.It
dma.conf
.El
.Pp
These two files are stored per default in
.Pa /etc/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 .
.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 of whether a smarthost is set
or not.
.It Ic ALIASES Xo
(string, default=/etc/aliases)
.Xc
Path to the local aliases file.
Just stick with the default.
The aliases file is of the format
.Dl nam: dest1 dest2 ...
In this case, mails to
.Li nam
will instead be delivered to
.Li dest1
and
.Li dest2 ,
which in turn could be entries in
.Pa /etc/aliases .
The special name
.Ql *
can be used to create a catch-all alias, which gets used if no other
matching alias is found.
Use the catch-all alias only if you don't want any local mail to be
delivered.
.It Ic SPOOLDIR Xo
(string, default=/var/spool/dma)
.Xc
Path to
.Nm Ap s
spool directory.
Just stick with the default.
.It Ic AUTHPATH Xo
(string, default=not set)
.Xc
Path to the
.Sq auth.conf
file.
.It Ic SECURETRANS Xo
(boolean, default=commented)
.Xc
Uncomment if you want TLS/SSL secured transfer.
.It Ic STARTTLS Xo
(boolean, default=commented)
.Xc
Uncomment if you want to use STARTTLS.
Only useful together with
.Sq SECURETRANS .
.It Ic OPPORTUNISTIC_TLS Xo
(boolean, default=commented)
.Xc
Uncomment if you want to allow the STARTTLS negotiation to fail.
Most useful when
.Nm
is used without a smarthost, delivering remote messages directly to
the outside mail exchangers; in opportunistic TLS mode, the connection will
be encrypted if the remote server supports STARTTLS, but an unencrypted
delivery will still be made if the negotiation fails.
Only useful together with
.Sq SECURETRANS
and
.Sq STARTTLS .
.It Ic CERTFILE Xo
(string, default=empty)
.Xc
Path to your SSL certificate file.
.It Ic SECURE Xo
(boolean, default=commented)
.Xc
Uncomment this entry and change it to
.Sq INSECURE
to use plain text SMTP login over an insecure connection.
You have to rename this variable manually to prevent that you send your
password accidentally over an insecure connection.
.It Ic DEFER Xo
(boolean, default=commented)
.Xc
Uncomment 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.
.It Ic FULLBOUNCE Xo
(boolean, default=commented)
.Xc
Uncomment if you want the bounce message to include the complete original
message, not just the headers.
.It Ic MAILNAME Xo
(string, default=empty)
.Xc
The internet hostname
.Nm
uses to identify the host.
If not set or empty, the result of
.Xr gethostname 3
is used.
If
.Sq MAILNAME
is an absolute path to a file, the first line of this file will be used
as the hostname.
.It Ic MASQUERADE Xo
(string, default=empty)
.Xc
Masquerade the envelope-from addresses with this address/hostname.
Use this setting if mails are not accepted by destination mail servers
because your sender domain is invalid.
This setting is overridden by the
.Fl f
flag and the
.Ev EMAIL
environment variable.
.Pp
If
.Sq MASQUERADE
does not contain a
.Li @
sign, the string is interpreted as a host name.
For example, setting
.Sq MASQUERADE
to
.Ql john@
on host
.Ql hamlet
will send all mails as
.Ql john@hamlet ;
setting it to
.Ql percolator
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
.Nm
can be influenced by some environment variables.
.Bl -tag -width 4n
.It Ev EMAIL Xo
.Xc
Used to set the sender address (envelope-from).
Use a plain address, in the form of
.Li user@example.com .
This value will be overridden when the
.Fl f
flag is used.
.El
.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
.An -nosplit
.Nm
was written by
.An Matthias Schmidt Aq Mt matthias@dragonflybsd.org
and
.An Simon Schubert Aq Mt 2@0x2c.org .

631
contrib/dma/dma.c Normal file
View File

@ -0,0 +1,631 @@
/*
* 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.
*/
#include "dfcompat.h"
#include <sys/param.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.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 *);
struct aliases aliases = LIST_HEAD_INITIALIZER(aliases);
struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs);
struct authusers authusers = LIST_HEAD_INITIALIZER(authusers);
char username[USERNAME_SIZE];
uid_t useruid;
const char *logident_base;
char errmsg[ERRMSG_SIZE];
static int daemonize = 1;
static int doqueue = 0;
struct config config = {
.smarthost = NULL,
.port = 25,
.aliases = "/etc/aliases",
.spooldir = "/var/spool/dma",
.authpath = NULL,
.certfile = NULL,
.features = 0,
.mailname = NULL,
.masquerade_host = NULL,
.masquerade_user = NULL,
};
static void
sighup_handler(int signo)
{
(void)signo; /* so that gcc doesn't complain */
}
static char *
set_from(struct queue *queue, const char *osender)
{
const char *addr;
char *sender;
if (osender) {
addr = osender;
} else if (getenv("EMAIL") != NULL) {
addr = getenv("EMAIL");
} else {
if (config.masquerade_user)
addr = config.masquerade_user;
else
addr = username;
}
if (!strchr(addr, '@')) {
const char *from_host = hostname();
if (config.masquerade_host)
from_host = config.masquerade_host;
if (asprintf(&sender, "%s@%s", addr, from_host) <= 0)
return (NULL);
} else {
sender = strdup(addr);
if (sender == NULL)
return (NULL);
}
if (strchr(sender, '\n') != NULL) {
errno = EINVAL;
return (NULL);
}
queue->sender = sender;
return (sender);
}
static int
read_aliases(void)
{
yyin = fopen(config.aliases, "r");
if (yyin == NULL) {
/*
* Non-existing aliases file is not a fatal error
*/
if (errno == ENOENT)
return (0);
/* Other problems are. */
return (-1);
}
if (yyparse())
return (-1); /* fatal error, probably malloc() */
fclose(yyin);
return (0);
}
static int
do_alias(struct queue *queue, const char *addr)
{
struct alias *al;
struct stritem *sit;
int aliased = 0;
LIST_FOREACH(al, &aliases, next) {
if (strcmp(al->alias, addr) != 0)
continue;
SLIST_FOREACH(sit, &al->dests, next) {
if (add_recp(queue, sit->str, EXPAND_ADDR) != 0)
return (-1);
}
aliased = 1;
}
return (aliased);
}
int
add_recp(struct queue *queue, const char *str, int expand)
{
struct qitem *it, *tit;
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 = queue->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);
/**
* 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);
if (!aliased && expand == EXPAND_WILDCARD)
aliased = do_alias(queue, "*");
if (aliased < 0)
return (-1);
if (aliased) {
LIST_REMOVE(it, next);
} else {
/* Local destination, check */
pw = getpwnam(it->addr);
if (pw == NULL)
goto out;
/* XXX read .forward */
endpwent();
}
}
} else {
it->remote = 1;
}
return (0);
out:
free(it->addr);
free(it);
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_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)
goto retit;
pid = fork();
switch (pid) {
case -1:
syslog(LOG_ERR, "can not fork: %m");
exit(1);
break;
case 0:
/*
* Child:
*
* return and deliver mail
*/
retit:
/*
* If necessary, acquire the queue and * mail files.
* If this fails, we probably were raced by another
* process. It is okay to be raced if we're supposed
* to flush the queue.
*/
setlogident("%s", it->queueid);
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);
default:
/*
* Parent:
*
* fork next child
*/
break;
}
}
syslog(LOG_CRIT, "reached dead code");
exit(1);
}
static void
deliver(struct qitem *it)
{
int error;
unsigned int backoff = MIN_RETRY, slept;
struct timeval now;
struct stat st;
snprintf(errmsg, sizeof(errmsg), "unknown bounce reason");
retry:
syslog(LOG_INFO, "trying delivery");
if (it->remote)
error = deliver_remote(it);
else
error = deliver_local(it);
switch (error) {
case 0:
delqueue(it);
syslog(LOG_INFO, "delivery successful");
exit(0);
case 1:
if (stat(it->queuefn, &st) != 0) {
syslog(LOG_ERR, "lost queue file `%s'", it->queuefn);
exit(1);
}
if (gettimeofday(&now, NULL) == 0 &&
(now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) {
snprintf(errmsg, sizeof(errmsg),
"Could not deliver for the last %d seconds. Giving up.",
MAX_TIMEOUT);
goto bounce;
}
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)
backoff = MAX_RETRY;
}
goto retry;
case -1:
default:
break;
}
bounce:
bounce(it, errmsg);
/* NOTREACHED */
}
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;
int locked = 0; /* XXX */
if (LIST_EMPTY(&queue->queue)) {
printf("Mail queue is empty\n");
return;
}
LIST_FOREACH(it, &queue->queue, next) {
printf("ID\t: %s%s\n"
"From\t: %s\n"
"To\t: %s\n",
it->queueid,
locked ? "*" : "",
it->sender, it->addr);
if (LIST_NEXT(it, next) != NULL)
printf("--\n");
}
}
/*
* TODO:
*
* - alias processing
* - use group permissions
* - proper sysexit codes
*/
int
main(int argc, char **argv)
{
struct sigaction act;
char *sender = NULL;
struct queue queue;
int i, ch;
int nodot = 0, showq = 0, queue_only = 0;
int recp_from_header = 0;
set_username();
/*
* We never run as root. If called by root, drop permissions
* to the mail user.
*/
if (geteuid() == 0 || getuid() == 0) {
struct passwd *pw;
errno = 0;
pw = getpwnam(DMA_ROOT_USER);
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");
if (geteuid() == 0 || getuid() == 0)
errx(1, "cannot drop root privileges");
}
atexit(deltmp);
init_random();
bzero(&queue, sizeof(queue));
LIST_INIT(&queue.queue);
if (strcmp(argv[0], "mailq") == 0) {
argv++; argc--;
showq = 1;
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;
while ((ch = getopt(argc, argv, ":A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:tUV:vX:")) != -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 if (optarg[0] == 'q') {
queue_only = 1;
break;
}
/* else FALLTRHOUGH */
case 'D':
daemonize = 0;
break;
case 'L':
logident_base = optarg;
break;
case 'f':
case 'r':
sender = optarg;
break;
case 't':
recp_from_header = 1;
break;
case 'o':
/* -oX is being ignored, except for -oi */
if (optarg[0] != 'i')
break;
/* else FALLTRHOUGH */
case 'O':
break;
case 'i':
nodot = 1;
break;
case 'q':
/* Don't let getopt slup up other arguments */
if (optarg && *optarg == '-')
optind--;
doqueue = 1;
break;
/* Ignored options */
case 'B':
case 'C':
case 'd':
case 'F':
case 'h':
case 'N':
case 'n':
case 'R':
case 'U':
case 'V':
case 'v':
case 'X':
break;
case ':':
if (optopt == 'q') {
doqueue = 1;
break;
}
/* FALLTHROUGH */
default:
fprintf(stderr, "invalid argument: `-%c'\n", optopt);
exit(1);
}
}
argc -= optind;
argv += optind;
opterr = 1;
if (argc != 0 && (showq || doqueue))
errx(1, "sending mail and queue operations are mutually exclusive");
if (showq + doqueue > 1)
errx(1, "conflicting queue operations");
skipopts:
if (logident_base == NULL)
logident_base = "dma";
setlogident(NULL);
act.sa_handler = sighup_handler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
if (sigaction(SIGHUP, &act, NULL) != 0)
syslog(LOG_WARNING, "can not set signal handler: %m");
parse_conf(CONF_PATH "/dma.conf");
if (config.authpath != NULL)
parse_authfile(config.authpath);
if (showq) {
if (load_queue(&queue) < 0)
errlog(1, "can not load queue");
show_queue(&queue);
return (0);
}
if (doqueue) {
flushqueue_signal();
if (load_queue(&queue) < 0)
errlog(1, "can not load queue");
run_queue(&queue);
return (0);
}
if (read_aliases() != 0)
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 in `%s'", config.spooldir);
setlogident("%s", queue.id);
for (i = 0; i < argc; i++) {
if (add_recp(&queue, argv[i], EXPAND_WILDCARD) != 0)
errlogx(1, "invalid recipient `%s'", argv[i]);
}
if (LIST_EMPTY(&queue.queue) && !recp_from_header)
errlogx(1, "no recipients");
if (readmail(&queue, nodot, recp_from_header) != 0)
errlog(1, "can not read mail");
if (LIST_EMPTY(&queue.queue))
errlogx(1, "no recipients");
if (linkspool(&queue) != 0)
errlog(1, "can not create spools");
/* From here on the mail is safe. */
if (config.features & DEFER || queue_only)
return (0);
run_queue(&queue);
/* NOTREACHED */
return (0);
}

66
contrib/dma/dma.conf Normal file
View File

@ -0,0 +1,66 @@
# $DragonFly: src/etc/dma/dma.conf,v 1.2 2008/02/04 10:11:41 matthias Exp $
#
# Your smarthost (also called relayhost). Leave blank if you don't want
# smarthost support.
# NOTE: on Debian systems this is handled via debconf!
# Please use dpkg-reconfigure dma to change this value.
#SMARTHOST
# Use this SMTP port. Most users will be fine with the default (25)
#PORT 25
# Path to your alias file. Just stay with the default.
#ALIASES /etc/aliases
# Path to your spooldir. Just stay with the default.
#SPOOLDIR /var/spool/dma
# SMTP authentication
#AUTHPATH /etc/dma/auth.conf
# Uncomment if yout want TLS/SSL support
#SECURETRANSFER
# Uncomment if you want STARTTLS support (only used in combination with
# SECURETRANSFER)
#STARTTLS
# Uncomment if you have specified STARTTLS above and it should be allowed
# to fail ("opportunistic TLS", use an encrypted connection when available
# but allow an unencrypted one to servers that do not support it)
#OPPORTUNISTIC_TLS
# Path to your local SSL certificate
#CERTFILE
# If you want to use plain text SMTP login without using encryption, change
# the SECURE entry below to INSECURE. Otherwise plain login will only work
# over a secure connection. Use this option with caution.
#SECURE
# Uncomment if you want to defer your mails. This is useful if you are
# behind a dialup line. You have to submit your mails manually with dma -q
#DEFER
# Uncomment if you want the bounce message to include the complete original
# message, not just the headers.
#FULLBOUNCE
# The internet hostname dma uses to identify the host.
# If not set or empty, the result of gethostname(2) is used.
# If MAILNAME is an absolute path to a file, the first line of this file
# will be used as the hostname.
#MAILNAME mail.example.net
# Masquerade envelope from addresses with this address/hostname.
# Use this if mails are not accepted by destination mail servers because
# your sender domain is invalid.
# By default, MASQUERADE is not set.
# Format: MASQUERADE [user@][host]
# Examples:
# 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

237
contrib/dma/dma.h Normal file
View File

@ -0,0 +1,237 @@
/*
* 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.
*/
#ifndef DMA_H
#define DMA_H
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <netdb.h>
#define VERSION "DragonFly Mail Agent " DMA_VERSION
#define BUF_SIZE 2048
#define ERRMSG_SIZE 200
#define USERNAME_SIZE 50
#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
#define SMTP_PORT 25 /* Default SMTP port */
#define CON_TIMEOUT (5*60) /* Connection timeout per RFC5321 */
#define STARTTLS 0x002 /* StartTLS support */
#define SECURETRANS 0x004 /* SSL/TLS in general */
#define NOSSL 0x008 /* Do not use SSL */
#define DEFER 0x010 /* Defer mails */
#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
#endif
#ifndef LIBEXEC_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
#endif
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 *mailfn;
char *queueid;
FILE *queuef;
FILE *mailf;
int remote;
};
LIST_HEAD(queueh, qitem);
struct queue {
struct queueh queue;
char *id;
FILE *mailf;
char *tmpf;
const char *sender;
};
struct config {
const char *smarthost;
int port;
const char *aliases;
const char *spooldir;
const char *authpath;
const char *certfile;
int features;
const char *mailname;
const char *masquerade_host;
const char *masquerade_user;
/* XXX does not belong into config */
SSL *ssl;
};
struct authuser {
SLIST_ENTRY(authuser) next;
char *login;
char *password;
char *host;
};
SLIST_HEAD(authusers, authuser);
struct mx_hostentry {
char host[MAXDNAME];
char addr[INET6_ADDRSTRLEN];
int pref;
struct addrinfo ai;
struct sockaddr_storage sa;
};
/* global variables */
extern struct aliases aliases;
extern struct config config;
extern struct strlist tmpfs;
extern struct authusers authusers;
extern char username[USERNAME_SIZE];
extern uid_t useruid;
extern const char *logident_base;
extern char neterr[ERRMSG_SIZE];
extern char errmsg[ERRMSG_SIZE];
/* aliases_parse.y */
int yyparse(void);
extern FILE *yyin;
/* conf.c */
void trim_line(char *);
void parse_conf(const char *);
void parse_authfile(const char *);
/* crypto.c */
void hmac_md5(unsigned char *, int, unsigned char *, int, unsigned char *);
int smtp_auth_md5(int, char *, char *);
int smtp_init_crypto(int, int);
/* dns.c */
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*, ...) __attribute__((__nonnull__(2), __format__ (__printf__, 2, 3)));
int deliver_remote(struct qitem *);
/* base64.c */
int base64_encode(const void *, int, char **);
int base64_decode(const char *, void *);
/* dma.c */
#define EXPAND_ADDR 1
#define EXPAND_WILDCARD 2
int add_recp(struct queue *, const char *, int);
void run_queue(struct queue *);
/* spool.c */
int newspoolf(struct queue *);
int linkspool(struct queue *);
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 *);
/* mail.c */
void bounce(struct qitem *, const char *);
int readmail(struct queue *, int, int);
/* util.c */
const char *hostname(void);
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);
int open_locked(const char *, int, ...);
char *rfc822date(void);
int strprefixcmp(const char *, const char *);
void init_random(void);
#endif

280
contrib/dma/dns.c Normal file
View File

@ -0,0 +1,280 @@
/*
* 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.
*/
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <errno.h>
#include <netdb.h>
#include <resolv.h>
#include <string.h>
#include <stdlib.h>
#include "dma.h"
static int
sort_pref(const void *a, const void *b)
{
const struct mx_hostentry *ha = a, *hb = b;
int v;
/* sort increasing by preference primarily */
v = ha->pref - hb->pref;
if (v != 0)
return (v);
/* sort PF_INET6 before PF_INET */
v = - (ha->ai.ai_family - hb->ai.ai_family);
return (v);
}
static int
add_host(int pref, const char *host, int port, struct mx_hostentry **he, size_t *ps)
{
struct addrinfo hints, *res, *res0 = NULL;
char servname[10];
struct mx_hostentry *p;
const int count_inc = 10;
int err;
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);
err = getaddrinfo(host, servname, &hints, &res0);
if (err)
return (err == EAI_AGAIN ? 1 : -1);
for (res = res0; res != NULL; res = res->ai_next) {
if (*ps + 1 >= roundup(*ps, count_inc)) {
size_t newsz = roundup(*ps + 2, count_inc);
*he = reallocf(*he, newsz * sizeof(**he));
if (*he == NULL)
goto out;
}
p = &(*he)[*ps];
strlcpy(p->host, host, sizeof(p->host));
p->pref = pref;
p->ai = *res;
p->ai.ai_addr = NULL;
bcopy(res->ai_addr, &p->sa, p->ai.ai_addrlen);
getnameinfo((struct sockaddr *)&p->sa, p->ai.ai_addrlen,
p->addr, sizeof(p->addr),
NULL, 0, NI_NUMERICHOST);
(*ps)++;
}
freeaddrinfo(res0);
return (0);
out:
if (res0 != NULL)
freeaddrinfo(res0);
return (1);
}
int
dns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx)
{
char outname[MAXDNAME];
ns_msg msg;
ns_rr rr;
const char *searchhost;
const unsigned char *cp;
unsigned char *ans;
struct mx_hostentry *hosts = NULL;
size_t nhosts = 0;
size_t anssz;
int pref;
int cname_recurse;
int have_mx = 0;
int err;
int i;
res_init();
searchhost = host;
cname_recurse = 0;
anssz = 65536;
ans = malloc(anssz);
if (ans == NULL)
return (1);
if (no_mx)
goto out;
repeat:
err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz);
if (err < 0) {
switch (h_errno) {
case NO_DATA:
/*
* Host exists, but no MX (or CNAME) entry.
* Not an error, use host name instead.
*/
goto out;
case TRY_AGAIN:
/* transient error */
goto transerr;
case NO_RECOVERY:
case HOST_NOT_FOUND:
default:
errno = ENOENT;
goto err;
}
}
if (!ns_initparse(ans, anssz, &msg))
goto transerr;
switch (ns_msg_getflag(msg, ns_f_rcode)) {
case ns_r_noerror:
break;
case ns_r_nxdomain:
goto err;
default:
goto transerr;
}
for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) {
if (ns_parserr(&msg, ns_s_an, i, &rr))
goto transerr;
cp = ns_rr_rdata(rr);
switch (ns_rr_type(rr)) {
case ns_t_mx:
have_mx = 1;
pref = ns_get16(cp);
cp += 2;
err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
cp, outname, sizeof(outname));
if (err < 0)
goto transerr;
err = add_host(pref, outname, port, &hosts, &nhosts);
if (err == -1)
goto err;
break;
case ns_t_cname:
err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
cp, outname, sizeof(outname));
if (err < 0)
goto transerr;
/* Prevent a CNAME loop */
if (cname_recurse++ > 10)
goto err;
searchhost = outname;
goto repeat;
default:
break;
}
}
out:
err = 0;
if (0) {
transerr:
if (nhosts == 0)
err = 1;
}
if (0) {
err:
err = -1;
}
free(ans);
if (err == 0) {
if (!have_mx) {
/*
* If we didn't find any MX, use the hostname instead.
*/
err = add_host(0, host, port, &hosts, &nhosts);
} else if (nhosts == 0) {
/*
* We did get MX, but couldn't resolve any of them
* due to transient errors.
*/
err = 1;
}
}
if (nhosts > 0) {
qsort(hosts, nhosts, sizeof(*hosts), sort_pref);
/* terminate list */
*hosts[nhosts].host = 0;
} else {
if (hosts != NULL)
free(hosts);
hosts = NULL;
}
*he = hosts;
return (err);
free(ans);
if (hosts != NULL)
free(hosts);
return (err);
}
#if defined(TESTING)
int
main(int argc, char **argv)
{
struct mx_hostentry *he, *p;
int err;
err = dns_get_mx_list(argv[1], 53, &he, 0);
if (err)
return (err);
for (p = he; *p->host != 0; p++) {
printf("%d\t%s\t%s\n", p->pref, p->host, p->addr);
}
return (0);
}
#endif

9
contrib/dma/get-version.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
gitver=$(git describe 2>/dev/null | tr - .)
filever=$(cat VERSION)
version=${gitver}
: ${version:=$filever}
echo "$version"

253
contrib/dma/local.c Normal file
View File

@ -0,0 +1,253 @@
/*
* 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.
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include "dma.h"
static int
create_mbox(const char *name)
{
struct sigaction sa, osa;
pid_t child, waitchild;
int status;
int i;
long maxfd;
int e;
int r = -1;
/*
* We need to enable SIGCHLD temporarily so that waitpid works.
*/
bzero(&sa, sizeof(sa));
sa.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &sa, &osa);
do_timeout(100, 0);
child = fork();
switch (child) {
case 0:
/* child */
maxfd = sysconf(_SC_OPEN_MAX);
if (maxfd == -1)
maxfd = 1024; /* what can we do... */
for (i = 3; i <= maxfd; ++i)
close(i);
execl(LIBEXEC_PATH "/dma-mbox-create", "dma-mbox-create", name, NULL);
syslog(LOG_ERR, "cannot execute "LIBEXEC_PATH"/dma-mbox-create: %m");
exit(1);
default:
/* parent */
waitchild = waitpid(child, &status, 0);
e = errno;
do_timeout(0, 0);
if (waitchild == -1 && e == EINTR) {
syslog(LOG_ERR, "hung child while creating mbox `%s': %m", name);
break;
}
if (waitchild == -1) {
syslog(LOG_ERR, "child disappeared while creating mbox `%s': %m", name);
break;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
syslog(LOG_ERR, "error creating mbox `%s'", name);
break;
}
/* success */
r = 0;
break;
case -1:
/* error */
syslog(LOG_ERR, "error creating mbox");
break;
}
sigaction(SIGCHLD, &osa, NULL);
return (r);
}
int
deliver_local(struct qitem *it)
{
char fn[PATH_MAX+1];
char line[1000];
const char *sender;
const char *newline = "\n";
size_t linelen;
int tries = 0;
int mbox;
int error;
int hadnl = 0;
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_NOTICE, "local delivery deferred: %m");
return (1);
}
retry:
/* wait for a maximum of 100s to get the lock to the file */
do_timeout(100, 0);
/* don't use O_CREAT here, because we might be running as the wrong user. */
mbox = open_locked(fn, O_WRONLY|O_APPEND);
if (mbox < 0) {
int e = errno;
do_timeout(0, 0);
switch (e) {
case EACCES:
case ENOENT:
/*
* The file does not exist or we can't access it.
* Call dma-mbox-create to create it and fix permissions.
*/
if (tries > 0 || create_mbox(it->addr) != 0) {
syslog(LOG_ERR, "local delivery deferred: can not create `%s'", fn);
return (1);
}
++tries;
goto retry;
case EINTR:
syslog(LOG_NOTICE, "local delivery deferred: can not lock `%s'", fn);
break;
default:
syslog(LOG_NOTICE, "local delivery deferred: can not open `%s': %m", fn);
break;
}
return (1);
}
do_timeout(0, 0);
mboxlen = lseek(mbox, 0, SEEK_END);
/* New mails start with \nFrom ...., unless we're at the beginning of the mbox */
if (mboxlen == 0)
newline = "";
/* If we're bouncing a message, claim it comes from MAILER-DAEMON */
sender = it->sender;
if (strcmp(sender, "") == 0)
sender = "MAILER-DAEMON";
if (fseek(it->mailf, 0, SEEK_SET) != 0) {
syslog(LOG_NOTICE, "local delivery deferred: can not seek: %m");
goto out;
}
error = snprintf(line, sizeof(line), "%sFrom %s\t%s", newline, sender, ctime(&now));
if (error < 0 || (size_t)error >= sizeof(line)) {
syslog(LOG_NOTICE, "local delivery deferred: can not write header: %m");
goto out;
}
if (write(mbox, line, error) != error)
goto wrerror;
while (!feof(it->mailf)) {
if (fgets(line, sizeof(line), it->mailf) == NULL)
break;
linelen = strlen(line);
if (linelen == 0 || line[linelen - 1] != '\n') {
syslog(LOG_CRIT, "local delivery failed: corrupted queue file");
snprintf(errmsg, sizeof(errmsg), "corrupted queue file");
error = -1;
goto chop;
}
/*
* mboxro processing:
* - escape lines that start with "From " with a > sign.
* - be reversable by escaping lines that contain an arbitrary
* number of > signs, followed by "From ", i.e. />*From / in regexp.
* - strict mbox processing only requires escaping after empty lines,
* yet most MUAs seem to relax this requirement and will treat any
* line starting with "From " as the beginning of a new mail.
*/
if ((!MBOX_STRICT || hadnl) &&
strncmp(&line[strspn(line, ">")], "From ", 5) == 0) {
const char *gt = ">";
if (write(mbox, gt, 1) != 1)
goto wrerror;
hadnl = 0;
} else if (strcmp(line, "\n") == 0) {
hadnl = 1;
} else {
hadnl = 0;
}
if ((size_t)write(mbox, line, linelen) != linelen)
goto wrerror;
}
close(mbox);
return (0);
wrerror:
syslog(LOG_ERR, "local delivery failed: write error: %m");
error = 1;
chop:
if (ftruncate(mbox, mboxlen) != 0)
syslog(LOG_WARNING, "error recovering mbox `%s': %m", fn);
out:
close(mbox);
return (error);
}

457
contrib/dma/mail.c Normal file
View File

@ -0,0 +1,457 @@
/*
* 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.
*/
#include <errno.h>
#include <inttypes.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include "dma.h"
void
bounce(struct qitem *it, const char *reason)
{
struct queue bounceq;
char line[1000];
size_t pos;
int error;
/* Don't bounce bounced mails */
if (it->sender[0] == 0) {
syslog(LOG_INFO, "can not bounce a bounce message, discarding");
exit(1);
}
bzero(&bounceq, sizeof(bounceq));
LIST_INIT(&bounceq.queue);
bounceq.sender = "";
if (add_recp(&bounceq, it->sender, EXPAND_WILDCARD) != 0)
goto fail;
if (newspoolf(&bounceq) != 0)
goto fail;
syslog(LOG_ERR, "delivery failed, bouncing as %s", bounceq.id);
setlogident("%s", bounceq.id);
error = fprintf(bounceq.mailf,
"Received: from MAILER-DAEMON\n"
"\tid %s\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: <%s@%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"
"%s\n"
"\n",
bounceq.id,
hostname(), VERSION,
rfc822date(),
it->addr,
it->sender,
bounceq.id, hostname(),
rfc822date(),
VERSION, hostname(),
it->addr,
reason,
config.features & FULLBOUNCE ?
"Original message follows." :
"Message headers follow.");
if (error < 0)
goto fail;
if (fseek(it->mailf, 0, SEEK_SET) != 0)
goto fail;
if (config.features & FULLBOUNCE) {
while ((pos = fread(line, 1, sizeof(line), it->mailf)) > 0) {
if (fwrite(line, 1, pos, bounceq.mailf) != pos)
goto fail;
}
} else {
while (!feof(it->mailf)) {
if (fgets(line, sizeof(line), it->mailf) == NULL)
break;
if (line[0] == '\n')
break;
if (fwrite(line, strlen(line), 1, bounceq.mailf) != 1)
goto fail;
}
}
if (linkspool(&bounceq) != 0)
goto fail;
/* bounce is safe */
delqueue(it);
run_queue(&bounceq);
/* NOTREACHED */
fail:
syslog(LOG_CRIT, "error creating bounce: %m");
delqueue(it);
exit(1);
}
struct parse_state {
char addr[1000];
int pos;
enum {
NONE = 0,
START,
MAIN,
EOL,
QUIT
} state;
int comment;
int quote;
int brackets;
int esc;
};
/*
* Simplified RFC2822 header/address parsing.
* We copy escapes and quoted strings directly, since
* we have to pass them like this to the mail server anyways.
* XXX local addresses will need treatment
*/
static int
parse_addrs(struct parse_state *ps, char *s, struct queue *queue)
{
char *addr;
again:
switch (ps->state) {
case NONE:
return (-1);
case START:
/* init our data */
bzero(ps, sizeof(*ps));
/* skip over header name */
while (*s != ':')
s++;
s++;
ps->state = MAIN;
break;
case MAIN:
/* all fine */
break;
case EOL:
switch (*s) {
case ' ':
case '\t':
s++;
/* continue */
break;
default:
ps->state = QUIT;
if (ps->pos != 0)
goto newaddr;
return (0);
}
case QUIT:
return (0);
}
for (; *s != 0; s++) {
if (ps->esc) {
ps->esc = 0;
switch (*s) {
case '\r':
case '\n':
goto err;
default:
goto copy;
}
}
if (ps->quote) {
switch (*s) {
case '"':
ps->quote = 0;
goto copy;
case '\\':
ps->esc = 1;
goto copy;
case '\r':
case '\n':
goto eol;
default:
goto copy;
}
}
switch (*s) {
case '(':
ps->comment++;
break;
case ')':
if (ps->comment)
ps->comment--;
else
goto err;
goto skip;
case '"':
ps->quote = 1;
goto copy;
case '\\':
ps->esc = 1;
goto copy;
case '\r':
case '\n':
goto eol;
}
if (ps->comment)
goto skip;
switch (*s) {
case ' ':
case '\t':
/* ignore whitespace */
goto skip;
case '<':
/* this is the real address now */
ps->brackets = 1;
ps->pos = 0;
goto skip;
case '>':
if (!ps->brackets)
goto err;
ps->brackets = 0;
s++;
goto newaddr;
case ':':
/* group - ignore */
ps->pos = 0;
goto skip;
case ',':
case ';':
/*
* Next address, copy previous one.
* However, we might be directly after
* a <address>, or have two consecutive
* commas.
* Skip the comma unless there is
* really something to copy.
*/
if (ps->pos == 0)
goto skip;
s++;
goto newaddr;
default:
goto copy;
}
copy:
if (ps->comment)
goto skip;
if (ps->pos + 1 == sizeof(ps->addr))
goto err;
ps->addr[ps->pos++] = *s;
skip:
;
}
eol:
ps->state = EOL;
return (0);
err:
ps->state = QUIT;
return (-1);
newaddr:
ps->addr[ps->pos] = 0;
ps->pos = 0;
addr = strdup(ps->addr);
if (addr == NULL)
errlog(1, NULL);
if (add_recp(queue, addr, EXPAND_WILDCARD) != 0)
errlogx(1, "invalid recipient `%s'", addr);
goto again;
}
int
readmail(struct queue *queue, int nodot, int recp_from_header)
{
struct parse_state parse_state;
char line[1000]; /* by RFC2822 */
size_t linelen;
size_t error;
int had_headers = 0;
int had_from = 0;
int had_messagid = 0;
int had_date = 0;
int had_last_line = 0;
int nocopy = 0;
parse_state.state = NONE;
error = fprintf(queue->mailf,
"Received: from %s (uid %d)\n"
"\t(envelope-from %s)\n"
"\tid %s\n"
"\tby %s (%s);\n"
"\t%s\n",
username, useruid,
queue->sender,
queue->id,
hostname(), VERSION,
rfc822date());
if ((ssize_t)error < 0)
return (-1);
while (!feof(stdin)) {
if (fgets(line, sizeof(line) - 1, stdin) == NULL)
break;
if (had_last_line)
errlogx(1, "bad mail input format");
linelen = strlen(line);
if (linelen == 0 || line[linelen - 1] != '\n') {
/*
* This line did not end with a newline character.
* If we fix it, it better be the last line of
* the file.
*/
line[linelen] = '\n';
line[linelen + 1] = 0;
had_last_line = 1;
}
if (!had_headers) {
/*
* Unless this is a continuation, switch of
* the Bcc: nocopy flag.
*/
if (!(line[0] == ' ' || line[0] == '\t'))
nocopy = 0;
if (strprefixcmp(line, "Date:") == 0)
had_date = 1;
else if (strprefixcmp(line, "Message-Id:") == 0)
had_messagid = 1;
else if (strprefixcmp(line, "From:") == 0)
had_from = 1;
else if (strprefixcmp(line, "Bcc:") == 0)
nocopy = 1;
if (parse_state.state != NONE) {
if (parse_addrs(&parse_state, line, queue) < 0) {
errlogx(1, "invalid address in header\n");
/* NOTREACHED */
}
}
if (recp_from_header && (
strprefixcmp(line, "To:") == 0 ||
strprefixcmp(line, "Cc:") == 0 ||
strprefixcmp(line, "Bcc:") == 0)) {
parse_state.state = START;
if (parse_addrs(&parse_state, line, queue) < 0) {
errlogx(1, "invalid address in header\n");
/* NOTREACHED */
}
}
}
if (strcmp(line, "\n") == 0 && !had_headers) {
had_headers = 1;
while (!had_date || !had_messagid || !had_from) {
if (!had_date) {
had_date = 1;
snprintf(line, sizeof(line), "Date: %s\n", rfc822date());
} else if (!had_messagid) {
/* XXX msgid, assign earlier and log? */
had_messagid = 1;
snprintf(line, sizeof(line), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n",
(uintmax_t)time(NULL),
queue->id,
(uintmax_t)random(),
hostname());
} else if (!had_from) {
had_from = 1;
snprintf(line, sizeof(line), "From: <%s>\n", queue->sender);
}
if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
return (-1);
}
strcpy(line, "\n");
}
if (!nodot && linelen == 2 && line[0] == '.')
break;
if (!nocopy) {
if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
return (-1);
}
}
return (0);
}

546
contrib/dma/net.c Normal file
View File

@ -0,0 +1,546 @@
/*
* 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.
*/
#include "dfcompat.h"
#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>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <netdb.h>
#include <setjmp.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include "dma.h"
char neterr[ERRMSG_SIZE];
char *
ssl_errstr(void)
{
long oerr, nerr;
oerr = 0;
while ((nerr = ERR_get_error()) != 0)
oerr = nerr;
return (ERR_error_string(oerr, NULL));
}
ssize_t
send_remote_command(int fd, const char* fmt, ...)
{
va_list va;
char cmd[4096];
size_t len, pos;
int s;
ssize_t n;
va_start(va, fmt);
s = vsnprintf(cmd, sizeof(cmd) - 2, fmt, va);
va_end(va);
if (s == sizeof(cmd) - 2 || s < 0) {
strcpy(neterr, "Internal error: oversized command string");
return (-1);
}
/* We *know* there are at least two more bytes available */
strcat(cmd, "\r\n");
len = strlen(cmd);
if (((config.features & SECURETRANS) != 0) &&
((config.features & NOSSL) == 0)) {
while ((s = SSL_write(config.ssl, (const char*)cmd, len)) <= 0) {
s = SSL_get_error(config.ssl, s);
if (s != SSL_ERROR_WANT_READ &&
s != SSL_ERROR_WANT_WRITE) {
strncpy(neterr, ssl_errstr(), sizeof(neterr));
return (-1);
}
}
}
else {
pos = 0;
while (pos < len) {
n = write(fd, cmd + pos, len - pos);
if (n < 0)
return (-1);
pos += n;
}
}
return (len);
}
int
read_remote(int fd, int extbufsize, char *extbuf)
{
ssize_t rlen = 0;
size_t pos, len, copysize;
char buff[BUF_SIZE];
int done = 0, status = 0, status_running = 0, extbufpos = 0;
enum { parse_status, parse_spacedash, parse_rest } parsestate;
if (do_timeout(CON_TIMEOUT, 1) != 0) {
snprintf(neterr, sizeof(neterr), "Timeout reached");
return (-1);
}
/*
* Remote reading code from femail.c written by Henning Brauer of
* OpenBSD and released under a BSD style license.
*/
len = 0;
pos = 0;
parsestate = parse_status;
neterr[0] = 0;
while (!(done && parsestate == parse_status)) {
rlen = 0;
if (pos == 0 ||
(pos > 0 && memchr(buff + pos, '\n', len - pos) == NULL)) {
memmove(buff, buff + pos, len - pos);
len -= pos;
pos = 0;
if (((config.features & SECURETRANS) != 0) &&
(config.features & NOSSL) == 0) {
if ((rlen = SSL_read(config.ssl, buff + len, sizeof(buff) - len)) == -1) {
strncpy(neterr, ssl_errstr(), sizeof(neterr));
goto error;
}
} else {
if ((rlen = read(fd, buff + len, sizeof(buff) - len)) == -1) {
strncpy(neterr, strerror(errno), sizeof(neterr));
goto error;
}
}
len += rlen;
copysize = sizeof(neterr) - strlen(neterr) - 1;
if (copysize > len)
copysize = len;
strncat(neterr, buff, copysize);
}
/*
* If there is an external buffer with a size bigger than zero
* and as long as there is space in the external buffer and
* there are new characters read from the mailserver
* copy them to the external buffer
*/
if (extbufpos <= (extbufsize - 1) && rlen > 0 && extbufsize > 0 && extbuf != NULL) {
/* do not write over the bounds of the buffer */
if(extbufpos + rlen > (extbufsize - 1)) {
rlen = extbufsize - extbufpos;
}
memcpy(extbuf + extbufpos, buff + len - rlen, rlen);
extbufpos += rlen;
}
if (pos == len)
continue;
switch (parsestate) {
case parse_status:
for (; pos < len; pos++) {
if (isdigit(buff[pos])) {
status_running = status_running * 10 + (buff[pos] - '0');
} else {
status = status_running;
status_running = 0;
parsestate = parse_spacedash;
break;
}
}
continue;
case parse_spacedash:
switch (buff[pos]) {
case ' ':
done = 1;
break;
case '-':
/* ignore */
/* XXX read capabilities */
break;
default:
strcpy(neterr, "invalid syntax in reply from server");
goto error;
}
pos++;
parsestate = parse_rest;
continue;
case parse_rest:
/* skip up to \n */
for (; pos < len; pos++) {
if (buff[pos] == '\n') {
pos++;
parsestate = parse_status;
break;
}
}
}
}
do_timeout(0, 0);
/* chop off trailing newlines */
while (neterr[0] != 0 && strchr("\r\n", neterr[strlen(neterr) - 1]) != 0)
neterr[strlen(neterr) - 1] = 0;
return (status/100);
error:
do_timeout(0, 0);
return (-1);
}
/*
* Handle SMTP authentication
*/
static int
smtp_login(int fd, char *login, char* password)
{
char *temp;
int len, res = 0;
res = smtp_auth_md5(fd, login, password);
if (res == 0) {
return (0);
} else if (res == -2) {
/*
* If the return code is -2, then then the login attempt failed,
* do not try other login mechanisms
*/
return (1);
}
if ((config.features & INSECURE) != 0 ||
(config.features & SECURETRANS) != 0) {
/* Send AUTH command according to RFC 2554 */
send_remote_command(fd, "AUTH LOGIN");
if (read_remote(fd, 0, NULL) != 3) {
syslog(LOG_NOTICE, "remote delivery deferred:"
" AUTH login not available: %s",
neterr);
return (1);
}
len = base64_encode(login, strlen(login), &temp);
if (len < 0) {
encerr:
syslog(LOG_ERR, "can not encode auth reply: %m");
return (1);
}
send_remote_command(fd, "%s", temp);
free(temp);
res = read_remote(fd, 0, NULL);
if (res != 3) {
syslog(LOG_NOTICE, "remote delivery %s: AUTH login failed: %s",
res == 5 ? "failed" : "deferred", neterr);
return (res == 5 ? -1 : 1);
}
len = base64_encode(password, strlen(password), &temp);
if (len < 0)
goto encerr;
send_remote_command(fd, "%s", temp);
free(temp);
res = read_remote(fd, 0, NULL);
if (res != 2) {
syslog(LOG_NOTICE, "remote delivery %s: Authentication failed: %s",
res == 5 ? "failed" : "deferred", neterr);
return (res == 5 ? -1 : 1);
}
} else {
syslog(LOG_WARNING, "non-encrypted SMTP login is disabled in config, so skipping it. ");
return (1);
}
return (0);
}
static int
open_connection(struct mx_hostentry *h)
{
int fd;
syslog(LOG_INFO, "trying remote delivery to %s [%s] pref %d",
h->host, h->addr, h->pref);
fd = socket(h->ai.ai_family, h->ai.ai_socktype, h->ai.ai_protocol);
if (fd < 0) {
syslog(LOG_INFO, "socket for %s [%s] failed: %m",
h->host, h->addr);
return (-1);
}
if (connect(fd, (struct sockaddr *)&h->sa, h->ai.ai_addrlen) < 0) {
syslog(LOG_INFO, "connect to %s [%s] failed: %m",
h->host, h->addr);
close(fd);
return (-1);
}
return (fd);
}
static void
close_connection(int fd)
{
if (config.ssl != NULL) {
if (((config.features & SECURETRANS) != 0) &&
((config.features & NOSSL) == 0))
SSL_shutdown(config.ssl);
SSL_free(config.ssl);
}
close(fd);
}
static int
deliver_to_host(struct qitem *it, struct mx_hostentry *host)
{
struct authuser *a;
char line[1000];
size_t linelen;
int fd, error = 0, do_auth = 0, res = 0;
if (fseek(it->mailf, 0, SEEK_SET) != 0) {
snprintf(errmsg, sizeof(errmsg), "can not seek: %s", strerror(errno));
return (-1);
}
fd = open_connection(host);
if (fd < 0)
return (1);
#define READ_REMOTE_CHECK(c, exp) \
res = read_remote(fd, 0, NULL); \
if (res == 5) { \
syslog(LOG_ERR, "remote delivery to %s [%s] failed after %s: %s", \
host->host, host->addr, c, neterr); \
snprintf(errmsg, sizeof(errmsg), "%s [%s] did not like our %s:\n%s", \
host->host, host->addr, c, neterr); \
return (-1); \
} else if (res != exp) { \
syslog(LOG_NOTICE, "remote delivery deferred: %s [%s] failed after %s: %s", \
host->host, host->addr, c, neterr); \
return (1); \
}
/* Check first reply from remote host */
if ((config.features & SECURETRANS) == 0 ||
(config.features & STARTTLS) != 0) {
config.features |= NOSSL;
READ_REMOTE_CHECK("connect", 2);
config.features &= ~NOSSL;
}
if ((config.features & SECURETRANS) != 0) {
error = smtp_init_crypto(fd, config.features);
if (error == 0)
syslog(LOG_DEBUG, "SSL initialization successful");
else
goto out;
if ((config.features & STARTTLS) == 0)
READ_REMOTE_CHECK("connect", 2);
}
/* XXX allow HELO fallback */
/* XXX record ESMTP keywords */
send_remote_command(fd, "EHLO %s", hostname());
READ_REMOTE_CHECK("EHLO", 2);
/*
* Use SMTP authentication if the user defined an entry for the remote
* or smarthost
*/
SLIST_FOREACH(a, &authusers, next) {
if (strcmp(a->host, host->host) == 0) {
do_auth = 1;
break;
}
}
if (do_auth == 1) {
/*
* Check if the user wants plain text login without using
* encryption.
*/
syslog(LOG_INFO, "using SMTP authentication for user %s", a->login);
error = smtp_login(fd, a->login, a->password);
if (error < 0) {
syslog(LOG_ERR, "remote delivery failed:"
" SMTP login failed: %m");
snprintf(errmsg, sizeof(errmsg), "SMTP login to %s failed", host->host);
return (-1);
}
/* SMTP login is not available, so try without */
else if (error > 0) {
syslog(LOG_WARNING, "SMTP login not available. Trying without.");
}
}
/* XXX send ESMTP ENVID, RET (FULL/HDRS) and 8BITMIME */
send_remote_command(fd, "MAIL FROM:<%s>", it->sender);
READ_REMOTE_CHECK("MAIL FROM", 2);
/* XXX send ESMTP ORCPT */
send_remote_command(fd, "RCPT TO:<%s>", it->addr);
READ_REMOTE_CHECK("RCPT TO", 2);
send_remote_command(fd, "DATA");
READ_REMOTE_CHECK("DATA", 3);
error = 0;
while (!feof(it->mailf)) {
if (fgets(line, sizeof(line), it->mailf) == NULL)
break;
linelen = strlen(line);
if (linelen == 0 || line[linelen - 1] != '\n') {
syslog(LOG_CRIT, "remote delivery failed: corrupted queue file");
snprintf(errmsg, sizeof(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_NOTICE, "remote delivery deferred: write error");
error = 1;
goto out;
}
}
send_remote_command(fd, ".");
READ_REMOTE_CHECK("final DATA", 2);
send_remote_command(fd, "QUIT");
if (read_remote(fd, 0, NULL) != 2)
syslog(LOG_INFO, "remote delivery succeeded but QUIT failed: %s", neterr);
out:
close_connection(fd);
return (error);
}
int
deliver_remote(struct qitem *it)
{
struct mx_hostentry *hosts, *h;
const char *host;
int port;
int error = 1, smarthost = 0;
port = SMTP_PORT;
/* Smarthost support? */
if (config.smarthost != NULL) {
host = config.smarthost;
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) {
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);
}
for (h = hosts; *h->host != 0; h++) {
switch (deliver_to_host(it, h)) {
case 0:
/* success */
error = 0;
goto out;
case 1:
/* temp failure */
error = 1;
break;
default:
/* perm failure */
error = -1;
goto out;
}
}
out:
free(hosts);
return (error);
}

440
contrib/dma/spool.c Normal file
View File

@ -0,0 +1,440 @@
/*
* 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.
*/
#include "dfcompat.h"
#include <sys/file.h>
#include <sys/stat.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <unistd.h>
#include <syslog.h>
#include "dma.h"
/*
* Spool file format:
*
* 'Q'id files (queue):
* Organized like an RFC822 header, field: value. Ignores unknown fields.
* ID: id
* Sender: envelope-from
* Recipient: envelope-to
*
* 'M'id files (data):
* mail data
*
* Each queue file needs to have a corresponding data file.
* One data file might be shared by linking it several times.
*
* Queue ids are unique, formed from the inode of the data file
* and a unique identifier.
*/
int
newspoolf(struct queue *queue)
{
char fn[PATH_MAX+1];
struct stat st;
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);
/* XXX group rights */
if (fchmod(fd, 0660) < 0)
goto fail;
if (flock(fd, LOCK_EX) == -1)
goto fail;
queue->tmpf = strdup(fn);
if (queue->tmpf == NULL)
goto fail;
/*
* Assign queue id
*/
if (fstat(fd, &st) != 0)
goto fail;
if (asprintf(&queue->id, "%"PRIxMAX, (uintmax_t)st.st_ino) < 0)
goto fail;
queue->mailf = fdopen(fd, "r+");
if (queue->mailf == NULL)
goto fail;
t = malloc(sizeof(*t));
if (t != NULL) {
t->str = queue->tmpf;
SLIST_INSERT_HEAD(&tmpfs, t, next);
}
return (0);
fail:
if (queue->mailf != NULL)
fclose(queue->mailf);
close(fd);
unlink(fn);
return (-1);
}
static int
writequeuef(struct qitem *it)
{
int error;
int queuefd;
queuefd = open_locked(it->queuefn, O_CREAT|O_EXCL|O_RDWR, 0660);
if (queuefd == -1)
return (-1);
if (fchmod(queuefd, 0660) < 0)
return (-1);
it->queuef = fdopen(queuefd, "w+");
if (it->queuef == NULL)
return (-1);
error = fprintf(it->queuef,
"ID: %s\n"
"Sender: %s\n"
"Recipient: %s\n",
it->queueid,
it->sender,
it->addr);
if (error <= 0)
return (-1);
if (fflush(it->queuef) != 0 || fsync(fileno(it->queuef)) != 0)
return (-1);
return (0);
}
static struct qitem *
readqueuef(struct queue *queue, char *queuefn)
{
char line[1000];
struct queue itmqueue;
FILE *queuef = NULL;
char *s;
char *queueid = NULL, *sender = NULL, *addr = NULL;
struct qitem *it = NULL;
bzero(&itmqueue, sizeof(itmqueue));
LIST_INIT(&itmqueue.queue);
queuef = fopen(queuefn, "r");
if (queuef == NULL)
goto out;
while (!feof(queuef)) {
if (fgets(line, sizeof(line), queuef) == NULL || line[0] == 0)
break;
line[strlen(line) - 1] = 0; /* chop newline */
s = strchr(line, ':');
if (s == NULL)
goto malformed;
*s = 0;
s++;
while (isspace(*s))
s++;
s = strdup(s);
if (s == NULL)
goto malformed;
if (strcmp(line, "ID") == 0) {
queueid = s;
} else if (strcmp(line, "Sender") == 0) {
sender = s;
} else if (strcmp(line, "Recipient") == 0) {
addr = s;
} else {
syslog(LOG_DEBUG, "ignoring unknown queue info `%s' in `%s'",
line, queuefn);
free(s);
}
}
if (queueid == NULL || sender == NULL || addr == NULL ||
*queueid == 0 || *addr == 0) {
malformed:
errno = EINVAL;
syslog(LOG_ERR, "malformed queue file `%s'", queuefn);
goto out;
}
if (add_recp(&itmqueue, addr, 0) != 0)
goto out;
it = LIST_FIRST(&itmqueue.queue);
it->sender = sender; sender = NULL;
it->queueid = queueid; queueid = NULL;
it->queuefn = queuefn; queuefn = NULL;
LIST_INSERT_HEAD(&queue->queue, it, next);
out:
if (sender != NULL)
free(sender);
if (queueid != NULL)
free(queueid);
if (addr != NULL)
free(addr);
if (queuef != NULL)
fclose(queuef);
return (it);
}
int
linkspool(struct queue *queue)
{
struct stat st;
struct qitem *it;
if (fflush(queue->mailf) != 0 || fsync(fileno(queue->mailf)) != 0)
goto delfiles;
syslog(LOG_INFO, "new mail from user=%s uid=%d envelope_from=<%s>",
username, getuid(), queue->sender);
LIST_FOREACH(it, &queue->queue, next) {
if (asprintf(&it->queueid, "%s.%"PRIxPTR, queue->id, (uintptr_t)it) <= 0)
goto delfiles;
if (asprintf(&it->queuefn, "%s/Q%s", config.spooldir, it->queueid) <= 0)
goto delfiles;
if (asprintf(&it->mailfn, "%s/M%s", config.spooldir, it->queueid) <= 0)
goto delfiles;
/* Neither file may not exist yet */
if (stat(it->queuefn, &st) == 0 || stat(it->mailfn, &st) == 0)
goto delfiles;
if (writequeuef(it) != 0)
goto delfiles;
if (link(queue->tmpf, it->mailfn) != 0)
goto delfiles;
}
LIST_FOREACH(it, &queue->queue, next) {
syslog(LOG_INFO, "mail to=<%s> queued as %s",
it->addr, it->queueid);
}
unlink(queue->tmpf);
return (0);
delfiles:
LIST_FOREACH(it, &queue->queue, next) {
unlink(it->mailfn);
unlink(it->queuefn);
}
return (-1);
}
int
load_queue(struct queue *queue)
{
struct stat sb;
struct qitem *it;
DIR *spooldir;
struct dirent *de;
char *queuefn;
char *mailfn;
bzero(queue, sizeof(*queue));
LIST_INIT(&queue->queue);
spooldir = opendir(config.spooldir);
if (spooldir == NULL)
err(1, "reading queue");
while ((de = readdir(spooldir)) != NULL) {
queuefn = NULL;
mailfn = NULL;
/* ignore non-queue files */
if (de->d_name[0] != 'Q')
continue;
if (asprintf(&queuefn, "%s/Q%s", config.spooldir, de->d_name + 1) < 0)
goto fail;
if (asprintf(&mailfn, "%s/M%s", config.spooldir, de->d_name + 1) < 0)
goto fail;
/*
* Some file systems don't provide a de->d_type, so we have to
* do an explicit stat on the queue file.
* Move on if it turns out to be something else than a file.
*/
if (stat(queuefn, &sb) != 0)
goto skip_item;
if (!S_ISREG(sb.st_mode)) {
errno = EINVAL;
goto skip_item;
}
if (stat(mailfn, &sb) != 0)
goto skip_item;
it = readqueuef(queue, queuefn);
if (it == NULL)
goto skip_item;
it->mailfn = mailfn;
continue;
skip_item:
syslog(LOG_INFO, "could not pick up queue file: `%s'/`%s': %m", queuefn, mailfn);
if (queuefn != NULL)
free(queuefn);
if (mailfn != NULL)
free(mailfn);
}
closedir(spooldir);
return (0);
fail:
return (-1);
}
void
delqueue(struct qitem *it)
{
unlink(it->mailfn);
unlink(it->queuefn);
if (it->queuef != NULL)
fclose(it->queuef);
if (it->mailf != NULL)
fclose(it->mailf);
free(it);
}
int
acquirespool(struct qitem *it)
{
int queuefd;
if (it->queuef == NULL) {
queuefd = open_locked(it->queuefn, O_RDWR|O_NONBLOCK);
if (queuefd < 0)
goto fail;
it->queuef = fdopen(queuefd, "r+");
if (it->queuef == NULL)
goto fail;
}
if (it->mailf == NULL) {
it->mailf = fopen(it->mailfn, "r");
if (it->mailf == NULL)
goto fail;
}
return (0);
fail:
if (errno == EWOULDBLOCK)
return (1);
syslog(LOG_INFO, "could not acquire queue file: %m");
return (-1);
}
void
dropspool(struct queue *queue, struct qitem *keep)
{
struct qitem *it;
LIST_FOREACH(it, &queue->queue, next) {
if (it == keep)
continue;
if (it->queuef != NULL)
fclose(it->queuef);
if (it->mailf != NULL)
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);
}

View File

@ -0,0 +1,23 @@
From: test <test@example.org>
To: test <test@example.org>
Subject: testing!
From what i can see...
He came along,
from far away.
The rabbit comes out
From his rabbit hole.
Fromtal nudity!
> From the previous mail
>From the quote
From here
From there
>>>From everywhere.
last line.

345
contrib/dma/util.c Normal file
View File

@ -0,0 +1,345 @@
/*
* 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.
*/
#include <sys/param.h>
#include <sys/file.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include "dma.h"
const char *
hostname(void)
{
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif
static char name[HOST_NAME_MAX+1];
static int initialized = 0;
char *s;
if (initialized)
return (name);
if (config.mailname == NULL || !*config.mailname)
goto local;
if (config.mailname[0] == '/') {
/*
* If the mailname looks like an absolute path,
* treat it as a file.
*/
FILE *fp;
fp = fopen(config.mailname, "r");
if (fp == NULL)
goto local;
s = fgets(name, sizeof(name), fp);
fclose(fp);
if (s == NULL)
goto local;
for (s = name; *s != 0 && (isalnum(*s) || strchr("_.-", *s)); ++s)
/* NOTHING */;
*s = 0;
if (!*name)
goto local;
initialized = 1;
return (name);
} else {
snprintf(name, sizeof(name), "%s", config.mailname);
initialized = 1;
return (name);
}
local:
if (gethostname(name, sizeof(name)) != 0)
*name = 0;
/*
* gethostname() is allowed to truncate name without NUL-termination
* and at the same time not return an error.
*/
name[sizeof(name) - 1] = 0;
for (s = name; *s != 0 && (isalnum(*s) || strchr("_.-", *s)); ++s)
/* NOTHING */;
*s = 0;
if (!*name)
snprintf(name, sizeof(name), "unknown-hostname");
initialized = 1;
return (name);
}
void
setlogident(const char *fmt, ...)
{
static char tag[50];
snprintf(tag, sizeof(tag), "%s", logident_base);
if (fmt != NULL) {
va_list ap;
char sufx[50];
va_start(ap, fmt);
vsnprintf(sufx, sizeof(sufx), fmt, ap);
va_end(ap);
snprintf(tag, sizeof(tag), "%s[%s]", logident_base, sufx);
}
closelog();
openlog(tag, 0, LOG_MAIL);
}
void
errlog(int exitcode, const char *fmt, ...)
{
int oerrno = errno;
va_list ap;
char outs[ERRMSG_SIZE];
outs[0] = 0;
if (fmt != NULL) {
va_start(ap, fmt);
vsnprintf(outs, sizeof(outs), fmt, ap);
va_end(ap);
}
errno = oerrno;
if (*outs != 0) {
syslog(LOG_ERR, "%s: %m", outs);
fprintf(stderr, "%s: %s: %s\n", getprogname(), outs, strerror(oerrno));
} else {
syslog(LOG_ERR, "%m");
fprintf(stderr, "%s: %s\n", getprogname(), strerror(oerrno));
}
exit(exitcode);
}
void
errlogx(int exitcode, const char *fmt, ...)
{
va_list ap;
char outs[ERRMSG_SIZE];
outs[0] = 0;
if (fmt != NULL) {
va_start(ap, fmt);
vsnprintf(outs, sizeof(outs), fmt, ap);
va_end(ap);
}
if (*outs != 0) {
syslog(LOG_ERR, "%s", outs);
fprintf(stderr, "%s: %s\n", getprogname(), outs);
} else {
syslog(LOG_ERR, "Unknown error");
fprintf(stderr, "%s: Unknown error\n", getprogname());
}
exit(exitcode);
}
static int
check_username(const char *name, uid_t ckuid)
{
struct passwd *pwd;
if (name == NULL)
return (0);
pwd = getpwnam(name);
if (pwd == NULL || pwd->pw_uid != ckuid)
return (0);
snprintf(username, sizeof(username), "%s", name);
return (1);
}
void
set_username(void)
{
struct passwd *pwd;
useruid = getuid();
if (check_username(getlogin(), useruid))
return;
if (check_username(getenv("LOGNAME"), useruid))
return;
if (check_username(getenv("USER"), useruid))
return;
pwd = getpwuid(useruid);
if (pwd != NULL && pwd->pw_name != NULL && pwd->pw_name[0] != '\0') {
if (check_username(pwd->pw_name, useruid))
return;
}
snprintf(username, sizeof(username), "uid=%ld", (long)useruid);
}
void
deltmp(void)
{
struct stritem *t;
SLIST_FOREACH(t, &tmpfs, next) {
unlink(t->str);
}
}
static sigjmp_buf sigbuf;
static int sigbuf_valid;
static void
sigalrm_handler(int signo)
{
(void)signo; /* so that gcc doesn't complain */
if (sigbuf_valid)
siglongjmp(sigbuf, 1);
}
int
do_timeout(int timeout, int dojmp)
{
struct sigaction act;
int ret = 0;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (timeout) {
act.sa_handler = sigalrm_handler;
if (sigaction(SIGALRM, &act, NULL) != 0)
syslog(LOG_WARNING, "can not set signal handler: %m");
if (dojmp) {
ret = sigsetjmp(sigbuf, 1);
if (ret)
goto disable;
/* else just programmed */
sigbuf_valid = 1;
}
alarm(timeout);
} else {
disable:
alarm(0);
act.sa_handler = SIG_IGN;
if (sigaction(SIGALRM, &act, NULL) != 0)
syslog(LOG_WARNING, "can not remove signal handler: %m");
sigbuf_valid = 0;
}
return (ret);
}
int
open_locked(const char *fname, int flags, ...)
{
int mode = 0;
if (flags & O_CREAT) {
va_list ap;
va_start(ap, flags);
mode = va_arg(ap, int);
va_end(ap);
}
#ifndef O_EXLOCK
int fd, save_errno;
fd = open(fname, flags, mode);
if (fd < 0)
return(fd);
if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) {
save_errno = errno;
close(fd);
errno = save_errno;
return(-1);
}
return(fd);
#else
return(open(fname, flags|O_EXLOCK, mode));
#endif
}
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);
}
int
strprefixcmp(const char *str, const char *prefix)
{
return (strncasecmp(str, prefix, strlen(prefix)));
}
void
init_random(void)
{
unsigned int seed;
int rf;
rf = open("/dev/urandom", O_RDONLY);
if (rf == -1)
rf = open("/dev/random", O_RDONLY);
if (!(rf != -1 && read(rf, &seed, sizeof(seed)) == sizeof(seed)))
seed = (time(NULL) ^ getpid()) + (uintptr_t)&seed;
srandom(seed);
if (rf != -1)
close(rf);
}

View File

@ -32,6 +32,8 @@
..
devd
..
dma
..
gnats
..
gss

View File

@ -74,6 +74,8 @@
rwho gname=daemon mode=0775
..
spool
dma name=root gname=mail mode=0770
..
lock uname=uucp gname=dialer mode=0775
..
/set gname=daemon

View File

@ -8,6 +8,8 @@ SUBDIR= ${_atf} \
bootpd \
${_casper} \
${_comsat} \
${_dma} \
${_dma-mbox-create} \
fingerd \
ftpd \
getty \
@ -47,6 +49,11 @@ _casper= casper
_comsat= comsat
.endif
.if ${MK_DMA} != "no"
_dma= dma
_dma-mbox-create= dma-mbox-create
.endif
.if ${MK_NIS} != "no"
_mknetid= mknetid
_ypxfr= ypxfr

32
libexec/dma/Makefile Normal file
View File

@ -0,0 +1,32 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../contrib/dma
CFLAGS= -I${.CURDIR}/../../contrib/dma \
-DHAVE_REALLOCF -DHAVE_STRLCPY -DHAVE_GETPROGNAME \
-DCONF_PATH='"/etc/dma"' \
-DLIBEXEC_PATH='"/usr/libexec"' -DDMA_VERSION='"v0.9+"'
DPADD= ${LIBSSL} ${LIBCRYPTO}
LDADD= -lssl -lcrypto
PROG= dma
SRCS= aliases_parse.y \
aliases_scan.l \
base64.c \
conf.c \
crypto.c \
dma.c \
dns.c \
local.c \
mail.c \
net.c \
spool.c \
util.c
MAN8= dma.8
YFLAGS+= -i
CLEANFILES= aliases_parse.i
BINGRP= mail
BINMODE= 2555
.include <bsd.prog.mk>

View File

@ -271,6 +271,7 @@ __DEFAULT_YES_OPTIONS = \
CTM \
CXX \
DICT \
DMA \
DYNAMICROOT \
ED_CRYPTO \
EXAMPLES \
@ -520,6 +521,7 @@ MK_GROFF:= no
.if ${MK_MAIL} == "no"
MK_MAILWRAPPER:= no
MK_SENDMAIL:= no
MK_DMA:= no
.endif
.if ${MK_NETGRAPH} == "no"

View File

@ -4306,3 +4306,9 @@ OLD_FILES+=usr/bin/svnserve
OLD_FILES+=usr/bin/svnsync
OLD_FILES+=usr/bin/svnversion
.endif
.if ${MK_DMA} == no
OLD_FILES+=usr/libexec/dma
OLD_FILES+=usr/libexec/dma-mbox-create
OLD_FILES+=usr/share/man/man8/dma.8.gz
.endif

View File

@ -0,0 +1,2 @@
.\" $FreeBSD$
Set to not build dma Mail Transport Agent