]> git.ipfire.org Git - thirdparty/squid.git/blame - src/neighbors.cc
- added MEM_CACHE_DIGEST and MEM_DIGEST_FETCH_STATE pools
[thirdparty/squid.git] / src / neighbors.cc
CommitLineData
22f3fd98 1
30a4f2a8 2/*
aee4e794 3 * $Id: neighbors.cc,v 1.193 1998/04/06 22:24:36 wessels Exp $
30a4f2a8 4 *
5 * DEBUG: section 15 Neighbor Routines
6 * AUTHOR: Harvest Derived
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
4d64d74a 30 */
019dd986 31
32/*
30a4f2a8 33 * Copyright (c) 1994, 1995. All rights reserved.
34 *
35 * The Harvest software was developed by the Internet Research Task
36 * Force Research Group on Resource Discovery (IRTF-RD):
37 *
38 * Mic Bowman of Transarc Corporation.
39 * Peter Danzig of the University of Southern California.
40 * Darren R. Hardy of the University of Colorado at Boulder.
41 * Udi Manber of the University of Arizona.
42 * Michael F. Schwartz of the University of Colorado at Boulder.
43 * Duane Wessels of the University of Colorado at Boulder.
44 *
45 * This copyright notice applies to software in the Harvest
46 * ``src/'' directory only. Users should consult the individual
47 * copyright notices in the ``components/'' subdirectories for
48 * copyright information about other software bundled with the
49 * Harvest source code distribution.
50 *
51 * TERMS OF USE
52 *
53 * The Harvest software may be used and re-distributed without
54 * charge, provided that the software origin and research team are
55 * cited in any use of the system. Most commonly this is
56 * accomplished by including a link to the Harvest Home Page
57 * (http://harvest.cs.colorado.edu/) from the query page of any
58 * Broker you deploy, as well as in the query result pages. These
59 * links are generated automatically by the standard Broker
60 * software distribution.
61 *
62 * The Harvest software is provided ``as is'', without express or
63 * implied warranty, and with no support nor obligation to assist
64 * in its use, correction, modification or enhancement. We assume
65 * no liability with respect to the infringement of copyrights,
66 * trade secrets, or any patents, and are not responsible for
67 * consequential damages. Proper use of the Harvest software is
68 * entirely the responsibility of the user.
69 *
70 * DERIVATIVE WORKS
71 *
72 * Users may make derivative works from the Harvest software, subject
73 * to the following constraints:
74 *
75 * - You must include the above copyright notice and these
76 * accompanying paragraphs in all forms of derivative works,
77 * and any documentation and other materials related to such
78 * distribution and use acknowledge that the software was
79 * developed at the above institutions.
80 *
81 * - You must notify IRTF-RD regarding your distribution of
82 * the derivative work.
83 *
84 * - You must clearly notify users that your are distributing
85 * a modified version and not the original Harvest software.
86 *
87 * - Any derivative product is also subject to these copyright
88 * and use restrictions.
89 *
90 * Note that the Harvest software is NOT in the public domain. We
91 * retain copyright, as specified above.
92 *
93 * HISTORY OF FREE SOFTWARE STATUS
94 *
95 * Originally we required sites to license the software in cases
96 * where they were going to build commercial products/services
97 * around Harvest. In June 1995 we changed this policy. We now
98 * allow people to use the core Harvest software (the code found in
99 * the Harvest ``src/'' directory) for free. We made this change
100 * in the interest of encouraging the widest possible deployment of
101 * the technology. The Harvest software is really a reference
102 * implementation of a set of protocols and formats, some of which
103 * we intend to standardize. We encourage commercial
104 * re-implementations of code complying to this set of standards.
019dd986 105 */
090089c4 106
44a47c6e 107#include "squid.h"
090089c4 108
429fdbec 109/* count mcast group peers every 15 minutes */
110#define MCAST_COUNT_RATE 900
111
f5b8bbc4 112static int peerAllowedToUse(const peer *, request_t *);
113static int peerHTTPOkay(const peer *, request_t *);
114static int peerWouldBePinged(const peer *, request_t *);
115static void neighborRemove(peer *);
ea3a2a69 116static peer *whichPeer(const struct sockaddr_in *from);
f5b8bbc4 117static void neighborAlive(peer *, const MemObject *, const icp_common_t *);
118static void neighborCountIgnored(peer *, icp_opcode op_unused);
119static void peerRefreshDNS(void *);
b69f7771 120static IPH peerDNSConfigure;
f5b8bbc4 121static void peerCheckConnect(void *);
b69f7771 122static IPH peerCheckConnect2;
603a02fd 123static CNCB peerCheckConnectDone;
f5b8bbc4 124static void peerCountMcastPeersDone(void *data);
125static void peerCountMcastPeersStart(void *data);
126static void peerCountMcastPeersSchedule(peer * p, time_t when);
b3264694 127static IRCB peerCountHandleIcpReply;
f5b8bbc4 128static void neighborIgnoreNonPeer(const struct sockaddr_in *, icp_opcode);
ed7f5615 129static OBJH neighborDumpPeers;
130static OBJH neighborDumpNonPeers;
131static void dump_peers(StoreEntry * sentry, peer * peers);
090089c4 132
090089c4 133static icp_common_t echo_hdr;
30a4f2a8 134static u_short echo_port;
30a4f2a8 135
c7a3724d 136static int NLateReplies = 0;
40a1495e 137static peer *first_ping = NULL;
28070024 138
c7a3724d 139char *
b69f7771 140neighborTypeStr(const peer * p)
69a71bd7 141{
e102ebda 142 if (p->type == PEER_NONE)
143 return "Non-Peer";
b69f7771 144 if (p->type == PEER_SIBLING)
69a71bd7 145 return "Sibling";
b69f7771 146 if (p->type == PEER_MULTICAST)
7b3bd12c 147 return "Multicast Group";
69a71bd7 148 return "Parent";
149}
150
090089c4 151
deb79f06 152static peer *
153whichPeer(const struct sockaddr_in *from)
090089c4 154{
22e4fa85 155 int j;
4eda6afe 156 u_short port = ntohs(from->sin_port);
157 struct in_addr ip = from->sin_addr;
b69f7771 158 peer *p = NULL;
a3d5953d 159 debug(15, 3) ("whichPeer: from %s port %d\n", inet_ntoa(ip), port);
40a1495e 160 for (p = Config.peers; p; p = p->next) {
b69f7771 161 for (j = 0; j < p->n_addresses; j++) {
162 if (ip.s_addr == p->addresses[j].s_addr && port == p->icp_port) {
163 return p;
090089c4 164 }
165 }
166 }
4eda6afe 167 return NULL;
090089c4 168}
169
b6c0e933 170static peer_t
b69f7771 171neighborType(const peer * p, const request_t * request)
24a1003d 172{
b012353a 173 const struct _domain_type *d = NULL;
b69f7771 174 for (d = p->typelist; d; d = d->next) {
24a1003d 175 if (matchDomainName(d->domain, request->host))
deb79f06 176 if (d->type != PEER_NONE)
b012353a 177 return d->type;
24a1003d 178 }
b69f7771 179 return p->type;
24a1003d 180}
181
7b3bd12c 182/*
62fd6124 183 * peerAllowedToUse
7b3bd12c 184 *
dd4bd44e 185 * this function figures out if it is appropriate to fetch REQUEST
62fd6124 186 * from PEER.
7b3bd12c 187 */
b8d8561b 188static int
b69f7771 189peerAllowedToUse(const peer * p, request_t * request)
090089c4 190{
b012353a 191 const struct _domain_ping *d = NULL;
090089c4 192 int do_ping = 1;
0ee4272b 193 const struct _acl_list *a = NULL;
f88bb09c 194 aclCheck_t checklist;
ce66013b 195 assert(request != NULL);
79a15e0a 196 if (EBIT_TEST(request->flags, REQ_NOCACHE))
b69f7771 197 if (neighborType(p, request) == PEER_SIBLING)
24a1003d 198 return 0;
79a15e0a 199 if (EBIT_TEST(request->flags, REQ_REFRESH))
b69f7771 200 if (neighborType(p, request) == PEER_SIBLING)
d950cec8 201 return 0;
b69f7771 202 if (p->pinglist == NULL && p->acls == NULL)
090089c4 203 return do_ping;
090089c4 204 do_ping = 0;
b69f7771 205 for (d = p->pinglist; d; d = d->next) {
130faaed 206 if (matchDomainName(d->domain, request->host)) {
207 do_ping = d->do_ping;
208 break;
209 }
30a4f2a8 210 do_ping = !d->do_ping;
211 }
130faaed 212 if (0 == do_ping)
213 return do_ping;
8ae8bb05 214 checklist.src_addr = request->client_addr;
f88bb09c 215 checklist.request = request;
b69f7771 216 for (a = p->acls; a; a = a->next) {
130faaed 217 if (aclMatchAcl(a->acl, &checklist)) {
218 do_ping = a->op;
219 break;
220 }
30a4f2a8 221 do_ping = !a->op;
090089c4 222 }
223 return do_ping;
224}
225
62fd6124 226/* Return TRUE if it is okay to send an ICP request to this peer. */
227static int
b69f7771 228peerWouldBePinged(const peer * p, request_t * request)
62fd6124 229{
b69f7771 230 if (!peerAllowedToUse(p, request))
62fd6124 231 return 0;
a369131d 232 if (EBIT_TEST(p->options, NEIGHBOR_NO_QUERY))
62fd6124 233 return 0;
a369131d 234 if (EBIT_TEST(p->options, NEIGHBOR_MCAST_RESPONDER))
62fd6124 235 return 0;
236 /* the case below seems strange, but can happen if the
237 * URL host is on the other side of a firewall */
b69f7771 238 if (p->type == PEER_SIBLING)
79a15e0a 239 if (!EBIT_TEST(request->flags, REQ_HIERARCHICAL))
62fd6124 240 return 0;
b69f7771 241 if (p->icp_port == echo_port)
242 if (!neighborUp(p))
62fd6124 243 return 0;
b69f7771 244 if (p->n_addresses == 0)
dd4bd44e 245 return 0;
62fd6124 246 return 1;
247}
248
249/* Return TRUE if it is okay to send an HTTP request to this peer. */
250static int
b69f7771 251peerHTTPOkay(const peer * p, request_t * request)
62fd6124 252{
b69f7771 253 if (!peerAllowedToUse(p, request))
62fd6124 254 return 0;
b69f7771 255 if (!neighborUp(p))
62fd6124 256 return 0;
257 return 1;
258}
259
3c6e634f 260int
261neighborsCount(request_t * request)
090089c4 262{
b69f7771 263 peer *p = NULL;
090089c4 264 int count = 0;
40a1495e 265 for (p = Config.peers; p; p = p->next)
b69f7771 266 if (peerWouldBePinged(p, request))
3c6e634f 267 count++;
a3d5953d 268 debug(15, 3) ("neighborsCount: %d\n", count);
3c6e634f 269 return count;
270}
090089c4 271
deb79f06 272peer *
3c6e634f 273getSingleParent(request_t * request)
274{
deb79f06 275 peer *p = NULL;
b69f7771 276 peer *q = NULL;
40a1495e 277 for (q = Config.peers; q; q = q->next) {
b69f7771 278 if (!peerHTTPOkay(q, request))
caebbe00 279 continue;
b69f7771 280 if (neighborType(q, request) != PEER_PARENT)
3c6e634f 281 return NULL; /* oops, found SIBLING */
282 if (p)
283 return NULL; /* oops, found second parent */
b69f7771 284 p = q;
090089c4 285 }
a369131d 286 if (p != NULL && !EBIT_TEST(p->options, NEIGHBOR_NO_QUERY))
287 return NULL;
a3d5953d 288 debug(15, 3) ("getSingleParent: returning %s\n", p ? p->host : "NULL");
3c6e634f 289 return p;
090089c4 290}
291
deb79f06 292peer *
b8d8561b 293getFirstUpParent(request_t * request)
090089c4 294{
b69f7771 295 peer *p = NULL;
40a1495e 296 for (p = Config.peers; p; p = p->next) {
b69f7771 297 if (!neighborUp(p))
30a4f2a8 298 continue;
b69f7771 299 if (neighborType(p, request) != PEER_PARENT)
090089c4 300 continue;
b69f7771 301 if (!peerHTTPOkay(p, request))
e924600d 302 continue;
303 break;
090089c4 304 }
a3d5953d 305 debug(15, 3) ("getFirstUpParent: returning %s\n", p ? p->host : "NULL");
b69f7771 306 return p;
090089c4 307}
308
deb79f06 309peer *
48b38d01 310getRoundRobinParent(request_t * request)
311{
b69f7771 312 peer *p;
313 peer *q = NULL;
40a1495e 314 for (p = Config.peers; p; p = p->next) {
79a15e0a 315 if (!EBIT_TEST(p->options, NEIGHBOR_ROUNDROBIN))
0c8177e6 316 continue;
b69f7771 317 if (neighborType(p, request) != PEER_PARENT)
0c8177e6 318 continue;
b69f7771 319 if (!peerHTTPOkay(p, request))
48b38d01 320 continue;
b69f7771 321 if (q && q->rr_count < p->rr_count)
48b38d01 322 continue;
b69f7771 323 q = p;
48b38d01 324 }
b69f7771 325 if (q)
326 q->rr_count++;
a3d5953d 327 debug(15, 3) ("getRoundRobinParent: returning %s\n", q ? q->host : "NULL");
b69f7771 328 return q;
48b38d01 329}
330
deb79f06 331peer *
5269d0bd 332getDefaultParent(request_t * request)
333{
b69f7771 334 peer *p = NULL;
40a1495e 335 for (p = Config.peers; p; p = p->next) {
b69f7771 336 if (neighborType(p, request) != PEER_PARENT)
5269d0bd 337 continue;
79a15e0a 338 if (!EBIT_TEST(p->options, NEIGHBOR_DEFAULT_PARENT))
5269d0bd 339 continue;
b69f7771 340 if (!peerHTTPOkay(p, request))
5269d0bd 341 continue;
a3d5953d 342 debug(15, 3) ("getDefaultParent: returning %s\n", p->host);
b69f7771 343 return p;
5269d0bd 344 }
345 return NULL;
346}
347
deb79f06 348peer *
b69f7771 349getNextPeer(peer * p)
090089c4 350{
b69f7771 351 return p->next;
090089c4 352}
353
deb79f06 354peer *
355getFirstPeer(void)
090089c4 356{
40a1495e 357 return Config.peers;
090089c4 358}
359
b8d8561b 360static void
deb79f06 361neighborRemove(peer * target)
30a4f2a8 362{
b69f7771 363 peer *p = NULL;
364 peer **P = NULL;
40a1495e 365 p = Config.peers;
366 P = &Config.peers;
b69f7771 367 while (p) {
368 if (target == p)
30a4f2a8 369 break;
b69f7771 370 P = &p->next;
371 p = p->next;
372 }
373 if (p) {
374 *P = p->next;
b69f7771 375 peerDestroy(p);
40a1495e 376 Config.npeers--;
4d64d74a 377 }
40a1495e 378 first_ping = Config.peers;
4d64d74a 379}
380
b8d8561b 381void
382neighbors_open(int fd)
090089c4 383{
30a4f2a8 384 struct sockaddr_in name;
30a4f2a8 385 int len = sizeof(struct sockaddr_in);
30a4f2a8 386 struct servent *sep = NULL;
30a4f2a8 387 memset(&name, '\0', sizeof(struct sockaddr_in));
388 if (getsockname(fd, (struct sockaddr *) &name, &len) < 0)
a3d5953d 389 debug(15, 1) ("getsockname(%d,%p,%p) failed.\n", fd, &name, &len);
dd4bd44e 390 peerRefreshDNS(NULL);
30a4f2a8 391 if (0 == echo_hdr.opcode) {
27cd7235 392 echo_hdr.opcode = ICP_SECHO;
30a4f2a8 393 echo_hdr.version = ICP_VERSION_CURRENT;
394 echo_hdr.length = 0;
395 echo_hdr.reqnum = 0;
396 echo_hdr.flags = 0;
397 echo_hdr.pad = 0;
30a4f2a8 398 echo_hdr.shostid = name.sin_addr.s_addr;
399 sep = getservbyname("echo", "udp");
400 echo_port = sep ? ntohs((u_short) sep->s_port) : 7;
090089c4 401 }
c68e4e90 402 first_ping = Config.peers;
ed7f5615 403 cachemgrRegister("server_list",
404 "Peer Cache Statistics",
405 neighborDumpPeers, 0);
22f3fd98 406 cachemgrRegister("non_peers",
407 "List of Unknown sites sending ICP messages",
408 neighborDumpNonPeers, 0);
090089c4 409}
410
b8d8561b 411int
b6c0e933 412neighborsUdpPing(request_t * request,
641941c0 413 StoreEntry * entry,
582b6456 414 IRCB * callback,
641941c0 415 void *callback_data,
416 int *exprep)
090089c4 417{
9fb13bb6 418 const char *url = storeUrl(entry);
b6c0e933 419 MemObject *mem = entry->mem_obj;
b69f7771 420 peer *p = NULL;
090089c4 421 int i;
1061b406 422 int reqnum = 0;
6d2296d4 423 int flags;
9dee2904 424 icp_common_t *query;
429fdbec 425 int queries_sent = 0;
0a0bf5db 426 int peers_pinged = 0;
090089c4 427
40a1495e 428 if (Config.peers == NULL)
090089c4 429 return 0;
ce66013b 430 if (theOutIcpConnection < 0)
431 fatal("neighborsUdpPing: There is no ICP socket!");
9fb13bb6 432 assert(entry->swap_status == SWAPOUT_NONE);
b6c0e933 433 mem->start_ping = current_time;
434 mem->icp_reply_callback = callback;
435 mem->ircb_data = callback_data;
40a1495e 436 for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) {
b69f7771 437 if (p == NULL)
40a1495e 438 p = Config.peers;
a3d5953d 439 debug(15, 5) ("neighborsUdpPing: Peer %s\n", p->host);
b69f7771 440 if (!peerWouldBePinged(p, request))
deb79f06 441 continue; /* next peer */
0a0bf5db 442 peers_pinged++;
a3d5953d 443 debug(15, 4) ("neighborsUdpPing: pinging peer %s for '%s'\n",
b69f7771 444 p->host, url);
445 if (p->type == PEER_MULTICAST)
03a1ee42 446 mcastSetTtl(theOutIcpConnection, p->mcast.ttl);
9fb13bb6 447 reqnum = mem->reqnum;
3a4a63e7 448 debug(15, 3) ("neighborsUdpPing: key = '%s'\n", storeKeyText(entry->key));
a3d5953d 449 debug(15, 3) ("neighborsUdpPing: reqnum = %d\n", reqnum);
090089c4 450
dc9d133b 451#if USE_HTCP
e8d185d2 452 if (EBIT_TEST(p->options, NEIGHBOR_HTCP)) {
1afe05c5 453 debug(15, 0) ("neighborsUdpPing: sending HTCP query\n");
176ae152 454 htcpQuery(entry, request, p);
dc9d133b 455 } else
456#endif
1afe05c5 457 if (p->icp_port == echo_port) {
a3d5953d 458 debug(15, 4) ("neighborsUdpPing: Looks like a dumb cache, send DECHO ping\n");
1061b406 459 echo_hdr.reqnum = reqnum;
27cd7235 460 query = icpCreateMessage(ICP_DECHO, 0, url, reqnum, 0);
af00901c 461 icpUdpSend(theOutIcpConnection,
b69f7771 462 &p->in_addr,
9dee2904 463 query,
a528eb87 464 LOG_TAG_NONE,
aee4e794 465 0);
090089c4 466 } else {
6d2296d4 467 flags = 0;
17a0a4ee 468 if (Config.onoff.query_icmp)
b69f7771 469 if (p->icp_version == ICP_VERSION_2)
429fdbec 470 flags |= ICP_FLAG_SRC_RTT;
27cd7235 471 query = icpCreateMessage(ICP_QUERY, flags, url, reqnum, 0);
af00901c 472 icpUdpSend(theOutIcpConnection,
b69f7771 473 &p->in_addr,
9dee2904 474 query,
a528eb87 475 LOG_TAG_NONE,
aee4e794 476 0);
090089c4 477 }
429fdbec 478 queries_sent++;
090089c4 479
b69f7771 480 p->stats.pings_sent++;
b69f7771 481 if (p->type == PEER_MULTICAST) {
dc835977 482 /*
483 * set a bogus last_reply time so neighborUp() never
484 * says a multicast peer is dead.
485 */
486 p->stats.last_reply = squid_curtime;
b69f7771 487 (*exprep) += p->mcast.n_replies_expected;
488 } else if (neighborUp(p)) {
1061b406 489 /* its alive, expect a reply from it */
b6c0e933 490 (*exprep)++;
ae95b16f 491 } else if (squid_curtime - p->stats.last_query > Config.Timeout.deadPeer) {
492 /*
493 * fake a recent reply if its been a long time since our
494 * last query
495 */
496 p->stats.last_reply = squid_curtime;
090089c4 497 } else {
1061b406 498 /* Neighbor is dead; ping it anyway, but don't expect a reply */
090089c4 499 /* log it once at the threshold */
dc835977 500 if (p->stats.logged_state == PEER_ALIVE) {
ab4b78e9 501 debug(15, 1) ("Detected DEAD %s: %s/%d/%d\n",
b69f7771 502 neighborTypeStr(p),
503 p->host, p->http_port, p->icp_port);
dc835977 504 p->stats.logged_state = PEER_DEAD;
090089c4 505 }
506 }
dc835977 507 p->stats.last_query = squid_curtime;
090089c4 508 }
40a1495e 509 if ((first_ping = first_ping->next) == NULL)
510 first_ping = Config.peers;
090089c4 511
0ab965da 512#if ALLOW_SOURCE_PING
090089c4 513 /* only do source_ping if we have neighbors */
40a1495e 514 if (Config.npeers) {
a369131d 515 const ipcache_addrs *ia = NULL;
516 struct sockaddr_in to_addr;
517 char *host = request->host;
17a0a4ee 518 if (!Config.onoff.source_ping) {
a3d5953d 519 debug(15, 6) ("neighborsUdpPing: Source Ping is disabled.\n");
0a0bf5db 520 } else if ((ia = ipcache_gethostbyname(host, 0))) {
a3d5953d 521 debug(15, 6) ("neighborsUdpPing: Source Ping: to %s for '%s'\n",
1061b406 522 host, url);
1061b406 523 echo_hdr.reqnum = reqnum;
16b204c4 524 if (icmp_sock != -1) {
e5f6c5c2 525 icmpSourcePing(ia->in_addrs[ia->cur], &echo_hdr, url);
16b204c4 526 } else {
527 to_addr.sin_family = AF_INET;
e5f6c5c2 528 to_addr.sin_addr = ia->in_addrs[ia->cur];
16b204c4 529 to_addr.sin_port = htons(echo_port);
27cd7235 530 query = icpCreateMessage(ICP_SECHO, 0, url, reqnum, 0);
16b204c4 531 icpUdpSend(theOutIcpConnection,
16b204c4 532 &to_addr,
9dee2904 533 query,
16b204c4 534 LOG_TAG_NONE,
aee4e794 535 0);
16b204c4 536 }
090089c4 537 } else {
a3d5953d 538 debug(15, 6) ("neighborsUdpPing: Source Ping: unknown host: %s\n",
1061b406 539 host);
090089c4 540 }
541 }
0ab965da 542#endif
429fdbec 543#if LOG_ICP_NUMBERS
544 request->hierarchy.n_sent = peers_pinged;
545 request->hierarchy.n_expect = *exprep;
546#endif
0a0bf5db 547 return peers_pinged;
090089c4 548}
549
d73844ca 550static void
b69f7771 551neighborAlive(peer * p, const MemObject * mem, const icp_common_t * header)
4eda6afe 552{
553 int rtt;
554 int n;
39ac34a9 555 if (p->stats.logged_state == PEER_DEAD && p->tcp_up) {
ab4b78e9 556 debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n",
b69f7771 557 neighborTypeStr(p),
558 p->host, p->http_port, p->icp_port);
dc835977 559 p->stats.logged_state = PEER_ALIVE;
4eda6afe 560 }
dc835977 561 p->stats.last_reply = squid_curtime;
b69f7771 562 n = ++p->stats.pings_acked;
27cd7235 563 if ((icp_opcode) header->opcode <= ICP_END)
b69f7771 564 p->stats.counts[header->opcode]++;
4eda6afe 565 if (mem) {
566 rtt = tvSubMsec(mem->start_ping, current_time);
9e4ad609 567 p->stats.rtt = intAverage(p->stats.rtt, rtt, n, RTT_AV_FACTOR);
b69f7771 568 p->icp_version = (int) header->version;
4eda6afe 569 }
570}
090089c4 571
38792624 572static void
79d39a72 573neighborCountIgnored(peer * p, icp_opcode opnotused)
a7e59001 574{
b69f7771 575 if (p == NULL)
a7e59001 576 return;
b69f7771 577 p->stats.ignored_replies++;
c7a3724d 578 NLateReplies++;
a7e59001 579}
580
e102ebda 581static peer *non_peers = NULL;
582
583static void
584neighborIgnoreNonPeer(const struct sockaddr_in *from, icp_opcode opcode)
585{
586 peer *np;
587 double x;
588 for (np = non_peers; np; np = np->next) {
589 if (np->in_addr.sin_addr.s_addr != from->sin_addr.s_addr)
590 continue;
591 if (np->in_addr.sin_port != from->sin_port)
592 continue;
593 break;
594 }
595 if (np == NULL) {
596 np = xcalloc(1, sizeof(peer));
597 np->in_addr.sin_addr = from->sin_addr;
598 np->in_addr.sin_port = from->sin_port;
599 np->icp_port = ntohl(from->sin_port);
600 np->type = PEER_NONE;
601 np->host = xstrdup(inet_ntoa(from->sin_addr));
602 np->next = non_peers;
603 non_peers = np;
604 }
605 np->stats.ignored_replies++;
606 np->stats.counts[opcode]++;
607 x = log(np->stats.ignored_replies) / log(10.0);
608 if (0.0 != x - (double) (int) x)
609 return;
610 debug(15, 1) ("WARNING: Ignored %d replies from non-peer %s\n",
611 np->stats.ignored_replies, np->host);
612}
613
429fdbec 614/* ignoreMulticastReply
615 *
616 * We want to ignore replies from multicast peers if the
617 * cache_host_domain rules would normally prevent the peer
618 * from being used
619 */
620static int
b69f7771 621ignoreMulticastReply(peer * p, MemObject * mem)
429fdbec 622{
b69f7771 623 if (p == NULL)
429fdbec 624 return 0;
79a15e0a 625 if (!EBIT_TEST(p->options, NEIGHBOR_MCAST_RESPONDER))
429fdbec 626 return 0;
b69f7771 627 if (peerHTTPOkay(p, mem->request))
429fdbec 628 return 0;
629 return 1;
630}
631
632/* I should attach these records to the entry. We take the first
633 * hit we get our wait until everyone misses. The timeout handler
634 * call needs to nip this shopping list or call one of the misses.
635 *
636 * If a hit process is already started, then sobeit
637 */
b8d8561b 638void
5ad33356 639neighborsUdpAck(const cache_key * key, icp_common_t * header, const struct sockaddr_in *from)
090089c4 640{
b69f7771 641 peer *p = NULL;
5ad33356 642 StoreEntry *entry;
643 MemObject *mem = NULL;
b6c0e933 644 peer_t ntype = PEER_NONE;
5e5126cc 645 char *opcode_d;
a7e59001 646 icp_opcode opcode = (icp_opcode) header->opcode;
090089c4 647
5ad33356 648 debug(15, 6) ("neighborsUdpAck: opcode %d '%s'\n",
649 (int) opcode, storeKeyText(key));
650 if (NULL != (entry = storeGet(key)))
651 mem = entry->mem_obj;
b69f7771 652 if ((p = whichPeer(from)))
653 neighborAlive(p, mem, header);
27cd7235 654 if (opcode > ICP_END)
d2af9477 655 return;
27cd7235 656 opcode_d = icp_opcode_str[opcode];
5ad33356 657 /* Does the entry exist? */
658 if (NULL == entry) {
659 debug(12, 3) ("neighborsUdpAck: Cache key '%s' not found\n",
660 storeKeyText(key));
661 neighborCountIgnored(p, opcode);
662 return;
663 }
2d1c6a4f 664 /* check if someone is already fetching it */
79a15e0a 665 if (EBIT_TEST(entry->flag, ENTRY_DISPATCHED)) {
5ad33356 666 debug(15, 3) ("neighborsUdpAck: '%s' already being fetched.\n",
667 storeKeyText(key));
b69f7771 668 neighborCountIgnored(p, opcode);
d2af9477 669 return;
670 }
2d1c6a4f 671 if (mem == NULL) {
5ad33356 672 debug(15, 2) ("Ignoring %s for missing mem_obj: %s\n",
673 opcode_d, storeKeyText(key));
b69f7771 674 neighborCountIgnored(p, opcode);
8de2f7ad 675 return;
676 }
677 if (entry->ping_status != PING_WAITING) {
5ad33356 678 debug(15, 2) ("neighborsUdpAck: Unexpected %s for %s\n",
679 opcode_d, storeKeyText(key));
b69f7771 680 neighborCountIgnored(p, opcode);
8de2f7ad 681 return;
682 }
4eda6afe 683 if (entry->lock_count == 0) {
5ad33356 684 debug(12, 1) ("neighborsUdpAck: '%s' has no locks\n",
685 storeKeyText(key));
b69f7771 686 neighborCountIgnored(p, opcode);
4eda6afe 687 return;
090089c4 688 }
a3d5953d 689 debug(15, 3) ("neighborsUdpAck: %s for '%s' from %s \n",
5ad33356 690 opcode_d, storeKeyText(key), p ? p->host : "source");
b69f7771 691 if (p)
692 ntype = neighborType(p, mem->request);
693 if (ignoreMulticastReply(p, mem)) {
694 neighborCountIgnored(p, opcode);
27cd7235 695 } else if (opcode == ICP_SECHO) {
429fdbec 696 /* Received source-ping reply */
b69f7771 697 if (p) {
a3d5953d 698 debug(15, 1) ("Ignoring SECHO from neighbor %s\n", p->host);
b69f7771 699 neighborCountIgnored(p, opcode);
429fdbec 700 } else {
701 /* if we reach here, source-ping reply is the first 'parent',
702 * so fetch directly from the source */
a3d5953d 703 debug(15, 6) ("Source is the first to respond.\n");
b3264694 704 mem->icp_reply_callback(NULL, ntype, header, mem->ircb_data);
429fdbec 705 }
27cd7235 706 } else if (opcode == ICP_MISS) {
b69f7771 707 if (p == NULL) {
e102ebda 708 neighborIgnoreNonPeer(from, opcode);
d2af9477 709 } else {
b3264694 710 mem->icp_reply_callback(p, ntype, header, mem->ircb_data);
30a4f2a8 711 }
a7c05555 712 } else if (opcode == ICP_HIT) {
b69f7771 713 if (p == NULL) {
e102ebda 714 neighborIgnoreNonPeer(from, opcode);
d2af9477 715 } else {
27cd7235 716 header->opcode = ICP_HIT;
b3264694 717 mem->icp_reply_callback(p, ntype, header, mem->ircb_data);
090089c4 718 }
27cd7235 719 } else if (opcode == ICP_DECHO) {
b69f7771 720 if (p == NULL) {
e102ebda 721 neighborIgnoreNonPeer(from, opcode);
deb79f06 722 } else if (ntype == PEER_SIBLING) {
5e604a0e 723 debug_trap("neighborsUdpAck: Found non-ICP cache as SIBLING\n");
724 debug_trap("neighborsUdpAck: non-ICP neighbors must be a PARENT\n");
090089c4 725 } else {
b3264694 726 mem->icp_reply_callback(p, ntype, header, mem->ircb_data);
090089c4 727 }
27cd7235 728 } else if (opcode == ICP_SECHO) {
b69f7771 729 if (p) {
a3d5953d 730 debug(15, 1) ("Ignoring SECHO from neighbor %s\n", p->host);
b69f7771 731 neighborCountIgnored(p, opcode);
0ab965da 732#if ALLOW_SOURCE_PING
733 } else if (Config.onoff.source_ping) {
b3264694 734 mem->icp_reply_callback(NULL, ntype, header, mem->ircb_data);
0ab965da 735#endif
736 } else {
737 debug(15, 1) ("Unsolicited SECHO from %s\n", inet_ntoa(from->sin_addr));
090089c4 738 }
27cd7235 739 } else if (opcode == ICP_DENIED) {
b69f7771 740 if (p == NULL) {
e102ebda 741 neighborIgnoreNonPeer(from, opcode);
b69f7771 742 } else if (p->stats.pings_acked > 100) {
27cd7235 743 if (100 * p->stats.counts[ICP_DENIED] / p->stats.pings_acked > 95) {
a3d5953d 744 debug(15, 0) ("95%% of replies from '%s' are UDP_DENIED\n", p->host);
745 debug(15, 0) ("Disabling '%s', please check your configuration.\n", p->host);
b69f7771 746 neighborRemove(p);
747 p = NULL;
e006d372 748 } else {
b69f7771 749 neighborCountIgnored(p, opcode);
30a4f2a8 750 }
751 }
27cd7235 752 } else if (opcode == ICP_MISS_NOFETCH) {
b3264694 753 mem->icp_reply_callback(p, ntype, header, mem->ircb_data);
090089c4 754 } else {
a3d5953d 755 debug(15, 0) ("neighborsUdpAck: Unexpected ICP reply: %s\n", opcode_d);
d2af9477 756 }
090089c4 757}
758
deb79f06 759peer *
40a1495e 760peerFindByName(const char *name)
98ffb7e4 761{
b69f7771 762 peer *p = NULL;
40a1495e 763 for (p = Config.peers; p; p = p->next) {
b69f7771 764 if (!strcasecmp(name, p->host))
98ffb7e4 765 break;
766 }
b69f7771 767 return p;
98ffb7e4 768}
b012353a 769
5269d0bd 770int
b69f7771 771neighborUp(const peer * p)
5269d0bd 772{
b69f7771 773 if (!p->tcp_up)
e924600d 774 return 0;
ae95b16f 775 if (squid_curtime - p->stats.last_query > Config.Timeout.deadPeer)
dc835977 776 return 1;
777 if (p->stats.last_query - p->stats.last_reply >= Config.Timeout.deadPeer)
5269d0bd 778 return 0;
779 return 1;
780}
781
e6e3b09b 782void
b69f7771 783peerDestroy(peer * p)
e6e3b09b 784{
ee4a1f5d 785 struct _domain_ping *l = NULL;
786 struct _domain_ping *nl = NULL;
b69f7771 787 if (p == NULL)
3c6e634f 788 return;
88738790 789 if (p->ck_conn_event_pend)
b69f7771 790 eventDelete(peerCheckConnect, p);
791 if (p->type == PEER_MULTICAST) {
792 if (p->mcast.flags & PEER_COUNT_EVENT_PENDING)
793 eventDelete(peerCountMcastPeersStart, p);
794 if (p->mcast.flags & PEER_COUNTING)
795 eventDelete(peerCountMcastPeersDone, p);
796 }
797 for (l = p->pinglist; l; l = nl) {
ee4a1f5d 798 nl = l->next;
799 safe_free(l->domain);
800 safe_free(l);
801 }
b69f7771 802 safe_free(p->host);
8407afee 803 cbdataFree(p);
e6e3b09b 804}
c7a3724d 805
dd4bd44e 806static void
03a1ee42 807peerDNSConfigure(const ipcache_addrs * ia, void *data)
dd4bd44e 808{
b69f7771 809 peer *p = data;
dd4bd44e 810 struct sockaddr_in *ap;
811 int j;
b69f7771 812 if (p->n_addresses == 0) {
a3d5953d 813 debug(15, 1) ("Configuring %s %s/%d/%d\n", neighborTypeStr(p),
b69f7771 814 p->host, p->http_port, p->icp_port);
815 if (p->type == PEER_MULTICAST)
a3d5953d 816 debug(15, 1) (" Multicast TTL = %d\n", p->mcast.ttl);
b69f7771 817 }
818 p->n_addresses = 0;
dd4bd44e 819 if (ia == NULL) {
a3d5953d 820 debug(0, 0) ("WARNING: DNS lookup for '%s' failed!\n", p->host);
dd4bd44e 821 return;
822 }
c04fe656 823 if ((int) ia->count < 1) {
a3d5953d 824 debug(0, 0) ("WARNING: No IP address found for '%s'!\n", p->host);
dd4bd44e 825 return;
826 }
c04fe656 827 for (j = 0; j < (int) ia->count && j < PEER_MAX_ADDRESSES; j++) {
b69f7771 828 p->addresses[j] = ia->in_addrs[j];
a3d5953d 829 debug(15, 2) ("--> IP address #%d: %s\n", j, inet_ntoa(p->addresses[j]));
b69f7771 830 p->n_addresses++;
dd4bd44e 831 }
b69f7771 832 ap = &p->in_addr;
dd4bd44e 833 memset(ap, '\0', sizeof(struct sockaddr_in));
834 ap->sin_family = AF_INET;
b69f7771 835 ap->sin_addr = p->addresses[0];
836 ap->sin_port = htons(p->icp_port);
837 if (p->type == PEER_MULTICAST)
838 peerCountMcastPeersSchedule(p, 10);
dd4bd44e 839}
840
841static void
79d39a72 842peerRefreshDNS(void *datanotused)
dd4bd44e 843{
b69f7771 844 peer *p = NULL;
40a1495e 845 peer *next = Config.peers;
79d39a72 846 while ((p = next) != NULL) {
b69f7771 847 next = p->next;
429fdbec 848 /* some random, bogus FD for ipcache */
03a1ee42 849 p->test_fd = Squid_MaxFD + current_time.tv_usec;
8407afee 850 ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
dd4bd44e 851 }
852 /* Reconfigure the peers every hour */
853 eventAdd("peerRefreshDNS", peerRefreshDNS, NULL, 3600);
854}
e924600d 855
856static void
857peerCheckConnect(void *data)
858{
859 peer *p = data;
860 int fd;
88738790 861 if (p->ck_conn_event_pend != 1)
862 debug_trap("bad ck_conn_event_pend counter");
863 p->ck_conn_event_pend--;
e924600d 864 fd = comm_open(SOCK_STREAM, 0, Config.Addrs.tcp_outgoing,
865 0, COMM_NONBLOCKING, p->host);
866 if (fd < 0)
867 return;
03a1ee42 868 p->test_fd = fd;
8407afee 869 ipcache_nbgethostbyname(p->host, peerCheckConnect2, p);
e924600d 870}
871
872static void
79d39a72 873peerCheckConnect2(const ipcache_addrs * ianotused, void *data)
e924600d 874{
875 peer *p = data;
03a1ee42 876 commConnectStart(p->test_fd,
e924600d 877 p->host,
878 p->http_port,
879 peerCheckConnectDone,
880 p);
881}
882
883static void
884peerCheckConnectDone(int fd, int status, void *data)
885{
886 peer *p = data;
887 p->tcp_up = status == COMM_OK ? 1 : 0;
888 if (p->tcp_up) {
a3d5953d 889 debug(15, 0) ("TCP connection to %s/%d succeeded\n",
e924600d 890 p->host, p->http_port);
891 } else {
88738790 892 p->ck_conn_event_pend++;
e924600d 893 eventAdd("peerCheckConnect", peerCheckConnect, p, 80);
894 }
895 comm_close(fd);
896 return;
897}
898
899void
900peerCheckConnectStart(peer * p)
901{
902 if (!p->tcp_up)
903 return;
a3d5953d 904 debug(15, 0) ("TCP connection to %s/%d failed\n", p->host, p->http_port);
e924600d 905 p->tcp_up = 0;
906 p->last_fail_time = squid_curtime;
88738790 907 p->ck_conn_event_pend++;
e924600d 908 eventAdd("peerCheckConnect", peerCheckConnect, p, 80);
909}
429fdbec 910
911static void
912peerCountMcastPeersSchedule(peer * p, time_t when)
913{
914 if (p->mcast.flags & PEER_COUNT_EVENT_PENDING)
915 return;
916 eventAdd("peerCountMcastPeersStart",
917 peerCountMcastPeersStart,
918 p,
919 when);
920 p->mcast.flags |= PEER_COUNT_EVENT_PENDING;
921}
922
923static void
924peerCountMcastPeersStart(void *data)
925{
926 peer *p = data;
927 ps_state *psstate = xcalloc(1, sizeof(ps_state));
928 StoreEntry *fake;
929 MemObject *mem;
930 icp_common_t *query;
931 LOCAL_ARRAY(char, url, MAX_URL);
ce66013b 932 assert(p->type == PEER_MULTICAST);
429fdbec 933 p->mcast.flags &= ~PEER_COUNT_EVENT_PENDING;
042461c3 934 snprintf(url, MAX_URL, "http://%s/", inet_ntoa(p->in_addr.sin_addr));
88738790 935 fake = storeCreateEntry(url, url, 0, METHOD_GET);
429fdbec 936 psstate->request = requestLink(urlParse(METHOD_GET, url));
937 psstate->entry = fake;
938 psstate->callback = NULL;
939 psstate->fail_callback = NULL;
940 psstate->callback_data = p;
941 psstate->icp.start = current_time;
942 mem = fake->mem_obj;
2db609de 943 mem->request = requestLink(psstate->request);
429fdbec 944 mem->start_ping = current_time;
945 mem->icp_reply_callback = peerCountHandleIcpReply;
946 mem->ircb_data = psstate;
03a1ee42 947 mcastSetTtl(theOutIcpConnection, p->mcast.ttl);
9fb13bb6 948 p->mcast.reqnum = mem->reqnum;
27cd7235 949 query = icpCreateMessage(ICP_QUERY, 0, url, p->mcast.reqnum, 0);
429fdbec 950 icpUdpSend(theOutIcpConnection,
951 &p->in_addr,
952 query,
953 LOG_TAG_NONE,
aee4e794 954 0);
429fdbec 955 fake->ping_status = PING_WAITING;
956 eventAdd("peerCountMcastPeersDone",
957 peerCountMcastPeersDone,
2db609de 958 psstate,
429fdbec 959 Config.neighborTimeout);
960 p->mcast.flags |= PEER_COUNTING;
961 peerCountMcastPeersSchedule(p, MCAST_COUNT_RATE);
962}
963
964static void
965peerCountMcastPeersDone(void *data)
966{
967 ps_state *psstate = data;
968 peer *p = psstate->callback_data;
969 StoreEntry *fake = psstate->entry;
429fdbec 970 p->mcast.flags &= ~PEER_COUNTING;
9e4ad609 971 p->mcast.avg_n_members = doubleAverage(p->mcast.avg_n_members,
972 (double) psstate->icp.n_recv,
973 ++p->mcast.n_times_counted,
974 10);
a3d5953d 975 debug(15, 1) ("Group %s: %d replies, %4.1f average\n",
429fdbec 976 p->host,
977 psstate->icp.n_recv,
978 p->mcast.avg_n_members);
979 p->mcast.n_replies_expected = (int) p->mcast.avg_n_members;
980 fake->store_status = STORE_ABORTED;
e7a22b88 981 requestUnlink(fake->mem_obj->request);
f0d5043e 982 fake->mem_obj->request = NULL;
429fdbec 983 storeReleaseRequest(fake);
984 storeUnlockObject(fake);
e7a22b88 985 requestUnlink(psstate->request);
2db609de 986 xfree(psstate);
429fdbec 987}
988
989static void
79d39a72 990peerCountHandleIcpReply(peer * pnotused, peer_t type, icp_common_t * hdrnotused, void *data)
429fdbec 991{
992 ps_state *psstate = data;
993 psstate->icp.n_recv++;
429fdbec 994}
ed7f5615 995
996static void
997neighborDumpPeers(StoreEntry * sentry)
998{
999 dump_peers(sentry, Config.peers);
1000}
1001
1002static void
1003neighborDumpNonPeers(StoreEntry * sentry)
1004{
1005 dump_peers(sentry, non_peers);
1006}
1007
a369131d 1008void
1009dump_peer_options(StoreEntry * sentry, peer * p)
1010{
1011 if (EBIT_TEST(p->options, NEIGHBOR_PROXY_ONLY))
1012 storeAppendPrintf(sentry, " proxy-only");
1013 if (EBIT_TEST(p->options, NEIGHBOR_NO_QUERY))
1014 storeAppendPrintf(sentry, " no-query");
8638fc66 1015 if (EBIT_TEST(p->options, NEIGHBOR_NO_DIGEST))
1016 storeAppendPrintf(sentry, " no-digest");
a369131d 1017 if (EBIT_TEST(p->options, NEIGHBOR_DEFAULT_PARENT))
1018 storeAppendPrintf(sentry, " default");
1019 if (EBIT_TEST(p->options, NEIGHBOR_ROUNDROBIN))
1020 storeAppendPrintf(sentry, " round-robin");
1021 if (EBIT_TEST(p->options, NEIGHBOR_MCAST_RESPONDER))
1022 storeAppendPrintf(sentry, " multicast-responder");
1023 if (EBIT_TEST(p->options, NEIGHBOR_CLOSEST_ONLY))
1024 storeAppendPrintf(sentry, " closest-only");
dc9d133b 1025#if USE_HTCP
e8d185d2 1026 if (EBIT_TEST(p->options, NEIGHBOR_HTCP))
1027 storeAppendPrintf(sentry, " htcp");
dc9d133b 1028#endif
a369131d 1029 if (p->mcast.ttl > 0)
1030 storeAppendPrintf(sentry, " ttl=%d", p->mcast.ttl);
1031 storeAppendPrintf(sentry, "\n");
1032}
1033
ed7f5615 1034static void
1035dump_peers(StoreEntry * sentry, peer * peers)
1036{
1037 peer *e = NULL;
1038 struct _domain_ping *d = NULL;
1039 icp_opcode op;
06e6d12a 1040 int i;
ed7f5615 1041 if (peers == NULL)
1042 storeAppendPrintf(sentry, "There are no neighbors installed.\n");
1043 for (e = peers; e; e = e->next) {
1044 assert(e->host != NULL);
1045 storeAppendPrintf(sentry, "\n%-11.11s: %s/%d/%d\n",
1046 neighborTypeStr(e),
1047 e->host,
1048 e->http_port,
1049 e->icp_port);
a369131d 1050 storeAppendPrintf(sentry, "Flags :");
1051 dump_peer_options(sentry, e);
06e6d12a 1052 for (i = 0; i < e->n_addresses; i++) {
1053 storeAppendPrintf(sentry, "Address[%d] : %s\n", i,
1054 inet_ntoa(e->addresses[i]));
1055 }
ed7f5615 1056 storeAppendPrintf(sentry, "Status : %s\n",
1057 neighborUp(e) ? "Up" : "Down");
1058 storeAppendPrintf(sentry, "AVG RTT : %d msec\n", e->stats.rtt);
1059 storeAppendPrintf(sentry, "LAST QUERY : %8d seconds ago\n",
1060 (int) (squid_curtime - e->stats.last_query));
1061 storeAppendPrintf(sentry, "LAST REPLY : %8d seconds ago\n",
1062 (int) (squid_curtime - e->stats.last_reply));
1063 storeAppendPrintf(sentry, "PINGS SENT : %8d\n", e->stats.pings_sent);
1064 storeAppendPrintf(sentry, "PINGS ACKED: %8d %3d%%\n",
1065 e->stats.pings_acked,
1066 percent(e->stats.pings_acked, e->stats.pings_sent));
1067 storeAppendPrintf(sentry, "FETCHES : %8d %3d%%\n",
1068 e->stats.fetches,
1069 percent(e->stats.fetches, e->stats.pings_acked));
1070 storeAppendPrintf(sentry, "IGNORED : %8d %3d%%\n",
1071 e->stats.ignored_replies,
1072 percent(e->stats.ignored_replies, e->stats.pings_acked));
1073 storeAppendPrintf(sentry, "Histogram of PINGS ACKED:\n");
1074 for (op = ICP_INVALID; op < ICP_END; op++) {
1075 if (e->stats.counts[op] == 0)
1076 continue;
1077 storeAppendPrintf(sentry, " %12.12s : %8d %3d%%\n",
1078 icp_opcode_str[op],
1079 e->stats.counts[op],
1080 percent(e->stats.counts[op], e->stats.pings_acked));
1081 }
1082 if (e->last_fail_time) {
1083 storeAppendPrintf(sentry, "Last failed connect() at: %s\n",
1084 mkhttpdlogtime(&(e->last_fail_time)));
1085 }
1086 if (e->pinglist != NULL)
1087 storeAppendPrintf(sentry, "DOMAIN LIST: ");
1088 for (d = e->pinglist; d; d = d->next) {
1089 if (d->do_ping)
1090 storeAppendPrintf(sentry, "%s ", d->domain);
1091 else
1092 storeAppendPrintf(sentry, "!%s ", d->domain);
1093 }
035f5831 1094 storeAppendPrintf(sentry, "\n");
ed7f5615 1095 storeAppendPrintf(sentry, "Keep-Alive Ratio: %d%%\n",
1096 percent(e->stats.n_keepalives_recv, e->stats.n_keepalives_sent));
1097 }
1098}