git: fbe95b885f34 - main - dma: import snapshot 2021-07-10

Baptiste Daroussin bapt at FreeBSD.org
Wed Sep 22 09:11:16 UTC 2021


The branch main has been updated by bapt:

URL: https://cgit.FreeBSD.org/src/commit/?id=fbe95b885f3431b1d8003545b32e8ffa88f2d16b

commit fbe95b885f3431b1d8003545b32e8ffa88f2d16b
Merge: 9413dfd331e7 16a84834c279
Author:     Baptiste Daroussin <bapt at FreeBSD.org>
AuthorDate: 2021-09-22 09:09:27 +0000
Commit:     Baptiste Daroussin <bapt at FreeBSD.org>
CommitDate: 2021-09-22 09:10:58 +0000

    dma: import snapshot 2021-07-10

 contrib/dma/Makefile       |   2 +-
 contrib/dma/VERSION        |   2 +-
 contrib/dma/conf.c         |  20 +++-
 contrib/dma/crypto.c       |  43 ++++++-
 contrib/dma/dfcompat.c     |   6 +-
 contrib/dma/dma.8          |  17 ++-
 contrib/dma/dma.c          |  38 +++---
 contrib/dma/dma.conf       |   6 +-
 contrib/dma/dma.h          |  17 ++-
 contrib/dma/dns.c          |   5 -
 contrib/dma/get-version.sh |   0
 contrib/dma/local.c        |   5 +-
 contrib/dma/mail.c         |  40 +++++--
 contrib/dma/net.c          | 284 ++++++++++++++++++++++++++++++++-------------
 contrib/dma/spool.c        |   2 +
 contrib/dma/util.c         |  20 ++++
 16 files changed, 378 insertions(+), 129 deletions(-)

diff --cc contrib/dma/Makefile
index aed2ef7246cf,000000000000..8cae5b28f98b
mode 100644,000000..100644
--- a/contrib/dma/Makefile
+++ b/contrib/dma/Makefile
@@@ -1,111 -1,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}"'
++CFLAGS+=	-Wall -Wno-format-truncation -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
diff --cc contrib/dma/conf.c
index b8a6a2e2cbd7,000000000000..13cfac7a6de4
mode 100644,000000..100644
--- a/contrib/dma/conf.c
+++ b/contrib/dma/conf.c
@@@ -1,245 -1,0 +1,261 @@@
 +/*
 + * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
 + *
 + * This code is derived from software contributed to The DragonFly Project
 + * by Matthias Schmidt <matthias at 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(EX_DATAERR);
 +		}
 +		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(EX_NOINPUT, "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(EX_OSERR, "calloc()");
 +
 +		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(EX_CONFIG, "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(EX_NOINPUT, "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)
++		else if (strcmp(word, "FINGERPRINT") == 0) {
++			if (strlen(data) != SHA256_DIGEST_LENGTH * 2) {
++				errlogx(EX_CONFIG, "invalid sha256 fingerprint length");
++			}
++			unsigned char *fingerprint = malloc(SHA256_DIGEST_LENGTH);
++			if (fingerprint == NULL) {
++				errlogx(EX_CONFIG, "fingerprint allocation failed");
++			}
++			unsigned int i;
++			for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
++				if(sscanf(data + 2 * i, "%02hhx", &fingerprint[i]) != 1) {
++					errlogx(EX_CONFIG, "failed to read fingerprint");
++				}
++			}
++			free(data);
++			config.fingerprint = fingerprint;
++		} else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL)
 +			config.features |= TLS_OPP;
 +		else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL)
- 			config.features |= SECURETRANS;
++			config.features |= SECURETRANSFER;
 +		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(EX_CONFIG, "syntax error in %s:%d", config_path, lineno);
 +			/* NOTREACHED */
 +		}
 +	}
 +
 +	if ((config.features & NULLCLIENT) && config.smarthost == NULL) {
 +		errlogx(EX_CONFIG, "%s: NULLCLIENT requires SMARTHOST", config_path);
 +		/* NOTREACHED */
 +	}
 +
 +	fclose(conf);
 +}
diff --cc contrib/dma/dma.8
index cadf899e50fc,a906f75447f2..e0f5e79ff47d
--- a/contrib/dma/dma.8
+++ b/contrib/dma/dma.8
@@@ -305,10 -313,10 +313,11 @@@ will send all mails a
  setting it to
  .Ql percolator
  will send all mails as
 -.Ql Sm off Va username @percolator .
 +.Sm off
 +.Ql Va username @percolator .
  .Sm on
  .It Ic NULLCLIENT Xo
+ (boolean, default=commented)
  .Xc
  Bypass aliases and local delivery, and instead forward all mails to
  the defined
diff --cc contrib/dma/dma.c
index b553c0fa0eef,ae0018a243d2..72115ae2b55e
--- a/contrib/dma/dma.c
+++ b/contrib/dma/dma.c
@@@ -596,8 -595,11 +595,11 @@@ skipopts
  	if (read_aliases() != 0)
  		errlog(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases);
  
+ 	if (newaliases)
+ 		return(0);
+ 
  	if ((sender = set_from(&queue, sender)) == NULL)
 -		errlog(EX_SOFTWARE, NULL);
 +		errlog(EX_SOFTWARE, "set_from()");
  
  	if (newspoolf(&queue) != 0)
  		errlog(EX_CANTCREAT, "can not create temp file in `%s'", config.spooldir);
diff --cc contrib/dma/get-version.sh
index d9691ac37c95,d9691ac37c95..d9691ac37c95
mode 100755,100644..100644
--- a/contrib/dma/get-version.sh
+++ b/contrib/dma/get-version.sh
diff --cc contrib/dma/mail.c
index ad928272e1a1,000000000000..48c8ee6d4dd2
mode 100644,000000..100644
--- a/contrib/dma/mail.c
+++ b/contrib/dma/mail.c
@@@ -1,492 -1,0 +1,518 @@@
 +/*
 + * Copyright (c) 2008-2014, Simon Schubert <2 at 0x2c.org>.
 + * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
 + *
 + * This code is derived from software contributed to The DragonFly Project
 + * by Simon Schubert <2 at 0x2c.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.
 + */
 +
 +#include <errno.h>
 +#include <inttypes.h>
 +#include <signal.h>
++#include <strings.h>
 +#include <string.h>
 +#include <syslog.h>
 +#include <unistd.h>
 +
 +#include "dma.h"
 +
 +#define MAX_LINE_RFC822	1000
 +
 +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(EX_SOFTWARE);
 +	}
 +
 +	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"
++		"\tby %s (%s on %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,
++		hostname(), VERSION, systemhostname(),
 +		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(EX_IOERR);
 +}
 +
 +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 */
++			ps->state = MAIN;
 +			break;
 +
 +		default:
 +			ps->state = QUIT;
 +			if (ps->pos != 0)
 +				goto newaddr;
 +			return (0);
 +		}
++		break;
 +
 +	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(EX_SOFTWARE, "strdup");
 +
 +	if (add_recp(queue, addr, EXPAND_WILDCARD) != 0)
 +		errlogx(EX_DATAERR, "invalid recipient `%s'", addr);
 +
 +	goto again;
 +}
 +
 +static int
 +writeline(struct queue *queue, const char *line, ssize_t linelen)
 +{
 +	ssize_t len;
 +
 +	while (linelen > 0) {
 +		len = linelen;
 +		if (linelen > MAX_LINE_RFC822) {
 +			len = MAX_LINE_RFC822 - 10;
 +		}
 +
 +		if (fwrite(line, len, 1, queue->mailf) != 1)
 +			return (-1);
 +
 +		if (linelen <= MAX_LINE_RFC822)
 +			break;
 +
 +		if (fwrite("\n", 1, 1, queue->mailf) != 1)
 +			return (-1);
 +
 +		line += MAX_LINE_RFC822 - 10;
 +		linelen = strlen(line);
 +	}
 +	return (0);
 +}
 +
 +int
 +readmail(struct queue *queue, int nodot, int recp_from_header)
 +{
 +	struct parse_state parse_state;
 +	char *line = NULL;
 +	ssize_t linelen;
 +	size_t linecap = 0;
 +	char newline[MAX_LINE_RFC822];
 +	size_t error;
 +	int had_headers = 0;
 +	int had_from = 0;
 +	int had_messagid = 0;
 +	int had_date = 0;
++	int had_first_line = 0;
++	int had_last_line = 0;
 +	int nocopy = 0;
 +	int ret = -1;
 +
 +	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"
++		"\tby %s (%s on %s);\n"
 +		"\t%s\n",
 +		username, useruid,
 +		queue->sender,
 +		queue->id,
- 		hostname(), VERSION,
++		hostname(), VERSION, systemhostname(),
 +		rfc822date());
 +	if ((ssize_t)error < 0)
 +		return (-1);
 +
 +	while (!feof(stdin)) {
 +		newline[0] = '\0';
 +		if ((linelen = getline(&line, &linecap, stdin)) <= 0)
 +			break;
- 
++		if (had_last_line)
++			errlogx(EX_DATAERR, "bad mail input format:"
++				" from %s (uid %d) (envelope-from %s)",
++				username, useruid, queue->sender);
++		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_first_line) {
++			/*
++			 * Ignore a leading RFC-976 From_ or >From_ line mistakenly
++			 * inserted by some programs.
++			 */
++			if (strprefixcmp(line, "From ") == 0 || strprefixcmp(line, ">From ") == 0)
++				continue;
++			had_first_line = 1;
++		}
 +		if (!had_headers) {
 +			if (linelen > MAX_LINE_RFC822) {
 +				/* XXX also split headers */
 +				errlogx(EX_DATAERR, "bad mail input format:"
 +				    " from %s (uid %d) (envelope-from %s)",
 +				    username, useruid, queue->sender);
 +			}
 +			/*
 +			 * 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(EX_DATAERR, "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(EX_DATAERR, "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(newline, sizeof(newline), "Date: %s\n", rfc822date());
 +				} else if (!had_messagid) {
 +					/* XXX msgid, assign earlier and log? */
 +					had_messagid = 1;
 +					snprintf(newline, sizeof(newline), "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(newline, sizeof(newline), "From: <%s>\n", queue->sender);
 +				}
 +				if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1)
 +					goto fail;
 +			}
 +			strlcpy(newline, "\n", sizeof(newline));
 +		}
 +		if (!nodot && linelen == 2 && line[0] == '.')
 +			break;
 +		if (!nocopy) {
 +			if (newline[0]) {
 +				if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1)
 +					goto fail;
 +			} else {
 +				if (writeline(queue, line, linelen) != 0)
 +					goto fail;
 +			}
 +		}
 +	}
 +
 +	ret = 0;
 +fail:
 +	free(line);
 +	return (ret);
*** 1 LINES SKIPPED ***


More information about the dev-commits-src-main mailing list