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