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