]> git.ipfire.org Git - thirdparty/squid.git/blame - src/icp_v2.cc
Upgrade Acl::Node::name to SBuf; remove AclMatchedName global (#1766)
[thirdparty/squid.git] / src / icp_v2.cc
CommitLineData
9cef6668 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
9cef6668 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9cef6668 7 */
8
bbc27441
AJ
9/* DEBUG: section 12 Internet Cache Protocol (ICP) */
10
63be0a78 11/**
12 \defgroup ServerProtocolICPInternal2 ICPv2 Internals
13 \ingroup ServerProtocolICPAPI
14 */
15
582c2af2
FC
16#include "squid.h"
17#include "AccessLogEntry.h"
18#include "acl/Acl.h"
19#include "acl/FilledChecklist.h"
e5ddd4ce 20#include "base/AsyncCallbacks.h"
95e6d864 21#include "client_db.h"
063dc1eb 22#include "comm.h"
09cee743 23#include "comm/Connection.h"
d841c88d 24#include "comm/Loops.h"
c4ad1349 25#include "fd.h"
f9b72e0c 26#include "HttpRequest.h"
9b5c4a9a 27#include "icmp/net_db.h"
582c2af2 28#include "ICP.h"
96d89ea0 29#include "ip/Address.h"
e0f8b709 30#include "ip/tools.h"
e5ddd4ce 31#include "ipc/StartListening.h"
714e68b7 32#include "ipcache.h"
582c2af2 33#include "md5.h"
afabcc13 34#include "multicast.h"
f0ba2534 35#include "neighbors.h"
c6f15d40 36#include "refresh.h"
1fa9b1a7 37#include "rfc1738.h"
4d5904f7 38#include "SquidConfig.h"
582c2af2
FC
39#include "StatCounters.h"
40#include "Store.h"
fb548aaf 41#include "store_key_md5.h"
4e540555 42#include "tools.h"
582c2af2 43#include "wordlist.h"
7a2f978b 44
1a30fdf5 45#include <cerrno>
21d845b1 46
d2a6dcba
EB
47/// a delayed icpUdpSend() call
48class DelayedUdpSend {
49public:
50 Ip::Address address; ///< remote peer (which may not be a cache_peer)
51 icp_common_t *msg = nullptr; ///< ICP message with network byte order fields
52 DelayedUdpSend *next = nullptr; ///< invasive FIFO queue of delayed ICP messages
53 AccessLogEntryPointer ale; ///< sender's master transaction summary
8b082ed9 54 struct timeval queue_time = {}; ///< queuing timestamp
d2a6dcba
EB
55};
56
e5ddd4ce 57static void icpIncomingConnectionOpened(Ipc::StartListeningAnswer &);
013e320c 58
63be0a78 59/// \ingroup ServerProtocolICPInternal2
d2a6dcba 60static void icpLogIcp(const Ip::Address &, const LogTags_ot, int, const char *, const int, AccessLogEntryPointer &);
62e76326 61
63be0a78 62/// \ingroup ServerProtocolICPInternal2
b7ac5457 63static void icpHandleIcpV2(int, Ip::Address &, char *, int);
63be0a78 64
65/// \ingroup ServerProtocolICPInternal2
071a3ae7 66static void icpCount(void *, int, size_t, int);
17b6e784 67
d2a6dcba
EB
68static LogTags_ot icpLogFromICPCode(icp_opcode);
69
70static int icpUdpSend(int fd, const Ip::Address &to, icp_common_t * msg, int delay, AccessLogEntryPointer al);
71
819be284 72static void
d2a6dcba 73icpSyncAle(AccessLogEntryPointer &al, const Ip::Address &caddr, const char *url, int len, int delay)
819be284
EB
74{
75 if (!al)
76 al = new AccessLogEntry();
77 al->icp.opcode = ICP_QUERY;
78 al->cache.caddr = caddr;
819be284 79 al->url = url;
bec110e4 80 al->setVirginUrlForMissingRequest(al->url);
819be284
EB
81 // XXX: move to use icp.clientReply instead
82 al->http.clientReplySz.payloadData = len;
83 al->cache.start_time = current_time;
84 al->cache.start_time.tv_sec -= delay;
85 al->cache.trTime.tv_sec = delay;
86 al->cache.trTime.tv_usec = 0;
87}
88
63be0a78 89/**
90 \ingroup ServerProtocolICPInternal2
48382032 91 * IcpQueueHead is global so comm_incoming() knows whether or not
92 * to call icpUdpSendQueue.
93 */
aee3523a 94static DelayedUdpSend *IcpQueueHead = nullptr;
63be0a78 95/// \ingroup ServerProtocolICPInternal2
aee3523a 96static DelayedUdpSend *IcpQueueTail = nullptr;
7a2f978b 97
e1f7507e 98/// \ingroup ServerProtocolICPInternal2
aee3523a 99Comm::ConnectionPointer icpIncomingConn = nullptr;
e0d28505 100/// \ingroup ServerProtocolICPInternal2
aee3523a 101Comm::ConnectionPointer icpOutgoingConn = nullptr;
e0d28505 102
e6ccf245 103/* icp_common_t */
cb835136 104icp_common_t::icp_common_t() :
f53969cc
SM
105 opcode(ICP_INVALID), version(0), length(0), reqnum(0),
106 flags(0), pad(0), shostid(0)
62e76326 107{}
e6ccf245 108
cb835136 109icp_common_t::icp_common_t(char *buf, unsigned int len) :
f53969cc 110 opcode(ICP_INVALID), version(0), reqnum(0), flags(0), pad(0), shostid(0)
e6ccf245 111{
cb835136 112 if (len < sizeof(icp_common_t)) {
62e76326 113 /* mark as invalid */
114 length = len + 1;
115 return;
e6ccf245 116 }
62e76326 117
41d00cd3 118 memcpy(this, buf, sizeof(icp_common_t));
e6ccf245 119 /*
120 * Convert network order sensitive fields
121 */
122 length = ntohs(length);
123 reqnum = ntohl(reqnum);
124 flags = ntohl(flags);
125 pad = ntohl(pad);
126}
127
128icp_opcode
cb835136 129icp_common_t::getOpCode() const
e6ccf245 130{
2fefc43c 131 if (opcode > static_cast<char>(icp_opcode::ICP_END))
62e76326 132 return ICP_INVALID;
133
2fefc43c 134 return static_cast<icp_opcode>(opcode);
e6ccf245 135}
136
137/* ICPState */
138
63be0a78 139ICPState::ICPState(icp_common_t &aHeader, HttpRequest *aRequest):
f53969cc
SM
140 header(aHeader),
141 request(aRequest),
142 fd(-1),
aee3523a 143 url(nullptr)
b248c2a3
AJ
144{
145 HTTPMSGLOCK(request);
146}
e6ccf245 147
148ICPState::~ICPState()
149{
150 safe_free(url);
f72fb56b 151 HTTPMSGUNLOCK(request);
e6ccf245 152}
153
819be284 154bool
7976fed3
EB
155ICPState::isHit() const
156{
157 const auto e = storeGetPublic(url, Http::METHOD_GET);
158
159 const auto hit = e && confirmAndPrepHit(*e);
160
161 if (e)
d868b138 162 e->abandon(__func__);
7976fed3
EB
163
164 return hit;
165}
166
167bool
168ICPState::confirmAndPrepHit(const StoreEntry &e) const
819be284 169{
819be284
EB
170 if (!e.validToSend())
171 return false;
172
173 if (!Config.onoff.icp_hit_stale && refreshCheckICP(&e, request))
174 return false;
175
d2a6dcba 176 if (e.hittingRequiresCollapsing() && !startCollapsingOn(e, false))
819be284
EB
177 return false;
178
179 return true;
180}
181
d2a6dcba 182LogTags *
7976fed3 183ICPState::loggingTags() const
d2a6dcba
EB
184{
185 // calling icpSyncAle(LOG_TAG_NONE) here would not change cache.code
186 if (!al)
187 al = new AccessLogEntry();
188 return &al->cache.code;
189}
190
819be284
EB
191void
192ICPState::fillChecklist(ACLFilledChecklist &checklist) const
193{
194 checklist.setRequest(request);
d2a6dcba 195 icpSyncAle(al, from, url, 0, 0);
819be284
EB
196 checklist.al = al;
197}
198
e6ccf245 199/* End ICPState */
200
201/* ICP2State */
62e76326 202
63be0a78 203/// \ingroup ServerProtocolICPInternal2
819be284 204class ICP2State: public ICPState
62e76326 205{
206
207public:
f72fb56b 208 ICP2State(icp_common_t & aHeader, HttpRequest *aRequest):
f53969cc 209 ICPState(aHeader, aRequest),rtt(0),src_rtt(0),flags(0) {}
62e76326 210
337b9aa4 211 ~ICP2State() override;
e6ccf245 212
213 int rtt;
214 int src_rtt;
09aabd84 215 uint32_t flags;
e6ccf245 216};
217
63be0a78 218ICP2State::~ICP2State()
62e76326 219{}
e6ccf245 220
e6ccf245 221/* End ICP2State */
222
d2a6dcba 223/// updates ALE (if any) and logs the transaction (if needed)
7a2f978b 224static void
d2a6dcba 225icpLogIcp(const Ip::Address &caddr, const LogTags_ot logcode, const int len, const char *url, int delay, AccessLogEntry::Pointer &al)
7a2f978b 226{
d2a6dcba 227 assert(logcode != LOG_TAG_NONE);
62e76326 228
d2a6dcba
EB
229 // Optimization: No premature (ALE creation in) icpSyncAle().
230 if (al) {
231 icpSyncAle(al, caddr, url, len, delay);
232 al->cache.code.update(logcode);
233 }
62e76326 234
d2a6dcba
EB
235 if (logcode == LOG_ICP_QUERY)
236 return; // we never log queries
62e76326 237
d2a6dcba
EB
238 if (!Config.onoff.log_udp) {
239 clientdbUpdate(caddr, al ? al->cache.code : LogTags(logcode), AnyP::PROTO_ICP, len);
62e76326 240 return;
d2a6dcba 241 }
62e76326 242
d2a6dcba
EB
243 if (!al) {
244 // The above attempt to optimize ALE creation has failed. We do need it.
245 icpSyncAle(al, caddr, url, len, delay);
246 al->cache.code.update(logcode);
247 }
248 clientdbUpdate(caddr, al->cache.code, AnyP::PROTO_ICP, len);
aee3523a 249 accessLogLog(al, nullptr);
7a2f978b 250}
251
63be0a78 252/// \ingroup ServerProtocolICPInternal2
8b082ed9 253static void
ced8def3 254icpUdpSendQueue(int fd, void *)
7a2f978b 255{
d2a6dcba 256 DelayedUdpSend *q;
62e76326 257
aee3523a 258 while ((q = IcpQueueHead) != nullptr) {
b115733c 259 int delay = tvSubUsec(q->queue_time, current_time);
62e76326 260 /* increment delay to prevent looping */
d2a6dcba 261 const int x = icpUdpSend(fd, q->address, q->msg, ++delay, q->ale);
62e76326 262 IcpQueueHead = q->next;
d2a6dcba 263 delete q;
62e76326 264
265 if (x < 0)
266 break;
7a2f978b 267 }
268}
269
cb835136
AR
270icp_common_t *
271icp_common_t::CreateMessage(
7a2f978b 272 icp_opcode opcode,
273 int flags,
274 const char *url,
275 int reqnum,
276 int pad)
277{
aee3523a
AR
278 char *buf = nullptr;
279 icp_common_t *headerp = nullptr;
280 char *urloffset = nullptr;
7a2f978b 281 int buf_len;
282 buf_len = sizeof(icp_common_t) + strlen(url) + 1;
62e76326 283
27cd7235 284 if (opcode == ICP_QUERY)
09aabd84 285 buf_len += sizeof(uint32_t);
62e76326 286
e6ccf245 287 buf = (char *) xcalloc(buf_len, 1);
62e76326 288
7a2f978b 289 headerp = (icp_common_t *) (void *) buf;
62e76326 290
79d39a72 291 headerp->opcode = (char) opcode;
62e76326 292
7a2f978b 293 headerp->version = ICP_VERSION_CURRENT;
62e76326 294
09aabd84 295 headerp->length = (uint16_t) htons(buf_len);
62e76326 296
7a2f978b 297 headerp->reqnum = htonl(reqnum);
62e76326 298
7a2f978b 299 headerp->flags = htonl(flags);
62e76326 300
7a2f978b 301 headerp->pad = htonl(pad);
62e76326 302
9ca56f05 303 headerp->shostid = 0;
62e76326 304
7a2f978b 305 urloffset = buf + sizeof(icp_common_t);
62e76326 306
27cd7235 307 if (opcode == ICP_QUERY)
09aabd84 308 urloffset += sizeof(uint32_t);
62e76326 309
41d00cd3 310 memcpy(urloffset, url, strlen(url));
62e76326 311
e6ccf245 312 return (icp_common_t *)buf;
7a2f978b 313}
314
d2a6dcba
EB
315// TODO: Move retries to icpCreateAndSend(); the other caller does not retry.
316/// writes the given UDP msg to the socket; queues a retry on the first failure
317/// \returns a negative number on failures
318static int
7a2f978b 319icpUdpSend(int fd,
b7ac5457 320 const Ip::Address &to,
62e76326 321 icp_common_t * msg,
819be284
EB
322 int delay,
323 AccessLogEntryPointer al)
7a2f978b 324{
8e68922c 325 int x;
17b6e784 326 int len;
327 len = (int) ntohs(msg->length);
bf8fe701 328 debugs(12, 5, "icpUdpSend: FD " << fd << " sending " <<
cc192b50 329 icp_opcode_str[msg->opcode] << ", " << len << " bytes to " << to);
bf8fe701 330
cc192b50 331 x = comm_udp_sendto(fd, to, msg, len);
62e76326 332
26ac0430 333 if (x >= 0) {
62e76326 334 /* successfully written */
d2a6dcba 335 const auto logcode = icpLogFromICPCode(static_cast<icp_opcode>(msg->opcode));
819be284 336 icpLogIcp(to, logcode, len, (char *) (msg + 1), delay, al);
62e76326 337 icpCount(msg, SENT, (size_t) len, delay);
338 safe_free(msg);
26ac0430 339 } else if (0 == delay) {
62e76326 340 /* send failed, but queue it */
d2a6dcba 341 const auto queue = new DelayedUdpSend();
cc192b50 342 queue->address = to;
62e76326 343 queue->msg = msg;
62e76326 344 queue->queue_time = current_time;
d2a6dcba 345 queue->ale = al;
62e76326 346
aee3523a 347 if (IcpQueueHead == nullptr) {
62e76326 348 IcpQueueHead = queue;
349 IcpQueueTail = queue;
350 } else if (IcpQueueTail == IcpQueueHead) {
351 IcpQueueTail = queue;
352 IcpQueueHead->next = queue;
353 } else {
354 IcpQueueTail->next = queue;
355 IcpQueueTail = queue;
356 }
357
aee3523a 358 Comm::SetSelect(fd, COMM_SELECT_WRITE, icpUdpSendQueue, nullptr, 0);
95dc7ff4 359 ++statCounter.icp.replies_queued;
26ac0430 360 } else {
62e76326 361 /* don't queue it */
d2a6dcba 362 // XXX: safe_free(msg)
95dc7ff4 363 ++statCounter.icp.replies_dropped;
8e68922c 364 }
62e76326 365
17b6e784 366 return x;
7a2f978b 367}
368
63be0a78 369/**
bef81ea5 370 * This routine selects an ICP opcode for ICP misses.
63be0a78 371 *
372 \retval ICP_ERR no opcode selected here
373 \retval ICP_MISS_NOFETCH store is rebuilding, no fetch is possible yet
bef81ea5 374 */
e6ccf245 375icp_opcode
376icpGetCommonOpcode()
377{
bef81ea5 378 /* if store is rebuilding, return a UDP_MISS_NOFETCH */
62e76326 379
871899ca 380 if ((StoreController::store_dirs_rebuilding && opt_reload_hit_only) ||
62e76326 381 hit_only_mode_until > squid_curtime) {
382 return ICP_MISS_NOFETCH;
e6ccf245 383 }
62e76326 384
e6ccf245 385 return ICP_ERR;
386}
387
d2a6dcba 388static LogTags_ot
e6ccf245 389icpLogFromICPCode(icp_opcode opcode)
390{
391 if (opcode == ICP_ERR)
62e76326 392 return LOG_UDP_INVALID;
393
e6ccf245 394 if (opcode == ICP_DENIED)
62e76326 395 return LOG_UDP_DENIED;
396
e6ccf245 397 if (opcode == ICP_HIT)
62e76326 398 return LOG_UDP_HIT;
399
e6ccf245 400 if (opcode == ICP_MISS)
62e76326 401 return LOG_UDP_MISS;
402
e6ccf245 403 if (opcode == ICP_MISS_NOFETCH)
62e76326 404 return LOG_UDP_MISS_NOFETCH;
405
d2a6dcba
EB
406 if (opcode == ICP_DECHO)
407 return LOG_ICP_QUERY;
408
409 if (opcode == ICP_QUERY)
410 return LOG_ICP_QUERY;
411
e6ccf245 412 fatal("expected ICP opcode\n");
62e76326 413
e6ccf245 414 return LOG_UDP_INVALID;
415}
416
417void
819be284 418icpCreateAndSend(icp_opcode opcode, int flags, char const *url, int reqnum, int pad, int fd, const Ip::Address &from, AccessLogEntry::Pointer al)
e6ccf245 419{
d2a6dcba
EB
420 // update potentially shared ALE ASAP; the ICP query itself may be delayed
421 if (al)
422 al->cache.code.update(icpLogFromICPCode(opcode));
cb835136 423 icp_common_t *reply = icp_common_t::CreateMessage(opcode, flags, url, reqnum, pad);
d2a6dcba 424 icpUdpSend(fd, from, reply, 0, al);
e6ccf245 425}
426
427void
b7ac5457 428icpDenyAccess(Ip::Address &from, char *url, int reqnum, int fd)
e6ccf245 429{
26ac0430 430 if (clientdbCutoffDenied(from)) {
62e76326 431 /*
432 * count this DENIED query in the clientdb, even though
433 * we're not sending an ICP reply...
434 */
d2a6dcba 435 clientdbUpdate(from, LogTags(LOG_UDP_DENIED), AnyP::PROTO_ICP, 0);
26ac0430 436 } else {
819be284 437 icpCreateAndSend(ICP_DENIED, 0, url, reqnum, 0, fd, from, nullptr);
e6ccf245 438 }
439}
440
2efeb0b7 441bool
b7ac5457 442icpAccessAllowed(Ip::Address &from, HttpRequest * icp_request)
7a2f978b 443{
25aa6c9a
AR
444 if (!Config.accessList.icp) {
445 debugs(12, 2, "Access Denied due to lack of ICP access rules.");
638402dd 446 return false;
25aa6c9a 447 }
b50e327b 448
aee3523a 449 ACLFilledChecklist checklist(Config.accessList.icp, icp_request, nullptr);
cc192b50 450 checklist.src_addr = from;
4dd643d5 451 checklist.my_addr.setNoAddr();
25aa6c9a
AR
452 const auto &answer = checklist.fastCheck();
453 if (answer.allowed())
454 return true;
455
456 debugs(12, 2, "Access Denied for " << from << " by " << answer.lastCheckDescription() << ".");
457 return false;
e6ccf245 458}
459
190154cf 460HttpRequest *
b7ac5457 461icpGetRequest(char *url, int reqnum, int fd, Ip::Address &from)
e6ccf245 462{
26ac0430 463 if (strpbrk(url, w_space)) {
62e76326 464 url = rfc1738_escape(url);
819be284 465 icpCreateAndSend(ICP_ERR, 0, rfc1738_escape(url), reqnum, 0, fd, from, nullptr);
aee3523a 466 return nullptr;
e6ccf245 467 }
62e76326 468
ad05b958 469 const auto mx = MasterXaction::MakePortless<XactionInitiator::initIcp>();
6c880a16
AJ
470 auto *result = HttpRequest::FromUrlXXX(url, mx);
471 if (!result)
819be284 472 icpCreateAndSend(ICP_ERR, 0, url, reqnum, 0, fd, from, nullptr);
62e76326 473
e6ccf245 474 return result;
475
476}
477
478static void
b7ac5457 479doV2Query(int fd, Ip::Address &from, char *buf, icp_common_t header)
e6ccf245 480{
481 int rtt = 0;
7a2f978b 482 int src_rtt = 0;
09aabd84 483 uint32_t flags = 0;
e6ccf245 484 /* We have a valid packet */
09aabd84 485 char *url = buf + sizeof(icp_common_t) + sizeof(uint32_t);
cc192b50 486 HttpRequest *icp_request = icpGetRequest(url, header.reqnum, fd, from);
62e76326 487
e6ccf245 488 if (!icp_request)
62e76326 489 return;
490
6dd9f4bd 491 HTTPMSGLOCK(icp_request);
319bf5a7 492
26ac0430 493 if (!icpAccessAllowed(from, icp_request)) {
cc192b50 494 icpDenyAccess(from, url, header.reqnum, fd);
6dd9f4bd 495 HTTPMSGUNLOCK(icp_request);
62e76326 496 return;
e6ccf245 497 }
9b5c4a9a 498#if USE_ICMP
26ac0430 499 if (header.flags & ICP_FLAG_SRC_RTT) {
5c51bffb
AJ
500 rtt = netdbHostRtt(icp_request->url.host());
501 int hops = netdbHostHops(icp_request->url.host());
62e76326 502 src_rtt = ((hops & 0xFFFF) << 16) | (rtt & 0xFFFF);
503
504 if (rtt)
505 flags |= ICP_FLAG_SRC_RTT;
e6ccf245 506 }
9b5c4a9a 507#endif /* USE_ICMP */
62e76326 508
e6ccf245 509 /* The peer is allowed to use this cache */
7976fed3
EB
510 ICP2State state(header, icp_request);
511 state.fd = fd;
512 state.from = from;
513 state.url = xstrdup(url);
514 state.flags = flags;
515 state.rtt = rtt;
516 state.src_rtt = src_rtt;
62e76326 517
7976fed3
EB
518 icp_opcode codeToSend;
519
520 if (state.isHit()) {
521 codeToSend = ICP_HIT;
522 } else {
523#if USE_ICMP
524 if (Config.onoff.test_reachability && state.rtt == 0) {
525 if ((state.rtt = netdbHostRtt(state.request->url.host())) == 0)
526 netdbPingSite(state.request->url.host());
527 }
528#endif /* USE_ICMP */
529
530 if (icpGetCommonOpcode() != ICP_ERR)
531 codeToSend = icpGetCommonOpcode();
532 else if (Config.onoff.test_reachability && rtt == 0)
533 codeToSend = ICP_MISS_NOFETCH;
534 else
535 codeToSend = ICP_MISS;
536 }
537
538 icpCreateAndSend(codeToSend, flags, url, header.reqnum, src_rtt, fd, from, state.al);
319bf5a7 539
6dd9f4bd 540 HTTPMSGUNLOCK(icp_request);
e6ccf245 541}
542
543void
cb835136 544icp_common_t::handleReply(char *buf, Ip::Address &from)
e6ccf245 545{
26ac0430 546 if (neighbors_do_private_keys && reqnum == 0) {
fa84c01d
FC
547 debugs(12, DBG_CRITICAL, "icpHandleIcpV2: Neighbor " << from << " returned reqnum = 0");
548 debugs(12, DBG_CRITICAL, "icpHandleIcpV2: Disabling use of private keys");
62e76326 549 neighbors_do_private_keys = 0;
e6ccf245 550 }
62e76326 551
e6ccf245 552 char *url = buf + sizeof(icp_common_t);
cc192b50 553 debugs(12, 3, "icpHandleIcpV2: " << icp_opcode_str[opcode] << " from " << from << " for '" << url << "'");
bf8fe701 554
e6ccf245 555 const cache_key *key = icpGetCacheKey(url, (int) reqnum);
556 /* call neighborsUdpAck even if ping_status != PING_WAITING */
557 neighborsUdpAck(key, this, from);
558}
559
560static void
b7ac5457 561icpHandleIcpV2(int fd, Ip::Address &from, char *buf, int len)
e6ccf245 562{
26ac0430 563 if (len <= 0) {
bf8fe701 564 debugs(12, 3, "icpHandleIcpV2: ICP message is too small");
62e76326 565 return;
e6ccf245 566 }
62e76326 567
e6ccf245 568 icp_common_t header(buf, len);
7b83b3d9 569 /*
570 * Length field should match the number of bytes read
571 */
62e76326 572
26ac0430 573 if (len != header.length) {
bf8fe701 574 debugs(12, 3, "icpHandleIcpV2: ICP message is too small");
62e76326 575 return;
7b83b3d9 576 }
62e76326 577
7976fed3
EB
578 debugs(12, 5, "OPCODE " << icp_opcode_str[header.getOpCode()] << '=' << uint8_t(header.opcode));
579
26ac0430 580 switch (header.opcode) {
62e76326 581
27cd7235 582 case ICP_QUERY:
62e76326 583 /* We have a valid packet */
584 doV2Query(fd, from, buf, header);
585 break;
7a2f978b 586
27cd7235 587 case ICP_HIT:
62e76326 588
27cd7235 589 case ICP_DECHO:
62e76326 590
27cd7235 591 case ICP_MISS:
62e76326 592
27cd7235 593 case ICP_DENIED:
62e76326 594
27cd7235 595 case ICP_MISS_NOFETCH:
cc192b50 596 header.handleReply(buf, from);
62e76326 597 break;
7a2f978b 598
27cd7235 599 case ICP_INVALID:
62e76326 600
27cd7235 601 case ICP_ERR:
62e76326 602 break;
7a2f978b 603
604 default:
d816f28d 605 debugs(12, DBG_CRITICAL, "ERROR: icpHandleIcpV2: Unknown opcode: " << header.opcode << " from " << from);
bf8fe701 606
62e76326 607 break;
7a2f978b 608 }
7a2f978b 609}
610
7a2f978b 611void
ced8def3 612icpHandleUdp(int sock, void *)
7a2f978b 613{
d193a436 614 int *N = &incoming_sockets_accepted;
62e76326 615
b7ac5457 616 Ip::Address from;
7a2f978b 617 LOCAL_ARRAY(char, buf, SQUID_UDP_SO_RCVBUF);
0b6d1955 618 int len;
7a2f978b 619 int icp_version;
65d448bc 620 int max = INCOMING_UDP_MAX;
aee3523a 621 Comm::SetSelect(sock, COMM_SELECT_READ, icpHandleUdp, nullptr, 0);
62e76326 622
5e263176
FC
623 while (max) {
624 --max;
62e76326 625 len = comm_udp_recvfrom(sock,
626 buf,
627 SQUID_UDP_SO_RCVBUF - 1,
628 0,
cc192b50 629 from);
62e76326 630
631 if (len == 0)
632 break;
633
634 if (len < 0) {
b69e9ffa
AJ
635 int xerrno = errno;
636 if (ignoreErrno(xerrno))
62e76326 637 break;
638
1191b93b 639#if _SQUID_LINUX_
62e76326 640 /* Some Linux systems seem to set the FD for reading and then
641 * return ECONNREFUSED when sendto() fails and generates an ICMP
642 * port unreachable message. */
643 /* or maybe an EHOSTUNREACH "No route to host" message */
b69e9ffa 644 if (xerrno != ECONNREFUSED && xerrno != EHOSTUNREACH)
7a2f978b 645#endif
b69e9ffa 646 debugs(50, DBG_IMPORTANT, "icpHandleUdp: FD " << sock << " recvfrom: " << xstrerr(xerrno));
62e76326 647
648 break;
649 }
650
95dc7ff4 651 ++(*N);
62e76326 652 icpCount(buf, RECV, (size_t) len, 0);
653 buf[len] = '\0';
bf8fe701 654 debugs(12, 4, "icpHandleUdp: FD " << sock << ": received " <<
cc192b50 655 (unsigned long int)len << " bytes from " << from);
bf8fe701 656
0b6d1955 657 if ((size_t) len < sizeof(icp_common_t)) {
bf8fe701 658 debugs(12, 4, "icpHandleUdp: Ignoring too-small UDP packet");
62e76326 659 break;
660 }
661
f53969cc 662 icp_version = (int) buf[1]; /* cheat! */
62e76326 663
1b76e6c1 664 if (icpOutgoingConn->local == from)
7c8931a1
AJ
665 // ignore ICP packets which loop back (multicast usually)
666 debugs(12, 4, "icpHandleUdp: Ignoring UDP packet sent by myself");
667 else if (icp_version == ICP_VERSION_2)
62e76326 668 icpHandleIcpV2(sock, from, buf, len);
669 else if (icp_version == ICP_VERSION_3)
670 icpHandleIcpV3(sock, from, buf, len);
671 else
e0236918 672 debugs(12, DBG_IMPORTANT, "WARNING: Unused ICP version " << icp_version <<
26ac0430 673 " received from " << from);
7a2f978b 674 }
7a2f978b 675}
15df8349 676
677void
65d448bc 678icpOpenPorts(void)
15df8349 679{
09aabd84 680 uint16_t port;
62e76326 681
15df8349 682 if ((port = Config.Port.icp) <= 0)
62e76326 683 return;
684
e0d28505
AJ
685 icpIncomingConn = new Comm::Connection;
686 icpIncomingConn->local = Config.Addrs.udp_incoming;
4dd643d5 687 icpIncomingConn->local.port(port);
62e76326 688
4dd643d5 689 if (!Ip::EnableIpv6 && !icpIncomingConn->local.setIPv4()) {
e0d28505 690 debugs(12, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << icpIncomingConn->local << " is not an IPv4 address.");
e0f8b709
AJ
691 fatal("ICP port cannot be opened.");
692 }
e0209dae 693 /* split-stack for now requires default IPv4-only ICP */
4dd643d5
AJ
694 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && icpIncomingConn->local.isAnyAddr()) {
695 icpIncomingConn->local.setIPv4();
e0209dae 696 }
e0f8b709 697
e5ddd4ce 698 auto call = asyncCallbackFun(12, 2, icpIncomingConnectionOpened);
013e320c 699 Ipc::StartListening(SOCK_DGRAM,
5667a628 700 IPPROTO_UDP,
e0d28505 701 icpIncomingConn,
5667a628 702 Ipc::fdnInIcpSocket, call);
62e76326 703
4dd643d5 704 if ( !Config.Addrs.udp_outgoing.isNoAddr() ) {
e0d28505
AJ
705 icpOutgoingConn = new Comm::Connection;
706 icpOutgoingConn->local = Config.Addrs.udp_outgoing;
4dd643d5 707 icpOutgoingConn->local.port(port);
e0f8b709 708
4dd643d5 709 if (!Ip::EnableIpv6 && !icpOutgoingConn->local.setIPv4()) {
e0d28505 710 debugs(49, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << icpOutgoingConn->local << " is not an IPv4 address.");
e0f8b709
AJ
711 fatal("ICP port cannot be opened.");
712 }
e0209dae 713 /* split-stack for now requires default IPv4-only ICP */
4dd643d5
AJ
714 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && icpOutgoingConn->local.isAnyAddr()) {
715 icpOutgoingConn->local.setIPv4();
e0209dae 716 }
e0f8b709 717
e0d28505
AJ
718 enter_suid();
719 comm_open_listener(SOCK_DGRAM, IPPROTO_UDP, icpOutgoingConn, "Outgoing ICP Port");
62e76326 720 leave_suid();
721
e0d28505 722 if (!Comm::IsConnOpen(icpOutgoingConn))
62e76326 723 fatal("Cannot open Outgoing ICP Port");
724
e0d28505 725 debugs(12, DBG_CRITICAL, "Sending ICP messages from " << icpOutgoingConn->local);
62e76326 726
aee3523a 727 Comm::SetSelect(icpOutgoingConn->fd, COMM_SELECT_READ, icpHandleUdp, nullptr, 0);
25b481e6 728 fd_note(icpOutgoingConn->fd, "Outgoing ICP socket");
15df8349 729 }
236dedc0 730}
62e76326 731
013e320c 732static void
e5ddd4ce 733icpIncomingConnectionOpened(Ipc::StartListeningAnswer &answer)
013e320c 734{
e5ddd4ce
AR
735 const auto &conn = answer.conn;
736
09cee743 737 if (!Comm::IsConnOpen(conn))
013e320c
AR
738 fatal("Cannot open ICP Port");
739
aee3523a 740 Comm::SetSelect(conn->fd, COMM_SELECT_READ, icpHandleUdp, nullptr, 0);
013e320c
AR
741
742 for (const wordlist *s = Config.mcast_group_list; s; s = s->next)
aee3523a 743 ipcache_nbgethostbyname(s->key, mcastJoinGroups, nullptr); // XXX: pass the conn for mcastJoinGroups usage.
013e320c 744
09cee743 745 debugs(12, DBG_IMPORTANT, "Accepting ICP messages on " << conn->local);
013e320c 746
09cee743 747 fd_note(conn->fd, "Incoming ICP port");
013e320c 748
4dd643d5 749 if (Config.Addrs.udp_outgoing.isNoAddr()) {
09cee743 750 icpOutgoingConn = conn;
e0d28505
AJ
751 debugs(12, DBG_IMPORTANT, "Sending ICP messages from " << icpOutgoingConn->local);
752 }
013e320c
AR
753}
754
63be0a78 755/**
26ac0430 756 * icpConnectionShutdown only closes the 'in' socket if it is
17e6c0a1 757 * different than the 'out' socket.
758 */
c0fbae16 759void
17e6c0a1 760icpConnectionShutdown(void)
c0fbae16 761{
e0d28505 762 if (!Comm::IsConnOpen(icpIncomingConn))
62e76326 763 return;
764
e0d28505 765 debugs(12, DBG_IMPORTANT, "Stop receiving ICP on " << icpIncomingConn->local);
62e76326 766
e0d28505
AJ
767 /** Release the 'in' socket for lazy closure.
768 * in and out sockets may be sharing one same FD.
769 * This prevents this function from executing repeatedly.
c0fbae16 770 */
aee3523a 771 icpIncomingConn = nullptr;
62e76326 772
63be0a78 773 /**
c0fbae16 774 * Normally we only write to the outgoing ICP socket, but
775 * we also have a read handler there to catch messages sent
776 * to that specific interface. During shutdown, we must
777 * disable reading on the outgoing socket.
778 */
e0d28505 779 assert(Comm::IsConnOpen(icpOutgoingConn));
62e76326 780
aee3523a 781 Comm::SetSelect(icpOutgoingConn->fd, COMM_SELECT_READ, nullptr, nullptr, 0);
c0fbae16 782}
17e6c0a1 783
784void
65d448bc 785icpClosePorts(void)
17e6c0a1 786{
787 icpConnectionShutdown();
62e76326 788
aee3523a 789 if (icpOutgoingConn != nullptr) {
e0d28505 790 debugs(12, DBG_IMPORTANT, "Stop sending ICP from " << icpOutgoingConn->local);
aee3523a 791 icpOutgoingConn = nullptr;
17e6c0a1 792 }
793}
071a3ae7 794
795static void
796icpCount(void *buf, int which, size_t len, int delay)
797{
e6ccf245 798 icp_common_t *icp = (icp_common_t *) buf;
62e76326 799
071a3ae7 800 if (len < sizeof(*icp))
62e76326 801 return;
802
071a3ae7 803 if (SENT == which) {
95dc7ff4 804 ++statCounter.icp.pkts_sent;
a0864754 805 statCounter.icp.kbytes_sent += len;
62e76326 806
807 if (ICP_QUERY == icp->opcode) {
95dc7ff4 808 ++statCounter.icp.queries_sent;
a0864754 809 statCounter.icp.q_kbytes_sent += len;
62e76326 810 } else {
95dc7ff4 811 ++statCounter.icp.replies_sent;
a0864754 812 statCounter.icp.r_kbytes_sent += len;
62e76326 813 /* this is the sent-reply service time */
e8baef82 814 statCounter.icp.replySvcTime.count(delay);
62e76326 815 }
816
817 if (ICP_HIT == icp->opcode)
95dc7ff4 818 ++statCounter.icp.hits_sent;
071a3ae7 819 } else if (RECV == which) {
95dc7ff4 820 ++statCounter.icp.pkts_recv;
a0864754 821 statCounter.icp.kbytes_recv += len;
62e76326 822
823 if (ICP_QUERY == icp->opcode) {
95dc7ff4 824 ++statCounter.icp.queries_recv;
a0864754 825 statCounter.icp.q_kbytes_recv += len;
62e76326 826 } else {
95dc7ff4 827 ++statCounter.icp.replies_recv;
a0864754 828 statCounter.icp.r_kbytes_recv += len;
e8baef82 829 /* statCounter.icp.querySvcTime set in clientUpdateCounters */
62e76326 830 }
831
832 if (ICP_HIT == icp->opcode)
95dc7ff4 833 ++statCounter.icp.hits_recv;
071a3ae7 834 }
835}
007b8be4 836
837#define N_QUERIED_KEYS 8192
838#define N_QUERIED_KEYS_MASK 8191
c3031d67 839static cache_key queried_keys[N_QUERIED_KEYS][SQUID_MD5_DIGEST_LENGTH];
007b8be4 840
841int
5942e8d4 842icpSetCacheKey(const cache_key * key)
007b8be4 843{
844 static int reqnum = 0;
62e76326 845
007b8be4 846 if (++reqnum < 0)
62e76326 847 reqnum = 1;
848
007b8be4 849 storeKeyCopy(queried_keys[reqnum & N_QUERIED_KEYS_MASK], key);
62e76326 850
007b8be4 851 return reqnum;
852}
853
854const cache_key *
855icpGetCacheKey(const char *url, int reqnum)
856{
857 if (neighbors_do_private_keys && reqnum)
62e76326 858 return queried_keys[reqnum & N_QUERIED_KEYS_MASK];
859
c2a7cefd 860 return storeKeyPublic(url, Http::METHOD_GET);
007b8be4 861}
f53969cc 862