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