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