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