2 * Copyright (C) 1996-2015 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"
31 #include "SquidConfig.h"
32 #include "SquidTime.h"
33 #include "StatCounters.h"
35 #include "store_key_md5.h"
36 #include "StoreClient.h"
40 /** htcpDetail uses explicit alloc()/freeOne()
41 * XXX: convert to MEMPROXY_CLASS() API
45 typedef struct _Countstr Countstr
;
47 typedef struct _htcpHeader htcpHeader
;
49 typedef struct _htcpDataHeader htcpDataHeader
;
51 typedef struct _htcpDataHeaderSquid htcpDataHeaderSquid
;
53 typedef struct _htcpAuthHeader htcpAuthHeader
;
55 typedef struct _htcpDetail htcpDetail
;
68 struct _htcpDataHeaderSquid
{
72 unsigned int opcode
:4;
73 unsigned int response
:4;
75 unsigned int response
:4;
76 unsigned int opcode
:4;
80 unsigned int reserved
:6;
86 unsigned int reserved
:6;
92 struct _htcpDataHeader
{
116 /* RR == 0 --> F1 = RESPONSE DESIRED FLAG */
117 /* RR == 1 --> F1 = MESSAGE OVERALL FLAG */
118 /* RR == 0 --> REQUEST */
119 /* RR == 1 --> RESPONSE */
121 struct _htcpAuthHeader
{
129 class htcpSpecifier
: public StoreClient
131 MEMPROXY_CLASS(htcpSpecifier
);
141 checkHitRequest(NULL
),
146 void created (StoreEntry
*newEntry
);
148 void checkedHit(StoreEntry
*e
);
150 void setFrom(Ip::Address
&from
);
151 void setDataHeader(htcpDataHeader
*);
156 size_t reqHdrsSz
; ///< size of the req_hdrs content
157 HttpRequest
*request
;
160 HttpRequest
*checkHitRequest
;
162 Ip::Address from
; // was a ptr. return to such IFF needed. otherwise copy should do.
163 htcpDataHeader
*dhdr
;
180 htcpStuff(uint32_t id
, int o
, int r
, int f
) :
188 memset(&D
, 0, sizeof(D
));
210 static const char *const htcpOpcodeStr
[] = {
220 * values for htcpDataHeader->response
225 OPCODE_UNIMPLEMENTED
,
226 MAJOR_VERSION_UNSUPPORTED
,
227 MINOR_VERSION_UNSUPPORTED
,
232 * values for htcpDataHeader->RR
239 static void htcpIncomingConnectionOpened(const Comm::ConnectionPointer
&conn
, int errNo
);
240 static uint32_t msg_id_counter
= 0;
242 static Comm::ConnectionPointer htcpOutgoingConn
= NULL
;
243 static Comm::ConnectionPointer htcpIncomingConn
= NULL
;
244 #define N_QUERIED_KEYS 8192
245 static uint32_t queried_id
[N_QUERIED_KEYS
];
246 static cache_key queried_keys
[N_QUERIED_KEYS
][SQUID_MD5_DIGEST_LENGTH
];
248 static Ip::Address queried_addr
[N_QUERIED_KEYS
];
249 static MemAllocator
*htcpDetailPool
= NULL
;
251 static int old_squid_format
= 0;
253 static ssize_t
htcpBuildPacket(char *buf
, size_t buflen
, htcpStuff
* stuff
);
254 static htcpSpecifier
*htcpUnpackSpecifier(char *buf
, int sz
);
255 static htcpDetail
*htcpUnpackDetail(char *buf
, int sz
);
256 static ssize_t
htcpBuildAuth(char *buf
, size_t buflen
);
257 static ssize_t
htcpBuildCountstr(char *buf
, size_t buflen
, const char *s
, size_t len
);
258 static ssize_t
htcpBuildData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
259 static ssize_t
htcpBuildDetail(char *buf
, size_t buflen
, htcpStuff
* stuff
);
260 static ssize_t
htcpBuildOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
261 static ssize_t
htcpBuildSpecifier(char *buf
, size_t buflen
, htcpStuff
* stuff
);
262 static ssize_t
htcpBuildTstOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
263 static void htcpFreeSpecifier(htcpSpecifier
* s
);
264 static void htcpFreeDetail(htcpDetail
* s
);
266 static void htcpHandleMsg(char *buf
, int sz
, Ip::Address
&from
);
268 static void htcpLogHtcp(Ip::Address
&, int, LogTags
, const char *);
269 static void htcpHandleTst(htcpDataHeader
*, char *buf
, int sz
, Ip::Address
&from
);
271 static void htcpRecv(int fd
, void *data
);
273 static void htcpSend(const char *buf
, int len
, Ip::Address
&to
);
275 static void htcpTstReply(htcpDataHeader
*, StoreEntry
*, htcpSpecifier
*, Ip::Address
&);
277 static void htcpHandleTstRequest(htcpDataHeader
*, char *buf
, int sz
, Ip::Address
&from
);
279 static void htcpHandleTstResponse(htcpDataHeader
*, char *, int, Ip::Address
&);
282 htcpHexdump(const char *tag
, const char *s
, int sz
)
286 debugs(31, 3, "htcpHexdump " << tag
);
287 memset(hex
, '\0', sizeof(hex
));
289 for (int i
= 0; i
< sz
; ++i
) {
291 snprintf(&hex
[k
* 3], 4, " %02x", (int) *(s
+ i
));
293 if (k
< 15 && i
< (sz
- 1))
296 debugs(31, 3, "\t" << hex
);
298 memset(hex
, '\0', sizeof(hex
));
304 * STUFF FOR SENDING HTCP MESSAGES
308 htcpBuildAuth(char *buf
, size_t buflen
)
312 assert(2 == sizeof(uint16_t));
313 auth
.length
= htons(2);
315 if (buflen
< copy_sz
)
317 memcpy(buf
, &auth
, copy_sz
);
322 htcpBuildCountstr(char *buf
, size_t buflen
, const char *s
, size_t len
)
326 if (buflen
- off
< 2)
329 debugs(31, 3, "htcpBuildCountstr: LENGTH = " << len
);
331 debugs(31, 3, "htcpBuildCountstr: TEXT = {" << (s
? s
: "<NULL>") << "}");
333 uint16_t length
= htons((uint16_t) len
);
335 memcpy(buf
+ off
, &length
, 2);
339 if (buflen
- off
< len
)
343 memcpy(buf
+ off
, s
, len
);
351 htcpBuildSpecifier(char *buf
, size_t buflen
, htcpStuff
* stuff
)
355 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.method
, (stuff
->S
.method
?strlen(stuff
->S
.method
):0));
362 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.uri
, (stuff
->S
.uri
?strlen(stuff
->S
.uri
):0));
369 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.version
, (stuff
->S
.version
?strlen(stuff
->S
.version
):0));
376 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.req_hdrs
, stuff
->S
.reqHdrsSz
);
383 debugs(31, 3, "htcpBuildSpecifier: size " << off
);
389 htcpBuildDetail(char *buf
, size_t buflen
, htcpStuff
* stuff
)
393 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.resp_hdrs
, stuff
->D
.respHdrsSz
);
400 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.entity_hdrs
, stuff
->D
.entityHdrsSz
);
407 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.cache_hdrs
, stuff
->D
.cacheHdrsSz
);
418 htcpBuildTstOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
423 debugs(31, 3, "htcpBuildTstOpData: RR_REQUEST");
424 return htcpBuildSpecifier(buf
, buflen
, stuff
);
427 debugs(31, 3, "htcpBuildTstOpData: RR_RESPONSE");
428 debugs(31, 3, "htcpBuildTstOpData: F1 = " << stuff
->f1
);
430 if (stuff
->f1
) /* cache miss */
433 return htcpBuildDetail(buf
, buflen
, stuff
);
436 fatal_dump("htcpBuildTstOpData: bad RR value");
443 htcpBuildClrOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
445 unsigned short reason
;
449 debugs(31, 3, "htcpBuildClrOpData: RR_REQUEST");
450 reason
= htons((unsigned short)stuff
->reason
);
451 memcpy(buf
, &reason
, 2);
452 return htcpBuildSpecifier(buf
+ 2, buflen
- 2, stuff
) + 2;
456 fatal_dump("htcpBuildClrOpData: bad RR value");
463 htcpBuildOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
466 debugs(31, 3, "htcpBuildOpData: opcode " << htcpOpcodeStr
[stuff
->op
]);
471 off
= htcpBuildTstOpData(buf
+ off
, buflen
, stuff
);
475 off
= htcpBuildClrOpData(buf
+ off
, buflen
, stuff
);
487 htcpBuildData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
491 size_t hdr_sz
= sizeof(htcpDataHeader
);
496 off
+= hdr_sz
; /* skip! */
498 op_data_sz
= htcpBuildOpData(buf
+ off
, buflen
- off
, stuff
);
505 debugs(31, 3, "htcpBuildData: hdr.length = " << off
);
507 if (!old_squid_format
) {
509 memset(&hdr
, 0, sizeof(hdr
));
510 /* convert multi-byte fields */
511 hdr
.msg_id
= htonl(stuff
->msg_id
);
512 hdr
.length
= htons(static_cast<uint16_t>(off
));
513 hdr
.opcode
= stuff
->op
;
514 hdr
.response
= stuff
->response
;
517 memcpy(buf
, &hdr
, hdr_sz
);
519 htcpDataHeaderSquid hdrSquid
;
520 memset(&hdrSquid
, 0, sizeof(hdrSquid
));
521 hdrSquid
.length
= htons(static_cast<uint16_t>(off
));
522 hdrSquid
.opcode
= stuff
->op
;
523 hdrSquid
.response
= stuff
->response
;
524 hdrSquid
.F1
= stuff
->f1
;
525 hdrSquid
.RR
= stuff
->rr
;
526 memcpy(buf
, &hdrSquid
, hdr_sz
);
529 debugs(31, 3, "htcpBuildData: size " << off
);
535 * Build an HTCP packet into buf, maximum length buflen.
536 * Returns the packet length, or zero on failure.
539 htcpBuildPacket(char *buf
, size_t buflen
, htcpStuff
* stuff
)
543 size_t hdr_sz
= sizeof(htcpHeader
);
545 /* skip the header -- we don't know the overall length */
547 if (buflen
< hdr_sz
) {
552 s
= htcpBuildData(buf
+ off
, buflen
- off
, stuff
);
559 s
= htcpBuildAuth(buf
+ off
, buflen
- off
);
566 hdr
.length
= htons((uint16_t) off
);
569 if (old_squid_format
)
574 memcpy(buf
, &hdr
, hdr_sz
);
576 debugs(31, 3, "htcpBuildPacket: size " << off
);
582 htcpSend(const char *buf
, int len
, Ip::Address
&to
)
584 debugs(31, 3, HERE
<< to
);
585 htcpHexdump("htcpSend", buf
, len
);
587 if (comm_udp_sendto(htcpOutgoingConn
->fd
, to
, buf
, len
) < 0)
588 debugs(31, 3, HERE
<< htcpOutgoingConn
<< " sendto: " << xstrerror());
590 ++statCounter
.htcp
.pkts_sent
;
594 * STUFF FOR RECEIVING HTCP MESSAGES
598 htcpSpecifier::setFrom(Ip::Address
&aSocket
)
604 htcpSpecifier::setDataHeader(htcpDataHeader
*aDataHeader
)
610 htcpFreeSpecifier(htcpSpecifier
* s
)
612 HTTPMSGUNLOCK(s
->request
);
618 htcpFreeDetail(htcpDetail
* d
)
620 htcpDetailPool
->freeOne(d
);
624 * Unpack an HTCP SPECIFIER in place
625 * This will overwrite any following AUTH block
627 // XXX: this needs to be turned into an Htcp1::Parser inheriting from Http1::RequestParser
628 // but with different first-line and block unpacking logic.
629 static htcpSpecifier
*
630 htcpUnpackSpecifier(char *buf
, int sz
)
632 htcpSpecifier
*s
= new htcpSpecifier
;
633 HttpRequestMethod method
;
635 /* Find length of METHOD */
636 uint16_t l
= ntohs(*(uint16_t *) buf
);
641 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack METHOD");
642 htcpFreeSpecifier(s
);
650 debugs(31, 6, "htcpUnpackSpecifier: METHOD (" << l
<< "/" << sz
<< ") '" << s
->method
<< "'");
652 /* Find length of URI */
653 l
= ntohs(*(uint16_t *) buf
);
657 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack URI");
658 htcpFreeSpecifier(s
);
662 /* Add terminating null to METHOD */
670 debugs(31, 6, "htcpUnpackSpecifier: URI (" << l
<< "/" << sz
<< ") '" << s
->uri
<< "'");
672 /* Find length of VERSION */
673 l
= ntohs(*(uint16_t *) buf
);
677 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack VERSION");
678 htcpFreeSpecifier(s
);
682 /* Add terminating null to URI */
690 debugs(31, 6, "htcpUnpackSpecifier: VERSION (" << l
<< "/" << sz
<< ") '" << s
->version
<< "'");
692 /* Find length of REQ-HDRS */
693 l
= ntohs(*(uint16_t *) buf
);
697 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack REQ-HDRS");
698 htcpFreeSpecifier(s
);
702 /* Add terminating null to URI */
711 debugs(31, 6, "htcpUnpackSpecifier: REQ-HDRS (" << l
<< "/" << sz
<< ") '" << s
->req_hdrs
<< "'");
713 debugs(31, 3, "htcpUnpackSpecifier: " << sz
<< " bytes left");
716 * Add terminating null to REQ-HDRS. This is possible because we allocated
717 * an extra byte when we received the packet. This will overwrite any following
723 method
.HttpRequestMethodXXX(s
->method
);
725 s
->request
= HttpRequest::CreateFromUrlAndMethod(s
->uri
, method
== Http::METHOD_NONE
? HttpRequestMethod(Http::METHOD_GET
) : method
);
728 HTTPMSGLOCK(s
->request
);
734 * Unpack an HTCP DETAIL in place
735 * This will overwrite any following AUTH block
738 htcpUnpackDetail(char *buf
, int sz
)
740 htcpDetail
*d
= static_cast<htcpDetail
*>(htcpDetailPool
->alloc());
742 /* Find length of RESP-HDRS */
743 uint16_t l
= ntohs(*(uint16_t *) buf
);
748 debugs(31, 3, "htcpUnpackDetail: failed to unpack RESP_HDRS");
759 /* Find length of ENTITY-HDRS */
760 l
= ntohs(*(uint16_t *) buf
);
765 debugs(31, 3, "htcpUnpackDetail: failed to unpack ENTITY_HDRS");
770 /* Add terminating null to RESP-HDRS */
773 /* Set ENTITY-HDRS */
776 d
->entity_hdrs
= buf
;
781 /* Find length of CACHE-HDRS */
782 l
= ntohs(*(uint16_t *) buf
);
787 debugs(31, 3, "htcpUnpackDetail: failed to unpack CACHE_HDRS");
792 /* Add terminating null to ENTITY-HDRS */
803 debugs(31, 3, "htcpUnpackDetail: " << sz
<< " bytes left");
806 * Add terminating null to CACHE-HDRS. This is possible because we allocated
807 * an extra byte when we received the packet. This will overwrite any following
816 htcpAccessAllowed(acl_access
* acl
, htcpSpecifier
* s
, Ip::Address
&from
)
818 /* default deny if no access list present */
822 ACLFilledChecklist
checklist(acl
, s
->request
, NULL
);
823 checklist
.src_addr
= from
;
824 checklist
.my_addr
.setNoAddr();
825 return (checklist
.fastCheck() == ACCESS_ALLOWED
);
829 htcpTstReply(htcpDataHeader
* dhdr
, StoreEntry
* e
, htcpSpecifier
* spec
, Ip::Address
&from
)
831 static char pkt
[8192];
832 HttpHeader
hdr(hoHtcpReply
);
835 htcpStuff
stuff(dhdr
->msg_id
, HTCP_TST
, RR_RESPONSE
, 0);
836 stuff
.response
= e
? 0 : 1;
837 debugs(31, 3, "htcpTstReply: response = " << stuff
.response
);
840 stuff
.S
.method
= spec
->method
;
841 stuff
.S
.uri
= spec
->uri
;
842 stuff
.S
.version
= spec
->version
;
843 stuff
.S
.req_hdrs
= spec
->req_hdrs
;
844 stuff
.S
.reqHdrsSz
= spec
->reqHdrsSz
;
846 hdr
.putInt(Http::HdrType::AGE
, (e
->timestamp
<= squid_curtime
? (squid_curtime
- e
->timestamp
) : 0) );
848 hdr
.putInt(Http::HdrType::AGE
, 0);
852 stuff
.D
.resp_hdrs
= xstrdup(mb
.buf
);
853 stuff
.D
.respHdrsSz
= mb
.contentSize();
854 debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff
.D
.resp_hdrs
<< "}");
858 if (e
&& e
->expires
> -1)
859 hdr
.putTime(Http::HdrType::EXPIRES
, e
->expires
);
861 if (e
&& e
->lastmod
> -1)
862 hdr
.putTime(Http::HdrType::LAST_MODIFIED
, e
->lastmod
);
866 stuff
.D
.entity_hdrs
= xstrdup(mb
.buf
);
867 stuff
.D
.entityHdrsSz
= mb
.contentSize();
869 debugs(31, 3, "htcpTstReply: entity_hdrs = {" << stuff
.D
.entity_hdrs
<< "}");
875 if (char *host
= urlHostname(spec
->uri
)) {
879 netdbHostData(host
, &samp
, &rtt
, &hops
);
883 snprintf(cto_buf
, 128, "%s %d %f %d",
884 host
, samp
, 0.001 * rtt
, hops
);
885 hdr
.putExt("Cache-to-Origin", cto_buf
);
888 #endif /* USE_ICMP */
891 stuff
.D
.cache_hdrs
= xstrdup(mb
.buf
);
892 stuff
.D
.cacheHdrsSz
= mb
.contentSize();
893 debugs(31, 3, "htcpTstReply: cache_hdrs = {" << stuff
.D
.cache_hdrs
<< "}");
898 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
900 safe_free(stuff
.D
.resp_hdrs
);
901 stuff
.D
.respHdrsSz
= 0;
902 safe_free(stuff
.D
.entity_hdrs
);
903 stuff
.D
.entityHdrsSz
= 0;
904 safe_free(stuff
.D
.cache_hdrs
);
905 stuff
.D
.cacheHdrsSz
= 0;
908 debugs(31, 3, "htcpTstReply: htcpBuildPacket() failed");
912 htcpSend(pkt
, (int) pktlen
, from
);
917 htcpClrReply(htcpDataHeader
* dhdr
, int purgeSucceeded
, Ip::Address
&from
)
919 static char pkt
[8192];
922 /* If dhdr->F1 == 0, no response desired */
927 htcpStuff
stuff(dhdr
->msg_id
, HTCP_CLR
, RR_RESPONSE
, 0);
929 stuff
.response
= purgeSucceeded
? 0 : 2;
931 debugs(31, 3, "htcpClrReply: response = " << stuff
.response
);
933 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
936 debugs(31, 3, "htcpClrReply: htcpBuildPacket() failed");
940 htcpSend(pkt
, (int) pktlen
, from
);
944 htcpSpecifier::checkHit()
946 checkHitRequest
= request
;
948 if (NULL
== checkHitRequest
) {
949 debugs(31, 3, "htcpCheckHit: NO; failed to parse URL");
950 checkedHit(NullStoreEntry::getInstance());
954 if (!checkHitRequest
->header
.parse(req_hdrs
, reqHdrsSz
)) {
955 debugs(31, 3, "htcpCheckHit: NO; failed to parse request headers");
956 delete checkHitRequest
;
957 checkHitRequest
= NULL
;
958 checkedHit(NullStoreEntry::getInstance());
962 StoreEntry::getPublicByRequest(this, checkHitRequest
);
966 htcpSpecifier::created (StoreEntry
*e
)
968 StoreEntry
*hit
=NULL
;
972 debugs(31, 3, "htcpCheckHit: NO; public object not found");
973 } else if (!e
->validToSend()) {
974 debugs(31, 3, "htcpCheckHit: NO; entry not valid to send" );
975 } else if (refreshCheckHTCP(e
, checkHitRequest
)) {
976 debugs(31, 3, "htcpCheckHit: NO; cached response is stale");
978 debugs(31, 3, "htcpCheckHit: YES!?");
986 htcpClrStoreEntry(StoreEntry
* e
)
988 debugs(31, 4, "htcpClrStoreEntry: Clearing store for entry: " << e
->url() );
993 htcpClrStore(const htcpSpecifier
* s
)
995 HttpRequest
*request
= s
->request
;
996 StoreEntry
*e
= NULL
;
999 if (request
== NULL
) {
1000 debugs(31, 3, "htcpClrStore: failed to parse URL");
1004 /* Parse request headers */
1005 if (!request
->header
.parse(s
->req_hdrs
, s
->reqHdrsSz
)) {
1006 debugs(31, 2, "htcpClrStore: failed to parse request headers");
1010 /* Lookup matching entries. This matches both GET and HEAD */
1011 while ((e
= storeGetPublicByRequest(request
)) != NULL
) {
1013 htcpClrStoreEntry(e
);
1019 debugs(31, 4, "htcpClrStore: Cleared " << released
<< " matching entries");
1022 debugs(31, 4, "htcpClrStore: No matching entry found");
1029 htcpHandleTst(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1031 debugs(31, 3, "htcpHandleTst: sz = " << sz
);
1033 if (hdr
->RR
== RR_REQUEST
)
1034 htcpHandleTstRequest(hdr
, buf
, sz
, from
);
1036 htcpHandleTstResponse(hdr
, buf
, sz
, from
);
1039 HtcpReplyData::HtcpReplyData() :
1040 hit(0), hdr(hoHtcpReply
), msg_id(0), version(0.0)
1042 memset(&cto
, 0, sizeof(cto
));
1047 htcpHandleTstResponse(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1049 HtcpReplyData htcpReply
;
1050 cache_key
*key
= NULL
;
1053 htcpDetail
*d
= NULL
;
1056 if (queried_id
[hdr
->msg_id
% N_QUERIED_KEYS
] != hdr
->msg_id
) {
1057 debugs(31, 2, "htcpHandleTstResponse: No matching query id '" <<
1058 hdr
->msg_id
<< "' (expected " <<
1059 queried_id
[hdr
->msg_id
% N_QUERIED_KEYS
] << ") from '" <<
1065 key
= queried_keys
[hdr
->msg_id
% N_QUERIED_KEYS
];
1068 debugs(31, 3, "htcpHandleTstResponse: No query key for response id '" << hdr
->msg_id
<< "' from '" << from
<< "'");
1072 peer
= &queried_addr
[hdr
->msg_id
% N_QUERIED_KEYS
];
1074 if ( *peer
!= from
|| peer
->port() != from
.port() ) {
1075 debugs(31, 3, "htcpHandleTstResponse: Unexpected response source " << from
);
1080 debugs(31, 2, "htcpHandleTstResponse: error condition, F1/MO == 1");
1084 htcpReply
.msg_id
= hdr
->msg_id
;
1085 debugs(31, 3, "htcpHandleTstResponse: msg_id = " << htcpReply
.msg_id
);
1086 htcpReply
.hit
= hdr
->response
? 0 : 1;
1089 debugs(31, 3, "htcpHandleTstResponse: MISS");
1091 debugs(31, 3, "htcpHandleTstResponse: HIT");
1092 d
= htcpUnpackDetail(buf
, sz
);
1095 debugs(31, 3, "htcpHandleTstResponse: bad DETAIL");
1099 if ((t
= d
->resp_hdrs
))
1100 htcpReply
.hdr
.parse(t
, d
->respHdrsSz
);
1102 if ((t
= d
->entity_hdrs
))
1103 htcpReply
.hdr
.parse(t
, d
->entityHdrsSz
);
1105 if ((t
= d
->cache_hdrs
))
1106 htcpReply
.hdr
.parse(t
, d
->cacheHdrsSz
);
1109 debugs(31, 3, "htcpHandleTstResponse: key (" << key
<< ") " << storeKeyText(key
));
1110 neighborsHtcpReply(key
, &htcpReply
, from
);
1111 htcpReply
.hdr
.clean();
1118 htcpHandleTstRequest(htcpDataHeader
* dhdr
, char *buf
, int sz
, Ip::Address
&from
)
1120 /* buf should be a SPECIFIER */
1124 debugs(31, 3, "htcpHandleTst: nothing to do");
1131 /* s is a new object */
1132 s
= htcpUnpackSpecifier(buf
, sz
);
1135 debugs(31, 3, "htcpHandleTstRequest: htcpUnpackSpecifier failed");
1136 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1140 s
->setDataHeader(dhdr
);
1144 debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
1145 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1146 htcpFreeSpecifier(s
);
1150 if (!htcpAccessAllowed(Config
.accessList
.htcp
, s
, from
)) {
1151 debugs(31, 3, "htcpHandleTstRequest: Access denied");
1152 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_DENIED
, s
->uri
);
1153 htcpFreeSpecifier(s
);
1157 debugs(31, 2, "HTCP TST request: " << s
->method
<< " " << s
->uri
<< " " << s
->version
);
1158 debugs(31, 2, "HTCP TST headers: " << s
->req_hdrs
);
1163 htcpSpecifier::checkedHit(StoreEntry
*e
)
1166 htcpTstReply(dhdr
, e
, this, from
); /* hit */
1167 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_HIT
, uri
);
1169 htcpTstReply(dhdr
, NULL
, NULL
, from
); /* cache miss */
1170 htcpLogHtcp(from
, dhdr
->opcode
, LOG_UDP_MISS
, uri
);
1173 htcpFreeSpecifier(this);
1177 htcpHandleClr(htcpDataHeader
* hdr
, char *buf
, int sz
, Ip::Address
&from
)
1180 /* buf[0/1] is reserved and reason */
1181 int reason
= buf
[1] << 4;
1182 debugs(31, 2, "HTCP CLR reason: " << reason
);
1186 /* buf should be a SPECIFIER */
1189 debugs(31, 4, "htcpHandleClr: nothing to do");
1190 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1194 s
= htcpUnpackSpecifier(buf
, sz
);
1197 debugs(31, 3, "htcpHandleClr: htcpUnpackSpecifier failed");
1198 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1203 debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
1204 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_INVALID
, dash_str
);
1205 htcpFreeSpecifier(s
);
1209 if (!htcpAccessAllowed(Config
.accessList
.htcp_clr
, s
, from
)) {
1210 debugs(31, 3, "htcpHandleClr: Access denied");
1211 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_DENIED
, s
->uri
);
1212 htcpFreeSpecifier(s
);
1216 debugs(31, 2, "HTCP CLR request: " << s
->method
<< " " << s
->uri
<< " " << s
->version
);
1217 debugs(31, 2, "HTCP CLR headers: " << s
->req_hdrs
);
1219 /* Release objects from cache
1220 * analog to clientPurgeRequest in client_side.c
1223 switch (htcpClrStore(s
)) {
1226 htcpClrReply(hdr
, 1, from
); /* hit */
1227 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_HIT
, s
->uri
);
1231 htcpClrReply(hdr
, 0, from
); /* miss */
1232 htcpLogHtcp(from
, hdr
->opcode
, LOG_UDP_MISS
, s
->uri
);
1239 htcpFreeSpecifier(s
);
1243 * Forward a CLR request to all peers who have requested that CLRs be
1244 * forwarded to them.
1247 htcpForwardClr(char *buf
, int sz
)
1251 for (p
= Config
.peers
; p
; p
= p
->next
) {
1252 if (!p
->options
.htcp
) {
1255 if (!p
->options
.htcp_forward_clr
) {
1259 htcpSend(buf
, sz
, p
->in_addr
);
1264 * Do the first pass of handling an HTCP message. This used to be two
1265 * separate functions, htcpHandle and htcpHandleData. They were merged to
1266 * allow for forwarding HTCP packets easily to other peers if desired.
1268 * This function now works out what type of message we have received and then
1269 * hands it off to other functions to break apart message-specific data.
1272 htcpHandleMsg(char *buf
, int sz
, Ip::Address
&from
)
1279 if (sz
< 0 || (size_t)sz
< sizeof(htcpHeader
)) {
1280 // These are highly likely to be attack packets. Should probably get a bigger warning.
1281 debugs(31, 2, "htcpHandle: msg size less than htcpHeader size from " << from
);
1285 htcpHexdump("htcpHandle", buf
, sz
);
1286 memcpy(&htcpHdr
, buf
, sizeof(htcpHeader
));
1287 htcpHdr
.length
= ntohs(htcpHdr
.length
);
1289 if (htcpHdr
.minor
== 0)
1290 old_squid_format
= 1;
1292 old_squid_format
= 0;
1294 debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr
.length
);
1295 debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr
.major
);
1296 debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr
.minor
);
1298 if (sz
!= htcpHdr
.length
) {
1299 debugs(31, 3, "htcpHandle: sz/" << sz
<< " != htcpHdr.length/" <<
1300 htcpHdr
.length
<< " from " << from
);
1305 if (htcpHdr
.major
!= 0) {
1306 debugs(31, 3, "htcpHandle: Unknown major version " << htcpHdr
.major
<< " from " << from
);
1311 hbuf
= buf
+ sizeof(htcpHeader
);
1312 hsz
= sz
- sizeof(htcpHeader
);
1314 if ((size_t)hsz
< sizeof(htcpDataHeader
)) {
1315 debugs(31, 3, "htcpHandleData: msg size less than htcpDataHeader size");
1319 if (!old_squid_format
) {
1320 memcpy(&hdr
, hbuf
, sizeof(hdr
));
1322 htcpDataHeaderSquid hdrSquid
;
1323 memcpy(&hdrSquid
, hbuf
, sizeof(hdrSquid
));
1324 hdr
.length
= hdrSquid
.length
;
1325 hdr
.opcode
= hdrSquid
.opcode
;
1326 hdr
.response
= hdrSquid
.response
;
1327 hdr
.F1
= hdrSquid
.F1
;
1328 hdr
.RR
= hdrSquid
.RR
;
1330 hdr
.msg_id
= hdrSquid
.msg_id
;
1333 hdr
.length
= ntohs(hdr
.length
);
1334 hdr
.msg_id
= ntohl(hdr
.msg_id
);
1335 debugs(31, 3, "htcpHandleData: hsz = " << hsz
);
1336 debugs(31, 3, "htcpHandleData: length = " << hdr
.length
);
1338 if (hdr
.opcode
>= HTCP_END
) {
1339 debugs(31, 3, "htcpHandleData: client " << from
<< ", opcode " << hdr
.opcode
<< " out of range");
1343 debugs(31, 3, "htcpHandleData: opcode = " << hdr
.opcode
<< " " << htcpOpcodeStr
[hdr
.opcode
]);
1344 debugs(31, 3, "htcpHandleData: response = " << hdr
.response
);
1345 debugs(31, 3, "htcpHandleData: F1 = " << hdr
.F1
);
1346 debugs(31, 3, "htcpHandleData: RR = " << hdr
.RR
);
1347 debugs(31, 3, "htcpHandleData: msg_id = " << hdr
.msg_id
);
1349 if (hsz
< hdr
.length
) {
1350 debugs(31, 3, "htcpHandleData: sz < hdr.length");
1355 * set sz = hdr.length so we ignore any AUTH fields following
1358 hsz
= (int) hdr
.length
;
1359 hbuf
+= sizeof(htcpDataHeader
);
1360 hsz
-= sizeof(htcpDataHeader
);
1361 debugs(31, 3, "htcpHandleData: hsz = " << hsz
);
1363 htcpHexdump("htcpHandleData", hbuf
, hsz
);
1365 switch (hdr
.opcode
) {
1367 debugs(31, 3, "HTCP NOP not implemented");
1370 htcpHandleTst(&hdr
, hbuf
, hsz
, from
);
1373 debugs(31, 3, "HTCP MON not implemented");
1376 debugs(31, 3, "HTCP SET not implemented");
1379 htcpHandleClr(&hdr
, hbuf
, hsz
, from
);
1380 htcpForwardClr(buf
, sz
);
1388 htcpRecv(int fd
, void *)
1390 static char buf
[8192];
1392 static Ip::Address from
;
1394 /* Receive up to 8191 bytes, leaving room for a null */
1396 len
= comm_udp_recvfrom(fd
, buf
, sizeof(buf
) - 1, 0, from
);
1398 debugs(31, 3, "htcpRecv: FD " << fd
<< ", " << len
<< " bytes from " << from
);
1401 ++statCounter
.htcp
.pkts_recv
;
1403 htcpHandleMsg(buf
, len
, from
);
1405 Comm::SetSelect(fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1409 * ======================================================================
1411 * ======================================================================
1417 if (Config
.Port
.htcp
<= 0) {
1418 debugs(31, DBG_IMPORTANT
, "HTCP Disabled.");
1422 htcpIncomingConn
= new Comm::Connection
;
1423 htcpIncomingConn
->local
= Config
.Addrs
.udp_incoming
;
1424 htcpIncomingConn
->local
.port(Config
.Port
.htcp
);
1426 if (!Ip::EnableIpv6
&& !htcpIncomingConn
->local
.setIPv4()) {
1427 debugs(31, DBG_CRITICAL
, "ERROR: IPv6 is disabled. " << htcpIncomingConn
->local
<< " is not an IPv4 address.");
1428 fatal("HTCP port cannot be opened.");
1430 /* split-stack for now requires default IPv4-only HTCP */
1431 if (Ip::EnableIpv6
&IPV6_SPECIAL_SPLITSTACK
&& htcpIncomingConn
->local
.isAnyAddr()) {
1432 htcpIncomingConn
->local
.setIPv4();
1435 AsyncCall::Pointer call
= asyncCall(31, 2,
1436 "htcpIncomingConnectionOpened",
1437 Comm::UdpOpenDialer(&htcpIncomingConnectionOpened
));
1439 Ipc::StartListening(SOCK_DGRAM
,
1442 Ipc::fdnInHtcpSocket
, call
);
1444 if (!Config
.Addrs
.udp_outgoing
.isNoAddr()) {
1445 htcpOutgoingConn
= new Comm::Connection
;
1446 htcpOutgoingConn
->local
= Config
.Addrs
.udp_outgoing
;
1447 htcpOutgoingConn
->local
.port(Config
.Port
.htcp
);
1449 if (!Ip::EnableIpv6
&& !htcpOutgoingConn
->local
.setIPv4()) {
1450 debugs(31, DBG_CRITICAL
, "ERROR: IPv6 is disabled. " << htcpOutgoingConn
->local
<< " is not an IPv4 address.");
1451 fatal("HTCP port cannot be opened.");
1453 /* split-stack for now requires default IPv4-only HTCP */
1454 if (Ip::EnableIpv6
&IPV6_SPECIAL_SPLITSTACK
&& htcpOutgoingConn
->local
.isAnyAddr()) {
1455 htcpOutgoingConn
->local
.setIPv4();
1459 comm_open_listener(SOCK_DGRAM
, IPPROTO_UDP
, htcpOutgoingConn
, "Outgoing HTCP Socket");
1462 if (!Comm::IsConnOpen(htcpOutgoingConn
))
1463 fatal("Cannot open Outgoing HTCP Socket");
1465 Comm::SetSelect(htcpOutgoingConn
->fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1467 debugs(31, DBG_IMPORTANT
, "Sending HTCP messages from " << htcpOutgoingConn
->local
);
1470 if (!htcpDetailPool
) {
1471 htcpDetailPool
= memPoolCreate("htcpDetail", sizeof(htcpDetail
));
1476 htcpIncomingConnectionOpened(const Comm::ConnectionPointer
&conn
, int)
1478 if (!Comm::IsConnOpen(conn
))
1479 fatal("Cannot open HTCP Socket");
1481 Comm::SetSelect(conn
->fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1483 debugs(31, DBG_CRITICAL
, "Accepting HTCP messages on " << conn
->local
);
1485 if (Config
.Addrs
.udp_outgoing
.isNoAddr()) {
1486 htcpOutgoingConn
= conn
;
1487 debugs(31, DBG_IMPORTANT
, "Sending HTCP messages from " << htcpOutgoingConn
->local
);
1492 htcpQuery(StoreEntry
* e
, HttpRequest
* req
, CachePeer
* p
)
1494 cache_key
*save_key
;
1495 static char pkt
[8192];
1498 HttpHeader
hdr(hoRequest
);
1499 HttpStateFlags flags
;
1501 if (!Comm::IsConnOpen(htcpIncomingConn
))
1504 old_squid_format
= p
->options
.htcp_oldsquid
;
1505 memset(&flags
, '\0', sizeof(flags
));
1506 snprintf(vbuf
, sizeof(vbuf
), "%d/%d",
1507 req
->http_ver
.major
, req
->http_ver
.minor
);
1509 htcpStuff
stuff(++msg_id_counter
, HTCP_TST
, RR_REQUEST
, 1);
1510 SBuf sb
= req
->method
.image();
1511 stuff
.S
.method
= sb
.c_str();
1512 stuff
.S
.uri
= (char *) e
->url();
1513 stuff
.S
.version
= vbuf
;
1514 HttpStateData::httpBuildRequestHeader(req
, e
, NULL
, &hdr
, flags
);
1519 stuff
.S
.req_hdrs
= mb
.buf
;
1520 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
1523 debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
1527 htcpSend(pkt
, (int) pktlen
, p
->in_addr
);
1529 queried_id
[stuff
.msg_id
% N_QUERIED_KEYS
] = stuff
.msg_id
;
1530 save_key
= queried_keys
[stuff
.msg_id
% N_QUERIED_KEYS
];
1531 storeKeyCopy(save_key
, (const cache_key
*)e
->key
);
1532 queried_addr
[stuff
.msg_id
% N_QUERIED_KEYS
] = p
->in_addr
;
1533 debugs(31, 3, "htcpQuery: key (" << save_key
<< ") " << storeKeyText(save_key
));
1539 * Send an HTCP CLR message for a specified item to a given CachePeer.
1542 htcpClear(StoreEntry
* e
, const char *uri
, HttpRequest
* req
, const HttpRequestMethod
&, CachePeer
* p
, htcp_clr_reason reason
)
1544 static char pkt
[8192];
1547 HttpHeader
hdr(hoRequest
);
1549 HttpStateFlags flags
;
1551 if (!Comm::IsConnOpen(htcpIncomingConn
))
1554 old_squid_format
= p
->options
.htcp_oldsquid
;
1555 memset(&flags
, '\0', sizeof(flags
));
1556 snprintf(vbuf
, sizeof(vbuf
), "%d/%d",
1557 req
->http_ver
.major
, req
->http_ver
.minor
);
1559 htcpStuff
stuff(++msg_id_counter
, HTCP_CLR
, RR_REQUEST
, 0);
1560 if (reason
== HTCP_CLR_INVALIDATION
)
1563 SBuf sb
= req
->method
.image();
1564 stuff
.S
.method
= sb
.c_str();
1565 if (e
== NULL
|| e
->mem_obj
== NULL
) {
1569 stuff
.S
.uri
= xstrdup(uri
);
1571 stuff
.S
.uri
= (char *) e
->url();
1573 stuff
.S
.version
= vbuf
;
1574 if (reason
!= HTCP_CLR_INVALIDATION
) {
1575 HttpStateData::httpBuildRequestHeader(req
, e
, NULL
, &hdr
, flags
);
1579 stuff
.S
.req_hdrs
= mb
.buf
;
1581 stuff
.S
.req_hdrs
= NULL
;
1583 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
1584 if (reason
!= HTCP_CLR_INVALIDATION
) {
1591 debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
1595 htcpSend(pkt
, (int) pktlen
, p
->in_addr
);
1599 * htcpSocketShutdown only closes the 'in' socket if it is
1600 * different than the 'out' socket.
1603 htcpSocketShutdown(void)
1605 if (!Comm::IsConnOpen(htcpIncomingConn
))
1608 debugs(12, DBG_IMPORTANT
, "Stop accepting HTCP on " << htcpIncomingConn
->local
);
1610 * Here we just unlink htcpIncomingConn because the HTCP 'in'
1611 * and 'out' sockets might be just one FD. This prevents this
1612 * function from executing repeatedly. When we are really ready to
1613 * exit or restart, main will comm_close the 'out' descriptor.
1615 htcpIncomingConn
= NULL
;
1618 * Normally we only write to the outgoing HTCP socket, but
1619 * we also have a read handler there to catch messages sent
1620 * to that specific interface. During shutdown, we must
1621 * disable reading on the outgoing socket.
1623 /* XXX Don't we need this handler to read replies while shutting down?
1624 * I think there should be a separate hander for reading replies..
1626 assert(Comm::IsConnOpen(htcpOutgoingConn
));
1628 Comm::SetSelect(htcpOutgoingConn
->fd
, COMM_SELECT_READ
, NULL
, NULL
, 0);
1632 htcpClosePorts(void)
1634 htcpSocketShutdown();
1636 if (htcpOutgoingConn
!= NULL
) {
1637 debugs(12, DBG_IMPORTANT
, "Stop sending HTCP from " << htcpOutgoingConn
->local
);
1638 htcpOutgoingConn
= NULL
;
1643 htcpLogHtcp(Ip::Address
&caddr
, int opcode
, LogTags logcode
, const char *url
)
1645 AccessLogEntry::Pointer al
= new AccessLogEntry
;
1646 if (LOG_TAG_NONE
== logcode
.oldType
)
1648 if (!Config
.onoff
.log_udp
)
1650 al
->htcp
.opcode
= htcpOpcodeStr
[opcode
];
1652 al
->cache
.caddr
= caddr
;
1653 al
->cache
.code
= logcode
;
1654 al
->cache
.trTime
.tv_sec
= 0;
1655 al
->cache
.trTime
.tv_usec
= 0;
1656 accessLogLog(al
, NULL
);