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