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