diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/smtppass.c | 129 | ||||
-rw-r--r-- | common/smtppass.h | 1 | ||||
-rw-r--r-- | common/sock_any.c | 30 | ||||
-rw-r--r-- | common/sock_any.h | 4 | ||||
-rw-r--r-- | common/spio.c | 2 |
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; |