summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/smtppass.c129
-rw-r--r--common/smtppass.h1
-rw-r--r--common/sock_any.c30
-rw-r--r--common/sock_any.h4
-rw-r--r--common/spio.c2
5 files changed, 127 insertions, 39 deletions
diff --git a/common/smtppass.c b/common/smtppass.c
index e4c69b9..baed725 100644
--- a/common/smtppass.c
+++ b/common/smtppass.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/common/smtppass.h b/common/smtppass.h
index 2fc6f9e..eb9378e 100644
--- a/common/smtppass.h
+++ b/common/smtppass.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/common/sock_any.c b/common/sock_any.c
index 03621ff..e5f24c0 100644
--- a/common/sock_any.c
+++ b/common/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/common/sock_any.h b/common/sock_any.h
index 0b492b4..9fcb128 100644
--- a/common/sock_any.h
+++ b/common/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__ */
diff --git a/common/spio.c b/common/spio.c
index a2694bb..b4c9cb8 100644
--- a/common/spio.c
+++ b/common/spio.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;