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