summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS12
-rw-r--r--COPYING31
-rw-r--r--ChangeLog2
-rw-r--r--Makefile.am7
-rw-r--r--NEWS1
-rw-r--r--README5
-rw-r--r--acsite.m4200
-rw-r--r--configure.in109
-rw-r--r--src/Makefile.am10
-rw-r--r--src/proxsmtpd.c610
-rw-r--r--src/proxsmtpd.h43
11 files changed, 1029 insertions, 1 deletions
diff --git a/AUTHORS b/AUTHORS
index d2e76d6..a80252d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1,11 @@
-nielsen@memberwebs.com
+AUTHOR:
+Nate Nielsen <nielsen@memberwebs.com>
+
+CONTRIBUTORS:
+Andreas Steinmetz <ast@domdv.de>
+
+PATCHES:
+Berk D. Demir <demir@meteksan.net.tr>
+João Carlos Mendes Luís <jcmendes@int.gov.br>
+Jasper Slits <jasper@insiders.nl>
+Yamamoto Takao <takao@oakat.org>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..763af15
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,31 @@
+
+Copyright (c) 2004, Nate Nielsen
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+ * Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and
+ the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+ * The names of contributors to this software may not be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..383ffce
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,2 @@
+0.5
+ - Initial fork from clamsmtp version 0.9
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..33d8e51
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,7 @@
+
+EXTRA_DIST = config.sub acsite.m4 config.guess scripts common
+SUBDIRS = src doc
+
+dist-hook:
+ rm -rf `find $(distdir)/ -name CVS`
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..c7ab92a
--- /dev/null
+++ b/NEWS
@@ -0,0 +1 @@
+See ChangeLog \ No newline at end of file
diff --git a/README b/README
new file mode 100644
index 0000000..4c7af45
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
+=================================================================
+ PROXSMTP README
+
+See the website:
+http://memberwebs.com/nielsen/software/proxsmtp/
diff --git a/acsite.m4 b/acsite.m4
new file mode 100644
index 0000000..e8cdb22
--- /dev/null
+++ b/acsite.m4
@@ -0,0 +1,200 @@
+dnl Available from the GNU Autoconf Macro Archive at:
+dnl http://www.gnu.org/software/ac-archive/htmldoc/acx_pthread.html
+dnl
+AC_DEFUN([ACX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_SAVE
+AC_LANG_C
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+ AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthread or
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $flag])
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$flag])
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ AC_TRY_LINK([#include <pthread.h>],
+ [pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+ [acx_pthread_ok=yes])
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: threads are created detached by default
+ # and the JOINABLE attribute has a nonstandard name (UNDETACHED).
+ AC_MSG_CHECKING([for joinable pthread attribute])
+ AC_TRY_LINK([#include <pthread.h>],
+ [int attr=PTHREAD_CREATE_JOINABLE;],
+ ok=PTHREAD_CREATE_JOINABLE, ok=unknown)
+ if test x"$ok" = xunknown; then
+ AC_TRY_LINK([#include <pthread.h>],
+ [int attr=PTHREAD_CREATE_UNDETACHED;],
+ ok=PTHREAD_CREATE_UNDETACHED, ok=unknown)
+ fi
+ if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then
+ AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok,
+ [Define to the necessary symbol if this constant
+ uses a non-standard name on your system.])
+ fi
+ AC_MSG_RESULT(${ok})
+ if test x"$ok" = xunknown; then
+ AC_MSG_WARN([we do not know how to create joinable pthreads])
+ fi
+
+ AC_MSG_CHECKING([if more special flags are required for pthreads])
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ AC_MSG_RESULT(${flag})
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with cc_r
+ AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
+else
+ PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+ :
+else
+ acx_pthread_ok=no
+ $2
+fi
+AC_LANG_RESTORE
+])dnl ACX_PTHREAD
+
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..018132b
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,109 @@
+dnl
+dnl Copyright (c) 2004, Nate Nielsen
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions
+dnl are met:
+dnl
+dnl * Redistributions of source code must retain the above
+dnl copyright notice, this list of conditions and the
+dnl following disclaimer.
+dnl * Redistributions in binary form must reproduce the
+dnl above copyright notice, this list of conditions and
+dnl the following disclaimer in the documentation and/or
+dnl other materials provided with the distribution.
+dnl * The names of contributors to this software may not be
+dnl used to endorse or promote products derived from this
+dnl software without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+dnl FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+dnl COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+dnl INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+dnl BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+dnl OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+dnl AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+dnl OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+dnl THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+dnl DAMAGE.
+dnl
+dnl
+dnl CONTRIBUTORS
+dnl Nate Nielsen <nielsen@memberwebs.com>
+dnl
+
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(proxsmtp, 0.4.90, nielsen@memberwebs.com)
+AM_INIT_AUTOMAKE(proxsmtp, 0.4.90)
+
+LDFLAGS="$LDFLAGS -L/usr/local/lib"
+CFLAGS="$CFLAGS -I/usr/local/include"
+
+AC_CONFIG_SRCDIR([src/proxsmtpd.c])
+AM_CONFIG_HEADER([config.h])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+
+# Debug mode
+AC_ARG_ENABLE(debug,
+ AC_HELP_STRING([--enable-debug],
+ [Compile binaries in debug mode]))
+
+if test "$enable_debug" = "yes"; then
+ CFLAGS="$CFLAGS -g -O0 -Wall"
+ AC_DEFINE_UNQUOTED(_DEBUG, 1, [In debug mode])
+ echo "enabling debug compile mode"
+fi
+
+# TODO: Figure out why we need this wierd hack
+ACX_PTHREAD( , [echo "ERROR: Pthread support not found."; exit 1] )
+
+LIBS="$PTHREAD_LIBS $LIBS"
+CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(limits.h,,)
+AC_CHECK_HEADERS([unistd.h stdio.h stddef.h fcntl.h stdlib.h assert.h errno.h stdarg.h err.h string.h], ,
+ [echo "ERROR: Required C header missing"; exit 1])
+
+# Check for linux type transparent proxy support
+AC_CHECK_HEADERS([linux/netfilter_ipv4.h],
+ AC_DEFINE(LINUX_TRANSPARENT_PROXY, 1, [Whether the system supports a linux type transparent proxy]),
+ ,
+ [[
+ #ifdef HAVE_LIMITS_H
+ #include <limits.h>
+ #endif
+ ]] )
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+
+# We use error checking mutexes whenever possible
+AC_CHECK_DECL(PTHREAD_MUTEX_ERRORCHECK_NP, [AC_DEFINE(HAVE_ERR_MUTEX, 1, "Error Mutex Type")],
+ [AC_CHECK_DECL(PTHREAD_MUTEX_ERRORCHECK, [AC_DEFINE(HAVE_ERR_MUTEX, 2)], ,
+ [ #include <pthread.h> ])], [ #include <pthread.h> ])
+
+# Required Functions
+AC_CHECK_FUNCS([memset strerror malloc realloc getopt strchr tolower getaddrinfo], ,
+ [echo "ERROR: Required function missing"; exit 1])
+AC_CHECK_FUNCS([strlwr strlcat strlcpy strncat strncpy])
+
+# Have to resolve this for the path below
+if test "${prefix}" = "NONE"; then
+ prefix=$ac_default_prefix
+fi
+
+AC_DEFINE_UNQUOTED(CONF_PREFIX, "`eval echo ${sysconfdir}`", [Installation Prefix] )
+
+AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile])
+AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..208ae67
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,10 @@
+
+sbin_PROGRAMS = proxsmtpd
+
+proxsmtpd_SOURCES = proxsmtpd.c proxsmtpd.h \
+ ../common/spio.c ../common/smtppass.c ../common/smtppass.h ../common/sppriv.h \
+ ../common/stringx.c ../common/stringx.h ../common/sock_any.c ../common/sock_any.h \
+ ../common/usuals.h ../common/compat.c ../common/compat.h
+
+proxsmtpd_CFLAGS = -I${top_srcdir}/common/ -I${top_srcdir}/
+
diff --git a/src/proxsmtpd.c b/src/proxsmtpd.c
new file mode 100644
index 0000000..897faa9
--- /dev/null
+++ b/src/proxsmtpd.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <paths.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <errno.h>
+#include <err.h>
+
+#include "usuals.h"
+
+#include "compat.h"
+#include "sock_any.h"
+#include "stringx.h"
+#include "smtppass.h"
+
+/* -----------------------------------------------------------------------
+ * STRUCTURES
+ */
+
+typedef struct pxstate
+{
+ /* Settings ------------------------------- */
+ const char* command; /* The command to pipe email through */
+ /* TODO: Timeout for above command */
+ const char* directory; /* The directory for temp files */
+ int quarantine; /* Leave failed files in temp dir */
+ int debug_files; /* Leave all files in temp dir */
+}
+pxstate_t;
+
+/* -----------------------------------------------------------------------
+ * STRINGS
+ */
+
+#define CRLF "\r\n"
+
+XXXXXXXXXXXXXXXXXxx
+#define SMTP_DATAVIRUSOK "250 Virus Detected; Discarded Email" CRLF
+#define SMTP_DATAVIRUS "550 Virus Detected; Content Rejected" CRLF
+
+#define DEFAULT_CONFIG CONF_PREFIX "/proxsmtpd.conf"
+
+#define CFG_FILTER "FilterCommand"
+#define CFG_DIRECTORY "TempDirectory"
+#define CFG_QUARANTINE "Quarantine"
+#define CFG_DEBUGFILES "DebugFiles"
+
+/* -----------------------------------------------------------------------
+ * GLOBALS
+ */
+
+clstate_t g_clstate;
+
+/* -----------------------------------------------------------------------
+ * FORWARD DECLARATIONS
+ */
+
+static void usage();
+static int connect_clam(clctx_t* ctx);
+static int disconnect_clam(clctx_t* ctx);
+static int quarantine_virus(clctx_t* ctx);
+static int transfer_to_cache(clctx_t* ctx);
+static int clam_scan_file(clctx_t* ctx);
+
+/* -----------------------------------------------------------------------
+ * SIMPLE MACROS
+ */
+
+/* ----------------------------------------------------------------------------------
+ * STARTUP ETC...
+ */
+
+int main(int argc, char* argv[])
+{
+ const char* configfile = DEFAULT_CONFIG;
+ const char* pidfile = NULL;
+ int dbg_level = -1;
+ int warnargs = 0;
+ int ch = 0;
+ int r;
+ char* t;
+
+ /* Setup some defaults */
+ memset(&g_clstate, 0, sizeof(g_clstate));
+ g_clstate.header = DEFAULT_HEADER;
+ g_clstate.directory = _PATH_TMP;
+
+ /* We need the default to parse into a useable form, so we do this: */
+ r = cb_parse_option(CFG_CLAMADDR, DEFAULT_CLAMAV);
+ ASSERT(r == 1);
+
+ sp_init("clamsmtpd");
+
+ /*
+ * We still accept our old arguments for compatibility reasons.
+ * We fill them into the spstate structure directly
+ */
+
+ /* Parse the arguments nicely */
+ while((ch = getopt(argc, argv, "bc:d:D:f:h:l:m:p:qt:v")) != -1)
+ {
+ switch(ch)
+ {
+ /* COMPAT: Actively reject messages */
+ case 'b':
+ if((r = cb_parse_option(CFG_BOUNCE, "on")) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
+ break;
+
+ /* COMPAT: Change the CLAM socket */
+ case 'c':
+ if((r = cb_parse_option(CFG_CLAMADDR, "on")) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
+ break;
+
+ /* Don't daemonize */
+ case 'd':
+ dbg_level = strtol(optarg, &t, 10);
+ if(*t) /* parse error */
+ errx(1, "invalid debug log level");
+ dbg_level += LOG_ERR;
+ break;
+
+ /* COMPAT: The directory for the files */
+ case 'D':
+ if((r = sp_parse_option(CFG_DIRECTORY, optarg)) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
+ break;
+
+ /* The configuration file */
+ case 'f':
+ configfile = optarg;
+ break;
+
+ /* COMPAT: The header to add */
+ case 'h':
+ if((r = cb_parse_option(CFG_HEADER, optarg)) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
+ break;
+
+ /* COMPAT: Change our listening port */
+ case 'l':
+ if((r = sp_parse_option("Listen", optarg)) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
+ break;
+
+ /* COMPAT: The maximum number of threads */
+ case 'm':
+ if((r = sp_parse_option("MaxConnections", optarg)) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
+ break;
+
+ /* Write out a pid file */
+ case 'p':
+ pidfile = optarg;
+ break;
+
+ /* COMPAT: The timeout */
+ case 't':
+ if((r = sp_parse_option("TimeOut", optarg)) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
+ break;
+
+ /* COMPAT: Leave virus files in directory */
+ case 'q':
+ if((r = cb_parse_option(CFG_QUARANTINE, "on")) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
+ break;
+
+ /* Print version number */
+ case 'v':
+ printf("clamsmtpd (version %s)\n", VERSION);
+ printf(" (config: %s)\n", DEFAULT_CONFIG);
+ exit(0);
+ break;
+
+ /* COMPAT: Leave all files in the tmp directory */
+ case 'X':
+ if((r = cb_parse_option(CFG_DEBUGFILES, "on")) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
+ break;
+
+ /* Usage information */
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc > 1)
+ usage();
+ if(argc == 1)
+ {
+ /* COMPAT: The out address */
+ if((r = sp_parse_option("OutAddress", argv[0])) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
+ }
+
+ if(warnargs)
+ warnx("please use configuration file instead of command-line flags: %s", configfile);
+
+ r = sp_run(configfile, pidfile, dbg_level);
+
+ sp_done();
+
+ return r;
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: clamsmtpd [-d debuglevel] [-f configfile] [-p pidfile]\n");
+ fprintf(stderr, " clamsmtpd -v\n");
+ exit(2);
+}
+
+/* ----------------------------------------------------------------------------------
+ * SP CALLBACKS
+ */
+
+int cb_check_data(spctx_t* sp)
+{
+ int r = 0;
+ clctx_t* ctx = (clctx_t*)sp;
+
+ /* Connect to clamav */
+ if(!spio_valid(&(ctx->clam)))
+ r = connect_clam(ctx);
+
+ /* transfer_to_cache */
+ if(r != -1 && (r = transfer_to_cache(ctx)) > 0)
+
+ /* ClamAV doesn't like empty files */
+ r = clam_scan_file(ctx);
+
+ switch(r)
+ {
+
+ /*
+ * There was an error tell the client. We haven't notified
+ * the server about any of this yet
+ */
+ case -1:
+ if(sp_fail_data(sp, NULL) == -1)
+ return -1;
+ break;
+
+ /*
+ * No virus was found. Now we initiate a connection to the server
+ * and transfer the file to it.
+ */
+ case 0:
+ if(sp_done_data(sp, g_clstate.header) == -1)
+ return -1;
+ break;
+
+ /*
+ * A virus was found, normally we just drop the email. But if
+ * requested we can send a simple message back to our client.
+ * The server doesn't know data was ever sent, and the client can
+ * choose to reset the connection to reuse it if it wants.
+ */
+ case 1:
+ /* Any special post operation actions on the virus */
+ quarantine_virus(ctx);
+
+ if(sp_fail_data(sp, g_clstate.bounce ?
+ SMTP_DATAVIRUS : SMTP_DATAVIRUSOK) == -1)
+ return -1;
+ break;
+
+ default:
+ ASSERT(0 && "Invalid clam_scan_file return value");
+ break;
+ };
+
+ return 0;
+}
+
+int cb_parse_option(const char* name, const char* value)
+{
+ if(strcasecmp(CFG_CLAMADDR, name) == 0)
+ {
+ if(sock_any_pton(value, &(g_clstate.clamaddr), SANY_OPT_DEFLOCAL) == -1)
+ errx(2, "invalid " CFG_CLAMADDR " socket name: %s", value);
+ g_clstate.clamname = value;
+ return 1;
+ }
+
+ else if(strcasecmp(CFG_HEADER, name) == 0)
+ {
+ g_clstate.header = (const char*)trim_space((char*)value);
+
+ if(strlen(g_clstate.header) == 0)
+ g_clstate.header = NULL;
+
+ return 1;
+ }
+
+ else if(strcasecmp(CFG_DIRECTORY, name) == 0)
+ {
+ g_clstate.directory = value;
+ return 1;
+ }
+
+ else if(strcasecmp(CFG_BOUNCE, name) == 0)
+ {
+ if((g_clstate.bounce = strtob(value)) == -1)
+ errx(2, "invalid value for " CFG_BOUNCE);
+ return 1;
+ }
+
+ else if(strcasecmp(CFG_QUARANTINE, name) == 0)
+ {
+ if((g_clstate.quarantine = strtob(value)) == -1)
+ errx(2, "invalid value for " CFG_BOUNCE);
+ return 1;
+ }
+
+ else if(strcasecmp(CFG_DEBUGFILES, name) == 0)
+ {
+ if((g_clstate.debug_files = strtob(value)) == -1)
+ errx(2, "invalid value for " CFG_DEBUGFILES);
+ return 1;
+ }
+
+ return 0;
+}
+
+spctx_t* cb_new_context()
+{
+ clctx_t* ctx = (clctx_t*)calloc(1, sizeof(clctx_t));
+ if(!ctx)
+ {
+ sp_messagex(NULL, LOG_CRIT, "out of memory");
+ return NULL;
+ }
+
+ /* Initial preparation of the structure */
+ spio_init(&(ctx->clam), "CLAMAV");
+ return &(ctx->sp);
+}
+
+void cb_del_context(spctx_t* sp)
+{
+ clctx_t* ctx = (clctx_t*)sp;
+ ASSERT(sp);
+
+ disconnect_clam(ctx);
+ free(ctx);
+}
+
+/* ----------------------------------------------------------------------------------
+ * CLAM AV
+ */
+
+static int connect_clam(clctx_t* ctx)
+{
+ int ret = 0;
+ spctx_t* sp = &(ctx->sp);
+
+ ASSERT(ctx);
+ ASSERT(!spio_valid(&(ctx->clam)));
+
+ if(spio_connect(sp, &(ctx->clam), &(g_clstate.clamaddr), g_clstate.clamname) == -1)
+ RETURN(-1);
+
+ spio_read_junk(sp, &(ctx->clam));
+
+ /* Send a session and a check header to ClamAV */
+
+ if(spio_write_data(sp, &(ctx->clam), "SESSION\n") == -1)
+ RETURN(-1);
+
+ spio_read_junk(sp, &(ctx->clam));
+
+/*
+ if(spio_write_data(sp, &(ctx->clam), "PING\n") == -1 ||
+ spio_read_line(sp, &(ctx->clam), CLIO_DISCARD | CLIO_TRIM) == -1)
+ RETURN(-1);
+
+ if(strcmp(sp->line, CONNECT_RESPONSE) != 0)
+ {
+ sp_message(sp, LOG_ERR, "clamd sent an unexpected response: %s", ctx->line);
+ RETURN(-1);
+ }
+*/
+
+cleanup:
+
+ if(ret < 0)
+ spio_disconnect(sp, &(ctx->clam));
+
+ return ret;
+}
+
+static int disconnect_clam(clctx_t* ctx)
+{
+ spctx_t* sp = &(ctx->sp);
+
+ if(!spio_valid(&(ctx->clam)))
+ return 0;
+
+ if(spio_write_data(sp, &(ctx->clam), CLAM_DISCONNECT) != -1)
+ spio_read_junk(sp, &(ctx->clam));
+
+ spio_disconnect(sp, &(ctx->clam));
+ return 0;
+}
+
+static int clam_scan_file(clctx_t* ctx)
+{
+ int len;
+ spctx_t* sp = &(ctx->sp);
+
+ /* Needs to be long enough to hold path names */
+ ASSERT(SP_LINE_LENGTH > MAXPATHLEN + 32);
+
+ strcpy(sp->line, CLAM_SCAN);
+ strcat(sp->line, sp->cachename);
+ strcat(sp->line, "\n");
+
+ if(spio_write_data(sp, &(ctx->clam), sp->line) == -1)
+ return -1;
+
+ len = spio_read_line(sp, &(ctx->clam), SPIO_DISCARD | SPIO_TRIM);
+ if(len == 0)
+ {
+ sp_messagex(sp, LOG_ERR, "clamd disconnected unexpectedly");
+ return -1;
+ }
+
+ if(is_last_word(sp->line, CLAM_OK, KL(CLAM_OK)))
+ {
+ sp_add_log(sp, "status=", "CLEAN");
+ sp_messagex(sp, LOG_DEBUG, "no virus");
+ return 0;
+ }
+
+ if(is_last_word(sp->line, CLAM_FOUND, KL(CLAM_FOUND)))
+ {
+ len = strlen(sp->cachename);
+
+ if(sp->linelen > len)
+ sp_add_log(sp, "status=VIRUS:", sp->line + len + 1);
+ else
+ sp_add_log(sp, "status=", "VIRUS");
+
+ sp_messagex(sp, LOG_DEBUG, "found virus");
+ return 1;
+ }
+
+ if(is_last_word(sp->line, CLAM_ERROR, KL(CLAM_ERROR)))
+ {
+ sp_messagex(sp, LOG_ERR, "clamav error: %s", sp->line);
+ sp_add_log(sp, "status=", "CLAMAV-ERROR");
+ return -1;
+ }
+
+ sp_add_log(sp, "status=", "CLAMAV-ERROR");
+ sp_messagex(sp, LOG_ERR, "unexepected response from clamd: %s", sp->line);
+ return -1;
+}
+
+/* ----------------------------------------------------------------------------------
+ * TEMP FILE HANDLING
+ */
+
+static int quarantine_virus(clctx_t* ctx)
+{
+ char buf[MAXPATHLEN];
+ spctx_t* sp = &(ctx->sp);
+ char* t;
+
+ if(!g_clstate.quarantine)
+ return 0;
+
+ strlcpy(buf, g_clstate.directory, MAXPATHLEN);
+ strlcat(buf, "/virus.", MAXPATHLEN);
+
+ /* Points to null terminator */
+ t = buf + strlen(buf);
+
+ /*
+ * Yes, I know we're using mktemp. And yet we're doing it in
+ * a safe manner due to the link command below not overwriting
+ * existing files.
+ */
+ for(;;)
+ {
+ /* Null terminate off the ending, and replace with X's for mktemp */
+ *t = 0;
+ strlcat(buf, "XXXXXX", MAXPATHLEN);
+
+ if(!mktemp(buf))
+ {
+ sp_message(sp, LOG_ERR, "couldn't create quarantine file name");
+ return -1;
+ }
+
+ /* Try to link the file over to the temp */
+ if(link(sp->cachename, buf) == -1)
+ {
+ /* We don't want to allow race conditions */
+ if(errno == EEXIST)
+ {
+ sp_message(sp, LOG_WARNING, "race condition when quarantining virus file: %s", buf);
+ continue;
+ }
+
+ sp_message(sp, LOG_ERR, "couldn't quarantine virus file");
+ return -1;
+ }
+
+ break;
+ }
+
+ sp_messagex(sp, LOG_INFO, "quarantined virus file as: %s", buf);
+ return 0;
+}
+
+static int transfer_to_cache(clctx_t* ctx)
+{
+ spctx_t* sp = &(ctx->sp);
+ int r, count = 0;
+ const char* data;
+
+ while((r = sp_read_data(sp, &data)) != 0)
+ {
+ if(r < 0)
+ return -1; /* Message already printed */
+
+ count += r;
+
+ if((r = sp_write_data(sp, data, r)) < 0)
+ return -1; /* Message already printed */
+ }
+
+ /* End the caching */
+ if(sp_write_data(sp, NULL, 0) < 0)
+ return -1;
+
+ sp_messagex(sp, LOG_DEBUG, "wrote %d bytes to temp file", count);
+ return count;
+}
+
+
diff --git a/src/proxsmtpd.h b/src/proxsmtpd.h
new file mode 100644
index 0000000..06b2412
--- /dev/null
+++ b/src/proxsmtpd.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+#ifndef __PROXSMTPD_H__
+#define __PROXSMTPD_H__
+
+
+#endif /* __PROXSMTPD_H__ */