diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-11-21 23:13:13 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-11-21 23:13:13 +0200 |
commit | 7f4dfb88f23a14f0b9603e648d4a1a459a6f26a3 (patch) | |
tree | 2beb9dee6a3a93130bd7f783a62afa20ed14e853 | |
parent | d20b648b02aa1f8caf9975a116c54e05d67d1c39 (diff) | |
download | mailfromd-mtax-cleanup.tar.gz mailfromd-mtax-cleanup.tar.bz2 |
Use callout resolver to handle timed-out callouts.mtax-cleanup
* mfd/savclt.c: New file.
* mfd/Makefile.am (mailfromd_SOURCES): Add savclt.c
* mfd/callout.c (transcript): Get ID as 1st arg.
Remove static qualifier. All callers updated.
* mfd/engine.c (method_strict)
(method_standard): If callout returned mf_temp_failure,
try to pass the task to the callout server, if one is
defined.
* mfd/mailfromd.h (transcript)
(schedule_callout): New protos.
(callout_server_sa, callout_server_sa_len): New externs.
* mfd/main.c (force_remove): Initialize to 0 (see srvman.c)
(server_config_stmt): New statements single-process and
reuseaddr.
(add_legacy_milter_port, server_section_parser): Pass
flags to mfd_server_new.
(mf_cfg_param): New statement `callout-url'.
* mfd/savsrv.c (MF_SOURCE_NAME): Fixed.
(verify, callout_session_server): Set proctitle.
(callout_session_server): Fix memory leak.
* mfd/srvman.c (struct mfd_server): New member `flags'.
(mfd_server_new): Take flags as 4th argument.
(server_run): Use flags to set single-user and reuseaddr
modes.
* mfd/srvman.h (SRV_SINGLE_PROCESS)
(SRV_KEEP_EXISTING): New defines.
(mfd_server_new): Change signature.
(srvman_url_to_sockaddr): New proto.
-rw-r--r-- | mfd/Makefile.am | 1 | ||||
-rw-r--r-- | mfd/callout.c | 10 | ||||
-rw-r--r-- | mfd/engine.c | 6 | ||||
-rw-r--r-- | mfd/mailfromd.h | 15 | ||||
-rw-r--r-- | mfd/main.c | 55 | ||||
-rw-r--r-- | mfd/savclt.c | 275 | ||||
-rw-r--r-- | mfd/savsrv.c | 14 | ||||
-rw-r--r-- | mfd/srvman.c | 11 | ||||
-rw-r--r-- | mfd/srvman.h | 7 |
9 files changed, 364 insertions, 30 deletions
diff --git a/mfd/Makefile.am b/mfd/Makefile.am index 962c22a0..c68afcff 100644 --- a/mfd/Makefile.am +++ b/mfd/Makefile.am @@ -74,2 +74,3 @@ mailfromd_SOURCES = \ prog.h\ + savclt.c\ savsrv.c\ diff --git a/mfd/callout.c b/mfd/callout.c index c028838e..acecb26f 100644 --- a/mfd/callout.c +++ b/mfd/callout.c @@ -131,4 +131,4 @@ smtp_io_free(struct smtp_io_data *iop) -static void -transcript(struct smtp_io_data *io, char *prefix, const char *msg) +void +transcript(const char *id, char *prefix, const char *msg) { @@ -143,3 +143,3 @@ transcript(struct smtp_io_data *io, char *prefix, const char *msg) logmsg(LOG_INFO, "%s: %s %*.*s", - io->id, + id, prefix, len, len, msg); @@ -218,3 +218,3 @@ smtp_send(struct smtp_io_data *iop, const char *command) iop->reply = NULL; /* Clear reply for logging purposes */ - transcript(iop, "SEND:", command); + transcript(iop->id, "SEND:", command); do { @@ -345,3 +345,3 @@ smtp_recv(struct smtp_io_data *iop, enum smtp_timeout to) return -1; - transcript(iop, "RECV:", iop->reply); + transcript(iop->id, "RECV:", iop->reply); code = strtoul(iop->reply, &p, 0); diff --git a/mfd/engine.c b/mfd/engine.c index e1f2de06..c9741c1b 100644 --- a/mfd/engine.c +++ b/mfd/engine.c @@ -273,2 +273,3 @@ mfl_smtp_io_callback(void *data, const char *key, const char *value) + /* Method "strict". Verifies whether EMAIL is understood either by @@ -301,2 +302,4 @@ method_strict(eval_environ_t env, char *email, char *client_addr, cache_insert2(email, client_addr, rc); + else if (rc == mf_temp_failure) + schedule_callout(email, ehlo, mailfrom, client_addr); } else { @@ -332,2 +335,3 @@ method_standard(eval_environ_t env, char *email, char *ehlo, char *mailfrom) env); + smtp_io_setup_callout(io, email, ehlo, mailfrom); rc = callout_standard(io); @@ -336,2 +340,4 @@ method_standard(eval_environ_t env, char *email, char *ehlo, char *mailfrom) cache_insert(email, rc); + else if (rc == mf_temp_failure) + schedule_callout(email, ehlo, mailfrom, NULL); } else { diff --git a/mfd/mailfromd.h b/mfd/mailfromd.h index 5df54636..74cf1079 100644 --- a/mfd/mailfromd.h +++ b/mfd/mailfromd.h @@ -988,6 +988,2 @@ int xeval(eval_environ_t env, enum smtp_state tag); mf_status listens_on (const char *client_addr, int port, time_t timeout); -mf_status check_on_host(eval_environ_t env, char *email, char *client_addr, - char *ehlo, char *mailfrom); -mf_status check_mx_records(eval_environ_t env, char *email, char *client_addr, - char *ehlo, char *mailfrom, int *pcount); mf_status method_strict(eval_environ_t env, char *email, char *client_addr, @@ -1149,3 +1145,5 @@ mf_status callout_strict(struct smtp_io_data *, const char *); mf_status callout_standard(struct smtp_io_data *); - + +void transcript(const char *id, char *prefix, const char *msg); + /* savsrv.c */ @@ -1155 +1153,8 @@ int callout_session_server(const char *id, int fd, +/* savclt.c */ +void schedule_callout(const char *email, const char *ehlo, + const char *mailfrom, + const char *client_addr); +extern struct sockaddr *callout_server_sa; +extern socklen_t callout_server_sa_len; + @@ -72,5 +72,5 @@ unsigned optimization_level = 1; /* Optimization level */ int log_to_stderr; /* Use stderr for logging */ -int force_remove = 1; /* Remove local communication socket if it already - exists */ int foreground; /* Stay in foreground */ +int force_remove; /* Remove local communication socket if it already + exists */ int single_process_option; /* Run in single process mode. */ @@ -624,3 +624,4 @@ add_legacy_milter_port(const char *str, mu_debug_t dbg) mfd_server_t srv = mfd_server_new(id, url, - milter_session_server); + milter_session_server, + 0); if (srv) @@ -1383,3 +1384,2 @@ parse_opt(int key, char *arg, struct argp_state *state) case 'r': - /*FIXME*/ force_remove = 1; @@ -2101,2 +2101,4 @@ struct server_config_stmt { mfd_server_func_t server; + int single_process; + int reuseaddr; }; @@ -2128,2 +2130,8 @@ struct mu_cfg_param server_section_param[] = { N_("Maximum number of instances allowed for this server.") }, + { "single-process", mu_cfg_bool, + &server_config_stmt.single_process, 0, NULL, + N_("Single-process mode.") }, + { "reuseaddr", mu_cfg_bool, + &server_config_stmt.reuseaddr, 0, NULL, + N_("Reuse existing socket (default).") }, { "acl", mu_cfg_section, @@ -2143,2 +2151,3 @@ server_section_parser(enum mu_cfg_section_stage stage, memset(&server_config_stmt, 0, sizeof(server_config_stmt)); + server_config_stmt.reuseaddr = 1; if (mu_cfg_assert_value_type (node->label, MU_CFG_STRING, @@ -2162,6 +2171,13 @@ server_section_parser(enum mu_cfg_section_stage stage, if (server_config_stmt.url && server_config_stmt.server) { - mfd_server_t srv = - mfd_server_new(server_config_stmt.id, - server_config_stmt.url, - server_config_stmt.server); + int flags = 0; + mfd_server_t srv; + + if (server_config_stmt.single_process) + flags |= SRV_SINGLE_PROCESS; + if (!server_config_stmt.reuseaddr) + flags |= SRV_KEEP_EXISTING; + srv = mfd_server_new(server_config_stmt.id, + server_config_stmt.url, + server_config_stmt.server, + flags); if (srv) { @@ -2204,2 +2220,18 @@ cb_milter_listen(mu_debug_t err, void *data, mu_config_value_t *arg) +static int +cb_callout_url(mu_debug_t err, void *data, mu_config_value_t *arg) +{ + mu_url_t url; + if (mu_cfg_assert_value_type(arg, MU_CFG_STRING, err)) + return 1; + url = parse_milter_url(err, arg->v.string); + if (url) { + callout_server_sa = srvman_url_to_sockaddr(url, + &callout_server_sa_len); + mu_url_destroy(&url); + } + return 0; +} + + struct mu_cfg_param mf_cfg_param[] = { @@ -2294,2 +2326,9 @@ struct mu_cfg_param mf_cfg_param[] = { { "database", mu_cfg_section, NULL }, + + { "callout-url", mu_cfg_callback, + NULL, 0, + cb_callout_url, + N_("URL of the callout server"), + N_("url") }, + diff --git a/mfd/savclt.c b/mfd/savclt.c new file mode 100644 index 00000000..447f6857 --- /dev/null +++ b/mfd/savclt.c @@ -0,0 +1,275 @@ +/* This file is part of Mailfromd. + Copyright (C) 2005, 2006, 2007, 2008, 2009 Sergey Poznyakoff + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define MF_SOURCE_NAME MF_SOURCE_SAVCLT +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "mailfromd.h" + +struct sockaddr *callout_server_sa; +socklen_t callout_server_sa_len; +time_t savclt_timeout = 3; + +/* Ideally, I'd want to use smtp_io functions here. But they are + based on Mailutils' TCP streams, which so far do not support + UNIX sockets. So, for the time being, I use the straightforward + I/O approach. */ +int +savclt_getline(int fd, char **pbuf, size_t *pbufsize) +{ + char *buf = *pbuf; + size_t bufsize = *pbufsize; + int off = 0; +#define DELTA 128 + time_t start = time(NULL); + + if (buf == NULL) { + bufsize = DELTA; + buf = xmalloc(bufsize); + } + + do { + struct timeval tv; + int rc, flags; + time_t diff = time(NULL) - start; + + if (diff > savclt_timeout) { + errno = ETIMEDOUT; + return -1; + } + + tv.tv_sec = savclt_timeout - diff; + tv.tv_usec = 0; + flags = MU_STREAM_READY_RD; + rc = mu_fd_wait(fd, &flags, &tv); + if (rc) { + debug1(1, "mu_fd_wait: %s", mu_strerror(rc)); + errno = rc; + return -1; + } + if (!(flags & MU_STREAM_READY_RD)) { + errno = ETIMEDOUT; + return -1; + } + + if (off + 1 == bufsize) { + bufsize += DELTA; + buf = xrealloc(buf, bufsize); + } + + rc = read(fd, buf + off, 1); + if (rc == -1) + return -1; + if (rc == 0) + break; + off++; + } while (buf[off - 1] != '\n'); + + if (off + 1 == bufsize) { + bufsize++; + buf = xrealloc(buf, bufsize); + } + buf[off] = 0; + *pbuf = buf; + *pbufsize = bufsize; + transcript("savclt", "RECV:", buf); + return off; +} + +static int +send_line(int fd, const char *txt, size_t len) +{ + time_t start = time(NULL); + + while (len) { + struct timeval tv; + int rc, flags; + time_t diff = time(NULL) - start; + + if (diff > savclt_timeout) { + errno = ETIMEDOUT; + return -1; + } + + tv.tv_sec = savclt_timeout - diff; + tv.tv_usec = 0; + flags = MU_STREAM_READY_WR; + rc = mu_fd_wait(fd, &flags, &tv); + if (rc) { + debug1(1, "mu_fd_wait: %s", mu_strerror(rc)); + errno = rc; + return -1; + } + if (!(flags & MU_STREAM_READY_WR)) { + errno = ETIMEDOUT; + return -1; + } + + rc = write(fd, txt, len); + if (rc <= 0) { + if (errno == EAGAIN) + continue; + return -1; + } + txt += rc; + len -= rc; + } + return 0; +} + +int +savclt_wrtline(int fd, char **pbuf, size_t *pbufsize, + const char *fmt, ...) +{ + int rc; + va_list ap; + + va_start(ap, fmt); + rc = mu_vasnprintf(pbuf, pbufsize, fmt, ap); + va_end(ap); + if (rc == 0) { + transcript("savclt", "SEND:", *pbuf); + rc = send_line(fd, *pbuf, strlen(*pbuf)); + } + return rc; +} + + +static int +connect_callout_server() +{ + int fd; + int flags; + int rc; + + if (!callout_server_sa) + return -1; + fd = socket(callout_server_sa->sa_family, SOCK_STREAM, 0); + if (fd == -1) { + mu_error("schedule_callout: socket: %s", + mu_strerror(errno)); + return -1; + } + + flags = fcntl(fd, F_GETFL); + flags |= O_NONBLOCK; + fcntl(fd, F_SETFL, flags); + + rc = connect(fd, callout_server_sa, callout_server_sa_len); + if (rc == -1) { + if (errno == EINPROGRESS) { + struct timeval tv; + + tv.tv_sec = savclt_timeout; + tv.tv_usec = 0; + flags = MU_STREAM_READY_WR; + rc = mu_fd_wait(fd, &flags, &tv); + if (rc) { + debug1(1, "mu_fd_wait: %s", mu_strerror(rc)); + close(fd); + return -1; + } + + if (flags & MU_STREAM_READY_WR) { + socklen_t len = sizeof(flags); + rc = getsockopt(fd, SOL_SOCKET, SO_ERROR, + &flags, &len); + if (rc) { + mu_error("getsockopt: %s", + mu_strerror(rc)); + close(fd); + return -1; + } + } else + flags = ETIMEDOUT; + } else + flags = errno; + + if (flags) { + mu_error("connect: %s", mu_strerror(flags)); + close(fd); + return -1; + } + } + + if (rc) { + close(fd); + return -1; + } + + return fd; +} + +enum callout_server_state { + css_init, + css_send, + css_quit, +}; + +void +schedule_callout(const char *email, const char *ehlo, const char *mailfrom, + const char *client_addr) +{ + size_t size = 0; + char *buf = NULL; + int fd = connect_callout_server(); + enum callout_server_state state = css_init; + int rc; + + while (state != css_quit) { + if (savclt_getline(fd, &buf, &size) < 0) + break; + trimcrlf(buf); + if (!(strlen(buf) > 2 && memcmp(buf, "OK", 2) == 0 + && isspace(buf[2]))) + break; + + switch (state) { + case css_init: + if (client_addr) + rc = savclt_wrtline(fd, &buf, &size, + "SVRFY \"%s\" \"%s\" \"%s\" \"%s\"\r\n", + email, ehlo, mailfrom, client_addr); + else + rc = savclt_wrtline(fd, &buf, &size, + "VRFY \"%s\" \"%s\" \"%s\"\r\n", + email, ehlo, mailfrom); + state = (rc < 0) ? css_quit : css_send; + break; + + case css_send: + savclt_wrtline(fd, &buf, &size, "QUIT\r\n"); + break; + + case css_quit: + break; + } + } + close(fd); + free(buf); +} + diff --git a/mfd/savsrv.c b/mfd/savsrv.c index 4e90d364..34f95b43 100644 --- a/mfd/savsrv.c +++ b/mfd/savsrv.c @@ -16,3 +16,3 @@ -#define MF_SOURCE_NAME MF_SOURCE_CALLOUT +#define MF_SOURCE_NAME MF_SOURCE_SAVSRV #ifdef HAVE_CONFIG_H @@ -25,7 +25,2 @@ #include <stdio.h> -#include <stdarg.h> -#include <syslog.h> -#include <signal.h> -#include <pwd.h> -#include <grp.h> #include <sys/socket.h> @@ -59,2 +54,3 @@ verify(struct vrfy_queue *qp, FILE *fp) + mf_proctitle_format("callout server: %s", smtp_io_email(qp->io)); if (qp->hostname) { @@ -194,2 +190,3 @@ callout_session_server(const char *id, int fd, + mf_proctitle_format("callout server"); setvbuf(fp, NULL, _IOLBF, 0); @@ -203,2 +200,4 @@ callout_session_server(const char *id, int fd, trimcrlf(buf); + debug1(2,"C: %s", buf); + if (mu_argcv_get(buf, "", NULL, &argc, &argv)) { @@ -220,3 +219,4 @@ callout_session_server(const char *id, int fd, fclose(fp); - + free(buf); + /* Run queued verifications */ diff --git a/mfd/srvman.c b/mfd/srvman.c index 47a5b54d..67433858 100644 --- a/mfd/srvman.c +++ b/mfd/srvman.c @@ -47,2 +47,3 @@ struct mfd_server { int fd; /* Socket descriptor */ + int flags; /* SRV_* flags */ mfd_server_prefork_hook_t prefork_hook; /* Pre-fork function */ @@ -380,3 +381,3 @@ mfd_server_shutdown(struct mfd_server *srv) struct mfd_server * -mfd_server_new(const char *id, mu_url_t url, mfd_server_func_t conn) +mfd_server_new(const char *id, mu_url_t url, mfd_server_func_t conn, int flags) { @@ -396,2 +397,3 @@ mfd_server_new(const char *id, mu_url_t url, mfd_server_func_t conn) srv->conn = conn; + srv->flags = flags; return srv; @@ -498,3 +500,3 @@ server_run(int connfd, struct mfd_server *srv, - if (single_process_option) { + if (single_process_option || (srv->flags & SRV_SINGLE_PROCESS)) { if ((!srvman_param.prefork_hook @@ -709,3 +711,4 @@ server_prep(struct mfd_server *srv, int fd) return 1; - } else if (force_remove) { + } else if (force_remove + || !(srv->flags & SRV_KEEP_EXISTING)) { if (unlink(s_un->sun_path)) { @@ -724,3 +727,3 @@ server_prep(struct mfd_server *srv, int fd) case AF_INET: - if (force_remove) { + if (force_remove || !(srv->flags & SRV_KEEP_EXISTING)) { t = 1; diff --git a/mfd/srvman.h b/mfd/srvman.h index e2e63fa2..dffce48d 100644 --- a/mfd/srvman.h +++ b/mfd/srvman.h @@ -35,2 +35,5 @@ typedef int (*mfd_srvman_prefork_hook_t) (struct sockaddr const *sa, +#define SRV_SINGLE_PROCESS 0x01 +#define SRV_KEEP_EXISTING 0x02 + struct srvman_param { @@ -50,3 +53,3 @@ void mfd_server_shutdown(mfd_server_t srv); mfd_server_t mfd_server_new(const char *id, mu_url_t url, - mfd_server_func_t conn); + mfd_server_func_t conn, int flags); void mfd_server_free(mfd_server_t srv); @@ -68 +71,3 @@ size_t mfd_srvman_count_servers(void); void mfd_srvman_stop(void); + +struct sockaddr *srvman_url_to_sockaddr(mu_url_t url, size_t *psalen); |