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