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