aboutsummaryrefslogtreecommitdiff
path: root/gacopyz
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2006-11-03 12:58:18 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2006-11-03 12:58:18 +0000
commite093c2f4719787936463ae2a38ccf1259d6ca644 (patch)
tree3fe3393607ef7999ab846e06ded76c8fb2196828 /gacopyz
parenta45d85f2b8ecf06f2ad23136f58619c138b57606 (diff)
downloadmailfromd-e093c2f4719787936463ae2a38ccf1259d6ca644.tar.gz
mailfromd-e093c2f4719787936463ae2a38ccf1259d6ca644.tar.bz2
See /libmilter for the previous history
git-svn-id: file:///svnroot/mailfromd/trunk@747 7a8a7f39-df28-0410-adc6-e0d955640f24
Diffstat (limited to 'gacopyz')
-rw-r--r--gacopyz/Makefile.am43
-rw-r--r--gacopyz/context.c285
-rw-r--r--gacopyz/dummy.c114
-rw-r--r--gacopyz/gacopyz.c1559
-rw-r--r--gacopyz/gacopyz.h295
-rw-r--r--gacopyz/gacopyz_priv.h66
-rw-r--r--gacopyz/log.c121
-rw-r--r--gacopyz/mfapi.h4
-rw-r--r--gacopyz/proc.c154
-rw-r--r--gacopyz/smfi.c186
-rw-r--r--gacopyz/trans.awk75
-rw-r--r--gacopyz/trans.tab93
12 files changed, 2995 insertions, 0 deletions
diff --git a/gacopyz/Makefile.am b/gacopyz/Makefile.am
new file mode 100644
index 00000000..12e1cba6
--- /dev/null
+++ b/gacopyz/Makefile.am
@@ -0,0 +1,43 @@
+# This file is part of mailfrom filter.
+# Copyright (C) 2006 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 2, 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA
+
+noinst_LIBRARIES = @BUILD_LIBGACOPYZ_A@
+noinst_PROGRAMS = @BUILD_DUMMY@
+EXTRA_LIBRARIES=libgacopyz.a
+EXTRA_PROGRAMS=dummy
+
+libgacopyz_a_SOURCES = \
+ smfi.c\
+ gacopyz.c\
+ gacopyz.h\
+ gacopyz_priv.h\
+ context.c\
+ log.c\
+ mfapi.h\
+ proc.c\
+ trans.h
+
+dummy_SOURCES = dummy.c
+dummy_LDADD = ./libgacopyz.a
+
+EXTRA_DIST=trans.tab trans.awk
+BUILT_SOURCES=trans.h
+
+trans.h: ${top_srcdir}/libmilter/trans.tab ${top_srcdir}/libmilter/trans.awk
+ $(AWK) -f ${top_srcdir}/libmilter/trans.awk \
+ ${top_srcdir}/libmilter/trans.tab > trans.h \ No newline at end of file
diff --git a/gacopyz/context.c b/gacopyz/context.c
new file mode 100644
index 00000000..95e511a9
--- /dev/null
+++ b/gacopyz/context.c
@@ -0,0 +1,285 @@
+/* This file is part of gacopyz.
+ Copyright (C) 2006 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 2, 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA */
+
+#include <gacopyz_priv.h>
+
+int
+smfi_setpriv (SMFICTX *ctx, void *data)
+{
+ if (!ctx)
+ return MI_FAILURE;
+ ctx->privdata = data;
+ return MI_SUCCESS;
+}
+
+void *
+smfi_getpriv (SMFICTX *ctx)
+{
+ if (!ctx)
+ return NULL;
+ return ctx->privdata;
+}
+
+char *
+gacopyz_getsymval(SMFICTX *ctx, char *name)
+{
+ int i;
+ int len;
+
+ if (!ctx || !name)
+ return NULL;
+
+ if (name[0] == '{')
+ name++;
+ len = strlen(name);
+ if (len == 0)
+ return NULL;
+ if (name[len-1] == '}')
+ len--;
+
+ for (i = maci_max - 1; i >= 0; i--) {
+ if (ctx->macros[i].argv) {
+ char **p;
+
+ for (p = ctx->macros[i].argv; *p; p += 2) {
+ if (strlen(*p) == len
+ && memcmp(*p, name, len) == 0)
+ return *++p;
+ }
+ }
+ }
+ return NULL;
+}
+
+static int
+enhanced_code_p(const char *p)
+{
+ int count = 0;
+
+ if (!((*p == '2' || *p == '4' || *p == '5') && p[1] == '.'))
+ return 0;
+
+ while (*p) {
+ int i;
+ for (i = 0; isascii(*p) && isdigit(*p); i++, p++) {
+ if (i == 3)
+ return 0;
+ }
+ if (*p) {
+ if (*p++ != '.')
+ return 0;
+ }
+ count++;
+ }
+ return count == 3;
+}
+
+static int
+format_message(char **buf, const char *rcode, const char *xcode,
+ const char *message)
+{
+ size_t xcodelen, pfxlen, len, numlines, i;
+ const char *p;
+ char *q;
+
+ pfxlen = strlen(rcode) + 1;
+ if (xcode != NULL)
+ pfxlen += (xcodelen = strlen(xcode)) + 1;
+
+ numlines = 0;
+ len = 0;
+ for (p = message; *p; p++) {
+ if (*p == '\r') {
+ if (p[1] == '\n')
+ p++;
+ numlines++;
+ } else if (*p == '\n')
+ numlines++;
+ else
+ len++;
+ }
+
+ if (p > message && p[-1] != '\n')
+ numlines++;
+
+ len += numlines * (pfxlen + 2);
+
+ *buf = malloc(len + 1);
+ if (!*buf)
+ return MI_FAILURE;
+
+ q = *buf;
+ p = message;
+ for (i = 1; i <= numlines; i++) {
+ strcpy(q, rcode);
+ q += 3;
+ if (xcode) {
+ *q++ = (i < numlines) ? '-' : ' ';
+ memcpy(q, xcode, xcodelen);
+ q += xcodelen;
+ *q++ = ' ';
+ } else
+ *q++ = (i < numlines) ? '-' : ' ';
+ while (*p && !(*p == '\r' || *p == '\n'))
+ *q++ = *p++;
+ if (numlines > 1 && i != numlines) {
+ *q++ = '\r';
+ *q++ = '\n';
+ }
+ if (*p == '\r')
+ p++;
+ if (*p == '\n')
+ p++;
+ }
+ *q = 0;
+ return MI_SUCCESS;
+}
+
+#define MLBUF_INIT_ALLOC 512
+#define MLBUF_INCR_ALLOC 128
+int
+_gacopyz_setmlreply_va(SMFICTX *ctx, size_t max, const char *rcode,
+ const char *xcode, va_list ap)
+{
+ size_t bufsize = MLBUF_INIT_ALLOC, i, lasti, numlines;
+ char *buf, *p;
+ size_t xcodelen, pfxlen;
+
+ if (rcode == NULL || ctx == NULL)
+ return MI_FAILURE;
+
+ if ((rcode[0] != '4' && rcode[0] != '5') ||
+ !isascii(rcode[1]) || !isdigit(rcode[1]) ||
+ !isascii(rcode[2]) || !isdigit(rcode[2]))
+ return MI_FAILURE;
+
+ pfxlen = strlen(rcode);
+
+ if (strlen(rcode) != 3)
+ return MI_FAILURE;
+
+ if (xcode != NULL) {
+ if (!enhanced_code_p(xcode))
+ return MI_FAILURE;
+ } else {
+ if (rcode[0] == '4')
+ xcode = "4.0.0";
+ else
+ xcode = "5.0.0";
+ }
+ if (xcode != NULL)
+ pfxlen += (xcodelen = strlen(xcode)) + 1;
+
+ free(ctx->reply);
+ ctx->reply = NULL;
+ buf = malloc(bufsize);
+ if (!buf)
+ return MI_FAILURE;
+ i = 0;
+ lasti = 0;
+ numlines = 0;
+ for (p = va_arg(ap, char*);
+ p && (max == 0 || numlines < max); numlines++) {
+ size_t s = strlen(p) + pfxlen + 2;
+ if (strpbrk(p, "\r\n") != NULL)
+ break;
+ if (i + s > bufsize) {
+ char *newbuf;
+ size_t delta = (i + s - bufsize +
+ MLBUF_INCR_ALLOC) /
+ MLBUF_INCR_ALLOC;
+ bufsize += delta * MLBUF_INCR_ALLOC;
+ newbuf = realloc(buf, bufsize);
+ if (!newbuf) {
+ free(buf);
+ return MI_FAILURE;
+ }
+ buf = newbuf;
+ }
+
+ strcpy(buf + i, rcode);
+ i += 3;
+ buf[i] = '-';
+ lasti= i++;
+ memcpy(buf + i, xcode, xcodelen);
+ i += xcodelen;
+ buf[i++] = ' ';
+ strcpy(buf + i, p);
+ i += strlen(p);
+
+ p = va_arg(ap, char*);
+ if (p) {
+ buf[i++] = '\r';
+ buf[i++] = '\n';
+ }
+ }
+ buf[i] = 0;
+ buf[lasti] = ' ';
+
+ ctx->reply = buf;
+
+ return MI_SUCCESS;
+}
+
+int
+gacopyz_setreply(SMFICTX *ctx, const char *rcode, const char *xcode,
+ const char *message)
+{
+ if (rcode == NULL || ctx == NULL)
+ return MI_FAILURE;
+
+ if ((rcode[0] != '4' && rcode[0] != '5') ||
+ !isascii(rcode[1]) || !isdigit(rcode[1]) ||
+ !isascii(rcode[2]) || !isdigit(rcode[2]))
+ return MI_FAILURE;
+
+ if (strlen(rcode) != 3)
+ return MI_FAILURE;
+
+ if (xcode != NULL) {
+ if (!enhanced_code_p(xcode))
+ return MI_FAILURE;
+ }
+
+ free(ctx->reply);
+ ctx->reply = NULL;
+ return format_message(&ctx->reply, rcode, xcode, message);
+}
+
+
+int
+gacopyz_setmlreply_va(SMFICTX *ctx, const char *rcode, const char *xcode,
+ va_list ap)
+{
+ return _gacopyz_setmlreply_va(ctx, 0, rcode, xcode, ap);
+ return MI_SUCCESS;
+}
+
+int
+gacopyz_setmlreply_v(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
+{
+ int rc;
+ va_list ap;
+
+ va_start(ap, xcode);
+ rc = gacopyz_setmlreply_va(ctx, rcode, xcode, ap);
+ va_end(ap);
+
+ return MI_SUCCESS;
+}
+
diff --git a/gacopyz/dummy.c b/gacopyz/dummy.c
new file mode 100644
index 00000000..01c8eef5
--- /dev/null
+++ b/gacopyz/dummy.c
@@ -0,0 +1,114 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "mfapi.h"
+
+int
+dummy_idle (gacopyz_conn_t conn)
+{
+ printf("IDLING\n");
+ return 0;
+}
+
+sfsistat
+dummy_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
+{
+ printf("CONNECT FROM: %s, family: %d\n", hostname,
+ hostaddr ? hostaddr->sa.sa_family : -1);
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+dummy_envfrom(SMFICTX *ctx, char **argv)
+{
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+dummy_eom(SMFICTX *ctx)
+{
+ static char newbody[] = "Zawartosc wiadomosci zostala usunieta\n"
+ "wylacznie ze zlej woli administratora systemu\n"
+ "--\n"
+ "Bwana Budu\n";
+ smfi_addheader(ctx, "X-Been-Here", "Gacopyz");
+ smfi_insheader(ctx, 1, "X-New-Header", "nowy");
+ smfi_chgheader(ctx, "X-Duplicate-Header", 1, NULL);
+ smfi_addrcpt(ctx, "gray@gnu.org.ua");
+#if 0
+ smfi_delrcpt(ctx, "<gray@localhost>");
+ smfi_delrcpt(ctx, "gray");
+#endif
+ smfi_replacebody(ctx, newbody, sizeof newbody);
+ return SMFIS_CONTINUE;
+}
+
+struct smfiDesc smfilter =
+{
+ "DummyFilter",
+ SMFI_VERSION,
+ SMFIF_ADDHDRS|SMFIF_CHGHDRS|SMFIF_ADDRCPT|SMFIF_DELRCPT|
+ SMFIF_CHGBODY|SMFIF_QUARANTINE,
+ dummy_connect, /* connection info filter */
+ NULL, /* SMTP HELO command filter */
+ dummy_envfrom, /* envelope sender filter */
+ NULL, /* envelope recipient filter */
+ NULL, /* header filter */
+ NULL, /* end of header */
+ NULL, /* body block filter */
+ dummy_eom, /* end of message */
+ NULL, /* message aborted */
+ NULL, /* connection cleanup */
+ NULL, /* unknown command handler */
+ NULL, /* data handler */
+ NULL, /* child start */
+ NULL, /* child finish */
+ NULL /* idle callback */
+};
+
+char *level_name[] = {
+ "DEBUG",
+ "INFO",
+ "WARN",
+ "ERR",
+ "FATAL"
+};
+
+static void
+stderr_log_printer(int level, char *fmt, va_list ap)
+{
+ fprintf(stderr, "%s: ", level_name[level]);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ if (!argv[1]) {
+ fprintf(stderr, "usage: %s portspec\n", argv[0]);
+ exit(1);
+ }
+
+ gacopyz_set_logger(stderr_log_printer);
+ smfi_setdbg(1);
+
+ if (smfi_setconn(argv[1]) == MI_FAILURE) {
+ fprintf(stderr, "smfi_setconn: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (smfi_register(smfilter) == MI_FAILURE) {
+ fprintf(stderr, "smfi_register failed\n");
+ exit(1);
+ }
+ smfi_opensocket(1);
+ return smfi_main();
+}
diff --git a/gacopyz/gacopyz.c b/gacopyz/gacopyz.c
new file mode 100644
index 00000000..7466bffe
--- /dev/null
+++ b/gacopyz/gacopyz.c
@@ -0,0 +1,1559 @@
+/* This file is part of gacopyz.
+ Copyright (C) 2006 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 2, 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA */
+
+#include <gacopyz_priv.h>
+
+int
+gacopyz_init(gacopyz_conn_t *pconn, struct smfiDesc *desc)
+{
+ size_t len;
+ char *name;
+ gacopyz_conn_t conn;
+
+ name = desc->xxfi_name ? desc->xxfi_name : "Unknown";
+ len = strlen(name);
+ if (desc->xxfi_version != SMFI_VERSION) {
+ gacopyz_logmsg(SMI_LOG_ERR,
+ "smfi_register: %s: version mismatch; "
+ "application %d != implementation %d",
+ name, desc->xxfi_version, SMFI_VERSION);
+ return MI_FAILURE;
+ }
+
+ conn = malloc(sizeof(*conn) + len + 1);
+
+ if (!conn) {
+ gacopyz_logmsg(SMI_LOG_ERR,
+ "smfi_register: %s: not enough memory", name);
+ return MI_FAILURE;
+ }
+
+ memset(conn, 0, sizeof(*conn));
+ conn->sd = -1;
+ conn->master_timeout.tv_usec = 0;
+ conn->master_timeout.tv_sec = 5;
+ conn->ctx_timeout.tv_usec = 0;
+ conn->ctx_timeout.tv_sec = MI_TIMEOUT;
+ conn->logmask = SMI_DEFAULT_LOG_MASK;
+ conn->desc = *desc;
+ conn->desc.xxfi_name = (char*)(conn + 1);
+ strcpy(conn->desc.xxfi_name, name);
+ *pconn = conn;
+ return MI_SUCCESS;
+}
+
+void
+gacopyz_free(gacopyz_conn_t conn)
+{
+ free(conn);
+}
+
+static int
+parse_connection(gacopyz_conn_t conn,
+ const char *cstr,
+ char **pproto, char **pport, char **ppath)
+{
+ const char *p;
+ size_t len;
+
+ p = strchr(cstr, ':');
+ if (!p)
+ *pproto = NULL;
+ else {
+ len = p - cstr;
+ *pproto = malloc(len + 1);
+ if (!*pproto) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "parse_connection: not enough memory");
+ return MI_FAILURE;
+ }
+ memcpy(*pproto, cstr, len);
+ (*pproto)[len] = 0;
+
+ cstr = p + 1;
+ }
+
+ p = strchr(cstr, '@');
+ if (!p)
+ *pport = NULL;
+ else {
+ len = p - cstr;
+ *pport = malloc(len + 1);
+ if (!*pport) {
+ free(*pproto);
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "parse_connection: not enough memory");
+ return MI_FAILURE;
+ }
+ memcpy(*pport, cstr, len);
+ (*pport)[len] = 0;
+
+ cstr = p + 1;
+ }
+
+ *ppath = strdup(cstr);
+ if (!*ppath) {
+ free(*pproto);
+ free(*pport);
+ free(*ppath);
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "parse_connection: not enough memory");
+ return MI_FAILURE;
+ }
+ return MI_SUCCESS;
+}
+
+static int
+do_connect(gacopyz_conn_t conn,
+ const char *cstr, char *proto, char *port, char *path,
+ int backlog, int rmsocket)
+{
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_un sun;
+ } addr;
+ int socklen;
+ int fd, flags;
+ int yes = 1;
+
+ if (!proto
+ || strcmp(proto, "unix") == 0 || strcmp(proto, "local") == 0) {
+ struct stat st;
+
+ if (port) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: invalid connection type: %s; "
+ "port is meaningless for UNIX sockets",
+ conn->desc.xxfi_name, cstr);
+ return -1;
+ }
+
+ if (strlen(path) > sizeof addr.sun.sun_path) {
+ errno = EINVAL;
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: %s: UNIX socket name too long",
+ conn->desc.xxfi_name, path);
+ return -1;
+ }
+
+ addr.sa.sa_family = PF_UNIX;
+ socklen = sizeof(addr.sun);
+ strcpy(addr.sun.sun_path, path);
+
+ if (stat(path, &st)) {
+ if (errno != ENOENT) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: %s: cannot stat socket: %s",
+ conn->desc.xxfi_name, path,
+ strerror(errno));
+ return -1;
+ }
+ } else {
+ /* FIXME: Check permissions? */
+ if (!S_ISSOCK(st.st_mode)) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: %s: not a socket",
+ conn->desc.xxfi_name, path);
+ return -1;
+ }
+ if (rmsocket && unlink(path)) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: %s: cannot unlink: %s",
+ conn->desc.xxfi_name, path,
+ strerror(errno));
+ return -1;
+ }
+ }
+
+ } else if (strcmp(proto, "inet") == 0) {
+ short pnum;
+ long num;
+ char *p;
+
+ addr.sa.sa_family = PF_INET;
+ socklen = sizeof(addr.sin);
+
+ if (!port) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: invalid connection type: %s; "
+ "missing port number",
+ conn->desc.xxfi_name, cstr);
+ return -1;
+ }
+
+ num = pnum = strtol(port, &p, 0);
+ if (*p == 0) {
+ if (num != pnum) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: invalid connection type: %s; "
+ "bad port number",
+ conn->desc.xxfi_name, cstr);
+ return -1;
+ }
+ pnum = htons(pnum);
+ } else {
+ struct servent *sp = getservbyname(path, "tcp");
+ if (!sp) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: invalid connection type: %s; "
+ "unknown port name",
+ conn->desc.xxfi_name, cstr);
+ return -1;
+ }
+ pnum = sp->s_port;
+ }
+
+ if (!path)
+ addr.sin.sin_addr.s_addr = INADDR_ANY;
+ else {
+ struct hostent *hp = gethostbyname(path);
+ if (!hp) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: Unknown host name %s",
+ conn->desc.xxfi_name, path);
+ return -1;
+ }
+ addr.sa.sa_family = hp->h_addrtype;
+ switch (hp->h_addrtype) {
+ case AF_INET:
+ memmove(&addr.sin.sin_addr, hp->h_addr, 4);
+ addr.sin.sin_port = pnum;
+ break;
+
+ default:
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: invalid connection type: %s; "
+ "unsupported address family",
+ conn->desc.xxfi_name, cstr);
+ return -1;
+ }
+ }
+ }
+
+ fd = socket(addr.sa.sa_family, SOCK_STREAM, 0);
+ if (fd == -1) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: Unable to create new socket: %s",
+ conn->desc.xxfi_name, strerror(errno));
+ return -1;
+ }
+
+ if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: Cannot set close-on-exec: %s",
+ conn->desc.xxfi_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (addr.sa.sa_family != PF_UNIX
+ && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &yes,
+ sizeof(yes)) == -1) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: set reuseaddr failed (%s)",
+ conn->desc.xxfi_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (bind(fd, &addr.sa, socklen) < 0) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: Cannot bind to port %s: %s",
+ conn->desc.xxfi_name, cstr, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, backlog)) {
+ gacopyz_log(conn, SMI_LOG_ERR,
+ "%s: Cannot listen on port %s: %s",
+ conn->desc.xxfi_name, cstr, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+
+ return fd;
+}
+
+int
+gacopyz_open(gacopyz_conn_t conn, const char *cstr, int backlog, int rmsocket)
+{
+ char *proto;
+ char *port;
+ char *path;
+
+ if (!conn) {
+ gacopyz_logmsg(SMI_LOG_ERR,
+ "empty or missing socket information");
+ errno = EINVAL;
+ return MI_FAILURE;
+ }
+
+ gacopyz_log(conn, SMI_LOG_DEBUG,
+ "%s: opening listen socket on %s",
+ conn->desc.xxfi_name, cstr);
+ if (parse_connection(conn, cstr, &proto, &port, &path)) {
+ errno = ENOENT;
+ return MI_FAILURE;
+ }
+
+ conn->sd = do_connect(conn, cstr, proto, port, path, backlog,
+ rmsocket);
+
+ free(proto);
+ free(port);
+ free(path);
+ return conn->sd == -1 ? MI_FAILURE : MI_SUCCESS;
+}
+
+int
+gacopyz_get_logmask(gacopyz_conn_t conn, int *mask)
+{
+ if (!conn || !mask)
+ return MI_FAILURE;
+ *mask = conn->logmask;
+ return MI_SUCCESS;
+}
+
+int
+gacopyz_set_logmask(gacopyz_conn_t conn, int mask)
+{
+ if (!conn)
+ return MI_FAILURE;
+ conn->logmask = mask;
+ return MI_SUCCESS;
+}
+
+int
+gacopyz_set_foreground(gacopyz_conn_t conn, int fg)
+{
+ if (!conn)
+ return MI_FAILURE;
+ conn->foreground = fg;
+ return MI_SUCCESS;
+}
+
+int
+gacopyz_connect(gacopyz_conn_t *pconn, struct smfiDesc *desc,
+ const char *cstr, int backlog, int rmsocket)
+{
+ gacopyz_conn_t conn;
+
+ if (gacopyz_init(&conn, desc))
+ return MI_FAILURE;
+
+ if (gacopyz_open(conn, cstr, backlog, rmsocket) == -1) {
+ gacopyz_free(conn);
+ return MI_FAILURE;
+ }
+ *pconn = conn;
+ return MI_SUCCESS;
+}
+
+int
+gacopyz_get_fd(gacopyz_conn_t conn)
+{
+ if (!conn)
+ return -1;
+ return conn->sd;
+}
+
+int
+gacopyz_set_master_timeout(gacopyz_conn_t conn, struct timeval *timeout)
+{
+ if (!conn)
+ return MI_FAILURE;
+ conn->master_timeout = *timeout;
+ return MI_SUCCESS;
+}
+
+int
+gacopyz_get_master_timeout(gacopyz_conn_t conn, struct timeval *timeout)
+{
+ if (!conn)
+ return MI_FAILURE;
+ *timeout = conn->master_timeout;
+ return MI_SUCCESS;
+}
+
+int
+gacopyz_set_ctx_timeout(gacopyz_conn_t conn, struct timeval *timeout)
+{
+ if (!conn)
+ return MI_FAILURE;
+ conn->ctx_timeout = *timeout;
+ return MI_SUCCESS;
+}
+
+int
+gacopyz_get_ctx_timeout(gacopyz_conn_t conn, struct timeval *timeout)
+{
+ if (!conn)
+ return MI_FAILURE;
+ *timeout = conn->ctx_timeout;
+ return MI_SUCCESS;
+}
+
+#include "trans.h"
+
+int
+trans_ok(enum state from, enum state to)
+{
+ while (1) {
+ if (transtab[from][to])
+ return 1;
+ if (++from == st_skip)
+ return 0;
+ if (!transtab[from][st_skip])
+ return 0;
+ }
+}
+
+static int
+ctx_read(SMFICTX *ctx, char *buf, size_t size)
+{
+ int rc = MI_SUCCESS;
+
+ while (size) {
+ fd_set rset;
+ fd_set xset;
+ int res;
+ struct timeval to;
+
+ FD_ZERO(&rset);
+ FD_ZERO(&xset);
+ FD_SET(ctx->sd, &rset);
+ FD_SET(ctx->sd, &xset);
+
+ to = ctx->conn->ctx_timeout;
+
+ res = select(ctx->sd + 1, &rset, NULL, &xset, &to);
+ if (res == 0) {
+ errno = ETIMEDOUT;
+ continue;
+ } else if (res < 0) {
+ if (errno == EINTR)
+ continue;
+ rc = MI_FAILURE;
+ break;
+ } else if (rc > 0) {
+ if (FD_ISSET(ctx->sd, &xset)) {
+ gacopyz_log(ctx->conn,
+ SMI_LOG_ERR,
+ "ctx_read: exception on control fd");
+ rc = MI_FAILURE;
+ break;
+ }
+ /* Otherwise, FD_ISSET(ctx->sd, &rset) is true */
+ }
+
+ res = read(ctx->sd, buf, size);
+ if (res == -1) {
+ gacopyz_log(ctx->conn,
+ SMI_LOG_ERR,
+ "read failed: %s",
+ strerror(errno));
+ rc = MI_FAILURE;
+ break;
+ }
+
+ buf += res;
+ size -= res;
+ }
+ return rc;
+}
+
+static int
+ctx_write(SMFICTX *ctx, char *buf, size_t size)
+{
+ int rc = MI_SUCCESS;
+
+ while (size) {
+ fd_set wset;
+ fd_set xset;
+ int res;
+ struct timeval to;
+
+ FD_ZERO(&wset);
+ FD_ZERO(&xset);
+ FD_SET(ctx->sd, &wset);
+ FD_SET(ctx->sd, &xset);
+
+ to = ctx->conn->ctx_timeout;
+
+ res = select(ctx->sd + 1, NULL, &wset, &xset, &to);
+ if (res == 0) {
+ errno = ETIMEDOUT;
+ continue;
+ } else if (res < 0) {
+ if (errno == EINTR)
+ continue;
+ rc = MI_FAILURE;
+ break;
+ } else if (rc > 0) {
+ if (FD_ISSET(ctx->sd, &xset)) {
+ gacopyz_log(ctx->conn,
+ SMI_LOG_ERR,
+ "ctx_write: exception on control fd");
+ rc = MI_FAILURE;
+ break;
+ }
+ /* Otherwise, FD_ISSET(ctx->sd, &wset) is true */
+ }
+
+ res = write(ctx->sd, buf, size);
+ if (res == -1) {
+ gacopyz_log(ctx->conn,
+ SMI_LOG_ERR,
+ "write failed: %s",
+ strerror(errno));
+ rc = MI_FAILURE;
+ break;
+ }
+
+ buf += res;
+ size -= res;
+ }
+ return rc;
+}
+
+
+union header {
+ struct {
+ mi_uint32_t size;
+ unsigned char cmd;
+ } hdr;
+ char buf[5];
+};
+
+static int
+get_command(SMFICTX *ctx, unsigned char *cmd, size_t *pcount,
+ char **pbuf, size_t *psize)
+{
+ union header header;
+ size_t size;
+ int rc;
+
+ if ((rc = ctx_read(ctx, header.buf, sizeof header.buf)) != MI_SUCCESS)
+ return rc;
+
+ size = ntohl(header.hdr.size) - 1;
+ if (size + 1 > *psize) {
+ char *p = realloc(*pbuf, size + 1);
+ if (!p) {
+ gacopyz_log(ctx->conn,
+ SMI_LOG_ERR, "not enough memory");
+ return MI_FAILURE;
+ }
+ *pbuf = p;
+ *psize = size + 1;
+ }
+
+ gacopyz_log(ctx->conn, SMI_LOG_DEBUG,
+ "read header: size=%lu, cmd=%c",
+ size, header.hdr.cmd);
+
+ if ((rc = ctx_read(ctx, *pbuf, size)) != MI_SUCCESS)
+ return rc;
+ (*pbuf)[size] = 0;
+
+ gacopyz_logdump(ctx->conn, SMI_LOG_DEBUG,
+ "read data", *pbuf, size);
+ *pcount = size;
+ *cmd = header.hdr.cmd;
+ return MI_SUCCESS;
+}
+
+static int
+send_reply(SMFICTX *ctx, unsigned char cmd)
+{
+ int rc;
+ union header header;
+ char *buf = NULL;
+ size_t bufsize = 0;
+
+ switch (cmd) {
+ case SMFIR_CONTINUE:
+ break;
+
+ case SMFIR_TEMPFAIL:
+ if (ctx->reply && *ctx->reply == '4') {
+ cmd = SMFIR_REPLYCODE;
+ buf = ctx->reply;
+ bufsize = strlen(ctx->reply) + 1;
+ }
+ break;
+
+ case SMFIR_REJECT:
+ if (ctx->reply && *ctx->reply == '5') {
+ cmd = SMFIR_REPLYCODE;
+ buf = ctx->reply;
+ bufsize = strlen(ctx->reply) + 1;
+ }
+ break;
+
+ case SMFIR_DISCARD:
+ break;
+
+ case SMFIR_ACCEPT:
+ break;
+
+ case SMFIC_OPTNEG:
+ {
+ mi_uint32_t v[3];
+ v[0] = htonl(ctx->conn->desc.xxfi_version);
+ v[1] = htonl(ctx->conn->desc.xxfi_flags);
+ v[2] = htonl(ctx->flags);
+ buf = (char*) v;
+ bufsize = sizeof v;
+ break;
+ }
+
+ default: /* Ignore */
+ break;
+ }
+
+ header.hdr.size = htonl(bufsize + 1);
+ header.hdr.cmd = cmd;
+ rc = ctx_write(ctx, header.buf, sizeof header.buf);
+ if (rc != MI_SUCCESS)
+ return rc;
+ if (bufsize)
+ rc = ctx_write(ctx, buf, bufsize);
+ return rc;
+}
+
+static void
+macro_assoc_free(macro_assoc_t *p)
+{
+ free(p->argv);
+ free(p->buffer);
+}
+
+static void
+clear_macros(SMFICTX *ctx, int i)
+{
+ for (; i < maci_max; i++)
+ macro_assoc_free(&ctx->macros[i]);
+}
+
+enum state_arg_type {
+ arg_no_args, /* no arguments */
+ arg_one_string, /* one string */
+ arg_two_strings, /* two strings */
+ arg_ints, /* three integers */
+ arg_argv, /* NULL-terminated, \0 separated list of
+ arguments */
+ arg_argvc /* 1 byte command + arg_argv */
+};
+
+union state_arg {
+ struct {
+ char *ptr;
+ size_t len;
+ } string;
+ char *strings[2];
+ mi_uint32_t ints[3];
+ struct {
+ char cmd;
+ char **v;
+ char *buffer;
+ } argv;
+};
+
+typedef enum {
+ sret_fail,
+ sret_abort,
+ sret_noreply,
+ sret_reply,
+ sret_reject
+} state_ret_type;
+
+typedef state_ret_type (*state_handler_fn) (SMFICTX *,
+ union state_arg *,
+ unsigned char *);
+
+struct state_disp {
+ int cmd;
+ char *name;
+ enum state_arg_type arg_type;
+ state_handler_fn fn;
+ enum state next;
+ int flags;
+ int macro_ind;
+};
+
+static unsigned char
+convert_sfsistat(sfsistat stat)
+{
+ switch (stat) {
+ case SMFIS_CONTINUE:
+ return SMFIR_CONTINUE;
+ case SMFIS_REJECT:
+ return SMFIR_REJECT;
+ case SMFIS_DISCARD:
+ re