]> git.ipfire.org Git - thirdparty/squid.git/blame - src/icp_v2.cc
Updates for running on squid-cache.org
[thirdparty/squid.git] / src / icp_v2.cc
CommitLineData
9cef6668 1/*
63be0a78 2 * $Id: icp_v2.cc,v 1.103 2008/02/26 21:49:35 amosjeffries Exp $
9cef6668 3 *
cc192b50 4 * DEBUG: section 12 Internet Cache Protocol (ICP)
9cef6668 5 * AUTHOR: Duane Wessels
6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
9cef6668 8 * ----------------------------------------------------------
9 *
2b6662ba 10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
9cef6668 18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34
63be0a78 35/**
36 \defgroup ServerProtocolICPInternal2 ICPv2 Internals
37 \ingroup ServerProtocolICPAPI
38 */
39
7a2f978b 40#include "squid.h"
e6ccf245 41#include "Store.h"
063dc1eb 42#include "comm.h"
e6ccf245 43#include "ICP.h"
528b2c61 44#include "HttpRequest.h"
4fb35c3c 45#include "ACLChecklist.h"
b0dd28ba 46#include "ACL.h"
450e0c10 47#include "AccessLogEntry.h"
d295d770 48#include "wordlist.h"
985c86bc 49#include "SquidTime.h"
bef81ea5 50#include "SwapDir.h"
e1f7507e 51#include "IPAddress.h"
7a2f978b 52
63be0a78 53/// \ingroup ServerProtocolICPInternal2
cc192b50 54static void icpLogIcp(const IPAddress &, log_type, int, const char *, int);
62e76326 55
63be0a78 56/// \ingroup ServerProtocolICPInternal2
cc192b50 57static void icpHandleIcpV2(int, IPAddress &, char *, int);
63be0a78 58
59/// \ingroup ServerProtocolICPInternal2
071a3ae7 60static void icpCount(void *, int, size_t, int);
17b6e784 61
63be0a78 62/**
63 \ingroup ServerProtocolICPInternal2
48382032 64 * IcpQueueHead is global so comm_incoming() knows whether or not
65 * to call icpUdpSendQueue.
66 */
d4cb310b 67static icpUdpData *IcpQueueHead = NULL;
63be0a78 68/// \ingroup ServerProtocolICPInternal2
69static icpUdpData *IcpQueueTail = NULL;
7a2f978b 70
e1f7507e
AJ
71/// \ingroup ServerProtocolICPInternal2
72IPAddress theOutICPAddr;
73
e6ccf245 74/* icp_common_t */
75_icp_common_t::_icp_common_t() : opcode(ICP_INVALID), version(0), length(0), reqnum(0), flags(0), pad(0), shostid(0)
62e76326 76{}
e6ccf245 77
78_icp_common_t::_icp_common_t(char *buf, unsigned int len)
79{
80 if (len < sizeof(_icp_common_t)) {
62e76326 81 /* mark as invalid */
82 length = len + 1;
83 return;
e6ccf245 84 }
62e76326 85
e6ccf245 86 xmemcpy(this, buf, sizeof(icp_common_t));
87 /*
88 * Convert network order sensitive fields
89 */
90 length = ntohs(length);
91 reqnum = ntohl(reqnum);
92 flags = ntohl(flags);
93 pad = ntohl(pad);
94}
95
96icp_opcode
97_icp_common_t::getOpCode() const
98{
99 if (opcode > (char)ICP_END)
62e76326 100 return ICP_INVALID;
101
e6ccf245 102 return (icp_opcode)opcode;
103}
104
105/* ICPState */
106
63be0a78 107ICPState::ICPState(icp_common_t &aHeader, HttpRequest *aRequest):
f72fb56b 108 header(aHeader),
109 request(HTTPMSGLOCK(aRequest)),
62e76326 110 fd(-1),
111 url(NULL)
112{}
e6ccf245 113
114ICPState::~ICPState()
115{
116 safe_free(url);
f72fb56b 117 HTTPMSGUNLOCK(request);
e6ccf245 118}
119
120
121/* End ICPState */
122
123/* ICP2State */
62e76326 124
63be0a78 125/// \ingroup ServerProtocolICPInternal2
126class ICP2State : public ICPState, public StoreClient
62e76326 127{
128
129public:
f72fb56b 130 ICP2State(icp_common_t & aHeader, HttpRequest *aRequest):
131 ICPState(aHeader, aRequest),rtt(0),src_rtt(0),flags(0)
62e76326 132 {}
133
134 ~ICP2State();
e6ccf245 135 void created(StoreEntry * newEntry);
136
137 int rtt;
138 int src_rtt;
139 u_int32_t flags;
140};
141
63be0a78 142ICP2State::~ICP2State()
62e76326 143{}
e6ccf245 144
145void
63be0a78 146ICP2State::created(StoreEntry *newEntry)
e6ccf245 147{
148 StoreEntry *entry = newEntry->isNull () ? NULL : newEntry;
bf8fe701 149 debugs(12, 5, "icpHandleIcpV2: OPCODE " << icp_opcode_str[header.opcode]);
e6ccf245 150 icp_opcode codeToSend;
62e76326 151
e6ccf245 152 if (icpCheckUdpHit(entry, request)) {
62e76326 153 codeToSend = ICP_HIT;
e6ccf245 154 } else {
62e76326 155 if (Config.onoff.test_reachability && rtt == 0) {
cc192b50 156 if ((rtt = netdbHostRtt(request->GetHost())) == 0)
157 netdbPingSite(request->GetHost());
62e76326 158 }
159
160 if (icpGetCommonOpcode() != ICP_ERR)
161 codeToSend = icpGetCommonOpcode();
162 else if (Config.onoff.test_reachability && rtt == 0)
163 codeToSend = ICP_MISS_NOFETCH;
164 else
165 codeToSend = ICP_MISS;
e6ccf245 166 }
62e76326 167
cc192b50 168 icpCreateAndSend(codeToSend, flags, url, header.reqnum, src_rtt, fd, from);
e6ccf245 169 delete this;
170}
171
172/* End ICP2State */
173
63be0a78 174/// \ingroup ServerProtocolICPInternal2
7a2f978b 175static void
cc192b50 176icpLogIcp(const IPAddress &caddr, log_type logcode, int len, const char *url, int delay)
7a2f978b 177{
7a2f978b 178 AccessLogEntry al;
62e76326 179
96c617da 180 if (LOG_TAG_NONE == logcode)
62e76326 181 return;
182
071a3ae7 183 if (LOG_ICP_QUERY == logcode)
62e76326 184 return;
185
17b6e784 186 clientdbUpdate(caddr, logcode, PROTO_ICP, len);
62e76326 187
7a2f978b 188 if (!Config.onoff.log_udp)
62e76326 189 return;
190
27cd7235 191 al.icp.opcode = ICP_QUERY;
62e76326 192
7a2f978b 193 al.url = url;
62e76326 194
17b6e784 195 al.cache.caddr = caddr;
62e76326 196
1a86db31 197 al.cache.replySize = len;
62e76326 198
17b6e784 199 al.cache.code = logcode;
62e76326 200
17b6e784 201 al.cache.msec = delay;
62e76326 202
7684c4b1 203 accessLogLog(&al, NULL);
7a2f978b 204}
205
63be0a78 206/// \ingroup ServerProtocolICPInternal2
48382032 207void
17b6e784 208icpUdpSendQueue(int fd, void *unused)
7a2f978b 209{
17b6e784 210 icpUdpData *q;
7a2f978b 211 int x;
17b6e784 212 int delay;
62e76326 213
48382032 214 while ((q = IcpQueueHead) != NULL) {
62e76326 215 delay = tvSubUsec(q->queue_time, current_time);
216 /* increment delay to prevent looping */
cc192b50 217 x = icpUdpSend(fd, q->address, (icp_common_t *) q->msg, q->logcode, ++delay);
62e76326 218 IcpQueueHead = q->next;
219 safe_free(q);
220
221 if (x < 0)
222 break;
7a2f978b 223 }
224}
225
e6ccf245 226_icp_common_t *
227_icp_common_t::createMessage(
7a2f978b 228 icp_opcode opcode,
229 int flags,
230 const char *url,
231 int reqnum,
232 int pad)
233{
234 char *buf = NULL;
235 icp_common_t *headerp = NULL;
236 char *urloffset = NULL;
237 int buf_len;
238 buf_len = sizeof(icp_common_t) + strlen(url) + 1;
62e76326 239
27cd7235 240 if (opcode == ICP_QUERY)
62e76326 241 buf_len += sizeof(u_int32_t);
242
e6ccf245 243 buf = (char *) xcalloc(buf_len, 1);
62e76326 244
7a2f978b 245 headerp = (icp_common_t *) (void *) buf;
62e76326 246
79d39a72 247 headerp->opcode = (char) opcode;
62e76326 248
7a2f978b 249 headerp->version = ICP_VERSION_CURRENT;
62e76326 250
a9245686 251 headerp->length = (u_int16_t) htons(buf_len);
62e76326 252
7a2f978b 253 headerp->reqnum = htonl(reqnum);
62e76326 254
7a2f978b 255 headerp->flags = htonl(flags);
62e76326 256
7a2f978b 257 headerp->pad = htonl(pad);
62e76326 258
cc192b50 259 theOutICPAddr.GetInAddr( *((struct in_addr*)&headerp->shostid) );
62e76326 260
7a2f978b 261 urloffset = buf + sizeof(icp_common_t);
62e76326 262
27cd7235 263 if (opcode == ICP_QUERY)
62e76326 264 urloffset += sizeof(u_int32_t);
265
7a2f978b 266 xmemcpy(urloffset, url, strlen(url));
62e76326 267
e6ccf245 268 return (icp_common_t *)buf;
7a2f978b 269}
270
17b6e784 271int
7a2f978b 272icpUdpSend(int fd,
cc192b50 273 const IPAddress &to,
62e76326 274 icp_common_t * msg,
275 log_type logcode,
276 int delay)
7a2f978b 277{
17b6e784 278 icpUdpData *queue;
8e68922c 279 int x;
17b6e784 280 int len;
281 len = (int) ntohs(msg->length);
bf8fe701 282 debugs(12, 5, "icpUdpSend: FD " << fd << " sending " <<
cc192b50 283 icp_opcode_str[msg->opcode] << ", " << len << " bytes to " << to);
bf8fe701 284
cc192b50 285 x = comm_udp_sendto(fd, to, msg, len);
62e76326 286
287 if (x >= 0)
288 {
289 /* successfully written */
cc192b50 290 icpLogIcp(to, logcode, len, (char *) (msg + 1), delay);
62e76326 291 icpCount(msg, SENT, (size_t) len, delay);
292 safe_free(msg);
293 } else if (0 == delay)
294 {
295 /* send failed, but queue it */
296 queue = (icpUdpData *) xcalloc(1, sizeof(icpUdpData));
cc192b50 297 queue->address = to;
62e76326 298 queue->msg = msg;
299 queue->len = (int) ntohs(msg->length);
300 queue->queue_time = current_time;
301 queue->logcode = logcode;
302
303 if (IcpQueueHead == NULL) {
304 IcpQueueHead = queue;
305 IcpQueueTail = queue;
306 } else if (IcpQueueTail == IcpQueueHead) {
307 IcpQueueTail = queue;
308 IcpQueueHead->next = queue;
309 } else {
310 IcpQueueTail->next = queue;
311 IcpQueueTail = queue;
312 }
313
314 commSetSelect(fd, COMM_SELECT_WRITE, icpUdpSendQueue, NULL, 0);
315 statCounter.icp.replies_queued++;
316 } else
317 {
318 /* don't queue it */
319 statCounter.icp.replies_dropped++;
8e68922c 320 }
62e76326 321
17b6e784 322 return x;
7a2f978b 323}
324
325int
190154cf 326icpCheckUdpHit(StoreEntry * e, HttpRequest * request)
7a2f978b 327{
328 if (e == NULL)
62e76326 329 return 0;
330
3900307b 331 if (!e->validToSend())
62e76326 332 return 0;
333
7a2f978b 334 if (Config.onoff.icp_hit_stale)
62e76326 335 return 1;
336
829a9357 337 if (refreshCheckICP(e, request))
62e76326 338 return 0;
339
7a2f978b 340 return 1;
341}
7a2f978b 342
63be0a78 343/**
bef81ea5 344 * This routine selects an ICP opcode for ICP misses.
63be0a78 345 *
346 \retval ICP_ERR no opcode selected here
347 \retval ICP_MISS_NOFETCH store is rebuilding, no fetch is possible yet
bef81ea5 348 */
e6ccf245 349icp_opcode
350icpGetCommonOpcode()
351{
bef81ea5 352 /* if store is rebuilding, return a UDP_MISS_NOFETCH */
62e76326 353
871899ca 354 if ((StoreController::store_dirs_rebuilding && opt_reload_hit_only) ||
62e76326 355 hit_only_mode_until > squid_curtime) {
356 return ICP_MISS_NOFETCH;
e6ccf245 357 }
62e76326 358
e6ccf245 359 return ICP_ERR;
360}
361
362log_type
363icpLogFromICPCode(icp_opcode opcode)
364{
365 if (opcode == ICP_ERR)
62e76326 366 return LOG_UDP_INVALID;
367
e6ccf245 368 if (opcode == ICP_DENIED)
62e76326 369 return LOG_UDP_DENIED;
370
e6ccf245 371 if (opcode == ICP_HIT)
62e76326 372 return LOG_UDP_HIT;
373
e6ccf245 374 if (opcode == ICP_MISS)
62e76326 375 return LOG_UDP_MISS;
376
e6ccf245 377 if (opcode == ICP_MISS_NOFETCH)
62e76326 378 return LOG_UDP_MISS_NOFETCH;
379
e6ccf245 380 fatal("expected ICP opcode\n");
62e76326 381
e6ccf245 382 return LOG_UDP_INVALID;
383}
384
385void
cc192b50 386icpCreateAndSend(icp_opcode opcode, int flags, char const *url, int reqnum, int pad, int fd, const IPAddress &from)
e6ccf245 387{
388 icp_common_t *reply = _icp_common_t::createMessage(opcode, flags, url, reqnum, pad);
389 icpUdpSend(fd, from, reply, icpLogFromICPCode(opcode), 0);
390}
391
392void
cc192b50 393icpDenyAccess(IPAddress &from, char *url, int reqnum, int fd)
e6ccf245 394{
cc192b50 395 debugs(12, 2, "icpDenyAccess: Access Denied for " << from << " by " << AclMatchedName << ".");
62e76326 396
cc192b50 397 if (clientdbCutoffDenied(from))
62e76326 398 {
399 /*
400 * count this DENIED query in the clientdb, even though
401 * we're not sending an ICP reply...
402 */
cc192b50 403 clientdbUpdate(from, LOG_UDP_DENIED, PROTO_ICP, 0);
62e76326 404 } else
405 {
406 icpCreateAndSend(ICP_DENIED, 0, url, reqnum, 0, fd, from);
e6ccf245 407 }
408}
409
410int
cc192b50 411icpAccessAllowed(IPAddress &from, HttpRequest * icp_request)
7a2f978b 412{
4fb35c3c 413 ACLChecklist checklist;
cc192b50 414 checklist.src_addr = from;
415 checklist.my_addr.SetNoAddr();
6dd9f4bd 416 checklist.request = HTTPMSGLOCK(icp_request);
506768d9 417 checklist.accessList = cbdataReference(Config.accessList.icp);
108d65b2 418 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
b448c119 419 int result = checklist.fastCheck();
b448c119 420 return result;
e6ccf245 421}
422
423char const *
424icpGetUrlToSend(char *url)
425{
426 if (strpbrk(url, w_space))
62e76326 427 return rfc1738_escape(url);
e6ccf245 428 else
62e76326 429 return url;
e6ccf245 430}
431
190154cf 432HttpRequest *
cc192b50 433icpGetRequest(char *url, int reqnum, int fd, IPAddress &from)
e6ccf245 434{
62e76326 435 if (strpbrk(url, w_space))
436 {
437 url = rfc1738_escape(url);
438 icpCreateAndSend(ICP_ERR, 0, rfc1738_escape(url), reqnum, 0, fd, from);
439 return NULL;
e6ccf245 440 }
62e76326 441
190154cf 442 HttpRequest *result;
62e76326 443
c21ad0f5 444 if ((result = HttpRequest::CreateFromUrl(url)) == NULL)
62e76326 445 icpCreateAndSend(ICP_ERR, 0, url, reqnum, 0, fd, from);
446
e6ccf245 447 return result;
448
449}
450
451static void
cc192b50 452doV2Query(int fd, IPAddress &from, char *buf, icp_common_t header)
e6ccf245 453{
454 int rtt = 0;
7a2f978b 455 int src_rtt = 0;
a9245686 456 u_int32_t flags = 0;
e6ccf245 457 /* We have a valid packet */
458 char *url = buf + sizeof(icp_common_t) + sizeof(u_int32_t);
cc192b50 459 HttpRequest *icp_request = icpGetRequest(url, header.reqnum, fd, from);
62e76326 460
e6ccf245 461 if (!icp_request)
62e76326 462 return;
463
6dd9f4bd 464 HTTPMSGLOCK(icp_request);
319bf5a7 465
cc192b50 466 if (!icpAccessAllowed(from, icp_request))
62e76326 467 {
cc192b50 468 icpDenyAccess(from, url, header.reqnum, fd);
6dd9f4bd 469 HTTPMSGUNLOCK(icp_request);
62e76326 470 return;
e6ccf245 471 }
62e76326 472
473 if (header.flags & ICP_FLAG_SRC_RTT)
474 {
cc192b50 475 rtt = netdbHostRtt(icp_request->GetHost());
476 int hops = netdbHostHops(icp_request->GetHost());
62e76326 477 src_rtt = ((hops & 0xFFFF) << 16) | (rtt & 0xFFFF);
478
479 if (rtt)
480 flags |= ICP_FLAG_SRC_RTT;
e6ccf245 481 }
62e76326 482
e6ccf245 483 /* The peer is allowed to use this cache */
f72fb56b 484 ICP2State *state = new ICP2State (header, icp_request);
62e76326 485
e6ccf245 486 state->fd = fd;
62e76326 487
e6ccf245 488 state->from = from;
62e76326 489
e6ccf245 490 state->url = xstrdup (url);
62e76326 491
e6ccf245 492 state->flags = flags;
62e76326 493
e6ccf245 494 state->rtt = rtt;
62e76326 495
e6ccf245 496 state->src_rtt = src_rtt;
62e76326 497
3b13a8fd 498 StoreEntry::getPublic (state, url, METHOD_GET);
319bf5a7 499
6dd9f4bd 500 HTTPMSGUNLOCK(icp_request);
e6ccf245 501}
502
503void
cc192b50 504_icp_common_t::handleReply(char *buf, IPAddress &from)
e6ccf245 505{
62e76326 506 if (neighbors_do_private_keys && reqnum == 0)
507 {
cc192b50 508 debugs(12, 0, "icpHandleIcpV2: Neighbor " << from << " returned reqnum = 0");
bf8fe701 509 debugs(12, 0, "icpHandleIcpV2: Disabling use of private keys");
62e76326 510 neighbors_do_private_keys = 0;
e6ccf245 511 }
62e76326 512
e6ccf245 513 char *url = buf + sizeof(icp_common_t);
cc192b50 514 debugs(12, 3, "icpHandleIcpV2: " << icp_opcode_str[opcode] << " from " << from << " for '" << url << "'");
bf8fe701 515
e6ccf245 516 const cache_key *key = icpGetCacheKey(url, (int) reqnum);
517 /* call neighborsUdpAck even if ping_status != PING_WAITING */
518 neighborsUdpAck(key, this, from);
519}
520
521static void
cc192b50 522icpHandleIcpV2(int fd, IPAddress &from, char *buf, int len)
e6ccf245 523{
62e76326 524 if (len <= 0)
525 {
bf8fe701 526 debugs(12, 3, "icpHandleIcpV2: ICP message is too small");
62e76326 527 return;
e6ccf245 528 }
62e76326 529
e6ccf245 530 icp_common_t header(buf, len);
7b83b3d9 531 /*
532 * Length field should match the number of bytes read
533 */
62e76326 534
535 if (len != header.length)
536 {
bf8fe701 537 debugs(12, 3, "icpHandleIcpV2: ICP message is too small");
62e76326 538 return;
7b83b3d9 539 }
62e76326 540
541 switch (header.opcode)
542 {
543
27cd7235 544 case ICP_QUERY:
62e76326 545 /* We have a valid packet */
546 doV2Query(fd, from, buf, header);
547 break;
7a2f978b 548
27cd7235 549 case ICP_HIT:
62e76326 550
27cd7235 551 case ICP_DECHO:
62e76326 552
27cd7235 553 case ICP_MISS:
62e76326 554
27cd7235 555 case ICP_DENIED:
62e76326 556
27cd7235 557 case ICP_MISS_NOFETCH:
cc192b50 558 header.handleReply(buf, from);
62e76326 559 break;
7a2f978b 560
27cd7235 561 case ICP_INVALID:
62e76326 562
27cd7235 563 case ICP_ERR:
62e76326 564 break;
7a2f978b 565
566 default:
cc192b50 567 debugs(12, 0, "icpHandleIcpV2: UNKNOWN OPCODE: " << header.opcode << " from " << from);
bf8fe701 568
62e76326 569 break;
7a2f978b 570 }
7a2f978b 571}
572
573#ifdef ICP_PKT_DUMP
574static void
575icpPktDump(icp_common_t * pkt)
576{
62e76326 577
cc192b50 578 IPAddress a;
7a2f978b 579
4a7a3d56 580 debugs(12, 9, "opcode: " << std::setw(3) << pkt->opcode << " " << icp_opcode_str[pkt->opcode]);
581 debugs(12, 9, "version: "<< std::left << std::setw(8) << pkt->version);
582 debugs(12, 9, "length: "<< std::left << std::setw(8) << ntohs(pkt->length));
bf8fe701 583 debugs(12, 9, "reqnum: "<< std::left << std::setw(8) << ntohl(pkt->reqnum));
584 debugs(12, 9, "flags: "<< std::left << std::hex << std::setw(8) << ntohl(pkt->flags));
cc192b50 585 a = (struct in_addr)pkt->shostid;
586 debugs(12, 9, "shostid: " << a );
bf8fe701 587 debugs(12, 9, "payload: " << (char *) pkt + sizeof(icp_common_t));
7a2f978b 588}
62e76326 589
7a2f978b 590#endif
591
592void
ba4f8e5a 593icpHandleUdp(int sock, void *data)
7a2f978b 594{
d193a436 595 int *N = &incoming_sockets_accepted;
62e76326 596
cc192b50 597 IPAddress from;
7a2f978b 598 LOCAL_ARRAY(char, buf, SQUID_UDP_SO_RCVBUF);
0b6d1955 599 int len;
7a2f978b 600 int icp_version;
309ad3b6 601 int max = INCOMING_ICP_MAX;
7a2f978b 602 commSetSelect(sock, COMM_SELECT_READ, icpHandleUdp, NULL, 0);
62e76326 603
5b0aaa58 604 while (max--) {
62e76326 605 len = comm_udp_recvfrom(sock,
606 buf,
607 SQUID_UDP_SO_RCVBUF - 1,
608 0,
cc192b50 609 from);
62e76326 610
611 if (len == 0)
612 break;
613
614 if (len < 0) {
615 if (ignoreErrno(errno))
616 break;
617
7a2f978b 618#ifdef _SQUID_LINUX_
62e76326 619 /* Some Linux systems seem to set the FD for reading and then
620 * return ECONNREFUSED when sendto() fails and generates an ICMP
621 * port unreachable message. */
622 /* or maybe an EHOSTUNREACH "No route to host" message */
623 if (errno != ECONNREFUSED && errno != EHOSTUNREACH)
7a2f978b 624#endif
62e76326 625
bf8fe701 626 debugs(50, 1, "icpHandleUdp: FD " << sock << " recvfrom: " << xstrerror());
62e76326 627
628 break;
629 }
630
631 (*N)++;
632 icpCount(buf, RECV, (size_t) len, 0);
633 buf[len] = '\0';
bf8fe701 634 debugs(12, 4, "icpHandleUdp: FD " << sock << ": received " <<
cc192b50 635 (unsigned long int)len << " bytes from " << from);
bf8fe701 636
7a2f978b 637#ifdef ICP_PACKET_DUMP
62e76326 638
639 icpPktDump(buf);
7a2f978b 640#endif
62e76326 641
0b6d1955 642 if ((size_t) len < sizeof(icp_common_t)) {
bf8fe701 643 debugs(12, 4, "icpHandleUdp: Ignoring too-small UDP packet");
62e76326 644 break;
645 }
646
647 icp_version = (int) buf[1]; /* cheat! */
648
649 if (icp_version == ICP_VERSION_2)
650 icpHandleIcpV2(sock, from, buf, len);
651 else if (icp_version == ICP_VERSION_3)
652 icpHandleIcpV3(sock, from, buf, len);
653 else
bf8fe701 654 debugs(12, 1, "WARNING: Unused ICP version " << icp_version <<
cc192b50 655 " received from " << from);
7a2f978b 656 }
7a2f978b 657}
15df8349 658
659void
660icpConnectionsOpen(void)
661{
a9245686 662 u_int16_t port;
62e76326 663
cc192b50 664 IPAddress addr;
62e76326 665
cc192b50 666 struct addrinfo *xai = NULL;
15df8349 667 int x;
15df8349 668 wordlist *s;
62e76326 669
15df8349 670 if ((port = Config.Port.icp) <= 0)
62e76326 671 return;
672
15df8349 673 enter_suid();
62e76326 674
cc192b50 675 addr = Config.Addrs.udp_incoming;
676 addr.SetPort(port);
15df8349 677 theInIcpConnection = comm_open(SOCK_DGRAM,
bdb741f4 678 IPPROTO_UDP,
cc192b50 679 addr,
62e76326 680 COMM_NONBLOCKING,
681 "ICP Socket");
15df8349 682 leave_suid();
62e76326 683
15df8349 684 if (theInIcpConnection < 0)
62e76326 685 fatal("Cannot open ICP Port");
686
15df8349 687 commSetSelect(theInIcpConnection,
62e76326 688 COMM_SELECT_READ,
689 icpHandleUdp,
690 NULL,
691 0);
692
15df8349 693 for (s = Config.mcast_group_list; s; s = s->next)
62e76326 694 ipcache_nbgethostbyname(s->key, mcastJoinGroups, NULL);
695
cc192b50 696 debugs(12, 1, "Accepting ICP messages at " << addr << ", FD " << theInIcpConnection << ".");
62e76326 697
cc192b50 698 addr.SetEmpty(); // clear for next use.
699 addr = Config.Addrs.udp_outgoing;
700 if ( !addr.IsNoAddr() ) {
62e76326 701 enter_suid();
cc192b50 702 addr.SetPort(port);
62e76326 703 theOutIcpConnection = comm_open(SOCK_DGRAM,
bdb741f4 704 IPPROTO_UDP,
62e76326 705 addr,
62e76326 706 COMM_NONBLOCKING,
707 "ICP Port");
708 leave_suid();
709
710 if (theOutIcpConnection < 0)
711 fatal("Cannot open Outgoing ICP Port");
712
713 commSetSelect(theOutIcpConnection,
714 COMM_SELECT_READ,
715 icpHandleUdp,
716 NULL,
717 0);
718
cc192b50 719 debugs(12, 1, "Outgoing ICP messages on port " << addr.GetPort() << ", FD " << theOutIcpConnection << ".");
62e76326 720
721 fd_note(theOutIcpConnection, "Outgoing ICP socket");
722
723 fd_note(theInIcpConnection, "Incoming ICP socket");
15df8349 724 } else {
62e76326 725 theOutIcpConnection = theInIcpConnection;
15df8349 726 }
62e76326 727
cc192b50 728 theOutICPAddr.SetEmpty();
62e76326 729
cc192b50 730 theOutICPAddr.InitAddrInfo(xai);
62e76326 731
cc192b50 732 x = getsockname(theOutIcpConnection, xai->ai_addr, &xai->ai_addrlen);
62e76326 733
15df8349 734 if (x < 0)
bf8fe701 735 debugs(50, 1, "theOutIcpConnection FD " << theOutIcpConnection << ": getsockname: " << xstrerror());
15df8349 736 else
cc192b50 737 theOutICPAddr = *xai;
738
739 theOutICPAddr.FreeAddrInfo(xai);
15df8349 740}
c0fbae16 741
63be0a78 742/**
17e6c0a1 743 * icpConnectionShutdown only closes the 'in' socket if it is
744 * different than the 'out' socket.
745 */
c0fbae16 746void
17e6c0a1 747icpConnectionShutdown(void)
c0fbae16 748{
749 if (theInIcpConnection < 0)
62e76326 750 return;
751
17e6c0a1 752 if (theInIcpConnection != theOutIcpConnection) {
bf8fe701 753 debugs(12, 1, "FD " << theInIcpConnection << " Closing ICP connection");
62e76326 754 comm_close(theInIcpConnection);
17e6c0a1 755 }
62e76326 756
63be0a78 757 /**
c0fbae16 758 * Here we set 'theInIcpConnection' to -1 even though the ICP 'in'
759 * and 'out' sockets might be just one FD. This prevents this
760 * function from executing repeatedly. When we are really ready to
761 * exit or restart, main will comm_close the 'out' descriptor.
762 */
763 theInIcpConnection = -1;
62e76326 764
63be0a78 765 /**
c0fbae16 766 * Normally we only write to the outgoing ICP socket, but
767 * we also have a read handler there to catch messages sent
768 * to that specific interface. During shutdown, we must
769 * disable reading on the outgoing socket.
770 */
771 assert(theOutIcpConnection > -1);
62e76326 772
c0fbae16 773 commSetSelect(theOutIcpConnection, COMM_SELECT_READ, NULL, NULL, 0);
774}
17e6c0a1 775
776void
777icpConnectionClose(void)
778{
779 icpConnectionShutdown();
62e76326 780
17e6c0a1 781 if (theOutIcpConnection > -1) {
bf8fe701 782 debugs(12, 1, "FD " << theOutIcpConnection << " Closing ICP connection");
62e76326 783 comm_close(theOutIcpConnection);
784 theOutIcpConnection = -1;
17e6c0a1 785 }
786}
071a3ae7 787
788static void
789icpCount(void *buf, int which, size_t len, int delay)
790{
e6ccf245 791 icp_common_t *icp = (icp_common_t *) buf;
62e76326 792
071a3ae7 793 if (len < sizeof(*icp))
62e76326 794 return;
795
071a3ae7 796 if (SENT == which) {
62e76326 797 statCounter.icp.pkts_sent++;
798 kb_incr(&statCounter.icp.kbytes_sent, len);
799
800 if (ICP_QUERY == icp->opcode) {
801 statCounter.icp.queries_sent++;
802 kb_incr(&statCounter.icp.q_kbytes_sent, len);
803 } else {
804 statCounter.icp.replies_sent++;
805 kb_incr(&statCounter.icp.r_kbytes_sent, len);
806 /* this is the sent-reply service time */
807 statHistCount(&statCounter.icp.reply_svc_time, delay);
808 }
809
810 if (ICP_HIT == icp->opcode)
811 statCounter.icp.hits_sent++;
071a3ae7 812 } else if (RECV == which) {
62e76326 813 statCounter.icp.pkts_recv++;
814 kb_incr(&statCounter.icp.kbytes_recv, len);
815
816 if (ICP_QUERY == icp->opcode) {
817 statCounter.icp.queries_recv++;
818 kb_incr(&statCounter.icp.q_kbytes_recv, len);
819 } else {
820 statCounter.icp.replies_recv++;
821 kb_incr(&statCounter.icp.r_kbytes_recv, len);
822 /* statCounter.icp.query_svc_time set in clientUpdateCounters */
823 }
824
825 if (ICP_HIT == icp->opcode)
826 statCounter.icp.hits_recv++;
071a3ae7 827 }
828}
007b8be4 829
830#define N_QUERIED_KEYS 8192
831#define N_QUERIED_KEYS_MASK 8191
c3031d67 832static cache_key queried_keys[N_QUERIED_KEYS][SQUID_MD5_DIGEST_LENGTH];
007b8be4 833
834int
5942e8d4 835icpSetCacheKey(const cache_key * key)
007b8be4 836{
837 static int reqnum = 0;
62e76326 838
007b8be4 839 if (++reqnum < 0)
62e76326 840 reqnum = 1;
841
007b8be4 842 storeKeyCopy(queried_keys[reqnum & N_QUERIED_KEYS_MASK], key);
62e76326 843
007b8be4 844 return reqnum;
845}
846
847const cache_key *
848icpGetCacheKey(const char *url, int reqnum)
849{
850 if (neighbors_do_private_keys && reqnum)
62e76326 851 return queried_keys[reqnum & N_QUERIED_KEYS_MASK];
852
007b8be4 853 return storeKeyPublic(url, METHOD_GET);
854}