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