summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@thewalter.net>2010-10-28 15:17:05 +0000
committerStef Walter <stef@thewalter.net>2011-01-23 15:47:45 -0600
commitd806a9d9e19ae13ed8dbf4f26dce3c2afdde7c6a (patch)
tree0d89b936d1bb8c325efc6f5f731f14d18933f804
parente4c5c1f56e27da22aa0cd7960eee723750d6f3fe (diff)
Initial implementation of true transparent proxying on linux.
-rw-r--r--common/compat.h2
-rw-r--r--common/smtppass.c35
-rw-r--r--common/smtppass.h3
-rw-r--r--common/spio.c79
4 files changed, 75 insertions, 44 deletions
diff --git a/common/compat.h b/common/compat.h
index 43de453..b590f42 100644
--- a/common/compat.h
+++ b/common/compat.h
@@ -39,9 +39,7 @@
#ifndef _COMPAT_H_
#define _COMPAT_H_
-#ifdef HAVE_CONFIG_H
#include "config.h"
-#endif
#include <sys/types.h>
#include <stdlib.h>
diff --git a/common/smtppass.c b/common/smtppass.c
index 08ea5ef..2cddf41 100644
--- a/common/smtppass.c
+++ b/common/smtppass.c
@@ -738,9 +738,11 @@ static int make_connections(spctx_t* ctx, int client)
{
struct sockaddr_any peeraddr;
struct sockaddr_any addr;
- struct sockaddr_any* outaddr;
+ struct sockaddr_any* dstaddr;
+ struct sockaddr_any* srcaddr;
char buf[MAXPATHLEN];
- const char* outname;
+ const char* dstname;
+ const char* srcname;
ASSERT(client != -1);
@@ -749,8 +751,10 @@ static int make_connections(spctx_t* ctx, int client)
sp_messagex(ctx, LOG_INFO, "accepted connection from: %s", ctx->client.peername);
/* Create the server connection address */
- outaddr = &(g_state.outaddr);
- outname = g_state.outname;
+ dstaddr = &(g_state.outaddr);
+ dstname = g_state.outname;
+ srcaddr = NULL;
+ srcname = NULL;
/* For transparent proxying we have to discover the address to connect to */
if(g_state.transparent)
@@ -775,18 +779,23 @@ static int make_connections(spctx_t* ctx, int client)
return -1;
}
- outaddr = &addr;
+ dstaddr = &addr;
+#ifdef LINUX_NETFILTER
+ srcaddr = &peeraddr;
+ srcname = ctx->client.peername;
+#endif
}
/* Check for loopback option */
- else if(SANY_TYPE(*outaddr) == AF_INET &&
- outaddr->s.in.sin_addr.s_addr == 0)
+ else if(SANY_TYPE(*dstaddr) == AF_INET &&
+ dstaddr->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;
+ dstaddr = &addr;
}
+
#ifdef HAVE_INET6
/* IPv6 loopback? */
else if(SANY_TYPE(*outaddr) == AF_INET6 &&
@@ -810,16 +819,16 @@ static int make_connections(spctx_t* ctx, int client)
}
/* Reparse name if needed */
- if(outaddr != &(g_state.outaddr))
+ if(dstaddr != &(g_state.outaddr))
{
- if(sock_any_ntop(outaddr, buf, MAXPATHLEN, 0) != -1)
- outname = buf;
+ if(sock_any_ntop(dstaddr, buf, MAXPATHLEN, 0) != -1)
+ dstname = buf;
else
- outname = "unknown";
+ dstname = "unknown";
}
/* Connect to the server */
- if(spio_connect(ctx, &(ctx->server), outaddr, outname) == -1)
+ if(spio_connect(ctx, &(ctx->server), dstaddr, dstname, srcaddr, srcname) == -1)
return -1;
return 0;
diff --git a/common/smtppass.h b/common/smtppass.h
index c0f23d9..8c2a6cf 100644
--- a/common/smtppass.h
+++ b/common/smtppass.h
@@ -86,7 +86,8 @@ void spio_init(spio_t* io, const char* name);
void spio_attach(struct spctx* ctx, spio_t* io, int fd, struct sockaddr_any* peer);
/* Connect and disconnect from sockets */
-int spio_connect(struct spctx* ctx, spio_t* io, const struct sockaddr_any* sany, const char* addrname);
+int spio_connect(struct spctx* ctx, spio_t* io, const struct sockaddr_any* sdst,
+ const char* dstname, const struct sockaddr_any* ssrc, const char* srcname);
void spio_disconnect(struct spctx* ctx, spio_t* io);
#define SPIO_TRIM 0x00000001
diff --git a/common/spio.c b/common/spio.c
index 5d0733f..736ccc2 100644
--- a/common/spio.c
+++ b/common/spio.c
@@ -150,45 +150,68 @@ void spio_attach(spctx_t* ctx, spio_t* io, int fd, struct sockaddr_any* peer)
io->_ln = 0;
}
-int spio_connect(spctx_t* ctx, spio_t* io, const struct sockaddr_any* sany,
- const char* addrname)
+int spio_connect(spctx_t* ctx, spio_t* io, const struct sockaddr_any* sdst,
+ const char* dstname, const struct sockaddr_any* ssrc, const char *srcname)
{
- int ret = 0;
- int fd;
-
- ASSERT(ctx && io && sany && addrname);
- ASSERT(io->fd == -1);
-
- if((fd = socket(SANY_TYPE(*sany), SOCK_STREAM, 0)) == -1)
- RETURN(-1);
-
- if(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &(g_state.timeout), sizeof(g_state.timeout)) == -1 ||
- setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &(g_state.timeout), sizeof(g_state.timeout)) == -1)
- sp_messagex(ctx, LOG_DEBUG, "%s: couldn't set timeouts on connection", GET_IO_NAME(io));
+ int ret = 0;
+ int fd;
+
+ ASSERT(ctx && io && sdst && dstname);
+ ASSERT(io->fd == -1);
+
+ if((fd = socket(SANY_TYPE(*sdst), SOCK_STREAM, 0)) == -1)
+ RETURN(-1);
+
+ if(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &(g_state.timeout), sizeof(g_state.timeout)) == -1 ||
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &(g_state.timeout), sizeof(g_state.timeout)) == -1)
+ sp_messagex(ctx, LOG_DEBUG, "%s: couldn't set timeouts on connection", GET_IO_NAME(io));
+
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+
+ if (ssrc != NULL) {
+#ifdef LINUX_NETFILTER
+ int value = 1;
+ if(setsockopt(fd, SOL_IP, IP_TRANSPARENT, &value, sizeof(value)) < 0) {
+ sp_message(ctx, LOG_DEBUG, "%s: couldn't set transparent mode on connection",
+ GET_IO_NAME(io));
+ ssrc = NULL;
+ }
+#else
+ /* Can't set source address on other OS */
+ ssrc = NULL;
+#endif
+ }
- fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+ if (ssrc != NULL) {
+ if(bind(fd, &SANY_ADDR(*ssrc), SANY_LEN(*ssrc)) < 0)
+ sp_message(ctx, LOG_WARNING, "%s: couldn't set source of transparent connection to: %s",
+ GET_IO_NAME(io), srcname);
+ else
+ sp_messagex(ctx, LOG_DEBUG, "%s: setup source of transparent connection: %s",
+ GET_IO_NAME(io), srcname);
+ }
- if(connect(fd, &SANY_ADDR(*sany), SANY_LEN(*sany)) == -1)
+ if(connect(fd, &SANY_ADDR(*sdst), SANY_LEN(*sdst)) == -1)
{
close_raw(&fd);
- RETURN(-1);
+ RETURN(-1);
}
- spio_attach(ctx, io, fd, NULL);
+ spio_attach(ctx, io, fd, NULL);
cleanup:
- if(ret < 0)
- {
- if(spio_valid(io))
- close_raw(&(io->fd));
+ if(ret < 0)
+ {
+ if(spio_valid(io))
+ close_raw(&(io->fd));
- sp_message(ctx, LOG_ERR, "%s: couldn't connect to: %s", GET_IO_NAME(io), addrname);
- return -1;
- }
+ sp_message(ctx, LOG_ERR, "%s: couldn't connect to: %s", GET_IO_NAME(io), dstname);
+ return -1;
+ }
- ASSERT(io->fd != -1);
- sp_messagex(ctx, LOG_DEBUG, "%s connected to: %s", GET_IO_NAME(io), io->peername);
- return 0;
+ ASSERT(io->fd != -1);
+ sp_messagex(ctx, LOG_DEBUG, "%s connected to: %s", GET_IO_NAME(io), io->peername);
+ return 0;
}
void spio_disconnect(spctx_t* ctx, spio_t* io)