From def10dc7aa9ba0531c9342718772e2530a9f10eb Mon Sep 17 00:00:00 2001 From: Eric Faurot Date: Fri, 25 Oct 2013 21:31:24 +0000 Subject: local enqueuer improvements: - parse the whole input before trying to establish the connection to the local socket: fixes timeout problems when reading the output of a long running program. - use sendmail(8)-like exit status. --- usr.sbin/smtpd/enqueue.c | 93 ++++++++++++++++++++++++++---------------------- usr.sbin/smtpd/parse.y | 72 +------------------------------------ usr.sbin/smtpd/smtpctl.c | 8 ++--- usr.sbin/smtpd/smtpd.h | 4 +-- usr.sbin/smtpd/util.c | 71 +++++++++++++++++++++++++++++++++++- 5 files changed, 127 insertions(+), 121 deletions(-) (limited to 'usr.sbin/smtpd') diff --git a/usr.sbin/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c index e51f4a220fa..24f9abde637 100644 --- a/usr.sbin/smtpd/enqueue.c +++ b/usr.sbin/smtpd/enqueue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: enqueue.c,v 1.68 2013/05/24 17:03:14 eric Exp $ */ +/* $OpenBSD: enqueue.c,v 1.69 2013/10/25 21:31:23 eric Exp $ */ /* * Copyright (c) 2005 Henning Brauer @@ -30,11 +30,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include @@ -43,7 +43,6 @@ extern struct imsgbuf *ibuf; void usage(void); -static void sighdlr(int); static void build_from(char *, struct passwd *); static int parse_message(FILE *, int, int, FILE *); static void parse_addr(char *, size_t, int); @@ -53,6 +52,9 @@ static void rcpt_add(char *); static int open_connection(void); static void get_responses(FILE *, int); static int send_line(FILE *, int, char *, ...); +static int enqueue_offline(int, char *[], FILE *); + +extern int srv_connect(void); enum headerfields { HDR_NONE, @@ -125,15 +127,6 @@ struct { char buf[SMTP_LINELEN]; } pstate; -static void -sighdlr(int sig) -{ - if (sig == SIGALRM) { - write(STDERR_FILENO, TIMEOUTMSG, sizeof(TIMEOUTMSG)); - _exit(2); - } -} - static void qp_encoded_write(FILE *fp, char *buf, size_t len) { @@ -169,14 +162,21 @@ enqueue(int argc, char *argv[]) char *fake_from = NULL, *buf; struct passwd *pw; FILE *fp, *fout; + int fd; + char sfn[] = "/tmp/smtpd.XXXXXXXXXX"; size_t len; char *line; int dotted; int inheaders = 0; + int save_argc; + char **save_argv; bzero(&msg, sizeof(msg)); time(×tamp); + save_argc = argc; + save_argv = argv; + while ((ch = getopt(argc, argv, "A:B:b:E::e:F:f:iJ::L:mN:o:p:qR:tvx")) != -1) { switch (ch) { @@ -209,7 +209,7 @@ enqueue(int argc, char *argv[]) break; case 'q': /* XXX: implement "process all now" */ - return (0); + return (EX_SOFTWARE); default: usage(); } @@ -218,8 +218,8 @@ enqueue(int argc, char *argv[]) argc -= optind; argv += optind; - if (gethostname(host, sizeof(host)) == -1) - err(1, "gethostname"); + if (getmailname(host, sizeof(host)) == -1) + err(EX_NOHOST, "getmailname"); if ((pw = getpwuid(getuid())) == NULL) user = "anonymous"; if (pw != NULL) @@ -233,26 +233,35 @@ enqueue(int argc, char *argv[]) argc--; } - signal(SIGALRM, sighdlr); - alarm(300); - - fp = tmpfile(); - if (fp == NULL) - err(1, "tmpfile"); + if ((fd = mkstemp(sfn)) == -1 || + (fp = fdopen(fd, "w+")) == NULL) { + if (fd != -1) { + unlink(sfn); + close(fd); + } + err(EX_UNAVAILABLE, "mkstemp"); + } + unlink(sfn); noheader = parse_message(stdin, fake_from == NULL, tflag, fp); if (msg.rcpt_cnt == 0) - errx(1, "no recipients"); + errx(EX_SOFTWARE, "no recipients"); /* init session */ rewind(fp); + /* try to connect */ + /* If the server is not running, enqueue the message offline */ + + if (!srv_connect()) + return (enqueue_offline(save_argc, save_argv, fp)); + if ((msg.fd = open_connection()) == -1) - errx(1, "server too busy"); + errx(EX_UNAVAILABLE, "server too busy"); fout = fdopen(msg.fd, "a+"); if (fout == NULL) - err(1, "fdopen"); + err(EX_UNAVAILABLE, "fdopen"); /* * We need to call get_responses after every command because we don't @@ -265,11 +274,11 @@ enqueue(int argc, char *argv[]) send_line(fout, verbose, "EHLO localhost\n"); get_responses(fout, 1); - send_line(fout, verbose, "MAIL FROM: <%s>\n", msg.from); + send_line(fout, verbose, "MAIL FROM:<%s>\n", msg.from); get_responses(fout, 1); for (i = 0; i < msg.rcpt_cnt; i++) { - send_line(fout, verbose, "RCPT TO: <%s>\n", msg.rcpts[i]); + send_line(fout, verbose, "RCPT TO:<%s>\n", msg.rcpts[i]); get_responses(fout, 1); } @@ -318,12 +327,12 @@ enqueue(int argc, char *argv[]) for (;;) { buf = fgetln(fp, &len); if (buf == NULL && ferror(fp)) - err(1, "fgetln"); + err(EX_UNAVAILABLE, "fgetln"); if (buf == NULL && feof(fp)) break; /* newlines have been normalized on first parsing */ if (buf[len-1] != '\n') - errx(1, "expect EOL"); + errx(EX_SOFTWARE, "expect EOL"); dotted = 0; if (buf[0] == '.') { @@ -365,7 +374,7 @@ enqueue(int argc, char *argv[]) fclose(fp); fclose(fout); - exit(0); + exit(EX_OK); } static void @@ -377,7 +386,7 @@ get_responses(FILE *fin, int n) fflush(fin); if ((e = ferror(fin))) - errx(1, "ferror: %i", e); + errx(1, "ferror: %d", e); while (n) { buf = fgetln(fin, &len); @@ -728,8 +737,8 @@ open_connection(void) return fd; } -int -enqueue_offline(int argc, char *argv[]) +static int +enqueue_offline(int argc, char *argv[], FILE *ifile) { char path[SMTPD_MAXPATHLEN]; FILE *fp; @@ -737,51 +746,51 @@ enqueue_offline(int argc, char *argv[]) mode_t omode; if (ckdir(PATH_SPOOL PATH_OFFLINE, 01777, 0, 0, 0) == 0) - errx(1, "error in offline directory setup"); + errx(EX_UNAVAILABLE, "error in offline directory setup"); if (! bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL, PATH_OFFLINE, (long long int) time(NULL))) - err(1, "snprintf"); + err(EX_UNAVAILABLE, "snprintf"); omode = umask(7077); if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { warn("cannot create temporary file %s", path); if (fd != -1) unlink(path); - exit(1); + exit(EX_UNAVAILABLE); } umask(omode); if (fchmod(fd, 0600) == -1) { unlink(path); - exit(1); + exit(EX_SOFTWARE); } for (i = 1; i < argc; i++) { if (strchr(argv[i], '|') != NULL) { warnx("%s contains illegal character", argv[i]); unlink(path); - exit(1); + exit(EX_SOFTWARE); } fprintf(fp, "%s%s", i == 1 ? "" : "|", argv[i]); } fprintf(fp, "\n"); - while ((ch = fgetc(stdin)) != EOF) + while ((ch = fgetc(ifile)) != EOF) if (fputc(ch, fp) == EOF) { warn("write error"); unlink(path); - exit(1); + exit(EX_UNAVAILABLE); } - if (ferror(stdin)) { + if (ferror(ifile)) { warn("read error"); unlink(path); - exit(1); + exit(EX_UNAVAILABLE); } fclose(fp); - return (0); + return (EX_TEMPFAIL); } diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y index dedaa27c582..6b781c3d983 100644 --- a/usr.sbin/smtpd/parse.y +++ b/usr.sbin/smtpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.123 2013/07/19 21:14:52 eric Exp $ */ +/* $OpenBSD: parse.y,v 1.124 2013/10/25 21:31:23 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade @@ -104,7 +104,6 @@ int interface(const char *, int, const char *, const char *, void set_localaddrs(void); int delaytonum(char *); int is_if_in_group(const char *, const char *); -int getmailname(char *, size_t); typedef struct { union { @@ -1921,72 +1920,3 @@ end: close(s); return ret; } - -int -getmailname(char *hostname, size_t len) -{ - struct addrinfo hints, *res = NULL; - FILE *fp; - char *buf, *lbuf = NULL; - size_t buflen; - int error; - int ret = 0; - - /* First, check if we have "/etc/mailname" */ - if ((fp = fopen("/etc/mailname", "r")) == NULL) - goto nomailname; - - if ((buf = fgetln(fp, &buflen)) == NULL) - goto end; - - if (buf[buflen-1] == '\n') - buf[buflen - 1] = '\0'; - else { - if ((lbuf = calloc(buflen + 1, 1)) == NULL) - err(1, "calloc"); - memcpy(lbuf, buf, buflen); - } - - if (strlcpy(hostname, buf, len) >= len) - fprintf(stderr, "/etc/mailname entry too long"); - else { - ret = 1; - goto end; - } - - -nomailname: - if (gethostname(hostname, len) == -1) { - fprintf(stderr, "invalid hostname: gethostname() failed\n"); - goto end; - } - - if (strchr(hostname, '.') == NULL) { - memset(&hints, 0, sizeof hints); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_CANONNAME; - error = getaddrinfo(hostname, NULL, &hints, &res); - if (error) { - fprintf(stderr, "invalid hostname: getaddrinfo() failed: %s\n", - gai_strerror(error)); - goto end; - } - - if (strlcpy(hostname, res->ai_canonname, len) >= len) { - fprintf(stderr, "hostname too long"); - goto end; - } - } - - ret = 1; - -end: - free(lbuf); - if (res) - freeaddrinfo(res); - if (fp) - fclose(fp); - return ret; -} diff --git a/usr.sbin/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c index 006fed00cac..757bb5650a9 100644 --- a/usr.sbin/smtpd/smtpctl.c +++ b/usr.sbin/smtpd/smtpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpctl.c,v 1.109 2013/10/25 18:58:10 eric Exp $ */ +/* $OpenBSD: smtpctl.c,v 1.110 2013/10/25 21:31:23 eric Exp $ */ /* * Copyright (c) 2013 Eric Faurot @@ -725,7 +725,7 @@ do_show_stats(int argc, struct parameter *argv) (long long)kv.val.u.tv.tv_usec); break; case STAT_TIMESPEC: - printf("%s=%lli.%06li\n", + printf("%s=%lld.%06ld\n", kv.key, (long long)kv.val.u.ts.tv_sec * 1000000 + kv.val.u.ts.tv_nsec / 1000000, @@ -795,9 +795,7 @@ main(int argc, char **argv) if (strcmp(__progname, "sendmail") == 0 || strcmp(__progname, "send-mail") == 0) { sendmail = 1; - if (srv_connect()) - return (enqueue(argc, argv)); - return (enqueue_offline(argc, argv)); + return (enqueue(argc, argv)); } if (geteuid()) diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index 3eaf192d8c2..39c94a568f9 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.423 2013/07/19 22:22:39 eric Exp $ */ +/* $OpenBSD: smtpd.h,v 1.424 2013/10/25 21:31:23 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade @@ -1065,7 +1065,6 @@ void dns_imsg(struct mproc *, struct imsg *); /* enqueue.c */ int enqueue(int, char **); -int enqueue_offline(int, char **); /* envelope.c */ @@ -1365,6 +1364,7 @@ void log_envelope(const struct envelope *, const char *, const char *, void session_socket_blockmode(int, enum blockmodes); void session_socket_no_linger(int); int session_socket_error(int); +int getmailname(char *, size_t); /* waitq.c */ diff --git a/usr.sbin/smtpd/util.c b/usr.sbin/smtpd/util.c index 509915a3627..8e3dbad81fd 100644 --- a/usr.sbin/smtpd/util.c +++ b/usr.sbin/smtpd/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.97 2013/07/19 08:28:43 eric Exp $ */ +/* $OpenBSD: util.c,v 1.98 2013/10/25 21:31:23 eric Exp $ */ /* * Copyright (c) 2000,2001 Markus Friedl. All rights reserved. @@ -722,3 +722,72 @@ parse_smtp_response(char *line, size_t len, char **msg, int *cont) return NULL; } + +int +getmailname(char *hostname, size_t len) +{ + struct addrinfo hints, *res = NULL; + FILE *fp; + char *buf, *lbuf = NULL; + size_t buflen; + int error, ret = 0; + + /* First, check if we have "/etc/mailname" */ + if ((fp = fopen("/etc/mailname", "r")) == NULL) + goto nomailname; + + if ((buf = fgetln(fp, &buflen)) == NULL) + goto end; + + if (buf[buflen-1] == '\n') + buf[buflen - 1] = '\0'; + else { + if ((lbuf = calloc(buflen + 1, 1)) == NULL) { + log_warn("calloc"); + fatal("exiting"); + } + memcpy(lbuf, buf, buflen); + } + + if (strlcpy(hostname, buf, len) >= len) + fprintf(stderr, "/etc/mailname entry too long"); + else { + ret = 1; + goto end; + } + +nomailname: + if (gethostname(hostname, len) == -1) { + fprintf(stderr, "invalid hostname: gethostname() failed\n"); + goto end; + } + + if (strchr(hostname, '.') == NULL) { + memset(&hints, 0, sizeof hints); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(hostname, NULL, &hints, &res); + if (error) { + fprintf(stderr, "invalid hostname: getaddrinfo() failed: %s\n", + gai_strerror(error)); + goto end; + } + + if (strlcpy(hostname, res->ai_canonname, len) >= len) { + fprintf(stderr, "hostname too long"); + goto end; + } + } + + ret = 1; + +end: + free(lbuf); + if (res) + freeaddrinfo(res); + if (fp) + fclose(fp); + return ret; +} -- cgit v1.2.3