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