diff options
author | (no author) <(no author)> | 2004-07-08 18:21:20 +0000 |
---|---|---|
committer | (no author) <(no author)@37549a05-7cf3-0310-b41a-658052d0713c> | 2004-07-08 18:21:20 +0000 |
commit | c493a11b449f5071aed9adb34faa3e3dd85ea762 (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 | |
parent | e650d1da777c76d94f023557b110725113a2b778 (diff) |
New repository initialized by cvs2svn.
-rw-r--r-- | AUTHORS | 11 | ||||
-rw-r--r-- | COPYING | 31 | ||||
-rw-r--r-- | ChangeLog | 30 | ||||
-rw-r--r-- | INSTALL | 229 | ||||
-rw-r--r-- | Makefile.am | 7 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | README | 5 | ||||
-rw-r--r-- | acsite.m4 | 200 | ||||
-rw-r--r-- | common/compat.c | 413 | ||||
-rw-r--r-- | common/compat.h | 121 | ||||
-rw-r--r-- | common/smtppass.c | 1976 | ||||
-rw-r--r-- | common/smtppass.h | 281 | ||||
-rw-r--r-- | common/sock_any.c | 385 | ||||
-rw-r--r-- | common/sock_any.h | 90 | ||||
-rw-r--r-- | common/spio.c | 638 | ||||
-rw-r--r-- | common/sppriv.h | 77 | ||||
-rw-r--r-- | common/stringx.c | 174 | ||||
-rw-r--r-- | common/stringx.h | 53 | ||||
-rw-r--r-- | common/usuals.h | 78 | ||||
-rw-r--r-- | configure.in | 109 | ||||
-rw-r--r-- | doc/.cvsignore | 3 | ||||
-rw-r--r-- | doc/Makefile.am | 3 | ||||
-rw-r--r-- | doc/proxsmtpd.8 | 213 | ||||
-rw-r--r-- | doc/proxsmtpd.conf | 52 | ||||
-rw-r--r-- | doc/proxsmtpd.conf.5 | 177 | ||||
-rw-r--r-- | scripts/add_header.sh | 43 | ||||
-rw-r--r-- | scripts/proxsmtpd.sh | 32 | ||||
-rw-r--r-- | scripts/spamassassin.sh | 52 | ||||
-rw-r--r-- | src/.cvsignore | 4 | ||||
-rw-r--r-- | src/Makefile.am | 10 | ||||
-rw-r--r-- | src/proxsmtpd.c | 904 | ||||
-rw-r--r-- | src/proxsmtpd.h | 43 |
32 files changed, 0 insertions, 6445 deletions
diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 7aba7cb..0000000 --- a/AUTHORS +++ /dev/null @@ -1,11 +0,0 @@ -AUTHOR: -Nate Nielsen <nielsen@memberwebs.com> - -CONTRIBUTORS: -Andreas Steinmetz <ast@domdv.de> -Rubio Vaughan <rubio@passim.net> -Olivier Beyssac <ob@r14.freenix.org> - -PATCHES: -João Carlos Mendes Luís <jcmendes@int.gov.br> -Ben Mesman <ben@bncnetservice.nl> diff --git a/COPYING b/COPYING deleted file mode 100644 index 763af15..0000000 --- a/COPYING +++ /dev/null @@ -1,31 +0,0 @@ - -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 deleted file mode 100644 index df241cb..0000000 --- a/ChangeLog +++ /dev/null @@ -1,30 +0,0 @@ -1.2.1 [2005-04-15] - - Fixed bug (introduced in 1.2) when 'Header' option is not present. - -1.2 [2005-03-24] - - Don't leak file descriptors when clamsmtpd can't connect to outgoing - SMTP server [Chris Mason] - - 'Header' configuration option with special format arguments [Olivier Beyssac] - - Supress weird warnings when looking up names of local unix connections. - -1.1 [2005-01-27] - - Fixed crasher when outgoing connection couldn't be established - - Removed erroneous chown line from clamsmtpd.sh - -1.0 [2004-12-02] - - Added XCLIENT support. - - Drop XCLIENT commands coming in from clients for security. - - Added big scary warnings to the sample scripts about escaping variables. - - Documentation fixes [Olivier Beyssac] - -0.6 [2004-10-30] - - Added CLIENT and SERVER variables to VirusAction script environment. - - Even better logging for network errors and filter warnings. - - Print out proxsmtp version in debug logs - - Allow configuration of server keep alives (NOOPs). Default to none - -0.5 [2004-10-20] - - Added sample scripts. - - Initial fork from clamsmtp version 0.9.x - - Converted into filter based SMTP pass-through - diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 54caf7c..0000000 --- a/INSTALL +++ /dev/null @@ -1,229 +0,0 @@ -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software -Foundation, Inc. - - This file is free documentation; the Free Software Foundation gives -unlimited permission to copy, distribute and modify it. - -Basic Installation -================== - - These are generic installation instructions. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. (Caching is -disabled by default to prevent problems with accidental use of stale -cache files.) - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You only need -`configure.ac' if you want to change it or regenerate `configure' using -a newer version of `autoconf'. - -The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. If you're - using `csh' on an old version of System V, you might need to type - `sh ./configure' instead to prevent `csh' from trying to execute - `configure' itself. - - Running `configure' takes awhile. While running, it prints some - messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package. - - 4. Type `make install' to install the programs and any data files and - documentation. - - 5. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking that -the `configure' script does not know about. Run `./configure --help' -for details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - - You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you must use a version of `make' that -supports the `VPATH' variable, such as GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. - - If you have to use a `make' that does not support the `VPATH' -variable, you have to compile the package for one architecture at a -time in the source code directory. After you have installed the -package for one architecture, use `make distclean' before reconfiguring -for another architecture. - -Installation Names -================== - - By default, `make install' will install the package's files in -`/usr/local/bin', `/usr/local/man', etc. You can specify an -installation prefix other than `/usr/local' by giving `configure' the -option `--prefix=PATH'. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -give `configure' the option `--exec-prefix=PATH', the package will use -PATH as the prefix for installing programs and libraries. -Documentation and other data files will still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=PATH' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - -Optional Features -================= - - Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - -Specifying the System Type -========================== - - There may be some features `configure' cannot figure out -automatically, but needs to determine by the type of machine the package -will run on. Usually, assuming the package is built to be run on the -_same_ architectures, `configure' can figure that out, but if it prints -a message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the `--target=TYPE' option to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - - Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -will cause the specified gcc to be used as the C compiler (unless it is -overridden in the site shell script). - -`configure' Invocation -====================== - - `configure' recognizes the following options to control how it -operates. - -`--help' -`-h' - Print a summary of the options to `configure', and exit. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index 33d8e51..0000000 --- a/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ - -EXTRA_DIST = config.sub acsite.m4 config.guess scripts common -SUBDIRS = src doc - -dist-hook: - rm -rf `find $(distdir)/ -name CVS` - @@ -1 +0,0 @@ -See ChangeLog
\ No newline at end of file @@ -1,5 +0,0 @@ -================================================================= - PROXSMTP README - -See the website: -http://memberwebs.com/nielsen/software/proxsmtp/ diff --git a/acsite.m4 b/acsite.m4 deleted file mode 100644 index e8cdb22..0000000 --- a/acsite.m4 +++ /dev/null @@ -1,200 +0,0 @@ -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/common/compat.c b/common/compat.c deleted file mode 100644 index 23a73d0..0000000 --- a/common/compat.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright (c) 2004-2005, 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> - * - * - * PORTIONS FROM OPENBSD: ------------------------------------------------- - * - * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. - */ - - - -#include "usuals.h" -#include "compat.h" - -#include <ctype.h> -#include <stdlib.h> - -#ifndef HAVE_REALLOCF - -void* reallocf(void* ptr, size_t size) -{ - void* ret = realloc(ptr, size); - - if(!ret && size) - free(ptr); - - return ret; -} - -#endif - -#ifndef HAVE_STRLWR -char* strlwr(char* s) -{ - char* t = s; - while(*t) - { - *t = tolower(*t); - t++; - } - return s; -} -#endif - -#ifndef HAVE_STRUPR -char* strupr(char* s) -{ - char* t = s; - while(*t) - { - *t = toupper(*t); - t++; - } - return s; -} -#endif - -#ifndef HAVE_STRLCPY - -size_t strlcpy(char* dst, const char* src, size_t siz) -{ - char* d = dst; - const char* s = src; - size_t n = siz; - - /* Copy as many bytes as will fit */ - if(n != 0 && --n != 0) - { - do - { - if((*d++ = *s++) == 0) - break; - } - while(--n != 0); - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if(n == 0) - { - if(siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return s - src - 1; /* count does not include NUL */ -} - -#endif - -#ifndef HAVE_STRLCAT - -size_t strlcat(char* dst, const char* src, size_t siz) -{ - char* d = dst; - const char* s = src; - size_t n = siz; - size_t dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while(n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if(n == 0) - return dlen + strlen(s); - while(*s != '\0') - { - if(n != 1) - { - *d++ = *s; - n--; - } - - s++; - } - - *d = '\0'; - - return dlen + (s - src); /* count does not include NUL */ -} - -#endif - -#ifndef HAVE_SETENV - -#include <stdio.h> - -int setenv(const char* name, const char* value, int overwrite) -{ - char* t; - int r; - if(getenv(name) && !overwrite) - return 0; - t = (char*)malloc((strlen(name) + strlen(value) + 2) * sizeof(char)); - if(!t) return -1; - sprintf(t, "%s=%s", name, value); - r = putenv(t); - free(t); - return r; -} - -#endif - -#ifndef HAVE_DAEMON - -#include <sys/types.h> -#include <unistd.h> -#include <stdlib.h> -#include <signal.h> -#include <fcntl.h> - -int daemon(int nochdir, int noclose) -{ - struct sigaction osa, sa; - int oerrno, fd, osa_ok; - pid_t newgrp; - - /* A SIGHUP may be thrown when the parent exits below. */ - sigemptyset(&sa.sa_mask); - sa.sa_handler = SIG_IGN; - sa.sa_flags = 0; - osa_ok = sigaction(SIGHUP, &sa, &osa); - - switch(fork()) - { - case -1: - return -1; - case 0: - break; - default: - _exit(0); - } - - newgrp = setsid(); - oerrno = errno; - if(osa_ok != -1) - sigaction(SIGHUP, &osa, NULL); - if(newgrp == -1) - { - errno = oerrno; - return -1; - } - if(!nochdir) - chdir("/"); - if(!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) - { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - if(fd > 2) - close(fd); - } - return 0; - -} - -#endif - -#ifndef HAVE_ERR_H - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <stdarg.h> - -extern char** __argv; - -static const char* calc_prog_name() -{ - static char prognamebuf[256]; - static int prepared = 0; - - if(!prepared) - { - const char* beg = strrchr(__argv[0], '\\'); - const char* temp = strrchr(__argv[0], '/'); - beg = (beg > temp) ? beg : temp; - beg = (beg) ? beg + 1 : __argv[0]; - - temp = strrchr(__argv[0], '.'); - temp = (temp > beg) ? temp : __argv[0] + strlen(__argv[0]); - - if((temp - beg) > 255) - temp = beg + 255; - - strncpy(prognamebuf, beg, temp - beg); - prognamebuf[temp - beg] = 0; - prepared = 1; - } - - return prognamebuf; -} - -static FILE* err_file; /* file to use for error output */ - -/* - * This is declared to take a `void *' so that the caller is not required - * to include <stdio.h> first. However, it is really a `FILE *', and the - * manual page documents it as such. - */ -void err_set_file(void *fp) -{ - if (fp) - err_file = fp; - else - err_file = stderr; -} - -void err(int eval, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - verrc(eval, errno, fmt, ap); - va_end(ap); -} - -void verr(int eval, const char *fmt, va_list ap) -{ - verrc(eval, errno, fmt, ap); -} - -void errc(int eval, int code, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - verrc(eval, code, fmt, ap); - va_end(ap); -} - -void verrc(int eval, int code, const char *fmt, va_list ap) -{ - if (err_file == 0) - err_set_file((FILE *)0); - fprintf(err_file, "%s: ", calc_prog_name()); - if (fmt != NULL) { - vfprintf(err_file, fmt, ap); - fprintf(err_file, ": "); - } - fprintf(err_file, "%s\n", strerror(code)); - exit(eval); -} - -void errx(int eval, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - verrx(eval, fmt, ap); - va_end(ap); -} - -void verrx(int eval, const char *fmt, va_list ap) -{ - if (err_file == 0) - err_set_file((FILE *)0); - fprintf(err_file, "%s: ", calc_prog_name()); - if (fmt != NULL) - vfprintf(err_file, fmt, ap); - fprintf(err_file, "\n"); - exit(eval); -} - -void warn(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vwarnc(errno, fmt, ap); - va_end(ap); -} - -void vwarn(const char *fmt, va_list ap) -{ - vwarnc(errno, fmt, ap); -} - -void warnc(int code, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vwarnc(code, fmt, ap); - va_end(ap); -} - -void vwarnc(int code, const char *fmt, va_list ap) -{ - if (err_file == 0) - err_set_file((FILE *)0); - fprintf(err_file, "%s: ", calc_prog_name()); - if (fmt != NULL) - { - vfprintf(err_file, fmt, ap); - fprintf(err_file, ": "); - } - fprintf(err_file, "%s\n", strerror(code)); -} - -void warnx(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vwarnx(fmt, ap); - va_end(ap); -} - -void vwarnx(const char *fmt, va_list ap) -{ - if(err_file == 0) - err_set_file((FILE*)0); - fprintf(err_file, "%s: ", calc_prog_name()); - if(fmt != NULL) - vfprintf(err_file, fmt, ap); - fprintf(err_file, "\n"); -} - -#endif - diff --git a/common/compat.h b/common/compat.h deleted file mode 100644 index b772f63..0000000 --- a/common/compat.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 _COMPAT_H_ -#define _COMPAT_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <sys/types.h> - -#ifndef HAVE_STDARG_H -#error ERROR: Must have a working stdarg.h header -#else -#include <stdarg.h> -#endif - -#ifndef HAVE_REALLOCF -void* reallocf(void* p, size_t sz); -#endif - -#include <pthread.h> - -#if HAVE_ERR_MUTEX == 1 -# define MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK_NP -#else -# if HAVE_ERR_MUTEX == 2 -# define MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK -# else -# undef MUTEX_TYPE -# endif -#endif - -#ifndef HAVE_STRLWR -char* strlwr(char* s); -#endif - -#ifndef HAVE_STRUPR -char* strupr(char* s); -#endif - -#ifndef HAVE_STRLCAT -size_t strlcat(char *dst, const char *src, size_t size); -#endif - -#ifndef HAVE_STRLCPY -size_t strlcpy(char *dst, const char *src, size_t size); -#endif - -#ifndef HAVE_SETENV -int setenv(const char* name, const char* value, int overwrite); -#endif - -#ifndef HAVE_DAEMON -int daemon(int nochdir, int noclose); -#endif - -#ifdef HAVE_ERR_H -#include <err.h> -#else -#include <stdarg.h> -void err_set_file(void *fp); -void err_set_exit(void (*ef)(int)); -void err(int eval, const char *fmt, ...); -void verr(int eval, const char *fmt, va_list ap); -void errc(int eval, int code, const char *fmt, ...); -void verrc(int eval, int code, const char *fmt, va_list ap); -void errx(int eval, const char *fmt, ...); -void verrx(int eval, const char *fmt, va_list ap); -void warn(const char *fmt, ...); -void vwarn(const char *fmt, va_list ap); -void warnc(int code, const char *fmt, ...); -void vwarnc(int code, const char *fmt, va_list ap); -void warnx(const char *fmt, ...); -void vwarnx(const char *fmt, va_list ap); -#endif - -#ifdef HAVE_PATHS_H -#include <paths.h> -#else -#define _PATH_DEVNULL "/dev/null" -#define _PATH_TMP "/tmp" -#endif - -#endif /* _COMPAT_H_ */ diff --git a/common/smtppass.c b/common/smtppass.c deleted file mode 100644 index 2a312ad..0000000 --- a/common/smtppass.c +++ /dev/null @@ -1,1976 +0,0 @@ -/* - * 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> - * Andreas Steinmetz <ast@domdv.de> - * Rubio Vaughan <rubio@passim.net> - * Olivier Beyssac <ob@r14.freenix.org> - */ - -#include <sys/time.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/param.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <syslog.h> -#include <signal.h> -#include <errno.h> -#include <stdarg.h> -#include <pwd.h> -#include <time.h> - -#include "usuals.h" - -#ifdef LINUX_TRANSPARENT_PROXY -#include <linux/netfilter_ipv4.h> -#endif - -#include "compat.h" -#include "sock_any.h" -#include "stringx.h" -#include "sppriv.h" - -/* ----------------------------------------------------------------------- - * STRUCTURES - */ - -typedef struct spthread -{ - pthread_t tid; /* Written to by the main thread */ - int fd; /* The file descriptor or -1 */ -} -spthread_t; - -/* ----------------------------------------------------------------------- - * DATA - */ - -#define CRLF "\r\n" - -#define SMTP_TOOLONG "500 Line too long" CRLF -#define SMTP_STARTBUSY "554 Server Busy" CRLF -#define SMTP_STARTFAILED "554 Local Error" CRLF -#define SMTP_DATAINTERMED "354 Start mail input; end with <CRLF>.<CRLF>" CRLF -#define SMTP_FAILED "451 Local Error" CRLF -#define SMTP_NOTSUPP "502 Command not implemented" CRLF -#define SMTP_NOTAUTH "554 Insufficient authorization" CRLF -#define SMTP_OK "250 Ok" CRLF -#define SMTP_REJPREFIX "550 Content Rejected; " - -#define SMTP_DATA "DATA" CRLF -#define SMTP_NOOP "NOOP" CRLF -#define SMTP_XCLIENT "XCLIENT ADDR=%s" CRLF -#define SMTP_BANNER "220 smtp.passthru" CRLF -#define SMTP_HELO_RSP "250 smtp.passthru" CRLF -#define SMTP_EHLO_RSP "250-smtp.passthru" CRLF -#define SMTP_FEAT_RSP "250 XFILTERED" CRLF -#define SMTP_DELIMS "\r\n\t :" -#define SMTP_MULTI_DELIMS " -" - -#define ESMTP_PIPELINE "PIPELINING" -#define ESMTP_TLS "STARTTLS" -#define ESMTP_CHUNK "CHUNKING" -#define ESMTP_BINARY "BINARYMIME" -#define ESMTP_CHECK "CHECKPOINT" -#define ESMTP_XCLIENT "XCLIENT" - -#define HELO_CMD "HELO" -#define EHLO_CMD "EHLO" -#define FROM_CMD "MAIL FROM" -#define TO_CMD "RCPT TO" -#define DATA_CMD "DATA" -#define RSET_CMD "RSET" -#define STARTTLS_CMD "STARTTLS" -#define BDAT_CMD "BDAT" -#define XCLIENT_CMD "XCLIENT" - -#define DATA_END_SIG "." CRLF - -#define DATA_RSP "354" -#define OK_RSP "250" -#define START_RSP "220" - -#define RCVD_HEADER "Received:" - -/* The set of delimiters that can be present between config and value */ -#define CFG_DELIMS ": \t" - -/* Maximum length of the header argument */ -#define MAX_HEADER_LENGTH 1024 - -/* - * asctime_r manpage: "stores the string in a user-supplied buffer of - * length at least 26". We'll need some more bytes to put timezone - * information behind - */ -#define MAX_DATE_LENGTH 64 - -#define LINE_TOO_LONG(l) ((l) >= (SP_LINE_LENGTH - 2)) - -/* ----------------------------------------------------------------------- - * CONFIGURATION OPTIONS - * - * - Be sure that your configuration option needs to go into this - * file. More likely it'll go into clamsmtpd.c - * - When adding configuration options follow the instructions in - * clamsmtpd.c, except add option to spstate_t (sppriv.h) and parse in - * sp_parse_option (below) - */ - -#define CFG_MAXTHREADS "MaxConnections" -#define CFG_TIMEOUT "TimeOut" -#define CFG_OUTADDR "OutAddress" -#define CFG_LISTENADDR "Listen" -#define CFG_HEADER "Header" -#define CFG_TRANSPARENT "TransparentProxy" -#define CFG_DIRECTORY "TempDirectory" -#define CFG_KEEPALIVES "KeepAlives" -#define CFG_USER "User" -#define CFG_PIDFILE "PidFile" -#define CFG_XCLIENT "XClient" - -/* ----------------------------------------------------------------------- - * DEFAULT SETTINGS - */ - -#define DEFAULT_SOCKET "10025" -#define DEFAULT_PORT 10025 -#define DEFAULT_MAXTHREADS 64 -#define DEFAULT_TIMEOUT 180 -#define DEFAULT_KEEPALIVES 0 - -/* ----------------------------------------------------------------------- - * GLOBALS - */ - -spstate_t g_state; /* The state and configuration of the daemon */ -unsigned int g_unique_id = 0x00100000; /* For connection ids */ -pthread_mutex_t g_mutex; /* The main mutex */ -pthread_mutexattr_t g_mtxattr; - -/* ----------------------------------------------------------------------- - * FORWARD DECLARATIONS - */ - -static void on_quit(int signal); -static void drop_privileges(); -static void pid_file(int write); -static void connection_loop(int sock); -static void* thread_main(void* arg); -static int smtp_passthru(spctx_t* ctx); -static int make_connections(spctx_t* ctx, int client); -static int read_server_response(spctx_t* ctx); -static int parse_config_file(const char* configfile); -static char* parse_address(char* line); -static const char* get_successful_rsp(const char* line, int* cont); -static void do_server_noop(spctx_t* ctx); - -/* Used externally in some cases */ -int sp_parse_option(const char* name, const char* option); - -/* ---------------------------------------------------------------------------------- - * BASIC RUN FUNCTIONALITY - */ - -void sp_init(const char* name) -{ - int r; - - ASSERT(name); - - memset(&g_state, 0, sizeof(g_state)); - - sp_message(NULL, LOG_DEBUG, "%s (%s)", name, VERSION); - - /* Setup the defaults */ - g_state.debug_level = -1; - g_state.max_threads = DEFAULT_MAXTHREADS; - g_state.timeout.tv_sec = DEFAULT_TIMEOUT; - g_state.keepalives = DEFAULT_KEEPALIVES; - g_state.directory = _PATH_TMP; - g_state.name = name; - - /* We need the default to parse into a useable form, so we do this: */ - r = sp_parse_option(CFG_LISTENADDR, DEFAULT_SOCKET); - ASSERT(r == 1); - - /* Create the main mutex and condition variable */ - if(pthread_mutexattr_init(&g_mtxattr) != 0 || -#ifdef HAVE_ERR_MUTEX - pthread_mutexattr_settype(&g_mtxattr, MUTEX_TYPE) || -#endif - pthread_mutex_init(&g_mutex, &g_mtxattr) != 0) - errx(1, "threading problem. can't create mutex or condition var"); -} - -int sp_run(const char* configfile, const char* pidfile, int dbg_level) -{ - int sock; - int true = 1; - - ASSERT(configfile); - ASSERT(g_state.name); - - if(!(dbg_level == -1 || dbg_level <= LOG_DEBUG)) - errx(2, "invalid debug log level (must be between 1 and 4)"); - g_state.debug_level = dbg_level; - g_state.pidfile = pidfile; - - /* Now parse the configuration file */ - if(parse_config_file(configfile) == -1) - { - /* - * We used to do a check here before whether it was the default - * configuration file or not, but we can't do that any longer - * as it comes from the app. Usually lack of a configuration - * file will cause the following checks to fail - */ - warnx("configuration file not found: %s", configfile); - } - - /* This option has no default, but is required ... */ - if(g_state.outname == NULL && !g_state.transparent) - errx(2, "no " CFG_OUTADDR " specified."); - - /* ... unless we're in transparent proxy mode */ - else if(g_state.outname != NULL && g_state.transparent) - warnx("the " CFG_OUTADDR " option will be ignored when " CFG_TRANSPARENT " is enabled"); - - sp_messagex(NULL, LOG_DEBUG, "starting up..."); - - /* Drop privileges before daemonizing */ - drop_privileges(); - - /* When set to this we daemonize */ - if(g_state.debug_level == -1) - { - /* Fork a daemon nicely here */ - if(daemon(0, 0) == -1) - { - sp_message(NULL, LOG_ERR, "couldn't run as daemon"); - exit(1); - } - - sp_messagex(NULL, LOG_DEBUG, "running as a daemon"); - g_state.daemonized = 1; - - /* Open the system log */ - openlog(g_state.name, 0, LOG_MAIL); - } - - /* Create the socket */ - sock = socket(SANY_TYPE(g_state.listenaddr), SOCK_STREAM, 0); - if(sock < 0) - { - sp_message(NULL, LOG_CRIT, "couldn't open socket"); - exit(1); - } - - fcntl(sock, F_SETFD, fcntl(sock, F_GETFD, 0) | FD_CLOEXEC); - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&true, sizeof(true)); - - /* Unlink the socket file if it exists */ - if(SANY_TYPE(g_state.listenaddr) == AF_UNIX) - unlink(g_state.listenname); - - if(bind(sock, &SANY_ADDR(g_state.listenaddr), SANY_LEN(g_state.listenaddr)) != 0) - { - sp_message(NULL, LOG_CRIT, "couldn't bind to address: %s", g_state.listenname); - exit(1); - } - - /* Let 5 connections queue up */ - if(listen(sock, 5) != 0) - { - sp_message(NULL, LOG_CRIT, "couldn't listen on socket"); - exit(1); - } - - sp_messagex(NULL, LOG_DEBUG, "created socket: %s", g_state.listenname); - - /* Handle some signals */ - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, SIG_IGN); - signal(SIGINT, on_quit); - signal(SIGTERM, on_quit); - - siginterrupt(SIGINT, 1); - siginterrupt(SIGTERM, 1); - - pid_file(1); - - sp_messagex(NULL, LOG_DEBUG, "accepting connections"); - - connection_loop(sock); - - pid_file(0); - - /* Our listen socket */ - close(sock); - - sp_messagex(NULL, LOG_DEBUG, "stopped processing"); - return 0; -} - -void sp_quit() -{ - /* The handler sets the flag and this also interrupts io */ - kill(getpid(), SIGTERM); -} - -int sp_is_quit() -{ - return g_state.quit ? 1 : 0; -} - -void sp_done() -{ - /* Close the mutex */ - pthread_mutex_destroy(&g_mutex); - pthread_mutexattr_destroy(&g_mtxattr); - - if(g_state._p) - free(g_state._p); - - memset(&g_state, 0, sizeof(g_state)); -} - -static void on_quit(int signal) -{ - g_state.quit = 1; -} - -static void drop_privileges() -{ - char* t; - struct passwd* pw; - uid_t uid; - - if(g_state.user) - { - if(geteuid() != 0) - { - sp_messagex(NULL, LOG_WARNING, "must be started as root to switch to user: %s", g_state.user); - return; - } - - uid = strtol(g_state.user, &t, 10); - if(!t[0]) /* successful parse */ - pw = getpwuid(uid); - else /* must be a name */ - pw = getpwnam(g_state.user); - - if(pw == NULL) - errx(1, "couldn't look up user: %s", g_state.user); - - if(setgid(pw->pw_gid) == -1 || - setuid(pw->pw_uid) == -1) - err(1, "unable to switch to user: %s (uid %d, gid %d)", g_state.user, pw->pw_uid, pw->pw_gid); - - /* A paranoia check */ - if(setreuid(-1, 0) == 0) - err(1, "unable to completely drop privileges"); - - sp_messagex(NULL, LOG_DEBUG, "switched to user %s (uid %d, gid %d)", g_state.user, pw->pw_uid, pw->pw_gid); - } - - if(geteuid() == 0) - sp_messagex(NULL, LOG_WARNING, "running as root is NOT recommended"); -} - - -static void pid_file(int write) -{ - if(!g_state.pidfile) - return; - - if(write) - { - FILE* f = fopen(g_state.pidfile, "w"); - if(f == NULL) - { - sp_message(NULL, LOG_ERR, "couldn't open pid file: %s", g_state.pidfile); - } - else - { - fprintf(f, "%d\n", (int)getpid()); - - if(ferror(f)) - sp_message(NULL, LOG_ERR, "couldn't write to pid file: %s", g_state.pidfile); - if(fclose(f) == EOF) - sp_message(NULL, LOG_ERR, "couldn't write to pid file: %s", g_state.pidfile); - - } - - sp_messagex(NULL, LOG_DEBUG, "wrote pid file: %s", g_state.pidfile); - } - - else - { - unlink(g_state.pidfile); - sp_messagex(NULL, LOG_DEBUG, "removed pid file: %s", g_state.pidfile); - } -} - -static void connection_loop(int sock) -{ - spthread_t* threads = NULL; - int fd, i, x, r; - - /* Create the thread buffers */ - threads = (spthread_t*)calloc(g_state.max_threads, sizeof(spthread_t)); - if(!threads) - { - sp_messagex(NULL, LOG_CRIT, "out of memory"); - return; - } - - /* Now loop and accept the connections */ - while(!sp_is_quit()) - { - fd = accept(sock, NULL, NULL); - if(fd == -1) - { - switch(errno) - { - case EINTR: - case EAGAIN: - break; - - case ECONNABORTED: - sp_message(NULL, LOG_ERR, "couldn't accept a connection"); - break; - - default: - sp_message(NULL, LOG_ERR, "couldn't accept a connection"); - break; - }; - - if(sp_is_quit()) - break; - - continue; - } - - /* Set timeouts on client */ - if(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &(g_state.timeout), sizeof(g_state.timeout)) < 0 || - setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &(g_state.timeout), sizeof(g_state.timeout)) < 0) - sp_message(NULL, LOG_WARNING, "couldn't set timeouts on incoming connection"); - - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); - - /* Look for thread and also clean up others */ - for(i = 0; i < g_state.max_threads; i++) - { - /* Find a thread to run or clean up old threads */ - if(threads[i].tid != 0) - { - sp_lock(); - x = threads[i].fd; - sp_unlock(); - - if(x == -1) - { - sp_messagex(NULL, LOG_DEBUG, "cleaning up completed thread"); - pthread_join(threads[i].tid, NULL); - threads[i].tid = 0; - } -#ifdef _DEBUG - else - { - /* For debugging connection problems: */ - sp_messagex(NULL, LOG_DEBUG, "active connection thread: %x", (int)threads[i].tid); - } -#endif - } - - /* Start a new thread if neccessary */ - if(fd != -1 && threads[i].tid == 0) - { - threads[i].fd = fd; - r = pthread_create(&(threads[i].tid), NULL, thread_main, - (void*)(threads + i)); - if(r != 0) - { - errno = r; - sp_message(NULL, LOG_ERR, "couldn't create thread"); - - write(fd, SMTP_STARTFAILED, KL(SMTP_STARTFAILED)); - shutdown(fd, SHUT_RDWR); - close(fd); - fd = -1; - break; - } - - sp_messagex(NULL, LOG_DEBUG, "created thread for connection"); - fd = -1; - break; - } - } - - /* Check to make sure we have a thread */ - if(fd != -1) - { - sp_messagex(NULL, LOG_ERR, "too many connections open (max %d). sent 554 response", g_state.max_threads); - write(fd, SMTP_STARTBUSY, KL(SMTP_STARTBUSY)); - shutdown(fd, SHUT_RDWR); - close(fd); - fd = -1; - } - } - - sp_messagex(NULL, LOG_DEBUG, "waiting for threads to quit"); - - /* Quit all threads here */ - for(i = 0; i < g_state.max_threads; i++) - { - /* Clean up quit threads */ - if(threads[i].tid != 0) - { - if(threads[i].fd != -1) - { - sp_lock(); - fd = threads[i].fd; - threads[i].fd = -1; - sp_unlock(); - - shutdown(fd, SHUT_RDWR); - close(fd); - } - - pthread_join(threads[i].tid, NULL); - threads[i].tid = 0; - } - } - - free(threads); -} - -static spctx_t* init_thread(int fd) -{ - spctx_t* ctx; - - ctx = cb_new_context(); - if(ctx) - { - memset(ctx, 0, sizeof(*ctx)); - - spio_init(&(ctx->server), "SERVER"); - spio_init(&(ctx->client), "CLIENT"); - - sp_lock(); - /* Assign a unique id to the connection */ - ctx->id = g_unique_id++; - - /* We don't care about wraps, but we don't want zero */ - if(g_unique_id == 0) - g_unique_id++; - sp_unlock(); - - sp_messagex(ctx, LOG_DEBUG, "processing %d on thread %x", fd, (int)pthread_self()); - - /* Connect to the outgoing server ... */ - if(make_connections(ctx, fd) == -1) - { - cb_del_context(ctx); - ctx = NULL; - } - } - - return ctx; -} - -static void cleanup_context(spctx_t* ctx) -{ - ASSERT(ctx); - - if(ctx->cachefile) - { - fclose(ctx->cachefile); - ctx->cachefile = NULL; - } - - if(ctx->cachename[0]) - { - unlink(ctx->cachename); - ctx->cachename[0] = 0; - } - - if(ctx->recipients) - { - free(ctx->recipients); - ctx->recipients = NULL; - } - - if(ctx->sender) - { - free(ctx->sender); - ctx->sender = NULL; - } - - ctx->logline[0] = 0; -} - - -static void done_thread(spctx_t* ctx) -{ - ASSERT(ctx); - - spio_disconnect(ctx, &(ctx->client)); - spio_disconnect(ctx, &(ctx->server)); - - /* Clean up file stuff */ - cleanup_context(ctx); - cb_del_context(ctx); -} - -static void* thread_main(void* arg) -{ - spthread_t* thread = (spthread_t*)arg; - spctx_t* ctx = NULL; - int processing = 0; - int ret = 0; - int fd; - - ASSERT(thread); - - siginterrupt(SIGINT, 1); - siginterrupt(SIGTERM, 1); - - sp_lock(); - /* Get the client socket */ - fd = thread->fd; - sp_unlock(); - - /* Sometimes we get to this point and then quit is noted */ - if(sp_is_quit() || (ctx = init_thread(fd)) == NULL) - { - /* Special case. We don't have a context so clean up descriptor */ - close(fd); - - /* new_context() should have already logged reason */ - RETURN(-1); - } - - /* call the processor */ - processing = 1; - ret = smtp_passthru(ctx); - -cleanup: - - if(ctx) - { - /* Let the client know about fatal errors */ - if(!processing && ret == -1 && spio_valid(&(ctx->client))) - spio_write_data(ctx, &(ctx->client), SMTP_STARTFAILED); - - done_thread(ctx); - } - - /* mark this as done */ - sp_lock(); - thread->fd = -1; - sp_unlock(); - - return (void*)(ret == 0 ? 0 : 1); -} - -static int make_connections(spctx_t* ctx, int client) -{ - struct sockaddr_any peeraddr; - struct sockaddr_any addr; - struct sockaddr_any* outaddr; - char buf[MAXPATHLEN]; - const char* outname; - - ASSERT(client != -1); - - /* Setup the incoming connection. This also fills in peeraddr for us */ - spio_attach(ctx, &(ctx->client), client, &peeraddr); - sp_messagex(ctx, LOG_INFO, "accepted connection from: %s", ctx->client.peername); - - /* Create the server connection address */ - outaddr = &(g_state.outaddr); - outname = g_state.outname; - - /* For transparent proxying we have to discover the address to connect to */ - if(g_state.transparent) - { - memset(&addr, 0, sizeof(addr)); - SANY_LEN(addr) = sizeof(addr); - -#ifdef LINUX_TRANSPARENT_PROXY - if(getsockopt(ctx->client.fd, SOL_IP, SO_ORIGINAL_DST, &SANY_ADDR(addr), &SANY_LEN(addr)) == -1) -#else - if(getsockname(ctx->client.fd, &SANY_ADDR(addr), &SANY_LEN(addr)) == -1) -#endif - { - sp_message(ctx, LOG_ERR, "couldn't get source address for transparent proxying"); - return -1; - } - - /* Check address types */ - if(sock_any_cmp(&addr, &peeraddr, SANY_OPT_NOPORT) == 0) - { - sp_messagex(ctx, LOG_ERR, "loop detected in transparent proxying"); - return -1; - } - - outaddr = &addr; - } - - /* No transparent proxy but check for loopback option */ - else - { - if(SANY_TYPE(*outaddr) == AF_INET && - outaddr->s.in.sin_addr.s_addr == 0) - { - /* Use the incoming IP as the default */ - memcpy(&addr, &(g_state.outaddr), sizeof(addr)); - memcpy(&(addr.s.in.sin_addr), &(peeraddr.s.in.sin_addr), sizeof(addr.s.in.sin_addr)); - outaddr = &addr; - } -#ifdef HAVE_INET6 - else if(SANY_TYPE(*outaddr) == AF_INET6 && - outaddr->s.in.in6.sin_addr.s_addr == 0) - { - /* Use the incoming IP as the default */ - memcpy(&addr, &(g_state.outaddr), sizeof(addr)); - memcpy(&(addr.s.in.sin6_addr), &(peeraddr.s.in.sin6_addr), sizeof(addr.s.in.sin6_addr)); - outaddr = &addr; - } -#endif - } - - /* Reparse name if possible */ - if(outaddr != &(g_state.outaddr)) - { - if(sock_any_ntop(outaddr, buf, MAXPATHLEN, 0) != -1) - outname = buf; - else - outname = "unknown"; - } - - /* Connect to the server */ - if(spio_connect(ctx, &(ctx->server), outaddr, outname) == -1) - return -1; - - return 0; -} - -/* ---------------------------------------------------------------------------------- - * SMTP HANDLING - */ - -static int smtp_passthru(spctx_t* ctx) -{ - char* t; - const char* p; - int r, cont, ret = 0; - unsigned int mask; - int neterror = 0; - - int first_rsp = 1; /* The first 220 response from server to be filtered */ - int filter_host = 0; /* Next response is 250 hostname, which we change */ - - /* XCLIENT is for use in access control */ - int xclient_sup = 0; /* Is XCLIENT supported? */ - int xclient_sent = 0; /* Have we sent an XCLIENT command? */ - - ASSERT(spio_valid(&(ctx->client)) && - spio_valid(&(ctx->server))); - - #define C_LINE ctx->client.line - #define S_LINE ctx->server.line - - while(!sp_is_quit()) - { - mask = spio_select(ctx, &(ctx->client), &(ctx->server), NULL); - - if(mask == ~0) - { - neterror = 1; - RETURN(-1); - } - - /* Client has data available, read a line and process */ - if(mask & 1) - { - if((r = spio_read_line(ctx, &(ctx->client), SPIO_DISCARD)) == -1) - RETURN(-1); - - /* Client disconnected, we're done */ - if(r == 0) - RETURN(0); - - /* We don't let clients send really long lines */ - if(LINE_TOO_LONG(r)) - { - if(spio_write_data(ctx, &(ctx->client), SMTP_TOOLONG) == -1) - RETURN(-1); - - continue; - } - - /* Only valid after EHLO or HELO commands */ - filter_host = 0; - - /* - * At this point we may want to send our XCLIENT. This is a per - * connection command. - */ - if(xclient_sup && !xclient_sent && g_state.xclient) - { - sp_messagex(ctx, LOG_DEBUG, "sending XCLIENT"); - - if(spio_write_dataf(ctx, &(ctx->server), SMTP_XCLIENT, ctx->client.peername) == -1) - RETURN(-1); - - if(read_server_response(ctx) == -1) - RETURN(-1); - - if(!get_successful_rsp(S_LINE, NULL)) - sp_messagex(ctx, LOG_WARNING, "server didn't accept XCLIENT"); - - xclient_sent = 1; - } - - /* Handle the DATA section via our AV checker */ - if(is_first_word(C_LINE, DATA_CMD, KL(DATA_CMD))) - { - /* Send back the intermediate response to the client */ - if(spio_write_data(ctx, &(ctx->client), SMTP_DATAINTERMED) == -1) - RETURN(-1); - - /* - * Now go into avcheck mode. This also handles the eventual - * sending of the data to the server, making the av check - * transparent - */ - if(cb_check_data(ctx) == -1) - RETURN(-1); - - /* Print the log out for this email */ - sp_messagex(ctx, LOG_INFO, "%s", ctx->logline); - - /* Done with that email */ - cleanup_context(ctx); - - /* Command handled */ - continue; - } - - /* - * We need our response to HELO and EHLO to be modified in order - * to prevent complaints about mail loops - */ - else if(is_first_word(C_LINE, EHLO_CMD, KL(EHLO_CMD))) - { - /* EHLO can have multline responses so we set a flag */ - filter_host = 1; - } - - /* - * We always support XCLIENT on a HELO type connection. We do this - * for security reasons, so that a client can't get around filtering - * by backing up one on the protocol. - */ - else if(is_first_word(C_LINE, HELO_CMD, KL(HELO_CMD))) - { - sp_messagex(ctx, LOG_DEBUG, "XCLIENT support assumed"); - xclient_sup = 1; - - /* Filter host as with EHLO above */ - filter_host = 1; - } - - /* - * We don't like these commands. Filter them out. We should have - * filtered out their service extensions earlier in the EHLO response. - * This is just for errant clients. - */ - else if(is_first_word(C_LINE, STARTTLS_CMD, KL(STARTTLS_CMD)) || - is_first_word(C_LINE, BDAT_CMD, KL(BDAT_CMD))) - { - sp_messagex(ctx, LOG_DEBUG, "ESMTP feature not supported"); - - if(spio_write_data(ctx, &(ctx->client), SMTP_NOTSUPP) == -1) - RETURN(-1); - - /* Command handled */ - continue; - } - - /* - * For security reasons we're not about to forward any XCLIENTs - * from our client through. This could lead to a client using our - * privileged IP address to change an audit trail or relay etc... - */ - else if(is_first_word(C_LINE, XCLIENT_CMD, KL(XCLIENT_CMD))) - { - sp_messagex(ctx, LOG_WARNING, "client attempted use of privileged XCLIENT feature"); - - if(spio_write_data(ctx, &(ctx->client), SMTP_NOTAUTH) == -1) - RETURN(-1); - - /* Command handled */ - continue; - } - - /* All other commands just get passed through to server */ - if(spio_write_data(ctx, &(ctx->server), C_LINE) == -1) - RETURN(-1); - - continue; - } - - /* Server has data available, read a line and forward */ - if(mask & 2) - { - if((r = spio_read_line(ctx, &(ctx->server), SPIO_DISCARD)) == -1) - RETURN(-1); - - if(r == 0) - RETURN(0); - - if(LINE_TOO_LONG(r)) - sp_messagex(ctx, LOG_WARNING, "SMTP response line too long. discarded extra"); - - /* - * We intercept the first response we get from the server. - * This allows us to change header so that it doesn't look - * to the client server that we're in a wierd loop. - * - * In different situations using the local hostname or - * 'localhost' don't work because the receiving mail server - * expects one of those to be its own name. We use 'clamsmtp' - * instead. No properly configured server would have this - * as their domain name, and RFC 2821 allows us to use - * an arbitrary but identifying string. - */ - if(first_rsp) - { - first_rsp = 0; - - if(is_first_word(S_LINE, START_RSP, KL(START_RSP))) - { - sp_messagex(ctx, LOG_DEBUG, "intercepting initial response"); - - if(spio_write_data(ctx, &(ctx->client), SMTP_BANNER) == -1) - RETURN(-1); - - /* Command handled */ - continue; - } - } - - if((p = get_successful_rsp(S_LINE, &cont)) != NULL) - { - /* - * Certain mail servers (Postfix 1.x in particular) do a loop check - * on the 250 response after a EHLO or HELO. This is where we - * filter that to prevent loopback errors. - */ - if(filter_host) - { - /* Can have multi-line responses, and we want to be - * sure to only replace the first one. */ - filter_host = 0; - - sp_messagex(ctx, LOG_DEBUG, "intercepting host response"); - - if(spio_write_data(ctx, &(ctx->client), - cont ? SMTP_EHLO_RSP : SMTP_HELO_RSP) == -1) - RETURN(-1); - - /* A new email so cleanup */ - cleanup_context(ctx); - - continue; - } - - /* - * Filter out any EHLO responses that we can't or don't want - * to support. For example pipelining or TLS. - */ - if(is_first_word(C_LINE, EHLO_CMD, KL(EHLO_CMD))) - { - /* - * On ESMTP connections we let the server tell us whether it - * wants XCLIENTs or not. (In contrast to old SMTP above). - */ - if(is_first_word(p, ESMTP_XCLIENT, KL(ESMTP_XCLIENT))) - { - sp_messagex(ctx, LOG_DEBUG, "XCLIENT supported"); - xclient_sup = 1; - } - - if(is_first_word(p, ESMTP_PIPELINE, KL(ESMTP_PIPELINE)) || - is_first_word(p, ESMTP_TLS, KL(ESMTP_TLS)) || - is_first_word(p, ESMTP_CHUNK, KL(ESMTP_CHUNK)) || - is_first_word(p, ESMTP_BINARY, KL(ESMTP_BINARY)) || - is_first_word(p, ESMTP_CHECK, KL(ESMTP_CHECK)) || - is_first_word(p, ESMTP_XCLIENT, KL(ESMTP_XCLIENT))) - { - sp_messagex(ctx, LOG_DEBUG, "filtered ESMTP feature: %s", trim_space((char*)p)); - - /* - * If this is the last line in the EHLO response we need - * to replace it with something else - */ - if(!cont) - { - if(spio_write_data(ctx, &(ctx->client), SMTP_FEAT_RSP) == -1) - RETURN(-1); - } - - continue; - } - } - - /* MAIL FROM */ - if((r = check_first_word(C_LINE, FROM_CMD, KL(FROM_CMD), SMTP_DELIMS)) > 0) - { - t = parse_address(C_LINE + r); - sp_add_log(ctx, "from=", t); - - /* Make note of the sender for later */ - ctx->sender = (char*)reallocf(ctx->sender, strlen(t) + 1); - if(ctx->sender) - strcpy(ctx->sender, t); - } - - /* RCPT TO */ - else if((r = check_first_word(C_LINE, TO_CMD, KL(TO_CMD), SMTP_DELIMS)) > 0) - { - t = parse_address(C_LINE + r); - sp_add_log(ctx, "to=", t); - - /* Make note of the recipient for later */ - r = ctx->recipients ? strlen(ctx->recipients) : 0; - ctx->recipients = (char*)reallocf(ctx->recipients, r + strlen(t) + 2); - if(ctx->recipients) - { - /* Recipients are separated by lines */ - if(r != 0) - strcat(ctx->recipients, "\n"); - else - ctx->recipients[0] = 0; - - strcat(ctx->recipients, t); - } - } - - /* RSET */ - else if(is_first_word(C_LINE, RSET_CMD, KL(RSET_CMD))) - { - cleanup_context(ctx); - } - } - - if(spio_write_data(ctx, &(ctx->client), S_LINE) == -1) - RETURN(-1); - - continue; - } - } - -cleanup: - - if(!neterror && ret == -1 && spio_valid(&(ctx->client))) - spio_write_data(ctx, &(ctx->client), SMTP_FAILED); - - return ret; -} - -/* ----------------------------------------------------------------------------- - * SMTP PASSTHRU FUNCTIONS FOR DATA CHECK - */ - -static char* parse_address(char* line) -{ - char* t; - line = trim_start(line); - - /* - * We parse out emails in the form of <blah@blah.com> - * as well as accept other addresses. - */ - if(line[0] == '<') - { - if((t = strchr(line, '>')) != NULL) - { - *t = 0; - line++; - return line; - } - } - - return trim_end(line); -} - -static const char* get_successful_rsp(const char* line, int* cont) -{ - /* - * We check for both '250 xxx' type replies - * and the continued response '250-xxxx' type - */ - - line = trim_start(line); - - if(line[0] == '2' && isdigit(line[1]) && isdigit(line[2]) && - (line[3] == ' ' || line[3] == '-')) - { - if(cont) - *cont = (line[3] == '-'); - return line + 4; - } - - return NULL; -} - -void sp_add_log(spctx_t* ctx, char* prefix, char* line) -{ - char* t = ctx->logline; - int l = strlen(t); - int x; - - ASSERT(l <= SP_LOG_LINE_LEN); - - /* Add up necessary lengths */ - x = 2 + strlen(prefix) + strlen(line) + 1; - - if(l + x >= SP_LOG_LINE_LEN) - l = SP_LOG_LINE_LEN - x; - - t += l; - l = SP_LOG_LINE_LEN - l; - - *t = 0; - - if(ctx->logline[0] != 0) - strlcat(t, ", ", l); - - strlcat(t, prefix, l); - - /* Skip initial white space */ - line = trim_start(line); - - strlcat(t, line, l); - - /* Skip later white space */ - trim_end(t); -} - -int sp_read_data(spctx_t* ctx, const char** data) -{ - int r; - - ASSERT(ctx); - ASSERT(data); - - *data = NULL; - - switch(r = spio_read_line(ctx, &(ctx->client), SPIO_QUIET)) - { - case 0: - sp_messagex(ctx, LOG_ERR, "unexpected end of data from client"); - return -1; - case -1: - /* Message already printed */ - return -1; - }; - - if(g_state.keepalives > 0) - { - /* - * During this time we're just reading from the client. If we haven't - * had any interaction with the server recently then send something - * to let it know we're still around. - */ - if((ctx->server.last_action + g_state.keepalives) < time(NULL)) - do_server_noop(ctx); - } - - if(ctx->_crlf && strcmp(ctx->client.line, DATA_END_SIG) == 0) - return 0; - - /* Check if this line ended with a CRLF */ - ctx->_crlf = (strcmp(CRLF, ctx->client.line + (r - KL(CRLF))) == 0); - *data = ctx->client.line; - return r; -} - -int sp_write_data(spctx_t* ctx, const char* buf, int len) -{ - int r = 0; - - ASSERT(ctx); - - /* When a null buffer close the cache file */ - if(!buf) - { - if(ctx->cachefile) - { - if(fclose(ctx->cachefile) == EOF) - { - sp_message(ctx, LOG_ERR, "couldn't write to cache file: %s", ctx->cachename); - r = -1; - } - - ctx->cachefile = NULL; - } - - return r; - } - - /* Make sure we have a file open */ - if(!ctx->cachefile) - { - int tfd; - - /* Make sure afore mentioned file is gone */ - if(ctx->cachename[0]) - unlink(ctx->cachename); - - snprintf(ctx->cachename, MAXPATHLEN, "%s/%s.XXXXXX", - g_state.directory, g_state.name); - - if((tfd = mkstemp(ctx->cachename)) == -1 || - (ctx->cachefile = fdopen(tfd, "w")) == NULL) - { - if(tfd != -1) - close(tfd); - - sp_message(ctx, LOG_ERR, "couldn't open cache file"); - return -1; - } - - fcntl(tfd, F_SETFD, fcntl(tfd, F_GETFD, 0) | FD_CLOEXEC); - sp_messagex(ctx, LOG_DEBUG, "created cache file: %s", ctx->cachename); - } - - fwrite(buf, 1, len, ctx->cachefile); - - if(ferror(ctx->cachefile)) - { - sp_message(ctx, LOG_ERR, "couldn't write to cache file: %s", ctx->cachename); - return -1; - } - - return len; -} - -int sp_cache_data(spctx_t* ctx) -{ - int r, count = 0; - const char* data; - - while((r = sp_read_data(ctx, &data)) != 0) - { - if(r < 0) - return -1; /* Message already printed */ - - count += r; - - if((r = sp_write_data(ctx, data, r)) < 0) - return -1; /* Message already printed */ - } - - /* End the caching */ - if(sp_write_data(ctx, NULL, 0) < 0) - return -1; - - sp_messagex(ctx, LOG_DEBUG, "wrote %d bytes to cache", count); - return count; -} - -/* Important: |date| should be at least MAX_DATE_LENGTH long */ -static void make_date(spctx_t* ctx, char* date) -{ - size_t date_len; - struct tm t2; - time_t t; - - /* Get a basic date like: 'Wed Jun 30 21:49:08 1993' */ - if(time(&t) == (time_t)-1 || - !localtime_r(&t, &t2) || - !asctime_r(&t2, date)) - { - sp_message(ctx, LOG_WARNING, "unable to get date for header"); - date[0] = 0; - return; - } - - trim_end(date); - date_len = strlen(date); - - { -#ifdef HAVE_TM_GMTOFF - time_t timezone = t2.tm_gmtoff; - char *tzname[2] = { t2.tm_zone, "" }; -#endif - - snprintf(date + date_len, MAX_DATE_LENGTH - date_len, " %+03d%02d (%s)", - (int)(timezone / 3600), (int)(timezone % 3600), tzname[2]); - } - - /* Break it off just in case */ - date[MAX_DATE_LENGTH - 1] = 0; -} - -/* Important: |header| should be a buffer of MAX_HEADER_LENGTH */ -static int make_header(spctx_t* ctx, const char* format_str, char* header) -{ - char date[MAX_DATE_LENGTH]; - int remaining, l; - const char* f; - char* p; - - date[0] = 0; - remaining = MAX_HEADER_LENGTH - 1; - p = header; - - /* Parse the format string and replace special characters with our data */ - for(f = format_str; *f && remaining > 0; f++) - { - /* A backslash escapes certain characters */ - if(f[0] == '\\' && f[1] != 0) - { - switch(*(++f)) - { - case 'r': - *p = '\r'; - break; - case 'n': - *p = '\n'; - break; - case 't': - *p = '\t'; - break; - default: - *p = *f; - break; - } - - ++p; - --remaining; - } - - /* - * Special symbols: - * %i: client's IP - * %l: server's IP - * %d: date - */ - else if(f[0] == '%' && f[1] != 0) - { - switch(*(++f)) - { - case 'i': - l = strlen(ctx->client.peername); - strncpy(p, ctx->client.peername, remaining); - remaining -= l; - p += l; - break; - case 'l': - l = strlen(ctx->client.localname); - strncpy(p, ctx->client.localname, remaining); - remaining -= l; - p += l; - break; - case 'd': - if(date[0] == 0) - make_date(ctx, date); - l = strlen(date); - strncpy(p, date, remaining); - remaining -= l; - p += l; - break; - case '%': - *p = '%'; - ++p; - break; - default: - sp_messagex(ctx, LOG_WARNING, "invalid header symbol: %%%c", *f); - break; - }; - } - - else - { - *(p++) = *f; - remaining--; - } - } - - if((p + 1) < (header + MAX_HEADER_LENGTH)) - p[1] = 0; - header[MAX_HEADER_LENGTH - 1] = 0; - l = p - header; - return l >= MAX_HEADER_LENGTH ? MAX_HEADER_LENGTH - 1 : l; -} - -int sp_done_data(spctx_t* ctx) -{ - FILE* file = 0; - int had_header = 0; - int ret = 0; - char line[SP_LINE_LENGTH]; - char header[MAX_HEADER_LENGTH]; - size_t header_len; - - ASSERT(ctx->cachename[0]); /* Must still be around */ - ASSERT(!ctx->cachefile); /* File must be closed */ - - memset(header, 0, sizeof(header)); - - /* Open the file */ - file = fopen(ctx->cachename, "r"); - if(file == NULL) - { - sp_message(ctx, LOG_ERR, "couldn't open cache file: %s", ctx->cachename); - RETURN(-1); - } - - /* Ask the server for permission to send data */ - if(spio_write_data(ctx, &(ctx->server), SMTP_DATA) == -1) - RETURN(-1); - - if(read_server_response(ctx) == -1) - RETURN(-1); - - /* If server returns an error then tell the client */ - if(!is_first_word(ctx->server.line, DATA_RSP, KL(DATA_RSP))) - { - if(spio_write_data(ctx, &(ctx->client), ctx->server.line) == -1) - RETURN(-1); - - sp_messagex(ctx, LOG_DEBUG, "server refused data transfer"); - - RETURN(-1); - } - - sp_messagex(ctx, LOG_DEBUG, "sending from cache file: %s", ctx->cachename); - - if(g_state.header) - header_len = make_header(ctx, g_state.header, header); - - /* If we have to prepend the header, do it */ - if(header[0] && g_state.header_prepend) - { - if(spio_write_data_raw(ctx, &(ctx->server), (char*)header, header_len) == -1 || - spio_write_data_raw(ctx, &(ctx->server), CRLF, KL(CRLF)) == -1) - RETURN(-1); - had_header = 1; - } - - /* Transfer actual file data */ - while(fgets(line, SP_LINE_LENGTH, file) != NULL) - { - /* - * If the line is <CRLF>.<CRLF> we need to change it so that - * it doesn't end the email. We do this by adding a space. - * This won't occur much in clamsmtpd, but proxsmtpd might - * have filters that accidentally put this in. - */ - if(strcmp(line, "." CRLF) == 0) - strncpy(line, ". " CRLF, SP_LINE_LENGTH); - - if(header[0] && !had_header) - { - /* - * The first blank line we see means the headers are done. - * At this point we add in our virus checked header. - */ - if(is_blank_line(line)) - { - if(spio_write_data_raw(ctx, &(ctx->server), (char*)header, header_len) == -1 || - spio_write_data_raw(ctx, &(ctx->server), CRLF, KL(CRLF)) == -1) - RETURN(-1); - - had_header = 1; - } - } - - if(spio_write_data_raw(ctx, &(ctx->server), line, strlen(line)) == -1) - RETURN(-1); - } - - if(ferror(file)) - sp_message(ctx, LOG_ERR, "error reading cache file: %s", ctx->cachename); - - if(ferror(file) || spio_write_data(ctx, &(ctx->server), DATA_END_SIG) == -1) - { - /* Tell the client it went wrong */ - spio_write_data(ctx, &(ctx->client), SMTP_FAILED); - RETURN(-1); - } - - sp_messagex(ctx, LOG_DEBUG, "sent email data"); - - /* Okay read the response from the server and echo it to the client */ - if(read_server_response(ctx) == -1) - RETURN(-1); - - if(spio_write_data(ctx, &(ctx->client), ctx->server.line) == -1) - RETURN(-1); - -cleanup: - - if(file) - fclose(file); /* read-only so no error check */ - - return ret; -} - -int sp_fail_data(spctx_t* ctx, const char* smtp_status) -{ - char buf[256 + KL(SMTP_REJPREFIX) + KL(CRLF) + 1]; - char* t = NULL; - int len, x; - int pref = 0; - int crlf = 0; - - if(smtp_status == NULL) - smtp_status = SMTP_FAILED; - - x = strtol(smtp_status, &t, 10); - len = strlen(smtp_status); - - /* We need 3 digits and CRLF at the end for a premade SMTP message */ - if(x == 0 || t != smtp_status + 3) - pref = 1; - - /* We need a CRLF at the end */ - if(strcmp(smtp_status + (len - KL(CRLF)), CRLF) != 0) - crlf = 1; - - if(pref || crlf) - { - /* Note that we truncate long lines */ - snprintf(buf, sizeof(buf), "%s%.256s%s", pref ? SMTP_REJPREFIX : "", - smtp_status, crlf ? CRLF : ""); - buf[sizeof(buf) - 1] = 0; - smtp_status = buf; - } - - if(spio_write_data(ctx, &(ctx->client), smtp_status) == -1) - return -1; - - return 0; -} - -static int read_server_response(spctx_t* ctx) -{ - int r; - - /* Read response line from the server */ - if((r = spio_read_line(ctx, &(ctx->server), SPIO_DISCARD)) == -1) - return -1; - - if(r == 0) - { - sp_messagex(ctx, LOG_ERR, "server disconnected unexpectedly"); - - /* Tell the client it went wrong */ - spio_write_data(ctx, &(ctx->client), SMTP_FAILED); - return 0; - } - - if(LINE_TOO_LONG(r)) - sp_messagex(ctx, LOG_WARNING, "SMTP response line too long. discarded extra"); - - return 0; -} - -static void do_server_noop(spctx_t* ctx) -{ - if(spio_valid(&(ctx->server))) - { - if(spio_write_data(ctx, &(ctx->server), SMTP_NOOP) != -1) - spio_read_line(ctx, &(ctx->server), SPIO_DISCARD); - } -} - -void sp_setup_forked(spctx_t* ctx, int file) -{ - /* Signals we've messed with */ - signal(SIGPIPE, SIG_DFL); - signal(SIGHUP, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGTERM, SIG_DFL); - - siginterrupt(SIGINT, 0); - siginterrupt(SIGTERM, 0); - - if(ctx->sender) - setenv("SENDER", ctx->sender, 1); - - if(ctx->recipients) - setenv("RECIPIENTS", ctx->recipients, 1); - - if(file && ctx->cachename[0]) - setenv("EMAIL", ctx->cachename, 1); - - if(spio_valid(&(ctx->client))) - setenv("CLIENT", ctx->client.peername, 1); - - if(spio_valid(&(ctx->server))) - setenv("SERVER", ctx->server.peername, 1); - - setenv("TMPDIR", g_state.directory, 1); -} - - -/* ---------------------------------------------------------------------------------- - * LOGGING - */ - -const char kMsgDelimiter[] = ": "; -#define MAX_MSGLEN 256 - -static void vmessage(spctx_t* ctx, int level, int err, - const char* msg, va_list ap) -{ - size_t len; - char* m; - int e = errno; - - if(g_state.daemonized) - { - if(level >= LOG_DEBUG) - return; - } - else - { - if(g_state.debug_level < level) - return; - } - - ASSERT(msg); - - len = strlen(msg) + 20 + MAX_MSGLEN; - m = (char*)alloca(len); - - if(m) - { - if(ctx) - snprintf(m, len, "%06X: %s%s", ctx->id, msg, err ? ": " : ""); - else - snprintf(m, len, "%s%s", msg, err ? ": " : ""); - - if(err) - { - /* strerror_r doesn't want to work for us for some reason - strerror_r(e, m + strlen(m), MAX_MSGLEN); */ - - sp_lock(); - strncat(m, strerror(e), len); - sp_unlock(); - } - - m[len - 1] = 0; - msg = m; - } - - /* Either to syslog or stderr */ - if(g_state.daemonized) - vsyslog(level, msg, ap); - else - vwarnx(msg, ap); -} - -void sp_messagex(spctx_t* ctx, int level, const char* msg, ...) -{ - va_list ap; - - va_start(ap, msg); - vmessage(ctx, level, 0, msg, ap); - va_end(ap); -} - -void sp_message(spctx_t* ctx, int level, const char* msg, ...) -{ - va_list ap; - - va_start(ap, msg); - vmessage(ctx, level, 1, msg, ap); - va_end(ap); -} - - -/* ----------------------------------------------------------------------- - * LOCKING - */ - -void sp_lock() -{ - int r; - -#ifdef _DEBUG - int wait = 0; -#endif - -#ifdef _DEBUG - r = pthread_mutex_trylock(&g_mutex); - if(r == EBUSY) - { - wait = 1; - sp_message(NULL, LOG_DEBUG, "thread will block: %d", pthread_self()); - r = pthread_mutex_lock(&g_mutex); - } - -#else - r = pthread_mutex_lock(&g_mutex); - -#endif - - if(r != 0) - { - errno = r; - sp_message(NULL, LOG_CRIT, "threading problem. couldn't lock mutex"); - } - -#ifdef _DEBUG - else if(wait) - { - sp_message(NULL, LOG_DEBUG, "thread unblocked: %d", pthread_self()); - } -#endif -} - -void sp_unlock() -{ - int r = pthread_mutex_unlock(&g_mutex); - if(r != 0) - { - errno = r; - sp_message(NULL, LOG_CRIT, "threading problem. couldn't unlock mutex"); - } -} - -/* ----------------------------------------------------------------------------- - * CONFIG FILE - */ - -int sp_parse_option(const char* name, const char* value) -{ - char* t; - int ret = 0; - - if(strcasecmp(CFG_MAXTHREADS, name) == 0) - { - g_state.max_threads = strtol(value, &t, 10); - if(*t || g_state.max_threads <= 1 || g_state.max_threads >= 1024) - errx(2, "invalid setting: " CFG_MAXTHREADS " (must be between 1 and 1024)"); - ret = 1; - } - - else if(strcasecmp(CFG_TIMEOUT, name) == 0) - { - g_state.timeout.tv_sec = strtol(value, &t, 10); - if(*t || g_state.timeout.tv_sec <= 0) - errx(2, "invalid setting: " CFG_TIMEOUT); - ret = 1; - } - - else if(strcasecmp(CFG_KEEPALIVES, name) == 0) - { - g_state.keepalives = strtol(value, &t, 10); - if(*t || g_state.keepalives < 0) - errx(2, "invalid setting: " CFG_KEEPALIVES); - ret = 1; - } - - else if(strcasecmp(CFG_XCLIENT, name) == 0) - { - if((g_state.xclient = strtob(value)) == -1) - errx(2, "invalid value for " CFG_XCLIENT); - ret = 1; - } - - else if(strcasecmp(CFG_OUTADDR, name) == 0) - { - if(sock_any_pton(value, &(g_state.outaddr), SANY_OPT_DEFPORT(25)) == -1) - errx(2, "invalid " CFG_OUTADDR " socket name or ip: %s", value); - g_state.outname = value; - ret = 1; - } - - else if(strcasecmp(CFG_LISTENADDR, name) == 0) - { - if(sock_any_pton(value, &(g_state.listenaddr), SANY_OPT_DEFANY | SANY_OPT_DEFPORT(DEFAULT_PORT)) == -1) - errx(2, "invalid " CFG_LISTENADDR " socket name or ip: %s", value); - g_state.listenname = value; - ret = 1; - } - - else if(strcasecmp(CFG_TRANSPARENT, name) == 0) - { - if((g_state.transparent = strtob(value)) == -1) - errx(2, "invalid value for " CFG_TRANSPARENT); - ret = 1; - } - - else if(strcasecmp(CFG_DIRECTORY, name) == 0) - { - if(strlen(value) == 0) - errx(2, "invalid setting: " CFG_DIRECTORY); - g_state.directory = value; - ret = 1; - } - - else if(strcasecmp(CFG_USER, name) == 0) - { - if(strlen(value) == 0) - errx(2, "invalid setting: " CFG_USER); - g_state.user = value; - ret = 1; - } - - else if(strcasecmp(CFG_PIDFILE, name) == 0) - { - if(g_state.pidfile != NULL) - sp_messagex(NULL, LOG_WARNING, "ignoring pid file specified on the command line. "); - - if(strlen(value) == 0) - g_state.pidfile = NULL; - else - g_state.pidfile = value; - ret = 1; - } - - else if(strcasecmp(CFG_HEADER, name) == 0) - { - g_state.header = trim_start(value); - if(strlen(g_state.header) == 0) - g_state.header = NULL; - else if(is_first_word(RCVD_HEADER, g_state.header, KL(RCVD_HEADER))) - g_state.header_prepend = 1; - ret = 1; - } - - /* Always pass through to program */ - if(cb_parse_option(name, value) == 1) - ret = 1; - - return ret; -} - -static int parse_config_file(const char* configfile) -{ - FILE* f = NULL; - long len; - char* p; - char* t; - char* n; - - ASSERT(configfile); - ASSERT(!g_state._p); - - f = fopen(configfile, "r"); - if(f == NULL) - { - /* Soft errors when default config file and not found */ - if((errno == ENOENT || errno == ENOTDIR)) - return -1; - else - err(1, "couldn't open config file: %s", configfile); - } - - /* Figure out size */ - if(fseek(f, 0, SEEK_END) == -1 || (len = ftell(f)) == -1 || fseek(f, 0, SEEK_SET) == -1) - err(1, "couldn't seek config file: %s", configfile); - - if((g_state._p = (char*)malloc(len + 2)) == NULL) - errx(1, "out of memory"); - - /* And read in one block */ - if(fread(g_state._p, 1, len, f) != len) - err(1, "couldn't read config file: %s", configfile); - - fclose(f); - sp_messagex(NULL, LOG_DEBUG, "read config file: %s", configfile); - - /* Double null terminate the data */ - p = g_state._p; - p[len] = '\n'; - p[len + 1] = 0; - - n = g_state._p; - - /* Go through lines and process them */ - while((t = strchr(n, '\n')) != NULL) - { - *t = 0; - p = n; /* Do this before cleaning below */ - n = t + 1; - - p = trim_start(p); - - /* Comments and empty lines */ - if(*p == 0 || *p == '#') - continue; - - /* Look for the break between name: value */ - t = strchr(p, ':'); - if(t == NULL) - errx(2, "invalid config line: %s", p); - - /* Null terminate and split value part */ - *t = 0; - t++; - - t = trim_space(t); - p = trim_space(p); - - /* Pass it through our options parsers */ - if(sp_parse_option(p, t) == 0) - - /* If not recognized then it's invalid */ - errx(2, "invalid config line: %s", p); - - sp_messagex(NULL, LOG_DEBUG, "parsed option: %s: %s", p, t); - } - - return 0; -} - diff --git a/common/smtppass.h b/common/smtppass.h deleted file mode 100644 index 9dbe935..0000000 --- a/common/smtppass.h +++ /dev/null @@ -1,281 +0,0 @@ -/* - * 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 __SMTPPASS_H__ -#define __SMTPPASS_H__ - -/* Forward declarations */ -struct sockaddr_any; -struct spctx; - -/* ----------------------------------------------------------------------------- - * BUFFERED MULTIPLEXING IO - * - * This isn't meant to be a replacement library for all sorts of IO - * only things that are currently used go here. - */ - -/* - * A generous maximum line length. It needs to be longer than - * a full path on this system can be, because we pass the file - * name to clamd. - */ - -#if 2000 > MAXPATHLEN - #define SP_LINE_LENGTH 2000 -#else - #define SP_LINE_LENGTH (MAXPATHLEN + 128) -#endif - -typedef struct spio -{ - int fd; /* The file descriptor wrapped */ - const char* name; /* The name for logging */ - time_t last_action; /* Time of last action on descriptor */ - char peername[MAXPATHLEN]; /* Name of the peer on other side of socket */ - char localname[MAXPATHLEN]; /* Address where we accepted the connection */ - - /* Internal use only */ - char line[SP_LINE_LENGTH]; - char* _nx; - size_t _ln; -} -spio_t; - -#define spio_valid(io) ((io)->fd != -1) - -/* Setup the io structure (allocated elsewhere) */ -void spio_init(spio_t* io, const char* name); - -/* Attach an open descriptor to a socket, optionally returning the peer */ -void spio_attach(struct spctx* ctx, spio_t* io, int fd, struct sockaddr_any* peer); - -/* Connect and disconnect from sockets */ -int spio_connect(struct spctx* ctx, spio_t* io, const struct sockaddr_any* sany, const char* addrname); -void spio_disconnect(struct spctx* ctx, spio_t* io); - -#define SPIO_TRIM 0x00000001 -#define SPIO_DISCARD 0x00000002 -#define SPIO_QUIET 0x00000004 - -/* Read a line from a socket. Use options above. Line - * will be found in io->line */ -int spio_read_line(struct spctx* ctx, spio_t* io, int opts); - -/* Write data to socket (must supply line endings if needed). - * Guaranteed to accept all data or fail. */ -int spio_write_data(struct spctx* ctx, spio_t* io, const char* data); -int spio_write_dataf(struct spctx* ctx, spio_t* io, const char* fmt, ...); -int spio_write_data_raw(struct spctx* ctx, spio_t* io, unsigned char* buf, int len); - -/* Empty the given socket */ -void spio_read_junk(struct spctx* sp, spio_t* io); - -/* Pass up to 31 spio_t*, followed by NULL. Returns bitmap of ready for reading */ -unsigned int spio_select(struct spctx* ctx, ...); - - -/* ----------------------------------------------------------------------------- - * SMTP PASS THROUGH FUNCTIONALITY - */ - -/* Log lines have to be under roughly 900 chars otherwise - * they get truncated by syslog. */ -#define SP_LOG_LINE_LEN 768 - -typedef struct spctx -{ - unsigned int id; /* Identifier for the connection */ - - spio_t client; /* Connection to client */ - spio_t server; /* Connection to server */ - - FILE* cachefile; /* The file handle for the cached file */ - char cachename[MAXPATHLEN]; /* The name of the file that we cache into */ - char logline[SP_LOG_LINE_LEN]; /* Log line */ - - char* sender; /* The email of the sender */ - char* recipients; /* The email of the recipients */ - - int _crlf; /* Private data */ -} -spctx_t; - -/* - * sp_init initializes the SMTP Pass-Through functionality - * The name passed is the name of the app - */ -void sp_init(const char* name); - -/* - * This starts up the smtp pass thru program. It will call - * the cb_* functions as appropriate. - */ -int sp_run(const char* configfile, const char* pidfile, int dbg_level); - -/* - * Mark the application as shutting down. - * A signal will interupt most IO. - */ -void sp_quit(); - -/* - * Check if the application has been marked to quit. - * Useful for checking after interupted IO. - */ -int sp_is_quit(); - -/* - * Called to cleanup SMTP Pass-Through functionality just - * before the application quits. - */ -void sp_done(); - -/* - * clamsmtpd used to accept command line args. In order to - * process those args it needs to send the values to the - * config file routines. This is how it does that. - */ -#ifdef SP_LEGACY_OPTIONS -int sp_parse_option(const char* name, const char* option); -#endif - - -/* - * The following functions are to be called from within - * the spc_check_data function. - */ - -/* - * Adds a piece of info to the log line - */ -void sp_add_log(spctx_t* ctx, char* prefix, char* line); - -/* - * Reads a line of DATA from client. Or less than a line if - * line is longer than LINE_LENGTH. No trimming or anything - * is done on the read line. This will end automatically - * when <CRLF>.<CRLF> is detected (in which case 0 will - * be returned). The data is returned in data. - */ -int sp_read_data(spctx_t* ctx, const char** data); - -/* - * Writes a line (or piece) of data to a file buffer which is - * later sent to the client using sp_done_data. Calling it with - * a NULL buffer closes the cache file. Guaranteed to accept - * all data given to it or fail. - */ -int sp_write_data(spctx_t* ctx, const char* buf, int buflen); - -/* - * Sends all DATA from the client into the cache. The cache - * file is then available (in spctx_t->cachename) for use. - */ -int sp_cache_data(spctx_t* ctx); - -/* - * Sends the data in file buffer off to server. This is - * completes a successful mail transfer. - */ -int sp_done_data(spctx_t* ctx); - -/* - * Fails the data, deletes any temp data, and sends given - * status to client or if NULL then SMTP_DATAFAILED - */ -int sp_fail_data(spctx_t* ctx, const char* smtp_status); - -/* - * Setup the environment with context info. This is useful - * if you're going to fork another process. Be sure to exec - * soon after to prevent the strings from going out of scope. - */ -void sp_setup_forked(spctx_t* ctx, int file); - -/* - * Log a message. levels are syslog levels. Syntax is just - * like printf etc.. Can specify a ctx of NULL in which case - * no connection prefix is prepended. - */ -void sp_message(spctx_t* ctx, int level, const char* msg, ...); -void sp_messagex(spctx_t* ctx, int level, const char* msg, ...); - -/* - * Lock or unlock the main mutex around thread common - * functionality. - */ -void sp_lock(); -void sp_unlock(); - - -/* ----------------------------------------------------------------------------- - * CALLBACKS IMPLMEMENTED BY PROGRAM - */ - -/* - * The following functions create and destroy contexts for a new - * thread. Perform initialization in there and return a spctx - * structure for the thread to use. Return NULL failed. Be sure - * to log message. - */ -extern spctx_t* cb_new_context(); -extern void cb_del_context(spctx_t* ctx); - -/* - * Called when the data section of an email is being transferred. - * Once inside this function you can transfer files using - * sp_read_data, sp_write_data. - * - * After scanning or figuring out the status call either - * sp_done_data or sp_fail_data. Most failures should be handled - * internally using sp_fail_data, unless it's an out of memory - * condition, or sp_fail_data failed. - */ -extern int cb_check_data(spctx_t* ctx); - -/* - * Parse options from the config file. The memory for these - * options will stay around until sp_done is called. Return 0 - * for unrecognized options, 1 for recognized, and quit the - * program for invalid. - */ -extern int cb_parse_option(const char* name, const char* value); - - -#endif /* __SMTPPASS_H__ */ diff --git a/common/sock_any.c b/common/sock_any.c deleted file mode 100644 index 7535e02..0000000 --- a/common/sock_any.c +++ /dev/null @@ -1,385 +0,0 @@ -/* - * 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/socket.h> - -#include <ctype.h> -#include <stdlib.h> -#include <errno.h> -#include <netdb.h> -#include <string.h> -#include <stdio.h> - -#include "sock_any.h" - -#include <arpa/inet.h> - -#define LOCALHOST_ADDR 0x7F000001 - -int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts) -{ - size_t l; - char buf[256]; - char* t; - char* t2; - int defport = (opts & 0xFFFF); - - memset(any, 0, sizeof(*any)); - - /* Just a port? */ - do - { - #define PORT_CHARS "0123456789" - #define PORT_MIN 1 - #define PORT_MAX 5 - - int port = 0; - - l = strspn(addr, PORT_CHARS); - if(l < PORT_MIN || l > PORT_MAX || addr[l] != 0) - break; - - port = strtol(addr, &t2, 10); - if(*t2 || port <= 0 || port >= 65536) - break; - - any->s.in.sin_port = htons(port); - - /* Fill in the type based on defaults */ -#ifdef HAVE_INET6 - if(opts & SANY_OPT_DEFINET6) - any->s.in.sin_family = AF_INET6; - else -#endif - any->s.in.sin_family = AF_INET; - - /* Fill in the address based on defaults */ - if(opts & SANY_OPT_DEFLOCAL) - { -#ifdef HAVE_INET6 - if(opts & SANY_OPT_DEFINET6) - memcpy(&(any->s.in.sin6_addr), &in6addr_loopback, sizeof(struct in6_addr)); - else -#endif - any->s.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - } - - /* - * Note the 'any' option is the default since we zero out - * the entire structure above. - */ - - any->namelen = sizeof(any->s.in); - return AF_INET; - } - while(0); - - /* Look and see if we can parse an ipv4 address */ - do - { - #define IPV4_PORT_CHARS - #define IPV4_CHARS "0123456789." - #define IPV4_MIN 3 - #define IPV4_MAX 18 - - int port = 0; - t = NULL; - - l = strlen(addr); - if(l < IPV4_MIN || l > IPV4_MAX) - break; - - strcpy(buf, addr); - - /* Find the last set that contains just numbers */ - l = strspn(buf, IPV4_CHARS); - if(l < IPV4_MIN) - break; - - /* Either end of string or port */ - if(buf[l] != 0 && buf[l] != ':') - break; - - /* Get the port out */ - if(buf[l] != 0) - { - t = buf + l + 1; - buf[l] = 0; - } - - if(t) - { - port = strtol(t, &t2, 10); - if(*t2 || port <= 0 || port >= 65536) - break; - } - - any->s.in.sin_family = AF_INET; - any->s.in.sin_port = htons((unsigned short)(port <= 0 ? defport : port)); - - if(inet_pton(AF_INET, buf, &(any->s.in.sin_addr)) <= 0) - break; - - any->namelen = sizeof(any->s.in); - return AF_INET; - } - while(0); - -#ifdef HAVE_INET6 - do - { - #define IPV6_CHARS "0123456789:" - #define IPV6_MIN 3 - #define IPV6_MAX 48 - - int port = -1; - t = NULL; - - l = strlen(addr); - if(l < IPV6_MIN || l > IPV6_MAX) - break; - - /* If it starts with a '[' then we can get port */ - if(buf[0] == '[') - { - port = 0; - addr++; - } - - strcpy(buf, addr); - - /* Find the last set that contains just numbers */ - l = strspn(buf, IPV6_CHARS); - if(l < IPV6_MIN) - break; - - /* Either end of string or port */ - if(buf[l] != 0) - { - /* If had bracket, then needs to end with a bracket */ - if(port != 0 || buf[l] != ']') - break; - - /* Get the port out */ - t = buf + l + 1; - - if(*t = ':') - t++; - } - - if(t) - { - port = strtol(t, &t, 10); - if(*t || port <= 0 || port >= 65536) - break; - } - - any->s.in6.sin6_family = AF_INET6; - any->s.in6.sin6_port = htons((unsigned short)port <= 0 : defport : port); - - if(inet_pton(AF_INET6, buf, &(any->s.in6.sin6_addr)) >= 0) - break; - - any->namelen = sizeof(any->s.in6); - return AF_INET6; - } - while(0); -#endif - - /* A unix socket path */ - do - { - /* No colon and must have a path component */ - if(strchr(addr, ':') || !strchr(addr, '/')) - break; - - l = strlen(addr); - if(l >= sizeof(any->s.un.sun_path)) - break; - - any->s.un.sun_family = AF_UNIX; - strcpy(any->s.un.sun_path, addr); - - any->namelen = sizeof(any->s.un) - (sizeof(any->s.un.sun_path) - l); - return AF_UNIX; - } - while(0); - - /* A DNS name and a port? */ - do - { - struct addrinfo* res; - int port = 0; - t = NULL; - - l = strlen(addr); - if(l >= 255 || !isalpha(addr[0])) - break; - - /* Some basic illegal character checks */ - if(strcspn(addr, " /\\") != l) - break; - - strcpy(buf, addr); - - /* Find the last set that contains just numbers */ - t = strchr(buf, ':'); - if(t) - { - *t = 0; - t++; - } - - if(t) - { - port = strtol(t, &t2, 10); - if(*t2 || port <= 0 || port >= 65536) - break; - } - - /* Try and resolve the domain name */ - if(getaddrinfo(buf, NULL, NULL, &res) != 0 || !res) - break; - - memcpy(&(any->s.a), res->ai_addr, sizeof(struct sockaddr)); - any->namelen = res->ai_addrlen; - freeaddrinfo(res); - - port = htons((unsigned short)(port <= 0 ? defport : port)); - - switch(any->s.a.sa_family) - { - case PF_INET: - any->s.in.sin_port = port; - break; -#ifdef HAVE_INET6 - case PF_INET6: - any->s.in6.sin6_port = port; - break; -#endif - }; - - return any->s.a.sa_family; - } - while(0); - - return -1; -} - -int sock_any_ntop(const struct sockaddr_any* any, char* addr, size_t addrlen, int opts) -{ - int len = 0; - int port = 0; - - switch(any->s.a.sa_family) - { - case AF_UNIX: - len = strlen(any->s.un.sun_path); - if(addrlen < len + 1) - { - errno = ENOSPC; - return -1; - } - - strcpy(addr, any->s.un.sun_path); - break; - - case AF_INET: - if(inet_ntop(any->s.a.sa_family, &(any->s.in.sin_addr), addr, addrlen) == NULL) - return -1; - port = ntohs(any->s.in.sin_port); - break; - -#ifdef HAVE_INET6 - case AF_INET6: - if(inet_ntop(any->s.a.sa_family, &(any->s.in6.sin6_addr), addr, addrlen) == NULL) - return -1; - port = ntohs(any->s.in6.sin6_port); - break; -#endif - - default: - errno = EAFNOSUPPORT; - return -1; - } - - if(!(opts & SANY_OPT_NOPORT) && port != 0) - { - strncat(addr, ":", addrlen); - addr[addrlen - 1] = 0; - - len = strlen(addr); - addr += len; - addrlen -= len; - - snprintf(addr, addrlen, "%d", port); - } - - return 0; -} - -int sock_any_cmp(const struct sockaddr_any* a1, const struct sockaddr_any* a2, int opts) -{ - if(a1->s.a.sa_family != a2->s.a.sa_family) - return -1; - - switch(a1->s.a.sa_family) - { - case AF_UNIX: - return strcmp(a1->s.un.sun_path, a2->s.un.sun_path); - - case AF_INET: - if(memcmp(&(a1->s.in.sin_addr), &(a2->s.in.sin_addr), sizeof(a2->s.in.sin_addr)) != 0) - return -1; - if(!(opts && SANY_OPT_NOPORT) && a1->s.in.sin_port != a2->s.in.sin_port) - return -1; - return 0; -#ifdef HAVE_INET6 - case AF_INET6: - if(memcmp(&(a1->s.in6.sin6_addr), &(a2->s.in6.sin6_addr), sizeof(a2->s.in6.sin6_addr)) != 0) - return -1; - if(!(opts && SANY_OPT_NOPORT) && a1->s.in6.sin6_port != a2->s.in6.sin6_port) - return -1; - return 0; -#endif - default: - errno = EAFNOSUPPORT; - return -1; - } -} diff --git a/common/sock_any.h b/common/sock_any.h deleted file mode 100644 index 31cb13b..0000000 --- a/common/sock_any.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 __SOCK_ANY_H__ -#define __SOCK_ANY_H__ - -#include <sys/socket.h> -#include <sys/un.h> -#include <netinet/in.h> - -struct sockaddr_any -{ - union _sockaddr_any - { - /* The header */ - struct sockaddr a; - - /* The different types */ - struct sockaddr_un un; - struct sockaddr_in in; -#ifdef HAVE_INET6 - struct sockaddr_in6 in6; -#endif - } s; - size_t namelen; -}; - -#define SANY_ADDR(any) ((any).s.a) -#define SANY_LEN(any) ((any).namelen) -#define SANY_TYPE(any) ((any).s.a.sa_family) - -int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts); - -/* The default port to fill in when no IP/IPv6 port specified */ -#define SANY_OPT_DEFPORT(p) (int)((p) & 0xFFFF) - -/* When only port specified default to IPANY */ -#define SANY_OPT_DEFANY 0x00000000 - -/* When only port specified default to LOCALHOST */ -#define SANY_OPT_DEFLOCAL 0x00100000 - -/* When only port specified default to IPv6 */ -#ifdef HAVE_INET6 -#define SANY_OPT_DEFINET6 0x00200000 -#endif - -int sock_any_ntop(const struct sockaddr_any* any, char* addr, size_t addrlen, int opts); - -/* Don't print or compare the port */ -#define SANY_OPT_NOPORT 0x01000000 - -int sock_any_cmp(const struct sockaddr_any* a1, const struct sockaddr_any* a2, int opts); - -#endif /* __SOCK_ANY_H__ */ diff --git a/common/spio.c b/common/spio.c deleted file mode 100644 index d0efe05..0000000 --- a/common/spio.c +++ /dev/null @@ -1,638 +0,0 @@ -/* - * 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> - */ - -/* - * select() and stdio are basically mutually exclusive. - * Hence all of this code to try to get some buffering - * along with select IO multiplexing. - */ - -#include <sys/time.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/param.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <syslog.h> -#include <errno.h> -#include <stdarg.h> -#include <unistd.h> -#include <errno.h> - -#include "compat.h" -#include "usuals.h" -#include "sock_any.h" -#include "stringx.h" -#include "sppriv.h" - -#define MAX_LOG_LINE 79 -#define GET_IO_NAME(io) ((io)->name ? (io)->name : "??? ") -#define HAS_EXTRA(io) ((io)->_ln > 0) - -static void close_raw(int* fd) -{ - ASSERT(fd); - shutdown(*fd, SHUT_RDWR); - close(*fd); - *fd = -1; -} - -static void log_io_data(spctx_t* ctx, spio_t* io, const char* data, int read) -{ - char buf[MAX_LOG_LINE + 1]; - int pos, len; - - ASSERT(ctx && io && data); - - for(;;) - { - data += strspn(data, "\r\n"); - - if(!*data) - break; - - pos = strcspn(data, "\r\n"); - - len = pos < MAX_LOG_LINE ? pos : MAX_LOG_LINE; - memcpy(buf, data, len); - buf[len] = 0; - - sp_messagex(ctx, LOG_DEBUG, "%s%s%s", GET_IO_NAME(io), - read ? " < " : " > ", buf); - - data += pos; - } -} - -void spio_init(spio_t* io, const char* name) -{ - ASSERT(io && name); - memset(io, 0, sizeof(*io)); - io->name = name; - io->fd = -1; -} - -void spio_attach(spctx_t* ctx, spio_t* io, int fd, struct sockaddr_any* peer) -{ - struct sockaddr_any peeraddr; - struct sockaddr_any locaddr; - - io->fd = fd; - - /* Get the address on which we accepted the connection */ - memset(&locaddr, 0, sizeof(locaddr)); - SANY_LEN(locaddr) = sizeof(locaddr); - - if(getsockname(fd, &SANY_ADDR(locaddr), &SANY_LEN(locaddr)) == -1 || - sock_any_ntop(&locaddr, io->localname, MAXPATHLEN, SANY_OPT_NOPORT) == -1) - { - if (errno != EAFNOSUPPORT) - sp_message(ctx, LOG_WARNING, "%s: couldn't get socket address", GET_IO_NAME(io)); - strlcpy(io->localname, "UNKNOWN", MAXPATHLEN); - } - - /* If the caller doesn't want the peer then use our own */ - if (peer == NULL) - peer = &peeraddr; - - memset(peer, 0, sizeof(*peer)); - SANY_LEN(*peer) = sizeof(*peer); - - if(getpeername(fd, &SANY_ADDR(*peer), &SANY_LEN(*peer)) == -1 || - sock_any_ntop(peer, io->peername, MAXPATHLEN, SANY_OPT_NOPORT) == -1) - { - if (errno != EAFNOSUPPORT) - sp_message(ctx, LOG_WARNING, "%s: couldn't get peer address", GET_IO_NAME(io)); - strlcpy(io->peername, "UNKNOWN", MAXPATHLEN); - } - - /* As a double check */ - io->line[0] = 0; - io->_nx = NULL; - io->_ln = 0; -} - -int spio_connect(spctx_t* ctx, spio_t* io, const struct sockaddr_any* sany, - const char* addrname) -{ - int ret = 0; - int fd; - - ASSERT(ctx && io && sany && addrname); - ASSERT(io->fd == -1); - - if((fd = socket(SANY_TYPE(*sany), SOCK_STREAM, 0)) == -1) - RETURN(-1); - - if(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &(g_state.timeout), sizeof(g_state.timeout)) == -1 || - setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &(g_state.timeout), sizeof(g_state.timeout)) == -1) - sp_messagex(ctx, LOG_WARNING, "%s: couldn't set timeouts on connection", GET_IO_NAME(io)); - - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); - - if(connect(fd, &SANY_ADDR(*sany), SANY_LEN(*sany)) == -1) - { - close_raw(&fd); - RETURN(-1); - } - - spio_attach(ctx, io, fd, NULL); - -cleanup: - if(ret < 0) - { - if(spio_valid(io)) - close_raw(&(io->fd)); - - sp_message(ctx, LOG_ERR, "%s: couldn't connect to: %s", GET_IO_NAME(io), addrname); - return -1; - } - - ASSERT(io->fd != -1); - sp_messagex(ctx, LOG_DEBUG, "%s connected to: %s", GET_IO_NAME(io), io->peername); - return 0; -} - -void spio_disconnect(spctx_t* ctx, spio_t* io) -{ - ASSERT(ctx && io); - - if(spio_valid(io)) - { - close_raw(&(io->fd)); - sp_messagex(ctx, LOG_DEBUG, "%s connection closed", GET_IO_NAME(io)); - } -} - -unsigned int spio_select(spctx_t* ctx, ...) -{ - fd_set mask; - spio_t* io; - int ret = 0; - int have = 0; - int i = 0; - va_list ap; - struct timeval timeout; - - ASSERT(ctx); - FD_ZERO(&mask); - - va_start(ap, ctx); - - while((io = va_arg(ap, spio_t*)) != NULL) - { - if(spio_valid(io)) - { - /* We can't handle more than 31 args */ - if(i > (sizeof(int) * 8) - 2) - break; - - /* Check if the buffer has something in it */ - if(HAS_EXTRA(io)) - ret |= (1 << i); - - /* Mark for select */ - FD_SET(io->fd, &mask); - have = 1; - } - - i++; - } - - va_end(ap); - - /* If any buffers had something present, then return */ - if(ret != 0) - return ret; - - /* No valid file descriptors */ - if(!have) - return ~0; - - for(;;) - { - /* Select can modify the timeout argument so we copy */ - memcpy(&timeout, &(g_state.timeout), sizeof(timeout)); - - /* Otherwise wait on more data */ - switch(select(FD_SETSIZE, &mask, NULL, NULL, &timeout)) - { - case 0: - sp_messagex(ctx, LOG_ERR, "network operation timed out"); - return ~0; - - case -1: - if(errno == EINTR) - { - if(!sp_is_quit()) - continue; - } - - else - sp_message(ctx, LOG_ERR, "couldn't select on sockets"); - - return ~0; - }; - - break; - } - - /* See what came in */ - i = 0; - - va_start(ap, ctx); - - while((io = va_arg(ap, spio_t*)) != NULL) - { - /* We can't handle more than 31 args */ - if(i > (sizeof(int) * 8) - 2) - break; - - /* We have data on the descriptor, which is an action */ - io->last_action = time(NULL); - - /* Check if the buffer has something in it */ - if(FD_ISSET(io->fd, &mask)) - ret |= (1 << i); - - i++; - } - - return ret; -} - -int read_raw(spctx_t* ctx, spio_t* io, int opts) -{ - int len, x, count; - char* at; - char* p; - - /* - * Just a refresher: - * - * _nx: Extra data read on last read. - * _ln: Length of that extra data. - * - * _nx should never be equal to line when entering this function. - * And _ln should always be less than a full buffer. - */ - - count = 0; - io->line[0] = 0; - - /* Remaining data in the buffer */ - if(io->_nx && io->_ln > 0) - { - ASSERT(!io->_nx || io->_nx > io->line); - ASSERT(io->_ln < SP_LINE_LENGTH); - ASSERT(io->_nx + io->_ln <= io->line + SP_LINE_LENGTH); - - /* Check for a return in the current buffer */ - if((p = (char*)memchr(io->_nx, '\n', io->_ln)) != NULL) - { - /* Move data to front */ - x = (p - io->_nx) + 1; - ASSERT(x > 0); - memmove(io->line, io->_nx, x); - - /* Null teriminate it */ - io->line[x] = 0; - - /* Do maintanence for next time around */ - io->_ln -= x; - io->_nx += x; - - /* A double check on the return value */ - count += x; - return count; - } - - /* Otherwise move all old data to front */ - memmove(io->line, io->_nx, io->_ln); - count += io->_ln; - - /* We always leave space for a null terminator */ - len = (SP_LINE_LENGTH - io->_ln) - 1; - at = io->line + io->_ln; - } - - /* No data at front just read straight in */ - else - { - /* We always leave space for a null terminator */ - len = SP_LINE_LENGTH - 1; - at = io->line; - } - - for(;;) - { - /* Read a block of data */ - ASSERT(io->fd != -1); - x = read(io->fd, at, sizeof(char) * len); - - if(x == -1) - { - if(errno == EINTR) - { - /* When the application is quiting */ - if(sp_is_quit()) - return -1; - - /* For any other signal we go again */ - continue; - } - - if(errno == ECONNRESET) /* Not usually a big deal so supresse the error */ - sp_messagex(ctx, LOG_DEBUG, "%s: connection disconnected by peer", GET_IO_NAME(io)); - else if(errno == EAGAIN) - sp_messagex(ctx, LOG_WARNING, "%s: network read operation timed out", GET_IO_NAME(io)); - else - sp_message(ctx, LOG_ERR, "%s: couldn't read data from socket", GET_IO_NAME(io)); - - /* - * The basic logic here is that if we've had a fatal error - * reading from the socket once then we shut it down as it's - * no good trying to read from again later. - */ - close_raw(&(io->fd)); - - return -1; - } - - /* End of data */ - else if(x == 0) - { - /* Maintenance for remaining data */ - io->_nx = NULL; - io->_ln = 0; - - return count; - } - - /* Read data which is a descriptor action */ - io->last_action = time(NULL); - - /* Check for a new line */ - p = (char*)memchr(at, '\n', x); - if(p != NULL) - { - p++; - count += (p - at); - - /* Insert the null terminator */ - len = x - (p - at); - memmove(p + 1, p, len); - *p = 0; - - /* Do maintenence for remaining data */ - io->_nx = p + 1; - io->_ln = len; - - return count; - } - - /* Move the buffer pointer along */ - at += x; - len -= x; - count += x; - - if(len <= 0) - { - /* Keep reading until we hit a new line */ - if(opts & SPIO_DISCARD) - { - /* - * K, basically the logic is that we're discarding - * data ond the data will be screwed up. So overwriting - * some valid data in order to flush the line and - * keep the buffering simple is a price we pay gladly :) - */ - - ASSERT(128 < SP_LINE_LENGTH); - at = (io->line + SP_LINE_LENGTH) - 128; - len = 128; - - /* Go for next read */ - continue; - } - - io->_nx = NULL; - io->_ln = 0; - - /* Null terminate */ - io->line[SP_LINE_LENGTH] = 0; - - /* A double check on the return value */ - return count; - } - } -} - -int spio_read_line(spctx_t* ctx, spio_t* io, int opts) -{ - int x, l; - char* t; - - ASSERT(ctx && io); - - if(!spio_valid(io)) - { - sp_messagex(ctx, LOG_WARNING, "%s: tried to read from a closed connection", GET_IO_NAME(io)); - return 0; - } - - x = read_raw(ctx, io, opts); - - if(x > 0) - { - if(opts & SPIO_TRIM) - { - t = io->line; - - while(*t && isspace(*t)) - t++; - - /* Bump the entire line down */ - l = t - io->line; - memmove(io->line, t, (x + 1) - l); - x -= l; - - /* Now the end */ - t = io->line + x; - - while(t > io->line && isspace(*(t - 1))) - { - *(--t) = 0; - x--; - } - } - - if(!(opts & SPIO_QUIET)) - log_io_data(ctx, io, io->line, 1); - } - - return x; -} - -int spio_write_data(spctx_t* ctx, spio_t* io, const char* data) -{ - int len = strlen(data); - ASSERT(ctx && io && data); - - if(!spio_valid(io)) - { - sp_message(ctx, LOG_ERR, "%s: connection closed. can't write data", GET_IO_NAME(io)); - return -1; - } - - log_io_data(ctx, io, data, 0); - return spio_write_data_raw(ctx, io, (unsigned char*)data, len); -} - -int spio_write_dataf(struct spctx* ctx, spio_t* io, const char* fmt, ...) -{ - char buf[SP_LINE_LENGTH]; - va_list ap; - ASSERT(ctx && io && fmt); - - buf[0] = 0; - - va_start(ap, fmt); - vsnprintf(buf, SP_LINE_LENGTH, fmt, ap); - va_end(ap); - - buf[SP_LINE_LENGTH - 1] = 0; - - return spio_write_data(ctx, io, buf); -} - -int spio_write_data_raw(spctx_t* ctx, spio_t* io, unsigned char* buf, int len) -{ - int r; - - ASSERT(ctx && io && buf); - - if(io->fd == -1) - return 0; - - io->last_action = time(NULL); - - while(len > 0) - { - r = write(io->fd, buf, len); - - if(r > 0) - { - buf += r; - len -= r; - } - - else if(r == -1) - { - if(errno == EINTR) - { - /* When the application is quiting */ - if(sp_is_quit()) - return -1; - - /* For any other signal we go again */ - continue; - } - - /* - * The basic logic here is that if we've had a fatal error - * writing to the socket once then we shut it down as it's - * no good trying to write to it again later. - */ - close_raw(&(io->fd)); - - if(errno == EAGAIN) - sp_messagex(ctx, LOG_WARNING, "%s: network write operation timed out", GET_IO_NAME(io)); - else - sp_message(ctx, LOG_ERR, "%s: couldn't write data to socket", GET_IO_NAME(io)); - - return -1; - } - } - - return 0; -} - -void spio_read_junk(spctx_t* ctx, spio_t* io) -{ - char buf[16]; - const char* t; - int said = 0; - int l; - - ASSERT(ctx); - ASSERT(io); - - /* Truncate any data in buffer */ - io->_ln = 0; - io->_nx = 0; - - if(!spio_valid(io)) - return; - - /* Make it non blocking */ - fcntl(io->fd, F_SETFL, fcntl(io->fd, F_GETFL, 0) | O_NONBLOCK); - - for(;;) - { - l = read(io->fd, buf, sizeof(buf) - 1); - if(l <= 0) - break; - - io->last_action = time(NULL); - - buf[l] = 0; - t = trim_start(buf); - - if(!said && *t) - { - sp_messagex(ctx, LOG_DEBUG, "%s: received junk data from daemon", GET_IO_NAME(io)); - said = 1; - } - } - - fcntl(io->fd, F_SETFL, fcntl(io->fd, F_GETFL, 0) & ~O_NONBLOCK); -} diff --git a/common/sppriv.h b/common/sppriv.h deleted file mode 100644 index a690f3c..0000000 --- a/common/sppriv.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 __SPPRIV_H__ -#define __SPPRIV_H__ - -#include "smtppass.h" - -typedef struct spstate -{ - /* Settings ------------------------------- */ - int debug_level; /* The level to print stuff to console */ - int max_threads; /* Maximum number of threads to process at once */ - struct timeval timeout; /* Timeout for communication */ - int keepalives; /* Send server keep alives at this interval */ - int transparent; /* Transparent proxying */ - int xclient; /* Send XFORWARD info */ - const char* directory; /* The temp directory */ - const char* user; /* User to run as */ - const char* pidfile; /* The pid file for daemon */ - const char* header; /* A header to include in the email */ - int header_prepend; /* Prepend the header or not */ - - struct sockaddr_any outaddr; /* The outgoing address */ - const char* outname; - struct sockaddr_any listenaddr; /* Address to listen on */ - const char* listenname; - - /* State --------------------------------- */ - const char* name; /* The name of the program */ - int quit; /* Quit the process */ - int daemonized; /* Whether process is daemonized or not */ - - /* Internal Use ------------------------- */ - char* _p; -} -spstate_t; - -extern spstate_t g_state; - -#endif /* __SPPRIV_H__ */ - diff --git a/common/stringx.c b/common/stringx.c deleted file mode 100644 index f3c788e..0000000 --- a/common/stringx.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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 <ctype.h> -#include <syslog.h> -#include <stdlib.h> -#include <stdio.h> -#include <strings.h> - -#include "usuals.h" -#include "compat.h" -#include "stringx.h" - -/* ---------------------------------------------------------------------------------- - * Parsing - */ - -int is_first_word(const char* line, const char* word, int len) -{ - ASSERT(line); - ASSERT(word); - ASSERT(len > 0); - - while(*line && isspace(*line)) - line++; - - if(strncasecmp(line, word, len) != 0) - return 0; - - line += len; - return !*line || isspace(*line); -} - -int check_first_word(const char* line, const char* word, int len, char* delims) -{ - const char* t; - int found = 0; - - ASSERT(line); - ASSERT(word); - ASSERT(len > 0); - - t = line; - - while(*t && strchr(delims, *t)) - t++; - - if(strncasecmp(t, word, len) != 0) - return 0; - - t += len; - - while(*t && strchr(delims, *t)) - { - found = 1; - t++; - } - - return (!*t || found) ? t - line : 0; -} - -int is_last_word(const char* line, const char* word, int len) -{ - const char* t; - - ASSERT(line); - ASSERT(word); - ASSERT(len > 0); - - t = line + strlen(line); - - while(t > line && isspace(*(t - 1))) - --t; - - if(t - len < line) - return 0; - - return strncasecmp(t - len, word, len) == 0; -} - -int is_blank_line(const char* line) -{ - /* Small optimization */ - if(!*line) - return 1; - - while(*line && isspace(*line)) - line++; - - return *line == 0; -} - -char* trim_start(const char* data) -{ - while(*data && isspace(*data)) - ++data; - return (char*)data; -} - -char* trim_end(char* data) -{ - char* t = data + strlen(data); - - while(t > data && isspace(*(t - 1))) - { - t--; - *t = 0; - } - - return data; -} - -char* trim_space(char* data) -{ - data = (char*)trim_start(data); - return trim_end(data); -} - -/* String to bool helper function */ -int strtob(const char* str) -{ - if(strcasecmp(str, "0") == 0 || - strcasecmp(str, "no") == 0 || - strcasecmp(str, "false") == 0 || - strcasecmp(str, "f") == 0 || - strcasecmp(str, "off") == 0) - return 0; - - if(strcasecmp(str, "1") == 0 || - strcasecmp(str, "yes") == 0 || - strcasecmp(str, "true") == 0 || - strcasecmp(str, "t") == 0 || - strcasecmp(str, "on") == 0) - return 1; - - return -1; -} diff --git a/common/stringx.h b/common/stringx.h deleted file mode 100644 index deb8f4b..0000000 --- a/common/stringx.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 __STRINGX_H__ -#define __STRINGX_H__ - -int check_first_word(const char* line, const char* word, int len, char* delims); -int is_first_word(const char* line, const char* word, int len); -int is_last_word(const char* line, const char* word, int len); -int is_blank_line(const char* line); - -char* trim_start(const char* data); -char* trim_end(char* data); -char* trim_space(char* data); - -int strtob(const char* str); - -#endif /* __STRINGX_H__ */ diff --git a/common/usuals.h b/common/usuals.h deleted file mode 100644 index 48d372f..0000000 --- a/common/usuals.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 __USUALS_H__ -#define __USUALS_H__ - -#include <sys/types.h> - -#include "config.h" - -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> - -#include "compat.h" - -#ifndef NULL -#define NULL 0 -#endif - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -#define countof(x) (sizeof(x) / sizeof(x[0])) - -#ifdef _DEBUG - #include "assert.h" - #define ASSERT assert -#else - #define ASSERT -#endif - -#define KL(s) ((sizeof(s) - 1) / sizeof(char)) -#define RETURN(x) { ret = (x); goto cleanup; } - - -#endif /* __USUALS_H__ */ diff --git a/configure.in b/configure.in deleted file mode 100644 index 8a6707d..0000000 --- a/configure.in +++ /dev/null @@ -1,109 +0,0 @@ -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, 1.2.1, nielsen@memberwebs.com) -AM_INIT_AUTOMAKE(proxsmtp, 1.2.1) - -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 usleep], , - [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/doc/.cvsignore b/doc/.cvsignore deleted file mode 100644 index 9ad01f5..0000000 --- a/doc/.cvsignore +++ /dev/null @@ -1,3 +0,0 @@ -Makefile -Makefile.in -test.conf diff --git a/doc/Makefile.am b/doc/Makefile.am deleted file mode 100644 index 9bc2bb3..0000000 --- a/doc/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ - -man_MANS = proxsmtpd.8 proxsmtpd.conf.5 -EXTRA_DIST = $(man_MANS) proxsmtpd.conf diff --git a/doc/proxsmtpd.8 b/doc/proxsmtpd.8 deleted file mode 100644 index 9f9f9d0..0000000 --- a/doc/proxsmtpd.8 +++ /dev/null @@ -1,213 +0,0 @@ -.\" -.\" 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> -.\" -.Dd September, 2004 -.Dt proxsmtpd 8 -.Os proxsmtp -.Sh NAME -.Nm proxsmtpd -.Nd an SMTP server for performing filtering -.Sh SYNOPSIS -.Nm -.Op Fl d Ar level -.Op Fl f Ar configfile -.Op Fl p Ar pidfile -.Nm -.Fl v -.Sh DESCRIPTION -.Nm -is an SMTP filter that allows you to perform arbitrary filtering on email. It -accepts SMTP connections and forwards the SMTP commands and responses to another -SMTP server. -.Pp -The DATA email body is intercepted and scanned before forwarding. Email can be -altered, bounced, or silently dropped. -.Pp -.Nm -aims to be lightweight and simple rather than have a myriad of options. The options -it does have are configured by editing the -.Xr proxsmtpd.conf 5 -file. See the man page for -.Xr proxsmtpd.conf 5 -for more info on the default location of the configuration file. -.Sh OPTIONS -The options are as follows. -.Bl -tag -width Fl -.It Fl d -Don't detach from the console and run as a daemon. In addition the -.Ar level -argument specifies what level of error messages to display. 0 being -the least, 4 the most. -.It Fl f -.Ar configfile -specifies an alternate location for the -.Nm -configuration file. See -.Xr proxsmtpd.conf 5 -for more details on where the configuration file is located by default. -.It Fl p -.Ar pidfile -specifies a location for the a process id file to be written to. This file -contains the process id of -.Nm -and can be used to stop the daemon. -.It Fl v -Prints the proxsmtp version number and exits. -.El -.Sh FILTER SCRIPTS -The filter script is specified using the -.Ar FilterCommand -option. By default the email is piped through the script on standard input. -Standard output is read for the filtered email. Standard error is also read -for error messages. -.Pp -If the -.Ar FilterType -option is set to 'file', your filter will operate on a file rather than processing -standard in and standard out. The file name will be passed to your filter -command using the -.Ar EMAIL -environment variable. Your script can change the file as needed. Standard error -is still processed as outlined below. -.Pp -If the filter command returns a successful exit code (ie: 0), then the filtered -email is sent to the destination mail server as usual. When a error exit code -(ie: anything but 0) a failure message is sent back to the sending server. In -this case the email is not sent. -.Pp -You can customize the error message sent back. The last line of output printed -to standard error will be used in this case. If you specify a full SMTP error -code then it will be used (ie: '550 Bad Email'). If it's just a text message -then a 550 SMTP error code will be used. -.Pp -You can silently drop messages by using an error message with a 250 SMTP code. -This gives the illusion to the sending server that the email was accepted. -.Pp -Various environment variables will be present when your script is run. You -may need to escape them properly before use in your favorite scripting -language. Failure to do this could lead to a REMOTE COMPROMISE of your -machine. -.Bl -tag -width Fl -.It Ar CLIENT -The network address of the SMTP client connected. -.It Ar EMAIL -When the -.Ar FilterType -option is set to 'file', this specifies the file that the email was saved to. -.It Ar RECIPIENTS -The email addresses of the email recipients. These are specified one per -line, in standard address format. -.It Ar SENDER -The email address for the sender of the email. -.It Ar SERVER -The network address of the SMTP server we're connected to. -.It Ar TMPDIR -The path to the temp directory in use. This is the same as the -.Ar TempDirectory -option. -.El -.Sh LOGGING -.Nm -logs to -.Xr syslogd -by default under the 'mail' facility. You can also output logs to the console -using the -.Fl d -option. -.Sh LOOPBACK FEATURE -In some cases it's advantageous to consolidate the filtering for several mail -servers on one machine. -.Nm -allows this by providing a loopback feature to connect back to the IP that an -SMTP connection comes in from. -.Pp -To use this feature specify only a port number (no IP address) for the -.Ar OutAddress -setting in the configuration file. This will cause -.Nm -to pass the email back to the said port on the incoming IP address. -.Pp -Make sure the -.Ar MaxConnections -setting is set high enough to handle the mail from all the servers without refusing -connections. -.Sh TRANSPARENT PROXY FEATURE -A transparent proxy is a configuration on a gateway that routes certain types of -traffic through a proxy server without any changes on the client computers. -.Nm -has support for transparent proxying of SMTP traffic by enabling the -.Ar TransparentProxy -setting. This type of setup usually involves firewall rules which redirect traffic to -.Nm -and the setup varies from OS to OS. The SMTP traffic will be forwarded to it's -original destination after being scanned. -.Pp -Note that some features (such as SSL/TLS) will not be available -when going through the transparent proxy. -.Pp -Make sure that the -.Ar MaxConnections -setting is set high enough for your transparent proxying. Because -.Nm -is not being used as a filter inside a queue, which usually throttles the amount -of email going through, this setting may need to be higher than usual. -.Sh SECURITY -There's no reason to run this daemon as root. It is meant as a filter and should -listen on a high TCP port. -.Pp -Care should be taken with the directory that -.Nm -writes its temporary files to. In order to be secure, it should not be a world -writeable location. Specify the directory using the -.Ar TempDirectory -setting. -.Pp -Make sure you understand the issues involved with escaping external data. The -environment variables such as -.Ar SENDER -or -.Ar RECIPIENTS -need to be treated with care. -.Pp -If running -.Nm -on a publicly accessible IP address or without a firewall please be sure to -understand all the possible security issues. This is especially true if the -loopback feature is used (see above). -.Sh SEE ALSO -.Xr proxsmtpd.conf 5 -.Sh AUTHOR -.An Nate Nielsen Aq nielsen@memberwebs.com diff --git a/doc/proxsmtpd.conf b/doc/proxsmtpd.conf deleted file mode 100644 index 2b87e84..0000000 --- a/doc/proxsmtpd.conf +++ /dev/null @@ -1,52 +0,0 @@ -# ------------------------------------------------------------------------------ -# SAMPLE PROXSMTPD CONFIG FILE -# ------------------------------------------------------------------------------ -# -# - Comments are a line that starts with a # -# - All the options are found below with sample settings - - -# The address to send scanned mail to. -# This option is required unless TransparentProxy is enabled -OutAddress: 10026 - -# The Filter Command run for each email. See 'man proxsmtpd' for details -# The following command is a simple which just creates temp files. -#FilterCommand: tee `mktemp -t sample-filter.XXXXXX` - -# The amount of time to wait for data from FilterCommand -#FilterTimeout: 10 - -# The type of filter ('pipe' to pipe data through filter, -# or 'file' to pass a file to the filter) -#FilterType: pipe - -# The maximum number of connection allowed at once. -# Be sure that clamd can also handle this many connections -#MaxConnections: 64 - -# Amount of time (in seconds) to wait on network IO -#TimeOut: 180 - -# A header to add to all scanned email -#Header: X-Filtered: By ProxSMTP - -# Keep Alives (ie: NOOP's to server) -#KeepAlives: 0 - -# Send XCLIENT commands to receiving server -#XClient: off - -# Address to listen on (defaults to all local addresses on port 10025) -#Listen: 0.0.0.0:10025 - -# Directory for temporary files -#TempDirectory: /tmp - -# Enable transparent proxy support -#TransparentProxy: off - -# User to switch to -#User: nobody - - diff --git a/doc/proxsmtpd.conf.5 b/doc/proxsmtpd.conf.5 deleted file mode 100644 index 916bc7b..0000000 --- a/doc/proxsmtpd.conf.5 +++ /dev/null @@ -1,177 +0,0 @@ -.\" -.\" 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> -.\" -.Dd September, 2004 -.Dt proxsmtpd.conf 5 -.Os proxsmtp -.Sh NAME -.Nm proxsmtpd.conf -.Nd the configuration file for -.Xr proxsmtpd 8 -.Sh DESCRIPTION -.Xr proxsmtpd 8 -reads a configuration file when starting up. The location of the file is dependent -on how you compiled proxsmtp but it should usually be in either the -.Pa /usr/local/etc/ -or -.Pa /etc/ -directories. If -.Xr proxsmtpd 8 -does not find its configuration file it'll print a warning when it starts up along -with the location it's expecting to find it in. You can also specify a different -location for a config file by passing the -.Fl f -argument to -.Xr proxsmtpd 8 -.Pp -The settings are specified one per line. The setting names come first, followed -by a colon and then the value. Comments start with the '#' character on a line -of their own. Whitespace is ignored at the beginning of line, end of line and -around the colons. -.Pp -A sample configuration file can be found in the -.Pa doc/ -directory of the proxsmtp distribution. -.Sh SETTINGS -The various settings are as follows: -.Bl -tag -width Fl -.It Ar FilterCommand -This is the command used to filter email through. If not specified then no -filtering will be done. Specify all the arguments the command needs as you -would on a command-line. -.Pp -[ Default: no filtering ] -.It Ar FilterTimeout -The amount of time in seconds to wait for the -.Ar FilterCommand -to process email data. -.Pp -[ Default: 30 seconds ] -.It Ar FilterType -When set to 'pipe' the email data is piped through the -.Ar FilterCommand -using standard in and standard out. When set to 'file' the email data is saved -to a file and the file name is passed to the -.Ar FilterCommand -using the -.Ar EMAIL -environment variable. -.Pp -[ Default: pipe ] -.It Ar Header -A header to add to scanned messages. Put an empty value to suppress adding -a header. You can include the following special formatting characters in the -string to include special values: -.Bl -inset -.It Ar %i -Client IP Address -.It Ar %l -Local IP Address -.It Ar %d -Current Date -.El -.Pp -You can also include the standard \\r or \\n escapes. -.Pp -[ Optional ] -.It Ar KeepAlives -On slow connections the server will sometimes timeout before -.Xr proxsmtpd 8 -is finished filtering the file. This option sends NOOP's to the server -to keep the connection alive. Specify the number of seconds, or 0 -to disable. -.Pp -[ Default: 0 ] -.It Ar Listen -The address and port to listen for SMTP connections on. See syntax of -addresses below. -.Pp -[ Default: port 10025 on all local IP addresses ] -.It Ar MaxConnections -Specifies the maximum number of connections to accept at once. -.Pp -[ Default: 64 ] -.It Ar OutAddress -The address of the SMTP server to send email to once it's been scanned. See -syntax of addreses below. -.Pp -[ Required ] -.It Ar TempDirectory -The directory to write temp files to. -.Pp -[ Default: -.Pa /tmp -] -.It Ar TimeOut -The number of seconds to wait while reading data from network connections. -.Pp -[ Default: 180 seconds ] -.It Ar TransparentProxy -This option enables transparent proxy support, which allows you to route all -SMTP traffic that's going through a gateway through proxsmtp which will then -send it on to its final destination. This setup usually involves firewall -rules which redirect traffic to proxsmtp, and the setup varies from OS to OS. -.Pp -[ Default: off ] -.It Ar User -The user to run as. If this option is specified then -.Xr proxsmtpd 8 -must be started as root. It will then drop root privileges and run as the -specified user. The user can either be a name or a numerical user id. -.Pp -[ Optional ] -.It Ar XClient -Send an XCLIENT command to the receiving server. This is useful for forwarding -client addresses and connection info to servers that support this feature. -.Pp -[ Default: off ] -.El -.Sh ADDRESSES -Addresses can be specified in multiple formats: -.Bl -bullet -.It -Unix local addresses can be specified by specifying their full path. -(ie: '/var/run/socket'). -.It -IP addresses can be specified using dotted notation with a colon before -the port number (ie: '127.0.0.1:3310'). -.It -IPv6 addresses are implemented but disabled. The code needs testing. -.El -.Sh SEE ALSO -.Xr proxsmtpd 8 -.Sh AUTHOR -.An Nate Nielsen Aq nielsen@memberwebs.com diff --git a/scripts/add_header.sh b/scripts/add_header.sh deleted file mode 100644 index d4d524a..0000000 --- a/scripts/add_header.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh - -################################################################################ -# PROXSMTP SAMPLE SCRIPT -# -# These sample scripts are to give you an idea of how to use proxsmtp -# filtering. They are NOT intended for use on production servers. -# -# A simple proxsmtp script which replaces the subject line with one -# containing the senders email address. Uses the 'formail' command -# that comes with the 'procmail' package. -# -# Make sure the option 'FilterType' is set as follows: -# FilterType: pipe -# -# See proxsmtpd.conf(5) for configuration details -# - -# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -# WARNING WARNING WARNING WARNING WARNING WARNING WARNING -# -# By using variables passed in from clamsmtpd in file -# manipulation commands without escaping their contents -# you are opening yourself up to REMOTE COMPROMISE. You -# have been warned. Do NOT do the following unless you -# want to be screwed big time: -# -# mv $EMAIL "$SENDER.eml" -# -# An attacker can use the above command to compromise your -# computer. The only variable that is guaranteed safe in -# this regard is $EMAIL. -# -# The following script does not escape its variables -# because it only uses them in safe ways. -# -# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -# Pipe the email through this command -formail -i "Subject: Changed subject from $SENDER ..." - -# Filter success -exit 0
\ No newline at end of file diff --git a/scripts/proxsmtpd.sh b/scripts/proxsmtpd.sh deleted file mode 100644 index fcc3493..0000000 --- a/scripts/proxsmtpd.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -########################################################################### -# CONFIGURATION - -# Most configuration options are found in the proxsmtpd.conf file. -# For more info see: -# man proxsmtpd.conf - -# The prefix proxsmtpd was installed to -prefix=/usr/local/ - -# The location for pid file -piddir=/var/run/ - -########################################################################### -# SCRIPT - -case $1 in -start) - mkdir -p $piddir - $prefix/sbin/proxsmtpd -p $piddir/proxsmtpd.pid - echo -n "proxsmtpd " - ;; -stop) - [ -f $piddir/proxsmtpd.pid ] && kill `cat $piddir/proxsmtpd.pid` - echo -n "proxsmtpd " - ;; -*) - echo "usage: proxsmptd.sh {start|stop}" >&2 - ;; -esac diff --git a/scripts/spamassassin.sh b/scripts/spamassassin.sh deleted file mode 100644 index 9e88f75..0000000 --- a/scripts/spamassassin.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh - -################################################################################ -# PROXSMTP SAMPLE SCRIPT -# -# These sample scripts are to give you an idea of how to use proxsmtp -# filtering. They are NOT intended for use on production servers. -# -# A simple proxsmtp script which sends email through spamassassin. -# -# Make sure the option 'FilterType' is set as follows: -# FilterType: pipe -# -# See proxsmtpd.conf(5) for configuration details -# - -# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -# WARNING WARNING WARNING WARNING WARNING WARNING WARNING -# -# By using variables passed in from clamsmtpd in file -# manipulation commands without escaping their contents -# you are opening yourself up to REMOTE COMPROMISE. You -# have been warned. Do NOT do the following unless you -# want to be screwed big time: -# -# mv $EMAIL "$SENDER.eml" -# -# An attacker can use the above command to compromise your -# computer. The only variable that is guaranteed safe in -# this regard is $EMAIL. -# -# The following script does not escape its variables -# because it only uses them in safe ways. -# -# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -# Pipe mail through this command -spamassassin -e - -# Now check return value -if [ $? -ne 0 ]; then - - # The last line of output to stderr will be used - # as an error message when the filter fails - echo "550 Content Rejected: We don't like spam" >&2 - - # Cause the filter to fail, email will be rejected - exit 1 -fi - -# Filter success -exit 0 diff --git a/src/.cvsignore b/src/.cvsignore deleted file mode 100644 index 0564b29..0000000 --- a/src/.cvsignore +++ /dev/null @@ -1,4 +0,0 @@ -Makefile -Makefile.in -proxsmtpd -.deps diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index 208ae67..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ - -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 deleted file mode 100644 index fbd5d25..0000000 --- a/src/proxsmtpd.c +++ /dev/null @@ -1,904 +0,0 @@ -/* - * 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 <sys/wait.h> - -#include <paths.h> -#include <ctype.h> -#include <stdio.h> -#include <unistd.h> -#include <syslog.h> -#include <errno.h> -#include <fcntl.h> -#include <err.h> -#include <signal.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 */ - struct timeval timeout; /* The command timeout */ - int pipe_cmd; /* Whether command is a pipe or not */ - const char* directory; /* The directory for temp files */ -} -pxstate_t; - -/* ----------------------------------------------------------------------- - * STRINGS - */ - -#define REJECTED "Content Rejected" - -#define DEFAULT_CONFIG CONF_PREFIX "/proxsmtpd.conf" -#define DEFAULT_TIMEOUT 30 - -#define CFG_FILTERCMD "FilterCommand" -#define CFG_FILTERTYPE "FilterType" -#define CFG_DIRECTORY "TempDirectory" -#define CFG_DEBUGFILES "DebugFiles" -#define CFG_CMDTIMEOUT "FilterTimeout" - -#define TYPE_PIPE "pipe" -#define TYPE_FILE "file" - -/* Poll time for waiting operations in milli seconds */ -#define POLL_TIME 20 - -/* read & write ends of a pipe */ -#define READ_END 0 -#define WRITE_END 1 - -/* pre-set file descriptors */ -#define STDIN 0 -#define STDOUT 1 -#define STDERR 2 - -/* ----------------------------------------------------------------------- - * GLOBALS - */ - -pxstate_t g_pxstate; - -/* ----------------------------------------------------------------------- - * FORWARD DECLARATIONS - */ - -static void usage(); -static int process_file_command(spctx_t* sp); -static int process_pipe_command(spctx_t* sp); -static void final_reject_message(char* buf, int buflen); -static void buffer_reject_message(char* data, char* buf, int buflen); -static int kill_process(spctx_t* sp, pid_t pid); -static int wait_process(spctx_t* sp, pid_t pid, int* status); - -/* ---------------------------------------------------------------------------------- - * STARTUP ETC... - */ - -int main(int argc, char* argv[]) -{ - const char* configfile = DEFAULT_CONFIG; - const char* pidfile = NULL; - int dbg_level = -1; - int ch = 0; - int r; - char* t; - - /* Setup some defaults */ - memset(&g_pxstate, 0, sizeof(g_pxstate)); - g_pxstate.directory = _PATH_TMP; - g_pxstate.pipe_cmd = 1; - g_pxstate.timeout.tv_sec = DEFAULT_TIMEOUT; - - sp_init("proxsmtpd"); - - /* - * 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, "d:f:p:v")) != -1) - { - switch(ch) - { - /* 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; - - /* The configuration file */ - case 'f': - configfile = optarg; - break; - - /* Write out a pid file */ - case 'p': - pidfile = optarg; - break; - - /* Print version number */ - case 'v': - printf("clamsmtpd (version %s)\n", VERSION); - printf(" (config: %s)\n", DEFAULT_CONFIG); - exit(0); - break; - - /* Usage information */ - case '?': - default: - usage(); - break; - } - } - - argc -= optind; - argv += optind; - - if(argc > 0) - usage(); - - r = sp_run(configfile, pidfile, dbg_level); - - sp_done(); - - return r; -} - -static void usage() -{ - fprintf(stderr, "usage: proxsmtpd [-d debuglevel] [-f configfile] [-p pidfile]\n"); - fprintf(stderr, " proxsmtpd -v\n"); - exit(2); -} - -/* ---------------------------------------------------------------------------------- - * SP CALLBACKS - */ - -int cb_check_data(spctx_t* ctx) -{ - int r = 0; - - if(!g_pxstate.command) - { - sp_messagex(ctx, LOG_WARNING, "no filter command specified. passing message through"); - - if(sp_cache_data(ctx) == -1 || - sp_done_data(ctx) == -1) - return -1; /* Message already printed */ - - return 0; - } - - /* Cleanup any old filters hanging around */ - while(waitpid(-1, &r, WNOHANG) > 0) - ; - - if(g_pxstate.pipe_cmd) - r = process_pipe_command(ctx); - else - r = process_file_command(ctx); - - if(r == -1) - { - if(sp_fail_data(ctx, NULL) == -1) - return -1; - } - - return 0; -} - -int cb_parse_option(const char* name, const char* value) -{ - char* t; - - if(strcasecmp(CFG_FILTERCMD, name) == 0) - { - g_pxstate.command = value; - return 1; - } - - else if(strcasecmp(CFG_DIRECTORY, name) == 0) - { - g_pxstate.directory = value; - return 1; - } - - else if(strcasecmp(CFG_CMDTIMEOUT, name) == 0) - { - g_pxstate.timeout.tv_sec = strtol(value, &t, 10); - if(*t || g_pxstate.timeout.tv_sec <= 0) - errx(2, "invalid setting: " CFG_CMDTIMEOUT); - return 1; - } - - else if(strcasecmp(CFG_FILTERTYPE, name) == 0) - { - if(strcasecmp(value, TYPE_PIPE) == 0) - g_pxstate.pipe_cmd = 1; - else if(strcasecmp(value, TYPE_FILE) == 0) - g_pxstate.pipe_cmd = 0; - else - errx(2, "invalid value for " CFG_FILTERTYPE " (must specify 'pipe' or 'file')"); - return 1; - } - - return 0; -} - -spctx_t* cb_new_context() -{ - spctx_t* ctx = (spctx_t*)calloc(1, sizeof(spctx_t)); - if(!ctx) - sp_messagex(NULL, LOG_CRIT, "out of memory"); - return ctx; -} - -void cb_del_context(spctx_t* ctx) -{ - free(ctx); -} - -/* ----------------------------------------------------------------------------- - * IMPLEMENTATION - */ - -static pid_t fork_filter(spctx_t* sp, int* infd, int* outfd, int* errfd) -{ - pid_t pid; - int ret = 0; - int r = 0; - - /* Pipes for input, output, err */ - int pipe_i[2]; - int pipe_o[2]; - int pipe_e[2]; - - memset(pipe_i, ~0, sizeof(pipe_i)); - memset(pipe_o, ~0, sizeof(pipe_o)); - memset(pipe_e, ~0, sizeof(pipe_e)); - - ASSERT(g_pxstate.command); - - /* Create the pipes we need */ - if((infd && pipe(pipe_i) == -1) || - (outfd && pipe(pipe_o) == -1) || - (errfd && pipe(pipe_e) == -1)) - { - sp_message(sp, LOG_ERR, "couldn't create pipe for filter command"); - RETURN(-1); - } - - /* Now fork the pipes across processes */ - switch(pid = fork()) - { - case -1: - sp_message(sp, LOG_ERR, "couldn't fork for filter command"); - RETURN(-1); - - /* The child process */ - case 0: - - if(r >= 0 && infd) - { - close(pipe_i[WRITE_END]); - r = dup2(pipe_i[READ_END], STDIN); - } - - if(r >= 0 && outfd) - { - close(pipe_o[READ_END]); - r = dup2(pipe_o[WRITE_END], STDOUT); - } - - if(r >= 0 && errfd) - { - close(pipe_e[READ_END]); - r = dup2(pipe_e[WRITE_END], STDERR); - } - - if(r < 0) - { - sp_message(sp, LOG_ERR, "couldn't dup descriptors for filter command"); - _exit(1); - } - - /* All the necessary environment vars */ - sp_setup_forked(sp, 1); - - /* Now run the filter command */ - execl("/bin/sh", "sh", "-c", g_pxstate.command, NULL); - - /* If that returned then there was an error */ - sp_message(sp, LOG_ERR, "error executing the shell for filter command"); - _exit(1); - break; - }; - - /* The parent process */ - sp_messagex(sp, LOG_DEBUG, "executed filter command: %s (pid: %d)", g_pxstate.command, (int)pid); - - /* Setup all our return values */ - if(infd) - { - *infd = pipe_i[WRITE_END]; - pipe_i[WRITE_END] = -1; - fcntl(*infd, F_SETFL, fcntl(*infd, F_GETFL, 0) | O_NONBLOCK); - } - - if(outfd) - { - *outfd = pipe_o[READ_END]; - pipe_o[READ_END] = -1; - fcntl(*outfd, F_SETFL, fcntl(*outfd, F_GETFL, 0) | O_NONBLOCK); - } - - if(errfd) - { - *errfd = pipe_e[READ_END]; - pipe_e[READ_END] = -1; - fcntl(*errfd, F_SETFL, fcntl(*errfd, F_GETFL, 0) | O_NONBLOCK); - } - -cleanup: - if(pipe_i[READ_END] != -1) - close(pipe_i[READ_END]); - if(pipe_i[WRITE_END] != -1) - close(pipe_i[WRITE_END]); - if(pipe_o[READ_END] != -1) - close(pipe_o[READ_END]); - if(pipe_o[WRITE_END] != -1) - close(pipe_o[WRITE_END]); - if(pipe_e[READ_END] != -1) - close(pipe_e[READ_END]); - if(pipe_e[WRITE_END] != -1) - close(pipe_e[WRITE_END]); - - return ret >= 0 ? pid : (pid_t)-1; -} - -static int process_file_command(spctx_t* sp) -{ - pid_t pid; - int ret = 0, status, r; - struct timeval timeout; - - /* For reading data from the process */ - int errfd; - fd_set rmask; - char obuf[1024]; - char ebuf[256]; - - memset(ebuf, 0, sizeof(ebuf)); - - if(sp_cache_data(sp) == -1) - RETURN(-1); /* message already printed */ - - pid = fork_filter(sp, NULL, NULL, &errfd); - if(pid == (pid_t)-1) - RETURN(-1); - - /* Main read write loop */ - while(errfd != -1) - { - FD_ZERO(&rmask); - FD_SET(errfd, &rmask); - - /* Select can modify the timeout argument so we copy */ - memcpy(&timeout, &(g_pxstate.timeout), sizeof(timeout)); - - r = select(FD_SETSIZE, &rmask, NULL, NULL, &timeout); - - switch(r) - { - case -1: - sp_message(sp, LOG_ERR, "couldn't select while listening to filter command"); - RETURN(-1); - case 0: - sp_messagex(sp, LOG_ERR, "timeout while listening to filter command"); - RETURN(-1); - }; - - ASSERT(FD_ISSET(errfd, &rmask)); - - /* Note because we handle as string we save one byte for null-termination */ - r = read(errfd, obuf, sizeof(obuf) - 1); - if(r < 0) - { - if(errno != EINTR && errno != EAGAIN) - { - sp_message(sp, LOG_ERR, "couldn't read data from filter command"); - RETURN(-1); - } - - continue; - } - - if(r == 0) - { - close(errfd); - errfd = -1; - break; - } - - /* Null terminate */ - obuf[r] = 0; - - /* And process */ - buffer_reject_message(obuf, ebuf, sizeof(ebuf)); - - if(sp_is_quit()) - RETURN(-1); - } - - /* exit the process if not completed */ - if(wait_process(sp, pid, &status) == -1) - { - sp_messagex(sp, LOG_ERR, "timeout waiting for filter command to exit"); - RETURN(-1); - } - - pid = 0; - - /* We only trust well behaved programs */ - if(!WIFEXITED(status)) - { - sp_messagex(sp, LOG_ERR, "filter command terminated abnormally"); - RETURN(-1); - } - - sp_messagex(sp, LOG_DEBUG, "filter exit code: %d", (int)WEXITSTATUS(status)); - - /* A successful response */ - if(WEXITSTATUS(status) == 0) - { - if(sp_done_data(sp) == -1) - RETURN(-1); /* message already printed */ - - sp_add_log(sp, "status=", "FILTERED"); - } - - /* Check code and use stderr if bad code */ - else - { - final_reject_message(ebuf, sizeof(ebuf)); - - if(sp_fail_data(sp, ebuf) == -1) - RETURN(-1); /* message already printed */ - - sp_add_log(sp, "status=", ebuf); - } - - ret = 0; - -cleanup: - - if(pid != 0) - { - sp_messagex(sp, LOG_WARNING, "killing filter process (pid %d)", (int)pid); - kill_process(sp, pid); - } - - if(errfd != -1) - close(errfd); - - if(ret < 0) - sp_add_log(sp, "status=", "FILTER-ERROR"); - - return ret; -} - -static int process_pipe_command(spctx_t* sp) -{ - pid_t pid; - int ret = 0, status, r; - struct timeval timeout; - - /* For sending data to the process */ - const char* ibuf = NULL; - int ilen = 0; - int infd; - int icount = 0; - fd_set wmask; - - /* For reading data from the process */ - int outfd; - int errfd; - fd_set rmask; - char obuf[1024]; - char ebuf[256]; - int ocount = 0; - - ASSERT(g_pxstate.command); - - memset(ebuf, 0, sizeof(ebuf)); - - pid = fork_filter(sp, &infd, &outfd, &errfd); - if(pid == (pid_t)-1) - RETURN(-1); - - /* Opens cache file */ - if(sp_write_data(sp, obuf, 0) == -1) - RETURN(-1); /* message already printed */ - - /* Main read write loop */ - while(infd != -1 || outfd != -1 || errfd != -1) - { - FD_ZERO(&rmask); - FD_ZERO(&wmask); - - /* We only select on those that are still open */ - if(infd != -1) - FD_SET(infd, &wmask); - - if(outfd != -1) - FD_SET(outfd, &rmask); - - if(errfd != -1) - FD_SET(errfd, &rmask); - - /* Select can modify the timeout argument so we copy */ - memcpy(&timeout, &(g_pxstate.timeout), sizeof(timeout)); - - r = select(FD_SETSIZE, &rmask, &wmask, NULL, &timeout); - switch(r) - { - case -1: - sp_message(sp, LOG_ERR, "couldn't select while listening to filter command"); - RETURN(-1); - case 0: - sp_messagex(sp, LOG_WARNING, "timeout while listening to filter command"); - RETURN(-1); - }; - - /* Handling of process's stdin */ - if(infd != -1 && FD_ISSET(infd, &wmask)) - { - if(ilen <= 0) - { - /* Read some more data into buffer */ - switch(r = sp_read_data(sp, &ibuf)) - { - case -1: - RETURN(-1); /* Message already printed */ - case 0: - close(infd); /* Done with the input */ - infd = -1; - break; - default: - ASSERT(r > 0); - ilen = r; - break; - }; - } - - if(ilen > 0) - { - /* Write data from buffer */ - r = write(infd, ibuf, ilen); - if(r == -1) - { - if(errno == EPIPE) - { - sp_messagex(sp, LOG_WARNING, "filter command closed input early"); - - /* Eat up the rest of the data */ - while(sp_read_data(sp, &ibuf) > 0) - ; - - close(infd); - infd = -1; - break; - } - else if(errno != EAGAIN && errno != EINTR) - { - /* Otherwise it's a normal error */ - sp_message(sp, LOG_ERR, "couldn't write to filter command"); - RETURN(-1); - } - } - - /* A good normal write */ - else - { - icount += r; - ilen -= r; - ibuf += r; - } - } - } - - /* Handling of stdout, which should be email data */ - if(outfd != -1 && FD_ISSET(outfd, &rmask)) - { - r = read(outfd, obuf, sizeof(obuf)); - if(r > 0) - { - if(sp_write_data(sp, obuf, r) == -1) - RETURN(-1); /* message already printed */ - - ocount += r; - } - - else if(r == 0) - { - close(outfd); - outfd = -1; - } - - else if(r < 0) - { - if(errno != EINTR && errno != EAGAIN) - { - sp_message(sp, LOG_ERR, "couldn't read data from filter command"); - RETURN(-1); - } - } - } - - /* Handling of stderr, the last line of which we use as an err message*/ - if(errfd != -1 && FD_ISSET(errfd, &rmask)) - { - /* Note because we handle as string we save one byte for null-termination */ - r = read(errfd, obuf, sizeof(obuf) - 1); - if(r < 0) - { - if(errno != EINTR && errno != EAGAIN) - { - sp_message(sp, LOG_ERR, "couldn't read data from filter command"); - RETURN(-1); - } - } - - else if(r == 0) - { - close(errfd); - errfd = -1; - } - - else if(r > 0) - { - /* Null terminate */ - obuf[r] = 0; - - /* And process */ - buffer_reject_message(obuf, ebuf, sizeof(ebuf)); - } - } - - if(sp_is_quit()) - RETURN(-1); - } - - sp_messagex(sp, LOG_DEBUG, "wrote %d bytes to filter, read %d bytes", icount, ocount); - - /* Close the cache file */ - if(sp_write_data(sp, NULL, 0) == -1) - RETURN(-1); /* message already printed */ - - if(wait_process(sp, pid, &status) == -1) - { - sp_messagex(sp, LOG_ERR, "timeout waiting for filter command to exit"); - RETURN(-1); - } - - pid = 0; - - /* We only trust well behaved programs */ - if(!WIFEXITED(status)) - { - sp_messagex(sp, LOG_ERR, "filter command terminated abnormally"); - RETURN(-1); - } - - sp_messagex(sp, LOG_DEBUG, "filter exit code: %d", (int)WEXITSTATUS(status)); - - /* A successful response */ - if(WEXITSTATUS(status) == 0) - { - if(sp_done_data(sp) == -1) - RETURN(-1); /* message already printed */ - - sp_add_log(sp, "status=", "FILTERED"); - } - - /* Check code and use stderr if bad code */ - else - { - final_reject_message(ebuf, sizeof(ebuf)); - - if(sp_fail_data(sp, ebuf) == -1) - RETURN(-1); /* message already printed */ - - sp_add_log(sp, "status=", ebuf); - } - - ret = 0; - -cleanup: - - if(infd != -1) - close(infd); - if(outfd != -1) - close(outfd); - if(errfd != -1) - close(errfd); - - if(pid != 0) - { - sp_messagex(sp, LOG_WARNING, "killing filter process (pid %d)", (int)pid); - kill_process(sp, pid); - } - - if(ret < 0) - sp_add_log(sp, "status=", "FILTER-ERROR"); - - return ret; -} - -static void final_reject_message(char* buf, int buflen) -{ - if(buf[0] == 0) - strlcpy(buf, REJECTED, buflen); - else - trim_end(buf); -} - -static void buffer_reject_message(char* data, char* buf, int buflen) -{ - int len = strlen(data); - char* t = data + len; - int newline = 0; - - while(t > data && isspace(*(t - 1))) - { - t--; - - if(*t == '\n') - newline = 1; - } - - /* No valid line */ - if(t > data) - { - if(newline) - *t = 0; - - t = strrchr(data, '\n'); - if(t == NULL) - { - t = trim_start(data); - - /* - * Basically if we already have a newline at the end - * then we need to start a new line - */ - if(buf[strlen(buf)] == '\n') - buf[0] = 0; - } - else - { - t = trim_start(t); - - /* Start a new line */ - buf[0] = 0; - } - - /* t points to a valid line */ - strlcat(buf, t, buflen); - } - - /* Always append if we found a newline */ - if(newline) - strlcat(buf, "\n", buflen); -} - -static int wait_process(spctx_t* sp, pid_t pid, int* status) -{ - /* We poll x times a second */ - int waits = g_pxstate.timeout.tv_sec * (1000 / POLL_TIME); - - while(waits > 0) - { - switch(waitpid(pid, status, WNOHANG)) - { - case 0: - break; - case -1: - if(errno != ECHILD || errno != ESRCH) - { - sp_message(sp, LOG_CRIT, "error waiting on process"); - return -1; - } - /* fall through */ - default: - return 0; - } - - usleep(POLL_TIME * 1000); - waits--; - } - - return -1; -} - -static int kill_process(spctx_t* sp, pid_t pid) -{ - int status; - - if(kill(pid, SIGTERM) == -1) - { - if(errno == ESRCH) - return 0; - - sp_message(sp, LOG_ERR, "couldn't send signal to process"); - return -1; - } - - if(wait_process(sp, pid, &status) == -1) - { - if(kill(pid, SIGKILL) == -1) - { - if(errno == ESRCH) - return 0; - - sp_message(sp, LOG_ERR, "couldn't send signal to process"); - return -1; - } - - sp_messagex(sp, LOG_ERR, "process wouldn't quit. forced termination"); - } - - return 0; -} diff --git a/src/proxsmtpd.h b/src/proxsmtpd.h deleted file mode 100644 index 06b2412..0000000 --- a/src/proxsmtpd.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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__ */ |