summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2004-09-04 00:05:08 +0000
committerStef Walter <stef@memberwebs.com>2004-09-04 00:05:08 +0000
commitfdafaaeec8b05ab1e62457ecb6202ced98caa202 (patch)
tree3b093585fcce8975acee7b8b5ca5700702821ec6 /src
parent4fb48e235e0f76d3e5fcdd61e88ac63fa46cc14d (diff)
Transparent proxy support.
Diffstat (limited to 'src')
-rw-r--r--src/clamsmtpd.c129
-rw-r--r--src/clamsmtpd.h1
-rw-r--r--src/clio.c2
-rw-r--r--src/clstate.c24
-rw-r--r--src/sock_any.c30
-rw-r--r--src/sock_any.h4
6 files changed, 144 insertions, 46 deletions
diff --git a/src/clamsmtpd.c b/src/clamsmtpd.c
index e4c69b9..baed725 100644
--- a/src/clamsmtpd.c
+++ b/src/clamsmtpd.c
@@ -52,6 +52,11 @@
#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"
@@ -142,6 +147,7 @@ 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);
@@ -542,10 +548,6 @@ static void connection_loop(int sock)
static void* thread_main(void* arg)
{
clamsmtp_thread_t* thread = (clamsmtp_thread_t*)arg;
- struct sockaddr_any addr;
- struct sockaddr_any* outaddr;
- const char* outname;
- char buf[MAXPATHLEN];
clamsmtp_context_t* ctx = NULL;
int processing = 0;
int ret = 0;
@@ -590,45 +592,14 @@ static void* thread_main(void* arg)
ASSERT(ctx->client.fd != -1);
messagex(ctx, LOG_DEBUG, "processing %d on thread %x", ctx->client.fd, (int)pthread_self());
- memset(&addr, 0, sizeof(addr));
- SANY_LEN(addr) = sizeof(addr);
-
- /* Get the peer name */
- if(getpeername(ctx->client.fd, &SANY_ADDR(addr), &SANY_LEN(addr)) == -1 ||
- sock_any_ntop(&addr, 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;
-
- if(SANY_TYPE(*outaddr) == AF_INET &&
- outaddr->s.in.sin_addr.s_addr == 0)
- {
- /* Use the incoming IP as the default */
- in_addr_t in = addr.s.in.sin_addr.s_addr;
- memcpy(&addr, &(g_state.outaddr), sizeof(addr));
- addr.s.in.sin_addr.s_addr = in;
-
- outaddr = &addr;
-
- if(sock_any_ntop(outaddr, buf, MAXPATHLEN, 0) != -1)
- outname = buf;
- }
-
-
- /* Connect to the server */
- if(clio_connect(ctx, &(ctx->server), outaddr, outname) == -1)
+ /* 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);
@@ -655,6 +626,90 @@ cleanup:
return (void*)(ret == 0 ? 0 : 1);
}
+static int connect_out(clamsmtp_context_t* ctx)
+{
+ struct sockaddr_any peeraddr;
+ struct sockaddr_any addr;
+ 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(addr1), &SANY_LEN(addr1)) == -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 = "unknown";
+ }
+
+ /* Connect to the server */
+ if(clio_connect(ctx, &(ctx->server), outaddr, outname) == -1)
+ return -1;
+
+ return 0;
+}
/* ----------------------------------------------------------------------------------
* SMTP HANDLING
diff --git a/src/clamsmtpd.h b/src/clamsmtpd.h
index 2fc6f9e..eb9378e 100644
--- a/src/clamsmtpd.h
+++ b/src/clamsmtpd.h
@@ -122,6 +122,7 @@ typedef struct clstate
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 */
diff --git a/src/clio.c b/src/clio.c
index a2694bb..b4c9cb8 100644
--- a/src/clio.c
+++ b/src/clio.c
@@ -90,7 +90,7 @@ static void log_io_data(clamsmtp_context_t* ctx, clio_t* io, const char* data, i
memcpy(buf, data, len);
buf[len] = 0;
- messagex(0, LOG_DEBUG, "%s%s%s", GET_IO_NAME(io),
+ messagex(ctx, LOG_DEBUG, "%s%s%s", GET_IO_NAME(io),
read ? " < " : " > ", buf);
data += pos;
diff --git a/src/clstate.c b/src/clstate.c
index 0a166f9..2c74e11 100644
--- a/src/clstate.c
+++ b/src/clstate.c
@@ -86,6 +86,7 @@
#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"
@@ -230,7 +231,6 @@ int clstate_parse_config(clstate_t* state, const char* configfile)
else if(PARSE(CFG_DIRECTORY))
state->directory = VAL;
-
else if(PARSE(CFG_BOUNCE))
state->bounce = strtob(VAL);
@@ -240,6 +240,9 @@ int clstate_parse_config(clstate_t* state, const char* configfile)
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);
@@ -264,10 +267,6 @@ void clstate_validate(clstate_t* state)
if(state->timeout.tv_sec <= 0)
errx(2, "invalid setting: " CFG_TIMEOUT);
- /* This option has no default, but is required */
- if(state->outname == NULL)
- errx(2, "no " CFG_OUTADDR " specified.");
-
if(state->bounce == -1)
errx(2, "invalid value for " CFG_BOUNCE);
if(state->quarantine == -1)
@@ -275,11 +274,22 @@ void clstate_validate(clstate_t* state)
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->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->clamname, &(state->clamaddr), SANY_OPT_DEFLOCAL) == -1)
errx(2, "invalid " CFG_CLAMADDR " socket name: %s", state->clamname);
diff --git a/src/sock_any.c b/src/sock_any.c
index 03621ff..e5f24c0 100644
--- a/src/sock_any.c
+++ b/src/sock_any.c
@@ -353,3 +353,33 @@ int sock_any_ntop(struct sockaddr_any* any, char* addr, size_t addrlen, int opts
return 0;
}
+
+int sock_any_cmp(struct sockaddr_any* a1, 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
index 0b492b4..9fcb128 100644
--- a/src/sock_any.h
+++ b/src/sock_any.h
@@ -82,7 +82,9 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts);
int sock_any_ntop(struct sockaddr_any* any, char* addr, size_t addrlen, int opts);
-/* Don't print the port */
+/* Don't print or compare the port */
#define SANY_OPT_NOPORT 0x01000000
+int sock_any_cmp(struct sockaddr_any* a1, struct sockaddr_any* a2, int opts);
+
#endif /* __SOCK_ANY_H__ */