From fc68f6b1d87d78fe3764e23ae5738ee0a8791e50 Mon Sep 17 00:00:00 2001 From: serassio <> Date: Sun, 15 Apr 2007 20:46:11 +0000 Subject: [PATCH] Bug #1630: TPROXY needs porting from 2.6 to 3.x This patch adds Linux TPROXY support to Squid 3 forward porting 2.6 code. Based on Steven Swilton work. The changes are verified to not break non-TPROXY use, while the TPROXY code is still untested. If there will be issues in the optional TPROXY code we deal with them as bugs later on. --- configure.in | 45 +++++++++++++++++-- include/autoconf.h.in | 6 +++ src/ICAP/ICAPXaction.cc | 4 +- src/cache_cf.cc | 10 ++++- src/cf.data.pre | 6 ++- src/client_side.cc | 67 ++++++++++++++++++++-------- src/forward.cc | 98 ++++++++++++++++++++++++++++++++++------- src/forward.h | 5 ++- src/globals.h | 6 ++- src/http.cc | 59 ++++++++++++++++++------- src/main.cc | 13 ++++-- src/pconn.cc | 33 +++++++++----- src/pconn.h | 6 +-- src/protos.h | 3 +- src/structs.h | 13 +++++- src/tools.cc | 46 ++++++++++++++++++- 16 files changed, 338 insertions(+), 82 deletions(-) diff --git a/configure.in b/configure.in index 899e960a34..7169b59789 100644 --- a/configure.in +++ b/configure.in @@ -1,7 +1,7 @@ dnl Configuration input file for Squid dnl -dnl $Id: configure.in,v 1.449 2007/04/12 23:51:55 wessels Exp $ +dnl $Id: configure.in,v 1.450 2007/04/15 14:46:11 serassio Exp $ dnl dnl dnl @@ -11,7 +11,7 @@ AM_CONFIG_HEADER(include/autoconf.h) AC_CONFIG_AUX_DIR(cfgaux) AC_CONFIG_SRCDIR([src/main.cc]) AM_INIT_AUTOMAKE([tar-ustar]) -AC_REVISION($Revision: 1.449 $)dnl +AC_REVISION($Revision: 1.450 $)dnl AC_PREFIX_DEFAULT(/usr/local/squid) AM_MAINTAINER_MODE @@ -1274,6 +1274,21 @@ dnl "-64" from LDFLAGS esac fi +dnl Enable Linux transparent proxy support +AC_ARG_ENABLE(linux-tproxy, +[ --enable-linux-tproxy + Enable real Transparent Proxy support for Netfilter TPROXY.], +[ if test "$enableval" = "yes" ; then + echo "Linux Netfilter/TPROXY enabled" + AC_DEFINE(LINUX_TPROXY, 1, [Enable real Transparent Proxy support for Netfilter TPROXY.]) + LINUX_TPROXY="yes" + if test -z "$LINUX_NETFILTER"; then + echo "Linux-Netfilter Transparent Proxy automatically enabled" + LINUX_NETFILTER="yes" + fi + fi +]) + AM_CONDITIONAL(MAKE_LEAKFINDER, false) dnl Enable Leak Finding Functions AC_ARG_ENABLE(leakfinder, @@ -1951,7 +1966,10 @@ AC_CHECK_HEADERS( \ db_185.h ) -AC_CHECK_HEADERS(linux/netfilter_ipv4.h,,, +AC_CHECK_HEADERS( + linux/netfilter_ipv4.h \ + linux/netfilter_ipv4/ip_tproxy.h \ +,,, SQUID_DEFAULT_INCLUDES #if HAVE_LIMITS_H #include @@ -2785,6 +2803,27 @@ if test "$LINUX_NETFILTER" = "no" ; then sleep 10 fi +dnl Linux Netfilter/TPROXY support requires some specific header files +dnl Shamelessly copied from shamelessly copied from above +if test "$LINUX_TPROXY" ; then + AC_MSG_CHECKING(if TPROXY header files are installed) + # hold on to your hats... + if test "$ac_cv_header_linux_netfilter_ipv4_ip_tproxy_h" = "yes" && test "$LINUX_NETFILTER" = "yes"; then + LINUX_TPROXY="yes" + AC_DEFINE(LINUX_TPROXY, 1, [Enable real Transparent Proxy support for Netfilter TPROXY.]) + else + LINUX_TPROXY="no" + AC_DEFINE(LINUX_TPROXY, 0, [Enable real Transparent Proxy support for Netfilter TPROXY.]) + fi + AC_MSG_RESULT($LINUX_TPROXY) +fi +if test "$LINUX_TPROXY" = "no" && test "$LINUX_NETFILTER" = "yes"; then + echo "WARNING: Cannot find TPROXY headers, you need to install the" + echo "tproxy package from:" + echo " - lynx http://www.balabit.com/downloads/tproxy/" + sleep 10 +fi + if test -z "$USE_GNUREGEX" ; then case "$host" in *-sun-solaris2.[[0-4]]) diff --git a/include/autoconf.h.in b/include/autoconf.h.in index 1b8ae14aa6..e8efc39f81 100644 --- a/include/autoconf.h.in +++ b/include/autoconf.h.in @@ -275,6 +275,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_NETFILTER_IPV4_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_NETFILTER_IPV4_IP_TPROXY_H + /* long is defined in system headers */ #undef HAVE_LONG @@ -736,6 +739,9 @@ /* Enable support for Transparent Proxy on Linux (Netfilter) systems */ #undef LINUX_NETFILTER +/* Enable real Transparent Proxy support for Netfilter TPROXY. */ +#undef LINUX_TPROXY + /* If we need to declare sys_errlist[] as external */ #undef NEED_SYS_ERRLIST diff --git a/src/ICAP/ICAPXaction.cc b/src/ICAP/ICAPXaction.cc index 359e845de3..bc2e260238 100644 --- a/src/ICAP/ICAPXaction.cc +++ b/src/ICAP/ICAPXaction.cc @@ -104,7 +104,7 @@ void ICAPXaction::openConnection() { const ICAPServiceRep &s = service(); // TODO: check whether NULL domain is appropriate here - connection = icapPconnPool->pop(s.host.buf(), s.port, NULL); + connection = icapPconnPool->pop(s.host.buf(), s.port, NULL, NULL); if (connection >= 0) { debugs(93,3, HERE << "reused pconn FD " << connection); @@ -169,7 +169,7 @@ void ICAPXaction::closeConnection() if (reuseConnection) { debugs(93,3, HERE << "pushing pconn" << status()); commSetTimeout(connection, -1, NULL, NULL); - icapPconnPool->push(connection, theService->host.buf(), theService->port, NULL); + icapPconnPool->push(connection, theService->host.buf(), theService->port, NULL, NULL); } else { debugs(93,3, HERE << "closing pconn" << status()); // comm_close will clear timeout diff --git a/src/cache_cf.cc b/src/cache_cf.cc index 2c5af9d00c..61757c9628 100644 --- a/src/cache_cf.cc +++ b/src/cache_cf.cc @@ -1,6 +1,6 @@ /* - * $Id: cache_cf.cc,v 1.507 2007/04/11 22:57:34 wessels Exp $ + * $Id: cache_cf.cc,v 1.508 2007/04/15 14:46:12 serassio Exp $ * * DEBUG: section 3 Configuration File Parsing * AUTHOR: Harvest Derived @@ -2739,6 +2739,14 @@ parse_http_port_option(http_port_list * s, char *token) s->disable_pmtu_discovery = DISABLE_PMTU_ALWAYS; else self_destruct(); + +#if LINUX_TPROXY + + } else if (strcmp(token, "tproxy") == 0) { + s->tproxy = 1; + need_linux_tproxy = 1; +#endif + } else { self_destruct(); } diff --git a/src/cf.data.pre b/src/cf.data.pre index ff5495f894..fa5bf21914 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -1,6 +1,6 @@ # -# $Id: cf.data.pre,v 1.431 2007/04/07 09:35:38 serassio Exp $ +# $Id: cf.data.pre,v 1.432 2007/04/15 14:46:13 serassio Exp $ # # # SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -100,6 +100,10 @@ DOC_START protocol= Protocol to reconstruct accelerated requests with. Defaults to http + tproxy Support Linux TPROXY for spoofing + outgoing connections using the client + IP address. + disable-pmtu-discovery= Control Path-MTU discovery usage: off lets OS decide on what to do (default). diff --git a/src/client_side.cc b/src/client_side.cc index 6176c8f99c..63b2a9b837 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -1,6 +1,6 @@ /* - * $Id: client_side.cc,v 1.745 2007/04/06 04:50:05 rousskov Exp $ + * $Id: client_side.cc,v 1.746 2007/04/15 14:46:15 serassio Exp $ * * DEBUG: section 33 Client-side Routines * AUTHOR: Duane Wessels @@ -1271,6 +1271,7 @@ clientSocketRecipient(clientStreamNode * node, ClientHttpRequest * http, http->al.reply = HTTPMSGLOCK(rep); context->sendStartOfMessage(rep, recievedData); } + PROF_stop(clientSocketRecipient); } @@ -1549,6 +1550,7 @@ void ClientSocketContext::initiateClose(const char *reason) { debugs(33, 5, HERE << "initiateClose: closing for " << reason); + if (http != NULL) { ConnStateData::Pointer conn = http->getConn(); @@ -1563,7 +1565,7 @@ ClientSocketContext::initiateClose(const char *reason) debugs(33, 2, HERE << "avoiding double-closing " << conn); return; } - + /* * XXX We assume the reply fits in the TCP transmit * window. If not the connection may stall while sending @@ -1573,6 +1575,7 @@ ClientSocketContext::initiateClose(const char *reason) * this may be an issue. */ conn->startClosing(reason); + return; } } @@ -1594,6 +1597,7 @@ ClientSocketContext::writeComplete(int fd, char *bufnotused, size_t size, comm_e assert (this->fd() == fd); /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */ + if (errflag == COMM_ERR_CLOSING) return; @@ -1841,13 +1845,16 @@ parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * metho /* Attempt to parse the first line; this'll define the method, url, version and header begin */ r = HttpParserParseReqLine(hp); + if (r == 0) { debug(33, 5) ("Incomplete request, waiting for end of request line\n"); - return NULL; + return NULL; } + if (r == -1) { return parseHttpRequestAbort(conn, "error:invalid-request"); } + /* Request line is valid here .. */ *http_ver = HttpVersion(hp->v_maj, hp->v_min); @@ -1865,10 +1872,13 @@ parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * metho /* We know the whole request is in hp->buf now */ assert(req_sz <= (size_t) hp->bufsiz); + /* Will the following be true with HTTP/0.9 requests? probably not .. */ /* So the rest of the code will need to deal with '0'-byte headers (ie, none, so don't try parsing em) */ assert(req_sz > 0); + hp->hdr_end = req_sz - 1; + hp->hdr_start = hp->req_end + 1; /* Enforce max_request_size */ @@ -1879,10 +1889,11 @@ parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * metho /* Set method_p */ *method_p = HttpRequestMethod(&hp->buf[hp->m_start], &hp->buf[hp->m_end]); + if (*method_p == METHOD_NONE) { - /* XXX need a way to say "this many character length string" */ + /* XXX need a way to say "this many character length string" */ debug(33, 1) ("clientParseRequestMethod: Unsupported method in request '%s'\n", hp->buf); - /* XXX where's the method set for this error? */ + /* XXX where's the method set for this error? */ return parseHttpRequestAbort(conn, "error:unsupported-request-method"); } @@ -1892,7 +1903,9 @@ parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * metho * below needs to be modified to not expect a mutable nul-terminated string. */ url = (char *)xmalloc(hp->u_end - hp->u_start + 16); + memcpy(url, hp->buf + hp->u_start, hp->u_end - hp->u_start + 1); + url[hp->u_end - hp->u_start + 1] = '\0'; /* @@ -1901,13 +1914,16 @@ parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * metho */ /* XXX this code should be modified to take a const char * later! */ req_hdr = (char *) hp->buf + hp->req_end + 1; + debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr); + end = (char *) hp->buf + hp->hdr_end; + debug(33, 3) ("parseHttpRequest: end = {%s}\n", end); if (strstr(req_hdr, "\r\r\n")) { debug(33, 1) ("WARNING: suspicious HTTP request contains double CR\n"); - xfree(url); + xfree(url); return parseHttpRequestAbort(conn, "error:double-CR"); } @@ -2140,7 +2156,7 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC assert(context->http->out.offset == 0); context->pullData(); conn->flags.readMoreRequests = false; - goto finish; + goto finish; } if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) { @@ -2154,7 +2170,7 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC assert(context->http->out.offset == 0); context->pullData(); conn->flags.readMoreRequests = false; - goto finish; + goto finish; } /* compile headers */ @@ -2171,13 +2187,18 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC assert(context->http->out.offset == 0); context->pullData(); conn->flags.readMoreRequests = false; - goto finish; + goto finish; } request->flags.accelerated = http->flags.accel; request->flags.transparent = http->flags.transparent; +#if LINUX_TPROXY + + request->flags.tproxy = conn->port->tproxy; +#endif + if (internalCheck(request->urlpath.buf())) { if (internalHostnameIs(request->host) && request->port == getMyPort()) { @@ -2214,7 +2235,7 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC assert(context->http->out.offset == 0); context->pullData(); conn->flags.readMoreRequests = false; - goto finish; + goto finish; } @@ -2228,13 +2249,14 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC assert(context->http->out.offset == 0); context->pullData(); conn->flags.readMoreRequests = false; - goto finish; + goto finish; } http->request = HTTPMSGLOCK(request); clientSetKeepaliveFlag(http); /* Do we expect a request-body? */ + if (request->content_length > 0) { request->body_pipe = conn->expectRequestBody(request->content_length); @@ -2243,6 +2265,7 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC notedUseOfBuffer = true; conn->handleRequestBodyData(); + if (!request->body_pipe->exhausted()) conn->readSomeData(); @@ -2312,6 +2335,7 @@ ssize_t ConnStateData::bodySizeLeft() { // XXX: this logic will not work for chunked requests with unknown sizes + if (bodyPipe != NULL) return bodyPipe->unproducedSize(); @@ -2339,9 +2363,10 @@ clientParseRequest(ConnStateData::Pointer conn, bool &do_next_read) while (conn->in.notYetUsed > 0 && conn->bodySizeLeft() == 0) { connStripBufferWhitespace (conn); - /* Don't try to parse if the buffer is empty */ - if (conn->in.notYetUsed == 0) - break; + /* Don't try to parse if the buffer is empty */ + + if (conn->in.notYetUsed == 0) + break; /* Limit the number of concurrent requests to 2 */ @@ -2353,13 +2378,15 @@ clientParseRequest(ConnStateData::Pointer conn, bool &do_next_read) /* Terminate the string */ conn->in.buf[conn->in.notYetUsed] = '\0'; - /* Begin the parsing */ - HttpParserInit(&hp, conn->in.buf, conn->in.notYetUsed); + /* Begin the parsing */ + HttpParserInit(&hp, conn->in.buf, conn->in.notYetUsed); /* Process request */ - PROF_start(parseHttpRequest); + PROF_start(parseHttpRequest); + context = parseHttpRequest(conn, &hp, &method, &http_ver); - PROF_stop(parseHttpRequest); + + PROF_stop(parseHttpRequest); /* partial or incomplete request */ if (!context) { @@ -2394,6 +2421,7 @@ clientParseRequest(ConnStateData::Pointer conn, bool &do_next_read) continue; /* while offset > 0 && conn->bodySizeLeft() == 0 */ } } /* while offset > 0 && conn->bodySizeLeft() == 0 */ + /* XXX where to 'finish' the parsing pass? */ return parsed_req; @@ -2494,6 +2522,7 @@ ConnStateData::handleReadData(char *buf, size_t size) xmemmove(current_buf, buf, size); in.notYetUsed += size; + in.buf[in.notYetUsed] = '\0'; /* Terminate the string */ // if we are reading a body, stuff data into the body pipe @@ -3192,7 +3221,7 @@ ConnStateData::closing() const return closing_; } -// Called by ClientSocketContext to give the connection a chance to read +// Called by ClientSocketContext to give the connection a chance to read // the entire body before closing the socket. void ConnStateData::startClosing(const char *reason) diff --git a/src/forward.cc b/src/forward.cc index 5b0fc156fc..07c9f25fff 100644 --- a/src/forward.cc +++ b/src/forward.cc @@ -1,6 +1,6 @@ /* - * $Id: forward.cc,v 1.155 2007/04/12 14:07:10 rousskov Exp $ + * $Id: forward.cc,v 1.156 2007/04/15 14:46:16 serassio Exp $ * * DEBUG: section 17 Request Forwarding * AUTHOR: Duane Wessels @@ -49,6 +49,10 @@ #include "SquidTime.h" #include "Store.h" +#if LINUX_TPROXY +#include +#endif + static PSC fwdStartCompleteWrapper; static PF fwdServerClosedWrapper; #if USE_SSL @@ -102,7 +106,8 @@ FwdState::FwdState(int fd, StoreEntry * e, HttpRequest * r) } // Called once, right after object creation, when it is safe to set self -void FwdState::start(Pointer aSelf) { +void FwdState::start(Pointer aSelf) +{ // Protect ourselves from being destroyed when the only Server pointing // to us is gone (while we expect to talk to more Servers later). // Once we set self, we are responsible for clearing it when we do not @@ -115,18 +120,19 @@ void FwdState::start(Pointer aSelf) { storeRegisterAbort(entry, FwdState::abort, this); peerSelect(request, entry, fwdStartCompleteWrapper, this); - // TODO: set self _after_ the peer is selected because we do not need + // TODO: set self _after_ the peer is selected because we do not need // self until we start talking to some Server. } void FwdState::completed() { - if (flags.forward_completed == 1) { - debugs(17, 1, HERE << "FwdState::completed called on a completed request! Bad!"); - return; - } - flags.forward_completed = 1; + if (flags.forward_completed == 1) { + debugs(17, 1, HERE << "FwdState::completed called on a completed request! Bad!"); + return; + } + + flags.forward_completed = 1; #if URL_CHECKSUM_DEBUG @@ -157,8 +163,9 @@ FwdState::completed() FwdState::~FwdState() { debugs(17, 3, HERE << "FwdState destructor starting"); + if (! flags.forward_completed) - completed(); + completed(); serversFree(&servers); @@ -181,6 +188,7 @@ FwdState::~FwdState() debug(17, 3) ("fwdStateFree: closing FD %d\n", fd); comm_close(fd); } + debugs(17, 3, HERE << "FwdState destructor done"); } @@ -260,6 +268,14 @@ FwdState::fwdStart(int client_fd, StoreEntry *entry, HttpRequest *request) default: FwdState::Pointer fwd = new FwdState(client_fd, entry, request); +#if LINUX_TPROXY + /* If we need to transparently proxy the request + * then we need the client source address and port */ + fwd->src.sin_family = AF_INET; + fwd->src.sin_addr = request->client_addr; + fwd->src.sin_port = request->client_port; +#endif + fwd->start(fwd); return; } @@ -332,9 +348,11 @@ FwdState::complete() debug(17, 3) ("fwdComplete: not re-forwarding status %d\n", entry->getReply()->sline.status); EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); - entry->complete(); - if (server_fd < 0) - completed(); + entry->complete(); + + if (server_fd < 0) + completed(); + self = NULL; // refcounted } } @@ -721,9 +739,15 @@ FwdState::connectStart() const char *domain = NULL; int ctimeout; int ftimeout = Config.Timeout.forward - (squid_curtime - start_t); +#if LINUX_TPROXY + + struct in_tproxy itp; +#endif struct IN_ADDR outgoing; unsigned short tos; + + struct IN_ADDR *client_addr = NULL; assert(fs); assert(server_fd == -1); debug(17, 3) ("fwdConnectStart: %s\n", url); @@ -742,13 +766,19 @@ FwdState::connectStart() ctimeout = Config.Timeout.connect; } +#if LINUX_TPROXY + if (request->flags.tproxy) + client_addr = &request->client_addr; + +#endif + if (ftimeout < 0) ftimeout = 5; if (ftimeout < ctimeout) ctimeout = ftimeout; - if ((fd = fwdPconnPool->pop(host, port, domain)) >= 0) { + if ((fd = fwdPconnPool->pop(host, port, domain, client_addr)) >= 0) { if (checkRetriable()) { debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd); server_fd = fd; @@ -822,10 +852,42 @@ FwdState::connectStart() commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this); - if (fs->_peer) + if (fs->_peer) { hierarchyNote(&request->hier, fs->code, fs->_peer->host); - else + } else { +#if LINUX_TPROXY + + if (request->flags.tproxy) { + itp.v.addr.faddr.s_addr = src.sin_addr.s_addr; + itp.v.addr.fport = 0; + + /* If these syscalls fail then we just fallback to connecting + * normally by simply ignoring the errors... + */ + itp.op = TPROXY_ASSIGN; + + if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) { + debug(20, 1) ("tproxy ip=%s,0x%x,port=%d ERROR ASSIGN\n", + inet_ntoa(itp.v.addr.faddr), + itp.v.addr.faddr.s_addr, + itp.v.addr.fport); + request->flags.tproxy = 0; + } else { + itp.op = TPROXY_FLAGS; + itp.v.flags = ITP_CONNECT; + + if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) { + debug(20, 1) ("tproxy ip=%x,port=%d ERROR CONNECT\n", + itp.v.addr.faddr.s_addr, + itp.v.addr.fport); + request->flags.tproxy = 0; + } + } + } + +#endif hierarchyNote(&request->hier, fs->code, request->host); + } commConnectStart(fd, host, port, fwdConnectDoneWrapper, this); } @@ -923,6 +985,7 @@ FwdState::dispatch() break; case PROTO_WAIS: /* Not implemented */ + default: debug(17, 1) ("fwdDispatch: Cannot retrieve '%s'\n", storeUrl(entry)); @@ -1047,9 +1110,10 @@ FwdState::reforwardableStatus(http_status s) } void -FwdState::pconnPush(int fd, const char *host, int port, const char *domain) + +FwdState::pconnPush(int fd, const char *host, int port, const char *domain, struct IN_ADDR *client_addr) { - fwdPconnPool->push(fd, host, port, domain); + fwdPconnPool->push(fd, host, port, domain, client_addr); } void diff --git a/src/forward.h b/src/forward.h index 19b7961480..483af54ed8 100644 --- a/src/forward.h +++ b/src/forward.h @@ -43,7 +43,7 @@ public: bool checkRetry(); bool checkRetriable(); void dispatch(); - void pconnPush(int fd, const char *host, int port, const char *domain); + void pconnPush(int fd, const char *host, int port, const char *domain, struct IN_ADDR *client_addr); bool dontRetry() { return flags.dont_retry; } @@ -103,6 +103,9 @@ unsigned int forward_completed:1; } flags; +#if LINUX_NETFILTER + struct sockaddr_in src; +#endif }; diff --git a/src/globals.h b/src/globals.h index 9a58646f6b..74100c2705 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1,6 +1,6 @@ /* - * $Id: globals.h,v 1.139 2006/09/13 18:55:10 serassio Exp $ + * $Id: globals.h,v 1.140 2007/04/15 14:46:16 serassio Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -183,6 +183,10 @@ extern "C" extern const char *external_acl_message; /* NULL */ extern int opt_send_signal; /* -1 */ extern int opt_no_daemon; /* 0 */ +#if LINUX_TPROXY + extern int need_linux_tproxy; /* 0 */ +#endif + #ifdef __cplusplus } diff --git a/src/http.cc b/src/http.cc index 0a35768741..67780b077e 100644 --- a/src/http.cc +++ b/src/http.cc @@ -1,6 +1,6 @@ /* - * $Id: http.cc,v 1.512 2007/04/06 12:15:51 serassio Exp $ + * $Id: http.cc,v 1.513 2007/04/15 14:46:16 serassio Exp $ * * DEBUG: section 11 Hypertext Transfer Protocol (HTTP) * AUTHOR: Harvest Derived @@ -75,7 +75,7 @@ static void icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data) #endif HttpStateData::HttpStateData(FwdState *theFwdState) : ServerStateData(theFwdState), - header_bytes_read(0), reply_bytes_read(0) + header_bytes_read(0), reply_bytes_read(0) { debugs(11,5,HERE << "HttpStateData " << this << " created"); ignoreCacheControl = false; @@ -152,7 +152,8 @@ HttpStateData::~HttpStateData() } int -HttpStateData::dataDescriptor() const { +HttpStateData::dataDescriptor() const +{ return fd; } @@ -466,9 +467,9 @@ HttpStateData::cacheableReply() */ if (!refreshIsCachable(entry)) { - debug(22, 3) ("refreshIsCachable() returned non-cacheable..\n"); + debug(22, 3) ("refreshIsCachable() returned non-cacheable..\n"); return 0; - } + } /* don't cache objects from peers w/o LMT, Date, or Expires */ /* check that is it enough to check headers @?@ */ @@ -844,6 +845,7 @@ no_cache: #if HEADERS_LOG headersLog(1, 0, request->method, getReply()); + #endif ctx_exit(ctx); @@ -902,10 +904,12 @@ HttpStateData::persistentConnStatus() const /* If we haven't seen the end of reply headers, we are not done */ debug(11,5)("persistentConnStatus: flags.headers_parsed=%d\n", flags.headers_parsed); + if (!flags.headers_parsed) return INCOMPLETE_MSG; const int clen = reply->bodySize(request->method); + debug(11,5)("persistentConnStatus: clen=%d\n", clen); /* If the body size is unknown we must wait for EOF */ @@ -918,7 +922,7 @@ HttpStateData::persistentConnStatus() const // if (entry->mem_obj->endOffset() < reply->content_length + reply->hdr_sz) const int body_bytes_read = reply_bytes_read - header_bytes_read; debugs(11,5, "persistentConnStatus: body_bytes_read=" << - body_bytes_read << " content_length=" << reply->content_length); + body_bytes_read << " content_length=" << reply->content_length); if (body_bytes_read < reply->content_length) return INCOMPLETE_MSG; @@ -1041,11 +1045,10 @@ HttpStateData::readReply (size_t len, comm_err_t flag, int xerrno) * definately at EOF, so we want to process the reply * headers. */ - PROF_start(HttpStateData_processReplyHeader); + PROF_start(HttpStateData_processReplyHeader); processReplyHeader(); - PROF_stop(HttpStateData_processReplyHeader); - } - else if (getReply()->sline.status == HTTP_INVALID_HEADER && HttpVersion(0,9) != getReply()->sline.version) { + PROF_stop(HttpStateData_processReplyHeader); + } else if (getReply()->sline.status == HTTP_INVALID_HEADER && HttpVersion(0,9) != getReply()->sline.version) { fwd->fail(errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, fwd->request)); flags.do_next_read = 0; } else { @@ -1061,9 +1064,9 @@ HttpStateData::readReply (size_t len, comm_err_t flag, int xerrno) } } else { if (!flags.headers_parsed) { - PROF_start(HttpStateData_processReplyHeader); + PROF_start(HttpStateData_processReplyHeader); processReplyHeader(); - PROF_stop(HttpStateData_processReplyHeader); + PROF_stop(HttpStateData_processReplyHeader); if (flags.headers_parsed) { bool fail = reply == NULL; @@ -1101,21 +1104,26 @@ HttpStateData::writeReplyBody() int len = readBuf->contentSize(); #if ICAP_CLIENT + if (virginBodyDestination != NULL) { const size_t putSize = virginBodyDestination->putMoreData(data, len); readBuf->consume(putSize); return; } + // Even if we are done with sending the virgin body to ICAP, we may still // be waiting for adapted headers. We need them before writing to store. if (adaptedHeadSource != NULL) { debugs(11,5, HERE << "need adapted head from " << adaptedHeadSource); return; } + #endif entry->write (StoreIOBuffer(len, currentOffset, (char*)data)); + readBuf->consume(len); + currentOffset += len; } @@ -1129,6 +1137,9 @@ HttpStateData::writeReplyBody() void HttpStateData::processReplyBody() { + + struct IN_ADDR *client_addr = NULL; + if (!flags.headers_parsed) { flags.do_next_read = 1; maybeReadVirginBody(); @@ -1138,6 +1149,7 @@ HttpStateData::processReplyBody() #if ICAP_CLIENT if (icapAccessCheckPending) return; + #endif /* @@ -1178,14 +1190,20 @@ HttpStateData::processReplyBody() comm_remove_close_handler(fd, httpStateFree, this); fwd->unregister(fd); +#if LINUX_TPROXY + + if (orig_request->flags.tproxy) + client_addr = &orig_request->client_addr; + +#endif if (_peer) { if (_peer->options.originserver) - fwd->pconnPush(fd, _peer->name, orig_request->port, orig_request->host); + fwd->pconnPush(fd, _peer->name, orig_request->port, orig_request->host, client_addr); else - fwd->pconnPush(fd, _peer->name, _peer->http_port, NULL); + fwd->pconnPush(fd, _peer->name, _peer->http_port, NULL, client_addr); } else { - fwd->pconnPush(fd, request->host, request->port, NULL); + fwd->pconnPush(fd, request->host, request->port, NULL, client_addr); } fd = -1; @@ -1211,6 +1229,7 @@ HttpStateData::maybeReadVirginBody() #if RE_ENABLE_THIS_IF_NEEDED_OR_DELETE // This code is not broken, but is probably not needed because we // probably can read more than will fit into the BodyPipe buffer. + if (virginBodyDestination != NULL) { /* * BodyPipe buffer has a finite size limit. We @@ -1227,11 +1246,12 @@ HttpStateData::maybeReadVirginBody() if (icap_space < read_sz) read_sz = icap_space; } + #endif #endif debugs(11,9, HERE << (flags.do_next_read ? "may" : "wont") << - " read up to " << read_sz << " bytes from FD " << fd); + " read up to " << read_sz << " bytes from FD " << fd); /* * why <2? Because delayAwareRead() won't actually read if @@ -1300,6 +1320,7 @@ void HttpStateData::closeServer() { debugs(11,5, HERE << "closing HTTP server FD " << fd << " this " << this); + if (fd >= 0) { fwd->unregister(fd); comm_remove_close_handler(fd, httpStateFree, this); @@ -1732,11 +1753,13 @@ HttpStateData::sendRequest() if (orig_request->body_pipe != NULL) { requestBodySource = orig_request->body_pipe; + if (!requestBodySource->setConsumerIfNotLate(this)) { debugs(32,3, HERE << "aborting on partially consumed body"); requestBodySource = NULL; return false; } + requestSender = HttpStateData::sentRequestBodyWrapper; debugs(32,3, HERE << "expecting request body on pipe " << requestBodySource); } else { @@ -1847,8 +1870,10 @@ HttpStateData::handleMoreRequestBodyAvailable() } assert(requestBodySource != NULL); + if (requestBodySource->buf().hasContent()) { // XXX: why does not this trigger a debug message on every request? + if (flags.headers_parsed && !flags.abuse_detected) { flags.abuse_detected = 1; debug(11, 1) ("http handleMoreRequestBodyAvailable: Likely proxy abuse detected '%s' -> '%s'\n", @@ -1880,6 +1905,7 @@ HttpStateData::sentRequestBody(int fd, size_t size, comm_err_t errflag) { if (size > 0) kb_incr(&statCounter.server.http.kbytes_out, size); + ServerStateData::sentRequestBody(fd, size, errflag); } @@ -1891,6 +1917,7 @@ HttpStateData::abortTransaction(const char *reason) { debugs(11,5, HERE << "aborting transaction for " << reason << "; FD " << fd << ", this " << this); + if (fd >= 0) comm_close(fd); else diff --git a/src/main.cc b/src/main.cc index 48194e404b..b69d380951 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,6 +1,6 @@ /* - * $Id: main.cc,v 1.444 2007/04/13 23:12:31 wessels Exp $ + * $Id: main.cc,v 1.445 2007/04/15 14:46:17 serassio Exp $ * * DEBUG: section 1 Startup and Main Loop * AUTHOR: Harvest Derived @@ -165,6 +165,7 @@ bool SignalDispatcher::dispatch() { PROF_start(SignalDispatcher_dispatch); + if (do_reconfigure) { mainReconfigure(); do_reconfigure = 0; @@ -334,6 +335,7 @@ mainParseOptions(int argc, char *argv[]) break; case 'k': + if ((int) strlen(optarg) < 1) usage(); @@ -387,10 +389,10 @@ mainParseOptions(int argc, char *argv[]) case 'm': if (optarg) { #if MALLOC_DBG - malloc_debug_level = atoi(optarg); + malloc_debug_level = atoi(optarg); #else - fatal("Need to add -DMALLOC_DBG when compiling to use -mX option"); + fatal("Need to add -DMALLOC_DBG when compiling to use -mX option"); #endif } else { @@ -726,6 +728,7 @@ mainRotate(void) static void setEffectiveUser(void) { + keepCapabilities(); leave_suid(); /* Run as non privilegied user */ #ifdef _SQUID_OS2_ @@ -962,7 +965,7 @@ mainInitialize(void) storeDigestRegisterWithCacheManager(manager); StoreFileSystem::RegisterAllFsWithCacheManager(manager); storeRegisterWithCacheManager(manager); - storeLogRegisterWithCacheManager(manager); + storeLogRegisterWithCacheManager(manager); #if DEBUGSTRINGS StringRegistry::Instance().registerWithCacheManager(manager); @@ -988,6 +991,7 @@ mainInitialize(void) serverConnectionsOpen(); neighbors_init(); + neighborsRegisterWithCacheManager(manager); if (Config.chroot_dir) @@ -1644,6 +1648,7 @@ SquidShutdown() unlinkdClose(); /* after sync/flush */ #endif + storeDirWriteCleanLogs(0); PrintRusage(); dumpMallocStats(); diff --git a/src/pconn.cc b/src/pconn.cc index 39715a2a6e..7d072ae93f 100644 --- a/src/pconn.cc +++ b/src/pconn.cc @@ -1,6 +1,6 @@ /* - * $Id: pconn.cc,v 1.47 2006/09/03 21:05:20 hno Exp $ + * $Id: pconn.cc,v 1.48 2007/04/15 14:46:17 serassio Exp $ * * DEBUG: section 48 Persistent Connections * AUTHOR: Duane Wessels @@ -175,12 +175,17 @@ IdleConnList::timeout(int fd, void *data) /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */ const char * -PconnPool::key(const char *host, u_short port, const char *domain) + +PconnPool::key(const char *host, u_short port, const char *domain, struct IN_ADDR *client_address) { LOCAL_ARRAY(char, buf, SQUIDHOSTNAMELEN * 2 + 10); - if (domain) + if (domain && client_address) + snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d-%s/%s", host, (int) port, inet_ntoa(*client_address), domain); + else if (domain && (!client_address)) snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d/%s", host, (int) port, domain); + else if ((!domain) && client_address) + snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d-%s", host, (int) port, inet_ntoa(*client_address)); else snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d", host, (int) port); @@ -222,27 +227,31 @@ PconnPool::PconnPool(const char *aDescr) : table(NULL), descr(aDescr) } void -PconnPool::push(int fd, const char *host, u_short port, const char *domain) + +PconnPool::push(int fd, const char *host, u_short port, const char *domain, struct IN_ADDR *client_address) { IdleConnList *list; const char *aKey; LOCAL_ARRAY(char, desc, FD_DESC_SZ); - if (fdUsageHigh()) { + if (fdUsageHigh()) + { debug(48, 3) ("PconnPool::push: Not many unused FDs\n"); comm_close(fd); return; - } else if (shutting_down) { + } else if (shutting_down) + { comm_close(fd); return; } - aKey = key(host, port, domain); + aKey = key(host, port, domain, client_address); list = (IdleConnList *) hash_lookup(table, aKey); - if (list == NULL) { + if (list == NULL) + { list = new IdleConnList(aKey, this); debug(48, 3) ("pconnNew: adding %s\n", hashKeyStr(&list->hash)); hash_join(table, &list->hash); @@ -260,10 +269,11 @@ PconnPool::push(int fd, const char *host, u_short port, const char *domain) * return a pconn fd for host:port, or -1 if none are available */ int -PconnPool::pop(const char *host, u_short port, const char *domain) + +PconnPool::pop(const char *host, u_short port, const char *domain, struct IN_ADDR *client_address) { IdleConnList *list; - const char * aKey = key(host, port, domain); + const char * aKey = key(host, port, domain, client_address); list = (IdleConnList *)hash_lookup(table, aKey); if (list == NULL) @@ -271,7 +281,8 @@ PconnPool::pop(const char *host, u_short port, const char *domain) int fd = list->findUseableFD(); - if (fd >= 0) { + if (fd >= 0) + { list->clearHandlers(fd); list->removeFD(fd); /* might delete list */ } diff --git a/src/pconn.h b/src/pconn.h index 41a1584d7b..0d3ec5d8a9 100644 --- a/src/pconn.h +++ b/src/pconn.h @@ -48,15 +48,15 @@ public: PconnPool(const char *); void moduleInit(); - void push(int fd, const char *host, u_short port, const char *domain); - int pop(const char *host, u_short port, const char *domain); + void push(int fd, const char *host, u_short port, const char *domain, struct IN_ADDR *client_address); + int pop(const char *host, u_short port, const char *domain, struct IN_ADDR *client_address); void count(int uses); void dumpHist(StoreEntry *e); void unlinkList(IdleConnList *list) const; private: - static const char *key(const char *host, u_short port, const char *domain); + static const char *key(const char *host, u_short port, const char *domain, struct IN_ADDR *client_address); int hist[PCONN_HIST_SZ]; hash_table *table; diff --git a/src/protos.h b/src/protos.h index 4e14eb9bbb..fc9785d6d9 100644 --- a/src/protos.h +++ b/src/protos.h @@ -1,6 +1,6 @@ /* - * $Id: protos.h,v 1.539 2007/04/13 23:12:31 wessels Exp $ + * $Id: protos.h,v 1.540 2007/04/15 14:46:17 serassio Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -606,6 +606,7 @@ SQUIDCEXTERN int percent(int, int); SQUIDCEXTERN double dpercent(double, double); SQUIDCEXTERN void squid_signal(int sig, SIGHDLR *, int flags); SQUIDCEXTERN pid_t readPidFile(void); +SQUIDCEXTERN void keepCapabilities(void); SQUIDCEXTERN struct IN_ADDR inaddrFromHostent(const struct hostent *hp); SQUIDCEXTERN int intAverage(int, int, int, int); diff --git a/src/structs.h b/src/structs.h index 9a42b0dafc..9ba0287f2b 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1,6 +1,6 @@ /* - * $Id: structs.h,v 1.553 2007/04/07 09:35:38 serassio Exp $ + * $Id: structs.h,v 1.554 2007/04/15 14:46:17 serassio Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -150,6 +150,10 @@ unsigned int vhost: int vport; /* virtual port support, -1 for dynamic, >0 static*/ int disable_pmtu_discovery; +#if LINUX_TPROXY +unsigned int tproxy: + 1; /* spoof client ip using tproxy */ +#endif }; @@ -1237,6 +1241,9 @@ struct request_flags #if HTTP_VIOLATIONS nocache_hack = 0; #endif +#if LINUX_TPROXY + tproxy = 0; +#endif } @@ -1287,6 +1294,10 @@ unsigned int accelerated: unsigned int transparent: 1; +#if LINUX_TPROXY +unsigned int tproxy: + 1; /* spoof client ip using tproxy */ +#endif unsigned int internal: 1; diff --git a/src/tools.cc b/src/tools.cc index 4e81a13f48..f6326de50d 100644 --- a/src/tools.cc +++ b/src/tools.cc @@ -1,6 +1,6 @@ /* - * $Id: tools.cc,v 1.272 2006/11/04 17:10:43 hno Exp $ + * $Id: tools.cc,v 1.273 2007/04/15 14:46:17 serassio Exp $ * * DEBUG: section 21 Misc Functions * AUTHOR: Harvest Derived @@ -40,6 +40,15 @@ #include "wordlist.h" #include "SquidTime.h" +#if LINUX_TPROXY +#undef _POSIX_SOURCE +/* Ugly glue to get around linux header madness colliding with glibc */ +#define _LINUX_TYPES_H +#define _LINUX_FS_H +typedef uint32_t __u32; +#include +#endif + #if HAVE_SYS_PRCTL_H #include #endif @@ -706,6 +715,27 @@ leave_suid(void) if (setuid(Config2.effectiveUserID) < 0) debug(50, 0) ("ALERT: setuid: %s\n", xstrerror()); +#endif +#if LINUX_TPROXY + + if (need_linux_tproxy) { + cap_user_header_t head = (cap_user_header_t) xcalloc(1, sizeof(cap_user_header_t)); + cap_user_data_t cap = (cap_user_data_t) xcalloc(1, sizeof(cap_user_data_t)); + + head->version = _LINUX_CAPABILITY_VERSION; + head->pid = 0; + cap->inheritable = cap->permitted = cap->effective = (1 << CAP_NET_ADMIN) + (1 << CAP_NET_BIND_SERVICE) + (1 << CAP_NET_BROADCAST); + + if (capset(head, cap) != 0) { + xfree(head); + xfree(cap); + fatal("Error giving up capabilities"); + } + + xfree(head); + xfree(cap); + } + #endif #if HAVE_PRCTL && defined(PR_SET_DUMPABLE) /* Set Linux DUMPABLE flag */ @@ -1292,3 +1322,17 @@ strwordquote(MemBuf * mb, const char *str) if (quoted) mb->append("\"", 1); } + +void +keepCapabilities(void) +{ +#if LINUX_TPROXY + + if (need_linux_tproxy) { + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { + debug(1, 1) ("Error - tproxy support requires capability setting which has failed. Continuing without tproxy support\n"); + } + } + +#endif +} -- 2.47.2