]> git.ipfire.org Git - thirdparty/squid.git/blame - src/peer_select.cc
Bug 3610: peername_regex ACL
[thirdparty/squid.git] / src / peer_select.cc
CommitLineData
062e2281 1/*
262a0e14 2 * $Id$
062e2281 3 *
4 * DEBUG: section 44 Peer Selection Algorithm
5 * AUTHOR: Duane Wessels
6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 8 * ----------------------------------------------------------
062e2281 9 *
2b6662ba 10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
062e2281 18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
26ac0430 23 *
062e2281 24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
26ac0430 28 *
062e2281 29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
cbdec147 31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 32 *
062e2281 33 */
34
f7f3304a 35#include "squid-old.h"
cfd66529 36#include "DnsLookupDetails.h"
a37fdd8a 37#include "errorpage.h"
a553a5a3 38#include "event.h"
b24880fe 39#include "PeerSelectState.h"
e6ccf245 40#include "Store.h"
bbaf2685 41#include "hier_code.h"
cfd66529 42#include "ICP.h"
528b2c61 43#include "HttpRequest.h"
127dce76 44#include "acl/FilledChecklist.h"
924f73bc 45#include "htcp.h"
b6b6f466 46#include "forward.h"
cfd66529 47#include "SquidTime.h"
9b5c4a9a 48#include "icmp/net_db.h"
714e68b7 49#include "ipcache.h"
062e2281 50
26ac0430 51static struct {
75e88d56 52 int timeouts;
2fadd50d 53} PeerStats;
062e2281 54
26ac0430
AJ
55static const char *DirectStr[] = {
56 "DIRECT_UNKNOWN",
57 "DIRECT_NO",
58 "DIRECT_MAYBE",
59 "DIRECT_YES"
60};
75e88d56 61
f5b8bbc4 62static void peerSelectFoo(ps_state *);
63static void peerPingTimeout(void *data);
86aebcda 64static IRCB peerHandlePingReply;
f5b8bbc4 65static void peerSelectStateFree(ps_state * psstate);
66static void peerIcpParentMiss(peer *, icp_common_t *, ps_state *);
44e237d0 67#if USE_HTCP
68static void peerHtcpParentMiss(peer *, htcpReplyData *, ps_state *);
69static void peerHandleHtcpReply(peer *, peer_t, htcpReplyData *, void *);
70#endif
f5b8bbc4 71static int peerCheckNetdbDirect(ps_state * psstate);
db1cd23c 72static void peerGetSomeNeighbor(ps_state *);
73static void peerGetSomeNeighborReplies(ps_state *);
74static void peerGetSomeDirect(ps_state *);
75static void peerGetSomeParent(ps_state *);
168dfda9 76static void peerGetAllParents(ps_state *);
db1cd23c 77static void peerAddFwdServer(FwdServer **, peer *, hier_code);
d67acb4e 78static void peerSelectPinned(ps_state * ps);
cfd66529
AJ
79static void peerSelectDnsResults(const ipcache_addrs *ia, const DnsLookupDetails &details, void *data);
80
aa839030 81CBDATA_CLASS_INIT(ps_state);
82
348b2031 83static void
84peerSelectStateFree(ps_state * psstate)
85{
5ae21d99
AJ
86 if (psstate->entry) {
87 debugs(44, 3, HERE << psstate->entry->url());
88
89 if (psstate->entry->ping_status == PING_WAITING)
90 eventDelete(peerPingTimeout, psstate);
91
92 psstate->entry->ping_status = PING_DONE;
93 }
94
348b2031 95 if (psstate->acl_checklist) {
e0236918 96 debugs(44, DBG_IMPORTANT, "calling aclChecklistFree() from peerSelectStateFree");
62e76326 97 delete (psstate->acl_checklist);
348b2031 98 }
62e76326 99
6dd9f4bd 100 HTTPMSGUNLOCK(psstate->request);
62e76326 101
73a201f8 102 if (psstate->entry) {
62e76326 103 assert(psstate->entry->ping_status != PING_WAITING);
97b5e68f 104 psstate->entry->unlock();
62e76326 105 psstate->entry = NULL;
73a201f8 106 }
62e76326 107
a37fdd8a
AJ
108 delete psstate->lastError;
109
8407afee 110 cbdataFree(psstate);
348b2031 111}
062e2281 112
2d72d4fd 113static int
190154cf 114peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry)
062e2281 115{
7b665aeb 116 int n;
db1cd23c 117 assert(entry);
118 assert(entry->ping_status == PING_NONE);
9bd6a36d 119 assert(direct != DIRECT_YES);
bf8fe701 120 debugs(44, 3, "peerSelectIcpPing: " << entry->url() );
62e76326 121
92695e5e 122 if (!request->flags.hierarchical && direct != DIRECT_NO)
62e76326 123 return 0;
124
d46a87a8 125 if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys)
62e76326 126 if (direct != DIRECT_NO)
127 return 0;
128
7b665aeb 129 n = neighborsCount(request);
62e76326 130
bf8fe701 131 debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors");
62e76326 132
7b665aeb 133 return n;
062e2281 134}
135
136
062e2281 137void
00ae51e4 138peerSelect(Comm::ConnectionList * paths,
cfd66529 139 HttpRequest * request,
62e76326 140 StoreEntry * entry,
141 PSC * callback,
142 void *callback_data)
75e88d56 143{
28c60158 144 ps_state *psstate;
62e76326 145
86b389fc 146 if (entry)
bf8fe701 147 debugs(44, 3, "peerSelect: " << entry->url() );
86b389fc 148 else
60745f24 149 debugs(44, 3, "peerSelect: " << RequestMethodStr(request->method));
62e76326 150
b24880fe 151 psstate = new ps_state;
62e76326 152
6dd9f4bd 153 psstate->request = HTTPMSGLOCK(request);
62e76326 154
b6c0e933 155 psstate->entry = entry;
cfd66529
AJ
156 psstate->paths = paths;
157
b6c0e933 158 psstate->callback = callback;
62e76326 159
fa80a8ef 160 psstate->callback_data = cbdataReference(callback_data);
62e76326 161
6cfa8966 162#if USE_CACHE_DIGESTS
62e76326 163
39edba21 164 request->hier.peer_select_start = current_time;
62e76326 165
39edba21 166#endif
62e76326 167
2395cb21 168 if (psstate->entry)
cc192b50 169 psstate->entry->lock();
62e76326 170
b6c0e933 171 peerSelectFoo(psstate);
75e88d56 172}
173
174static void
2efeb0b7 175peerCheckNeverDirectDone(allow_t answer, void *data)
75e88d56 176{
e6ccf245 177 ps_state *psstate = (ps_state *) data;
348b2031 178 psstate->acl_checklist = NULL;
bf8fe701 179 debugs(44, 3, "peerCheckNeverDirectDone: " << answer);
2efeb0b7 180 psstate->never_direct = answer;
3985b3f8 181 switch (answer) {
ae026ec6 182 case ACCESS_ALLOWED:
f365d297
AJ
183 /** if never_direct says YES, do that. */
184 psstate->direct = DIRECT_NO;
ae026ec6
AJ
185 debugs(44, 3, HERE << "direct = " << DirectStr[psstate->direct] << " (never_direct allow)");
186 break;
187 case ACCESS_DENIED: // not relevant.
1f585949 188 case ACCESS_DUNNO: // not relevant.
ae026ec6 189 break;
1f585949 190 case ACCESS_AUTH_REQUIRED:
ae026ec6 191 debugs(44, DBG_IMPORTANT, "WARNING: never_direct resulted in " << answer << ". Username ACLs are not reliable here.");
1f585949 192 break;
ae026ec6 193 }
b6c0e933 194 peerSelectFoo(psstate);
75e88d56 195}
196
197static void
2efeb0b7 198peerCheckAlwaysDirectDone(allow_t answer, void *data)
75e88d56 199{
e6ccf245 200 ps_state *psstate = (ps_state *)data;
348b2031 201 psstate->acl_checklist = NULL;
bf8fe701 202 debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer);
2efeb0b7 203 psstate->always_direct = answer;
3985b3f8 204 switch (answer) {
ae026ec6
AJ
205 case ACCESS_ALLOWED:
206 /** if always_direct says YES, do that. */
207 psstate->direct = DIRECT_YES;
208 debugs(44, 3, HERE << "direct = " << DirectStr[psstate->direct] << " (always_direct allow)");
209 break;
210 case ACCESS_DENIED: // not relevant.
1f585949 211 case ACCESS_DUNNO: // not relevant.
ae026ec6 212 break;
1f585949 213 case ACCESS_AUTH_REQUIRED:
ae026ec6 214 debugs(44, DBG_IMPORTANT, "WARNING: always_direct resulted in " << answer << ". Username ACLs are not reliable here.");
1f585949 215 break;
ae026ec6 216 }
b6c0e933 217 peerSelectFoo(psstate);
75e88d56 218}
219
cfd66529
AJ
220void
221peerSelectDnsPaths(ps_state *psstate)
222{
223 FwdServer *fs = psstate->servers;
224
7177edfb
AJ
225 // Bug 3243: CVE 2009-0801
226 // Bypass of browser same-origin access control in intercepted communication
227 // To resolve this we must use only the original client destination when going DIRECT
228 // on intercepted traffic which failed Host verification
229 const HttpRequest *req = psstate->request;
230 const bool isIntercepted = !req->flags.redirected &&
231 (req->flags.intercepted || req->flags.spoof_client_ip);
232 const bool useOriginalDst = Config.onoff.client_dst_passthru || !req->flags.hostVerified;
233 const bool choseDirect = fs && fs->code == HIER_DIRECT;
234 if (isIntercepted && useOriginalDst && choseDirect) {
235 // construct a "result" adding the ORIGINAL_DST to the set instead of DIRECT
236 Comm::ConnectionPointer p = new Comm::Connection();
237 p->remote = req->clientConnectionManager->clientConnection->local;
238 p->peerType = fs->code;
239 p->setPeer(fs->_peer);
240
241 // check for a configured outgoing address for this destination...
242 getOutgoingAddress(psstate->request, p);
243 psstate->paths->push_back(p);
244
245 // clear the used fs and continue
246 psstate->servers = fs->next;
247 cbdataReferenceDone(fs->_peer);
248 memFree(fs, MEM_FWD_SERVER);
249 peerSelectDnsPaths(psstate);
250 return;
251 }
252
cfd66529 253 // convert the list of FwdServer destinations into destinations IP addresses
d6327017 254 if (fs && psstate->paths->size() < (unsigned int)Config.forward_max_tries) {
cfd66529
AJ
255 // send the next one off for DNS lookup.
256 const char *host = fs->_peer ? fs->_peer->host : psstate->request->GetHost();
3dfe8371 257 debugs(44, 2, "Find IP destination for: " << psstate->entry->url() << "' via " << host);
cfd66529
AJ
258 ipcache_nbgethostbyname(host, peerSelectDnsResults, psstate);
259 return;
260 }
261
c2f4e9cc
AJ
262 // Bug 3605: clear any extra listed FwdServer destinations, when the options exceeds max_foward_tries.
263 // due to the allocation method of fs, we must deallocate each manually.
264 // TODO: use a std::list so we can get the size and abort adding whenever the selection loops reach Config.forward_max_tries
265 if (fs && psstate->paths->size() >= (unsigned int)Config.forward_max_tries) {
9bc68187 266 while (fs) {
c2f4e9cc
AJ
267 FwdServer *next = fs->next;
268 cbdataReferenceDone(fs->_peer);
269 memFree(fs, MEM_FWD_SERVER);
270 fs = next;
271 }
272 }
273
cfd66529 274 // done with DNS lookups. pass back to caller
3dfe8371 275 PSC *callback = psstate->callback;
fa80a8ef 276 psstate->callback = NULL;
62e76326 277
5ae21d99 278 if (psstate->paths->size() < 1) {
ce63af7f
AJ
279 debugs(44, DBG_IMPORTANT, "Failed to select source for '" << psstate->entry->url() << "'");
280 debugs(44, DBG_IMPORTANT, " always_direct = " << psstate->always_direct);
281 debugs(44, DBG_IMPORTANT, " never_direct = " << psstate->never_direct);
282 debugs(44, DBG_IMPORTANT, " timedout = " << psstate->ping.timedout);
5ae21d99 283 } else {
ce63af7f
AJ
284 debugs(44, 2, "Found sources for '" << psstate->entry->url() << "'");
285 debugs(44, 2, " always_direct = " << psstate->always_direct);
286 debugs(44, 2, " never_direct = " << psstate->never_direct);
5db6bf73 287 for (size_t i = 0; i < psstate->paths->size(); ++i) {
ce63af7f
AJ
288 if ((*psstate->paths)[i]->peerType == HIER_DIRECT)
289 debugs(44, 2, " DIRECT = " << (*psstate->paths)[i]);
7177edfb
AJ
290 else if ((*psstate->paths)[i]->peerType == ORIGINAL_DST)
291 debugs(44, 2, " ORIGINAL_DST = " << (*psstate->paths)[i]);
292 else if ((*psstate->paths)[i]->peerType == PINNED)
293 debugs(44, 2, " PINNED = " << (*psstate->paths)[i]);
ce63af7f
AJ
294 else
295 debugs(44, 2, " cache_peer = " << (*psstate->paths)[i]);
efbd2436 296 }
ce63af7f 297 debugs(44, 2, " timedout = " << psstate->ping.timedout);
5ae21d99
AJ
298 }
299
300 psstate->ping.stop = current_time;
301 psstate->request->hier.ping = psstate->ping;
3dfe8371 302
cfd66529 303 void *cbdata;
fa80a8ef 304 if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) {
a37fdd8a
AJ
305 callback(psstate->paths, psstate->lastError, cbdata);
306 psstate->lastError = NULL; // FwdState has taken control over the ErrorState object.
db1cd23c 307 }
62e76326 308
348b2031 309 peerSelectStateFree(psstate);
93775f90 310}
311
cfd66529
AJ
312static void
313peerSelectDnsResults(const ipcache_addrs *ia, const DnsLookupDetails &details, void *data)
314{
315 ps_state *psstate = (ps_state *)data;
316
317 psstate->request->recordLookup(details);
318
319 FwdServer *fs = psstate->servers;
320 if (ia != NULL) {
321
322 assert(ia->cur < ia->count);
323
324 // loop over each result address, adding to the possible destinations.
cfd66529 325 int ip = ia->cur;
5db6bf73 326 for (int n = 0; n < ia->count; ++n, ++ip) {
5229395c
AJ
327 Comm::ConnectionPointer p;
328
cfd66529
AJ
329 if (ip >= ia->count) ip = 0; // looped back to zero.
330
27d1f0a0 331 // Enforce forward_max_tries configuration.
d6327017 332 if (psstate->paths->size() >= (unsigned int)Config.forward_max_tries)
27d1f0a0
AJ
333 break;
334
cfd66529
AJ
335 // for TPROXY we must skip unusable addresses.
336 if (psstate->request->flags.spoof_client_ip && !(fs->_peer && fs->_peer->options.no_tproxy) ) {
dc49061a 337 if (ia->in_addrs[n].IsIPv4() != psstate->request->client_addr.IsIPv4()) {
cfd66529
AJ
338 // we CAN'T spoof the address on this link. find another.
339 continue;
340 }
341 }
342
343 p = new Comm::Connection();
344 p->remote = ia->in_addrs[n];
cb443714
AJ
345 if (fs->_peer)
346 p->remote.SetPort(fs->_peer->http_port);
347 else
348 p->remote.SetPort(psstate->request->port);
5229395c 349 p->peerType = fs->code;
ce63af7f 350 p->setPeer(fs->_peer);
cfd66529
AJ
351
352 // check for a configured outgoing address for this destination...
353 getOutgoingAddress(psstate->request, p);
07f889c1 354 psstate->paths->push_back(p);
cfd66529
AJ
355 }
356 } else {
ceddefdd 357 debugs(44, 3, HERE << "Unknown host: " << (fs->_peer ? fs->_peer->host : psstate->request->GetHost()));
a37fdd8a
AJ
358 // discard any previous error.
359 delete psstate->lastError;
360 psstate->lastError = NULL;
361 if (fs->code == HIER_DIRECT) {
362 psstate->lastError = new ErrorState(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE, psstate->request);
363 psstate->lastError->dnsError = details.error;
364 }
cfd66529
AJ
365 }
366
367 psstate->servers = fs->next;
368 cbdataReferenceDone(fs->_peer);
369 memFree(fs, MEM_FWD_SERVER);
370
371 // see if more paths can be found
372 peerSelectDnsPaths(psstate);
373}
374
b3264694 375static int
376peerCheckNetdbDirect(ps_state * psstate)
377{
9b5c4a9a 378#if USE_ICMP
5f84d830 379 peer *p;
b3264694 380 int myrtt;
381 int myhops;
62e76326 382
0886a797 383 if (psstate->direct == DIRECT_NO)
62e76326 384 return 0;
385
9b5c4a9a
AJ
386 /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
387
cc192b50 388 myrtt = netdbHostRtt(psstate->request->GetHost());
62e76326 389
bf8fe701 390 debugs(44, 3, "peerCheckNetdbDirect: MY RTT = " << myrtt << " msec");
391 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_rtt = " << Config.minDirectRtt << " msec");
62e76326 392
5f84d830 393 if (myrtt && myrtt <= Config.minDirectRtt)
62e76326 394 return 1;
395
cc192b50 396 myhops = netdbHostHops(psstate->request->GetHost());
62e76326 397
bf8fe701 398 debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops);
399 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops);
62e76326 400
b3264694 401 if (myhops && myhops <= Config.minDirectHops)
62e76326 402 return 1;
403
cc192b50 404 p = whichPeer(psstate->closest_parent_miss);
62e76326 405
5f84d830 406 if (p == NULL)
62e76326 407 return 0;
408
bf8fe701 409 debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec");
62e76326 410
5f84d830 411 if (myrtt && myrtt <= psstate->ping.p_rtt)
62e76326 412 return 1;
413
9b5c4a9a
AJ
414#endif /* USE_ICMP */
415
b3264694 416 return 0;
417}
418
93775f90 419static void
511f47bb 420peerSelectFoo(ps_state * ps)
062e2281 421{
511f47bb 422 StoreEntry *entry = ps->entry;
190154cf 423 HttpRequest *request = ps->request;
60745f24 424 debugs(44, 3, "peerSelectFoo: '" << RequestMethodStr(request->method) << " " << request->GetHost() << "'");
62e76326 425
ae026ec6 426 /** If we don't know whether DIRECT is permitted ... */
511f47bb 427 if (ps->direct == DIRECT_UNKNOWN) {
ae026ec6
AJ
428 if (ps->always_direct == ACCESS_DUNNO) {
429 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (always_direct to be checked)");
b50e327b 430 /** check always_direct; */
ae026ec6 431 ps->acl_checklist = new ACLFilledChecklist(Config.accessList.AlwaysDirect, request, NULL);
b50e327b 432 ps->acl_checklist->nonBlockingCheck(peerCheckAlwaysDirectDone, ps);
62e76326 433 return;
ae026ec6
AJ
434 } else if (ps->never_direct == ACCESS_DUNNO) {
435 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (never_direct to be checked)");
b50e327b 436 /** check never_direct; */
ae026ec6
AJ
437 ps->acl_checklist = new ACLFilledChecklist(Config.accessList.NeverDirect, request, NULL);
438 ps->acl_checklist->nonBlockingCheck(peerCheckNeverDirectDone, ps);
62e76326 439 return;
7f7bdd96 440 } else if (request->flags.no_direct) {
b50e327b 441 /** if we are accelerating, direct is not an option. */
62e76326 442 ps->direct = DIRECT_NO;
ae026ec6 443 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (forced non-direct)");
62e76326 444 } else if (request->flags.loopdetect) {
b50e327b 445 /** if we are in a forwarding-loop, direct is not an option. */
62e76326 446 ps->direct = DIRECT_YES;
ae026ec6 447 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (forwarding loop detected)");
62e76326 448 } else if (peerCheckNetdbDirect(ps)) {
449 ps->direct = DIRECT_YES;
ae026ec6 450 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (checkNetdbDirect)");
62e76326 451 } else {
452 ps->direct = DIRECT_MAYBE;
ae026ec6 453 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (default)");
62e76326 454 }
455
bf8fe701 456 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
db1cd23c 457 }
62e76326 458
d67acb4e
AJ
459 if (!entry || entry->ping_status == PING_NONE)
460 peerSelectPinned(ps);
df503d6c 461 if (entry == NULL) {
62e76326 462 (void) 0;
df503d6c 463 } else if (entry->ping_status == PING_NONE) {
62e76326 464 peerGetSomeNeighbor(ps);
465
466 if (entry->ping_status == PING_WAITING)
467 return;
db1cd23c 468 } else if (entry->ping_status == PING_WAITING) {
62e76326 469 peerGetSomeNeighborReplies(ps);
470 entry->ping_status = PING_DONE;
db1cd23c 471 }
62e76326 472
168dfda9 473 switch (ps->direct) {
62e76326 474
168dfda9 475 case DIRECT_YES:
62e76326 476 peerGetSomeDirect(ps);
477 break;
478
168dfda9 479 case DIRECT_NO:
62e76326 480 peerGetSomeParent(ps);
481 peerGetAllParents(ps);
482 break;
483
168dfda9 484 default:
62e76326 485
486 if (Config.onoff.prefer_direct)
487 peerGetSomeDirect(ps);
488
8b9a89c9 489 if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct) {
62e76326 490 peerGetSomeParent(ps);
8b9a89c9
AJ
491 peerGetAllParents(ps);
492 }
62e76326 493
494 if (!Config.onoff.prefer_direct)
495 peerGetSomeDirect(ps);
496
497 break;
168dfda9 498 }
62e76326 499
3dfe8371
AJ
500 // resolve the possible peers
501 peerSelectDnsPaths(ps);
db1cd23c 502}
503
2efeb0b7 504bool peerAllowedToUse(const peer * p, HttpRequest * request);
cfd66529 505
27774cee 506/**
d67acb4e
AJ
507 * peerSelectPinned
508 *
27774cee 509 * Selects a pinned connection.
d67acb4e 510 */
d67acb4e
AJ
511static void
512peerSelectPinned(ps_state * ps)
513{
514 HttpRequest *request = ps->request;
d67acb4e
AJ
515 if (!request->pinnedConnection())
516 return;
e3a4aecc
AJ
517 peer *pear = request->pinnedConnection()->pinnedPeer();
518 if (Comm::IsConnOpen(request->pinnedConnection()->validatePinnedConnection(request, pear))) {
519 if (pear && peerAllowedToUse(pear, request)) {
520 peerAddFwdServer(&ps->servers, pear, PINNED);
d67acb4e
AJ
521 if (ps->entry)
522 ps->entry->ping_status = PING_DONE; /* Skip ICP */
e3a4aecc 523 } else if (!pear && ps->direct != DIRECT_NO) {
d67acb4e
AJ
524 peerAddFwdServer(&ps->servers, NULL, PINNED);
525 if (ps->entry)
526 ps->entry->ping_status = PING_DONE; /* Skip ICP */
527 }
528 }
529}
530
27774cee 531/**
db1cd23c 532 * peerGetSomeNeighbor
26ac0430 533 *
db1cd23c 534 * Selects a neighbor (parent or sibling) based on one of the
535 * following methods:
536 * Cache Digests
537 * CARP
9b5c4a9a 538 * ICMP Netdb RTT estimates
db1cd23c 539 * ICP/HTCP queries
540 */
541static void
542peerGetSomeNeighbor(ps_state * ps)
543{
544 StoreEntry *entry = ps->entry;
190154cf 545 HttpRequest *request = ps->request;
db1cd23c 546 peer *p;
547 hier_code code = HIER_NONE;
548 assert(entry->ping_status == PING_NONE);
62e76326 549
db1cd23c 550 if (ps->direct == DIRECT_YES) {
62e76326 551 entry->ping_status = PING_DONE;
552 return;
124511e5 553 }
62e76326 554
6cfa8966 555#if USE_CACHE_DIGESTS
f66a9ef4 556 if ((p = neighborsDigestSelect(request))) {
62e76326 557 if (neighborType(p, request) == PEER_PARENT)
558 code = CD_PARENT_HIT;
559 else
560 code = CD_SIBLING_HIT;
db1cd23c 561 } else
c127134a 562#endif
62e76326 563 if ((p = netdbClosestParent(request))) {
564 code = CLOSEST_PARENT;
565 } else if (peerSelectIcpPing(request, ps->direct, entry)) {
bf8fe701 566 debugs(44, 3, "peerSelect: Doing ICP pings");
62e76326 567 ps->ping.start = current_time;
568 ps->ping.n_sent = neighborsUdpPing(request,
569 entry,
570 peerHandlePingReply,
571 ps,
572 &ps->ping.n_replies_expected,
573 &ps->ping.timeout);
574
575 if (ps->ping.n_sent == 0)
fa84c01d 576 debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0");
26ac0430
AJ
577 debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected <<
578 " ICP replies expected, RTT " << ps->ping.timeout <<
579 " msec");
62e76326 580
62e76326 581
582 if (ps->ping.n_replies_expected > 0) {
583 entry->ping_status = PING_WAITING;
584 eventAdd("peerPingTimeout",
585 peerPingTimeout,
586 ps,
587 0.001 * ps->ping.timeout,
588 0);
589 return;
590 }
591 }
592
db1cd23c 593 if (code != HIER_NONE) {
62e76326 594 assert(p);
4b981814 595 debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << p->host);
62e76326 596 peerAddFwdServer(&ps->servers, p, code);
db1cd23c 597 }
62e76326 598
db1cd23c 599 entry->ping_status = PING_DONE;
600}
601
602/*
603 * peerGetSomeNeighborReplies
26ac0430 604 *
db1cd23c 605 * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
606 */
607static void
608peerGetSomeNeighborReplies(ps_state * ps)
609{
190154cf 610 HttpRequest *request = ps->request;
db1cd23c 611 peer *p = NULL;
612 hier_code code = HIER_NONE;
4c5a1592 613 assert(ps->entry->ping_status == PING_WAITING);
db1cd23c 614 assert(ps->direct != DIRECT_YES);
62e76326 615
db1cd23c 616 if (peerCheckNetdbDirect(ps)) {
62e76326 617 code = CLOSEST_DIRECT;
4b981814 618 debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << request->GetHost());
62e76326 619 peerAddFwdServer(&ps->servers, NULL, code);
620 return;
db1cd23c 621 }
62e76326 622
db1cd23c 623 if ((p = ps->hit)) {
62e76326 624 code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
26ac0430 625 } else {
cc192b50 626 if (!ps->closest_parent_miss.IsAnyAddr()) {
627 p = whichPeer(ps->closest_parent_miss);
628 code = CLOSEST_PARENT_MISS;
629 } else if (!ps->first_parent_miss.IsAnyAddr()) {
630 p = whichPeer(ps->first_parent_miss);
631 code = FIRST_PARENT_MISS;
632 }
633 }
db1cd23c 634 if (p && code != HIER_NONE) {
4b981814 635 debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << p->host);
62e76326 636 peerAddFwdServer(&ps->servers, p, code);
db1cd23c 637 }
638}
639
640
641/*
642 * peerGetSomeDirect
26ac0430 643 *
db1cd23c 644 * Simply adds a 'direct' entry to the FwdServers list if this
645 * request can be forwarded directly to the origin server
646 */
647static void
648peerGetSomeDirect(ps_state * ps)
649{
650 if (ps->direct == DIRECT_NO)
62e76326 651 return;
652
db80e881 653 /* WAIS is not implemented natively */
0c3d3f65 654 if (ps->request->protocol == AnyP::PROTO_WAIS)
26ac0430 655 return;
db80e881 656
657 peerAddFwdServer(&ps->servers, NULL, HIER_DIRECT);
db1cd23c 658}
659
660static void
661peerGetSomeParent(ps_state * ps)
662{
663 peer *p;
190154cf 664 HttpRequest *request = ps->request;
db1cd23c 665 hier_code code = HIER_NONE;
60745f24 666 debugs(44, 3, "peerGetSomeParent: " << RequestMethodStr(request->method) << " " << request->GetHost());
62e76326 667
6b8e7481 668 if (ps->direct == DIRECT_YES)
62e76326 669 return;
670
b4ab8666
AJ
671 if ((p = peerSourceHashSelectParent(request))) {
672 code = SOURCEHASH_PARENT;
2f1431ea 673#if USE_AUTH
f7e1d9ce
HN
674 } else if ((p = peerUserHashSelectParent(request))) {
675 code = USERHASH_PARENT;
2f1431ea 676#endif
b3995439 677 } else if ((p = carpSelectParent(request))) {
62e76326 678 code = CARP;
db1cd23c 679 } else if ((p = getRoundRobinParent(request))) {
62e76326 680 code = ROUNDROBIN_PARENT;
d1b63fc8 681 } else if ((p = getWeightedRoundRobinParent(request))) {
62e76326 682 code = ROUNDROBIN_PARENT;
db1cd23c 683 } else if ((p = getFirstUpParent(request))) {
62e76326 684 code = FIRSTUP_PARENT;
b4ab8666
AJ
685 } else if ((p = getDefaultParent(request))) {
686 code = DEFAULT_PARENT;
db1cd23c 687 }
62e76326 688
db1cd23c 689 if (code != HIER_NONE) {
4b981814 690 debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << p->host);
62e76326 691 peerAddFwdServer(&ps->servers, p, code);
062e2281 692 }
693}
694
168dfda9 695/* Adds alive parents. Used as a last resort for never_direct.
696 */
697static void
698peerGetAllParents(ps_state * ps)
699{
700 peer *p;
190154cf 701 HttpRequest *request = ps->request;
168dfda9 702 /* Add all alive parents */
62e76326 703
168dfda9 704 for (p = Config.peers; p; p = p->next) {
62e76326 705 /* XXX: neighbors.c lacks a public interface for enumerating
706 * parents to a request so we have to dig some here..
707 */
708
709 if (neighborType(p, request) != PEER_PARENT)
710 continue;
711
712 if (!peerHTTPOkay(p, request))
713 continue;
714
bf8fe701 715 debugs(15, 3, "peerGetAllParents: adding alive parent " << p->host);
62e76326 716
717 peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT);
168dfda9 718 }
62e76326 719
168dfda9 720 /* XXX: should add dead parents here, but it is currently
721 * not possible to find out which parents are dead or which
722 * simply are not configured to handle the request.
723 */
724 /* Add default parent as a last resort */
725 if ((p = getDefaultParent(request))) {
62e76326 726 peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT);
168dfda9 727 }
728}
729
d9586c3c 730static void
93775f90 731peerPingTimeout(void *data)
062e2281 732{
e6ccf245 733 ps_state *psstate = (ps_state *)data;
b6c0e933 734 StoreEntry *entry = psstate->entry;
62e76326 735
fefb0227 736 if (entry)
bf8fe701 737 debugs(44, 3, "peerPingTimeout: '" << entry->url() << "'" );
62e76326 738
fa80a8ef 739 if (!cbdataReferenceValid(psstate->callback_data)) {
62e76326 740 /* request aborted */
741 entry->ping_status = PING_DONE;
742 cbdataReferenceDone(psstate->callback_data);
743 peerSelectStateFree(psstate);
744 return;
73a201f8 745 }
62e76326 746
5db6bf73 747 ++PeerStats.timeouts;
44e237d0 748 psstate->ping.timedout = 1;
b6c0e933 749 peerSelectFoo(psstate);
85034133 750}
751
752void
753peerSelectInit(void)
754{
75e88d56 755 memset(&PeerStats, '\0', sizeof(PeerStats));
cfd66529 756 memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
062e2281 757}
93775f90 758
b3264694 759static void
760peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps)
761{
762 int rtt;
62e76326 763
9b5c4a9a 764#if USE_ICMP
b3264694 765 if (Config.onoff.query_icmp) {
62e76326 766 if (header->flags & ICP_FLAG_SRC_RTT) {
767 rtt = header->pad & 0xFFFF;
9b5c4a9a 768 int hops = (header->pad >> 16) & 0xFFFF;
62e76326 769
770 if (rtt > 0 && rtt < 0xFFFF)
771 netdbUpdatePeer(ps->request, p, rtt, hops);
772
773 if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
774 ps->closest_parent_miss = p->in_addr;
775 ps->ping.p_rtt = rtt;
776 }
777 }
b3264694 778 }
9b5c4a9a 779#endif /* USE_ICMP */
62e76326 780
18ec72b2 781 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
cd196bc8 782 if (p->options.closest_only)
62e76326 783 return;
784
85223cd7 785 /* set FIRST_MISS if there is no CLOSEST parent */
cc192b50 786 if (!ps->closest_parent_miss.IsAnyAddr())
62e76326 787 return;
788
d1b63fc8 789 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
62e76326 790
d1b63fc8 791 if (rtt < 1)
62e76326 792 rtt = 1;
793
cc192b50 794 if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
62e76326 795 ps->first_parent_miss = p->in_addr;
796 ps->ping.w_rtt = rtt;
b3264694 797 }
798}
93775f90 799
b6c0e933 800static void
b3264694 801peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data)
93775f90 802{
e6ccf245 803 ps_state *psstate = (ps_state *)data;
804 icp_opcode op = header->getOpCode();
bf8fe701 805 debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->entry->url() );
69c95dd3 806#if USE_CACHE_DIGESTS && 0
26b164ac 807 /* do cd lookup to count false misses */
62e76326 808
3ab66981 809 if (p && request)
62e76326 810 peerNoteDigestLookup(request, p,
811 peerDigestLookup(p, request, psstate->entry));
812
26b164ac 813#endif
62e76326 814
5db6bf73 815 ++ psstate->ping.n_recv;
62e76326 816
27cd7235 817 if (op == ICP_MISS || op == ICP_DECHO) {
62e76326 818 if (type == PEER_PARENT)
819 peerIcpParentMiss(p, header, psstate);
a7c05555 820 } else if (op == ICP_HIT) {
62e76326 821 psstate->hit = p;
822 psstate->hit_type = type;
823 peerSelectFoo(psstate);
824 return;
db1cd23c 825 }
62e76326 826
44e237d0 827 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
62e76326 828 return;
829
b6c0e933 830 peerSelectFoo(psstate);
93775f90 831}
86aebcda 832
833#if USE_HTCP
834static void
44e237d0 835peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data)
86aebcda 836{
e6ccf245 837 ps_state *psstate = (ps_state *)data;
26ac0430
AJ
838 debugs(44, 3, "peerHandleHtcpReply: " <<
839 (htcp->hit ? "HIT" : "MISS") << " " <<
840 psstate->entry->url() );
5db6bf73 841 ++ psstate->ping.n_recv;
62e76326 842
44e237d0 843 if (htcp->hit) {
62e76326 844 psstate->hit = p;
845 psstate->hit_type = type;
846 peerSelectFoo(psstate);
847 return;
44e237d0 848 }
62e76326 849
44e237d0 850 if (type == PEER_PARENT)
62e76326 851 peerHtcpParentMiss(p, htcp, psstate);
852
44e237d0 853 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
62e76326 854 return;
855
44e237d0 856 peerSelectFoo(psstate);
857}
858
859static void
860peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps)
861{
862 int rtt;
62e76326 863
9b5c4a9a 864#if USE_ICMP
44e237d0 865 if (Config.onoff.query_icmp) {
62e76326 866 if (htcp->cto.rtt > 0) {
867 rtt = (int) htcp->cto.rtt * 1000;
9b5c4a9a 868 int hops = (int) htcp->cto.hops * 1000;
62e76326 869 netdbUpdatePeer(ps->request, p, rtt, hops);
870
871 if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
872 ps->closest_parent_miss = p->in_addr;
873 ps->ping.p_rtt = rtt;
874 }
875 }
44e237d0 876 }
9b5c4a9a 877#endif /* USE_ICMP */
62e76326 878
44e237d0 879 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
cd196bc8 880 if (p->options.closest_only)
62e76326 881 return;
882
44e237d0 883 /* set FIRST_MISS if there is no CLOSEST parent */
cc192b50 884 if (!ps->closest_parent_miss.IsAnyAddr())
62e76326 885 return;
886
d1b63fc8 887 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
62e76326 888
d1b63fc8 889 if (rtt < 1)
62e76326 890 rtt = 1;
891
cc192b50 892 if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
62e76326 893 ps->first_parent_miss = p->in_addr;
894 ps->ping.w_rtt = rtt;
44e237d0 895 }
86aebcda 896}
62e76326 897
86aebcda 898#endif
899
900static void
0c3d3f65 901peerHandlePingReply(peer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data)
86aebcda 902{
0c3d3f65 903 if (proto == AnyP::PROTO_ICP)
62e76326 904 peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data);
905
86aebcda 906#if USE_HTCP
62e76326 907
0c3d3f65 908 else if (proto == AnyP::PROTO_HTCP)
62e76326 909 peerHandleHtcpReply(p, type, (htcpReplyData *)pingdata, data);
910
86aebcda 911#endif
62e76326 912
86aebcda 913 else
e0236918 914 debugs(44, DBG_IMPORTANT, "peerHandlePingReply: unknown protocol " << proto);
86aebcda 915}
db1cd23c 916
917static void
b73f343e 918peerAddFwdServer(FwdServer ** FSVR, peer * p, hier_code code)
db1cd23c 919{
e6ccf245 920 FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER);
26ac0430
AJ
921 debugs(44, 5, "peerAddFwdServer: adding " <<
922 (p ? p->host : "DIRECT") << " " <<
4b981814 923 hier_code_str[code] );
29b8d8d6 924 fs->_peer = cbdataReference(p);
db1cd23c 925 fs->code = code;
62e76326 926
b73f343e 927 while (*FSVR)
928 FSVR = &(*FSVR)->next;
62e76326 929
b73f343e 930 *FSVR = fs;
db1cd23c 931}
b24880fe 932
933void *
934ps_state::operator new(size_t)
935{
aa839030 936 CBDATA_INIT_TYPE(ps_state);
b24880fe 937 return cbdataAlloc(ps_state);
938}
939
940ps_state::ps_state() : request (NULL),
941 entry (NULL),
ae026ec6
AJ
942 always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED),
943 never_direct(Config.accessList.NeverDirect?ACCESS_DUNNO:ACCESS_DENIED),
944 direct(DIRECT_UNKNOWN),
b24880fe 945 callback (NULL),
946 callback_data (NULL),
a37fdd8a 947 lastError(NULL),
b24880fe 948 servers (NULL),
cc192b50 949 first_parent_miss(),
950 closest_parent_miss(),
b24880fe 951 hit(NULL),
952 hit_type(PEER_NONE),
b24880fe 953 acl_checklist (NULL)
954{
cc192b50 955 ; // no local defaults.
b24880fe 956}
957
958ping_data::ping_data() :
959 n_sent(0),
960 n_recv(0),
961 n_replies_expected(0),
962 timeout(0),
963 timedout(0),
964 w_rtt(0),
965 p_rtt(0)
966{
967 start.tv_sec = 0;
968 start.tv_usec = 0;
969 stop.tv_sec = 0;
970 stop.tv_usec = 0;
971}