diff options
Diffstat (limited to 'usr.sbin/smtpd/enqueue.c')
-rw-r--r-- | usr.sbin/smtpd/enqueue.c | 146 |
1 files changed, 122 insertions, 24 deletions
diff --git a/usr.sbin/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c index 05983e41ba5..00ca3bc8ba1 100644 --- a/usr.sbin/smtpd/enqueue.c +++ b/usr.sbin/smtpd/enqueue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: enqueue.c,v 1.85 2014/10/08 10:56:01 deraadt Exp $ */ +/* $OpenBSD: enqueue.c,v 1.86 2014/10/26 11:26:08 gilles Exp $ */ /* * Copyright (c) 2005 Henning Brauer <henning@bulabula.org> @@ -51,9 +51,10 @@ static void parse_addr_terminal(int); static char *qualify_addr(char *); static void rcpt_add(char *); static int open_connection(void); -static void get_responses(FILE *, int); +static int get_responses(FILE *, int); static int send_line(FILE *, int, char *, ...); static int enqueue_offline(int, char *[], FILE *); +static int savedeadletter(struct passwd *, FILE *); extern int srv_connect(void); @@ -120,6 +121,7 @@ struct { int saw_content_disposition; int saw_content_transfer_encoding; int saw_user_agent; + int noheader; } msg; struct { @@ -204,7 +206,7 @@ send_header(FILE *fout, const char *line, size_t len) int enqueue(int argc, char *argv[]) { - int i, ch, tflag = 0, noheader; + int i, ch, tflag = 0; char *fake_from = NULL, *buf; struct passwd *pw; FILE *fp, *fout; @@ -297,7 +299,7 @@ enqueue(int argc, char *argv[]) errc(EX_UNAVAILABLE, saved_errno, "mkstemp"); } unlink(sfn); - noheader = parse_message(stdin, fake_from == NULL, tflag, fp); + msg.noheader = parse_message(stdin, fake_from == NULL, tflag, fp); if (msg.rcpt_cnt == 0) errx(EX_SOFTWARE, "no recipients"); @@ -324,10 +326,12 @@ enqueue(int argc, char *argv[]) */ /* banner */ - get_responses(fout, 1); + if (! get_responses(fout, 1)) + goto fail; send_line(fout, verbose, "EHLO localhost\n"); - get_responses(fout, 1); + if (! get_responses(fout, 1)) + goto fail; if (msg.dsn_envid != NULL) envid_sz = strlen(msg.dsn_envid); @@ -338,18 +342,21 @@ enqueue(int argc, char *argv[]) msg.dsn_ret ? msg.dsn_ret : "", envid_sz ? "ENVID=" : "", envid_sz ? msg.dsn_envid : ""); - get_responses(fout, 1); + if (! get_responses(fout, 1)) + goto fail; for (i = 0; i < msg.rcpt_cnt; i++) { send_line(fout, verbose, "RCPT TO:<%s> %s%s\n", msg.rcpts[i], msg.dsn_notify ? "NOTIFY=" : "", msg.dsn_notify ? msg.dsn_notify : ""); - get_responses(fout, 1); + if (! get_responses(fout, 1)) + goto fail; } send_line(fout, verbose, "DATA\n"); - get_responses(fout, 1); + if (! get_responses(fout, 1)) + goto fail; /* add From */ if (!msg.saw_from) @@ -382,7 +389,7 @@ enqueue(int argc, char *argv[]) } /* add separating newline */ - if (noheader) + if (msg.noheader) send_line(fout, 0, "\n"); else inheaders = 1; @@ -405,7 +412,7 @@ enqueue(int argc, char *argv[]) line = buf; - if (msg.saw_content_transfer_encoding || noheader || + if (msg.saw_content_transfer_encoding || msg.noheader || inheaders || !msg.need_linesplit) { if (inheaders) send_header(fout, line, len); @@ -432,18 +439,25 @@ enqueue(int argc, char *argv[]) } while (len); } send_line(fout, verbose, ".\n"); - get_responses(fout, 1); + if (! get_responses(fout, 1)) + goto fail; send_line(fout, verbose, "QUIT\n"); - get_responses(fout, 1); + if (! get_responses(fout, 1)) + goto fail; fclose(fp); fclose(fout); exit(EX_OK); + +fail: + if (pw) + savedeadletter(pw, fp); + exit(EX_SOFTWARE); } -static void +static int get_responses(FILE *fin, int n) { char *buf; @@ -451,34 +465,45 @@ get_responses(FILE *fin, int n) int e; fflush(fin); - if ((e = ferror(fin))) - errx(1, "ferror: %d", e); + if ((e = ferror(fin))) { + warnx("ferror: %d", e); + return 0; + } while (n) { buf = fgetln(fin, &len); - if (buf == NULL && ferror(fin)) - err(1, "fgetln"); + if (buf == NULL && ferror(fin)) { + warn("fgetln"); + return 0; + } if (buf == NULL && feof(fin)) break; - if (buf == NULL || len < 1) - err(1, "fgetln weird"); + if (buf == NULL || len < 1) { + warn("fgetln weird"); + return 0; + } /* account for \r\n linebreaks */ if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n') buf[--len - 1] = '\n'; - if (len < 4) - errx(1, "bad response"); + if (len < 4) { + warnx("bad response"); + return 0; + } if (verbose) printf("<<< %.*s", (int)len, buf); if (buf[3] == '-') continue; - if (buf[0] != '2' && buf[0] != '3') - errx(1, "command failed: %.*s", (int)len, buf); + if (buf[0] != '2' && buf[0] != '3') { + warnx("command failed: %.*s", (int)len, buf); + return 0; + } n--; } + return 1; } static int @@ -860,3 +885,76 @@ enqueue_offline(int argc, char *argv[], FILE *ifile) return (EX_TEMPFAIL); } + +static int +savedeadletter(struct passwd *pw, FILE *in) +{ + char buffer[SMTPD_MAXPATHLEN]; + FILE *fp; + char *buf, *lbuf; + size_t len; + + (void)snprintf(buffer, sizeof buffer, "%s/dead.letter", pw->pw_dir); + + if (fseek(in, 0, SEEK_SET) != 0) + return 0; + + if ((fp = fopen(buffer, "a")) == NULL) + return 0; + + /* add From */ + if (!msg.saw_from) + fprintf(fp, "From: %s%s<%s>\n", + msg.fromname ? msg.fromname : "", + msg.fromname ? " " : "", + msg.from); + + /* add Date */ + if (!msg.saw_date) + fprintf(fp, "Date: %s\n", time_to_text(timestamp)); + + /* add Message-Id */ + if (!msg.saw_msgid) + fprintf(fp, "Message-Id: <%"PRIu64".enqueue@%s>\n", + generate_uid(), host); + + if (msg.need_linesplit) { + /* we will always need to mime encode for long lines */ + if (!msg.saw_mime_version) + fprintf(fp, "MIME-Version: 1.0\n"); + if (!msg.saw_content_type) + fprintf(fp, "Content-Type: text/plain; " + "charset=unknown-8bit\n"); + if (!msg.saw_content_disposition) + fprintf(fp, "Content-Disposition: inline\n"); + if (!msg.saw_content_transfer_encoding) + fprintf(fp, "Content-Transfer-Encoding: " + "quoted-printable\n"); + } + + /* add separating newline */ + if (msg.noheader) + fprintf(fp, "\n"); + + + lbuf = NULL; + while ((buf = fgetln(in, &len))) { + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + else { + /* EOF without EOL, copy and add the NUL */ + if ((lbuf = malloc(len + 1)) == NULL) + err(1, NULL); + memcpy(lbuf, buf, len); + lbuf[len] = '\0'; + buf = lbuf; + } + fprintf(fp, "%s\n", buf); + } + free(lbuf); + + fprintf(fp, "\n"); + fclose(fp); + + return 1; +} |