Date: Wed, 22 Sep 2021 09:11:15 GMT From: Baptiste Daroussin <bapt@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: fbe95b885f34 - main - dma: import snapshot 2021-07-10 Message-ID: <202109220911.18M9BFjB002449@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
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@FreeBSD.org> AuthorDate: 2021-09-22 09:09:27 +0000 Commit: Baptiste Daroussin <bapt@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@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@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@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 ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202109220911.18M9BFjB002449>