diff --git a/src/address.c b/src/address.c index 9a544a5b..a2ac36f5 100644 --- a/src/address.c +++ b/src/address.c @@ -34,6 +34,7 @@ #include /* inet_pton */ #include #include +#include #include "address.h" @@ -259,6 +260,30 @@ address_compare(const struct Address *addr_1, const struct Address *addr_2) { return result; } +int +address_addr_eq(const struct Address *addr, const struct sockaddr *sa) { + if (addr->type != SOCKADDR) + return 0; + + if (address_sa(addr)->sa_family != sa->sa_family) + return 0; + + switch (address_sa(addr)->sa_family) { + case AF_INET: + return !memcmp(&((struct sockaddr_in *)addr->data)->sin_addr, + &((struct sockaddr_in *)sa)->sin_addr, + sizeof(((struct sockaddr_in *)sa)->sin_addr)); + + case AF_INET6: + return !memcmp(&((struct sockaddr_in6 *)addr->data)->sin6_addr, + &((struct sockaddr_in6 *)sa)->sin6_addr, + sizeof(((struct sockaddr_in6 *)sa)->sin6_addr)); + + default: + return 0; + } +} + int address_is_hostname(const struct Address *addr) { return addr != NULL && addr->type == HOSTNAME; diff --git a/src/address.h b/src/address.h index 66125eb6..6d0f5f2f 100644 --- a/src/address.h +++ b/src/address.h @@ -44,6 +44,7 @@ struct Address *new_address_sa(const struct sockaddr *, socklen_t); struct Address *copy_address(const struct Address *); size_t address_len(const struct Address *); int address_compare(const struct Address *, const struct Address *); +int address_addr_eq(const struct Address *, const struct sockaddr *); int address_is_hostname(const struct Address *); int address_is_sockaddr(const struct Address *); int address_is_wildcard(const struct Address *); diff --git a/src/connection.c b/src/connection.c index c10a1090..1e3e7495 100644 --- a/src/connection.c +++ b/src/connection.c @@ -630,23 +630,45 @@ initiate_server_connect(struct Connection *con, struct ev_loop *loop) { if (con->listener->transparent_proxy && con->client.addr.ss_family == con->server.addr.ss_family) { + int result; + + if (address_addr_eq(con->listener->address, + (struct sockaddr *)&con->client.addr)) { + /* Client is the same host as the listener, bind to a new port. */ + struct sockaddr_storage self = con->client.addr; + + switch (con->client.addr.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&self)->sin_port = 0; + break; + + case AF_INET6: + ((struct sockaddr_in6 *)&self)->sin6_port = 0; + break; + } + + result = bind(sockfd, (struct sockaddr *)&self, + con->client.addr_len); + } else { #ifdef IP_TRANSPARENT - int on = 1; - int result = setsockopt(sockfd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); + int on = 1; + result = setsockopt(sockfd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); #else - int result = -EPERM; - /* XXX error: not implemented would be better, but this shouldn't be - * reached since it is prohibited in the configuration parser. */ + result = -EPERM; + /* XXX error: not implemented would be better, but this shouldn't be + * reached since it is prohibited in the configuration parser. */ #endif - if (result < 0) { - err("setsockopt IP_TRANSPARENT failed: %s", strerror(errno)); - close(sockfd); - abort_connection(con); - return; + if (result < 0) { + err("setsockopt IP_TRANSPARENT failed: %s", strerror(errno)); + close(sockfd); + abort_connection(con); + return; + } + + result = bind(sockfd, (struct sockaddr *)&con->client.addr, + con->client.addr_len); } - result = bind(sockfd, (struct sockaddr *)&con->client.addr, - con->client.addr_len); if (result < 0) { err("bind failed: %s", strerror(errno)); close(sockfd);