From d806a9d9e19ae13ed8dbf4f26dce3c2afdde7c6a Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Thu, 28 Oct 2010 15:17:05 +0000 Subject: Initial implementation of true transparent proxying on linux. --- common/compat.h | 2 -- common/smtppass.c | 35 +++++++++++++++--------- common/smtppass.h | 3 ++- common/spio.c | 79 +++++++++++++++++++++++++++++++++++-------------------- 4 files changed, 75 insertions(+), 44 deletions(-) (limited to 'common') 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 #include 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) -- cgit v1.2.3