Import dma 89702b7f14 (2013-02-13) into vendors
This commit is contained in:
commit
d38539d805
28
BSDmakefile
Normal file
28
BSDmakefile
Normal 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
INSTALL
Normal file
28
INSTALL
Normal 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
LICENSE
Normal file
109
LICENSE
Normal 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
Makefile
Normal file
111
Makefile
Normal 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
Makefile.etc
Normal file
15
Makefile.etc
Normal 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>
|
32
README.markdown
Normal file
32
README.markdown
Normal 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
TODO
Normal file
5
TODO
Normal 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
|
112
aliases_parse.y
Normal file
112
aliases_parse.y
Normal 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;
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
24
aliases_scan.l
Normal file
24
aliases_scan.l
Normal 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
auth.conf
Normal file
4
auth.conf
Normal 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
base64.c
Normal file
135
base64.c
Normal 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
conf.c
Normal file
246
conf.c
Normal 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
crypto.c
Normal file
312
crypto.c
Normal 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
debian/NEWS
vendored
Normal file
29
debian/NEWS
vendored
Normal 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
|
||||
|
12
debian/README.Debian
vendored
Normal file
12
debian/README.Debian
vendored
Normal 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
|
297
debian/changelog
vendored
Normal file
297
debian/changelog
vendored
Normal 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
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@ -0,0 +1 @@
|
||||
7
|
37
debian/control
vendored
Normal file
37
debian/control
vendored
Normal 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.
|
91
debian/copyright
vendored
Normal file
91
debian/copyright
vendored
Normal 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.
|
2
debian/dma-migrate.dirs
vendored
Normal file
2
debian/dma-migrate.dirs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
usr/sbin
|
||||
usr/share/man/man8
|
1
debian/dma-migrate.install
vendored
Normal file
1
debian/dma-migrate.install
vendored
Normal file
@ -0,0 +1 @@
|
||||
debian/migrate/dma-migrate usr/sbin
|
1
debian/dma-migrate.manpages
vendored
Normal file
1
debian/dma-migrate.manpages
vendored
Normal file
@ -0,0 +1 @@
|
||||
debian/migrate/dma-migrate.8
|
4
debian/dma.dirs
vendored
Normal file
4
debian/dma.dirs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
etc/dma
|
||||
usr/sbin
|
||||
usr/share/lintian/overrides
|
||||
usr/share/man/man8
|
6
debian/dma.links
vendored
Normal file
6
debian/dma.links
vendored
Normal 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
|
8
debian/dma.lintian-overrides
vendored
Normal file
8
debian/dma.lintian-overrides
vendored
Normal 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
|
4
debian/migrate/Makefile
vendored
Normal file
4
debian/migrate/Makefile
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
all: dma-migrate
|
||||
|
||||
clean:
|
||||
rm -f dma-migrate dma-migrate.o
|
6
debian/migrate/NEWS
vendored
Normal file
6
debian/migrate/NEWS
vendored
Normal 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>
|
98
debian/migrate/dma-migrate.8
vendored
Normal file
98
debian/migrate/dma-migrate.8
vendored
Normal 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
|
413
debian/migrate/dma-migrate.c
vendored
Normal file
413
debian/migrate/dma-migrate.c
vendored
Normal 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
debian/rules
vendored
Executable file
49
debian/rules
vendored
Executable 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 $@
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@ -0,0 +1 @@
|
||||
3.0 (native)
|
2
debian/source/options
vendored
Normal file
2
debian/source/options
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
compression = "bzip2"
|
||||
compression-level = 9
|
122
dfcompat.c
Normal file
122
dfcompat.c
Normal 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
dfcompat.h
Normal file
24
dfcompat.h
Normal 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 */
|
160
dma-mbox-create.c
Normal file
160
dma-mbox-create.c
Normal 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
dma.8
Normal file
363
dma.8
Normal 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
dma.c
Normal file
631
dma.c
Normal 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
dma.conf
Normal file
66
dma.conf
Normal 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
dma.h
Normal file
237
dma.h
Normal 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
dns.c
Normal file
280
dns.c
Normal 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
get-version.sh
Executable file
9
get-version.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
gitver=$(git describe 2>/dev/null | tr - .)
|
||||
filever=$(cat VERSION)
|
||||
|
||||
version=${gitver}
|
||||
: ${version:=$filever}
|
||||
|
||||
echo "$version"
|
253
local.c
Normal file
253
local.c
Normal 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
mail.c
Normal file
457
mail.c
Normal 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
net.c
Normal file
546
net.c
Normal 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
spool.c
Normal file
440
spool.c
Normal 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);
|
||||
}
|
23
test/quote.rfc2822
Normal file
23
test/quote.rfc2822
Normal 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
util.c
Normal file
345
util.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user