From: hno <> Date: Sat, 3 Apr 2004 21:07:38 +0000 (+0000) Subject: Bug #14: connection setup may look like syn flood attack if server is X-Git-Tag: SQUID_3_0_PRE4~1131 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4ed0e0753d0da5edf9eed8686e67dc1714fd270f;p=thirdparty%2Fsquid.git Bug #14: connection setup may look like syn flood attack if server is refusing connection If the contacted server refuses connection then the repeated attempts to connect to the server may look like a syn flood attack. This patch makes Squid behave a little friendler in such case and: * Delays a little between the repeated attempts. Longer if the attempt was to an origin server. * Limits origin server attempts to 3 connection setup attempts or 2 request forwarding attempts (was 10 on both which only makes sense in peering relations) * Changes the default for maximum_single_addr_tries to 1 as there is plenty of reforwarding attempts done by Squid and at least 3 attempts to initiate the request which makes this directive redundant. * removes a redundant lock from commConnect*() (cbdata managed) * Adds a small delay to commConnect() reconnection attempts when the contacted destination has more than one IP address or maximum_single_addr_tries is used. * Small cleanup in how/when digest considers a peer usable to not disturb the peer probing. * Cleanup of peer TCP probing to correct timeout management etc and to more promptly recover after a failure. --- diff --git a/src/cf.data.pre b/src/cf.data.pre index 4a0e1c4dfc..8e31b02d79 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -1,6 +1,6 @@ # -# $Id: cf.data.pre,v 1.344 2004/01/06 03:44:11 hno Exp $ +# $Id: cf.data.pre,v 1.345 2004/04/03 14:07:38 hno Exp $ # # # SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -3523,15 +3523,18 @@ DOC_END NAME: maximum_single_addr_tries TYPE: int LOC: Config.retry.maxtries -DEFAULT: 3 +DEFAULT: 1 DOC_START This sets the maximum number of connection attempts for a host that only has one address (for multiple-address hosts, each address is tried once). - The default value is three tries, the (not recommended) + The default value is one attempt, the (not recommended) maximum is 255 tries. A warning message will be generated if it is set to a value greater than ten. + + Note: This is in addition to the request reforwarding which + takes place if Squid fails to get a satisfying response. DOC_END NAME: snmp_port @@ -3543,6 +3546,10 @@ DOC_START Squid can now serve statistics and status information via SNMP. By default it listens to port 3401 on the machine. If you don't wish to use SNMP, set this to "0". + + Note: If you want Squid to use parents for all requests then see + the never_direct directive. prefer_direct only modifies how Squid + acts on cachable requests. DOC_END NAME: snmp_access diff --git a/src/comm.cc b/src/comm.cc index d74ec3334d..fe6e5aa176 100644 --- a/src/comm.cc +++ b/src/comm.cc @@ -1,6 +1,6 @@ /* - * $Id: comm.cc,v 1.393 2004/03/01 01:37:34 adrian Exp $ + * $Id: comm.cc,v 1.394 2004/04/03 14:07:38 hno Exp $ * * DEBUG: section 5 Socket Functions * AUTHOR: Harvest Derived @@ -66,7 +66,6 @@ public: CallBack callback; struct in_addr in_addr; - int locks; int fd; int tries; int addrcount; @@ -1304,7 +1303,6 @@ commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void * cs->port = port; cs->callback = CallBack(callback, data); comm_add_close_handler(fd, commConnectFree, cs); - cs->locks++; ipcache_nbgethostbyname(host, commConnectDnsHandle, cs); } @@ -1312,8 +1310,6 @@ static void commConnectDnsHandle(const ipcache_addrs * ia, void *data) { ConnectStateData *cs = (ConnectStateData *)data; - assert(cs->locks == 1); - cs->locks--; if (ia == NULL) { debug(5, 3) ("commConnectDnsHandle: Unknown host: %s\n", cs->host); @@ -1466,6 +1462,13 @@ commRetryConnect(ConnectStateData * cs) return commResetFD(cs); } +static void +commReconnect(void *data) +{ + ConnectStateData *cs = (ConnectStateData *)data; + ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs); +} + /* Connect SOCK to specified DEST_PORT at DEST_HOST. */ void ConnectStateData::Connect (int fd, void *me) @@ -1512,8 +1515,7 @@ ConnectStateData::connect() netdbDeleteAddrNetwork(S.sin_addr); if (commRetryConnect(this)) { - locks++; - ipcache_nbgethostbyname(host, commConnectDnsHandle, this); + eventAdd("commReconnect", commReconnect, this, this->addrcount == 1 ? 0.05 : 0.0, 0); } else { callCallback(COMM_ERR_CONNECT, errno); } diff --git a/src/forward.cc b/src/forward.cc index 6b276f659f..81a215da82 100644 --- a/src/forward.cc +++ b/src/forward.cc @@ -1,6 +1,6 @@ /* - * $Id: forward.cc,v 1.115 2003/11/09 11:05:24 hno Exp $ + * $Id: forward.cc,v 1.116 2004/04/03 14:07:39 hno Exp $ * * DEBUG: section 17 Request Forwarding * AUTHOR: Duane Wessels @@ -164,6 +164,9 @@ fwdCheckRetry(FwdState * fwdState) if (fwdState->n_tries > 10) return 0; + if (fwdState->origin_tries > 2) + return 0; + if (squid_curtime - fwdState->start > Config.Timeout.connect) return 0; @@ -220,6 +223,7 @@ fwdServerClosed(int fd, void *data) fwdState->server_fd = -1; if (fwdCheckRetry(fwdState)) { + int originserver = (fwdState->servers->_peer == NULL); debug(17, 3) ("fwdServerClosed: re-forwarding (%d tries, %d secs)\n", fwdState->n_tries, (int) (squid_curtime - fwdState->start)); @@ -241,11 +245,12 @@ fwdServerClosed(int fd, void *data) /* Use next. The last "direct" entry is retried multiple times */ fwdState->servers = fs->next; fwdServerFree(fs); + originserver = 0; } } - /* use eventAdd to break potential call sequence loops */ - eventAdd("fwdConnectStart", fwdConnectStart, fwdState, 0.0, 0); + /* use eventAdd to break potential call sequence loops and to slow things down a little */ + eventAdd("fwdConnectStart", fwdConnectStart, fwdState, originserver ? 0.05 : 0.005, 0); return; } @@ -589,8 +594,14 @@ fwdConnectStart(void *data) debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd); fwdState->server_fd = fd; fwdState->n_tries++; + + if (!fs->_peer) + fwdState->origin_tries++; + comm_add_close_handler(fd, fwdServerClosed, fwdState); + fwdDispatch(fwdState); + return; } } @@ -627,6 +638,10 @@ fwdConnectStart(void *data) fwdState->server_fd = fd; fwdState->n_tries++; + + if (!fs->_peer) + fwdState->origin_tries++; + /* * stats.conn_open is used to account for the number of * connections that we have open to the peer, so we can limit @@ -809,6 +824,9 @@ fwdReforward(FwdState * fwdState) if (fwdState->n_tries > 9) return 0; + if (fwdState->origin_tries > 1) + return 0; + if (fwdState->request->flags.body_sent) return 0; diff --git a/src/neighbors.cc b/src/neighbors.cc index 11c8227294..dac8c3dfdd 100644 --- a/src/neighbors.cc +++ b/src/neighbors.cc @@ -1,6 +1,6 @@ /* - * $Id: neighbors.cc,v 1.324 2003/10/16 21:40:16 robertc Exp $ + * $Id: neighbors.cc,v 1.325 2004/04/03 14:07:39 hno Exp $ * * DEBUG: section 15 Neighbor Routines * AUTHOR: Harvest Derived @@ -55,8 +55,7 @@ static void neighborAliveHtcp(peer *, const MemObject *, const htcpReplyData *); static void neighborCountIgnored(peer *); static void peerRefreshDNS(void *); static IPH peerDNSConfigure; -static void peerProbeConnect(peer *); -static IPH peerProbeConnect2; +static int peerProbeConnect(peer *); static CNCB peerProbeConnectDone; static void peerCountMcastPeersDone(void *data); static void peerCountMcastPeersStart(void *data); @@ -794,21 +793,17 @@ peerDigestLookup(peer * p, HttpRequest * request) if (!p->digest) { debug(15, 5) ("peerDigestLookup: gone!\n"); return LOOKUP_NONE; - } else if (!peerHTTPOkay(p, request)) { - debug(15, 5) ("peerDigestLookup: !peerHTTPOkay\n"); - return LOOKUP_NONE; - } else if (p->digest->flags.usable) { - debug(15, 5) ("peerDigestLookup: usable\n"); - /* fall through; put here to have common case on top */ - ; } else if (!p->digest->flags.needed) { debug(15, 5) ("peerDigestLookup: note need\n"); peerDigestNeeded(p->digest); return LOOKUP_NONE; - } else { + } else if (!p->digest->flags.usable) { debug(15, 5) ("peerDigestLookup: !ready && %srequested\n", p->digest->flags.requested ? "" : "!"); return LOOKUP_NONE; + } else if (!peerHTTPOkay(p, request)) { + debug(15, 5) ("peerDigestLookup: !peerHTTPOkay\n"); + return LOOKUP_NONE; } debug(15, 5) ("peerDigestLookup: OK to lookup peer %s\n", p->host); @@ -1230,8 +1225,8 @@ int neighborUp(const peer * p) { if (!p->tcp_up) { - peerProbeConnect((peer *) p); - return 0; + if (!peerProbeConnect((peer *) p)) + return 0; } if (p->options.no_query) @@ -1356,8 +1351,8 @@ peerRefreshDNS(void *data) eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 3600.0, 1); } -void -peerConnectFailed(peer * p) +static void +peerConnectFailedSilent(peer * p) { p->stats.last_connect_failure = squid_curtime; @@ -1366,7 +1361,6 @@ peerConnectFailed(peer * p) return; } - debug(15, 1) ("TCP connection to %s/%d failed\n", p->host, p->http_port); p->tcp_up--; if (!p->tcp_up) { @@ -1376,6 +1370,13 @@ peerConnectFailed(peer * p) } } +void +peerConnectFailed(peer *p) +{ + debug(15, 1) ("TCP connection to %s/%d failed\n", p->host, p->http_port); + peerConnectFailedSilent(p); +} + void peerConnectSucceded(peer * p) { @@ -1389,42 +1390,51 @@ peerConnectSucceded(peer * p) p->tcp_up = PEER_TCP_MAGIC_COUNT; } -/* - * peerProbeConnect will be called on dead peers by neighborUp - */ static void +peerProbeConnectTimeout(int fd, void *data) +{ + peer * p = (peer *)data; + comm_close(fd); + p->test_fd = -1; + peerConnectFailedSilent(p); +} + +/* +* peerProbeConnect will be called on dead peers by neighborUp +*/ +static int peerProbeConnect(peer * p) { int fd; + time_t ctimeout = p->connect_timeout > 0 ? p->connect_timeout + : Config.Timeout.peer_connect; + int ret = squid_curtime - p->stats.last_connect_failure > ctimeout * 10; if (p->test_fd != -1) - return; /* probe already running */ + return ret;/* probe already running */ - if (squid_curtime - p->stats.last_connect_probe < 1) - return; /* don't probe to often */ + if (squid_curtime - p->stats.last_connect_probe == 0) + return ret;/* don't probe to often */ fd = comm_open(SOCK_STREAM, IPPROTO_TCP, getOutgoingAddr(NULL), 0, COMM_NONBLOCKING, p->host); if (fd < 0) - return; + return ret; + + commSetTimeout(fd, ctimeout, peerProbeConnectTimeout, p); p->test_fd = fd; p->stats.last_connect_probe = squid_curtime; - ipcache_nbgethostbyname(p->host, peerProbeConnect2, p); -} - -static void -peerProbeConnect2(const ipcache_addrs * ianotused, void *data) -{ - peer *p = (peer *)data; commConnectStart(p->test_fd, p->host, p->http_port, peerProbeConnectDone, p); + + return ret; } static void @@ -1435,7 +1445,7 @@ peerProbeConnectDone(int fd, comm_err_t status, int xerrno, void *data) if (status == COMM_OK) { peerConnectSucceded(p); } else { - peerConnectFailed(p); + peerConnectFailedSilent(p); } comm_close(fd); diff --git a/src/structs.h b/src/structs.h index 86761ed5f5..69cd4ee34a 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1,6 +1,6 @@ /* - * $Id: structs.h,v 1.484 2003/10/20 12:33:01 robertc Exp $ + * $Id: structs.h,v 1.485 2004/04/03 14:07:39 hno Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -1841,6 +1841,7 @@ struct _FwdState ErrorState *err; time_t start; int n_tries; + int origin_tries; #if WIP_FWD_LOG http_status last_status;