aboutsummaryrefslogtreecommitdiff
path: root/mfd/savclt.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-11-21 23:13:13 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2009-11-21 23:13:13 +0200
commit7f4dfb88f23a14f0b9603e648d4a1a459a6f26a3 (patch)
tree2beb9dee6a3a93130bd7f783a62afa20ed14e853 /mfd/savclt.c
parentd20b648b02aa1f8caf9975a116c54e05d67d1c39 (diff)
downloadmailfromd-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.c275
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);
+}
+

Return to:

Send suggestions and report system problems to the System administrator.