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