]> git.ipfire.org Git - thirdparty/squid.git/blame - src/icp_v2.cc
Prep for 3.3.12 and 3.4.4
[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
582c2af2
FC
38#include "squid.h"
39#include "AccessLogEntry.h"
40#include "acl/Acl.h"
41#include "acl/FilledChecklist.h"
95e6d864 42#include "client_db.h"
063dc1eb 43#include "comm.h"
09cee743 44#include "comm/Connection.h"
d841c88d 45#include "comm/Loops.h"
09cee743 46#include "comm/UdpOpenDialer.h"
c4ad1349 47#include "fd.h"
f9b72e0c 48#include "HttpRequest.h"
9b5c4a9a 49#include "icmp/net_db.h"
582c2af2 50#include "ICP.h"
96d89ea0 51#include "ip/Address.h"
e0f8b709 52#include "ip/tools.h"
714e68b7 53#include "ipcache.h"
582c2af2 54#include "md5.h"
afabcc13 55#include "multicast.h"
f0ba2534 56#include "neighbors.h"
c6f15d40 57#include "refresh.h"
1fa9b1a7 58#include "rfc1738.h"
4d5904f7 59#include "SquidConfig.h"
582c2af2
FC
60#include "SquidTime.h"
61#include "StatCounters.h"
62#include "Store.h"
fb548aaf 63#include "store_key_md5.h"
582c2af2 64#include "SwapDir.h"
4e540555 65#include "tools.h"
582c2af2 66#include "wordlist.h"
7a2f978b 67
21d845b1
FC
68#if HAVE_ERRNO_H
69#include <errno.h>
70#endif
71
09cee743 72static void icpIncomingConnectionOpened(const Comm::ConnectionPointer &conn, int errNo);
013e320c 73
63be0a78 74/// \ingroup ServerProtocolICPInternal2
02c8dde5 75static void icpLogIcp(const Ip::Address &, LogTags, int, const char *, int);
62e76326 76
63be0a78 77/// \ingroup ServerProtocolICPInternal2
b7ac5457 78static void icpHandleIcpV2(int, Ip::Address &, char *, int);
63be0a78 79
80/// \ingroup ServerProtocolICPInternal2
071a3ae7 81static void icpCount(void *, int, size_t, int);
17b6e784 82
63be0a78 83/**
84 \ingroup ServerProtocolICPInternal2
48382032 85 * IcpQueueHead is global so comm_incoming() knows whether or not
86 * to call icpUdpSendQueue.
87 */
d4cb310b 88static icpUdpData *IcpQueueHead = NULL;
63be0a78 89/// \ingroup ServerProtocolICPInternal2
90static icpUdpData *IcpQueueTail = NULL;
7a2f978b 91
e1f7507e 92/// \ingroup ServerProtocolICPInternal2
e0d28505
AJ
93Comm::ConnectionPointer icpIncomingConn = NULL;
94/// \ingroup ServerProtocolICPInternal2
95Comm::ConnectionPointer icpOutgoingConn = NULL;
96
e6ccf245 97/* icp_common_t */
fad2588a 98_icp_common_t::_icp_common_t() :
f53156c2
A
99 opcode(ICP_INVALID), version(0), length(0), reqnum(0),
100 flags(0), pad(0), shostid(0)
62e76326 101{}
e6ccf245 102
fad2588a 103_icp_common_t::_icp_common_t(char *buf, unsigned int len) :
f53156c2 104 opcode(ICP_INVALID), version(0), reqnum(0), flags(0), pad(0), shostid(0)
e6ccf245 105{
106 if (len < sizeof(_icp_common_t)) {
62e76326 107 /* mark as invalid */
108 length = len + 1;
109 return;
e6ccf245 110 }
62e76326 111
41d00cd3 112 memcpy(this, buf, sizeof(icp_common_t));
e6ccf245 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 134 header(aHeader),
b248c2a3 135 request(aRequest),
62e76326 136 fd(-1),
137 url(NULL)
b248c2a3
AJ
138{
139 HTTPMSGLOCK(request);
140}
e6ccf245 141
142ICPState::~ICPState()
143{
144 safe_free(url);
f72fb56b 145 HTTPMSGUNLOCK(request);
e6ccf245 146}
147
e6ccf245 148/* End ICPState */
149
150/* ICP2State */
62e76326 151
63be0a78 152/// \ingroup ServerProtocolICPInternal2
153class ICP2State : public ICPState, public StoreClient
62e76326 154{
155
156public:
f72fb56b 157 ICP2State(icp_common_t & aHeader, HttpRequest *aRequest):
26ac0430 158 ICPState(aHeader, aRequest),rtt(0),src_rtt(0),flags(0) {}
62e76326 159
160 ~ICP2State();
e6ccf245 161 void created(StoreEntry * newEntry);
162
163 int rtt;
164 int src_rtt;
09aabd84 165 uint32_t flags;
e6ccf245 166};
167
63be0a78 168ICP2State::~ICP2State()
62e76326 169{}
e6ccf245 170
171void
63be0a78 172ICP2State::created(StoreEntry *newEntry)
e6ccf245 173{
174 StoreEntry *entry = newEntry->isNull () ? NULL : newEntry;
bf8fe701 175 debugs(12, 5, "icpHandleIcpV2: OPCODE " << icp_opcode_str[header.opcode]);
e6ccf245 176 icp_opcode codeToSend;
62e76326 177
e6ccf245 178 if (icpCheckUdpHit(entry, request)) {
62e76326 179 codeToSend = ICP_HIT;
e6ccf245 180 } else {
9b5c4a9a 181#if USE_ICMP
62e76326 182 if (Config.onoff.test_reachability && rtt == 0) {
cc192b50 183 if ((rtt = netdbHostRtt(request->GetHost())) == 0)
184 netdbPingSite(request->GetHost());
62e76326 185 }
9b5c4a9a 186#endif /* USE_ICMP */
62e76326 187
188 if (icpGetCommonOpcode() != ICP_ERR)
189 codeToSend = icpGetCommonOpcode();
190 else if (Config.onoff.test_reachability && rtt == 0)
191 codeToSend = ICP_MISS_NOFETCH;
192 else
193 codeToSend = ICP_MISS;
e6ccf245 194 }
62e76326 195
cc192b50 196 icpCreateAndSend(codeToSend, flags, url, header.reqnum, src_rtt, fd, from);
e6ccf245 197 delete this;
198}
199
200/* End ICP2State */
201
63be0a78 202/// \ingroup ServerProtocolICPInternal2
7a2f978b 203static void
02c8dde5 204icpLogIcp(const Ip::Address &caddr, LogTags logcode, int len, const char *url, int delay)
7a2f978b 205{
41ebd397 206 AccessLogEntry::Pointer al = new AccessLogEntry();
62e76326 207
96c617da 208 if (LOG_TAG_NONE == logcode)
62e76326 209 return;
210
071a3ae7 211 if (LOG_ICP_QUERY == logcode)
62e76326 212 return;
213
0c3d3f65 214 clientdbUpdate(caddr, logcode, AnyP::PROTO_ICP, len);
62e76326 215
7a2f978b 216 if (!Config.onoff.log_udp)
62e76326 217 return;
218
41ebd397 219 al->icp.opcode = ICP_QUERY;
62e76326 220
41ebd397 221 al->url = url;
62e76326 222
41ebd397 223 al->cache.caddr = caddr;
62e76326 224
d6df21d2 225 // XXX: move to use icp.clientReply instead
cc0ca3b9 226 al->http.clientReplySz.payloadData = len;
62e76326 227
41ebd397 228 al->cache.code = logcode;
62e76326 229
41ebd397 230 al->cache.msec = delay;
62e76326 231
41ebd397 232 accessLogLog(al, NULL);
7a2f978b 233}
234
63be0a78 235/// \ingroup ServerProtocolICPInternal2
48382032 236void
17b6e784 237icpUdpSendQueue(int fd, void *unused)
7a2f978b 238{
17b6e784 239 icpUdpData *q;
62e76326 240
48382032 241 while ((q = IcpQueueHead) != NULL) {
b115733c 242 int delay = tvSubUsec(q->queue_time, current_time);
62e76326 243 /* increment delay to prevent looping */
b115733c 244 const int x = icpUdpSend(fd, q->address, (icp_common_t *) q->msg, q->logcode, ++delay);
62e76326 245 IcpQueueHead = q->next;
b115733c 246 xfree(q);
62e76326 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
9ca56f05 286 headerp->shostid = 0;
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
41d00cd3 293 memcpy(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,
02c8dde5 302 LogTags logcode,
62e76326 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
d841c88d 339 Comm::SetSelect(fd, COMM_SELECT_WRITE, icpUdpSendQueue, NULL, 0);
95dc7ff4 340 ++statCounter.icp.replies_queued;
26ac0430 341 } else {
62e76326 342 /* don't queue it */
95dc7ff4 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
02c8dde5 386LogTags
e6ccf245 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 */
0c3d3f65 426 clientdbUpdate(from, LOG_UDP_DENIED, AnyP::PROTO_ICP, 0);
26ac0430 427 } else {
62e76326 428 icpCreateAndSend(ICP_DENIED, 0, url, reqnum, 0, fd, from);
e6ccf245 429 }
430}
431
2efeb0b7 432bool
b7ac5457 433icpAccessAllowed(Ip::Address &from, HttpRequest * icp_request)
7a2f978b 434{
638402dd 435 /* absent any explicit rules, we deny all */
b50e327b 436 if (!Config.accessList.icp)
638402dd 437 return false;
b50e327b 438
c0941a6a 439 ACLFilledChecklist checklist(Config.accessList.icp, icp_request, NULL);
cc192b50 440 checklist.src_addr = from;
4dd643d5 441 checklist.my_addr.setNoAddr();
2efeb0b7 442 return (checklist.fastCheck() == ACCESS_ALLOWED);
e6ccf245 443}
444
445char const *
446icpGetUrlToSend(char *url)
447{
448 if (strpbrk(url, w_space))
62e76326 449 return rfc1738_escape(url);
e6ccf245 450 else
62e76326 451 return url;
e6ccf245 452}
453
190154cf 454HttpRequest *
b7ac5457 455icpGetRequest(char *url, int reqnum, int fd, Ip::Address &from)
e6ccf245 456{
26ac0430 457 if (strpbrk(url, w_space)) {
62e76326 458 url = rfc1738_escape(url);
459 icpCreateAndSend(ICP_ERR, 0, rfc1738_escape(url), reqnum, 0, fd, from);
460 return NULL;
e6ccf245 461 }
62e76326 462
190154cf 463 HttpRequest *result;
62e76326 464
c21ad0f5 465 if ((result = HttpRequest::CreateFromUrl(url)) == NULL)
62e76326 466 icpCreateAndSend(ICP_ERR, 0, url, reqnum, 0, fd, from);
467
e6ccf245 468 return result;
469
470}
471
472static void
b7ac5457 473doV2Query(int fd, Ip::Address &from, char *buf, icp_common_t header)
e6ccf245 474{
475 int rtt = 0;
7a2f978b 476 int src_rtt = 0;
09aabd84 477 uint32_t flags = 0;
e6ccf245 478 /* We have a valid packet */
09aabd84 479 char *url = buf + sizeof(icp_common_t) + sizeof(uint32_t);
cc192b50 480 HttpRequest *icp_request = icpGetRequest(url, header.reqnum, fd, from);
62e76326 481
e6ccf245 482 if (!icp_request)
62e76326 483 return;
484
6dd9f4bd 485 HTTPMSGLOCK(icp_request);
319bf5a7 486
26ac0430 487 if (!icpAccessAllowed(from, icp_request)) {
cc192b50 488 icpDenyAccess(from, url, header.reqnum, fd);
6dd9f4bd 489 HTTPMSGUNLOCK(icp_request);
62e76326 490 return;
e6ccf245 491 }
9b5c4a9a 492#if USE_ICMP
26ac0430 493 if (header.flags & ICP_FLAG_SRC_RTT) {
cc192b50 494 rtt = netdbHostRtt(icp_request->GetHost());
495 int hops = netdbHostHops(icp_request->GetHost());
62e76326 496 src_rtt = ((hops & 0xFFFF) << 16) | (rtt & 0xFFFF);
497
498 if (rtt)
499 flags |= ICP_FLAG_SRC_RTT;
e6ccf245 500 }
9b5c4a9a 501#endif /* USE_ICMP */
62e76326 502
e6ccf245 503 /* The peer is allowed to use this cache */
86c63190 504 ICP2State *state = new ICP2State(header, icp_request);
e6ccf245 505 state->fd = fd;
506 state->from = from;
86c63190 507 state->url = xstrdup(url);
e6ccf245 508 state->flags = flags;
509 state->rtt = rtt;
510 state->src_rtt = src_rtt;
62e76326 511
86c63190 512 StoreEntry::getPublic(state, url, Http::METHOD_GET);
319bf5a7 513
6dd9f4bd 514 HTTPMSGUNLOCK(icp_request);
e6ccf245 515}
516
517void
b7ac5457 518_icp_common_t::handleReply(char *buf, Ip::Address &from)
e6ccf245 519{
26ac0430 520 if (neighbors_do_private_keys && reqnum == 0) {
fa84c01d
FC
521 debugs(12, DBG_CRITICAL, "icpHandleIcpV2: Neighbor " << from << " returned reqnum = 0");
522 debugs(12, DBG_CRITICAL, "icpHandleIcpV2: Disabling use of private keys");
62e76326 523 neighbors_do_private_keys = 0;
e6ccf245 524 }
62e76326 525
e6ccf245 526 char *url = buf + sizeof(icp_common_t);
cc192b50 527 debugs(12, 3, "icpHandleIcpV2: " << icp_opcode_str[opcode] << " from " << from << " for '" << url << "'");
bf8fe701 528
e6ccf245 529 const cache_key *key = icpGetCacheKey(url, (int) reqnum);
530 /* call neighborsUdpAck even if ping_status != PING_WAITING */
531 neighborsUdpAck(key, this, from);
532}
533
534static void
b7ac5457 535icpHandleIcpV2(int fd, Ip::Address &from, char *buf, int len)
e6ccf245 536{
26ac0430 537 if (len <= 0) {
bf8fe701 538 debugs(12, 3, "icpHandleIcpV2: ICP message is too small");
62e76326 539 return;
e6ccf245 540 }
62e76326 541
e6ccf245 542 icp_common_t header(buf, len);
7b83b3d9 543 /*
544 * Length field should match the number of bytes read
545 */
62e76326 546
26ac0430 547 if (len != header.length) {
bf8fe701 548 debugs(12, 3, "icpHandleIcpV2: ICP message is too small");
62e76326 549 return;
7b83b3d9 550 }
62e76326 551
26ac0430 552 switch (header.opcode) {
62e76326 553
27cd7235 554 case ICP_QUERY:
62e76326 555 /* We have a valid packet */
556 doV2Query(fd, from, buf, header);
557 break;
7a2f978b 558
27cd7235 559 case ICP_HIT:
62e76326 560
27cd7235 561 case ICP_DECHO:
62e76326 562
27cd7235 563 case ICP_MISS:
62e76326 564
27cd7235 565 case ICP_DENIED:
62e76326 566
27cd7235 567 case ICP_MISS_NOFETCH:
cc192b50 568 header.handleReply(buf, from);
62e76326 569 break;
7a2f978b 570
27cd7235 571 case ICP_INVALID:
62e76326 572
27cd7235 573 case ICP_ERR:
62e76326 574 break;
7a2f978b 575
576 default:
fa84c01d 577 debugs(12, DBG_CRITICAL, "icpHandleIcpV2: UNKNOWN OPCODE: " << header.opcode << " from " << from);
bf8fe701 578
62e76326 579 break;
7a2f978b 580 }
7a2f978b 581}
582
583#ifdef ICP_PKT_DUMP
584static void
585icpPktDump(icp_common_t * pkt)
586{
b7ac5457 587 Ip::Address a;
7a2f978b 588
4a7a3d56 589 debugs(12, 9, "opcode: " << std::setw(3) << pkt->opcode << " " << icp_opcode_str[pkt->opcode]);
590 debugs(12, 9, "version: "<< std::left << std::setw(8) << pkt->version);
591 debugs(12, 9, "length: "<< std::left << std::setw(8) << ntohs(pkt->length));
bf8fe701 592 debugs(12, 9, "reqnum: "<< std::left << std::setw(8) << ntohl(pkt->reqnum));
593 debugs(12, 9, "flags: "<< std::left << std::hex << std::setw(8) << ntohl(pkt->flags));
cc192b50 594 a = (struct in_addr)pkt->shostid;
595 debugs(12, 9, "shostid: " << a );
bf8fe701 596 debugs(12, 9, "payload: " << (char *) pkt + sizeof(icp_common_t));
7a2f978b 597}
62e76326 598
7a2f978b 599#endif
600
601void
ba4f8e5a 602icpHandleUdp(int sock, void *data)
7a2f978b 603{
d193a436 604 int *N = &incoming_sockets_accepted;
62e76326 605
b7ac5457 606 Ip::Address from;
7a2f978b 607 LOCAL_ARRAY(char, buf, SQUID_UDP_SO_RCVBUF);
0b6d1955 608 int len;
7a2f978b 609 int icp_version;
65d448bc 610 int max = INCOMING_UDP_MAX;
d841c88d 611 Comm::SetSelect(sock, COMM_SELECT_READ, icpHandleUdp, NULL, 0);
62e76326 612
5e263176
FC
613 while (max) {
614 --max;
62e76326 615 len = comm_udp_recvfrom(sock,
616 buf,
617 SQUID_UDP_SO_RCVBUF - 1,
618 0,
cc192b50 619 from);
62e76326 620
621 if (len == 0)
622 break;
623
624 if (len < 0) {
625 if (ignoreErrno(errno))
626 break;
627
1191b93b 628#if _SQUID_LINUX_
62e76326 629 /* Some Linux systems seem to set the FD for reading and then
630 * return ECONNREFUSED when sendto() fails and generates an ICMP
631 * port unreachable message. */
632 /* or maybe an EHOSTUNREACH "No route to host" message */
633 if (errno != ECONNREFUSED && errno != EHOSTUNREACH)
7a2f978b 634#endif
62e76326 635
e0236918 636 debugs(50, DBG_IMPORTANT, "icpHandleUdp: FD " << sock << " recvfrom: " << xstrerror());
62e76326 637
638 break;
639 }
640
95dc7ff4 641 ++(*N);
62e76326 642 icpCount(buf, RECV, (size_t) len, 0);
643 buf[len] = '\0';
bf8fe701 644 debugs(12, 4, "icpHandleUdp: FD " << sock << ": received " <<
cc192b50 645 (unsigned long int)len << " bytes from " << from);
bf8fe701 646
7a2f978b 647#ifdef ICP_PACKET_DUMP
62e76326 648
649 icpPktDump(buf);
7a2f978b 650#endif
62e76326 651
0b6d1955 652 if ((size_t) len < sizeof(icp_common_t)) {
bf8fe701 653 debugs(12, 4, "icpHandleUdp: Ignoring too-small UDP packet");
62e76326 654 break;
655 }
656
657 icp_version = (int) buf[1]; /* cheat! */
658
1b76e6c1 659 if (icpOutgoingConn->local == from)
7c8931a1
AJ
660 // ignore ICP packets which loop back (multicast usually)
661 debugs(12, 4, "icpHandleUdp: Ignoring UDP packet sent by myself");
662 else if (icp_version == ICP_VERSION_2)
62e76326 663 icpHandleIcpV2(sock, from, buf, len);
664 else if (icp_version == ICP_VERSION_3)
665 icpHandleIcpV3(sock, from, buf, len);
666 else
e0236918 667 debugs(12, DBG_IMPORTANT, "WARNING: Unused ICP version " << icp_version <<
26ac0430 668 " received from " << from);
7a2f978b 669 }
7a2f978b 670}
15df8349 671
672void
65d448bc 673icpOpenPorts(void)
15df8349 674{
09aabd84 675 uint16_t port;
62e76326 676
15df8349 677 if ((port = Config.Port.icp) <= 0)
62e76326 678 return;
679
e0d28505
AJ
680 icpIncomingConn = new Comm::Connection;
681 icpIncomingConn->local = Config.Addrs.udp_incoming;
4dd643d5 682 icpIncomingConn->local.port(port);
62e76326 683
4dd643d5 684 if (!Ip::EnableIpv6 && !icpIncomingConn->local.setIPv4()) {
e0d28505 685 debugs(12, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << icpIncomingConn->local << " is not an IPv4 address.");
e0f8b709
AJ
686 fatal("ICP port cannot be opened.");
687 }
e0209dae 688 /* split-stack for now requires default IPv4-only ICP */
4dd643d5
AJ
689 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && icpIncomingConn->local.isAnyAddr()) {
690 icpIncomingConn->local.setIPv4();
e0209dae 691 }
e0f8b709 692
013e320c 693 AsyncCall::Pointer call = asyncCall(12, 2,
5667a628 694 "icpIncomingConnectionOpened",
09cee743 695 Comm::UdpOpenDialer(&icpIncomingConnectionOpened));
62e76326 696
013e320c 697 Ipc::StartListening(SOCK_DGRAM,
5667a628 698 IPPROTO_UDP,
e0d28505 699 icpIncomingConn,
5667a628 700 Ipc::fdnInIcpSocket, call);
62e76326 701
4dd643d5 702 if ( !Config.Addrs.udp_outgoing.isNoAddr() ) {
e0d28505
AJ
703 icpOutgoingConn = new Comm::Connection;
704 icpOutgoingConn->local = Config.Addrs.udp_outgoing;
4dd643d5 705 icpOutgoingConn->local.port(port);
e0f8b709 706
4dd643d5 707 if (!Ip::EnableIpv6 && !icpOutgoingConn->local.setIPv4()) {
e0d28505 708 debugs(49, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << icpOutgoingConn->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 && icpOutgoingConn->local.isAnyAddr()) {
713 icpOutgoingConn->local.setIPv4();
e0209dae 714 }
e0f8b709 715
e0d28505
AJ
716 enter_suid();
717 comm_open_listener(SOCK_DGRAM, IPPROTO_UDP, icpOutgoingConn, "Outgoing ICP Port");
62e76326 718 leave_suid();
719
e0d28505 720 if (!Comm::IsConnOpen(icpOutgoingConn))
62e76326 721 fatal("Cannot open Outgoing ICP Port");
722
e0d28505 723 debugs(12, DBG_CRITICAL, "Sending ICP messages from " << icpOutgoingConn->local);
62e76326 724
8bbb16e3 725 Comm::SetSelect(icpOutgoingConn->fd, COMM_SELECT_READ, icpHandleUdp, NULL, 0);
25b481e6 726 fd_note(icpOutgoingConn->fd, "Outgoing ICP socket");
15df8349 727 }
236dedc0 728}
62e76326 729
013e320c 730static void
09cee743 731icpIncomingConnectionOpened(const Comm::ConnectionPointer &conn, int errNo)
013e320c 732{
09cee743 733 if (!Comm::IsConnOpen(conn))
013e320c
AR
734 fatal("Cannot open ICP Port");
735
09cee743 736 Comm::SetSelect(conn->fd, COMM_SELECT_READ, icpHandleUdp, NULL, 0);
013e320c
AR
737
738 for (const wordlist *s = Config.mcast_group_list; s; s = s->next)
09cee743 739 ipcache_nbgethostbyname(s->key, mcastJoinGroups, NULL); // XXX: pass the conn for mcastJoinGroups usage.
013e320c 740
09cee743 741 debugs(12, DBG_IMPORTANT, "Accepting ICP messages on " << conn->local);
013e320c 742
09cee743 743 fd_note(conn->fd, "Incoming ICP port");
013e320c 744
4dd643d5 745 if (Config.Addrs.udp_outgoing.isNoAddr()) {
09cee743 746 icpOutgoingConn = conn;
e0d28505
AJ
747 debugs(12, DBG_IMPORTANT, "Sending ICP messages from " << icpOutgoingConn->local);
748 }
013e320c
AR
749}
750
63be0a78 751/**
26ac0430 752 * icpConnectionShutdown only closes the 'in' socket if it is
17e6c0a1 753 * different than the 'out' socket.
754 */
c0fbae16 755void
17e6c0a1 756icpConnectionShutdown(void)
c0fbae16 757{
e0d28505 758 if (!Comm::IsConnOpen(icpIncomingConn))
62e76326 759 return;
760
e0d28505 761 debugs(12, DBG_IMPORTANT, "Stop receiving ICP on " << icpIncomingConn->local);
62e76326 762
e0d28505
AJ
763 /** Release the 'in' socket for lazy closure.
764 * in and out sockets may be sharing one same FD.
765 * This prevents this function from executing repeatedly.
c0fbae16 766 */
e0d28505 767 icpIncomingConn = NULL;
62e76326 768
63be0a78 769 /**
c0fbae16 770 * Normally we only write to the outgoing ICP socket, but
771 * we also have a read handler there to catch messages sent
772 * to that specific interface. During shutdown, we must
773 * disable reading on the outgoing socket.
774 */
e0d28505 775 assert(Comm::IsConnOpen(icpOutgoingConn));
62e76326 776
8bbb16e3 777 Comm::SetSelect(icpOutgoingConn->fd, COMM_SELECT_READ, NULL, NULL, 0);
c0fbae16 778}
17e6c0a1 779
780void
65d448bc 781icpClosePorts(void)
17e6c0a1 782{
783 icpConnectionShutdown();
62e76326 784
e0d28505
AJ
785 if (icpOutgoingConn != NULL) {
786 debugs(12, DBG_IMPORTANT, "Stop sending ICP from " << icpOutgoingConn->local);
787 icpOutgoingConn = NULL;
17e6c0a1 788 }
789}
071a3ae7 790
791static void
792icpCount(void *buf, int which, size_t len, int delay)
793{
e6ccf245 794 icp_common_t *icp = (icp_common_t *) buf;
62e76326 795
071a3ae7 796 if (len < sizeof(*icp))
62e76326 797 return;
798
071a3ae7 799 if (SENT == which) {
95dc7ff4 800 ++statCounter.icp.pkts_sent;
62e76326 801 kb_incr(&statCounter.icp.kbytes_sent, len);
802
803 if (ICP_QUERY == icp->opcode) {
95dc7ff4 804 ++statCounter.icp.queries_sent;
62e76326 805 kb_incr(&statCounter.icp.q_kbytes_sent, len);
806 } else {
95dc7ff4 807 ++statCounter.icp.replies_sent;
62e76326 808 kb_incr(&statCounter.icp.r_kbytes_sent, len);
809 /* this is the sent-reply service time */
e8baef82 810 statCounter.icp.replySvcTime.count(delay);
62e76326 811 }
812
813 if (ICP_HIT == icp->opcode)
95dc7ff4 814 ++statCounter.icp.hits_sent;
071a3ae7 815 } else if (RECV == which) {
95dc7ff4 816 ++statCounter.icp.pkts_recv;
62e76326 817 kb_incr(&statCounter.icp.kbytes_recv, len);
818
819 if (ICP_QUERY == icp->opcode) {
95dc7ff4 820 ++statCounter.icp.queries_recv;
62e76326 821 kb_incr(&statCounter.icp.q_kbytes_recv, len);
822 } else {
95dc7ff4 823 ++statCounter.icp.replies_recv;
62e76326 824 kb_incr(&statCounter.icp.r_kbytes_recv, len);
e8baef82 825 /* statCounter.icp.querySvcTime set in clientUpdateCounters */
62e76326 826 }
827
828 if (ICP_HIT == icp->opcode)
95dc7ff4 829 ++statCounter.icp.hits_recv;
071a3ae7 830 }
831}
007b8be4 832
833#define N_QUERIED_KEYS 8192
834#define N_QUERIED_KEYS_MASK 8191
c3031d67 835static cache_key queried_keys[N_QUERIED_KEYS][SQUID_MD5_DIGEST_LENGTH];
007b8be4 836
837int
5942e8d4 838icpSetCacheKey(const cache_key * key)
007b8be4 839{
840 static int reqnum = 0;
62e76326 841
007b8be4 842 if (++reqnum < 0)
62e76326 843 reqnum = 1;
844
007b8be4 845 storeKeyCopy(queried_keys[reqnum & N_QUERIED_KEYS_MASK], key);
62e76326 846
007b8be4 847 return reqnum;
848}
849
850const cache_key *
851icpGetCacheKey(const char *url, int reqnum)
852{
853 if (neighbors_do_private_keys && reqnum)
62e76326 854 return queried_keys[reqnum & N_QUERIED_KEYS_MASK];
855
c2a7cefd 856 return storeKeyPublic(url, Http::METHOD_GET);
007b8be4 857}