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