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