--- tinyproxy-1.9.0/src/transparent-proxy.c 2017-05-09 18:31:10.632130236 +1000 +++ tinyproxy-1.9.0-p2/src/transparent-proxy.c 2017-05-10 10:12:13.578849899 +1000 @@ -32,6 +32,19 @@ #include "text.h" #include "conf.h" +/* This is for FreeBSD and pf with a rdr rule */ +# if defined(__FreeBSD__) + +# include +# include +# define PATH_DEV_PF "/dev/pf" + +# elif defined(__linux__) +/* +// The only place SO_ORIGINAL_DST (80) is defined as addendum by Rusty R. +*/ +# include +# endif /* * Build a URL from parts. */ @@ -52,6 +65,69 @@ return snprintf (*url, len, "http://%s:%d%s", host, port, path); } +# if defined(__FreeBSD__) +/* Use a rtl constructor to initialize open "/dev/pf" +// _NOTE_: is called before main() which will +// be prior to any setuid/guid calls. ie usually as root. +*/ + +static int dev_pf = -1; +static void __attribute__((constructor)) dev_pf_open () { + dev_pf = open (PATH_DEV_PF, O_RDONLY); +} + +static int get_original_destination (int sd, struct sockaddr_in* daddr, socklen_t* slen) { + struct pfioc_natlook nl; + struct sockaddr_in from; + socklen_t flen = sizeof(from); + struct sockaddr_in to; + socklen_t tlen = sizeof(to); + + int result = getpeername (sd, (struct sockaddr*)&from, &flen); + if (result==0) { + result = getsockname (sd, (struct sockaddr*)&to, &tlen); + if (result==0) { + memcpy (daddr, &to, to.sin_len); + daddr->sin_len = to.sin_len; + + if (dev_pf >= 0) { + struct sockaddr_in* to4 = &to; + struct sockaddr_in* from4 = &from; + + memset (&nl, 0, sizeof(nl)); + nl.af = AF_INET; + nl.daddr.v4 = to4->sin_addr; + nl.dport = to4->sin_port; + nl.saddr.v4 = from4->sin_addr; + nl.sport = from4->sin_port; + + nl.proto = IPPROTO_TCP; + nl.direction = PF_OUT; + + result = ioctl (dev_pf, DIOCNATLOOK, &nl); + if (result == 0) { + daddr->sin_family = nl.af; + daddr->sin_addr = nl.rdaddr.v4; + daddr->sin_port = nl.rdport; + } + } + } + } + return result; +} +# elif defined(__linux__) +static int get_original_destination (int sd, struct sockaddr_in* daddr, socklen_t* slen) { + int result = getsockopt (sd, SOL_IP, SO_ORIGINAL_DST, (char *)&daddr, slen); + return result; +} +# else +static int get_original_destination (int sd, struct sockaddr_in* daddr, socklen_t* slen) { + int result = getsockname (sd, (struct sockaddr *) daddr, slen); + return result; +} + +# endif + int do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders, struct request_s *request, struct config_s *conf, @@ -65,10 +141,8 @@ length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data); if (length <= 0) { struct sockaddr_in dest_addr; - - if (getsockname - (connptr->client_fd, (struct sockaddr *) &dest_addr, - &length) < 0) { + length = sizeof (dest_addr); /* This is needed!! */ + if (get_original_destination (connptr->client_fd, &dest_addr, &length) < 0) { log_message (LOG_ERR, "process_request: cannot get destination IP for %d", connptr->client_fd); @@ -77,7 +151,6 @@ "url", *url, NULL); return 0; } - request->host = (char *) safemalloc (17); strlcpy (request->host, inet_ntoa (dest_addr.sin_addr), 17);