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