]> git.ipfire.org Git - thirdparty/squid.git/blame_incremental - src/htcp.cc
Fix parsing of malformed quoted squid.conf strings (#2239)
[thirdparty/squid.git] / src / htcp.cc
... / ...
CommitLineData
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9/* DEBUG: section 31 Hypertext Caching Protocol */
10
11#include "squid.h"
12#include "AccessLogEntry.h"
13#include "acl/Acl.h"
14#include "acl/FilledChecklist.h"
15#include "base/AsyncCallbacks.h"
16#include "base/RunnersRegistry.h"
17#include "CachePeer.h"
18#include "CachePeers.h"
19#include "comm.h"
20#include "comm/Connection.h"
21#include "comm/Loops.h"
22#include "compat/xalloc.h"
23#include "debug/Messages.h"
24#include "globals.h"
25#include "htcp.h"
26#include "http.h"
27#include "http/ContentLengthInterpreter.h"
28#include "HttpRequest.h"
29#include "icmp/net_db.h"
30#include "ip/tools.h"
31#include "ipc/StartListening.h"
32#include "md5.h"
33#include "mem/forward.h"
34#include "MemBuf.h"
35#include "refresh.h"
36#include "SquidConfig.h"
37#include "StatCounters.h"
38#include "Store.h"
39#include "store_key_md5.h"
40#include "StoreClient.h"
41#include "tools.h"
42
43#include <memory>
44typedef struct _Countstr Countstr;
45
46typedef struct _htcpHeader htcpHeader;
47
48typedef struct _htcpDataHeader htcpDataHeader;
49
50typedef struct _htcpDataHeaderSquid htcpDataHeaderSquid;
51
52typedef struct _htcpAuthHeader htcpAuthHeader;
53
54struct _Countstr {
55 uint16_t length;
56 char *text;
57};
58
59struct _htcpHeader {
60 uint16_t length;
61 u_char major;
62 u_char minor;
63};
64
65struct _htcpDataHeaderSquid {
66 uint16_t length;
67
68#if !WORDS_BIGENDIAN
69 unsigned int opcode:4;
70 unsigned int response:4;
71#else
72 unsigned int response:4;
73 unsigned int opcode:4;
74#endif
75
76#if !WORDS_BIGENDIAN
77 unsigned int reserved:6;
78 unsigned int F1:1;
79 unsigned int RR:1;
80#else
81 unsigned int RR:1;
82 unsigned int F1:1;
83 unsigned int reserved:6;
84#endif
85
86 uint32_t msg_id;
87};
88
89struct _htcpDataHeader {
90 uint16_t length;
91
92#if WORDS_BIGENDIAN
93 uint8_t opcode:4;
94 uint8_t response:4;
95#else
96 uint8_t response:4;
97 uint8_t opcode:4;
98#endif
99
100#if WORDS_BIGENDIAN
101 uint8_t reserved:6;
102 uint8_t F1:1;
103 uint8_t RR:1;
104#else
105 uint8_t RR:1;
106 uint8_t F1:1;
107 uint8_t reserved:6;
108#endif
109
110 uint32_t msg_id;
111};
112
113/* RR == 0 --> F1 = RESPONSE DESIRED FLAG */
114/* RR == 1 --> F1 = MESSAGE OVERALL FLAG */
115/* RR == 0 --> REQUEST */
116/* RR == 1 --> RESPONSE */
117
118struct _htcpAuthHeader {
119 uint16_t length;
120 time_t sig_time;
121 time_t sig_expire;
122 Countstr key_name;
123 Countstr signature;
124};
125
126class htcpSpecifier: public CodeContext, public StoreClient
127{
128 MEMPROXY_CLASS(htcpSpecifier);
129
130public:
131 typedef RefCount<htcpSpecifier> Pointer;
132
133 void checkHit();
134 void checkedHit(StoreEntry *);
135
136 void setFrom(Ip::Address &anIp) { from = anIp; }
137 void setDataHeader(htcpDataHeader *aDataHeader) {
138 dhdr = aDataHeader;
139 }
140
141 /* CodeContext API */
142 ScopedId codeContextGist() const override; // override
143 std::ostream &detailCodeContext(std::ostream &os) const override; // override
144
145 /* StoreClient API */
146 LogTags *loggingTags() const override;
147 void fillChecklist(ACLFilledChecklist &) const override;
148
149public:
150 const char *method = nullptr;
151 const char *uri = nullptr;
152 char *version = nullptr;
153 char *req_hdrs = nullptr;
154 size_t reqHdrsSz = 0; ///< size of the req_hdrs content
155 HttpRequest::Pointer request;
156
157 /// optimization: nil until needed
158 mutable AccessLogEntryPointer al;
159
160private:
161 HttpRequest::Pointer checkHitRequest;
162
163 Ip::Address from;
164 htcpDataHeader *dhdr = nullptr;
165};
166
167class htcpDetail
168{
169 MEMPROXY_CLASS(htcpDetail);
170public:
171 char *resp_hdrs = nullptr;
172 size_t respHdrsSz = 0;
173
174 char *entity_hdrs = nullptr;
175 size_t entityHdrsSz = 0;
176
177 char *cache_hdrs = nullptr;
178 size_t cacheHdrsSz = 0;
179};
180
181class htcpStuff
182{
183public:
184 htcpStuff(uint32_t id, int o, int r, int f) :
185 op(o),
186 rr(r),
187 f1(f),
188 msg_id(id)
189 {}
190
191 int op = 0;
192 int rr = 0;
193 int f1 = 0;
194 int response = 0;
195 int reason = 0;
196 uint32_t msg_id;
197 htcpSpecifier S;
198 htcpDetail D;
199};
200
201enum {
202 HTCP_NOP,
203 HTCP_TST,
204 HTCP_MON,
205 HTCP_SET,
206 HTCP_CLR,
207 HTCP_END
208};
209
210static const char *const htcpOpcodeStr[] = {
211 "HTCP_NOP",
212 "HTCP_TST",
213 "HTCP_MON",
214 "HTCP_SET",
215 "HTCP_CLR",
216 "HTCP_END"
217};
218
219/*
220 * values for htcpDataHeader->response
221 */
222enum {
223 AUTH_REQUIRED,
224 AUTH_FAILURE,
225 OPCODE_UNIMPLEMENTED,
226 MAJOR_VERSION_UNSUPPORTED,
227 MINOR_VERSION_UNSUPPORTED,
228 INVALID_OPCODE
229};
230
231/*
232 * values for htcpDataHeader->RR
233 */
234enum {
235 RR_REQUEST,
236 RR_RESPONSE
237};
238
239static void htcpIncomingConnectionOpened(Ipc::StartListeningAnswer&);
240static uint32_t msg_id_counter = 0;
241
242static Comm::ConnectionPointer htcpOutgoingConn = nullptr;
243static Comm::ConnectionPointer htcpIncomingConn = nullptr;
244#define N_QUERIED_KEYS 8192
245static uint32_t queried_id[N_QUERIED_KEYS];
246static cache_key queried_keys[N_QUERIED_KEYS][SQUID_MD5_DIGEST_LENGTH];
247
248static Ip::Address queried_addr[N_QUERIED_KEYS];
249
250static int old_squid_format = 0;
251
252static ssize_t htcpBuildPacket(char *buf, size_t buflen, htcpStuff * stuff);
253static htcpDetail *htcpUnpackDetail(char *buf, int sz);
254static ssize_t htcpBuildAuth(char *buf, size_t buflen);
255static ssize_t htcpBuildCountstr(char *buf, size_t buflen, const char *s, size_t len);
256static ssize_t htcpBuildData(char *buf, size_t buflen, htcpStuff * stuff);
257static ssize_t htcpBuildDetail(char *buf, size_t buflen, htcpStuff * stuff);
258static ssize_t htcpBuildOpData(char *buf, size_t buflen, htcpStuff * stuff);
259static ssize_t htcpBuildSpecifier(char *buf, size_t buflen, htcpStuff * stuff);
260static ssize_t htcpBuildTstOpData(char *buf, size_t buflen, htcpStuff * stuff);
261
262static void htcpHandleMsg(char *buf, int sz, Ip::Address &from);
263
264static void htcpLogHtcp(Ip::Address &, const int, const LogTags_ot, const char *, AccessLogEntryPointer);
265static void htcpHandleTst(htcpDataHeader *, char *buf, int sz, Ip::Address &from);
266
267static void htcpRecv(int fd, void *data);
268
269static void htcpSend(const char *buf, int len, Ip::Address &to);
270
271static void htcpTstReply(htcpDataHeader *, StoreEntry *, htcpSpecifier *, Ip::Address &);
272
273static void htcpHandleTstRequest(htcpDataHeader *, char *buf, int sz, Ip::Address &from);
274
275static void htcpHandleTstResponse(htcpDataHeader *, char *, int, Ip::Address &);
276
277static void
278htcpSyncAle(AccessLogEntryPointer &al, const Ip::Address &caddr, const int opcode, const char *url)
279{
280 if (!al)
281 al = new AccessLogEntry();
282 al->cache.caddr = caddr;
283 al->htcp.opcode = htcpOpcodeStr[opcode];
284 al->url = url;
285 al->setVirginUrlForMissingRequest(al->url);
286 // HTCP transactions do not wait
287 al->cache.start_time = current_time;
288 al->cache.trTime.tv_sec = 0;
289 al->cache.trTime.tv_usec = 0;
290}
291
292static void
293htcpHexdump(const char *tag, const char *s, int sz)
294{
295#if USE_HEXDUMP
296 char hex[80];
297 debugs(31, 3, "htcpHexdump " << tag);
298 memset(hex, '\0', sizeof(hex));
299
300 for (int i = 0; i < sz; ++i) {
301 int k = i % 16;
302 snprintf(&hex[k * 3], 4, " %02x", (int) *(s + i));
303
304 if (k < 15 && i < (sz - 1))
305 continue;
306
307 debugs(31, 3, "\t" << hex);
308
309 memset(hex, '\0', sizeof(hex));
310 }
311#else
312 (void)tag;
313 (void)s;
314 (void)sz;
315#endif
316}
317
318static bool
319parseUint16(const char * const buf, const int sz, uint16_t &out, const char * const field)
320{
321 if (sz < 2) {
322 debugs(31, 3, "too short for " << field);
323 return false;
324 }
325
326 memcpy(&out, buf, 2);
327 out = ntohs(out);
328 return true;
329}
330
331/*
332 * STUFF FOR SENDING HTCP MESSAGES
333 */
334
335static ssize_t
336htcpBuildAuth(char *buf, size_t buflen)
337{
338 htcpAuthHeader auth;
339 size_t copy_sz = 0;
340 assert(2 == sizeof(uint16_t));
341 auth.length = htons(2);
342 copy_sz += 2;
343 if (buflen < copy_sz)
344 return -1;
345 memcpy(buf, &auth, copy_sz);
346 return copy_sz;
347}
348
349static ssize_t
350htcpBuildCountstr(char *buf, size_t buflen, const char *s, size_t len)
351{
352 int off = 0;
353
354 if (buflen - off < 2)
355 return -1;
356
357 debugs(31, 3, "htcpBuildCountstr: LENGTH = " << len);
358
359 debugs(31, 3, "htcpBuildCountstr: TEXT = {" << (s ? s : "<NULL>") << "}");
360
361 uint16_t length = htons((uint16_t) len);
362
363 memcpy(buf + off, &length, 2);
364
365 off += 2;
366
367 if (buflen - off < len)
368 return -1;
369
370 if (len)
371 memcpy(buf + off, s, len);
372
373 off += len;
374
375 return off;
376}
377
378static ssize_t
379htcpBuildSpecifier(char *buf, size_t buflen, htcpStuff * stuff)
380{
381 ssize_t off = 0;
382 ssize_t s;
383 s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.method, (stuff->S.method?strlen(stuff->S.method):0));
384
385 if (s < 0)
386 return s;
387
388 off += s;
389
390 s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.uri, (stuff->S.uri?strlen(stuff->S.uri):0));
391
392 if (s < 0)
393 return s;
394
395 off += s;
396
397 s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.version, (stuff->S.version?strlen(stuff->S.version):0));
398
399 if (s < 0)
400 return s;
401
402 off += s;
403
404 s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.req_hdrs, stuff->S.reqHdrsSz);
405
406 if (s < 0)
407 return s;
408
409 off += s;
410
411 debugs(31, 3, "htcpBuildSpecifier: size " << off);
412
413 return off;
414}
415
416static ssize_t
417htcpBuildDetail(char *buf, size_t buflen, htcpStuff * stuff)
418{
419 ssize_t off = 0;
420 ssize_t s;
421 s = htcpBuildCountstr(buf + off, buflen - off, stuff->D.resp_hdrs, stuff->D.respHdrsSz);
422
423 if (s < 0)
424 return s;
425
426 off += s;
427
428 s = htcpBuildCountstr(buf + off, buflen - off, stuff->D.entity_hdrs, stuff->D.entityHdrsSz);
429
430 if (s < 0)
431 return s;
432
433 off += s;
434
435 s = htcpBuildCountstr(buf + off, buflen - off, stuff->D.cache_hdrs, stuff->D.cacheHdrsSz);
436
437 if (s < 0)
438 return s;
439
440 off += s;
441
442 return off;
443}
444
445static ssize_t
446htcpBuildTstOpData(char *buf, size_t buflen, htcpStuff * stuff)
447{
448 switch (stuff->rr) {
449
450 case RR_REQUEST:
451 debugs(31, 3, "htcpBuildTstOpData: RR_REQUEST");
452 return htcpBuildSpecifier(buf, buflen, stuff);
453
454 case RR_RESPONSE:
455 debugs(31, 3, "htcpBuildTstOpData: RR_RESPONSE");
456 debugs(31, 3, "htcpBuildTstOpData: F1 = " << stuff->f1);
457
458 if (stuff->f1) /* cache miss */
459 return 0;
460 else /* cache hit */
461 return htcpBuildDetail(buf, buflen, stuff);
462
463 default:
464 fatal_dump("htcpBuildTstOpData: bad RR value");
465 }
466
467 return 0;
468}
469
470static ssize_t
471htcpBuildClrOpData(char *buf, size_t buflen, htcpStuff * stuff)
472{
473 unsigned short reason;
474
475 switch (stuff->rr) {
476 case RR_REQUEST:
477 debugs(31, 3, "htcpBuildClrOpData: RR_REQUEST");
478 reason = htons((unsigned short)stuff->reason);
479 if (buflen < 2)
480 return -1;
481 memcpy(buf, &reason, 2);
482 {
483 const auto s = htcpBuildSpecifier(buf + 2, buflen - 2, stuff);
484 if (s < 0)
485 return s;
486 return s + 2;
487 }
488 case RR_RESPONSE:
489 break;
490 default:
491 fatal_dump("htcpBuildClrOpData: bad RR value");
492 }
493
494 return 0;
495}
496
497static ssize_t
498htcpBuildOpData(char *buf, size_t buflen, htcpStuff * stuff)
499{
500 ssize_t off = 0;
501 debugs(31, 3, "htcpBuildOpData: opcode " << htcpOpcodeStr[stuff->op]);
502
503 switch (stuff->op) {
504
505 case HTCP_TST:
506 off = htcpBuildTstOpData(buf + off, buflen, stuff);
507 break;
508
509 case HTCP_CLR:
510 off = htcpBuildClrOpData(buf + off, buflen, stuff);
511 break;
512
513 default:
514 assert(0);
515 break;
516 }
517
518 return off;
519}
520
521static ssize_t
522htcpBuildData(char *buf, size_t buflen, htcpStuff * stuff)
523{
524 ssize_t off = 0;
525 ssize_t op_data_sz;
526 size_t hdr_sz = sizeof(htcpDataHeader);
527
528 if (buflen < hdr_sz)
529 return -1;
530
531 off += hdr_sz; /* skip! */
532
533 op_data_sz = htcpBuildOpData(buf + off, buflen - off, stuff);
534
535 if (op_data_sz < 0)
536 return op_data_sz;
537
538 off += op_data_sz;
539
540 debugs(31, 3, "htcpBuildData: hdr.length = " << off);
541
542 if (!old_squid_format) {
543 htcpDataHeader hdr;
544 memset(&hdr, 0, sizeof(hdr));
545 /* convert multi-byte fields */
546 hdr.msg_id = htonl(stuff->msg_id);
547 hdr.length = htons(static_cast<uint16_t>(off));
548 hdr.opcode = stuff->op;
549 hdr.response = stuff->response;
550 hdr.RR = stuff->rr;
551 hdr.F1 = stuff->f1;
552 memcpy(buf, &hdr, hdr_sz);
553 } else {
554 htcpDataHeaderSquid hdrSquid;
555 memset(&hdrSquid, 0, sizeof(hdrSquid));
556 hdrSquid.length = htons(static_cast<uint16_t>(off));
557 hdrSquid.opcode = stuff->op;
558 hdrSquid.response = stuff->response;
559 hdrSquid.F1 = stuff->f1;
560 hdrSquid.RR = stuff->rr;
561 memcpy(buf, &hdrSquid, hdr_sz);
562 }
563
564 debugs(31, 3, "htcpBuildData: size " << off);
565
566 return off;
567}
568
569/*
570 * Build an HTCP packet into buf, maximum length buflen.
571 * Returns the packet length, or zero on failure.
572 */
573static ssize_t
574htcpBuildPacket(char *buf, size_t buflen, htcpStuff * stuff)
575{
576 ssize_t s;
577 ssize_t off = 0;
578 size_t hdr_sz = sizeof(htcpHeader);
579 htcpHeader hdr;
580 /* skip the header -- we don't know the overall length */
581
582 if (buflen < hdr_sz) {
583 return 0;
584 }
585
586 off += hdr_sz;
587 s = htcpBuildData(buf + off, buflen - off, stuff);
588
589 if (s < 0) {
590 return 0;
591 }
592
593 off += s;
594 s = htcpBuildAuth(buf + off, buflen - off);
595
596 if (s < 0) {
597 return 0;
598 }
599
600 off += s;
601 hdr.length = htons((uint16_t) off);
602 hdr.major = 0;
603
604 if (old_squid_format)
605 hdr.minor = 0;
606 else
607 hdr.minor = 1;
608
609 memcpy(buf, &hdr, hdr_sz);
610
611 debugs(31, 3, "htcpBuildPacket: size " << off);
612
613 return off;
614}
615
616static void
617htcpSend(const char *buf, int len, Ip::Address &to)
618{
619 debugs(31, 3, to);
620 htcpHexdump("htcpSend", buf, len);
621
622 if (comm_udp_sendto(htcpOutgoingConn->fd, to, buf, len) < 0) {
623 int xerrno = errno;
624 debugs(31, 3, htcpOutgoingConn << " sendto: " << xstrerr(xerrno));
625 } else
626 ++statCounter.htcp.pkts_sent;
627}
628
629/*
630 * Unpack an HTCP SPECIFIER in place
631 * This will overwrite any following AUTH block
632 */
633// XXX: this needs to be turned into an Htcp1::Parser inheriting from Http1::RequestParser
634// but with different first-line and block unpacking logic.
635static htcpSpecifier::Pointer
636htcpUnpackSpecifier(char *buf, int sz)
637{
638 static const htcpSpecifier::Pointer nil;
639 htcpSpecifier::Pointer s(new htcpSpecifier);
640 HttpRequestMethod method;
641
642 /* Find length of METHOD */
643 uint16_t l = 0;
644 if (!parseUint16(buf, sz, l, "METHOD length"))
645 return nil;
646
647 sz -= 2;
648 buf += 2;
649
650 if (l > sz) {
651 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack METHOD");
652 return nil;
653 }
654
655 /* Set METHOD */
656 s->method = buf;
657 buf += l;
658 sz -= l;
659 debugs(31, 6, "htcpUnpackSpecifier: METHOD (" << l << "/" << sz << ") '" << s->method << "'");
660
661 /* Find length of URI */
662 if (!parseUint16(buf, sz, l, "URI length"))
663 return nil;
664
665 sz -= 2;
666
667 if (l > sz) {
668 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack URI");
669 return nil;
670 }
671
672 /* Add terminating null to METHOD */
673 *buf = '\0';
674 buf += 2;
675
676 /* Set URI */
677 s->uri = buf;
678 buf += l;
679 sz -= l;
680 debugs(31, 6, "htcpUnpackSpecifier: URI (" << l << "/" << sz << ") '" << s->uri << "'");
681
682 /* Find length of VERSION */
683 if (!parseUint16(buf, sz, l, "VERSION length"))
684 return nil;
685
686 sz -= 2;
687
688 if (l > sz) {
689 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack VERSION");
690 return nil;
691 }
692
693 /* Add terminating null to URI */
694 *buf = '\0';
695 buf += 2;
696
697 /* Set VERSION */
698 s->version = buf;
699 buf += l;
700 sz -= l;
701 debugs(31, 6, "htcpUnpackSpecifier: VERSION (" << l << "/" << sz << ") '" << s->version << "'");
702
703 /* Find length of REQ-HDRS */
704 if (!parseUint16(buf, sz, l, "REQ-HDRS length"))
705 return nil;
706
707 sz -= 2;
708
709 if (l > sz) {
710 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack REQ-HDRS");
711 return nil;
712 }
713
714 /* Add terminating null to URI */
715 *buf = '\0';
716 buf += 2;
717
718 /* Set REQ-HDRS */
719 s->req_hdrs = buf;
720 buf += l;
721 sz -= l;
722 s->reqHdrsSz = l;
723 debugs(31, 6, "htcpUnpackSpecifier: REQ-HDRS (" << l << "/" << sz << ") '" << s->req_hdrs << "'");
724
725 debugs(31, 3, "htcpUnpackSpecifier: " << sz << " bytes left");
726
727 /*
728 * Add terminating null to REQ-HDRS. This is possible because we allocated
729 * an extra byte when we received the packet. This will overwrite any following
730 * AUTH block.
731 */
732 *buf = '\0';
733
734 // Parse the request
735 method.HttpRequestMethodXXX(s->method);
736
737 const auto mx = MasterXaction::MakePortless<XactionInitiator::initHtcp>();
738 s->request = HttpRequest::FromUrlXXX(s->uri, mx, method == Http::METHOD_NONE ? HttpRequestMethod(Http::METHOD_GET) : method);
739 if (!s->request) {
740 debugs(31, 3, "failed to create request. Invalid URI?");
741 return nil;
742 }
743
744 return s;
745}
746
747/*
748 * Unpack an HTCP DETAIL in place
749 * This will overwrite any following AUTH block
750 */
751static htcpDetail *
752htcpUnpackDetail(char *buf, int sz)
753{
754 auto d = std::make_unique<htcpDetail>();
755
756 /* Find length of RESP-HDRS */
757 uint16_t l = 0;
758 if (!parseUint16(buf, sz, l, "RESP-HDRS length"))
759 return nullptr;
760
761 sz -= 2;
762 buf += 2;
763
764 if (l > sz) {
765 debugs(31, 3, "htcpUnpackDetail: failed to unpack RESP_HDRS");
766 return nullptr;
767 }
768
769 /* Set RESP-HDRS */
770 d->resp_hdrs = buf;
771 buf += l;
772 d->respHdrsSz = l;
773 sz -= l;
774
775 /* Find length of ENTITY-HDRS */
776 if (!parseUint16(buf, sz, l, "ENTITY-HDRS length"))
777 return nullptr;
778
779 sz -= 2;
780
781 if (l > sz) {
782 debugs(31, 3, "htcpUnpackDetail: failed to unpack ENTITY_HDRS");
783 return nullptr;
784 }
785
786 /* Add terminating null to RESP-HDRS */
787 *buf = '\0';
788
789 /* Set ENTITY-HDRS */
790 buf += 2;
791
792 d->entity_hdrs = buf;
793 buf += l;
794 d->entityHdrsSz = l;
795 sz -= l;
796
797 /* Find length of CACHE-HDRS */
798 if (!parseUint16(buf, sz, l, "CACHE-HDRS length"))
799 return nullptr;
800
801 sz -= 2;
802
803 if (l > sz) {
804 debugs(31, 3, "htcpUnpackDetail: failed to unpack CACHE_HDRS");
805 return nullptr;
806 }
807
808 /* Add terminating null to ENTITY-HDRS */
809 *buf = '\0';
810
811 /* Set CACHE-HDRS */
812 buf += 2;
813
814 d->cache_hdrs = buf;
815 buf += l;
816 d->cacheHdrsSz = l;
817 sz -= l;
818
819 debugs(31, 3, "htcpUnpackDetail: " << sz << " bytes left");
820
821 /*
822 * Add terminating null to CACHE-HDRS. This is possible because we allocated
823 * an extra byte when we received the packet. This will overwrite any following
824 * AUTH block.
825 */
826 *buf = '\0';
827
828 return d.release();
829}
830
831static bool
832htcpAccessAllowed(acl_access * acl, const htcpSpecifier::Pointer &s, Ip::Address &from)
833{
834 /* default deny if no access list present */
835 if (!acl)
836 return false;
837
838 ACLFilledChecklist checklist(acl, s->request.getRaw());
839 checklist.src_addr = from;
840 checklist.my_addr.setNoAddr();
841 return checklist.fastCheck().allowed();
842}
843
844static void
845htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Address &from)
846{
847 static char pkt[8192];
848 HttpHeader hdr(hoHtcpReply);
849 ssize_t pktlen;
850
851 htcpStuff stuff(dhdr->msg_id, HTCP_TST, RR_RESPONSE, 0);
852 stuff.response = e ? 0 : 1;
853 debugs(31, 3, "htcpTstReply: response = " << stuff.response);
854
855 if (spec) {
856 stuff.S.method = spec->method;
857 stuff.S.request = spec->request;
858 stuff.S.uri = spec->uri;
859 stuff.S.version = spec->version;
860 stuff.S.req_hdrs = spec->req_hdrs;
861 stuff.S.reqHdrsSz = spec->reqHdrsSz;
862 if (e)
863 hdr.putInt(Http::HdrType::AGE, (e->timestamp <= squid_curtime ? (squid_curtime - e->timestamp) : 0) );
864 else
865 hdr.putInt(Http::HdrType::AGE, 0);
866 MemBuf mb;
867 mb.init();
868 hdr.packInto(&mb);
869 stuff.D.resp_hdrs = xstrdup(mb.buf);
870 stuff.D.respHdrsSz = mb.contentSize();
871 debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff.D.resp_hdrs << "}");
872 mb.reset();
873 hdr.clean();
874
875 if (e && e->expires > -1)
876 hdr.putTime(Http::HdrType::EXPIRES, e->expires);
877
878 if (e && e->lastModified() > -1)
879 hdr.putTime(Http::HdrType::LAST_MODIFIED, e->lastModified());
880
881 hdr.packInto(&mb);
882
883 stuff.D.entity_hdrs = xstrdup(mb.buf);
884 stuff.D.entityHdrsSz = mb.contentSize();
885
886 debugs(31, 3, "htcpTstReply: entity_hdrs = {" << stuff.D.entity_hdrs << "}");
887
888 mb.reset();
889 hdr.clean();
890
891#if USE_ICMP
892 if (const char *host = spec->request->url.host()) {
893 int rtt = 0;
894 int hops = 0;
895 int samp = 0;
896 netdbHostData(host, &samp, &rtt, &hops);
897
898 if (rtt || hops) {
899 char cto_buf[SQUIDHOSTNAMELEN+128];
900 snprintf(cto_buf, sizeof(cto_buf), "%s %d %f %d",
901 host, samp, 0.001 * rtt, hops);
902 hdr.putExt("Cache-to-Origin", cto_buf);
903 }
904 }
905#endif /* USE_ICMP */
906
907 hdr.packInto(&mb);
908 stuff.D.cache_hdrs = xstrdup(mb.buf);
909 stuff.D.cacheHdrsSz = mb.contentSize();
910 debugs(31, 3, "htcpTstReply: cache_hdrs = {" << stuff.D.cache_hdrs << "}");
911 mb.clean();
912 hdr.clean();
913 }
914
915 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
916
917 safe_free(stuff.D.resp_hdrs);
918 stuff.D.respHdrsSz = 0;
919 safe_free(stuff.D.entity_hdrs);
920 stuff.D.entityHdrsSz = 0;
921 safe_free(stuff.D.cache_hdrs);
922 stuff.D.cacheHdrsSz = 0;
923
924 if (!pktlen) {
925 debugs(31, 3, "htcpTstReply: htcpBuildPacket() failed");
926 return;
927 }
928
929 htcpSend(pkt, (int) pktlen, from);
930}
931
932static void
933
934htcpClrReply(htcpDataHeader * dhdr, int purgeSucceeded, Ip::Address &from)
935{
936 static char pkt[8192];
937 ssize_t pktlen;
938
939 /* If dhdr->F1 == 0, no response desired */
940
941 if (dhdr->F1 == 0)
942 return;
943
944 htcpStuff stuff(dhdr->msg_id, HTCP_CLR, RR_RESPONSE, 0);
945
946 stuff.response = purgeSucceeded ? 0 : 2;
947
948 debugs(31, 3, "htcpClrReply: response = " << stuff.response);
949
950 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
951
952 if (pktlen == 0) {
953 debugs(31, 3, "htcpClrReply: htcpBuildPacket() failed");
954 return;
955 }
956
957 htcpSend(pkt, (int) pktlen, from);
958}
959
960ScopedId
961htcpSpecifier::codeContextGist() const
962{
963 if (al) {
964 const auto gist = al->codeContextGist();
965 if (gist.value)
966 return gist;
967 }
968
969 if (request) {
970 if (const auto &mx = request->masterXaction)
971 return mx->id.detach();
972 }
973
974 return ScopedId("HTCP w/o master");
975}
976
977std::ostream &
978htcpSpecifier::detailCodeContext(std::ostream &os) const
979{
980 if (al)
981 return al->detailCodeContext(os);
982
983 if (request) {
984 if (const auto &mx = request->masterXaction)
985 return os << Debug::Extra << "current master transaction: " << mx->id;
986 }
987
988 // TODO: Report method, uri, and version if they have been set
989 return os;
990}
991
992void
993htcpSpecifier::checkHit()
994{
995 checkHitRequest = request;
996
997 if (!checkHitRequest) {
998 debugs(31, 3, "htcpCheckHit: NO; failed to parse URL");
999 checkedHit(nullptr);
1000 return;
1001 }
1002
1003 if (!checkHitRequest->parseHeader(req_hdrs, reqHdrsSz)) {
1004 debugs(31, 3, "htcpCheckHit: NO; failed to parse request headers");
1005 checkHitRequest = nullptr;
1006 checkedHit(nullptr);
1007 return;
1008 }
1009
1010 const auto e = storeGetPublicByRequest(checkHitRequest.getRaw());
1011 StoreEntry *hit = nullptr;
1012
1013 if (!e) {
1014 debugs(31, 3, "NO; public object not found");
1015 } else if (!e->validToSend()) {
1016 debugs(31, 3, "NO; entry not valid to send" );
1017 } else if (e->hittingRequiresCollapsing() && !startCollapsingOn(*e, false)) {
1018 debugs(31, 3, "NO; prohibited CF hit: " << *e);
1019 } else if (!didCollapse && refreshCheckHTCP(e, checkHitRequest.getRaw())) {
1020 debugs(31, 3, "NO; cached response is stale");
1021 } else {
1022 debugs(31, 3, "YES!?");
1023 hit = e;
1024 }
1025
1026 checkedHit(hit);
1027
1028 // TODO: StoreClients must either store/lock or abandon found entries.
1029 //if (e)
1030 // e->abandon();
1031}
1032
1033LogTags *
1034htcpSpecifier::loggingTags() const
1035{
1036 // calling htcpSyncAle() here would not change cache.code
1037 if (!al)
1038 al = new AccessLogEntry();
1039 return &al->cache.code;
1040}
1041
1042void
1043htcpSpecifier::fillChecklist(ACLFilledChecklist &checklist) const
1044{
1045 checklist.setRequest(request.getRaw());
1046 htcpSyncAle(al, from, dhdr->opcode, uri);
1047 checklist.al = al;
1048}
1049
1050static void
1051htcpClrStoreEntry(StoreEntry * e)
1052{
1053 debugs(31, 4, "htcpClrStoreEntry: Clearing store for entry: " << e->url() );
1054 e->releaseRequest();
1055}
1056
1057static int
1058htcpClrStore(const htcpSpecifier::Pointer &s)
1059{
1060 HttpRequestPointer request(s->request);
1061 if (!request) {
1062 debugs(31, 3, "htcpClrStore: failed to parse URL");
1063 return -1;
1064 }
1065
1066 /* Parse request headers */
1067 if (!request->parseHeader(s->req_hdrs, s->reqHdrsSz)) {
1068 debugs(31, 2, "htcpClrStore: failed to parse request headers");
1069 return -1;
1070 }
1071
1072 StoreEntry *e = nullptr;
1073 int released = 0;
1074 /* Lookup matching entries. This matches both GET and HEAD */
1075 while ((e = storeGetPublicByRequest(request.getRaw()))) {
1076 htcpClrStoreEntry(e);
1077 ++released;
1078 }
1079
1080 if (released) {
1081 debugs(31, 4, "htcpClrStore: Cleared " << released << " matching entries");
1082 return 1;
1083 } else {
1084 debugs(31, 4, "htcpClrStore: No matching entry found");
1085 return 0;
1086 }
1087}
1088
1089static void
1090
1091htcpHandleTst(htcpDataHeader * hdr, char *buf, int sz, Ip::Address &from)
1092{
1093 debugs(31, 3, "htcpHandleTst: sz = " << sz);
1094
1095 if (hdr->RR == RR_REQUEST)
1096 htcpHandleTstRequest(hdr, buf, sz, from);
1097 else
1098 htcpHandleTstResponse(hdr, buf, sz, from);
1099}
1100
1101HtcpReplyData::HtcpReplyData() :
1102 hit(0), hdr(hoHtcpReply), msg_id(0), version(0.0)
1103{
1104 memset(&cto, 0, sizeof(cto));
1105}
1106
1107bool
1108HtcpReplyData::parseHeader(const char *buffer, const size_t size)
1109{
1110 Http::ContentLengthInterpreter interpreter;
1111 // no applyStatusCodeRules() -- HTCP replies lack cached HTTP status code
1112 return hdr.parse(buffer, size, interpreter);
1113}
1114
1115static void
1116
1117htcpHandleTstResponse(htcpDataHeader * hdr, char *buf, int sz, Ip::Address &from)
1118{
1119 HtcpReplyData htcpReply;
1120 cache_key *key = nullptr;
1121
1122 Ip::Address *peer;
1123 htcpDetail *d = nullptr;
1124 char *t;
1125
1126 if (queried_id[hdr->msg_id % N_QUERIED_KEYS] != hdr->msg_id) {
1127 debugs(31, 2, "htcpHandleTstResponse: No matching query id '" <<
1128 hdr->msg_id << "' (expected " <<
1129 queried_id[hdr->msg_id % N_QUERIED_KEYS] << ") from '" <<
1130 from << "'");
1131
1132 return;
1133 }
1134
1135 key = queried_keys[hdr->msg_id % N_QUERIED_KEYS];
1136
1137 if (!key) {
1138 debugs(31, 3, "htcpHandleTstResponse: No query key for response id '" << hdr->msg_id << "' from '" << from << "'");
1139 return;
1140 }
1141
1142 peer = &queried_addr[hdr->msg_id % N_QUERIED_KEYS];
1143
1144 if ( *peer != from || peer->port() != from.port() ) {
1145 debugs(31, 3, "htcpHandleTstResponse: Unexpected response source " << from );
1146 return;
1147 }
1148
1149 if (hdr->F1 == 1) {
1150 debugs(31, 2, "htcpHandleTstResponse: error condition, F1/MO == 1");
1151 return;
1152 }
1153
1154 htcpReply.msg_id = hdr->msg_id;
1155 debugs(31, 3, "htcpHandleTstResponse: msg_id = " << htcpReply.msg_id);
1156 htcpReply.hit = hdr->response ? 0 : 1;
1157
1158 if (hdr->F1) {
1159 debugs(31, 3, "htcpHandleTstResponse: MISS");
1160 } else {
1161 debugs(31, 3, "htcpHandleTstResponse: HIT");
1162 d = htcpUnpackDetail(buf, sz);
1163
1164 if (d == nullptr) {
1165 debugs(31, 3, "htcpHandleTstResponse: bad DETAIL");
1166 return;
1167 }
1168
1169 if ((t = d->resp_hdrs))
1170 htcpReply.parseHeader(t, d->respHdrsSz);
1171
1172 if ((t = d->entity_hdrs))
1173 htcpReply.parseHeader(t, d->entityHdrsSz);
1174
1175 if ((t = d->cache_hdrs))
1176 htcpReply.parseHeader(t, d->cacheHdrsSz);
1177 }
1178
1179 debugs(31, 3, "htcpHandleTstResponse: key (" << key << ") " << storeKeyText(key));
1180 neighborsHtcpReply(key, &htcpReply, from);
1181 htcpReply.hdr.clean();
1182
1183 delete d;
1184}
1185
1186static void
1187htcpHandleTstRequest(htcpDataHeader * dhdr, char *buf, int sz, Ip::Address &from)
1188{
1189 if (sz == 0) {
1190 debugs(31, 3, "htcpHandleTst: nothing to do");
1191 return;
1192 }
1193
1194 if (dhdr->F1 == 0)
1195 return;
1196
1197 /* buf should be a SPECIFIER */
1198 htcpSpecifier::Pointer s(htcpUnpackSpecifier(buf, sz));
1199
1200 if (!s) {
1201 debugs(31, 3, "htcpHandleTstRequest: htcpUnpackSpecifier failed");
1202 htcpLogHtcp(from, dhdr->opcode, LOG_UDP_INVALID, dash_str, nullptr);
1203 return;
1204 } else {
1205 s->setFrom(from);
1206 s->setDataHeader(dhdr);
1207 }
1208
1209 if (!s->request) {
1210 debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
1211 htcpLogHtcp(from, dhdr->opcode, LOG_UDP_INVALID, dash_str, s->al);
1212 return;
1213 }
1214
1215 if (!htcpAccessAllowed(Config.accessList.htcp, s, from)) {
1216 debugs(31, 3, "htcpHandleTstRequest: Access denied");
1217 htcpLogHtcp(from, dhdr->opcode, LOG_UDP_DENIED, s->uri, s->al);
1218 return;
1219 }
1220
1221 debugs(31, 2, "HTCP TST request: " << s->method << " " << s->uri << " " << s->version);
1222 debugs(31, 2, "HTCP TST headers: " << s->req_hdrs);
1223 s->checkHit();
1224}
1225
1226void
1227htcpSpecifier::checkedHit(StoreEntry *e)
1228{
1229 if (e) {
1230 htcpTstReply(dhdr, e, this, from); /* hit */
1231 htcpLogHtcp(from, dhdr->opcode, LOG_UDP_HIT, uri, al);
1232 } else {
1233 htcpTstReply(dhdr, nullptr, nullptr, from); /* cache miss */
1234 htcpLogHtcp(from, dhdr->opcode, LOG_UDP_MISS, uri, al);
1235 }
1236}
1237
1238static void
1239htcpHandleClr(htcpDataHeader * hdr, char *buf, int sz, Ip::Address &from)
1240{
1241 /* buf[0/1] is reserved and reason */
1242 if (sz < 2) {
1243 debugs(31, 4, "too short for reserved+reason fields (sz=" << sz << ")");
1244 htcpLogHtcp(from, hdr->opcode, LOG_UDP_INVALID, dash_str, nullptr);
1245 return;
1246 }
1247 int reason = static_cast<unsigned char>(buf[1]) << 4;
1248 debugs(31, 2, "HTCP CLR reason: " << reason);
1249 buf += 2;
1250 sz -= 2;
1251
1252 /* buf should be a SPECIFIER */
1253
1254 if (sz == 0) {
1255 debugs(31, 4, "nothing to do");
1256 htcpLogHtcp(from, hdr->opcode, LOG_UDP_INVALID, dash_str, nullptr);
1257 return;
1258 }
1259
1260 htcpSpecifier::Pointer s(htcpUnpackSpecifier(buf, sz));
1261
1262 if (!s) {
1263 debugs(31, 3, "htcpUnpackSpecifier failed");
1264 htcpLogHtcp(from, hdr->opcode, LOG_UDP_INVALID, dash_str, nullptr);
1265 return;
1266 } else {
1267 s->setFrom(from);
1268 s->setDataHeader(hdr);
1269 }
1270
1271 if (!s->request) {
1272 debugs(31, 3, "failed to parse request");
1273 htcpLogHtcp(from, hdr->opcode, LOG_UDP_INVALID, dash_str, s->al);
1274 return;
1275 }
1276
1277 if (!htcpAccessAllowed(Config.accessList.htcp_clr, s, from)) {
1278 debugs(31, 3, "Access denied");
1279 htcpLogHtcp(from, hdr->opcode, LOG_UDP_DENIED, s->uri, s->al);
1280 return;
1281 }
1282
1283 debugs(31, 2, "HTCP CLR request: " << s->method << " " << s->uri << " " << s->version);
1284 debugs(31, 2, "HTCP CLR headers: " << s->req_hdrs);
1285
1286 /* Release objects from cache
1287 * analog to clientPurgeRequest in client_side.c
1288 */
1289
1290 switch (htcpClrStore(s)) {
1291
1292 case 1:
1293 htcpClrReply(hdr, 1, from); /* hit */
1294 htcpLogHtcp(from, hdr->opcode, LOG_UDP_HIT, s->uri, s->al);
1295 break;
1296
1297 case 0:
1298 htcpClrReply(hdr, 0, from); /* miss */
1299 htcpLogHtcp(from, hdr->opcode, LOG_UDP_MISS, s->uri, s->al);
1300 break;
1301
1302 default:
1303 break;
1304 }
1305}
1306
1307/*
1308 * Forward a CLR request to all peers who have requested that CLRs be
1309 * forwarded to them.
1310 */
1311static void
1312htcpForwardClr(char *buf, int sz)
1313{
1314 for (const auto &p: CurrentCachePeers()) {
1315 if (!p->options.htcp) {
1316 continue;
1317 }
1318 if (!p->options.htcp_forward_clr) {
1319 continue;
1320 }
1321
1322 htcpSend(buf, sz, p->in_addr);
1323 }
1324}
1325
1326/*
1327 * Do the first pass of handling an HTCP message. This used to be two
1328 * separate functions, htcpHandle and htcpHandleData. They were merged to
1329 * allow for forwarding HTCP packets easily to other peers if desired.
1330 *
1331 * This function now works out what type of message we have received and then
1332 * hands it off to other functions to break apart message-specific data.
1333 */
1334static void
1335htcpHandleMsg(char *buf, int sz, Ip::Address &from)
1336{
1337 // TODO: function-scoped CodeContext::Reset(...("HTCP message from", from))
1338
1339 htcpHeader htcpHdr;
1340 htcpDataHeader hdr;
1341 char *hbuf;
1342 int hsz;
1343
1344 if (sz < 0 || (size_t)sz < sizeof(htcpHeader)) {
1345 // These are highly likely to be attack packets. Should probably get a bigger warning.
1346 debugs(31, 2, "htcpHandle: msg size less than htcpHeader size from " << from);
1347 return;
1348 }
1349
1350 htcpHexdump("htcpHandle", buf, sz);
1351 memcpy(&htcpHdr, buf, sizeof(htcpHeader));
1352 htcpHdr.length = ntohs(htcpHdr.length);
1353
1354 if (htcpHdr.minor == 0)
1355 old_squid_format = 1;
1356 else
1357 old_squid_format = 0;
1358
1359 debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr.length);
1360 debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr.major);
1361 debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr.minor);
1362
1363 if (sz != htcpHdr.length) {
1364 debugs(31, 3, "htcpHandle: sz/" << sz << " != htcpHdr.length/" <<
1365 htcpHdr.length << " from " << from );
1366
1367 return;
1368 }
1369
1370 if (htcpHdr.major != 0) {
1371 debugs(31, 3, "htcpHandle: Unknown major version " << htcpHdr.major << " from " << from );
1372
1373 return;
1374 }
1375
1376 hbuf = buf + sizeof(htcpHeader);
1377 hsz = sz - sizeof(htcpHeader);
1378
1379 if ((size_t)hsz < sizeof(htcpDataHeader)) {
1380 debugs(31, 3, "htcpHandleData: msg size less than htcpDataHeader size");
1381 return;
1382 }
1383
1384 if (!old_squid_format) {
1385 memcpy(&hdr, hbuf, sizeof(hdr));
1386 } else {
1387 // Old Squid format (minor==0) uses a wider struct due to bitfield layout.
1388 // Never read more than available; zero-init then copy the safe prefix.
1389 htcpDataHeaderSquid hdrSquid;
1390 memset(&hdrSquid, 0, sizeof(hdrSquid));
1391 if (static_cast<size_t>(hsz) >= sizeof(htcpDataHeaderSquid)) {
1392 memcpy(&hdrSquid, hbuf, sizeof(htcpDataHeaderSquid));
1393 } else {
1394 // Guaranteed earlier: hsz >= sizeof(htcpDataHeader) (compact prefix).
1395 memcpy(&hdrSquid, hbuf, sizeof(htcpDataHeader));
1396 }
1397
1398 hdr.length = hdrSquid.length;
1399 hdr.opcode = hdrSquid.opcode;
1400 hdr.response = hdrSquid.response;
1401 hdr.F1 = hdrSquid.F1;
1402 hdr.RR = hdrSquid.RR;
1403 hdr.reserved = 0;
1404 hdr.msg_id = hdrSquid.msg_id;
1405 }
1406
1407 hdr.length = ntohs(hdr.length);
1408 hdr.msg_id = ntohl(hdr.msg_id);
1409 debugs(31, 3, "htcpHandleData: hsz = " << hsz);
1410 debugs(31, 3, "htcpHandleData: length = " << hdr.length);
1411
1412 if (hdr.opcode >= HTCP_END) {
1413 debugs(31, 3, "htcpHandleData: client " << from << ", opcode " << hdr.opcode << " out of range");
1414 return;
1415 }
1416
1417 debugs(31, 3, "htcpHandleData: opcode = " << hdr.opcode << " " << htcpOpcodeStr[hdr.opcode]);
1418 debugs(31, 3, "htcpHandleData: response = " << hdr.response);
1419 debugs(31, 3, "htcpHandleData: F1 = " << hdr.F1);
1420 debugs(31, 3, "htcpHandleData: RR = " << hdr.RR);
1421 debugs(31, 3, "htcpHandleData: msg_id = " << hdr.msg_id);
1422
1423 // DATA length must include at least the data header itself
1424 if (hdr.length < sizeof(htcpDataHeader)) {
1425 debugs(31, 3, "invalid hdr.length " << hdr.length << " (< " << sizeof(htcpDataHeader) << ")");
1426 return;
1427 }
1428
1429 if (hsz < hdr.length) {
1430 debugs(31, 3, "htcpHandleData: sz < hdr.length");
1431 return;
1432 }
1433
1434 /*
1435 * set sz = hdr.length so we ignore any AUTH fields following
1436 * the DATA.
1437 */
1438 hsz = (int) hdr.length;
1439 hbuf += sizeof(htcpDataHeader);
1440 hsz -= sizeof(htcpDataHeader);
1441 debugs(31, 3, "htcpHandleData: hsz = " << hsz);
1442
1443 htcpHexdump("htcpHandleData", hbuf, hsz);
1444
1445 switch (hdr.opcode) {
1446 case HTCP_NOP:
1447 debugs(31, 3, "HTCP NOP not implemented");
1448 break;
1449 case HTCP_TST:
1450 htcpHandleTst(&hdr, hbuf, hsz, from);
1451 break;
1452 case HTCP_MON:
1453 debugs(31, 3, "HTCP MON not implemented");
1454 break;
1455 case HTCP_SET:
1456 debugs(31, 3, "HTCP SET not implemented");
1457 break;
1458 case HTCP_CLR:
1459 htcpHandleClr(&hdr, hbuf, hsz, from);
1460 htcpForwardClr(buf, sz);
1461 break;
1462 default:
1463 break;
1464 }
1465}
1466
1467static void
1468htcpRecv(int fd, void *)
1469{
1470 static char buf[8192];
1471 int len;
1472 static Ip::Address from;
1473
1474 /* Receive up to 8191 bytes, leaving room for a null */
1475
1476 len = comm_udp_recvfrom(fd, buf, sizeof(buf) - 1, 0, from);
1477
1478 debugs(31, 3, "htcpRecv: FD " << fd << ", " << len << " bytes from " << from );
1479
1480 if (len)
1481 ++statCounter.htcp.pkts_recv;
1482
1483 htcpHandleMsg(buf, len, from);
1484
1485 Comm::SetSelect(fd, COMM_SELECT_READ, htcpRecv, nullptr, 0);
1486}
1487
1488static void
1489htcpOpenPorts()
1490{
1491 if (!IamWorkerProcess())
1492 return;
1493
1494 if (Config.Port.htcp <= 0) {
1495 debugs(31, Important(21), "HTCP Disabled.");
1496 return;
1497 }
1498
1499 htcpIncomingConn = new Comm::Connection;
1500 htcpIncomingConn->local = Config.Addrs.udp_incoming;
1501 htcpIncomingConn->local.port(Config.Port.htcp);
1502
1503 if (!Ip::EnableIpv6 && !htcpIncomingConn->local.setIPv4()) {
1504 debugs(31, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << htcpIncomingConn->local << " is not an IPv4 address.");
1505 fatal("HTCP port cannot be opened.");
1506 }
1507 /* split-stack for now requires default IPv4-only HTCP */
1508 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && htcpIncomingConn->local.isAnyAddr()) {
1509 htcpIncomingConn->local.setIPv4();
1510 }
1511
1512 auto call = asyncCallbackFun(31, 2, htcpIncomingConnectionOpened);
1513 Ipc::StartListening(SOCK_DGRAM,
1514 IPPROTO_UDP,
1515 htcpIncomingConn,
1516 Ipc::fdnInHtcpSocket, call);
1517
1518 if (!Config.Addrs.udp_outgoing.isNoAddr()) {
1519 htcpOutgoingConn = new Comm::Connection;
1520 htcpOutgoingConn->local = Config.Addrs.udp_outgoing;
1521 htcpOutgoingConn->local.port(Config.Port.htcp);
1522
1523 if (!Ip::EnableIpv6 && !htcpOutgoingConn->local.setIPv4()) {
1524 debugs(31, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << htcpOutgoingConn->local << " is not an IPv4 address.");
1525 fatal("HTCP port cannot be opened.");
1526 }
1527 /* split-stack for now requires default IPv4-only HTCP */
1528 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && htcpOutgoingConn->local.isAnyAddr()) {
1529 htcpOutgoingConn->local.setIPv4();
1530 }
1531
1532 enter_suid();
1533 comm_open_listener(SOCK_DGRAM, IPPROTO_UDP, htcpOutgoingConn, "Outgoing HTCP Socket");
1534 leave_suid();
1535
1536 if (!Comm::IsConnOpen(htcpOutgoingConn))
1537 fatal("Cannot open Outgoing HTCP Socket");
1538
1539 Comm::SetSelect(htcpOutgoingConn->fd, COMM_SELECT_READ, htcpRecv, nullptr, 0);
1540
1541 debugs(31, DBG_IMPORTANT, "Sending HTCP messages from " << htcpOutgoingConn->local);
1542 }
1543
1544}
1545
1546static void
1547htcpIncomingConnectionOpened(Ipc::StartListeningAnswer &answer)
1548{
1549 const auto &conn = answer.conn;
1550
1551 if (!Comm::IsConnOpen(conn))
1552 fatal("Cannot open HTCP Socket");
1553
1554 Comm::SetSelect(conn->fd, COMM_SELECT_READ, htcpRecv, nullptr, 0);
1555
1556 debugs(31, DBG_CRITICAL, "Accepting HTCP messages on " << conn->local);
1557
1558 if (Config.Addrs.udp_outgoing.isNoAddr()) {
1559 htcpOutgoingConn = conn;
1560 debugs(31, DBG_IMPORTANT, "Sending HTCP messages from " << htcpOutgoingConn->local);
1561 }
1562}
1563
1564int
1565htcpQuery(StoreEntry * e, HttpRequest * req, CachePeer * p)
1566{
1567 cache_key *save_key;
1568 static char pkt[8192];
1569 ssize_t pktlen;
1570 char vbuf[32];
1571 HttpHeader hdr(hoRequest);
1572 Http::StateFlags flags;
1573
1574 if (!Comm::IsConnOpen(htcpIncomingConn))
1575 return 0;
1576
1577 old_squid_format = p->options.htcp_oldsquid;
1578 snprintf(vbuf, sizeof(vbuf), "%d/%d",
1579 req->http_ver.major, req->http_ver.minor);
1580
1581 htcpStuff stuff(++msg_id_counter, HTCP_TST, RR_REQUEST, 1);
1582 SBuf sb = req->method.image();
1583 stuff.S.method = sb.c_str();
1584 stuff.S.uri = (char *) e->url();
1585 stuff.S.version = vbuf;
1586 HttpStateData::httpBuildRequestHeader(req, e, nullptr, &hdr, p, flags);
1587 MemBuf mb;
1588 mb.init();
1589 hdr.packInto(&mb);
1590 hdr.clean();
1591 stuff.S.req_hdrs = mb.buf;
1592 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
1593 mb.clean();
1594 if (!pktlen) {
1595 debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
1596 return -1;
1597 }
1598
1599 htcpSend(pkt, (int) pktlen, p->in_addr);
1600
1601 queried_id[stuff.msg_id % N_QUERIED_KEYS] = stuff.msg_id;
1602 save_key = queried_keys[stuff.msg_id % N_QUERIED_KEYS];
1603 storeKeyCopy(save_key, (const cache_key *)e->key);
1604 queried_addr[stuff.msg_id % N_QUERIED_KEYS] = p->in_addr;
1605 debugs(31, 3, "htcpQuery: key (" << save_key << ") " << storeKeyText(save_key));
1606
1607 return 1;
1608}
1609
1610/*
1611 * Send an HTCP CLR message for a specified item to a given CachePeer.
1612 */
1613void
1614htcpClear(StoreEntry * e, HttpRequest * req, const HttpRequestMethod &, CachePeer * p, htcp_clr_reason reason)
1615{
1616 static char pkt[8192];
1617 ssize_t pktlen;
1618 char vbuf[32];
1619 HttpHeader hdr(hoRequest);
1620 MemBuf mb;
1621 Http::StateFlags flags;
1622
1623 if (!Comm::IsConnOpen(htcpIncomingConn))
1624 return;
1625
1626 old_squid_format = p->options.htcp_oldsquid;
1627 snprintf(vbuf, sizeof(vbuf), "%d/%d",
1628 req->http_ver.major, req->http_ver.minor);
1629
1630 htcpStuff stuff(++msg_id_counter, HTCP_CLR, RR_REQUEST, 0);
1631 if (reason == HTCP_CLR_INVALIDATION)
1632 stuff.reason = 1;
1633
1634 SBuf sb = req->method.image();
1635 stuff.S.method = sb.c_str();
1636 stuff.S.request = req;
1637 SBuf uri = req->effectiveRequestUri();
1638 stuff.S.uri = uri.c_str();
1639 stuff.S.version = vbuf;
1640 if (reason != HTCP_CLR_INVALIDATION) {
1641 HttpStateData::httpBuildRequestHeader(req, e, nullptr, &hdr, p, flags);
1642 mb.init();
1643 hdr.packInto(&mb);
1644 hdr.clean();
1645 stuff.S.req_hdrs = mb.buf;
1646 } else {
1647 stuff.S.req_hdrs = nullptr;
1648 }
1649 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
1650 if (reason != HTCP_CLR_INVALIDATION) {
1651 mb.clean();
1652 }
1653 if (!pktlen) {
1654 debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
1655 return;
1656 }
1657
1658 htcpSend(pkt, (int) pktlen, p->in_addr);
1659}
1660
1661/*
1662 * htcpSocketShutdown only closes the 'in' socket if it is
1663 * different than the 'out' socket.
1664 */
1665static void
1666htcpSocketShutdown()
1667{
1668 if (!Comm::IsConnOpen(htcpIncomingConn))
1669 return;
1670
1671 debugs(12, DBG_IMPORTANT, "Stop accepting HTCP on " << htcpIncomingConn->local);
1672 /*
1673 * Here we just unlink htcpIncomingConn because the HTCP 'in'
1674 * and 'out' sockets might be just one FD. This prevents this
1675 * function from executing repeatedly. When we are really ready to
1676 * exit or restart, main will comm_close the 'out' descriptor.
1677 */
1678 htcpIncomingConn = nullptr;
1679
1680 /*
1681 * Normally we only write to the outgoing HTCP socket, but
1682 * we also have a read handler there to catch messages sent
1683 * to that specific interface. During shutdown, we must
1684 * disable reading on the outgoing socket.
1685 */
1686 /* XXX Don't we need this handler to read replies while shutting down?
1687 * I think there should be a separate handler for reading replies..
1688 */
1689 assert(Comm::IsConnOpen(htcpOutgoingConn));
1690
1691 Comm::SetSelect(htcpOutgoingConn->fd, COMM_SELECT_READ, nullptr, nullptr, 0);
1692}
1693
1694static void
1695htcpClosePorts()
1696{
1697 if (!IamWorkerProcess())
1698 return;
1699
1700 htcpSocketShutdown();
1701
1702 if (htcpOutgoingConn != nullptr) {
1703 debugs(12, DBG_IMPORTANT, "Stop sending HTCP from " << htcpOutgoingConn->local);
1704 htcpOutgoingConn = nullptr;
1705 }
1706}
1707
1708class HtcpRr : public RegisteredRunner
1709{
1710public:
1711 void useConfig() override { htcpOpenPorts(); }
1712 void startReconfigure() override { htcpClosePorts(); }
1713 void syncConfig() override { htcpOpenPorts(); }
1714 void startShutdown() override { htcpClosePorts(); }
1715};
1716DefineRunnerRegistrator(HtcpRr);
1717
1718static void
1719htcpLogHtcp(Ip::Address &caddr, const int opcode, const LogTags_ot logcode, const char *url, AccessLogEntryPointer al)
1720{
1721 if (!Config.onoff.log_udp)
1722 return;
1723
1724 htcpSyncAle(al, caddr, opcode, url);
1725
1726 assert(logcode != LOG_TAG_NONE);
1727 al->cache.code.update(logcode);
1728
1729 accessLogLog(al, nullptr);
1730}
1731