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