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