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