2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 31 Hypertext Caching Protocol */
12 #include "AccessLogEntry.h"
14 #include "acl/FilledChecklist.h"
15 #include "CachePeer.h"
17 #include "comm/Connection.h"
18 #include "comm/Loops.h"
19 #include "comm/UdpOpenDialer.h"
20 #include "compat/xalloc.h"
24 #include "HttpRequest.h"
25 #include "HttpStateFlags.h"
26 #include "icmp/net_db.h"
29 #include "mem/forward.h"
32 #include "SquidConfig.h"
33 #include "SquidTime.h"
34 #include "StatCounters.h"
36 #include "store_key_md5.h"
37 #include "StoreClient.h"
41 typedef struct _Countstr Countstr
;
43 typedef struct _htcpHeader htcpHeader
;
45 typedef struct _htcpDataHeader htcpDataHeader
;
47 typedef struct _htcpDataHeaderSquid htcpDataHeaderSquid
;
49 typedef struct _htcpAuthHeader htcpAuthHeader
;
62 struct _htcpDataHeaderSquid
{
66 unsigned int opcode
:4;
67 unsigned int response
:4;
69 unsigned int response
:4;
70 unsigned int opcode
:4;
74 unsigned int reserved
:6;
80 unsigned int reserved
:6;
86 struct _htcpDataHeader
{
110 /* RR == 0 --> F1 = RESPONSE DESIRED FLAG */
111 /* RR == 1 --> F1 = MESSAGE OVERALL FLAG */
112 /* RR == 0 --> REQUEST */
113 /* RR == 1 --> RESPONSE */
115 struct _htcpAuthHeader
{
123 class htcpSpecifier
: public StoreClient
125 MEMPROXY_CLASS(htcpSpecifier
);
135 checkHitRequest(NULL
),
140 void created (StoreEntry
*newEntry
);
142 void checkedHit(StoreEntry
*e
);
144 void setFrom(Ip::Address
&from
);
145 void setDataHeader(htcpDataHeader
*);
150 size_t reqHdrsSz
; ///< size of the req_hdrs content
151 HttpRequest
*request
;
154 HttpRequest
*checkHitRequest
;
156 Ip::Address from
; // was a ptr. return to such IFF needed. otherwise copy should do.
157 htcpDataHeader
*dhdr
;
161 MEMPROXY_CLASS(htcpDetail
);
163 htcpDetail() : resp_hdrs(nullptr), respHdrsSz(0), entity_hdrs(nullptr), entityHdrsSz(0), cache_hdrs(nullptr), cacheHdrsSz(0) {}
177 htcpStuff(uint32_t id
, int o
, int r
, int f
) :
185 memset(&D
, 0, sizeof(D
));
207 static const char *const htcpOpcodeStr
[] = {
217 * values for htcpDataHeader->response
222 OPCODE_UNIMPLEMENTED
,
223 MAJOR_VERSION_UNSUPPORTED
,
224 MINOR_VERSION_UNSUPPORTED
,
229 * values for htcpDataHeader->RR
236 static void htcpIncomingConnectionOpened(const Comm::ConnectionPointer
&conn
, int errNo
);
237 static uint32_t msg_id_counter
= 0;
239 static Comm::ConnectionPointer htcpOutgoingConn
= NULL
;
240 static Comm::ConnectionPointer htcpIncomingConn
= NULL
;
241 #define N_QUERIED_KEYS 8192
242 static uint32_t queried_id
[N_QUERIED_KEYS
];
243 static cache_key queried_keys
[N_QUERIED_KEYS
][SQUID_MD5_DIGEST_LENGTH
];
245 static Ip::Address queried_addr
[N_QUERIED_KEYS
];
247 static int old_squid_format
= 0;
249 static ssize_t
htcpBuildPacket(char *buf
, size_t buflen
, htcpStuff
* stuff
);
250 static htcpSpecifier
*htcpUnpackSpecifier(char *buf
, int sz
);
251 static htcpDetail
*htcpUnpackDetail(char *buf
, int sz
);
252 static ssize_t
htcpBuildAuth(char *buf
, size_t buflen
);
253 static ssize_t
htcpBuildCountstr(char *buf
, size_t buflen
, const char *s
, size_t len
);
254 static ssize_t
htcpBuildData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
255 static ssize_t
htcpBuildDetail(char *buf
, size_t buflen
, htcpStuff
* stuff
);
256 static ssize_t
htcpBuildOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
257 static ssize_t
htcpBuildSpecifier(char *buf
, size_t buflen
, htcpStuff
* stuff
);
258 static ssize_t
htcpBuildTstOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
259 static void htcpFreeSpecifier(htcpSpecifier
* s
);
261 static void htcpHandleMsg(char *buf
, int sz
, Ip::Address
&from
);
263 static void htcpLogHtcp(Ip::Address
&, int, LogTags
, const char *);
264 static void htcpHandleTst(htcpDataHeader
*, char *buf
, int sz
, Ip::Address
&from
);
266 static void htcpRecv(int fd
, void *data
);
268 static void htcpSend(const char *buf
, int len
, Ip::Address
&to
);
270 static void htcpTstReply(htcpDataHeader
*, StoreEntry
*, htcpSpecifier
*, Ip::Address
&);
272 static void htcpHandleTstRequest(htcpDataHeader
*, char *buf
, int sz
, Ip::Address
&from
);
274 static void htcpHandleTstResponse(htcpDataHeader
*, char *, int, Ip::Address
&);
277 htcpHexdump(const char *tag
, const char *s
, int sz
)
281 debugs(31, 3, "htcpHexdump " << tag
);
282 memset(hex
, '\0', sizeof(hex
));
284 for (int i
= 0; i
< sz
; ++i
) {
286 snprintf(&hex
[k
* 3], 4, " %02x", (int) *(s
+ i
));
288 if (k
< 15 && i
< (sz
- 1))
291 debugs(31, 3, "\t" << hex
);
293 memset(hex
, '\0', sizeof(hex
));
299 * STUFF FOR SENDING HTCP MESSAGES
303 htcpBuildAuth(char *buf
, size_t buflen
)
307 assert(2 == sizeof(uint16_t));
308 auth
.length
= htons(2);
310 if (buflen
< copy_sz
)
312 memcpy(buf
, &auth
, copy_sz
);
317 htcpBuildCountstr(char *buf
, size_t buflen
, const char *s
, size_t len
)
321 if (buflen
- off
< 2)
324 debugs(31, 3, "htcpBuildCountstr: LENGTH = " << len
);
326 debugs(31, 3, "htcpBuildCountstr: TEXT = {" << (s
? s
: "<NULL>") << "}");
328 uint16_t length
= htons((uint16_t) len
);
330 memcpy(buf
+ off
, &length
, 2);
334 if (buflen
- off
< len
)
338 memcpy(buf
+ off
, s
, len
);
346 htcpBuildSpecifier(char *buf
, size_t buflen
, htcpStuff
* stuff
)
350 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.method
, (stuff
->S
.method
?strlen(stuff
->S
.method
):0));
357 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.uri
, (stuff
->S
.uri
?strlen(stuff
->S
.uri
):0));
364 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.version
, (stuff
->S
.version
?strlen(stuff
->S
.version
):0));
371 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.req_hdrs
, stuff
->S
.reqHdrsSz
);
378 debugs(31, 3, "htcpBuildSpecifier: size " << off
);
384 htcpBuildDetail(char *buf
, size_t buflen
, htcpStuff
* stuff
)
388 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.resp_hdrs
, stuff
->D
.respHdrsSz
);
395 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.entity_hdrs
, stuff
->D
.entityHdrsSz
);
402 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.cache_hdrs
, stuff
->D
.cacheHdrsSz
);
413 htcpBuildTstOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
418 debugs(31, 3, "htcpBuildTstOpData: RR_REQUEST");
419 return htcpBuildSpecifier(buf
, buflen
, stuff
);
422 debugs(31, 3, "htcpBuildTstOpData: RR_RESPONSE");
423 debugs(31, 3, "htcpBuildTstOpData: F1 = " << stuff
->f1
);
425 if (stuff
->f1
) /* cache miss */
428 return htcpBuildDetail(buf
, buflen
, stuff
);
431 fatal_dump("htcpBuildTstOpData: bad RR value");
438 htcpBuildClrOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
440 unsigned short reason
;
444 debugs(31, 3, "htcpBuildClrOpData: RR_REQUEST");
445 reason
= htons((unsigned short)stuff
->reason
);
446 memcpy(buf
, &reason
, 2);
447 return htcpBuildSpecifier(buf
+ 2, buflen
- 2, stuff
) + 2;
451 fatal_dump("htcpBuildClrOpData: bad RR value");
458 htcpBuildOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
461 debugs(31, 3, "htcpBuildOpData: opcode " << htcpOpcodeStr
[stuff
->op
]);
466 off
= htcpBuildTstOpData(buf
+ off
, buflen
, stuff
);
470 off
= htcpBuildClrOpData(buf
+ off
, buflen
, stuff
);
482 htcpBuildData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
486 size_t hdr_sz
= sizeof(htcpDataHeader
);
491 off
+= hdr_sz
; /* skip! */
493 op_data_sz
= htcpBuildOpData(buf
+ off
, buflen
- off
, stuff
);
500 debugs(31, 3, "htcpBuildData: hdr.length = " << off
);
502 if (!old_squid_format
) {
504 memset(&hdr
, 0, sizeof(hdr
));
505 /* convert multi-byte fields */
506 hdr
.msg_id
= htonl(stuff
->msg_id
);
507 hdr
.length
= htons(static_cast<uint16_t>(off
));
508 hdr
.opcode
= stuff
->op
;
509 hdr
.response
= stuff
->response
;
512 memcpy(buf
, &hdr
, hdr_sz
);
514 htcpDataHeaderSquid hdrSquid
;
515 memset(&hdrSquid
, 0, sizeof(hdrSquid
));
516 hdrSquid
.length
= htons(static_cast<uint16_t>(off
));
517 hdrSquid
.opcode
= stuff
->op
;
518 hdrSquid
.response
= stuff
->response
;
519 hdrSquid
.F1
= stuff
->f1
;
520 hdrSquid
.RR
= stuff
->rr
;
521 memcpy(buf
, &hdrSquid
, hdr_sz
);
524 debugs(31, 3, "htcpBuildData: size " << off
);
530 * Build an HTCP packet into buf, maximum length buflen.
531 * Returns the packet length, or zero on failure.
534 htcpBuildPacket(char *buf
, size_t buflen
, htcpStuff
* stuff
)
538 size_t hdr_sz
= sizeof(htcpHeader
);
540 /* skip the header -- we don't know the overall length */
542 if (buflen
< hdr_sz
) {
547 s
= htcpBuildData(buf
+ off
, buflen
- off
, stuff
);
554 s
= htcpBuildAuth(buf
+ off
, buflen
- off
);
561 hdr
.length
= htons((uint16_t) off
);
564 if (old_squid_format
)
569 memcpy(buf
, &hdr
, hdr_sz
);
571 debugs(31, 3, "htcpBuildPacket: size " << off
);
577 htcpSend(const char *buf
, int len
, Ip::Address
&to
)
579 debugs(31, 3, HERE
<< to
);
580 htcpHexdump("htcpSend", buf
, len
);
582 if (comm_udp_sendto(htcpOutgoingConn
->fd
, to
, buf
, len
) < 0)
583 debugs(31, 3, HERE
<< htcpOutgoingConn
<< " sendto: " << xstrerror());
585 ++statCounter
.htcp
.pkts_sent
;
589 * STUFF FOR RECEIVING HTCP MESSAGES
593 htcpSpecifier::setFrom(Ip::Address
&aSocket
)
599 htcpSpecifier::setDataHeader(htcpDataHeader
*aDataHeader
)
605 htcpFreeSpecifier(htcpSpecifier
* s
)
607 HTTPMSGUNLOCK(s
->request
);
613 * Unpack an HTCP SPECIFIER in place
614 * This will overwrite any following AUTH block
616 // XXX: this needs to be turned into an Htcp1::Parser inheriting from Http1::RequestParser
617 // but with different first-line and block unpacking logic.
618 static htcpSpecifier
*
619 htcpUnpackSpecifier(char *buf
, int sz
)
621 htcpSpecifier
*s
= new htcpSpecifier
;
622 HttpRequestMethod method
;
624 /* Find length of METHOD */
625 uint16_t l
= ntohs(*(uint16_t *) buf
);
630 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack METHOD");
631 htcpFreeSpecifier(s
);
639 debugs(31, 6, "htcpUnpackSpecifier: METHOD (" << l
<< "/" << sz
<< ") '" << s
->method
<< "'");
641 /* Find length of URI */
642 l
= ntohs(*(uint16_t *) buf
);
646 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack URI");
647 htcpFreeSpecifier(s
);
651 /* Add terminating null to METHOD */
659 debugs(31, 6, "htcpUnpackSpecifier: URI (" << l
<< "/" << sz
<< ") '" << s
->uri
<< "'");
661 /* Find length of VERSION */
662 l
= ntohs(*(uint16_t *) buf
);
666 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack VERSION");
667 htcpFreeSpecifier(s
);
671 /* Add terminating null to URI */
679 debugs(31, 6, "htcpUnpackSpecifier: VERSION (" << l
<< "/" << sz
<< ") '" << s
->version
<< "'");
681 /* Find length of REQ-HDRS */
682 l
= ntohs(*(uint16_t *) buf
);
686 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack REQ-HDRS");
687 htcpFreeSpecifier(s
);
691 /* Add terminating null to URI */
700 debugs(31, 6, "htcpUnpackSpecifier: REQ-HDRS (" << l
<< "/" << sz
<< ") '" << s
->req_hdrs
<< "'");
702 debugs(31, 3, "htcpUnpackSpecifier: " << sz
<< " bytes left");
705 * Add terminating null to REQ-HDRS. This is possible because we allocated
706 * an extra byte when we received the packet. This will overwrite any following
712 method
.HttpRequestMethodXXX(s
->method
);
714 s
->request
= HttpRequest::CreateFromUrlAndMethod(s
->uri
, method
== Http::METHOD_NONE
? HttpRequestMethod(Http::METHOD_GET
) : method
);
717 HTTPMSGLOCK(s
->request
);
723 * Unpack an HTCP DETAIL in place
724 * This will overwrite any following AUTH block
727 htcpUnpackDetail(char *buf
, int sz
)
729 htcpDetail
*d
= new htcpDetail
;
731 /* Find length of RESP-HDRS */
732 uint16_t l
= ntohs(*(uint16_t *) buf
);
737 debugs(31, 3, "htcpUnpackDetail: failed to unpack RESP_HDRS");
748 /* Find length of ENTITY-HDRS */
749 l
= ntohs(*(uint16_t *) buf
);
754 debugs(31, 3, "htcpUnpackDetail: failed to unpack ENTITY_HDRS");
759 /* Add terminating null to RESP-HDRS */
762 /* Set ENTITY-HDRS */
765 d
->entity_hdrs
= buf
;
770 /* Find length of CACHE-HDRS */
771 l
= ntohs(*(uint16_t *) buf
);
776 debugs(31, 3, "htcpUnpackDetail: failed to unpack CACHE_HDRS");
781 /* Add terminating null to ENTITY-HDRS */
792 debugs(31, 3, "htcpUnpackDetail: " << sz
<< " bytes left");
795 * Add terminating null to CACHE-HDRS. This is possible because we allocated
796 * an extra byte when we received the packet. This will overwrite any following
805 htcpAccessAllowed(acl_access
* acl
, htcpSpecifier
* s
, Ip::Address
&from
)
807 /* default deny if no access list present */
811 ACLFilledChecklist
checklist(acl
, s
->request
, NULL
);
812 checklist
.src_addr
= from
;
813 checklist
.my_addr
.setNoAddr();
814 return (checklist
.fastCheck() == ACCESS_ALLOWED
);
818 htcpTstReply(htcpDataHeader
* dhdr
, StoreEntry
* e
, htcpSpecifier
* spec
, Ip::Address
&from
)
820 static char pkt
[8192];
821 HttpHeader
hdr(hoHtcpReply
);
824 htcpStuff
stuff(dhdr
->msg_id
, HTCP_TST
, RR_RESPONSE
, 0);
825 stuff
.response
= e
? 0 : 1;
826 debugs(31, 3, "htcpTstReply: response = " << stuff
.response
);
829 stuff
.S
.method
= spec
->method
;
830 stuff
.S
.uri
= spec
->uri
;
831 stuff
.S
.version
= spec
->version
;
832 stuff
.S
.req_hdrs
= spec
->req_hdrs
;
833 stuff
.S
.reqHdrsSz
= spec
->reqHdrsSz
;
835 hdr
.putInt(Http::HdrType::AGE
, (e
->timestamp
<= squid_curtime
? (squid_curtime
- e
->timestamp
) : 0) );
837 hdr
.putInt(Http::HdrType::AGE
, 0);
841 stuff
.D
.resp_hdrs
= xstrdup(mb
.buf
);
842 stuff
.D
.respHdrsSz
= mb
.contentSize();
843 debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff
.D
.resp_hdrs
<< "}");
847 if (e
&& e
->expires
> -1)
848 hdr
.putTime(Http::HdrType::EXPIRES
, e
->expires
);
850 if (e
&& e
->lastmod
> -1)
851 hdr
.putTime(Http::HdrType::LAST_MODIFIED
, e
->lastmod
);
855 stuff
.D
.entity_hdrs
= xstrdup(mb
.buf
);
856 stuff
.D
.entityHdrsSz
= mb
.contentSize();
858 debugs(31, 3, "htcpTstReply: entity_hdrs = {" << stuff
.D
.entity_hdrs
<< "}");
864 if (char *host
= urlHostname(spec
->uri
)) {
868 netdbHostData(host
, &samp
, &rtt
, &hops
);
872 snprintf(cto_buf
, 128, "%s %d %f %d",
873 host
, samp
, 0.001 * rtt
, hops
);
874 hdr
.putExt("Cache-to-Origin", cto_buf
);
877 #endif /* USE_ICMP */
880 stuff
.D
.cache_hdrs
= xstrdup(mb
.buf
);
881 stuff
.D
.cacheHdrsSz
= mb
.contentSize();
882 debugs(31, 3, "htcpTstReply: cache_hdrs = {" << stuff
.D
.cache_hdrs
<< "}");
887 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
889 safe_free(stuff
.D
.resp_hdrs
);
890 stuff
.D
.respHdrsSz
= 0;
891 safe_free(stuff
.D
.entity_hdrs
);
892 stuff
.D
.entityHdrsSz
= 0;
893 safe_free(stuff
.D
.cache_hdrs
);
894 stuff
.D
.cacheHdrsSz
= 0;
897 debugs(31, 3, "htcpTstReply: htcpBuildPacket() failed");
901 htcpSend(pkt
, (int) pktlen
, from
);
906 htcpClrReply(htcpDataHeader
* dhdr
, int purgeSucceeded
, Ip::Address
&from
)
908 static char pkt
[8192];
911 /* If dhdr->F1 == 0, no response desired */
916 htcpStuff
stuff(dhdr
->msg_id
, HTCP_CLR
, RR_RESPONSE
, 0);
918 stuff
.response
= purgeSucceeded
? 0 : 2;
920 debugs(31, 3, "htcpClrReply: response = " << stuff
.response
);
922 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
925 debugs(31, 3, "htcpClrReply: htcpBuildPacket() failed");
929 htcpSend(pkt
, (int) pktlen
, from
);
933 htcpSpecifier::checkHit()
935 checkHitRequest
= request
;
937 if (NULL
== checkHitRequest
) {
938 debugs(31, 3, "htcpCheckHit: NO; failed to parse URL");
939 checkedHit(NullStoreEntry::getInstance());
943 if (!checkHitRequest
->header
.parse(req_hdrs
, reqHdrsSz
)) {
944 debugs(31, 3, "htcpCheckHit: NO; failed to parse request headers");
945 delete checkHitRequest
;
946 checkHitRequest
= NULL
;
947 checkedHit(NullStoreEntry::getInstance());
951 StoreEntry::getPublicByRequest(this, checkHitRequest
);
955 htcpSpecifier::created (StoreEntry
*e
)
957 StoreEntry
*hit
=NULL
;
961 debugs(31, 3, "htcpCheckHit: NO; public object not found");
962 } else if (!e
->validToSend()) {
963 debugs(31, 3, "htcpCheckHit: NO; entry not valid to send" );
964 } else if (refreshCheckHTCP(e
, checkHitRequest
)) {
965 debugs(31, 3, "htcpCheckHit: NO; cached response is stale");
967 debugs(31, 3, "htcpCheckHit: YES!?");
975 htcpClrStoreEntry(StoreEntry
* e
)
977 debugs(31, 4, "htcpClrStoreEntry: Clearing store for entry: " << e
->url() );
982 htcpClrStore(const htcpSpecifier
* s
)
984 HttpRequest
*request
= s
->request
;
985 StoreEntry
*e
= NULL
;
988 if (request
== NULL
) {
989 debugs(31, 3, "htcpClrStore: failed to parse URL");
993 /* Parse request headers */
994 if (!request
->header
.parse(s
->req_hdrs
, s
->reqHdrsSz
)) {
995 debugs(31, 2, "htcpClrStore: failed to parse request headers");
999 /* Lookup matching entries. This matches both GET and HEAD */
1000 while ((e
= storeGetPublicByRequest(request
)) != NULL
) {
1002 htcpClrStoreEntry(e
);
1008 debugs(31, 4, "htcpClrStore: Cleared " << released
<< " matching entries");
1011 debugs(31, 4, "htcpClrStore: No matching entry found");
1018 htcpHandleTst(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1020 debugs(31, 3, "htcpHandleTst: sz = " << sz
);
1022 if (hdr
->RR
== RR_REQUEST
)
1023 htcpHandleTstRequest(hdr
, buf
, sz
, from
);
1025 htcpHandleTstResponse(hdr
, buf
, sz
, from
);
1028 HtcpReplyData::HtcpReplyData() :
1029 hit(0), hdr(hoHtcpReply
), msg_id(0), version(0.0)
1031 memset(&cto
, 0, sizeof(cto
));
1036 htcpHandleTstResponse(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1038 HtcpReplyData htcpReply
;
1039 cache_key
*key
= NULL
;
1042 htcpDetail
*d
= NULL
;
1045 if (queried_id
[hdr
->msg_id
% N_QUERIED_KEYS
] != hdr
->msg_id
) {
1046 debugs(31, 2, "htcpHandleTstResponse: No matching query id '" <<
1047 hdr
->msg_id
<< "' (expected " <<
1048 queried_id
[hdr
->msg_id
% N_QUERIED_KEYS
] << ") from '" <<
1054 key
= queried_keys
[hdr
->msg_id
% N_QUERIED_KEYS
];
1057 debugs(31, 3, "htcpHandleTstResponse: No query key for response id '" << hdr
->msg_id
<< "' from '" << from
<< "'");
1061 peer
= &queried_addr
[hdr
->msg_id
% N_QUERIED_KEYS
];
1063 if ( *peer
!= from
|| peer
->port() != from
.port() ) {
1064 debugs(31, 3, "htcpHandleTstResponse: Unexpected response source " << from
);
1069 debugs(31, 2, "htcpHandleTstResponse: error condition, F1/MO == 1");
1073 htcpReply
.msg_id
= hdr
->msg_id
;
1074 debugs(31, 3, "htcpHandleTstResponse: msg_id = " << htcpReply
.msg_id
);
1075 htcpReply
.hit
= hdr
->response
? 0 : 1;
1078 debugs(31, 3, "htcpHandleTstResponse: MISS");
1080 debugs(31, 3, "htcpHandleTstResponse: HIT");
1081 d
= htcpUnpackDetail(buf
, sz
);
1084 debugs(31, 3, "htcpHandleTstResponse: bad DETAIL");
1088 if ((t
= d
->resp_hdrs
))
1089 htcpReply
.hdr
.parse(t
, d
->respHdrsSz
);
1091 if ((t
= d
->entity_hdrs
))
1092 htcpReply
.hdr
.parse(t
, d
->entityHdrsSz
);
1094 if ((t
= d
->cache_hdrs
))
1095 htcpReply
.hdr
.parse(t
, d
->cacheHdrsSz
);
1098 debugs(31, 3, "htcpHandleTstResponse: key (" << key
<< ") " << storeKeyText(key
));
1099 neighborsHtcpReply(key
, &htcpReply
, from
);
1100 htcpReply
.hdr
.clean();
1106 htcpHandleTstRequest(htcpDataHeader
* dhdr
, char *buf
, int sz
, Ip::Address
&from
)
1108 /* buf should be a SPECIFIER */
1112 debugs(31, 3, "htcpHandleTst: nothing to do");
1119 /* s is a new object */
1120 s
= htcpUnpackSpecifier(buf
, sz
);
1123 debugs(31, 3, "htcpHandleTstRequest: htcpUnpackSpecifier failed");
1124 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1128 s
->setDataHeader(dhdr
);
1132 debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
1133 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1134 htcpFreeSpecifier(s
);
1138 if (!htcpAccessAllowed(Config
.accessList
.htcp
, s
, from
)) {
1139 debugs(31, 3, "htcpHandleTstRequest: Access denied");
1140 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_DENIED
, s
->uri
);
1141 htcpFreeSpecifier(s
);
1145 debugs(31, 2, "HTCP TST request: " << s
->method
<< " " << s
->uri
<< " " << s
->version
);
1146 debugs(31, 2, "HTCP TST headers: " << s
->req_hdrs
);
1151 htcpSpecifier::checkedHit(StoreEntry
*e
)
1154 htcpTstReply(dhdr
, e
, this, from
); /* hit */
1155 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_HIT
, uri
);
1157 htcpTstReply(dhdr
, NULL
, NULL
, from
); /* cache miss */
1158 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_MISS
, uri
);
1161 htcpFreeSpecifier(this);
1165 htcpHandleClr(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1168 /* buf[0/1] is reserved and reason */
1169 int reason
= buf
[1] << 4;
1170 debugs(31, 2, "HTCP CLR reason: " << reason
);
1174 /* buf should be a SPECIFIER */
1177 debugs(31, 4, "htcpHandleClr: nothing to do");
1178 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1182 s
= htcpUnpackSpecifier(buf
, sz
);
1185 debugs(31, 3, "htcpHandleClr: htcpUnpackSpecifier failed");
1186 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1191 debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
1192 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1193 htcpFreeSpecifier(s
);
1197 if (!htcpAccessAllowed(Config
.accessList
.htcp_clr
, s
, from
)) {
1198 debugs(31, 3, "htcpHandleClr: Access denied");
1199 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_DENIED
, s
->uri
);
1200 htcpFreeSpecifier(s
);
1204 debugs(31, 2, "HTCP CLR request: " << s
->method
<< " " << s
->uri
<< " " << s
->version
);
1205 debugs(31, 2, "HTCP CLR headers: " << s
->req_hdrs
);
1207 /* Release objects from cache
1208 * analog to clientPurgeRequest in client_side.c
1211 switch (htcpClrStore(s
)) {
1214 htcpClrReply(hdr
, 1, from
); /* hit */
1215 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_HIT
, s
->uri
);
1219 htcpClrReply(hdr
, 0, from
); /* miss */
1220 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_MISS
, s
->uri
);
1227 htcpFreeSpecifier(s
);
1231 * Forward a CLR request to all peers who have requested that CLRs be
1232 * forwarded to them.
1235 htcpForwardClr(char *buf
, int sz
)
1239 for (p
= Config
.peers
; p
; p
= p
->next
) {
1240 if (!p
->options
.htcp
) {
1243 if (!p
->options
.htcp_forward_clr
) {
1247 htcpSend(buf
, sz
, p
->in_addr
);
1252 * Do the first pass of handling an HTCP message. This used to be two
1253 * separate functions, htcpHandle and htcpHandleData. They were merged to
1254 * allow for forwarding HTCP packets easily to other peers if desired.
1256 * This function now works out what type of message we have received and then
1257 * hands it off to other functions to break apart message-specific data.
1260 htcpHandleMsg(char *buf
, int sz
, Ip::Address
&from
)
1267 if (sz
< 0 || (size_t)sz
< sizeof(htcpHeader
)) {
1268 // These are highly likely to be attack packets. Should probably get a bigger warning.
1269 debugs(31, 2, "htcpHandle: msg size less than htcpHeader size from " << from
);
1273 htcpHexdump("htcpHandle", buf
, sz
);
1274 memcpy(&htcpHdr
, buf
, sizeof(htcpHeader
));
1275 htcpHdr
.length
= ntohs(htcpHdr
.length
);
1277 if (htcpHdr
.minor
== 0)
1278 old_squid_format
= 1;
1280 old_squid_format
= 0;
1282 debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr
.length
);
1283 debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr
.major
);
1284 debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr
.minor
);
1286 if (sz
!= htcpHdr
.length
) {
1287 debugs(31, 3, "htcpHandle: sz/" << sz
<< " != htcpHdr.length/" <<
1288 htcpHdr
.length
<< " from " << from
);
1293 if (htcpHdr
.major
!= 0) {
1294 debugs(31, 3, "htcpHandle: Unknown major version " << htcpHdr
.major
<< " from " << from
);
1299 hbuf
= buf
+ sizeof(htcpHeader
);
1300 hsz
= sz
- sizeof(htcpHeader
);
1302 if ((size_t)hsz
< sizeof(htcpDataHeader
)) {
1303 debugs(31, 3, "htcpHandleData: msg size less than htcpDataHeader size");
1307 if (!old_squid_format
) {
1308 memcpy(&hdr
, hbuf
, sizeof(hdr
));
1310 htcpDataHeaderSquid hdrSquid
;
1311 memcpy(&hdrSquid
, hbuf
, sizeof(hdrSquid
));
1312 hdr
.length
= hdrSquid
.length
;
1313 hdr
.opcode
= hdrSquid
.opcode
;
1314 hdr
.response
= hdrSquid
.response
;
1315 hdr
.F1
= hdrSquid
.F1
;
1316 hdr
.RR
= hdrSquid
.RR
;
1318 hdr
.msg_id
= hdrSquid
.msg_id
;
1321 hdr
.length
= ntohs(hdr
.length
);
1322 hdr
.msg_id
= ntohl(hdr
.msg_id
);
1323 debugs(31, 3, "htcpHandleData: hsz = " << hsz
);
1324 debugs(31, 3, "htcpHandleData: length = " << hdr
.length
);
1326 if (hdr
.opcode
>= HTCP_END
) {
1327 debugs(31, 3, "htcpHandleData: client " << from
<< ", opcode " << hdr
.opcode
<< " out of range");
1331 debugs(31, 3, "htcpHandleData: opcode = " << hdr
.opcode
<< " " << htcpOpcodeStr
[hdr
.opcode
]);
1332 debugs(31, 3, "htcpHandleData: response = " << hdr
.response
);
1333 debugs(31, 3, "htcpHandleData: F1 = " << hdr
.F1
);
1334 debugs(31, 3, "htcpHandleData: RR = " << hdr
.RR
);
1335 debugs(31, 3, "htcpHandleData: msg_id = " << hdr
.msg_id
);
1337 if (hsz
< hdr
.length
) {
1338 debugs(31, 3, "htcpHandleData: sz < hdr.length");
1343 * set sz = hdr.length so we ignore any AUTH fields following
1346 hsz
= (int) hdr
.length
;
1347 hbuf
+= sizeof(htcpDataHeader
);
1348 hsz
-= sizeof(htcpDataHeader
);
1349 debugs(31, 3, "htcpHandleData: hsz = " << hsz
);
1351 htcpHexdump("htcpHandleData", hbuf
, hsz
);
1353 switch (hdr
.opcode
) {
1355 debugs(31, 3, "HTCP NOP not implemented");
1358 htcpHandleTst(&hdr
, hbuf
, hsz
, from
);
1361 debugs(31, 3, "HTCP MON not implemented");
1364 debugs(31, 3, "HTCP SET not implemented");
1367 htcpHandleClr(&hdr
, hbuf
, hsz
, from
);
1368 htcpForwardClr(buf
, sz
);
1376 htcpRecv(int fd
, void *)
1378 static char buf
[8192];
1380 static Ip::Address from
;
1382 /* Receive up to 8191 bytes, leaving room for a null */
1384 len
= comm_udp_recvfrom(fd
, buf
, sizeof(buf
) - 1, 0, from
);
1386 debugs(31, 3, "htcpRecv: FD " << fd
<< ", " << len
<< " bytes from " << from
);
1389 ++statCounter
.htcp
.pkts_recv
;
1391 htcpHandleMsg(buf
, len
, from
);
1393 Comm::SetSelect(fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1397 * ======================================================================
1399 * ======================================================================
1405 if (Config
.Port
.htcp
<= 0) {
1406 debugs(31, DBG_IMPORTANT
, "HTCP Disabled.");
1410 htcpIncomingConn
= new Comm::Connection
;
1411 htcpIncomingConn
->local
= Config
.Addrs
.udp_incoming
;
1412 htcpIncomingConn
->local
.port(Config
.Port
.htcp
);
1414 if (!Ip::EnableIpv6
&& !htcpIncomingConn
->local
.setIPv4()) {
1415 debugs(31, DBG_CRITICAL
, "ERROR: IPv6 is disabled. " << htcpIncomingConn
->local
<< " is not an IPv4 address.");
1416 fatal("HTCP port cannot be opened.");
1418 /* split-stack for now requires default IPv4-only HTCP */
1419 if (Ip::EnableIpv6
&IPV6_SPECIAL_SPLITSTACK
&& htcpIncomingConn
->local
.isAnyAddr()) {
1420 htcpIncomingConn
->local
.setIPv4();
1423 AsyncCall::Pointer call
= asyncCall(31, 2,
1424 "htcpIncomingConnectionOpened",
1425 Comm::UdpOpenDialer(&htcpIncomingConnectionOpened
));
1427 Ipc::StartListening(SOCK_DGRAM
,
1430 Ipc::fdnInHtcpSocket
, call
);
1432 if (!Config
.Addrs
.udp_outgoing
.isNoAddr()) {
1433 htcpOutgoingConn
= new Comm::Connection
;
1434 htcpOutgoingConn
->local
= Config
.Addrs
.udp_outgoing
;
1435 htcpOutgoingConn
->local
.port(Config
.Port
.htcp
);
1437 if (!Ip::EnableIpv6
&& !htcpOutgoingConn
->local
.setIPv4()) {
1438 debugs(31, DBG_CRITICAL
, "ERROR: IPv6 is disabled. " << htcpOutgoingConn
->local
<< " is not an IPv4 address.");
1439 fatal("HTCP port cannot be opened.");
1441 /* split-stack for now requires default IPv4-only HTCP */
1442 if (Ip::EnableIpv6
&IPV6_SPECIAL_SPLITSTACK
&& htcpOutgoingConn
->local
.isAnyAddr()) {
1443 htcpOutgoingConn
->local
.setIPv4();
1447 comm_open_listener(SOCK_DGRAM
, IPPROTO_UDP
, htcpOutgoingConn
, "Outgoing HTCP Socket");
1450 if (!Comm::IsConnOpen(htcpOutgoingConn
))
1451 fatal("Cannot open Outgoing HTCP Socket");
1453 Comm::SetSelect(htcpOutgoingConn
->fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1455 debugs(31, DBG_IMPORTANT
, "Sending HTCP messages from " << htcpOutgoingConn
->local
);
1461 htcpIncomingConnectionOpened(const Comm::ConnectionPointer
&conn
, int)
1463 if (!Comm::IsConnOpen(conn
))
1464 fatal("Cannot open HTCP Socket");
1466 Comm::SetSelect(conn
->fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1468 debugs(31, DBG_CRITICAL
, "Accepting HTCP messages on " << conn
->local
);
1470 if (Config
.Addrs
.udp_outgoing
.isNoAddr()) {
1471 htcpOutgoingConn
= conn
;
1472 debugs(31, DBG_IMPORTANT
, "Sending HTCP messages from " << htcpOutgoingConn
->local
);
1477 htcpQuery(StoreEntry
* e
, HttpRequest
* req
, CachePeer
* p
)
1479 cache_key
*save_key
;
1480 static char pkt
[8192];
1483 HttpHeader
hdr(hoRequest
);
1484 HttpStateFlags flags
;
1486 if (!Comm::IsConnOpen(htcpIncomingConn
))
1489 old_squid_format
= p
->options
.htcp_oldsquid
;
1490 memset(&flags
, '\0', sizeof(flags
));
1491 snprintf(vbuf
, sizeof(vbuf
), "%d/%d",
1492 req
->http_ver
.major
, req
->http_ver
.minor
);
1494 htcpStuff
stuff(++msg_id_counter
, HTCP_TST
, RR_REQUEST
, 1);
1495 SBuf sb
= req
->method
.image();
1496 stuff
.S
.method
= sb
.c_str();
1497 stuff
.S
.uri
= (char *) e
->url();
1498 stuff
.S
.version
= vbuf
;
1499 HttpStateData::httpBuildRequestHeader(req
, e
, NULL
, &hdr
, flags
);
1504 stuff
.S
.req_hdrs
= mb
.buf
;
1505 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
1508 debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
1512 htcpSend(pkt
, (int) pktlen
, p
->in_addr
);
1514 queried_id
[stuff
.msg_id
% N_QUERIED_KEYS
] = stuff
.msg_id
;
1515 save_key
= queried_keys
[stuff
.msg_id
% N_QUERIED_KEYS
];
1516 storeKeyCopy(save_key
, (const cache_key
*)e
->key
);
1517 queried_addr
[stuff
.msg_id
% N_QUERIED_KEYS
] = p
->in_addr
;
1518 debugs(31, 3, "htcpQuery: key (" << save_key
<< ") " << storeKeyText(save_key
));
1524 * Send an HTCP CLR message for a specified item to a given CachePeer.
1527 htcpClear(StoreEntry
* e
, const char *uri
, HttpRequest
* req
, const HttpRequestMethod
&, CachePeer
* p
, htcp_clr_reason reason
)
1529 static char pkt
[8192];
1532 HttpHeader
hdr(hoRequest
);
1534 HttpStateFlags flags
;
1536 if (!Comm::IsConnOpen(htcpIncomingConn
))
1539 old_squid_format
= p
->options
.htcp_oldsquid
;
1540 memset(&flags
, '\0', sizeof(flags
));
1541 snprintf(vbuf
, sizeof(vbuf
), "%d/%d",
1542 req
->http_ver
.major
, req
->http_ver
.minor
);
1544 htcpStuff
stuff(++msg_id_counter
, HTCP_CLR
, RR_REQUEST
, 0);
1545 if (reason
== HTCP_CLR_INVALIDATION
)
1548 SBuf sb
= req
->method
.image();
1549 stuff
.S
.method
= sb
.c_str();
1550 if (e
== NULL
|| e
->mem_obj
== NULL
) {
1554 stuff
.S
.uri
= xstrdup(uri
);
1556 stuff
.S
.uri
= (char *) e
->url();
1558 stuff
.S
.version
= vbuf
;
1559 if (reason
!= HTCP_CLR_INVALIDATION
) {
1560 HttpStateData::httpBuildRequestHeader(req
, e
, NULL
, &hdr
, flags
);
1564 stuff
.S
.req_hdrs
= mb
.buf
;
1566 stuff
.S
.req_hdrs
= NULL
;
1568 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
1569 if (reason
!= HTCP_CLR_INVALIDATION
) {
1576 debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
1580 htcpSend(pkt
, (int) pktlen
, p
->in_addr
);
1584 * htcpSocketShutdown only closes the 'in' socket if it is
1585 * different than the 'out' socket.
1588 htcpSocketShutdown(void)
1590 if (!Comm::IsConnOpen(htcpIncomingConn
))
1593 debugs(12, DBG_IMPORTANT
, "Stop accepting HTCP on " << htcpIncomingConn
->local
);
1595 * Here we just unlink htcpIncomingConn because the HTCP 'in'
1596 * and 'out' sockets might be just one FD. This prevents this
1597 * function from executing repeatedly. When we are really ready to
1598 * exit or restart, main will comm_close the 'out' descriptor.
1600 htcpIncomingConn
= NULL
;
1603 * Normally we only write to the outgoing HTCP socket, but
1604 * we also have a read handler there to catch messages sent
1605 * to that specific interface. During shutdown, we must
1606 * disable reading on the outgoing socket.
1608 /* XXX Don't we need this handler to read replies while shutting down?
1609 * I think there should be a separate hander for reading replies..
1611 assert(Comm::IsConnOpen(htcpOutgoingConn
));
1613 Comm::SetSelect(htcpOutgoingConn
->fd
, COMM_SELECT_READ
, NULL
, NULL
, 0);
1617 htcpClosePorts(void)
1619 htcpSocketShutdown();
1621 if (htcpOutgoingConn
!= NULL
) {
1622 debugs(12, DBG_IMPORTANT
, "Stop sending HTCP from " << htcpOutgoingConn
->local
);
1623 htcpOutgoingConn
= NULL
;
1628 htcpLogHtcp(Ip::Address
&caddr
, int opcode
, LogTags logcode
, const char *url
)
1630 AccessLogEntry::Pointer al
= new AccessLogEntry
;
1631 if (LOG_TAG_NONE
== logcode
.oldType
)
1633 if (!Config
.onoff
.log_udp
)
1635 al
->htcp
.opcode
= htcpOpcodeStr
[opcode
];
1637 al
->cache
.caddr
= caddr
;
1638 al
->cache
.code
= logcode
;
1639 al
->cache
.trTime
.tv_sec
= 0;
1640 al
->cache
.trTime
.tv_usec
= 0;
1641 accessLogLog(al
, NULL
);