3 * $Id: icp_v2.cc,v 1.82 2003/08/10 11:00:43 robertc Exp $
5 * DEBUG: section 12 Internet Cache Protocol
6 * AUTHOR: Duane Wessels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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.
40 #include "HttpRequest.h"
41 #include "ACLChecklist.h"
44 static void icpLogIcp(struct in_addr
, log_type
, int, const char *, int);
46 static void icpHandleIcpV2(int, struct sockaddr_in
, char *, int);
47 static void icpCount(void *, int, size_t, int);
50 * IcpQueueHead is global so comm_incoming() knows whether or not
51 * to call icpUdpSendQueue.
53 static icpUdpData
*IcpQueueTail
= NULL
;
54 static icpUdpData
*IcpQueueHead
= NULL
;
57 _icp_common_t::_icp_common_t() : opcode(ICP_INVALID
), version(0), length(0), reqnum(0), flags(0), pad(0), shostid(0)
60 _icp_common_t::_icp_common_t(char *buf
, unsigned int len
)
62 if (len
< sizeof(_icp_common_t
)) {
68 xmemcpy(this, buf
, sizeof(icp_common_t
));
70 * Convert network order sensitive fields
72 length
= ntohs(length
);
73 reqnum
= ntohl(reqnum
);
79 _icp_common_t::getOpCode() const
81 if (opcode
> (char)ICP_END
)
84 return (icp_opcode
)opcode
;
89 ICPState:: ICPState(icp_common_t
& aHeader
):header(aHeader
)
100 requestDestroy(request
);
108 class ICP2State
:public ICPState
, public StoreClient
112 ICP2State(icp_common_t
& aHeader
):ICPState(aHeader
),rtt(0),src_rtt(0),flags(0)
116 void created(StoreEntry
* newEntry
);
123 ICP2State::~ICP2State ()
127 ICP2State::created (StoreEntry
*newEntry
)
129 StoreEntry
*entry
= newEntry
->isNull () ? NULL
: newEntry
;
130 debug(12, 5) ("icpHandleIcpV2: OPCODE %s\n", icp_opcode_str
[header
.opcode
]);
131 icp_opcode codeToSend
;
133 if (icpCheckUdpHit(entry
, request
)) {
134 codeToSend
= ICP_HIT
;
136 if (Config
.onoff
.test_reachability
&& rtt
== 0) {
137 if ((rtt
= netdbHostRtt(request
->host
)) == 0)
138 netdbPingSite(request
->host
);
141 if (icpGetCommonOpcode() != ICP_ERR
)
142 codeToSend
= icpGetCommonOpcode();
143 else if (Config
.onoff
.test_reachability
&& rtt
== 0)
144 codeToSend
= ICP_MISS_NOFETCH
;
146 codeToSend
= ICP_MISS
;
149 icpCreateAndSend(codeToSend
, flags
, url
, header
.reqnum
, src_rtt
, fd
, &from
);
157 icpLogIcp(struct in_addr caddr
, log_type logcode
, int len
, const char *url
, int delay
)
161 if (LOG_TAG_NONE
== logcode
)
164 if (LOG_ICP_QUERY
== logcode
)
167 clientdbUpdate(caddr
, logcode
, PROTO_ICP
, len
);
169 if (!Config
.onoff
.log_udp
)
172 memset(&al
, '\0', sizeof(al
));
174 al
.icp
.opcode
= ICP_QUERY
;
178 al
.cache
.caddr
= caddr
;
182 al
.cache
.code
= logcode
;
184 al
.cache
.msec
= delay
;
186 accessLogLog(&al
, NULL
);
190 icpUdpSendQueue(int fd
, void *unused
)
196 while ((q
= IcpQueueHead
) != NULL
) {
197 delay
= tvSubUsec(q
->queue_time
, current_time
);
198 /* increment delay to prevent looping */
199 x
= icpUdpSend(fd
, &q
->address
, (icp_common_t
*) q
->msg
, q
->logcode
, ++delay
);
200 IcpQueueHead
= q
->next
;
209 _icp_common_t::createMessage(
217 icp_common_t
*headerp
= NULL
;
218 char *urloffset
= NULL
;
220 buf_len
= sizeof(icp_common_t
) + strlen(url
) + 1;
222 if (opcode
== ICP_QUERY
)
223 buf_len
+= sizeof(u_int32_t
);
225 buf
= (char *) xcalloc(buf_len
, 1);
227 headerp
= (icp_common_t
*) (void *) buf
;
229 headerp
->opcode
= (char) opcode
;
231 headerp
->version
= ICP_VERSION_CURRENT
;
233 headerp
->length
= (u_int16_t
) htons(buf_len
);
235 headerp
->reqnum
= htonl(reqnum
);
237 headerp
->flags
= htonl(flags
);
239 headerp
->pad
= htonl(pad
);
241 headerp
->shostid
= theOutICPAddr
.s_addr
;
243 urloffset
= buf
+ sizeof(icp_common_t
);
245 if (opcode
== ICP_QUERY
)
246 urloffset
+= sizeof(u_int32_t
);
248 xmemcpy(urloffset
, url
, strlen(url
));
250 return (icp_common_t
*)buf
;
256 const struct sockaddr_in
*to
,
264 len
= (int) ntohs(msg
->length
);
265 debug(12, 5) ("icpUdpSend: FD %d sending %s, %d bytes to %s:%d\n",
267 icp_opcode_str
[msg
->opcode
],
269 inet_ntoa(to
->sin_addr
),
270 ntohs(to
->sin_port
));
271 x
= comm_udp_sendto(fd
, to
, sizeof(*to
), msg
, len
);
275 /* successfully written */
276 icpLogIcp(to
->sin_addr
, logcode
, len
, (char *) (msg
+ 1), delay
);
277 icpCount(msg
, SENT
, (size_t) len
, delay
);
279 } else if (0 == delay
)
281 /* send failed, but queue it */
282 queue
= (icpUdpData
*) xcalloc(1, sizeof(icpUdpData
));
283 queue
->address
= *to
;
285 queue
->len
= (int) ntohs(msg
->length
);
286 queue
->queue_time
= current_time
;
287 queue
->logcode
= logcode
;
289 if (IcpQueueHead
== NULL
) {
290 IcpQueueHead
= queue
;
291 IcpQueueTail
= queue
;
292 } else if (IcpQueueTail
== IcpQueueHead
) {
293 IcpQueueTail
= queue
;
294 IcpQueueHead
->next
= queue
;
296 IcpQueueTail
->next
= queue
;
297 IcpQueueTail
= queue
;
300 commSetSelect(fd
, COMM_SELECT_WRITE
, icpUdpSendQueue
, NULL
, 0);
301 statCounter
.icp
.replies_queued
++;
305 statCounter
.icp
.replies_dropped
++;
312 icpCheckUdpHit(StoreEntry
* e
, HttpRequest
* request
)
317 if (!storeEntryValidToSend(e
))
320 if (Config
.onoff
.icp_hit_stale
)
323 if (refreshCheckICP(e
, request
))
329 /* ICP_ERR means no opcode selected here */
333 /* if store is rebuilding, return a UDP_HIT, but not a MISS */
335 if (store_dirs_rebuilding
&& opt_reload_hit_only
||
336 hit_only_mode_until
> squid_curtime
) {
337 return ICP_MISS_NOFETCH
;
344 icpLogFromICPCode(icp_opcode opcode
)
346 if (opcode
== ICP_ERR
)
347 return LOG_UDP_INVALID
;
349 if (opcode
== ICP_DENIED
)
350 return LOG_UDP_DENIED
;
352 if (opcode
== ICP_HIT
)
355 if (opcode
== ICP_MISS
)
358 if (opcode
== ICP_MISS_NOFETCH
)
359 return LOG_UDP_MISS_NOFETCH
;
361 fatal("expected ICP opcode\n");
363 return LOG_UDP_INVALID
;
368 icpCreateAndSend(icp_opcode opcode
, int flags
, char const *url
, int reqnum
, int pad
, int fd
, const struct sockaddr_in
*from
)
370 icp_common_t
*reply
= _icp_common_t::createMessage(opcode
, flags
, url
, reqnum
, pad
);
371 icpUdpSend(fd
, from
, reply
, icpLogFromICPCode(opcode
), 0);
376 icpDenyAccess(struct sockaddr_in
*from
, char *url
, int reqnum
, int fd
)
378 debug(12, 2) ("icpDenyAccess: Access Denied for %s by %s.\n",
379 inet_ntoa(from
->sin_addr
), AclMatchedName
);
381 if (clientdbCutoffDenied(from
->sin_addr
))
384 * count this DENIED query in the clientdb, even though
385 * we're not sending an ICP reply...
387 clientdbUpdate(from
->sin_addr
, LOG_UDP_DENIED
, PROTO_ICP
, 0);
390 icpCreateAndSend(ICP_DENIED
, 0, url
, reqnum
, 0, fd
, from
);
396 icpAccessAllowed(struct sockaddr_in
*from
, HttpRequest
* icp_request
)
398 ACLChecklist checklist
;
399 checklist
.src_addr
= from
->sin_addr
;
400 checklist
.my_addr
= no_addr
;
401 checklist
.request
= requestLink(icp_request
);
402 return aclCheckFast(Config
.accessList
.icp
, &checklist
);
406 icpGetUrlToSend(char *url
)
408 if (strpbrk(url
, w_space
))
409 return rfc1738_escape(url
);
416 icpGetRequest(char *url
, int reqnum
, int fd
, struct sockaddr_in
* from
)
418 if (strpbrk(url
, w_space
))
420 url
= rfc1738_escape(url
);
421 icpCreateAndSend(ICP_ERR
, 0, rfc1738_escape(url
), reqnum
, 0, fd
, from
);
427 if ((result
= urlParse(METHOD_GET
, url
)) == NULL
)
428 icpCreateAndSend(ICP_ERR
, 0, url
, reqnum
, 0, fd
, from
);
436 doV2Query(int fd
, struct sockaddr_in from
, char *buf
, icp_common_t header
)
441 /* We have a valid packet */
442 char *url
= buf
+ sizeof(icp_common_t
) + sizeof(u_int32_t
);
443 HttpRequest
*icp_request
= icpGetRequest(url
, header
.reqnum
, fd
, &from
);
448 if (!icpAccessAllowed(&from
, icp_request
))
450 icpDenyAccess(&from
, url
, header
.reqnum
, fd
);
451 requestDestroy(icp_request
);
455 if (header
.flags
& ICP_FLAG_SRC_RTT
)
457 rtt
= netdbHostRtt(icp_request
->host
);
458 int hops
= netdbHostHops(icp_request
->host
);
459 src_rtt
= ((hops
& 0xFFFF) << 16) | (rtt
& 0xFFFF);
462 flags
|= ICP_FLAG_SRC_RTT
;
465 /* The peer is allowed to use this cache */
466 ICP2State
*state
= new ICP2State (header
);
472 state
->url
= xstrdup (url
);
474 state
->flags
= flags
;
478 state
->src_rtt
= src_rtt
;
480 StoreEntry::getPublic (state
, url
, METHOD_GET
);
485 _icp_common_t::handleReply(char *buf
, struct sockaddr_in
*from
)
487 if (neighbors_do_private_keys
&& reqnum
== 0)
489 debug(12, 0) ("icpHandleIcpV2: Neighbor %s returned reqnum = 0\n",
490 inet_ntoa(from
->sin_addr
));
491 debug(12, 0) ("icpHandleIcpV2: Disabling use of private keys\n");
492 neighbors_do_private_keys
= 0;
495 char *url
= buf
+ sizeof(icp_common_t
);
496 debug(12, 3) ("icpHandleIcpV2: %s from %s for '%s'\n",
497 icp_opcode_str
[opcode
],
498 inet_ntoa(from
->sin_addr
),
500 const cache_key
*key
= icpGetCacheKey(url
, (int) reqnum
);
501 /* call neighborsUdpAck even if ping_status != PING_WAITING */
502 neighborsUdpAck(key
, this, from
);
507 icpHandleIcpV2(int fd
, struct sockaddr_in from
, char *buf
, int len
)
511 debug(12, 3) ("icpHandleIcpV2: ICP message is too small\n");
515 icp_common_t
header(buf
, len
);
517 * Length field should match the number of bytes read
520 if (len
!= header
.length
)
522 debug(12, 3) ("icpHandleIcpV2: ICP message is too small\n");
526 switch (header
.opcode
)
530 /* We have a valid packet */
531 doV2Query(fd
, from
, buf
, header
);
535 #if ALLOW_SOURCE_PING
546 case ICP_MISS_NOFETCH
:
547 header
.handleReply(buf
, &from
);
556 debug(12, 0) ("icpHandleIcpV2: UNKNOWN OPCODE: %d from %s\n",
557 header
.opcode
, inet_ntoa(from
.sin_addr
));
564 icpPktDump(icp_common_t
* pkt
)
569 debug(12, 9) ("opcode: %3d %s\n",
571 icp_opcode_str
[pkt
->opcode
]);
572 debug(12, 9) ("version: %-8d\n", (int) pkt
->version
);
573 debug(12, 9) ("length: %-8d\n", (int) ntohs(pkt
->length
));
574 debug(12, 9) ("reqnum: %-8d\n", ntohl(pkt
->reqnum
));
575 debug(12, 9) ("flags: %-8x\n", ntohl(pkt
->flags
));
576 a
.s_addr
= pkt
->shostid
;
577 debug(12, 9) ("shostid: %s\n", inet_ntoa(a
));
578 debug(12, 9) ("payload: %s\n", (char *) pkt
+ sizeof(icp_common_t
));
584 icpHandleUdp(int sock
, void *data
)
586 int *N
= &incoming_sockets_accepted
;
588 struct sockaddr_in from
;
590 LOCAL_ARRAY(char, buf
, SQUID_UDP_SO_RCVBUF
);
593 int max
= INCOMING_ICP_MAX
;
594 commSetSelect(sock
, COMM_SELECT_READ
, icpHandleUdp
, NULL
, 0);
597 from_len
= sizeof(from
);
598 memset(&from
, '\0', from_len
);
599 len
= comm_udp_recvfrom(sock
,
601 SQUID_UDP_SO_RCVBUF
- 1,
604 (struct sockaddr
*) &from
,
611 if (ignoreErrno(errno
))
615 /* Some Linux systems seem to set the FD for reading and then
616 * return ECONNREFUSED when sendto() fails and generates an ICMP
617 * port unreachable message. */
618 /* or maybe an EHOSTUNREACH "No route to host" message */
619 if (errno
!= ECONNREFUSED
&& errno
!= EHOSTUNREACH
)
622 debug(50, 1) ("icpHandleUdp: FD %d recvfrom: %s\n",
629 icpCount(buf
, RECV
, (size_t) len
, 0);
631 debug(12, 4) ("icpHandleUdp: FD %d: received %lu bytes from %s.\n",
633 (unsigned long int)len
,
634 inet_ntoa(from
.sin_addr
));
635 #ifdef ICP_PACKET_DUMP
640 if (len
< sizeof(icp_common_t
)) {
641 debug(12, 4) ("icpHandleUdp: Ignoring too-small UDP packet\n");
645 icp_version
= (int) buf
[1]; /* cheat! */
647 if (icp_version
== ICP_VERSION_2
)
648 icpHandleIcpV2(sock
, from
, buf
, len
);
649 else if (icp_version
== ICP_VERSION_3
)
650 icpHandleIcpV3(sock
, from
, buf
, len
);
652 debug(12, 1) ("WARNING: Unused ICP version %d received from %s:%d\n",
654 inet_ntoa(from
.sin_addr
),
655 ntohs(from
.sin_port
));
660 icpConnectionsOpen(void)
666 struct sockaddr_in xaddr
;
671 if ((port
= Config
.Port
.icp
) <= 0)
676 theInIcpConnection
= comm_open(SOCK_DGRAM
,
678 Config
.Addrs
.udp_incoming
,
685 if (theInIcpConnection
< 0)
686 fatal("Cannot open ICP Port");
688 commSetSelect(theInIcpConnection
,
694 for (s
= Config
.mcast_group_list
; s
; s
= s
->next
)
695 ipcache_nbgethostbyname(s
->key
, mcastJoinGroups
, NULL
);
697 debug(12, 1) ("Accepting ICP messages at %s, port %d, FD %d.\n",
698 inet_ntoa(Config
.Addrs
.udp_incoming
),
699 (int) port
, theInIcpConnection
);
701 if ((addr
= Config
.Addrs
.udp_outgoing
).s_addr
!= no_addr
.s_addr
) {
703 theOutIcpConnection
= comm_open(SOCK_DGRAM
,
711 if (theOutIcpConnection
< 0)
712 fatal("Cannot open Outgoing ICP Port");
714 commSetSelect(theOutIcpConnection
,
720 debug(12, 1) ("Outgoing ICP messages on port %d, FD %d.\n",
721 (int) port
, theOutIcpConnection
);
723 fd_note(theOutIcpConnection
, "Outgoing ICP socket");
725 fd_note(theInIcpConnection
, "Incoming ICP socket");
727 theOutIcpConnection
= theInIcpConnection
;
730 memset(&theOutICPAddr
, '\0', sizeof(struct in_addr
));
732 len
= sizeof(struct sockaddr_in
);
733 memset(&xaddr
, '\0', len
);
734 x
= getsockname(theOutIcpConnection
,
736 (struct sockaddr
*) &xaddr
, &len
);
739 debug(50, 1) ("theOutIcpConnection FD %d: getsockname: %s\n",
740 theOutIcpConnection
, xstrerror());
742 theOutICPAddr
= xaddr
.sin_addr
;
746 * icpConnectionShutdown only closes the 'in' socket if it is
747 * different than the 'out' socket.
750 icpConnectionShutdown(void)
752 if (theInIcpConnection
< 0)
755 if (theInIcpConnection
!= theOutIcpConnection
) {
756 debug(12, 1) ("FD %d Closing ICP connection\n", theInIcpConnection
);
757 comm_close(theInIcpConnection
);
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.
766 theInIcpConnection
= -1;
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.
774 assert(theOutIcpConnection
> -1);
776 commSetSelect(theOutIcpConnection
, COMM_SELECT_READ
, NULL
, NULL
, 0);
780 icpConnectionClose(void)
782 icpConnectionShutdown();
784 if (theOutIcpConnection
> -1) {
785 debug(12, 1) ("FD %d Closing ICP connection\n", theOutIcpConnection
);
786 comm_close(theOutIcpConnection
);
787 theOutIcpConnection
= -1;
792 icpCount(void *buf
, int which
, size_t len
, int delay
)
794 icp_common_t
*icp
= (icp_common_t
*) buf
;
796 if (len
< sizeof(*icp
))
800 statCounter
.icp
.pkts_sent
++;
801 kb_incr(&statCounter
.icp
.kbytes_sent
, len
);
803 if (ICP_QUERY
== icp
->opcode
) {
804 statCounter
.icp
.queries_sent
++;
805 kb_incr(&statCounter
.icp
.q_kbytes_sent
, len
);
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
);
813 if (ICP_HIT
== icp
->opcode
)
814 statCounter
.icp
.hits_sent
++;
815 } else if (RECV
== which
) {
816 statCounter
.icp
.pkts_recv
++;
817 kb_incr(&statCounter
.icp
.kbytes_recv
, len
);
819 if (ICP_QUERY
== icp
->opcode
) {
820 statCounter
.icp
.queries_recv
++;
821 kb_incr(&statCounter
.icp
.q_kbytes_recv
, len
);
823 statCounter
.icp
.replies_recv
++;
824 kb_incr(&statCounter
.icp
.r_kbytes_recv
, len
);
825 /* statCounter.icp.query_svc_time set in clientUpdateCounters */
828 if (ICP_HIT
== icp
->opcode
)
829 statCounter
.icp
.hits_recv
++;
833 #define N_QUERIED_KEYS 8192
834 #define N_QUERIED_KEYS_MASK 8191
835 static cache_key queried_keys
[N_QUERIED_KEYS
][MD5_DIGEST_CHARS
];
838 icpSetCacheKey(const cache_key
* key
)
840 static int reqnum
= 0;
845 storeKeyCopy(queried_keys
[reqnum
& N_QUERIED_KEYS_MASK
], key
);
851 icpGetCacheKey(const char *url
, int reqnum
)
853 if (neighbors_do_private_keys
&& reqnum
)
854 return queried_keys
[reqnum
& N_QUERIED_KEYS_MASK
];
856 return storeKeyPublic(url
, METHOD_GET
);