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 /mfd/savclt.c | |
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.
Diffstat (limited to 'mfd/savclt.c')
-rw-r--r-- | mfd/savclt.c | 275 |
1 files changed, 275 insertions, 0 deletions
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); +} + |