summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am8
-rw-r--r--src/clamsmtpd.c1312
-rw-r--r--src/clamsmtpd.h102
-rw-r--r--src/clio.c407
-rw-r--r--src/clstate.c322
-rw-r--r--src/compat.c182
-rw-r--r--src/compat.h87
-rw-r--r--src/sock_any.c385
-rw-r--r--src/sock_any.h90
-rw-r--r--src/usuals.h76
-rw-r--r--src/util.c283
-rw-r--r--src/util.h57
12 files changed, 235 insertions, 3076 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 3c8deb6..5c40237 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,8 +1,12 @@
sbin_PROGRAMS = clamsmtpd
-clamsmtpd_SOURCES = clamsmtpd.c clamsmtpd.h util.c util.h sock_any.h sock_any.c \
- compat.c compat.h usuals.h clio.c clstate.c
+clamsmtpd_SOURCES = clamsmtpd.c clamsmtpd.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
+
+clamsmtpd_CFLAGS = -I${top_srcdir}/common/ -I${top_srcdir}/
all-local:
@echo "NOTE: Ignore any warnings about mktemp(). It's used safely in this case."
diff --git a/src/clamsmtpd.c b/src/clamsmtpd.c
index 4c08919..9f35165 100644
--- a/src/clamsmtpd.c
+++ b/src/clamsmtpd.c
@@ -33,45 +33,51 @@
*
* CONTRIBUTORS
* Nate Nielsen <nielsen@memberwebs.com>
- * Andreas Steinmetz <ast@domdv.de>
*/
-#include <sys/time.h>
#include <sys/types.h>
-#include <sys/socket.h>
#include <sys/param.h>
-#include <sys/stat.h>
+#include <paths.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
-#include <fcntl.h>
#include <syslog.h>
-#include <signal.h>
#include <errno.h>
#include <err.h>
#include "usuals.h"
-#ifdef LINUX_TRANSPARENT_PROXY
-#include <linux/netfilter_ipv4.h>
-#endif
-
#include "compat.h"
#include "sock_any.h"
-#include "clamsmtpd.h"
-#include "util.h"
+#include "stringx.h"
+
+#define SP_LEGACY_OPTIONS
+#include "smtppass.h"
/* -----------------------------------------------------------------------
* STRUCTURES
*/
-typedef struct clamsmtp_thread
+typedef struct clstate
{
- pthread_t tid; /* Written to by the main thread */
- int fd; /* The file descriptor or -1 */
+ /* Settings ------------------------------- */
+ struct sockaddr_any clamaddr; /* Address for connecting to clamd */
+ const char* clamname;
+ const char* header; /* The header to add to email */
+ const char* directory; /* The directory for temp files */
+ int bounce; /* Send back a reject line */
+ int quarantine; /* Leave virus files in temp dir */
+ int debug_files; /* Leave all files in temp dir */
}
-clamsmtp_thread_t;
+clstate_t;
+
+typedef struct clctx
+{
+ spctx_t sp; /* The main sp context */
+ spio_t clam; /* Connection to clamd */
+}
+clctx_t;
/* -----------------------------------------------------------------------
* STRINGS
@@ -79,43 +85,8 @@ clamsmtp_thread_t;
#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_DATAVIRUS "550 Virus Detected; Content Rejected" 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_DATAVIRUSOK "250 Virus Detected; Discarded Email" CRLF
-#define SMTP_OK "250 Ok" CRLF
-
-#define SMTP_DATA "DATA" CRLF
-#define SMTP_BANNER "220 clamsmtp" CRLF
-#define SMTP_HELO_RSP "250 clamsmtp" CRLF
-#define SMTP_EHLO_RSP "250-clamsmtp" 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 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 DATA_END_SIG "." CRLF
-
-#define DATA_RSP "354"
-#define OK_RSP "250"
-#define START_RSP "220"
+#define SMTP_DATAVIRUS "550 Virus Detected; Content Rejected" CRLF
#define CLAM_OK "OK"
#define CLAM_ERROR "ERROR"
@@ -129,37 +100,35 @@ clamsmtp_thread_t;
#define DEFAULT_CONFIG CONF_PREFIX "/clamsmtpd.conf"
+#define CFG_CLAMADDR "ClamAddress"
+#define CFG_HEADER "ScanHeader"
+#define CFG_DIRECTORY "TempDirectory"
+#define CFG_BOUNCE "Bounce"
+#define CFG_QUARANTINE "Quarantine"
+#define CFG_DEBUGFILES "DebugFiles"
+
+#define DEFAULT_CLAMAV "/var/run/clamav/clamd"
+#define DEFAULT_HEADER "X-Virus-Scanned: ClamAV using ClamSMTP"
+
/* -----------------------------------------------------------------------
* GLOBALS
*/
-const clstate_t* g_state = NULL; /* The state and configuration of the daemon */
-unsigned int g_unique_id = 0x00100000; /* For connection ids */
-
+clstate_t g_clstate;
/* -----------------------------------------------------------------------
* FORWARD DECLARATIONS
*/
static void usage();
-static void on_quit(int signal);
-static void pid_file(const char* pidfile, int write);
-static void connection_loop(int sock);
-static void* thread_main(void* arg);
-static int smtp_passthru(clamsmtp_context_t* ctx);
-static int connect_out(clamsmtp_context_t* ctx);
-static int connect_clam(clamsmtp_context_t* ctx);
-static int disconnect_clam(clamsmtp_context_t* ctx);
-static void add_to_logline(char* logline, char* prefix, char* line);
-static int avcheck_data(clamsmtp_context_t* ctx, char* logline);
-static int quarantine_virus(clamsmtp_context_t* ctx, char* tempname);
-static int complete_data_transfer(clamsmtp_context_t* ctx, const char* tempname);
-static int transfer_to_file(clamsmtp_context_t* ctx, char* tempname);
-static int transfer_from_file(clamsmtp_context_t* ctx, const char* filename);
-static int clam_scan_file(clamsmtp_context_t* ctx, const char* tempname, char* logline);
-static int read_server_response(clamsmtp_context_t* ctx);
-static void read_junk(clamsmtp_context_t* ctx, int fd);
+static int connect_clam(clctx_t* ctx);
+static int disconnect_clam(clctx_t* ctx);
+static int quarantine_virus(clctx_t* ctx);
+static int clam_scan_file(clctx_t* ctx);
+/* -----------------------------------------------------------------------
+ * SIMPLE MACROS
+ */
/* ----------------------------------------------------------------------------------
* STARTUP ETC...
@@ -169,44 +138,62 @@ int main(int argc, char* argv[])
{
const char* configfile = DEFAULT_CONFIG;
const char* pidfile = NULL;
- clstate_t state;
+ int dbg_level = -1;
int warnargs = 0;
- int sock;
- int true = 1;
int ch = 0;
+ int r;
char* t;
- clstate_init(&state);
- g_state = &state;
+ /* Setup some defaults */
+ memset(&g_clstate, 0, sizeof(g_clstate));
+ g_clstate.header = DEFAULT_HEADER;
+ g_clstate.directory = _PATH_TMP;
+
+ /* We need the default to parse into a useable form, so we do this: */
+ r = cb_parse_option(CFG_CLAMADDR, DEFAULT_CLAMAV);
+ ASSERT(r == 1);
+
+ sp_init("clamsmtpd");
+
+ /*
+ * We still accept our old arguments for compatibility reasons.
+ * We fill them into the spstate structure directly
+ */
/* Parse the arguments nicely */
while((ch = getopt(argc, argv, "bc:d:D:f:h:l:m:p:qt:v")) != -1)
{
switch(ch)
{
- /* Actively reject messages */
+ /* COMPAT: Actively reject messages */
case 'b':
- state.bounce = 1;
+ if((r = cb_parse_option(CFG_BOUNCE, "on")) < 0)
+ usage();
+ ASSERT(r == 1);
warnargs = 1;
break;
- /* Change the CLAM socket */
+ /* COMPAT: Change the CLAM socket */
case 'c':
- state.clamname = optarg;
+ if((r = cb_parse_option(CFG_CLAMADDR, "on")) < 0)
+ usage();
+ ASSERT(r == 1);
warnargs = 1;
break;
/* Don't daemonize */
case 'd':
- state.debug_level = strtol(optarg, &t, 10);
+ dbg_level = strtol(optarg, &t, 10);
if(*t) /* parse error */
errx(1, "invalid debug log level");
- state.debug_level += LOG_ERR;
+ dbg_level += LOG_ERR;
break;
- /* The directory for the files */
+ /* COMPAT: The directory for the files */
case 'D':
- state.directory = optarg;
+ if((r = sp_parse_option(CFG_DIRECTORY, optarg)) < 0)
+ usage();
+ ASSERT(r == 1);
warnargs = 1;
break;
@@ -215,26 +202,27 @@ int main(int argc, char* argv[])
configfile = optarg;
break;
- /* The header to add */
+ /* COMPAT: The header to add */
case 'h':
- if(strlen(optarg) == 0)
- state.header = NULL;
- else
- state.header = optarg;
+ if((r = cb_parse_option(CFG_HEADER, optarg)) < 0)
+ usage();
+ ASSERT(r == 1);
warnargs = 1;
break;
- /* Change our listening port */
+ /* COMPAT: Change our listening port */
case 'l':
- state.listenname = optarg;
+ if((r = sp_parse_option("Listen", optarg)) < 0)
+ usage();
+ ASSERT(r == 1);
warnargs = 1;
break;
- /* The maximum number of threads */
+ /* COMPAT: The maximum number of threads */
case 'm':
- state.max_threads = strtol(optarg, &t, 10);
- if(*t) /* parse error */
- errx(1, "invalid max threads");
+ if((r = sp_parse_option("MaxConnections", optarg)) < 0)
+ usage();
+ ASSERT(r == 1);
warnargs = 1;
break;
@@ -243,28 +231,34 @@ int main(int argc, char* argv[])
pidfile = optarg;
break;
- /* The timeout */
+ /* COMPAT: The timeout */
case 't':
- state.timeout.tv_sec = strtol(optarg, &t, 10);
- if(*t) /* parse error */
- errx(1, "invalid timeout");
+ if((r = sp_parse_option("TimeOut", optarg)) < 0)
+ usage();
+ ASSERT(r == 1);
warnargs = 1;
break;
- /* Leave virus files in directory */
+ /* COMPAT: Leave virus files in directory */
case 'q':
- state.quarantine = 1;
+ if((r = cb_parse_option(CFG_QUARANTINE, "on")) < 0)
+ usage();
+ ASSERT(r == 1);
+ warnargs = 1;
break;
/* Print version number */
case 'v':
printf("clamsmtpd (version %s)\n", VERSION);
+ printf(" (config: %s)\n", DEFAULT_CONFIG);
exit(0);
break;
- /* Leave all files in the tmp directory */
+ /* COMPAT: Leave all files in the tmp directory */
case 'X':
- state.debug_files = 1;
+ if((r = cb_parse_option(CFG_DEBUGFILES, "on")) < 0)
+ usage();
+ ASSERT(r == 1);
warnargs = 1;
break;
@@ -283,106 +277,21 @@ int main(int argc, char* argv[])
usage();
if(argc == 1)
{
- state.outname = argv[0];
+ /* COMPAT: The out address */
+ if((r = sp_parse_option("OutAddress", argv[0])) < 0)
+ usage();
+ ASSERT(r == 1);
warnargs = 1;
}
if(warnargs)
warnx("please use configuration file instead of command-line flags: %s", configfile);
- /* Now parse the configuration file */
- if(clstate_parse_config(&state, configfile) == -1)
- {
- /* Only error when it was forced */
- if(configfile != DEFAULT_CONFIG)
- err(1, "couldn't open config file: %s", configfile);
- else
- warnx("default configuration file not found: %s", configfile);
- }
-
- clstate_validate(&state);
-
- messagex(NULL, LOG_DEBUG, "starting up...");
-
- /* When set to this we daemonize */
- if(g_state->debug_level == -1)
- {
- /* Fork a daemon nicely here */
- if(daemon(0, 0) == -1)
- {
- message(NULL, LOG_ERR, "couldn't run as daemon");
- exit(1);
- }
-
- messagex(NULL, LOG_DEBUG, "running as a daemon");
- state.daemonized = 1;
-
- /* Open the system log */
- openlog("clamsmtpd", 0, LOG_MAIL);
- }
-
- /* Create the socket */
- sock = socket(SANY_TYPE(g_state->listenaddr), SOCK_STREAM, 0);
- if(sock < 0)
- {
- message(NULL, LOG_CRIT, "couldn't open socket");
- exit(1);
- }
-
- 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)
- {
- 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)
- {
- message(NULL, LOG_CRIT, "couldn't listen on socket");
- exit(1);
- }
-
- 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);
-
- if(pidfile)
- pid_file(pidfile, 1);
-
- messagex(NULL, LOG_DEBUG, "accepting connections");
-
- connection_loop(sock);
-
- if(pidfile)
- pid_file(pidfile, 0);
+ r = sp_run(configfile, pidfile, dbg_level);
- messagex(NULL, LOG_DEBUG, "stopped");
+ sp_done();
- /*
- * We have to do this at the very end because even printing
- * messages requires that g_state is valid.
- */
- clstate_cleanup(&state);
- return 0;
-}
-
-static void on_quit(int signal)
-{
- ((clstate_t*)g_state)->quit = 1;
- /* fprintf(stderr, "clamsmtpd: got signal to quit\n"); */
+ return r;
}
static void usage()
@@ -392,633 +301,23 @@ static void usage()
exit(2);
}
-static void pid_file(const char* pidfile, int write)
-{
- if(write)
- {
- FILE* f = fopen(pidfile, "w");
- if(f == NULL)
- {
- message(NULL, LOG_ERR, "couldn't open pid file: %s", pidfile);
- }
- else
- {
- fprintf(f, "%d\n", (int)getpid());
-
- if(ferror(f))
- message(NULL, LOG_ERR, "couldn't write to pid file: %s", pidfile);
-
- fclose(f);
- }
-
- messagex(NULL, LOG_DEBUG, "wrote pid file: %s", pidfile);
- }
-
- else
- {
- unlink(pidfile);
- messagex(NULL, LOG_DEBUG, "removed pid file: %s", pidfile);
- }
-}
-
-
-/* ----------------------------------------------------------------------------------
- * CONNECTION HANDLING
- */
-
-static void connection_loop(int sock)
-{
- clamsmtp_thread_t* threads = NULL;
- int fd, i, x, r;
-
- /* Create the thread buffers */
- threads = (clamsmtp_thread_t*)calloc(g_state->max_threads, sizeof(clamsmtp_thread_t));
- if(!threads)
- errx(1, "out of memory");
-
- /* Now loop and accept the connections */
- while(!g_state->quit)
- {
- fd = accept(sock, NULL, NULL);
- if(fd == -1)
- {
- switch(errno)
- {
- case EINTR:
- case EAGAIN:
- break;
-
- case ECONNABORTED:
- message(NULL, LOG_ERR, "couldn't accept a connection");
- break;
-
- default:
- message(NULL, LOG_ERR, "couldn't accept a connection");
- ((clstate_t*)g_state)->quit = 1;
- break;
- };
-
- if(g_state->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)
- message(NULL, LOG_WARNING, "couldn't set timeouts on incoming connection");
-
- /* 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)
- {
- plock();
- x = threads[i].fd;
- punlock();
-
- if(x == -1)
- {
- 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: */
- 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)
- {
- threads[i].fd = -1;
- threads[i].tid = 0;
-
- errno = r;
- message(NULL, LOG_ERR, "couldn't create thread for connection");
- write(fd, SMTP_STARTFAILED, KL(SMTP_STARTFAILED));
-
- shutdown(fd, SHUT_RDWR);
- close(fd);
- fd = -1;
-
- break;
- }
-
- messagex(NULL, LOG_DEBUG, "created thread for connection");
- fd = -1;
- break;
- }
- }
-
- /* Check to make sure we have a thread */
- if(fd != -1)
- {
- 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;
- }
- }
-
- 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)
- {
- plock();
- fd = threads[i].fd;
- threads[i].fd = -1;
- punlock();
-
- shutdown(fd, SHUT_RDWR);
- close(fd);
- }
-
- pthread_join(threads[i].tid, NULL);
- }
- }
-}
-
-static void* thread_main(void* arg)
-{
- clamsmtp_thread_t* thread = (clamsmtp_thread_t*)arg;
- clamsmtp_context_t* ctx = NULL;
- int processing = 0;
- int ret = 0;
- int fd;
-
- ASSERT(thread);
-
- siginterrupt(SIGINT, 1);
- siginterrupt(SIGTERM, 1);
-
- plock();
- /* Get the client socket */
- fd = thread->fd;
- punlock();
-
- ctx = (clamsmtp_context_t*)calloc(1, sizeof(clamsmtp_context_t));
- if(!ctx)
- {
- /* Special case. We don't have a context so clean up descriptor */
- close(fd);
-
- messagex(NULL, LOG_CRIT, "out of memory");
- RETURN(-1);
- }
-
- memset(ctx, 0, sizeof(*ctx));
-
- clio_init(&(ctx->server), "SERVER");
- clio_init(&(ctx->client), "CLIENT");
- clio_init(&(ctx->clam), "CLAM ");
-
- plock();
- /* 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++;
- punlock();
-
- ctx->client.fd = fd;
- ASSERT(ctx->client.fd != -1);
- messagex(ctx, LOG_DEBUG, "processing %d on thread %x", ctx->client.fd, (int)pthread_self());
-
- /* Connect to the outgoing server ... */
- if(connect_out(ctx) == -1)
- RETURN(-1);
-
- /* ... and to the AV daemon */
- if(connect_clam(ctx) == -1)
- RETURN(-1);
-
- /* call the processor */
- processing = 1;
- ret = smtp_passthru(ctx);
-
-cleanup:
-
- if(ctx)
- {
- disconnect_clam(ctx);
-
- /* Let the client know about fatal errors */
- if(!processing && ret == -1 && clio_valid(&(ctx->client)))
- clio_write_data(ctx, &(ctx->client), SMTP_STARTFAILED);
-
- clio_disconnect(ctx, &(ctx->client));
- clio_disconnect(ctx, &(ctx->server));
- }
-
- /* mark this as done */
- plock();
- thread->fd = -1;
- punlock();
-
- return (void*)(ret == 0 ? 0 : 1);
-}
-
-static int connect_out(clamsmtp_context_t* ctx)
-{
- struct sockaddr_any peeraddr;
- struct sockaddr_any addr;
- const struct sockaddr_any* outaddr;
- char buf[MAXPATHLEN];
- const char* outname;
-
- memset(&peeraddr, 0, sizeof(peeraddr));
- SANY_LEN(peeraddr) = sizeof(peeraddr);
-
- /* Get the peer name */
- if(getpeername(ctx->client.fd, &SANY_ADDR(peeraddr), &SANY_LEN(peeraddr)) == -1 ||
- sock_any_ntop(&peeraddr, buf, MAXPATHLEN, SANY_OPT_NOPORT) == -1)
- message(ctx, LOG_WARNING, "couldn't get peer address");
- else
- messagex(ctx, LOG_INFO, "accepted connection from: %s", buf);
-
- /* 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
- {
- 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)
- {
- 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(clio_connect(ctx, &(ctx->server), outaddr, outname) == -1)
- return -1;
-
- return 0;
-}
-
/* ----------------------------------------------------------------------------------
- * SMTP HANDLING
+ * SP CALLBACKS
*/
-static int smtp_passthru(clamsmtp_context_t* ctx)
+int cb_check_data(spctx_t* sp)
{
- clio_t* io = NULL;
- char logline[LINE_LENGTH];
- int r, ret = 0;
- int neterror = 0;
+ int r = 0;
+ clctx_t* ctx = (clctx_t*)sp;
- int first_rsp = 1; /* The first 220 response from server to be filtered */
- int filter_ehlo = 0; /* Filtering parts of an EHLO extensions response */
- int filter_host = 0; /* Next response is 250 hostname, which we change */
+ /* Connect to clamav */
+ if(!spio_valid(&(ctx->clam)))
+ r = connect_clam(ctx);
- ASSERT(clio_valid(&(ctx->clam)) &&
- clio_valid(&(ctx->clam)));
- logline[0] = 0;
+ if(r != -1 && (r = sp_cache_data(sp)) > 0)
- for(;;)
- {
- if(clio_select(ctx, &io) == -1)
- {
- neterror = 1;
- RETURN(-1);
- }
-
- /* Client has data available, read a line and process */
- if(io == &(ctx->client))
- {
- if(clio_read_line(ctx, &(ctx->client), CLIO_DISCARD) == -1)
- RETURN(-1);
-
- /* Client disconnected, we're done */
- if(ctx->linelen == 0)
- RETURN(0);
-
- /* We don't let clients send really long lines */
- if(LINE_TOO_LONG(ctx))
- {
- if(clio_write_data(ctx, &(ctx->client), SMTP_TOOLONG) == -1)
- RETURN(-1);
-
- continue;
- }
-
- /* Only valid after EHLO or HELO commands */
- filter_ehlo = 0;
- filter_host = 0;
-
- /* Handle the DATA section via our AV checker */
- if(is_first_word(ctx->line, DATA_CMD, KL(DATA_CMD)))
- {
- /* Send back the intermediate response to the client */
- if(clio_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(avcheck_data(ctx, logline) == -1)
- RETURN(-1);
-
- /* Print the log out for this email */
- messagex(ctx, LOG_INFO, "%s", logline);
-
- /* Reset log line */
- logline[0] = 0;
-
- /* Command handled */
- continue;
- }
-
- /*
- * We filter out features that we can't support in
- * the EHLO response (ESMTP). See below
- */
- else if(is_first_word(ctx->line, EHLO_CMD, KL(EHLO_CMD)))
- {
- messagex(ctx, LOG_DEBUG, "filtering EHLO response");
- filter_ehlo = 1;
- filter_host = 1;
-
- /* A new message */
- logline[0] = 0;
- }
-
- /*
- * We need our response to HELO to be modified in order
- * to prevent complaints about mail loops
- */
- else if(is_first_word(ctx->line, HELO_CMD, KL(HELO_CMD)))
- {
- filter_host = 1;
-
- /* A new message line */
- logline[0] = 0;
- }
-
- /*
- * 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(ctx->line, STARTTLS_CMD, KL(STARTTLS_CMD)) ||
- is_first_word(ctx->line, BDAT_CMD, KL(BDAT_CMD)))
- {
- messagex(ctx, LOG_DEBUG, "ESMTP feature not supported");
-
- if(clio_write_data(ctx, &(ctx->client), SMTP_NOTSUPP) == -1)
- RETURN(-1);
-
- /* Command handled */
- continue;
- }
-
- /* Append recipients to log line */
- else if((r = check_first_word(ctx->line, FROM_CMD, KL(FROM_CMD), SMTP_DELIMS)) > 0)
- add_to_logline(logline, "from=", ctx->line + r);
-
- /* Append sender to log line */
- else if((r = check_first_word(ctx->line, TO_CMD, KL(TO_CMD), SMTP_DELIMS)) > 0)
- add_to_logline(logline, "to=", ctx->line + r);
-
- /* Reset log line */
- else if(is_first_word(ctx->line, RSET_CMD, KL(RSET_CMD)))
- logline[0] = 0;
-
- /* All other commands just get passed through to server */
- if(clio_write_data(ctx, &(ctx->server), ctx->line) == -1)
- RETURN(-1);
-
- continue;
- }
-
- /* Server has data available, read a line and forward */
- if(io == &(ctx->server))
- {
- if(clio_read_line(ctx, &(ctx->server), CLIO_DISCARD) == -1)
- RETURN(-1);
-
- if(ctx->linelen == 0)
- RETURN(0);
-
- if(LINE_TOO_LONG(ctx))
- 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(ctx->line, START_RSP, KL(START_RSP)))
- {
- messagex(ctx, LOG_DEBUG, "intercepting initial response");
-
- if(clio_write_data(ctx, &(ctx->client), SMTP_BANNER) == -1)
- RETURN(-1);
-
- /* Command handled */
- continue;
- }
- }
-
- /*
- * 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)
- {
- filter_host = 0;
-
- /* Check for a simple '250 xxxx' */
- if(is_first_word(ctx->line, OK_RSP, KL(OK_RSP)))
- {
- messagex(ctx, LOG_DEBUG, "intercepting host response");
-
- if(clio_write_data(ctx, &(ctx->client), SMTP_HELO_RSP) == -1)
- RETURN(-1);
-
- continue;
- }
-
- /* Check for the continued response '250-xxxx' */
- if(check_first_word(ctx->line, OK_RSP, KL(OK_RSP), SMTP_MULTI_DELIMS) > 0)
- {
- messagex(ctx, LOG_DEBUG, "intercepting host response");
-
- if(clio_write_data(ctx, &(ctx->client), SMTP_EHLO_RSP) == -1)
- RETURN(-1);
-
- continue;
- }
- }
-
- /*
- * Filter out any EHLO responses that we can't or don't want
- * to support. For example pipelining or TLS.
- */
- if(filter_ehlo)
- {
- if((r = check_first_word(ctx->line, OK_RSP, KL(OK_RSP), SMTP_MULTI_DELIMS)) > 0)
- {
- char* p = ctx->line + r;
- 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)))
- {
- messagex(ctx, LOG_DEBUG, "filtered ESMTP feature: %s", trim_space(p));
- continue;
- }
- }
- }
-
- if(clio_write_data(ctx, &(ctx->client), ctx->line) == -1)
- RETURN(-1);
-
- continue;
- }
- }
-
-cleanup:
-
- if(!neterror && ret == -1 && clio_valid(&(ctx->client)))
- clio_write_data(ctx, &(ctx->client), SMTP_FAILED);
-
- return ret;
-}
-
-static void add_to_logline(char* logline, char* prefix, char* line)
-{
- int l = strlen(logline);
- char* t = logline;
-
- /* Simple optimization */
- logline += l;
- l = LINE_LENGTH - l;
-
- ASSERT(l >= 0);
-
- if(t[0] != 0)
- strlcat(logline, ", ", l);
-
- strlcat(logline, prefix, l);
-
- /* Skip initial white space */
- line = trim_start(line);
-
- strlcat(logline, line, l);
-
- /* Skip later white space */
- trim_end(logline);
-}
-
-static int avcheck_data(clamsmtp_context_t* ctx, char* logline)
-{
- /*
- * Note that most failures are non fatal in this function.
- * We only return -1 for data connection errors and the like,
- * For most others we actually send a response back to the
- * client letting them know what happened and let the SMTP
- * connection continue.
- */
-
- char buf[MAXPATHLEN];
- int havefile = 0;
- int r, ret = 0;
-
- strlcpy(buf, g_state->directory, MAXPATHLEN);
- strlcat(buf, "/clamsmtpd.XXXXXX", MAXPATHLEN);
-
- /* transfer_to_file deletes the temp file on failure */
- if((r = transfer_to_file(ctx, buf)) > 0)
- {
- havefile = 1;
- r = clam_scan_file(ctx, buf, logline);
- }
+ /* ClamAV doesn't like empty files */
+ r = clam_scan_file(ctx);
switch(r)
{
@@ -1028,8 +327,8 @@ static int avcheck_data(clamsmtp_context_t* ctx, char* logline)
* the server about any of this yet
*/
case -1:
- if(clio_write_data(ctx, &(ctx->client), SMTP_FAILED))
- RETURN(-1);
+ if(sp_fail_data(sp, NULL) == -1)
+ return -1;
break;
/*
@@ -1037,8 +336,8 @@ static int avcheck_data(clamsmtp_context_t* ctx, char* logline)
* and transfer the file to it.
*/
case 0:
- if(complete_data_transfer(ctx, buf) == -1)
- RETURN(-1);
+ if(sp_done_data(sp, g_clstate.header) == -1)
+ return -1;
break;
/*
@@ -1048,12 +347,12 @@ static int avcheck_data(clamsmtp_context_t* ctx, char* logline)
* choose to reset the connection to reuse it if it wants.
*/
case 1:
- if(clio_write_data(ctx, &(ctx->client),
- g_state->bounce ? SMTP_DATAVIRUS : SMTP_DATAVIRUSOK) == -1)
- RETURN(-1);
-
/* Any special post operation actions on the virus */
- quarantine_virus(ctx, buf);
+ quarantine_virus(ctx);
+
+ if(sp_fail_data(sp, g_clstate.bounce ?
+ SMTP_DATAVIRUS : SMTP_DATAVIRUSOK) == -1)
+ return -1;
break;
default:
@@ -1061,109 +360,114 @@ static int avcheck_data(clamsmtp_context_t* ctx, char* logline)
break;
};
-cleanup:
- if(havefile && !g_state->debug_files)
- {
- messagex(ctx, LOG_DEBUG, "deleting temporary file: %s", buf);
- unlink(buf);
- }
-
- return ret;
+ return 0;
}
-static int complete_data_transfer(clamsmtp_context_t* ctx, const char* tempname)
+int cb_parse_option(const char* name, const char* value)
{
- ASSERT(ctx);
- ASSERT(tempname);
-
- /* Ask the server for permission to send data */
- if(clio_write_data(ctx, &(ctx->server), SMTP_DATA) == -1)
- return -1;
-
- if(read_server_response(ctx) == -1)
- return -1;
+ if(strcasecmp(CFG_CLAMADDR, name) == 0)
+ {
+ if(sock_any_pton(value, &(g_clstate.clamaddr), SANY_OPT_DEFLOCAL) == -1)
+ errx(2, "invalid " CFG_CLAMADDR " socket name: %s", value);
+ g_clstate.clamname = value;
+ return 1;
+ }
- /* If server returns an error then tell the client */
- if(!is_first_word(ctx->line, DATA_RSP, KL(DATA_RSP)))
+ else if(strcasecmp(CFG_HEADER, name) == 0)
{
- if(clio_write_data(ctx, &(ctx->client), ctx->line) == -1)
- return -1;
+ g_clstate.header = (const char*)trim_space((char*)value);
- messagex(ctx, LOG_DEBUG, "server refused data transfer");
+ if(strlen(g_clstate.header) == 0)
+ g_clstate.header = NULL;
- return 0;
+ return 1;
}
- /* Now pull up the file and send it to the server */
- if(transfer_from_file(ctx, tempname) == -1)
+ else if(strcasecmp(CFG_DIRECTORY, name) == 0)
{
- /* Tell the client it went wrong */
- clio_write_data(ctx, &(ctx->client), SMTP_FAILED);
- return -1;
+ g_clstate.directory = value;
+ return 1;
}
- /* Okay read the response from the server and echo it to the client */
- if(read_server_response(ctx) == -1)
- return -1;
+ else if(strcasecmp(CFG_BOUNCE, name) == 0)
+ {
+ if((g_clstate.bounce = strtob(value)) == -1)
+ errx(2, "invalid value for " CFG_BOUNCE);
+ return 1;
+ }
- if(clio_write_data(ctx, &(ctx->client), ctx->line) == -1)
- return -1;
+ else if(strcasecmp(CFG_QUARANTINE, name) == 0)
+ {
+ if((g_clstate.quarantine = strtob(value)) == -1)
+ errx(2, "invalid value for " CFG_BOUNCE);
+ return 1;
+ }
+
+ else if(strcasecmp(CFG_DEBUGFILES, name) == 0)
+ {
+ if((g_clstate.debug_files = strtob(value)) == -1)
+ errx(2, "invalid value for " CFG_DEBUGFILES);
+ return 1;
+ }
return 0;
}
-static int read_server_response(clamsmtp_context_t* ctx)
+spctx_t* cb_new_context()
{
- /* Read response line from the server */
- if(clio_read_line(ctx, &(ctx->server), CLIO_DISCARD) == -1)
- return -1;
-
- if(ctx->linelen == 0)
+ clctx_t* ctx = (clctx_t*)calloc(1, sizeof(clctx_t));
+ if(!ctx)
{
- messagex(ctx, LOG_ERR, "server disconnected unexpectedly");
-
- /* Tell the client it went wrong */
- clio_write_data(ctx, &(ctx->client), SMTP_FAILED);
- return 0;
+ sp_messagex(NULL, LOG_CRIT, "out of memory");
+ return NULL;
}
- if(LINE_TOO_LONG(ctx))
- messagex(ctx, LOG_WARNING, "SMTP response line too long. discarded extra");
-
- return 0;
+ /* Initial preparation of the structure */
+ spio_init(&(ctx->clam), "CLAMAV");
+ return &(ctx->sp);
}
+void cb_del_context(spctx_t* sp)
+{
+ clctx_t* ctx = (clctx_t*)sp;
+ ASSERT(sp);
+
+ disconnect_clam(ctx);
+ free(ctx);
+}
/* ----------------------------------------------------------------------------------
* CLAM AV
*/
-static int connect_clam(clamsmtp_context_t* ctx)
+static int connect_clam(clctx_t* ctx)
{
int ret = 0;
+ spctx_t* sp = &(ctx->sp);
ASSERT(ctx);
- ASSERT(!clio_valid(&(ctx->clam)));
+ ASSERT(!spio_valid(&(ctx->clam)));
- if(clio_connect(ctx, &(ctx->clam), &(g_state->clamaddr), g_state->clamname) == -1)
+ if(spio_connect(sp, &(ctx->clam), &(g_clstate.clamaddr), g_clstate.clamname) == -1)
RETURN(-1);
- read_junk(ctx, ctx->clam.fd);
+ spio_read_junk(sp, &(ctx->clam));
/* Send a session and a check header to ClamAV */
- if(clio_write_data(ctx, &(ctx->clam), "SESSION\n") == -1)
+ if(spio_write_data(sp, &(ctx->clam), "SESSION\n") == -1)
RETURN(-1);
- read_junk(ctx, ctx->clam.fd);
+ spio_read_junk(sp, &(ctx->clam));
+
/*
- if(clio_write_data(ctx, &(ctx->clam), "PING\n") == -1 ||
- clio_read_line(ctx, &(ctx->clam), CLIO_DISCARD | CLIO_TRIM) == -1)
+ if(spio_write_data(sp, &(ctx->clam), "PING\n") == -1 ||
+ spio_read_line(sp, &(ctx->clam), CLIO_DISCARD | CLIO_TRIM) == -1)
RETURN(-1);
- if(strcmp(ctx->line, CONNECT_RESPONSE) != 0)
+ if(strcmp(sp->line, CONNECT_RESPONSE) != 0)
{
- message(ctx, LOG_ERR, "clamd sent an unexpected response: %s", ctx->line);
+ sp_message(sp, LOG_ERR, "clamd sent an unexpected response: %s", ctx->line);
RETURN(-1);
}
*/
@@ -1171,89 +475,93 @@ static int connect_clam(clamsmtp_context_t* ctx)
cleanup:
if(ret < 0)
- clio_disconnect(ctx, &(ctx->clam));
+ spio_disconnect(sp, &(ctx->clam));
return ret;
}
-static int disconnect_clam(clamsmtp_context_t* ctx)
+static int disconnect_clam(clctx_t* ctx)
{
- if(!clio_valid(&(ctx->clam)))
+ spctx_t* sp = &(ctx->sp);
+
+ if(!spio_valid(&(ctx->clam)))
return 0;
- if(clio_write_data(ctx, &(ctx->clam), CLAM_DISCONNECT) != -1)
- read_junk(ctx, ctx->clam.fd);
+ if(spio_write_data(sp, &(ctx->clam), CLAM_DISCONNECT) != -1)
+ spio_read_junk(sp, &(ctx->clam));
- clio_disconnect(ctx, &(ctx->clam));
+ spio_disconnect(sp, &(ctx->clam));
return 0;
}
-static int clam_scan_file(clamsmtp_context_t* ctx, const char* tempname, char* logline)
+static int clam_scan_file(clctx_t* ctx)
{
int len;
+ spctx_t* sp = &(ctx->sp);
- ASSERT(LINE_LENGTH > MAXPATHLEN + 32);
+ /* Needs to be long enough to hold path names */
+ ASSERT(SP_LINE_LENGTH > MAXPATHLEN + 32);
- strcpy(ctx->line, CLAM_SCAN);
- strcat(ctx->line, tempname);
- strcat(ctx->line, "\n");
+ strcpy(sp->line, CLAM_SCAN);
+ strcat(sp->line, sp->cachename);
+ strcat(sp->line, "\n");
- if(clio_write_data(ctx, &(ctx->clam), ctx->line) == -1)
+ if(spio_write_data(sp, &(ctx->clam), sp->line) == -1)
return -1;
- len = clio_read_line(ctx, &(ctx->clam), CLIO_DISCARD | CLIO_TRIM);
+ len = spio_read_line(sp, &(ctx->clam), SPIO_DISCARD | SPIO_TRIM);
if(len == 0)
{
- messagex(ctx, LOG_ERR, "clamd disconnected unexpectedly");
+ sp_messagex(sp, LOG_ERR, "clamd disconnected unexpectedly");
return -1;
}
- if(is_last_word(ctx->line, CLAM_OK, KL(CLAM_OK)))
+ if(is_last_word(sp->line, CLAM_OK, KL(CLAM_OK)))
{
- add_to_logline(logline, "status=", "CLEAN");
- messagex(ctx, LOG_DEBUG, "no virus");
+ sp_add_log(sp, "status=", "CLEAN");
+ sp_messagex(sp, LOG_DEBUG, "no virus");
return 0;
}
- if(is_last_word(ctx->line, CLAM_FOUND, KL(CLAM_FOUND)))
+ if(is_last_word(sp->line, CLAM_FOUND, KL(CLAM_FOUND)))
{
- len = strlen(tempname);
+ len = strlen(sp->cachename);
- if(ctx->linelen > len)
- add_to_logline(logline, "status=VIRUS:", ctx->line + len + 1);
+ if(sp->linelen > len)
+ sp_add_log(sp, "status=VIRUS:", sp->line + len + 1);
else
- add_to_logline(logline, "status=", "VIRUS");
+ sp_add_log(sp, "status=", "VIRUS");
- messagex(ctx, LOG_DEBUG, "found virus");
+ sp_messagex(sp, LOG_DEBUG, "found virus");
return 1;
}
- if(is_last_word(ctx->line, CLAM_ERROR, KL(CLAM_ERROR)))
+ if(is_last_word(sp->line, CLAM_ERROR, KL(CLAM_ERROR)))
{
- messagex(ctx, LOG_ERR, "clamav error: %s", ctx->line);
- add_to_logline(logline, "status=", "CLAMAV-ERROR");
+ sp_messagex(sp, LOG_ERR, "clamav error: %s", sp->line);
+ sp_add_log(sp, "status=", "CLAMAV-ERROR");
return -1;
}
- add_to_logline(logline, "status=", "CLAMAV-ERROR");
- messagex(ctx, LOG_ERR, "unexepected response from clamd: %s", ctx->line);
+ sp_add_log(sp, "status=", "CLAMAV-ERROR");
+ sp_messagex(sp, LOG_ERR, "unexepected response from clamd: %s", sp->line);
return -1;
}
-
/* ----------------------------------------------------------------------------------
* TEMP FILE HANDLING
*/
-static int quarantine_virus(clamsmtp_context_t* ctx, char* tempname)
+static int quarantine_virus(clctx_t* ctx)
{
char buf[MAXPATHLEN];
+ spctx_t* sp = &(ctx->sp);
char* t;
- if(!g_state->quarantine)
+ if(!g_clstate.quarantine)
return 0;
- strlcpy(buf, g_state->directory, MAXPATHLEN);
+ strlcpy(buf, g_clstate.directory, MAXPATHLEN);
strlcat(buf, "/virus.", MAXPATHLEN);
/* Points to null terminator */
@@ -1272,191 +580,29 @@ static int quarantine_virus(clamsmtp_context_t* ctx, char* tempname)
if(!mktemp(buf))
{
- message(ctx, LOG_ERR, "couldn't create quarantine file name");
+ sp_message(sp, LOG_ERR, "couldn't create quarantine file name");
return -1;
}
/* Try to link the file over to the temp */
- if(link(tempname, buf) == -1)
+ if(link(sp->cachename, buf) == -1)
{
/* We don't want to allow race conditions */
if(errno == EEXIST)
{
- message(ctx, LOG_WARNING, "race condition when quarantining virus file: %s", buf);
+ sp_message(sp, LOG_WARNING, "race condition when quarantining virus file: %s", buf);
continue;
}
- message(ctx, LOG_ERR, "couldn't quarantine virus file");
+ sp_message(sp, LOG_ERR, "couldn't quarantine virus file");
return -1;
}
break;
}
- messagex(ctx, LOG_INFO, "quarantined virus file as: %s", buf);
+ sp_messagex(sp, LOG_INFO, "quarantined virus file as: %s", buf);
return 0;
}
-static int transfer_to_file(clamsmtp_context_t* ctx, char* tempname)
-{
- FILE* tfile = NULL;
- int tfd = -1;
- int ended_crlf = 1; /* If the last line ended with a CRLF */
- int ret = 0;
- int count = 0;
-
- if((tfd = mkstemp(tempname)) == -1 ||
- (tfile = fdopen(tfd, "w")) == NULL)
- {
- message(ctx, LOG_ERR, "couldn't open temp file");
- RETURN(-1);
- }
-
- messagex(ctx, LOG_DEBUG, "created temporary file: %s", tempname);
-
- for(;;)
- {
- switch(clio_read_line(ctx, &(ctx->client), CLIO_QUIET))
- {
- case 0:
- messagex(ctx, LOG_ERR, "unexpected end of data from client");
- RETURN(-1);
-
- case -1:
- /* Message already printed */
- RETURN(-1);
- };
-
- if(ended_crlf && strcmp(ctx->line, DATA_END_SIG) == 0)
- break;
-
- /* We check errors on this later */
- fwrite(ctx->line, 1, ctx->linelen, tfile);
- count += ctx->linelen;
-
- /* Check if this line ended with a CRLF */
- ended_crlf = (strcmp(CRLF, ctx->line + (ctx->linelen - KL(CRLF))) == 0);
- }
-
- if(ferror(tfile))
- {
- message(ctx, LOG_ERR, "error writing to temp file: %s", tempname);
- RETURN(-1);
- }
- ret = count;
- messagex(ctx, LOG_DEBUG, "wrote %d bytes to temp file", count);
-
-cleanup:
-
- if(tfile)
- fclose(tfile);
-
- if(tfd != -1)
- {
- /* Only close this if not opened as a stream */
- if(tfile == NULL)
- close(tfd);
-
- if(ret <= 0)
- {
- messagex(ctx, LOG_DEBUG, "discarding temporary file");
- unlink(tempname);
- }
- }
-
- return ret;
-}
-
-static int transfer_from_file(clamsmtp_context_t* ctx, const char* filename)
-{
- FILE* file = NULL;
- int header = 0;
- int ret = 0;
-
- file = fopen(filename, "r");
- if(file == NULL)
- {
- message(ctx, LOG_ERR, "couldn't open temporary file: %s", filename);
- RETURN(-1);
- }
-
- messagex(ctx, LOG_DEBUG, "sending from temporary file: %s", filename);
-
- while(fgets(ctx->line, LINE_LENGTH, file) != NULL)
- {
- if(g_state->header && !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(ctx->line))
- {
- if(clio_write_data_raw(ctx, &(ctx->server), (char*)g_state->header, strlen(g_state->header)) == -1 ||
- clio_write_data_raw(ctx, &(ctx->server), CRLF, KL(CRLF)) == -1)
- RETURN(-1);
-
- header = 1;
- }
- }
-
- if(clio_write_data_raw(ctx, &(ctx->server), ctx->line, strlen(ctx->line)) == -1)
- RETURN(-1);
- }
-
- if(ferror(file))
- {
- message(ctx, LOG_ERR, "error reading temporary file: %s", filename);
- RETURN(-1);
- }
-
- if(clio_write_data(ctx, &(ctx->server), DATA_END_SIG) == -1)
- RETURN(-1);
-
- messagex(ctx, LOG_DEBUG, "sent email data");
-
-cleanup:
-
- if(file != NULL)
- fclose(file);
-
- return ret;
-}
-
-
-/* ----------------------------------------------------------------------------------
- * NETWORKING
- */
-
-static void read_junk(clamsmtp_context_t* ctx, int fd)
-{
- char buf[16];
- const char* t;
- int said = 0;
- int l;
-
- if(fd == -1)
- return;
-
- /* Make it non blocking */
- fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
-
- for(;;)
- {
- l = read(fd, buf, sizeof(buf) - 1);
- if(l <= 0)
- break;
-
- buf[l] = 0;
- t = trim_start(buf);
-
- if(!said && *t)
- {
- messagex(ctx, LOG_DEBUG, "received junk data from daemon");
- said = 1;
- }
- }
-
- fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
-}
diff --git a/src/clamsmtpd.h b/src/clamsmtpd.h
index 10b4a1f..9224f32 100644
--- a/src/clamsmtpd.h
+++ b/src/clamsmtpd.h
@@ -39,107 +39,5 @@
#ifndef __CLAMSMTPD_H__
#define __CLAMSMTPD_H__
-#include <sock_any.h>
-
-/* IO Buffers see clio.c ---------------------------------------------------- */
-
-#define BUF_LEN 256
-
-typedef struct clio
-{
- int fd;
- const char* name;
- unsigned char buf[BUF_LEN];
- size_t buflen;
-}
-clio_t;
-
-/* The main context --------------------------------------------------------- */
-
-/*
- * 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 LINE_LENGTH 2000
-#else
- #define LINE_LENGTH (MAXPATHLEN + 128)
-#endif
-
-typedef struct clamsmtp_context
-{
- unsigned int id; /* Identifier for the connection */
-
- clio_t client; /* Connection to client */
- clio_t server; /* Connection to server */
- clio_t clam; /* Connection to clamd */
-
- char line[LINE_LENGTH]; /* Working buffer */
- int linelen; /* Length of valid data in above */
-}
-clamsmtp_context_t;
-
-#define LINE_TOO_LONG(ctx) ((ctx)->linelen >= (LINE_LENGTH - 2))
-#define RETURN(x) { ret = x; goto cleanup; }
-
-
-/* Implemented in clio.c ---------------------------------------------------- */
-
-#define CLIO_TRIM 0x00000001
-#define CLIO_DISCARD 0x00000002
-#define CLIO_QUIET 0x00000004
-#define clio_valid(io) ((io)->fd != -1)
-
-void clio_init(clio_t* io, const char* name);
-int clio_connect(clamsmtp_context_t* ctx, clio_t* io, const struct sockaddr_any* sany, const char* addrname);
-void clio_disconnect(clamsmtp_context_t* ctx, clio_t* io);
-int clio_select(clamsmtp_context_t* ctx, clio_t** io);
-int clio_read_line(clamsmtp_context_t* ctx, clio_t* io, int trim);
-int clio_write_data(clamsmtp_context_t* ctx, clio_t* io, const char* data);
-int clio_write_data_raw(clamsmtp_context_t* ctx, clio_t* io, unsigned char* buf, int len);
-
-
-/* Implemented in clstate.c ------------------------------------------------ */
-
-typedef struct clstate
-{
- /* 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 */
-
- struct sockaddr_any outaddr; /* The outgoing address */
- const char* outname;
- struct sockaddr_any clamaddr; /* Address for connecting to clamd */
- const char* clamname;
- struct sockaddr_any listenaddr; /* Address to listen on */
- const char* listenname;
-
- const char* header; /* The header to add to email */
- const char* directory; /* The directory for temp files */
- int bounce; /* Send back a reject line */
- int quarantine; /* Leave virus files in temp dir */
- int debug_files; /* Leave all files in temp dir */
- int transparent; /* Transparent proxying */
-
- /* State --------------------------------- */
- int daemonized; /* Whether process is daemonized or not */
- pthread_mutex_t mutex; /* The main mutex */
- int quit; /* Quit the process */
-
- /* Internal Use ------------------------- */
- char* _p;
- pthread_mutexattr_t _mtxattr;
-}
-clstate_t;
-
-extern const clstate_t* g_state;
-
-void clstate_init(clstate_t* state);
-int clstate_parse_config(clstate_t* state, const char* configfile);
-void clstate_validate(clstate_t* state);
-void clstate_cleanup(clstate_t* state);
#endif /* __CLAMSMTPD_H__ */
diff --git a/src/clio.c b/src/clio.c
deleted file mode 100644
index 36b495e..0000000
--- a/src/clio.c
+++ /dev/null
@@ -1,407 +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 "usuals.h"
-#include "sock_any.h"
-#include "clamsmtpd.h"
-#include "util.h"
-
-#define MAX_LOG_LINE 79
-#define GET_IO_NAME(io) ((io)->name ? (io)->name : "??? ")
-
-static void close_raw(int* fd)
-{
- ASSERT(fd);
- shutdown(*fd, SHUT_RDWR);
- close(*fd);
- *fd = -1;
-}
-
-static void log_io_data(clamsmtp_context_t* ctx, clio_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;
-
- messagex(ctx, LOG_DEBUG, "%s%s%s", GET_IO_NAME(io),
- read ? " < " : " > ", buf);
-
- data += pos;
- }
-}
-
-void clio_init(clio_t* io, const char* name)
-{
- ASSERT(io && name);
- memset(io, 0, sizeof(*io));
- io->name = name;
- io->fd = -1;
-}
-
-int clio_connect(clamsmtp_context_t* ctx, clio_t* io, const struct sockaddr_any* sany,
- const char* addrname)
-{
- int ret = 0;
-
- ASSERT(ctx && io && sany && addrname);
- ASSERT(io->fd == -1);
-
- if((io->fd = socket(SANY_TYPE(*sany), SOCK_STREAM, 0)) == -1)
- RETURN(-1);
-
- if(setsockopt(io->fd, SOL_SOCKET, SO_RCVTIMEO, &(g_state->timeout), sizeof(g_state->timeout)) == -1 ||
- setsockopt(io->fd, SOL_SOCKET, SO_SNDTIMEO, &(g_state->timeout), sizeof(g_state->timeout)) == -1)
- messagex(ctx, LOG_WARNING, "couldn't set timeouts on connection");
-
- if(connect(io->fd, &SANY_ADDR(*sany), SANY_LEN(*sany)) == -1)
- RETURN(-1);
-
-cleanup:
- if(ret < 0)
- {
- if(io->fd != -1)
- close(io->fd);
-
- message(ctx, LOG_ERR, "couldn't connect to: %s", addrname);
- return -1;
- }
-
- ASSERT(io->fd != -1);
- messagex(ctx, LOG_DEBUG, "%s connected to: %s", GET_IO_NAME(io), addrname);
- return 0;
-}
-
-void clio_disconnect(clamsmtp_context_t* ctx, clio_t* io)
-{
- ASSERT(ctx && io);
-
- if(clio_valid(io))
- {
- close_raw(&(io->fd));
- messagex(ctx, LOG_DEBUG, "%s connection closed", GET_IO_NAME(io));
- }
-}
-
-int clio_select(clamsmtp_context_t* ctx, clio_t** io)
-{
- fd_set mask;
-
- ASSERT(ctx && io);
- FD_ZERO(&mask);
- *io = NULL;
-
- /* First check if buffers have any data */
-
- if(clio_valid(&(ctx->server)))
- {
- if(ctx->server.buflen > 0)
- {
- *io = &(ctx->server);
- return 0;
- }
-
- FD_SET(ctx->server.fd, &mask);
- }
-
- if(clio_valid(&(ctx->client)))
- {
- if(ctx->client.buflen > 0)
- {
- *io = &(ctx->client);
- return 0;
- }
-
- FD_SET(ctx->client.fd, &mask);
- }
-
- /* Select on the above */
-
- switch(select(FD_SETSIZE, &mask, NULL, NULL, (struct timeval*)&(g_state->timeout)))
- {
- case 0:
- messagex(ctx, LOG_ERR, "network operation timed out");
- return -1;
- case -1:
- message(ctx, LOG_ERR, "couldn't select on sockets");
- return -1;
- };
-
- /* See what came in */
-
- if(FD_ISSET(ctx->server.fd, &mask))
- {
- *io = &(ctx->server);
- return 0;
- }
-
- if(FD_ISSET(ctx->client.fd, &mask))
- {
- *io = &(ctx->client);
- return 0;
- }
-
- ASSERT(0 && "invalid result from select");
- return -1;
-}
-
-int clio_read_line(clamsmtp_context_t* ctx, clio_t* io, int opts)
-{
- int l, x;
- char* t;
- unsigned char* p;
-
- ASSERT(ctx && io);
-
- if(!clio_valid(io))
- {
- messagex(ctx, LOG_WARNING, "tried to read from a closed connection");
- return 0;
- }
-
- ctx->line[0] = 0;
- t = ctx->line;
- l = LINE_LENGTH - 1;
-
- for(;;)
- {
- /* refil buffer if necessary */
- if(io->buflen == 0)
- {
- ASSERT(io->fd != -1);
- io->buflen = read(io->fd, io->buf, sizeof(char) * BUF_LEN);
-
- if(io->buflen == -1)
- {
- io->buflen = 0;
-
- if(errno == EINTR)
- {
- /* When the application is quiting */
- if(g_state->quit)
- return -1;
-
- /* For any other signal we go again */
- continue;
- }
-
- if(errno == ECONNRESET) /* Not usually a big deal so supresse the error */
- messagex(ctx, LOG_DEBUG, "connection disconnected by peer: %s", GET_IO_NAME(io));
- else if(errno == EAGAIN)
- messagex(ctx, LOG_WARNING, "network read operation timed out: %s", GET_IO_NAME(io));
- else
- message(ctx, LOG_ERR, "couldn't read data from socket: %s", 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 */
- if(io->buflen == 0)
- break;
-
- /* Check for a new line */
- p = (unsigned char*)memchr(io->buf, '\n', io->buflen);
-
- if(p != NULL)
- {
- x = (p - io->buf) + 1;
- io->buflen -= x;
- }
-
- else
- {
- x = io->buflen;
- io->buflen = 0;
- }
-
- if(x > l)
- x = l;
-
- /* Copy from buffer line */
- memcpy(t, io->buf, x);
- t += x;
- l -= x;
-
- /* Move whatever we have in the buffer to the front */
- if(io->buflen > 0)
- memmove(io->buf, io->buf + x, io->buflen);
-
- /* Found a new line, done */
- if(p != NULL)
- break;
-
- /* If discarding then don't break when full */
- if(!(opts && CLIO_DISCARD) && l == 0)
- break;
- }
-
- ctx->linelen = (LINE_LENGTH - l) - 1;
- ASSERT(ctx->linelen < LINE_LENGTH);
- ctx->line[ctx->linelen] = 0;
-
- if(opts & CLIO_TRIM && ctx->linelen > 0)
- {
- t = ctx->line;
-
- while(*t && isspace(*t))
- t++;
-
- /* Bump the entire line down */
- l = t - ctx->line;
- memmove(ctx->line, t, (ctx->linelen + 1) - l);
- ctx->linelen -= l;
-
- /* Now the end */
- t = ctx->line + ctx->linelen;
-
- while(t > ctx->line && isspace(*(t - 1)))
- {
- *(--t) = 0;
- ctx->linelen--;
- }
- }
-
- if(!(opts & CLIO_QUIET))
- log_io_data(ctx, io, ctx->line, 1);
-
- return ctx->linelen;
-}
-
-int clio_write_data(clamsmtp_context_t* ctx, clio_t* io, const char* data)
-{
- int len = strlen(data);
- ASSERT(ctx && io && data);
-
- if(!clio_valid(io))
- {
- message(ctx, LOG_ERR, "connection closed. can't write data.");
- return -1;
- }
-
- log_io_data(ctx, io, data, 0);
- return clio_write_data_raw(ctx, io, (unsigned char*)data, len);
-}
-
-int clio_write_data_raw(clamsmtp_context_t* ctx, clio_t* io, unsigned char* buf, int len)
-{
- int r;
-
- ASSERT(ctx && io && buf);
-
- if(io->fd == -1)
- return 0;
-
- 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(g_state->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)
- messagex(ctx, LOG_WARNING, "network write operation timed out: %s", GET_IO_NAME(io));
- else
- message(ctx, LOG_ERR, "couldn't write data to socket: %s", GET_IO_NAME(io));
-
- return -1;
- }
- }
-
- return 0;
-}
diff --git a/src/clstate.c b/src/clstate.c
deleted file mode 100644
index 2c74e11..0000000
--- a/src/clstate.c
+++ /dev/null
@@ -1,322 +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>
- * Yamamoto Takao <takao@oakat.org>
- */
-
-#include <paths.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <err.h>
-#include <pthread.h>
-#include <syslog.h>
-
-#include "usuals.h"
-#include "compat.h"
-#include "clamsmtpd.h"
-#include "util.h"
-
-/* -----------------------------------------------------------------------
- * DIRECTIONS FOR ADDING A CONFIGURATION OPTION
- *
- * - Add field to clstate_t structure in clamsmtpd.h
- * - Add default and set in clstate_init (below)
- * - Add config keyword (below)
- * - Parsing of option in clstate_parse_config (below)
- * - Validation of option in clstate_validate (below)
- * - Document in the sample doc/clamsmtpd.conf
- * - Document in doc/clamsmtpd.conf.5
- */
-
-/* -----------------------------------------------------------------------
- * DEFAULT SETTINGS
- */
-
-#define DEFAULT_SOCKET "10025"
-#define DEFAULT_PORT 10025
-#define DEFAULT_CLAMAV "/var/run/clamav/clamd"
-#define DEFAULT_MAXTHREADS 64
-#define DEFAULT_TIMEOUT 180
-#define DEFAULT_HEADER "X-AV-Checked: ClamAV using ClamSMTP"
-
-/* -----------------------------------------------------------------------
- * CONFIG KEYWORDS
- */
-
-#define CFG_MAXTHREADS "MaxConnections"
-#define CFG_TIMEOUT "TimeOut"
-#define CFG_OUTADDR "OutAddress"
-#define CFG_LISTENADDR "Listen"
-#define CFG_CLAMADDR "ClamAddress"
-#define CFG_HEADER "ScanHeader"
-#define CFG_DIRECTORY "TempDirectory"
-#define CFG_BOUNCE "Bounce"
-#define CFG_QUARANTINE "Quarantine"
-#define CFG_DEBUGFILES "DebugFiles"
-#define CFG_TRANSPARENT "TransparentProxy"
-
-/* The set of delimiters that can be present between config and value */
-#define CFG_DELIMS ": \t"
-
-/* -----------------------------------------------------------------------
- * CODE
- */
-
-/* String to bool helper function */
-static 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;
-}
-
-void clstate_init(clstate_t* state)
-{
- ASSERT(state);
- memset(state, 0, sizeof(*state));
-
- /* Setup the defaults */
- state->debug_level = -1;
- state->max_threads = DEFAULT_MAXTHREADS;
- state->timeout.tv_sec = DEFAULT_TIMEOUT;
- state->clamname = DEFAULT_CLAMAV;
- state->listenname = DEFAULT_SOCKET;
- state->header = DEFAULT_HEADER;
- state->directory = _PATH_TMP;
-
- /* Create the main mutex and condition variable */
- if(pthread_mutexattr_init(&(state->_mtxattr)) != 0 ||
- pthread_mutexattr_settype(&(state->_mtxattr), MUTEX_TYPE) ||
- pthread_mutex_init(&(state->mutex), &(state->_mtxattr)) != 0)
- errx(1, "threading problem. can't create mutex or condition var");
-}
-
-int clstate_parse_config(clstate_t* state, const char* configfile)
-{
- FILE* f = NULL;
- long len;
- int r;
- char* p;
- char* t;
- char* n;
-
- ASSERT(state);
- ASSERT(configfile);
- ASSERT(!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);
- }
-
- 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((state->_p = (char*)malloc(len + 2)) == NULL)
- errx(1, "out of memory");
-
- if(fread(state->_p, 1, len, f) != len)
- err(1, "couldn't read config file: %s", configfile);
-
- fclose(f);
- messagex(NULL, LOG_DEBUG, "opened config file: %s", configfile);
-
- /* Double null terminate the data */
- p = state->_p;
- p[len] = 0;
- p[len + 1] = 0;
-
- n = 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_space(p);
-
- /* Comments and empty lines */
- if(*p == 0 || *p == '#')
- continue;
-
- /* Save some code typing below */
- #define PARSE(o) \
- (r = check_first_word(p, (o), KL(o), CFG_DELIMS))
- #define VAL \
- (p + r)
-
- /*
- * Note that we don't validate here. If something's wrong
- * set it to an invalid value.
- */
-
- if(PARSE(CFG_MAXTHREADS))
- {
- state->max_threads = strtol(VAL, &t, 10);
- if(*t) /* parse failed */
- state->max_threads = -1;
- }
-
- else if(PARSE(CFG_TIMEOUT))
- {
- state->timeout.tv_sec = strtol(VAL, &t, 10);
- if(*t) /* parse failed */
- state->timeout.tv_sec = -1;
- }
-
- else if(PARSE(CFG_OUTADDR))
- state->outname = VAL;
-
- else if(PARSE(CFG_LISTENADDR))
- state->listenname = VAL;
-
- else if(PARSE(CFG_CLAMADDR))
- state->clamname = VAL;
-
- else if(PARSE(CFG_HEADER))
- state->header = VAL;
-
- else if(PARSE(CFG_DIRECTORY))
- state->directory = VAL;
-
- else if(PARSE(CFG_BOUNCE))
- state->bounce = strtob(VAL);
-
- else if(PARSE(CFG_QUARANTINE))
- state->quarantine = strtob(VAL);
-
- else if(PARSE(CFG_DEBUGFILES))
- state->debug_files = strtob(VAL);
-
- else if(PARSE(CFG_TRANSPARENT))
- state->transparent = strtob(VAL);
-
- /* Unrecognized option */
- else
- errx(2, "invalid config line: %s", p);
-
- messagex(NULL, LOG_DEBUG, "parsed line: %s", p);
- }
-
- return 0;
-}
-
-void clstate_validate(clstate_t* state)
-{
- ASSERT(state);
- messagex(NULL, LOG_DEBUG, "validating configuration options");
-
- if(!(state->debug_level == -1 || state->debug_level <= LOG_DEBUG))
- errx(2, "invalid debug log level (must be between 1 and 4)");
-
- if(state->max_threads <= 1 || state->max_threads >= 1024)
- errx(2, "invalid setting: " CFG_MAXTHREADS " (must be between 1 and 1024)");
-
- if(state->timeout.tv_sec <= 0)
- errx(2, "invalid setting: " CFG_TIMEOUT);
-
- if(state->bounce == -1)
- errx(2, "invalid value for " CFG_BOUNCE);
- if(state->quarantine == -1)
- errx(2, "invalid value for " CFG_QUARANTINE);
- if(state->debug_files == -1)
- errx(2, "invalid value for " CFG_DEBUGFILES);
-
- /* This option has no default, but is required */
- if(state->outname == NULL && !state->transparent)
- errx(2, "no " CFG_OUTADDR " specified.");
-
- /* Parse all the addresses */
- if(state->outname != NULL)
- {
- if(state->transparent)
- warnx("the " CFG_OUTADDR " option will be ignored when " CFG_TRANSPARENT " is enabled");
- else
- if(sock_any_pton(state->outname, &(state->outaddr), SANY_OPT_DEFPORT(25)) == -1)
- errx(2, "invalid " CFG_OUTADDR " socket name or ip: %s", state->outname);
- }
-
- if(sock_any_pton(state->listenname, &(state->listenaddr), SANY_OPT_DEFANY | SANY_OPT_DEFPORT(DEFAULT_PORT)) == -1)
- errx(2, "invalid " CFG_LISTENADDR " socket name or ip: %s", state->listenname);
- if(sock_any_pton(state->clamname, &(state->clamaddr), SANY_OPT_DEFLOCAL) == -1)
- errx(2, "invalid " CFG_CLAMADDR " socket name: %s", state->clamname);
-
- if(strlen(state->directory) == 0)
- errx(2, "invalid setting: " CFG_DIRECTORY);
-
- if(state->header)
- {
- /*
- * This is for when it comes from the command-line.
- * Once command line args are phased out this can be removed
- */
- state->header = (const char*)trim_space((char*)state->header);
-
- if(strlen(state->header) == 0)
- state->header = NULL;
- }
-}
-
-void clstate_cleanup(clstate_t* state)
-{
- /* Close the mutex */
- pthread_mutex_destroy(&(state->mutex));
- pthread_mutexattr_destroy(&(state->_mtxattr));
-
- if(state->_p)
- free(state->_p);
-
- memset(state, 0, sizeof(*state));
-}
diff --git a/src/compat.c b/src/compat.c
deleted file mode 100644
index 262f94d..0000000
--- a/src/compat.c
+++ /dev/null
@@ -1,182 +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>
- *
- *
- * 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
-
-
diff --git a/src/compat.h b/src/compat.h
deleted file mode 100644
index 98ebade..0000000
--- a/src/compat.h
+++ /dev/null
@@ -1,87 +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>
-
-/* TODO: Move this logic to configure */
-#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
-# error "Need error checking mutex functionality"
-# 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
-
-#endif /* _COMPAT_H_ */
diff --git a/src/sock_any.c b/src/sock_any.c
deleted file mode 100644
index 7535e02..0000000
--- a/src/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/src/sock_any.h b/src/sock_any.h
deleted file mode 100644
index 31cb13b..0000000
--- a/src/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/src/usuals.h b/src/usuals.h
deleted file mode 100644
index 385bcf9..0000000
--- a/src/usuals.h
+++ /dev/null
@@ -1,76 +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))
-
-#endif /* __USUALS_H__ */
diff --git a/src/util.c b/src/util.c
deleted file mode 100644
index 1067f27..0000000
--- a/src/util.c
+++ /dev/null
@@ -1,283 +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 <unistd.h>
-#include <errno.h>
-#include <err.h>
-#include <stdarg.h>
-#include <strings.h>
-
-#include "usuals.h"
-#include "compat.h"
-#include "clamsmtpd.h"
-#include "util.h"
-
-/* ----------------------------------------------------------------------------------
- * Logging
- */
-
-const char kMsgDelimiter[] = ": ";
-#define MAX_MSGLEN 256
-
-static void vmessage(clamsmtp_context_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)
- {
- /* TODO: strerror_r doesn't want to work for us
- strerror_r(e, m + strlen(m), MAX_MSGLEN); */
- strncat(m, strerror(e), len);
- }
-
- m[len - 1] = 0;
- msg = m;
- }
-
- /* Either to syslog or stderr */
- if(g_state->daemonized)
- vsyslog(level, msg, ap);
- else
- vwarnx(msg, ap);
-}
-
-void messagex(clamsmtp_context_t* ctx, int level, const char* msg, ...)
-{
- va_list ap;
-
- va_start(ap, msg);
- vmessage(ctx, level, 0, msg, ap);
- va_end(ap);
-}
-
-void message(clamsmtp_context_t* ctx, int level, const char* msg, ...)
-{
- va_list ap;
-
- va_start(ap, msg);
- vmessage(ctx, level, 1, msg, ap);
- va_end(ap);
-}
-
-/* ----------------------------------------------------------------------------------
- * 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);
-}
-
-/* -----------------------------------------------------------------------
- * Locking
- */
-
-void plock()
-{
- int r;
-
-#ifdef _DEBUG
- int wait = 0;
-#endif
-
-#ifdef _DEBUG
- r = pthread_mutex_trylock((pthread_mutex_t*)&(g_state->mutex));
- if(r == EBUSY)
- {
- wait = 1;
- message(NULL, LOG_DEBUG, "thread will block: %d", pthread_self());
- r = pthread_mutex_lock((pthread_mutex_t*)&(g_state->mutex));
- }
-
-#else
- r = pthread_mutex_lock(&(g_state->mutex));
-
-#endif
-
- if(r != 0)
- {
- errno = r;
- message(NULL, LOG_CRIT, "threading problem. couldn't lock mutex");
- }
-
-#ifdef _DEBUG
- else if(wait)
- {
- message(NULL, LOG_DEBUG, "thread unblocked: %d", pthread_self());
- }
-#endif
-}
-
-void punlock()
-{
- int r = pthread_mutex_unlock((pthread_mutex_t*)&(g_state->mutex));
- if(r != 0)
- {
- errno = r;
- message(NULL, LOG_CRIT, "threading problem. couldn't unlock mutex");
- }
-}
-
diff --git a/src/util.h b/src/util.h
deleted file mode 100644
index 37fa245..0000000
--- a/src/util.h
+++ /dev/null
@@ -1,57 +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 __UTIL_H__
-#define __UTIL_H__
-
-void messagex(clamsmtp_context_t* ctx, int level, const char* msg, ...);
-void message(clamsmtp_context_t* ctx, int level, const char* msg, ...);
-
-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);
-
-void plock();
-void punlock();
-
-#endif /* __UTIL_H__ */