5 * DEBUG: section 31 Hypertext Caching Protocol
6 * AUTHOR: Duane Wesssels
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.
36 #include "squid-old.h"
37 #include "AccessLogEntry.h"
38 #include "acl/FilledChecklist.h"
41 #include "comm/Loops.h"
44 #include "HttpRequest.h"
45 #include "icmp/net_db.h"
46 #include "ipc/StartListening.h"
49 #include "SquidTime.h"
50 #include "StatCounters.h"
52 #include "StoreClient.h"
53 #include "compat/xalloc.h"
55 /// dials htcpIncomingConnectionOpened call
56 class HtcpListeningStartedDialer
: public CallDialer
,
57 public Ipc::StartListeningCb
60 typedef void (*Handler
)(int errNo
);
61 HtcpListeningStartedDialer(Handler aHandler
): handler(aHandler
) {}
63 virtual void print(std::ostream
&os
) const { startPrint(os
) << ')'; }
64 virtual bool canDial(AsyncCall
&) const { return true; }
65 virtual void dial(AsyncCall
&) { (handler
)(errNo
); }
71 typedef struct _Countstr Countstr
;
73 typedef struct _htcpHeader htcpHeader
;
75 typedef struct _htcpDataHeader htcpDataHeader
;
77 typedef struct _htcpDataHeaderSquid htcpDataHeaderSquid
;
79 typedef struct _htcpAuthHeader htcpAuthHeader
;
81 typedef struct _htcpStuff htcpStuff
;
83 typedef struct _htcpDetail htcpDetail
;
96 struct _htcpDataHeaderSquid
{
100 unsigned int opcode
:4;
101 unsigned int response
:4;
103 unsigned int response
:4;
104 unsigned int opcode
:4;
108 unsigned int reserved
:6;
114 unsigned int reserved
:6;
120 struct _htcpDataHeader
{
154 /* RR == 0 --> F1 = RESPONSE DESIRED FLAG */
155 /* RR == 1 --> F1 = MESSAGE OVERALL FLAG */
156 /* RR == 0 --> REQUEST */
157 /* RR == 1 --> RESPONSE */
159 struct _htcpAuthHeader
{
167 class htcpSpecifier
: public StoreClient
171 MEMPROXY_CLASS(htcpSpecifier
);
173 void created (StoreEntry
*newEntry
);
175 void checkedHit(StoreEntry
*e
);
177 void setFrom(Ip::Address
&from
);
178 void setDataHeader(htcpDataHeader
*);
183 HttpRequest
*request
;
186 HttpRequest
*checkHitRequest
;
188 Ip::Address from
; // was a ptr. return to such IFF needed. otherwise copy should do.
189 htcpDataHeader
*dhdr
;
192 MEMPROXY_CLASS_INLINE(htcpSpecifier
);
220 static const char *const htcpOpcodeStr
[] = {
230 * values for htcpDataHeader->response
235 OPCODE_UNIMPLEMENTED
,
236 MAJOR_VERSION_UNSUPPORTED
,
237 MINOR_VERSION_UNSUPPORTED
,
242 * values for htcpDataHeader->RR
249 static void htcpIncomingConnectionOpened(int errNo
);
250 static uint32_t msg_id_counter
= 0;
252 static Comm::ConnectionPointer htcpOutgoingConn
= NULL
;
253 static Comm::ConnectionPointer htcpIncomingConn
= NULL
;
254 #define N_QUERIED_KEYS 8192
255 static uint32_t queried_id
[N_QUERIED_KEYS
];
256 static cache_key queried_keys
[N_QUERIED_KEYS
][SQUID_MD5_DIGEST_LENGTH
];
258 static Ip::Address queried_addr
[N_QUERIED_KEYS
];
259 static MemAllocator
*htcpDetailPool
= NULL
;
261 static int old_squid_format
= 0;
264 static ssize_t
htcpBuildPacket(char *buf
, size_t buflen
, htcpStuff
* stuff
);
265 static htcpSpecifier
*htcpUnpackSpecifier(char *buf
, int sz
);
266 static htcpDetail
*htcpUnpackDetail(char *buf
, int sz
);
267 static ssize_t
htcpBuildAuth(char *buf
, size_t buflen
);
268 static ssize_t
htcpBuildCountstr(char *buf
, size_t buflen
, const char *s
);
269 static ssize_t
htcpBuildData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
270 static ssize_t
htcpBuildDetail(char *buf
, size_t buflen
, htcpStuff
* stuff
);
271 static ssize_t
htcpBuildOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
272 static ssize_t
htcpBuildSpecifier(char *buf
, size_t buflen
, htcpStuff
* stuff
);
273 static ssize_t
htcpBuildTstOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
274 static void htcpFreeSpecifier(htcpSpecifier
* s
);
275 static void htcpFreeDetail(htcpDetail
* s
);
277 static void htcpHandleMsg(char *buf
, int sz
, Ip::Address
&from
);
279 static void htcpLogHtcp(Ip::Address
&, int, log_type
, const char *);
280 static void htcpHandleMon(htcpDataHeader
*, char *buf
, int sz
, Ip::Address
&from
);
282 static void htcpHandleNop(htcpDataHeader
*, char *buf
, int sz
, Ip::Address
&from
);
284 static void htcpHandleSet(htcpDataHeader
*, char *buf
, int sz
, Ip::Address
&from
);
286 static void htcpHandleTst(htcpDataHeader
*, char *buf
, int sz
, Ip::Address
&from
);
288 static void htcpRecv(int fd
, void *data
);
290 static void htcpSend(const char *buf
, int len
, Ip::Address
&to
);
292 static void htcpTstReply(htcpDataHeader
*, StoreEntry
*, htcpSpecifier
*, Ip::Address
&);
294 static void htcpHandleTstRequest(htcpDataHeader
*, char *buf
, int sz
, Ip::Address
&from
);
296 static void htcpHandleTstResponse(htcpDataHeader
*, char *, int, Ip::Address
&);
299 htcpHexdump(const char *tag
, const char *s
, int sz
)
305 debugs(31, 3, "htcpHexdump " << tag
);
306 memset(hex
, '\0', 80);
308 for (i
= 0; i
< sz
; i
++) {
310 snprintf(&hex
[k
* 3], 4, " %02x", (int) *(s
+ i
));
312 if (k
< 15 && i
< (sz
- 1))
315 debugs(31, 3, "\t" << hex
);
317 memset(hex
, '\0', 80);
324 * STUFF FOR SENDING HTCP MESSAGES
328 htcpBuildAuth(char *buf
, size_t buflen
)
332 assert(2 == sizeof(uint16_t));
333 auth
.length
= htons(2);
335 if (buflen
< copy_sz
)
337 memcpy(buf
, &auth
, copy_sz
);
342 htcpBuildCountstr(char *buf
, size_t buflen
, const char *s
)
348 if (buflen
- off
< 2)
356 debugs(31, 3, "htcpBuildCountstr: LENGTH = " << len
);
358 debugs(31, 3, "htcpBuildCountstr: TEXT = {" << (s
? s
: "<NULL>") << "}");
360 length
= htons((uint16_t) len
);
362 memcpy(buf
+ off
, &length
, 2);
366 if (buflen
- off
< len
)
370 memcpy(buf
+ off
, s
, len
);
378 htcpBuildSpecifier(char *buf
, size_t buflen
, htcpStuff
* stuff
)
382 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.method
);
389 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.uri
);
396 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.version
);
403 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.req_hdrs
);
410 debugs(31, 3, "htcpBuildSpecifier: size " << off
);
416 htcpBuildDetail(char *buf
, size_t buflen
, htcpStuff
* stuff
)
420 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.resp_hdrs
);
427 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.entity_hdrs
);
434 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.cache_hdrs
);
445 htcpBuildTstOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
450 debugs(31, 3, "htcpBuildTstOpData: RR_REQUEST");
451 return htcpBuildSpecifier(buf
, buflen
, stuff
);
454 debugs(31, 3, "htcpBuildTstOpData: RR_RESPONSE");
455 debugs(31, 3, "htcpBuildTstOpData: F1 = " << stuff
->f1
);
457 if (stuff
->f1
) /* cache miss */
460 return htcpBuildDetail(buf
, buflen
, stuff
);
463 fatal_dump("htcpBuildTstOpData: bad RR value");
470 htcpBuildClrOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
472 unsigned short reason
;
476 debugs(31, 3, "htcpBuildClrOpData: RR_REQUEST");
477 reason
= htons((unsigned short)stuff
->reason
);
478 memcpy(buf
, &reason
, 2);
479 return htcpBuildSpecifier(buf
+ 2, buflen
- 2, stuff
) + 2;
483 fatal_dump("htcpBuildClrOpData: bad RR value");
490 htcpBuildOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
493 debugs(31, 3, "htcpBuildOpData: opcode " << htcpOpcodeStr
[stuff
->op
]);
498 off
= htcpBuildTstOpData(buf
+ off
, buflen
, stuff
);
502 off
= htcpBuildClrOpData(buf
+ off
, buflen
, stuff
);
514 htcpBuildData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
518 size_t hdr_sz
= sizeof(htcpDataHeader
);
524 off
+= hdr_sz
; /* skip! */
526 op_data_sz
= htcpBuildOpData(buf
+ off
, buflen
- off
, stuff
);
533 debugs(31, 3, "htcpBuildData: hdr.length = " << off
);
535 hdr
.length
= (uint16_t) off
;
537 hdr
.opcode
= stuff
->op
;
539 hdr
.response
= stuff
->response
;
545 hdr
.msg_id
= stuff
->msg_id
;
547 /* convert multi-byte fields */
548 hdr
.length
= htons(hdr
.length
);
550 hdr
.msg_id
= htonl(hdr
.msg_id
);
552 if (!old_squid_format
) {
553 memcpy(buf
, &hdr
, hdr_sz
);
555 htcpDataHeaderSquid hdrSquid
;
556 memset(&hdrSquid
, 0, sizeof(hdrSquid
));
557 hdrSquid
.length
= hdr
.length
;
558 hdrSquid
.opcode
= hdr
.opcode
;
559 hdrSquid
.response
= hdr
.response
;
560 hdrSquid
.F1
= hdr
.F1
;
561 hdrSquid
.RR
= hdr
.RR
;
562 memcpy(buf
, &hdrSquid
, hdr_sz
);
565 debugs(31, 3, "htcpBuildData: size " << off
);
571 * Build an HTCP packet into buf, maximum length buflen.
572 * Returns the packet length, or zero on failure.
575 htcpBuildPacket(char *buf
, size_t buflen
, htcpStuff
* stuff
)
579 size_t hdr_sz
= sizeof(htcpHeader
);
581 /* skip the header -- we don't know the overall length */
583 if (buflen
< hdr_sz
) {
588 s
= htcpBuildData(buf
+ off
, buflen
- off
, stuff
);
595 s
= htcpBuildAuth(buf
+ off
, buflen
- off
);
602 hdr
.length
= htons((uint16_t) off
);
605 if (old_squid_format
)
610 memcpy(buf
, &hdr
, hdr_sz
);
612 debugs(31, 3, "htcpBuildPacket: size " << off
);
618 htcpSend(const char *buf
, int len
, Ip::Address
&to
)
620 debugs(31, 3, HERE
<< to
);
621 htcpHexdump("htcpSend", buf
, len
);
623 if (comm_udp_sendto(htcpOutgoingConn
->fd
, to
, buf
, len
) < 0)
624 debugs(31, 3, HERE
<< htcpOutgoingConn
<< " sendto: " << xstrerror());
626 statCounter
.htcp
.pkts_sent
++;
630 * STUFF FOR RECEIVING HTCP MESSAGES
634 htcpSpecifier::setFrom(Ip::Address
&aSocket
)
640 htcpSpecifier::setDataHeader(htcpDataHeader
*aDataHeader
)
646 htcpFreeSpecifier(htcpSpecifier
* s
)
648 HTTPMSGUNLOCK(s
->request
);
654 htcpFreeDetail(htcpDetail
* d
)
656 htcpDetailPool
->freeOne(d
);
660 * Unpack an HTCP SPECIFIER in place
661 * This will overwrite any following AUTH block
663 static htcpSpecifier
*
664 htcpUnpackSpecifier(char *buf
, int sz
)
666 htcpSpecifier
*s
= new htcpSpecifier
;
667 HttpRequestMethod method
;
669 /* Find length of METHOD */
670 uint16_t l
= ntohs(*(uint16_t *) buf
);
675 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack METHOD");
676 htcpFreeSpecifier(s
);
684 debugs(31, 6, "htcpUnpackSpecifier: METHOD (" << l
<< "/" << sz
<< ") '" << s
->method
<< "'");
686 /* Find length of URI */
687 l
= ntohs(*(uint16_t *) buf
);
691 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack URI");
692 htcpFreeSpecifier(s
);
696 /* Add terminating null to METHOD */
704 debugs(31, 6, "htcpUnpackSpecifier: URI (" << l
<< "/" << sz
<< ") '" << s
->uri
<< "'");
706 /* Find length of VERSION */
707 l
= ntohs(*(uint16_t *) buf
);
711 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack VERSION");
712 htcpFreeSpecifier(s
);
716 /* Add terminating null to URI */
724 debugs(31, 6, "htcpUnpackSpecifier: VERSION (" << l
<< "/" << sz
<< ") '" << s
->version
<< "'");
726 /* Find length of REQ-HDRS */
727 l
= ntohs(*(uint16_t *) buf
);
731 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack REQ-HDRS");
732 htcpFreeSpecifier(s
);
736 /* Add terminating null to URI */
744 debugs(31, 6, "htcpUnpackSpecifier: REQ-HDRS (" << l
<< "/" << sz
<< ") '" << s
->req_hdrs
<< "'");
746 debugs(31, 3, "htcpUnpackSpecifier: " << sz
<< " bytes left");
749 * Add terminating null to REQ-HDRS. This is possible because we allocated
750 * an extra byte when we received the packet. This will overwrite any following
758 method
= HttpRequestMethod(s
->method
, NULL
);
760 s
->request
= HttpRequest::CreateFromUrlAndMethod(s
->uri
, method
== METHOD_NONE
? HttpRequestMethod(METHOD_GET
) : method
);
763 HTTPMSGLOCK(s
->request
);
769 * Unpack an HTCP DETAIL in place
770 * This will overwrite any following AUTH block
773 htcpUnpackDetail(char *buf
, int sz
)
775 htcpDetail
*d
= static_cast<htcpDetail
*>(htcpDetailPool
->alloc());
777 /* Find length of RESP-HDRS */
778 uint16_t l
= ntohs(*(uint16_t *) buf
);
783 debugs(31, 3, "htcpUnpackDetail: failed to unpack RESP_HDRS");
795 /* Find length of ENTITY-HDRS */
796 l
= ntohs(*(uint16_t *) buf
);
801 debugs(31, 3, "htcpUnpackDetail: failed to unpack ENTITY_HDRS");
806 /* Add terminating null to RESP-HDRS */
809 /* Set ENTITY-HDRS */
812 d
->entity_hdrs
= buf
;
818 /* Find length of CACHE-HDRS */
819 l
= ntohs(*(uint16_t *) buf
);
824 debugs(31, 3, "htcpUnpackDetail: failed to unpack CACHE_HDRS");
829 /* Add terminating null to ENTITY-HDRS */
841 debugs(31, 3, "htcpUnpackDetail: " << sz
<< " bytes left");
844 * Add terminating null to CACHE-HDRS. This is possible because we allocated
845 * an extra byte when we received the packet. This will overwrite any following
854 htcpAccessAllowed(acl_access
* acl
, htcpSpecifier
* s
, Ip::Address
&from
)
856 /* default deny if no access list present */
860 ACLFilledChecklist
checklist(acl
, s
->request
, NULL
);
861 checklist
.src_addr
= from
;
862 checklist
.my_addr
.SetNoAddr();
863 return (checklist
.fastCheck() == ACCESS_ALLOWED
);
867 htcpTstReply(htcpDataHeader
* dhdr
, StoreEntry
* e
, htcpSpecifier
* spec
, Ip::Address
&from
)
870 static char pkt
[8192];
871 HttpHeader
hdr(hoHtcpReply
);
875 memset(&stuff
, '\0', sizeof(stuff
));
877 stuff
.rr
= RR_RESPONSE
;
879 stuff
.response
= e
? 0 : 1;
880 debugs(31, 3, "htcpTstReply: response = " << stuff
.response
);
881 stuff
.msg_id
= dhdr
->msg_id
;
885 packerToMemInit(&p
, &mb
);
886 stuff
.S
.method
= spec
->method
;
887 stuff
.S
.uri
= spec
->uri
;
888 stuff
.S
.version
= spec
->version
;
889 stuff
.S
.req_hdrs
= spec
->req_hdrs
;
891 hdr
.putInt(HDR_AGE
, (e
->timestamp
<= squid_curtime
? (squid_curtime
- e
->timestamp
) : 0) );
893 hdr
.putInt(HDR_AGE
, 0);
895 stuff
.D
.resp_hdrs
= xstrdup(mb
.buf
);
896 debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff
.D
.resp_hdrs
<< "}");
900 if (e
&& e
->expires
> -1)
901 hdr
.putTime(HDR_EXPIRES
, e
->expires
);
903 if (e
&& e
->lastmod
> -1)
904 hdr
.putTime(HDR_LAST_MODIFIED
, e
->lastmod
);
908 stuff
.D
.entity_hdrs
= xstrdup(mb
.buf
);
910 debugs(31, 3, "htcpTstReply: entity_hdrs = {" << stuff
.D
.entity_hdrs
<< "}");
917 if (char *host
= urlHostname(spec
->uri
)) {
921 netdbHostData(host
, &samp
, &rtt
, &hops
);
925 snprintf(cto_buf
, 128, "%s %d %f %d",
926 host
, samp
, 0.001 * rtt
, hops
);
927 hdr
.putExt("Cache-to-Origin", cto_buf
);
930 #endif /* USE_ICMP */
933 stuff
.D
.cache_hdrs
= xstrdup(mb
.buf
);
934 debugs(31, 3, "htcpTstReply: cache_hdrs = {" << stuff
.D
.cache_hdrs
<< "}");
940 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
942 safe_free(stuff
.D
.resp_hdrs
);
943 safe_free(stuff
.D
.entity_hdrs
);
944 safe_free(stuff
.D
.cache_hdrs
);
947 debugs(31, 3, "htcpTstReply: htcpBuildPacket() failed");
951 htcpSend(pkt
, (int) pktlen
, from
);
956 htcpClrReply(htcpDataHeader
* dhdr
, int purgeSucceeded
, Ip::Address
&from
)
959 static char pkt
[8192];
962 /* If dhdr->F1 == 0, no response desired */
967 memset(&stuff
, '\0', sizeof(stuff
));
971 stuff
.rr
= RR_RESPONSE
;
975 stuff
.response
= purgeSucceeded
? 0 : 2;
977 debugs(31, 3, "htcpClrReply: response = " << stuff
.response
);
979 stuff
.msg_id
= dhdr
->msg_id
;
981 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
984 debugs(31, 3, "htcpClrReply: htcpBuildPacket() failed");
988 htcpSend(pkt
, (int) pktlen
, from
);
993 htcpHandleNop(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
995 debugs(31, 3, "htcpHandleNop: Unimplemented");
999 htcpSpecifier::checkHit()
1002 checkHitRequest
= request
;
1004 if (NULL
== checkHitRequest
) {
1005 debugs(31, 3, "htcpCheckHit: NO; failed to parse URL");
1006 checkedHit(NullStoreEntry::getInstance());
1010 blk_end
= req_hdrs
+ strlen(req_hdrs
);
1012 if (!checkHitRequest
->header
.parse(req_hdrs
, blk_end
)) {
1013 debugs(31, 3, "htcpCheckHit: NO; failed to parse request headers");
1014 delete checkHitRequest
;
1015 checkHitRequest
= NULL
;
1016 checkedHit(NullStoreEntry::getInstance());
1020 StoreEntry::getPublicByRequest(this, checkHitRequest
);
1024 htcpSpecifier::created (StoreEntry
*e
)
1026 StoreEntry
*hit
=NULL
;
1030 debugs(31, 3, "htcpCheckHit: NO; public object not found");
1031 } else if (!e
->validToSend()) {
1032 debugs(31, 3, "htcpCheckHit: NO; entry not valid to send" );
1033 } else if (refreshCheckHTCP(e
, checkHitRequest
)) {
1034 debugs(31, 3, "htcpCheckHit: NO; cached response is stale");
1036 debugs(31, 3, "htcpCheckHit: YES!?");
1044 htcpClrStoreEntry(StoreEntry
* e
)
1046 debugs(31, 4, "htcpClrStoreEntry: Clearing store for entry: " << e
->url() );
1047 e
->releaseRequest();
1051 htcpClrStore(const htcpSpecifier
* s
)
1053 HttpRequest
*request
= s
->request
;
1055 StoreEntry
*e
= NULL
;
1058 if (request
== NULL
) {
1059 debugs(31, 3, "htcpClrStore: failed to parse URL");
1063 /* Parse request headers */
1064 blk_end
= s
->req_hdrs
+ strlen(s
->req_hdrs
);
1066 if (!request
->header
.parse(s
->req_hdrs
, blk_end
)) {
1067 debugs(31, 2, "htcpClrStore: failed to parse request headers");
1071 /* Lookup matching entries. This matches both GET and HEAD */
1072 while ((e
= storeGetPublicByRequest(request
)) != NULL
) {
1074 htcpClrStoreEntry(e
);
1080 debugs(31, 4, "htcpClrStore: Cleared " << released
<< " matching entries");
1083 debugs(31, 4, "htcpClrStore: No matching entry found");
1090 htcpHandleTst(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1092 debugs(31, 3, "htcpHandleTst: sz = " << sz
);
1094 if (hdr
->RR
== RR_REQUEST
)
1095 htcpHandleTstRequest(hdr
, buf
, sz
, from
);
1097 htcpHandleTstResponse(hdr
, buf
, sz
, from
);
1100 HtcpReplyData::HtcpReplyData() : hdr(hoHtcpReply
)
1105 htcpHandleTstResponse(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1107 htcpReplyData htcpReply
;
1108 cache_key
*key
= NULL
;
1111 htcpDetail
*d
= NULL
;
1114 if (queried_id
[hdr
->msg_id
% N_QUERIED_KEYS
] != hdr
->msg_id
) {
1115 debugs(31, 2, "htcpHandleTstResponse: No matching query id '" <<
1116 hdr
->msg_id
<< "' (expected " <<
1117 queried_id
[hdr
->msg_id
% N_QUERIED_KEYS
] << ") from '" <<
1123 key
= queried_keys
[hdr
->msg_id
% N_QUERIED_KEYS
];
1126 debugs(31, 3, "htcpHandleTstResponse: No query key for response id '" << hdr
->msg_id
<< "' from '" << from
<< "'");
1130 peer
= &queried_addr
[hdr
->msg_id
% N_QUERIED_KEYS
];
1132 if ( *peer
!= from
|| peer
->GetPort() != from
.GetPort() ) {
1133 debugs(31, 3, "htcpHandleTstResponse: Unexpected response source " << from
);
1138 debugs(31, 2, "htcpHandleTstResponse: error condition, F1/MO == 1");
1142 htcpReply
.msg_id
= hdr
->msg_id
;
1143 debugs(31, 3, "htcpHandleTstResponse: msg_id = " << htcpReply
.msg_id
);
1144 htcpReply
.hit
= hdr
->response
? 0 : 1;
1147 debugs(31, 3, "htcpHandleTstResponse: MISS");
1149 debugs(31, 3, "htcpHandleTstResponse: HIT");
1150 d
= htcpUnpackDetail(buf
, sz
);
1153 debugs(31, 3, "htcpHandleTstResponse: bad DETAIL");
1157 if ((t
= d
->resp_hdrs
))
1158 htcpReply
.hdr
.parse(t
, t
+ strlen(t
));
1160 if ((t
= d
->entity_hdrs
))
1161 htcpReply
.hdr
.parse(t
, t
+ strlen(t
));
1163 if ((t
= d
->cache_hdrs
))
1164 htcpReply
.hdr
.parse(t
, t
+ strlen(t
));
1167 debugs(31, 3, "htcpHandleTstResponse: key (" << key
<< ") " << storeKeyText(key
));
1168 neighborsHtcpReply(key
, &htcpReply
, from
);
1169 htcpReply
.hdr
.clean();
1176 htcpHandleTstRequest(htcpDataHeader
* dhdr
, char *buf
, int sz
, Ip::Address
&from
)
1178 /* buf should be a SPECIFIER */
1182 debugs(31, 3, "htcpHandleTst: nothing to do");
1189 /* s is a new object */
1190 s
= htcpUnpackSpecifier(buf
, sz
);
1194 s
->setDataHeader(dhdr
);
1197 debugs(31, 3, "htcpHandleTstRequest: htcpUnpackSpecifier failed");
1198 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1203 debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
1204 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1205 htcpFreeSpecifier(s
);
1209 if (!htcpAccessAllowed(Config
.accessList
.htcp
, s
, from
)) {
1210 debugs(31, 3, "htcpHandleTstRequest: Access denied");
1211 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_DENIED
, s
->uri
);
1212 htcpFreeSpecifier(s
);
1216 debugs(31, 2, "HTCP TST request: " << s
->method
<< " " << s
->uri
<< " " << s
->version
);
1217 debugs(31, 2, "HTCP TST headers: " << s
->req_hdrs
);
1222 htcpSpecifier::checkedHit(StoreEntry
*e
)
1225 htcpTstReply(dhdr
, e
, this, from
); /* hit */
1226 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_HIT
, uri
);
1228 htcpTstReply(dhdr
, NULL
, NULL
, from
); /* cache miss */
1229 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_MISS
, uri
);
1232 htcpFreeSpecifier(this);
1237 htcpHandleMon(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1239 debugs(31, 3, "htcpHandleMon: Unimplemented");
1244 htcpHandleSet(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1246 debugs(31, 3, "htcpHandleSet: Unimplemented");
1250 htcpHandleClr(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1253 /* buf[0/1] is reserved and reason */
1254 int reason
= buf
[1] << 4;
1255 debugs(31, 2, "HTCP CLR reason: " << reason
);
1259 /* buf should be a SPECIFIER */
1262 debugs(31, 4, "htcpHandleClr: nothing to do");
1263 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1267 s
= htcpUnpackSpecifier(buf
, sz
);
1270 debugs(31, 3, "htcpHandleClr: htcpUnpackSpecifier failed");
1271 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1276 debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
1277 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1278 htcpFreeSpecifier(s
);
1282 if (!htcpAccessAllowed(Config
.accessList
.htcp_clr
, s
, from
)) {
1283 debugs(31, 3, "htcpHandleClr: Access denied");
1284 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_DENIED
, s
->uri
);
1285 htcpFreeSpecifier(s
);
1289 debugs(31, 2, "HTCP CLR request: " << s
->method
<< " " << s
->uri
<< " " << s
->version
);
1290 debugs(31, 2, "HTCP CLR headers: " << s
->req_hdrs
);
1292 /* Release objects from cache
1293 * analog to clientPurgeRequest in client_side.c
1296 switch (htcpClrStore(s
)) {
1299 htcpClrReply(hdr
, 1, from
); /* hit */
1300 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_HIT
, s
->uri
);
1304 htcpClrReply(hdr
, 0, from
); /* miss */
1305 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_MISS
, s
->uri
);
1312 htcpFreeSpecifier(s
);
1316 * Forward a CLR request to all peers who have requested that CLRs be
1317 * forwarded to them.
1320 htcpForwardClr(char *buf
, int sz
)
1324 for (p
= Config
.peers
; p
; p
= p
->next
) {
1325 if (!p
->options
.htcp
) {
1328 if (!p
->options
.htcp_forward_clr
) {
1332 htcpSend(buf
, sz
, p
->in_addr
);
1337 * Do the first pass of handling an HTCP message. This used to be two
1338 * separate functions, htcpHandle and htcpHandleData. They were merged to
1339 * allow for forwarding HTCP packets easily to other peers if desired.
1341 * This function now works out what type of message we have received and then
1342 * hands it off to other functions to break apart message-specific data.
1345 htcpHandleMsg(char *buf
, int sz
, Ip::Address
&from
)
1352 if (sz
< 0 || (size_t)sz
< sizeof(htcpHeader
)) {
1353 // These are highly likely to be attack packets. Should probably get a bigger warning.
1354 debugs(31, 2, "htcpHandle: msg size less than htcpHeader size from " << from
);
1358 htcpHexdump("htcpHandle", buf
, sz
);
1359 memcpy(&htcpHdr
, buf
, sizeof(htcpHeader
));
1360 htcpHdr
.length
= ntohs(htcpHdr
.length
);
1362 if (htcpHdr
.minor
== 0)
1363 old_squid_format
= 1;
1365 old_squid_format
= 0;
1367 debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr
.length
);
1368 debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr
.major
);
1369 debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr
.minor
);
1371 if (sz
!= htcpHdr
.length
) {
1372 debugs(31, 3, "htcpHandle: sz/" << sz
<< " != htcpHdr.length/" <<
1373 htcpHdr
.length
<< " from " << from
);
1378 if (htcpHdr
.major
!= 0) {
1379 debugs(31, 3, "htcpHandle: Unknown major version " << htcpHdr
.major
<< " from " << from
);
1384 hbuf
= buf
+ sizeof(htcpHeader
);
1385 hsz
= sz
- sizeof(htcpHeader
);
1387 if ((size_t)hsz
< sizeof(htcpDataHeader
)) {
1388 debugs(31, 3, "htcpHandleData: msg size less than htcpDataHeader size");
1392 if (!old_squid_format
) {
1393 memcpy(&hdr
, hbuf
, sizeof(hdr
));
1395 htcpDataHeaderSquid hdrSquid
;
1396 memcpy(&hdrSquid
, hbuf
, sizeof(hdrSquid
));
1397 hdr
.length
= hdrSquid
.length
;
1398 hdr
.opcode
= hdrSquid
.opcode
;
1399 hdr
.response
= hdrSquid
.response
;
1400 hdr
.F1
= hdrSquid
.F1
;
1401 hdr
.RR
= hdrSquid
.RR
;
1403 hdr
.msg_id
= hdrSquid
.msg_id
;
1406 hdr
.length
= ntohs(hdr
.length
);
1407 hdr
.msg_id
= ntohl(hdr
.msg_id
);
1408 debugs(31, 3, "htcpHandleData: hsz = " << hsz
);
1409 debugs(31, 3, "htcpHandleData: length = " << hdr
.length
);
1411 if (hdr
.opcode
>= HTCP_END
) {
1412 debugs(31, 3, "htcpHandleData: client " << from
<< ", opcode " << hdr
.opcode
<< " out of range");
1416 debugs(31, 3, "htcpHandleData: opcode = " << hdr
.opcode
<< " " << htcpOpcodeStr
[hdr
.opcode
]);
1417 debugs(31, 3, "htcpHandleData: response = " << hdr
.response
);
1418 debugs(31, 3, "htcpHandleData: F1 = " << hdr
.F1
);
1419 debugs(31, 3, "htcpHandleData: RR = " << hdr
.RR
);
1420 debugs(31, 3, "htcpHandleData: msg_id = " << hdr
.msg_id
);
1422 if (hsz
< hdr
.length
) {
1423 debugs(31, 3, "htcpHandleData: sz < hdr.length");
1428 * set sz = hdr.length so we ignore any AUTH fields following
1431 hsz
= (int) hdr
.length
;
1432 hbuf
+= sizeof(htcpDataHeader
);
1433 hsz
-= sizeof(htcpDataHeader
);
1434 debugs(31, 3, "htcpHandleData: hsz = " << hsz
);
1436 htcpHexdump("htcpHandleData", hbuf
, hsz
);
1438 switch (hdr
.opcode
) {
1440 htcpHandleNop(&hdr
, hbuf
, hsz
, from
);
1443 htcpHandleTst(&hdr
, hbuf
, hsz
, from
);
1446 htcpHandleMon(&hdr
, hbuf
, hsz
, from
);
1449 htcpHandleSet(&hdr
, hbuf
, hsz
, from
);
1452 htcpHandleClr(&hdr
, hbuf
, hsz
, from
);
1453 htcpForwardClr(buf
, sz
);
1461 htcpRecv(int fd
, void *data
)
1463 static char buf
[8192];
1465 static Ip::Address from
;
1467 /* Receive up to 8191 bytes, leaving room for a null */
1469 len
= comm_udp_recvfrom(fd
, buf
, sizeof(buf
) - 1, 0, from
);
1471 debugs(31, 3, "htcpRecv: FD " << fd
<< ", " << len
<< " bytes from " << from
);
1474 statCounter
.htcp
.pkts_recv
++;
1476 htcpHandleMsg(buf
, len
, from
);
1478 Comm::SetSelect(fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1482 * ======================================================================
1484 * ======================================================================
1490 if (Config
.Port
.htcp
<= 0) {
1491 debugs(31, DBG_IMPORTANT
, "HTCP Disabled.");
1495 htcpIncomingConn
= new Comm::Connection
;
1496 htcpIncomingConn
->local
= Config
.Addrs
.udp_incoming
;
1497 htcpIncomingConn
->local
.SetPort(Config
.Port
.htcp
);
1499 if (!Ip::EnableIpv6
&& !htcpIncomingConn
->local
.SetIPv4()) {
1500 debugs(31, DBG_CRITICAL
, "ERROR: IPv6 is disabled. " << htcpIncomingConn
->local
<< " is not an IPv4 address.");
1501 fatal("HTCP port cannot be opened.");
1503 /* split-stack for now requires default IPv4-only HTCP */
1504 if (Ip::EnableIpv6
&IPV6_SPECIAL_SPLITSTACK
&& htcpIncomingConn
->local
.IsAnyAddr()) {
1505 htcpIncomingConn
->local
.SetIPv4();
1508 AsyncCall::Pointer call
= asyncCall(31, 2,
1509 "htcpIncomingConnectionOpened",
1510 HtcpListeningStartedDialer(&htcpIncomingConnectionOpened
));
1512 Ipc::StartListening(SOCK_DGRAM
,
1515 Ipc::fdnInHtcpSocket
, call
);
1517 if (!Config
.Addrs
.udp_outgoing
.IsNoAddr()) {
1518 htcpOutgoingConn
= new Comm::Connection
;
1519 htcpOutgoingConn
->local
= Config
.Addrs
.udp_outgoing
;
1520 htcpOutgoingConn
->local
.SetPort(Config
.Port
.htcp
);
1522 if (!Ip::EnableIpv6
&& !htcpOutgoingConn
->local
.SetIPv4()) {
1523 debugs(31, DBG_CRITICAL
, "ERROR: IPv6 is disabled. " << htcpOutgoingConn
->local
<< " is not an IPv4 address.");
1524 fatal("HTCP port cannot be opened.");
1526 /* split-stack for now requires default IPv4-only HTCP */
1527 if (Ip::EnableIpv6
&IPV6_SPECIAL_SPLITSTACK
&& htcpOutgoingConn
->local
.IsAnyAddr()) {
1528 htcpOutgoingConn
->local
.SetIPv4();
1532 comm_open_listener(SOCK_DGRAM
, IPPROTO_UDP
, htcpOutgoingConn
, "Outgoing HTCP Socket");
1535 if (!Comm::IsConnOpen(htcpOutgoingConn
))
1536 fatal("Cannot open Outgoing HTCP Socket");
1538 Comm::SetSelect(htcpOutgoingConn
->fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1540 debugs(31, DBG_IMPORTANT
, "Sending HTCP messages from " << htcpOutgoingConn
->local
);
1543 if (!htcpDetailPool
) {
1544 htcpDetailPool
= memPoolCreate("htcpDetail", sizeof(htcpDetail
));
1549 htcpIncomingConnectionOpened(int)
1551 if (!Comm::IsConnOpen(htcpIncomingConn
))
1552 fatal("Cannot open HTCP Socket");
1554 Comm::SetSelect(htcpIncomingConn
->fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1556 debugs(31, DBG_CRITICAL
, "Accepting HTCP messages on " << htcpIncomingConn
->local
);
1558 if (Config
.Addrs
.udp_outgoing
.IsNoAddr()) {
1559 htcpOutgoingConn
= htcpIncomingConn
;
1560 debugs(31, DBG_IMPORTANT
, "Sending HTCP messages from " << htcpOutgoingConn
->local
);
1565 htcpQuery(StoreEntry
* e
, HttpRequest
* req
, peer
* p
)
1567 cache_key
*save_key
;
1568 static char pkt
[8192];
1572 HttpHeader
hdr(hoRequest
);
1575 http_state_flags flags
;
1577 if (!Comm::IsConnOpen(htcpIncomingConn
))
1580 old_squid_format
= p
->options
.htcp_oldsquid
;
1581 memset(&flags
, '\0', sizeof(flags
));
1582 snprintf(vbuf
, sizeof(vbuf
), "%d/%d",
1583 req
->http_ver
.major
, req
->http_ver
.minor
);
1584 stuff
.op
= HTCP_TST
;
1585 stuff
.rr
= RR_REQUEST
;
1588 stuff
.msg_id
= ++msg_id_counter
;
1589 stuff
.S
.method
= (char *) RequestMethodStr(req
->method
);
1590 stuff
.S
.uri
= (char *) e
->url();
1591 stuff
.S
.version
= vbuf
;
1592 HttpStateData::httpBuildRequestHeader(req
, e
, &hdr
, flags
);
1594 packerToMemInit(&pa
, &mb
);
1598 stuff
.S
.req_hdrs
= mb
.buf
;
1599 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
1602 debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
1606 htcpSend(pkt
, (int) pktlen
, p
->in_addr
);
1608 queried_id
[stuff
.msg_id
% N_QUERIED_KEYS
] = stuff
.msg_id
;
1609 save_key
= queried_keys
[stuff
.msg_id
% N_QUERIED_KEYS
];
1610 storeKeyCopy(save_key
, (const cache_key
*)e
->key
);
1611 queried_addr
[stuff
.msg_id
% N_QUERIED_KEYS
] = p
->in_addr
;
1612 debugs(31, 3, "htcpQuery: key (" << save_key
<< ") " << storeKeyText(save_key
));
1618 * Send an HTCP CLR message for a specified item to a given peer.
1621 htcpClear(StoreEntry
* e
, const char *uri
, HttpRequest
* req
, const HttpRequestMethod
&method
, peer
* p
, htcp_clr_reason reason
)
1623 static char pkt
[8192];
1627 HttpHeader
hdr(hoRequest
);
1630 http_state_flags flags
;
1632 if (!Comm::IsConnOpen(htcpIncomingConn
))
1635 old_squid_format
= p
->options
.htcp_oldsquid
;
1636 memset(&flags
, '\0', sizeof(flags
));
1637 snprintf(vbuf
, sizeof(vbuf
), "%d/%d",
1638 req
->http_ver
.major
, req
->http_ver
.minor
);
1639 stuff
.op
= HTCP_CLR
;
1640 stuff
.rr
= RR_REQUEST
;
1643 stuff
.msg_id
= ++msg_id_counter
;
1645 case HTCP_CLR_INVALIDATION
:
1652 stuff
.S
.method
= (char *) RequestMethodStr(req
->method
);
1653 if (e
== NULL
|| e
->mem_obj
== NULL
) {
1657 stuff
.S
.uri
= xstrdup(uri
);
1659 stuff
.S
.uri
= (char *) e
->url();
1661 stuff
.S
.version
= vbuf
;
1662 if (reason
!= HTCP_CLR_INVALIDATION
) {
1663 HttpStateData::httpBuildRequestHeader(req
, e
, &hdr
, flags
);
1665 packerToMemInit(&pa
, &mb
);
1669 stuff
.S
.req_hdrs
= mb
.buf
;
1671 stuff
.S
.req_hdrs
= NULL
;
1673 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
1674 if (reason
!= HTCP_CLR_INVALIDATION
) {
1681 debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
1685 htcpSend(pkt
, (int) pktlen
, p
->in_addr
);
1689 * htcpSocketShutdown only closes the 'in' socket if it is
1690 * different than the 'out' socket.
1693 htcpSocketShutdown(void)
1695 if (!Comm::IsConnOpen(htcpIncomingConn
))
1698 debugs(12, DBG_IMPORTANT
, "Stop accepting HTCP on " << htcpIncomingConn
->local
);
1700 * Here we just unlink htcpIncomingConn because the HTCP 'in'
1701 * and 'out' sockets might be just one FD. This prevents this
1702 * function from executing repeatedly. When we are really ready to
1703 * exit or restart, main will comm_close the 'out' descriptor.
1705 htcpIncomingConn
= NULL
;
1708 * Normally we only write to the outgoing HTCP socket, but
1709 * we also have a read handler there to catch messages sent
1710 * to that specific interface. During shutdown, we must
1711 * disable reading on the outgoing socket.
1713 /* XXX Don't we need this handler to read replies while shutting down?
1714 * I think there should be a separate hander for reading replies..
1716 assert(Comm::IsConnOpen(htcpOutgoingConn
));
1718 Comm::SetSelect(htcpOutgoingConn
->fd
, COMM_SELECT_READ
, NULL
, NULL
, 0);
1722 htcpSocketClose(void)
1724 htcpSocketShutdown();
1726 if (htcpOutgoingConn
!= NULL
) {
1727 debugs(12, DBG_IMPORTANT
, "Stop sending HTCP from " << htcpOutgoingConn
->local
);
1728 htcpOutgoingConn
= NULL
;
1733 htcpLogHtcp(Ip::Address
&caddr
, int opcode
, log_type logcode
, const char *url
)
1736 if (LOG_TAG_NONE
== logcode
)
1738 if (!Config
.onoff
.log_udp
)
1740 al
.htcp
.opcode
= htcpOpcodeStr
[opcode
];
1742 al
.cache
.caddr
= caddr
;
1743 al
.cache
.code
= logcode
;
1745 accessLogLog(&al
, NULL
);