]> git.ipfire.org Git - thirdparty/squid.git/blob - src/htcp.cc
Minimize direct comparisons with ACCESS_ALLOWED and ACCESS_DENIED.
[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 const MasterXaction::Pointer mx = new MasterXaction(XactionInitiator::initHtcp);
682 s->request = HttpRequest::FromUrl(s->uri, mx, method == Http::METHOD_NONE ? HttpRequestMethod(Http::METHOD_GET) : method);
683 return s;
684 }
685
686 /*
687 * Unpack an HTCP DETAIL in place
688 * This will overwrite any following AUTH block
689 */
690 static htcpDetail *
691 htcpUnpackDetail(char *buf, int sz)
692 {
693 htcpDetail *d = new htcpDetail;
694
695 /* Find length of RESP-HDRS */
696 uint16_t l = ntohs(*(uint16_t *) buf);
697 sz -= 2;
698 buf += 2;
699
700 if (l > sz) {
701 debugs(31, 3, "htcpUnpackDetail: failed to unpack RESP_HDRS");
702 delete d;
703 return NULL;
704 }
705
706 /* Set RESP-HDRS */
707 d->resp_hdrs = buf;
708 buf += l;
709 d->respHdrsSz = l;
710 sz -= l;
711
712 /* Find length of ENTITY-HDRS */
713 l = ntohs(*(uint16_t *) buf);
714
715 sz -= 2;
716
717 if (l > sz) {
718 debugs(31, 3, "htcpUnpackDetail: failed to unpack ENTITY_HDRS");
719 delete d;
720 return NULL;
721 }
722
723 /* Add terminating null to RESP-HDRS */
724 *buf = '\0';
725
726 /* Set ENTITY-HDRS */
727 buf += 2;
728
729 d->entity_hdrs = buf;
730 buf += l;
731 d->entityHdrsSz = l;
732 sz -= l;
733
734 /* Find length of CACHE-HDRS */
735 l = ntohs(*(uint16_t *) buf);
736
737 sz -= 2;
738
739 if (l > sz) {
740 debugs(31, 3, "htcpUnpackDetail: failed to unpack CACHE_HDRS");
741 delete d;
742 return NULL;
743 }
744
745 /* Add terminating null to ENTITY-HDRS */
746 *buf = '\0';
747
748 /* Set CACHE-HDRS */
749 buf += 2;
750
751 d->cache_hdrs = buf;
752 buf += l;
753 d->cacheHdrsSz = l;
754 sz -= l;
755
756 debugs(31, 3, "htcpUnpackDetail: " << sz << " bytes left");
757
758 /*
759 * Add terminating null to CACHE-HDRS. This is possible because we allocated
760 * an extra byte when we received the packet. This will overwrite any following
761 * AUTH block.
762 */
763 *buf = '\0';
764
765 return d;
766 }
767
768 static bool
769 htcpAccessAllowed(acl_access * acl, const htcpSpecifier::Pointer &s, Ip::Address &from)
770 {
771 /* default deny if no access list present */
772 if (!acl)
773 return false;
774
775 ACLFilledChecklist checklist(acl, s->request.getRaw(), nullptr);
776 checklist.src_addr = from;
777 checklist.my_addr.setNoAddr();
778 return checklist.fastCheck().allowed();
779 }
780
781 static void
782 htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Address &from)
783 {
784 static char pkt[8192];
785 HttpHeader hdr(hoHtcpReply);
786 ssize_t pktlen;
787
788 htcpStuff stuff(dhdr->msg_id, HTCP_TST, RR_RESPONSE, 0);
789 stuff.response = e ? 0 : 1;
790 debugs(31, 3, "htcpTstReply: response = " << stuff.response);
791
792 if (spec) {
793 stuff.S.method = spec->method;
794 stuff.S.uri = spec->uri;
795 stuff.S.version = spec->version;
796 stuff.S.req_hdrs = spec->req_hdrs;
797 stuff.S.reqHdrsSz = spec->reqHdrsSz;
798 if (e)
799 hdr.putInt(Http::HdrType::AGE, (e->timestamp <= squid_curtime ? (squid_curtime - e->timestamp) : 0) );
800 else
801 hdr.putInt(Http::HdrType::AGE, 0);
802 MemBuf mb;
803 mb.init();
804 hdr.packInto(&mb);
805 stuff.D.resp_hdrs = xstrdup(mb.buf);
806 stuff.D.respHdrsSz = mb.contentSize();
807 debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff.D.resp_hdrs << "}");
808 mb.reset();
809 hdr.clean();
810
811 if (e && e->expires > -1)
812 hdr.putTime(Http::HdrType::EXPIRES, e->expires);
813
814 if (e && e->lastModified() > -1)
815 hdr.putTime(Http::HdrType::LAST_MODIFIED, e->lastModified());
816
817 hdr.packInto(&mb);
818
819 stuff.D.entity_hdrs = xstrdup(mb.buf);
820 stuff.D.entityHdrsSz = mb.contentSize();
821
822 debugs(31, 3, "htcpTstReply: entity_hdrs = {" << stuff.D.entity_hdrs << "}");
823
824 mb.reset();
825 hdr.clean();
826
827 #if USE_ICMP
828 if (char *host = urlHostname(spec->uri)) {
829 int rtt = 0;
830 int hops = 0;
831 int samp = 0;
832 netdbHostData(host, &samp, &rtt, &hops);
833
834 if (rtt || hops) {
835 char cto_buf[128];
836 snprintf(cto_buf, 128, "%s %d %f %d",
837 host, samp, 0.001 * rtt, hops);
838 hdr.putExt("Cache-to-Origin", cto_buf);
839 }
840 }
841 #endif /* USE_ICMP */
842
843 hdr.packInto(&mb);
844 stuff.D.cache_hdrs = xstrdup(mb.buf);
845 stuff.D.cacheHdrsSz = mb.contentSize();
846 debugs(31, 3, "htcpTstReply: cache_hdrs = {" << stuff.D.cache_hdrs << "}");
847 mb.clean();
848 hdr.clean();
849 }
850
851 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
852
853 safe_free(stuff.D.resp_hdrs);
854 stuff.D.respHdrsSz = 0;
855 safe_free(stuff.D.entity_hdrs);
856 stuff.D.entityHdrsSz = 0;
857 safe_free(stuff.D.cache_hdrs);
858 stuff.D.cacheHdrsSz = 0;
859
860 if (!pktlen) {
861 debugs(31, 3, "htcpTstReply: htcpBuildPacket() failed");
862 return;
863 }
864
865 htcpSend(pkt, (int) pktlen, from);
866 }
867
868 static void
869
870 htcpClrReply(htcpDataHeader * dhdr, int purgeSucceeded, Ip::Address &from)
871 {
872 static char pkt[8192];
873 ssize_t pktlen;
874
875 /* If dhdr->F1 == 0, no response desired */
876
877 if (dhdr->F1 == 0)
878 return;
879
880 htcpStuff stuff(dhdr->msg_id, HTCP_CLR, RR_RESPONSE, 0);
881
882 stuff.response = purgeSucceeded ? 0 : 2;
883
884 debugs(31, 3, "htcpClrReply: response = " << stuff.response);
885
886 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
887
888 if (pktlen == 0) {
889 debugs(31, 3, "htcpClrReply: htcpBuildPacket() failed");
890 return;
891 }
892
893 htcpSend(pkt, (int) pktlen, from);
894 }
895
896 void
897 htcpSpecifier::checkHit()
898 {
899 checkHitRequest = request;
900
901 if (!checkHitRequest) {
902 debugs(31, 3, "htcpCheckHit: NO; failed to parse URL");
903 checkedHit(NullStoreEntry::getInstance());
904 return;
905 }
906
907 if (!checkHitRequest->header.parse(req_hdrs, reqHdrsSz)) {
908 debugs(31, 3, "htcpCheckHit: NO; failed to parse request headers");
909 checkHitRequest = nullptr;
910 checkedHit(NullStoreEntry::getInstance());
911 return;
912 }
913
914 StoreEntry::getPublicByRequest(this, checkHitRequest.getRaw());
915 }
916
917 void
918 htcpSpecifier::created(StoreEntry *e)
919 {
920 StoreEntry *hit = nullptr;
921
922 if (!e || e->isNull()) {
923 debugs(31, 3, "htcpCheckHit: NO; public object not found");
924 } else if (!e->validToSend()) {
925 debugs(31, 3, "htcpCheckHit: NO; entry not valid to send" );
926 } else if (refreshCheckHTCP(e, checkHitRequest.getRaw())) {
927 debugs(31, 3, "htcpCheckHit: NO; cached response is stale");
928 } else {
929 debugs(31, 3, "htcpCheckHit: YES!?");
930 hit = e;
931 }
932
933 checkedHit(hit);
934 }
935
936 static void
937 htcpClrStoreEntry(StoreEntry * e)
938 {
939 debugs(31, 4, "htcpClrStoreEntry: Clearing store for entry: " << e->url() );
940 e->releaseRequest();
941 }
942
943 static int
944 htcpClrStore(const htcpSpecifier::Pointer &s)
945 {
946 HttpRequestPointer request(s->request);
947 if (!request) {
948 debugs(31, 3, "htcpClrStore: failed to parse URL");
949 return -1;
950 }
951
952 /* Parse request headers */
953 if (!request->header.parse(s->req_hdrs, s->reqHdrsSz)) {
954 debugs(31, 2, "htcpClrStore: failed to parse request headers");
955 return -1;
956 }
957
958 StoreEntry *e = nullptr;
959 int released = 0;
960 /* Lookup matching entries. This matches both GET and HEAD */
961 while ((e = storeGetPublicByRequest(request.getRaw()))) {
962 htcpClrStoreEntry(e);
963 ++released;
964 }
965
966 if (released) {
967 debugs(31, 4, "htcpClrStore: Cleared " << released << " matching entries");
968 return 1;
969 } else {
970 debugs(31, 4, "htcpClrStore: No matching entry found");
971 return 0;
972 }
973 }
974
975 static void
976
977 htcpHandleTst(htcpDataHeader * hdr, char *buf, int sz, Ip::Address &from)
978 {
979 debugs(31, 3, "htcpHandleTst: sz = " << sz);
980
981 if (hdr->RR == RR_REQUEST)
982 htcpHandleTstRequest(hdr, buf, sz, from);
983 else
984 htcpHandleTstResponse(hdr, buf, sz, from);
985 }
986
987 HtcpReplyData::HtcpReplyData() :
988 hit(0), hdr(hoHtcpReply), msg_id(0), version(0.0)
989 {
990 memset(&cto, 0, sizeof(cto));
991 }
992
993 static void
994
995 htcpHandleTstResponse(htcpDataHeader * hdr, char *buf, int sz, Ip::Address &from)
996 {
997 HtcpReplyData htcpReply;
998 cache_key *key = NULL;
999
1000 Ip::Address *peer;
1001 htcpDetail *d = NULL;
1002 char *t;
1003
1004 if (queried_id[hdr->msg_id % N_QUERIED_KEYS] != hdr->msg_id) {
1005 debugs(31, 2, "htcpHandleTstResponse: No matching query id '" <<
1006 hdr->msg_id << "' (expected " <<
1007 queried_id[hdr->msg_id % N_QUERIED_KEYS] << ") from '" <<
1008 from << "'");
1009
1010 return;
1011 }
1012
1013 key = queried_keys[hdr->msg_id % N_QUERIED_KEYS];
1014
1015 if (!key) {
1016 debugs(31, 3, "htcpHandleTstResponse: No query key for response id '" << hdr->msg_id << "' from '" << from << "'");
1017 return;
1018 }
1019
1020 peer = &queried_addr[hdr->msg_id % N_QUERIED_KEYS];
1021
1022 if ( *peer != from || peer->port() != from.port() ) {
1023 debugs(31, 3, "htcpHandleTstResponse: Unexpected response source " << from );
1024 return;
1025 }
1026
1027 if (hdr->F1 == 1) {
1028 debugs(31, 2, "htcpHandleTstResponse: error condition, F1/MO == 1");
1029 return;
1030 }
1031
1032 htcpReply.msg_id = hdr->msg_id;
1033 debugs(31, 3, "htcpHandleTstResponse: msg_id = " << htcpReply.msg_id);
1034 htcpReply.hit = hdr->response ? 0 : 1;
1035
1036 if (hdr->F1) {
1037 debugs(31, 3, "htcpHandleTstResponse: MISS");
1038 } else {
1039 debugs(31, 3, "htcpHandleTstResponse: HIT");
1040 d = htcpUnpackDetail(buf, sz);
1041
1042 if (d == NULL) {
1043 debugs(31, 3, "htcpHandleTstResponse: bad DETAIL");
1044 return;
1045 }
1046
1047 if ((t = d->resp_hdrs))
1048 htcpReply.hdr.parse(t, d->respHdrsSz);
1049
1050 if ((t = d->entity_hdrs))
1051 htcpReply.hdr.parse(t, d->entityHdrsSz);
1052
1053 if ((t = d->cache_hdrs))
1054 htcpReply.hdr.parse(t, d->cacheHdrsSz);
1055 }
1056
1057 debugs(31, 3, "htcpHandleTstResponse: key (" << key << ") " << storeKeyText(key));
1058 neighborsHtcpReply(key, &htcpReply, from);
1059 htcpReply.hdr.clean();
1060
1061 delete d;
1062 }
1063
1064 static void
1065 htcpHandleTstRequest(htcpDataHeader * dhdr, char *buf, int sz, Ip::Address &from)
1066 {
1067 if (sz == 0) {
1068 debugs(31, 3, "htcpHandleTst: nothing to do");
1069 return;
1070 }
1071
1072 if (dhdr->F1 == 0)
1073 return;
1074
1075 /* buf should be a SPECIFIER */
1076 htcpSpecifier::Pointer s(htcpUnpackSpecifier(buf, sz));
1077
1078 if (!s) {
1079 debugs(31, 3, "htcpHandleTstRequest: htcpUnpackSpecifier failed");
1080 htcpLogHtcp(from, dhdr->opcode, LOG_UDP_INVALID, dash_str);
1081 return;
1082 } else {
1083 s->setFrom(from);
1084 s->setDataHeader(dhdr);
1085 }
1086
1087 if (!s->request) {
1088 debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
1089 htcpLogHtcp(from, dhdr->opcode, LOG_UDP_INVALID, dash_str);
1090 return;
1091 }
1092
1093 if (!htcpAccessAllowed(Config.accessList.htcp, s, from)) {
1094 debugs(31, 3, "htcpHandleTstRequest: Access denied");
1095 htcpLogHtcp(from, dhdr->opcode, LOG_UDP_DENIED, s->uri);
1096 return;
1097 }
1098
1099 debugs(31, 2, "HTCP TST request: " << s->method << " " << s->uri << " " << s->version);
1100 debugs(31, 2, "HTCP TST headers: " << s->req_hdrs);
1101 s->checkHit();
1102 }
1103
1104 void
1105 htcpSpecifier::checkedHit(StoreEntry *e)
1106 {
1107 if (e) {
1108 htcpTstReply(dhdr, e, this, from); /* hit */
1109 htcpLogHtcp(from, dhdr->opcode, LOG_UDP_HIT, uri);
1110 } else {
1111 htcpTstReply(dhdr, NULL, NULL, from); /* cache miss */
1112 htcpLogHtcp(from, dhdr->opcode, LOG_UDP_MISS, uri);
1113 }
1114 }
1115
1116 static void
1117 htcpHandleClr(htcpDataHeader * hdr, char *buf, int sz, Ip::Address &from)
1118 {
1119 /* buf[0/1] is reserved and reason */
1120 int reason = buf[1] << 4;
1121 debugs(31, 2, "HTCP CLR reason: " << reason);
1122 buf += 2;
1123 sz -= 2;
1124
1125 /* buf should be a SPECIFIER */
1126
1127 if (sz == 0) {
1128 debugs(31, 4, "htcpHandleClr: nothing to do");
1129 htcpLogHtcp(from, hdr->opcode, LOG_UDP_INVALID, dash_str);
1130 return;
1131 }
1132
1133 htcpSpecifier::Pointer s(htcpUnpackSpecifier(buf, sz));
1134
1135 if (!s) {
1136 debugs(31, 3, "htcpHandleClr: htcpUnpackSpecifier failed");
1137 htcpLogHtcp(from, hdr->opcode, LOG_UDP_INVALID, dash_str);
1138 return;
1139 }
1140
1141 if (!s->request) {
1142 debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
1143 htcpLogHtcp(from, hdr->opcode, LOG_UDP_INVALID, dash_str);
1144 return;
1145 }
1146
1147 if (!htcpAccessAllowed(Config.accessList.htcp_clr, s, from)) {
1148 debugs(31, 3, "htcpHandleClr: Access denied");
1149 htcpLogHtcp(from, hdr->opcode, LOG_UDP_DENIED, s->uri);
1150 return;
1151 }
1152
1153 debugs(31, 2, "HTCP CLR request: " << s->method << " " << s->uri << " " << s->version);
1154 debugs(31, 2, "HTCP CLR headers: " << s->req_hdrs);
1155
1156 /* Release objects from cache
1157 * analog to clientPurgeRequest in client_side.c
1158 */
1159
1160 switch (htcpClrStore(s)) {
1161
1162 case 1:
1163 htcpClrReply(hdr, 1, from); /* hit */
1164 htcpLogHtcp(from, hdr->opcode, LOG_UDP_HIT, s->uri);
1165 break;
1166
1167 case 0:
1168 htcpClrReply(hdr, 0, from); /* miss */
1169 htcpLogHtcp(from, hdr->opcode, LOG_UDP_MISS, s->uri);
1170 break;
1171
1172 default:
1173 break;
1174 }
1175 }
1176
1177 /*
1178 * Forward a CLR request to all peers who have requested that CLRs be
1179 * forwarded to them.
1180 */
1181 static void
1182 htcpForwardClr(char *buf, int sz)
1183 {
1184 CachePeer *p;
1185
1186 for (p = Config.peers; p; p = p->next) {
1187 if (!p->options.htcp) {
1188 continue;
1189 }
1190 if (!p->options.htcp_forward_clr) {
1191 continue;
1192 }
1193
1194 htcpSend(buf, sz, p->in_addr);
1195 }
1196 }
1197
1198 /*
1199 * Do the first pass of handling an HTCP message. This used to be two
1200 * separate functions, htcpHandle and htcpHandleData. They were merged to
1201 * allow for forwarding HTCP packets easily to other peers if desired.
1202 *
1203 * This function now works out what type of message we have received and then
1204 * hands it off to other functions to break apart message-specific data.
1205 */
1206 static void
1207 htcpHandleMsg(char *buf, int sz, Ip::Address &from)
1208 {
1209 htcpHeader htcpHdr;
1210 htcpDataHeader hdr;
1211 char *hbuf;
1212 int hsz;
1213
1214 if (sz < 0 || (size_t)sz < sizeof(htcpHeader)) {
1215 // These are highly likely to be attack packets. Should probably get a bigger warning.
1216 debugs(31, 2, "htcpHandle: msg size less than htcpHeader size from " << from);
1217 return;
1218 }
1219
1220 htcpHexdump("htcpHandle", buf, sz);
1221 memcpy(&htcpHdr, buf, sizeof(htcpHeader));
1222 htcpHdr.length = ntohs(htcpHdr.length);
1223
1224 if (htcpHdr.minor == 0)
1225 old_squid_format = 1;
1226 else
1227 old_squid_format = 0;
1228
1229 debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr.length);
1230 debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr.major);
1231 debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr.minor);
1232
1233 if (sz != htcpHdr.length) {
1234 debugs(31, 3, "htcpHandle: sz/" << sz << " != htcpHdr.length/" <<
1235 htcpHdr.length << " from " << from );
1236
1237 return;
1238 }
1239
1240 if (htcpHdr.major != 0) {
1241 debugs(31, 3, "htcpHandle: Unknown major version " << htcpHdr.major << " from " << from );
1242
1243 return;
1244 }
1245
1246 hbuf = buf + sizeof(htcpHeader);
1247 hsz = sz - sizeof(htcpHeader);
1248
1249 if ((size_t)hsz < sizeof(htcpDataHeader)) {
1250 debugs(31, 3, "htcpHandleData: msg size less than htcpDataHeader size");
1251 return;
1252 }
1253
1254 if (!old_squid_format) {
1255 memcpy(&hdr, hbuf, sizeof(hdr));
1256 } else {
1257 htcpDataHeaderSquid hdrSquid;
1258 memcpy(&hdrSquid, hbuf, sizeof(hdrSquid));
1259 hdr.length = hdrSquid.length;
1260 hdr.opcode = hdrSquid.opcode;
1261 hdr.response = hdrSquid.response;
1262 hdr.F1 = hdrSquid.F1;
1263 hdr.RR = hdrSquid.RR;
1264 hdr.reserved = 0;
1265 hdr.msg_id = hdrSquid.msg_id;
1266 }
1267
1268 hdr.length = ntohs(hdr.length);
1269 hdr.msg_id = ntohl(hdr.msg_id);
1270 debugs(31, 3, "htcpHandleData: hsz = " << hsz);
1271 debugs(31, 3, "htcpHandleData: length = " << hdr.length);
1272
1273 if (hdr.opcode >= HTCP_END) {
1274 debugs(31, 3, "htcpHandleData: client " << from << ", opcode " << hdr.opcode << " out of range");
1275 return;
1276 }
1277
1278 debugs(31, 3, "htcpHandleData: opcode = " << hdr.opcode << " " << htcpOpcodeStr[hdr.opcode]);
1279 debugs(31, 3, "htcpHandleData: response = " << hdr.response);
1280 debugs(31, 3, "htcpHandleData: F1 = " << hdr.F1);
1281 debugs(31, 3, "htcpHandleData: RR = " << hdr.RR);
1282 debugs(31, 3, "htcpHandleData: msg_id = " << hdr.msg_id);
1283
1284 if (hsz < hdr.length) {
1285 debugs(31, 3, "htcpHandleData: sz < hdr.length");
1286 return;
1287 }
1288
1289 /*
1290 * set sz = hdr.length so we ignore any AUTH fields following
1291 * the DATA.
1292 */
1293 hsz = (int) hdr.length;
1294 hbuf += sizeof(htcpDataHeader);
1295 hsz -= sizeof(htcpDataHeader);
1296 debugs(31, 3, "htcpHandleData: hsz = " << hsz);
1297
1298 htcpHexdump("htcpHandleData", hbuf, hsz);
1299
1300 switch (hdr.opcode) {
1301 case HTCP_NOP:
1302 debugs(31, 3, "HTCP NOP not implemented");
1303 break;
1304 case HTCP_TST:
1305 htcpHandleTst(&hdr, hbuf, hsz, from);
1306 break;
1307 case HTCP_MON:
1308 debugs(31, 3, "HTCP MON not implemented");
1309 break;
1310 case HTCP_SET:
1311 debugs(31, 3, "HTCP SET not implemented");
1312 break;
1313 case HTCP_CLR:
1314 htcpHandleClr(&hdr, hbuf, hsz, from);
1315 htcpForwardClr(buf, sz);
1316 break;
1317 default:
1318 break;
1319 }
1320 }
1321
1322 static void
1323 htcpRecv(int fd, void *)
1324 {
1325 static char buf[8192];
1326 int len;
1327 static Ip::Address from;
1328
1329 /* Receive up to 8191 bytes, leaving room for a null */
1330
1331 len = comm_udp_recvfrom(fd, buf, sizeof(buf) - 1, 0, from);
1332
1333 debugs(31, 3, "htcpRecv: FD " << fd << ", " << len << " bytes from " << from );
1334
1335 if (len)
1336 ++statCounter.htcp.pkts_recv;
1337
1338 htcpHandleMsg(buf, len, from);
1339
1340 Comm::SetSelect(fd, COMM_SELECT_READ, htcpRecv, NULL, 0);
1341 }
1342
1343 /*
1344 * ======================================================================
1345 * PUBLIC FUNCTIONS
1346 * ======================================================================
1347 */
1348
1349 void
1350 htcpOpenPorts(void)
1351 {
1352 if (Config.Port.htcp <= 0) {
1353 debugs(31, DBG_IMPORTANT, "HTCP Disabled.");
1354 return;
1355 }
1356
1357 htcpIncomingConn = new Comm::Connection;
1358 htcpIncomingConn->local = Config.Addrs.udp_incoming;
1359 htcpIncomingConn->local.port(Config.Port.htcp);
1360
1361 if (!Ip::EnableIpv6 && !htcpIncomingConn->local.setIPv4()) {
1362 debugs(31, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << htcpIncomingConn->local << " is not an IPv4 address.");
1363 fatal("HTCP port cannot be opened.");
1364 }
1365 /* split-stack for now requires default IPv4-only HTCP */
1366 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && htcpIncomingConn->local.isAnyAddr()) {
1367 htcpIncomingConn->local.setIPv4();
1368 }
1369
1370 AsyncCall::Pointer call = asyncCall(31, 2,
1371 "htcpIncomingConnectionOpened",
1372 Comm::UdpOpenDialer(&htcpIncomingConnectionOpened));
1373
1374 Ipc::StartListening(SOCK_DGRAM,
1375 IPPROTO_UDP,
1376 htcpIncomingConn,
1377 Ipc::fdnInHtcpSocket, call);
1378
1379 if (!Config.Addrs.udp_outgoing.isNoAddr()) {
1380 htcpOutgoingConn = new Comm::Connection;
1381 htcpOutgoingConn->local = Config.Addrs.udp_outgoing;
1382 htcpOutgoingConn->local.port(Config.Port.htcp);
1383
1384 if (!Ip::EnableIpv6 && !htcpOutgoingConn->local.setIPv4()) {
1385 debugs(31, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << htcpOutgoingConn->local << " is not an IPv4 address.");
1386 fatal("HTCP port cannot be opened.");
1387 }
1388 /* split-stack for now requires default IPv4-only HTCP */
1389 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && htcpOutgoingConn->local.isAnyAddr()) {
1390 htcpOutgoingConn->local.setIPv4();
1391 }
1392
1393 enter_suid();
1394 comm_open_listener(SOCK_DGRAM, IPPROTO_UDP, htcpOutgoingConn, "Outgoing HTCP Socket");
1395 leave_suid();
1396
1397 if (!Comm::IsConnOpen(htcpOutgoingConn))
1398 fatal("Cannot open Outgoing HTCP Socket");
1399
1400 Comm::SetSelect(htcpOutgoingConn->fd, COMM_SELECT_READ, htcpRecv, NULL, 0);
1401
1402 debugs(31, DBG_IMPORTANT, "Sending HTCP messages from " << htcpOutgoingConn->local);
1403 }
1404
1405 }
1406
1407 static void
1408 htcpIncomingConnectionOpened(const Comm::ConnectionPointer &conn, int)
1409 {
1410 if (!Comm::IsConnOpen(conn))
1411 fatal("Cannot open HTCP Socket");
1412
1413 Comm::SetSelect(conn->fd, COMM_SELECT_READ, htcpRecv, NULL, 0);
1414
1415 debugs(31, DBG_CRITICAL, "Accepting HTCP messages on " << conn->local);
1416
1417 if (Config.Addrs.udp_outgoing.isNoAddr()) {
1418 htcpOutgoingConn = conn;
1419 debugs(31, DBG_IMPORTANT, "Sending HTCP messages from " << htcpOutgoingConn->local);
1420 }
1421 }
1422
1423 int
1424 htcpQuery(StoreEntry * e, HttpRequest * req, CachePeer * p)
1425 {
1426 cache_key *save_key;
1427 static char pkt[8192];
1428 ssize_t pktlen;
1429 char vbuf[32];
1430 HttpHeader hdr(hoRequest);
1431 Http::StateFlags flags;
1432
1433 if (!Comm::IsConnOpen(htcpIncomingConn))
1434 return 0;
1435
1436 old_squid_format = p->options.htcp_oldsquid;
1437 memset(&flags, '\0', sizeof(flags));
1438 snprintf(vbuf, sizeof(vbuf), "%d/%d",
1439 req->http_ver.major, req->http_ver.minor);
1440
1441 htcpStuff stuff(++msg_id_counter, HTCP_TST, RR_REQUEST, 1);
1442 SBuf sb = req->method.image();
1443 stuff.S.method = sb.c_str();
1444 stuff.S.uri = (char *) e->url();
1445 stuff.S.version = vbuf;
1446 HttpStateData::httpBuildRequestHeader(req, e, NULL, &hdr, flags);
1447 MemBuf mb;
1448 mb.init();
1449 hdr.packInto(&mb);
1450 hdr.clean();
1451 stuff.S.req_hdrs = mb.buf;
1452 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
1453 mb.clean();
1454 if (!pktlen) {
1455 debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
1456 return -1;
1457 }
1458
1459 htcpSend(pkt, (int) pktlen, p->in_addr);
1460
1461 queried_id[stuff.msg_id % N_QUERIED_KEYS] = stuff.msg_id;
1462 save_key = queried_keys[stuff.msg_id % N_QUERIED_KEYS];
1463 storeKeyCopy(save_key, (const cache_key *)e->key);
1464 queried_addr[stuff.msg_id % N_QUERIED_KEYS] = p->in_addr;
1465 debugs(31, 3, "htcpQuery: key (" << save_key << ") " << storeKeyText(save_key));
1466
1467 return 1;
1468 }
1469
1470 /*
1471 * Send an HTCP CLR message for a specified item to a given CachePeer.
1472 */
1473 void
1474 htcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &, CachePeer * p, htcp_clr_reason reason)
1475 {
1476 static char pkt[8192];
1477 ssize_t pktlen;
1478 char vbuf[32];
1479 HttpHeader hdr(hoRequest);
1480 MemBuf mb;
1481 Http::StateFlags flags;
1482
1483 if (!Comm::IsConnOpen(htcpIncomingConn))
1484 return;
1485
1486 old_squid_format = p->options.htcp_oldsquid;
1487 memset(&flags, '\0', sizeof(flags));
1488 snprintf(vbuf, sizeof(vbuf), "%d/%d",
1489 req->http_ver.major, req->http_ver.minor);
1490
1491 htcpStuff stuff(++msg_id_counter, HTCP_CLR, RR_REQUEST, 0);
1492 if (reason == HTCP_CLR_INVALIDATION)
1493 stuff.reason = 1;
1494
1495 SBuf sb = req->method.image();
1496 stuff.S.method = sb.c_str();
1497 if (e == NULL || e->mem_obj == NULL) {
1498 if (uri == NULL) {
1499 return;
1500 }
1501 stuff.S.uri = xstrdup(uri);
1502 } else {
1503 stuff.S.uri = (char *) e->url();
1504 }
1505 stuff.S.version = vbuf;
1506 if (reason != HTCP_CLR_INVALIDATION) {
1507 HttpStateData::httpBuildRequestHeader(req, e, NULL, &hdr, flags);
1508 mb.init();
1509 hdr.packInto(&mb);
1510 hdr.clean();
1511 stuff.S.req_hdrs = mb.buf;
1512 } else {
1513 stuff.S.req_hdrs = NULL;
1514 }
1515 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
1516 if (reason != HTCP_CLR_INVALIDATION) {
1517 mb.clean();
1518 }
1519 if (e == NULL) {
1520 xfree(stuff.S.uri);
1521 }
1522 if (!pktlen) {
1523 debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
1524 return;
1525 }
1526
1527 htcpSend(pkt, (int) pktlen, p->in_addr);
1528 }
1529
1530 /*
1531 * htcpSocketShutdown only closes the 'in' socket if it is
1532 * different than the 'out' socket.
1533 */
1534 void
1535 htcpSocketShutdown(void)
1536 {
1537 if (!Comm::IsConnOpen(htcpIncomingConn))
1538 return;
1539
1540 debugs(12, DBG_IMPORTANT, "Stop accepting HTCP on " << htcpIncomingConn->local);
1541 /*
1542 * Here we just unlink htcpIncomingConn because the HTCP 'in'
1543 * and 'out' sockets might be just one FD. This prevents this
1544 * function from executing repeatedly. When we are really ready to
1545 * exit or restart, main will comm_close the 'out' descriptor.
1546 */
1547 htcpIncomingConn = NULL;
1548
1549 /*
1550 * Normally we only write to the outgoing HTCP socket, but
1551 * we also have a read handler there to catch messages sent
1552 * to that specific interface. During shutdown, we must
1553 * disable reading on the outgoing socket.
1554 */
1555 /* XXX Don't we need this handler to read replies while shutting down?
1556 * I think there should be a separate hander for reading replies..
1557 */
1558 assert(Comm::IsConnOpen(htcpOutgoingConn));
1559
1560 Comm::SetSelect(htcpOutgoingConn->fd, COMM_SELECT_READ, NULL, NULL, 0);
1561 }
1562
1563 void
1564 htcpClosePorts(void)
1565 {
1566 htcpSocketShutdown();
1567
1568 if (htcpOutgoingConn != NULL) {
1569 debugs(12, DBG_IMPORTANT, "Stop sending HTCP from " << htcpOutgoingConn->local);
1570 htcpOutgoingConn = NULL;
1571 }
1572 }
1573
1574 static void
1575 htcpLogHtcp(Ip::Address &caddr, int opcode, LogTags logcode, const char *url)
1576 {
1577 AccessLogEntry::Pointer al = new AccessLogEntry;
1578 if (LOG_TAG_NONE == logcode.oldType)
1579 return;
1580 if (!Config.onoff.log_udp)
1581 return;
1582 al->htcp.opcode = htcpOpcodeStr[opcode];
1583 al->url = url;
1584 al->cache.caddr = caddr;
1585 al->cache.code = logcode;
1586 al->cache.trTime.tv_sec = 0;
1587 al->cache.trTime.tv_usec = 0;
1588 accessLogLog(al, NULL);
1589 }
1590