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