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