]> git.ipfire.org Git - thirdparty/squid.git/blob - src/htcp.cc
Polish: update Ip::Address to follow Squid coding guidelines
[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_key_md5.h"
58 #include "Store.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 char *method;
172 char *uri;
173 char *version;
174 char *req_hdrs;
175 HttpRequest *request;
176
177 private:
178 HttpRequest *checkHitRequest;
179
180 Ip::Address from; // was a ptr. return to such IFF needed. otherwise copy should do.
181 htcpDataHeader *dhdr;
182 };
183
184 MEMPROXY_CLASS_INLINE(htcpSpecifier);
185
186 struct _htcpDetail {
187 char *resp_hdrs;
188 char *entity_hdrs;
189 char *cache_hdrs;
190 };
191
192 struct _htcpStuff {
193 int op;
194 int rr;
195 int f1;
196 int response;
197 int reason;
198 uint32_t msg_id;
199 htcpSpecifier S;
200 htcpDetail D;
201 };
202
203 enum {
204 HTCP_NOP,
205 HTCP_TST,
206 HTCP_MON,
207 HTCP_SET,
208 HTCP_CLR,
209 HTCP_END
210 };
211
212 static const char *const htcpOpcodeStr[] = {
213 "HTCP_NOP",
214 "HTCP_TST",
215 "HTCP_MON",
216 "HTCP_SET",
217 "HTCP_CLR",
218 "HTCP_END"
219 };
220
221 /*
222 * values for htcpDataHeader->response
223 */
224 enum {
225 AUTH_REQUIRED,
226 AUTH_FAILURE,
227 OPCODE_UNIMPLEMENTED,
228 MAJOR_VERSION_UNSUPPORTED,
229 MINOR_VERSION_UNSUPPORTED,
230 INVALID_OPCODE
231 };
232
233 /*
234 * values for htcpDataHeader->RR
235 */
236 enum {
237 RR_REQUEST,
238 RR_RESPONSE
239 };
240
241 static void htcpIncomingConnectionOpened(const Comm::ConnectionPointer &conn, int errNo);
242 static uint32_t msg_id_counter = 0;
243
244 static Comm::ConnectionPointer htcpOutgoingConn = NULL;
245 static Comm::ConnectionPointer htcpIncomingConn = NULL;
246 #define N_QUERIED_KEYS 8192
247 static uint32_t queried_id[N_QUERIED_KEYS];
248 static cache_key queried_keys[N_QUERIED_KEYS][SQUID_MD5_DIGEST_LENGTH];
249
250 static Ip::Address queried_addr[N_QUERIED_KEYS];
251 static MemAllocator *htcpDetailPool = NULL;
252
253 static int old_squid_format = 0;
254
255 static ssize_t htcpBuildPacket(char *buf, size_t buflen, htcpStuff * stuff);
256 static htcpSpecifier *htcpUnpackSpecifier(char *buf, int sz);
257 static htcpDetail *htcpUnpackDetail(char *buf, int sz);
258 static ssize_t htcpBuildAuth(char *buf, size_t buflen);
259 static ssize_t htcpBuildCountstr(char *buf, size_t buflen, const char *s);
260 static ssize_t htcpBuildData(char *buf, size_t buflen, htcpStuff * stuff);
261 static ssize_t htcpBuildDetail(char *buf, size_t buflen, htcpStuff * stuff);
262 static ssize_t htcpBuildOpData(char *buf, size_t buflen, htcpStuff * stuff);
263 static ssize_t htcpBuildSpecifier(char *buf, size_t buflen, htcpStuff * stuff);
264 static ssize_t htcpBuildTstOpData(char *buf, size_t buflen, htcpStuff * stuff);
265 static void htcpFreeSpecifier(htcpSpecifier * s);
266 static void htcpFreeDetail(htcpDetail * s);
267
268 static void htcpHandleMsg(char *buf, int sz, Ip::Address &from);
269
270 static void htcpLogHtcp(Ip::Address &, int, LogTags, const char *);
271 static void htcpHandleMon(htcpDataHeader *, char *buf, int sz, Ip::Address &from);
272
273 static void htcpHandleNop(htcpDataHeader *, char *buf, int sz, Ip::Address &from);
274
275 static void htcpHandleSet(htcpDataHeader *, char *buf, int sz, Ip::Address &from);
276
277 static void htcpHandleTst(htcpDataHeader *, char *buf, int sz, Ip::Address &from);
278
279 static void htcpRecv(int fd, void *data);
280
281 static void htcpSend(const char *buf, int len, Ip::Address &to);
282
283 static void htcpTstReply(htcpDataHeader *, StoreEntry *, htcpSpecifier *, Ip::Address &);
284
285 static void htcpHandleTstRequest(htcpDataHeader *, char *buf, int sz, Ip::Address &from);
286
287 static void htcpHandleTstResponse(htcpDataHeader *, char *, int, Ip::Address &);
288
289 static void
290 htcpHexdump(const char *tag, const char *s, int sz)
291 {
292 #if USE_HEXDUMP
293 int i;
294 int k;
295 char hex[80];
296 debugs(31, 3, "htcpHexdump " << tag);
297 memset(hex, '\0', 80);
298
299 for (i = 0; i < sz; ++i) {
300 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', 80);
309 }
310
311 #endif
312 }
313
314 /*
315 * STUFF FOR SENDING HTCP MESSAGES
316 */
317
318 static ssize_t
319 htcpBuildAuth(char *buf, size_t buflen)
320 {
321 htcpAuthHeader auth;
322 size_t copy_sz = 0;
323 assert(2 == sizeof(uint16_t));
324 auth.length = htons(2);
325 copy_sz += 2;
326 if (buflen < copy_sz)
327 return -1;
328 memcpy(buf, &auth, copy_sz);
329 return copy_sz;
330 }
331
332 static ssize_t
333 htcpBuildCountstr(char *buf, size_t buflen, const char *s)
334 {
335 uint16_t length;
336 size_t len;
337 int off = 0;
338
339 if (buflen - off < 2)
340 return -1;
341
342 if (s)
343 len = strlen(s);
344 else
345 len = 0;
346
347 debugs(31, 3, "htcpBuildCountstr: LENGTH = " << len);
348
349 debugs(31, 3, "htcpBuildCountstr: TEXT = {" << (s ? s : "<NULL>") << "}");
350
351 length = htons((uint16_t) len);
352
353 memcpy(buf + off, &length, 2);
354
355 off += 2;
356
357 if (buflen - off < len)
358 return -1;
359
360 if (len)
361 memcpy(buf + off, s, len);
362
363 off += len;
364
365 return off;
366 }
367
368 static ssize_t
369 htcpBuildSpecifier(char *buf, size_t buflen, htcpStuff * stuff)
370 {
371 ssize_t off = 0;
372 ssize_t s;
373 s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.method);
374
375 if (s < 0)
376 return s;
377
378 off += s;
379
380 s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.uri);
381
382 if (s < 0)
383 return s;
384
385 off += s;
386
387 s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.version);
388
389 if (s < 0)
390 return s;
391
392 off += s;
393
394 s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.req_hdrs);
395
396 if (s < 0)
397 return s;
398
399 off += s;
400
401 debugs(31, 3, "htcpBuildSpecifier: size " << off);
402
403 return off;
404 }
405
406 static ssize_t
407 htcpBuildDetail(char *buf, size_t buflen, htcpStuff * stuff)
408 {
409 ssize_t off = 0;
410 ssize_t s;
411 s = htcpBuildCountstr(buf + off, buflen - off, stuff->D.resp_hdrs);
412
413 if (s < 0)
414 return s;
415
416 off += s;
417
418 s = htcpBuildCountstr(buf + off, buflen - off, stuff->D.entity_hdrs);
419
420 if (s < 0)
421 return s;
422
423 off += s;
424
425 s = htcpBuildCountstr(buf + off, buflen - off, stuff->D.cache_hdrs);
426
427 if (s < 0)
428 return s;
429
430 off += s;
431
432 return off;
433 }
434
435 static ssize_t
436 htcpBuildTstOpData(char *buf, size_t buflen, htcpStuff * stuff)
437 {
438 switch (stuff->rr) {
439
440 case RR_REQUEST:
441 debugs(31, 3, "htcpBuildTstOpData: RR_REQUEST");
442 return htcpBuildSpecifier(buf, buflen, stuff);
443
444 case RR_RESPONSE:
445 debugs(31, 3, "htcpBuildTstOpData: RR_RESPONSE");
446 debugs(31, 3, "htcpBuildTstOpData: F1 = " << stuff->f1);
447
448 if (stuff->f1) /* cache miss */
449 return 0;
450 else /* cache hit */
451 return htcpBuildDetail(buf, buflen, stuff);
452
453 default:
454 fatal_dump("htcpBuildTstOpData: bad RR value");
455 }
456
457 return 0;
458 }
459
460 static ssize_t
461 htcpBuildClrOpData(char *buf, size_t buflen, htcpStuff * stuff)
462 {
463 unsigned short reason;
464
465 switch (stuff->rr) {
466 case RR_REQUEST:
467 debugs(31, 3, "htcpBuildClrOpData: RR_REQUEST");
468 reason = htons((unsigned short)stuff->reason);
469 memcpy(buf, &reason, 2);
470 return htcpBuildSpecifier(buf + 2, buflen - 2, stuff) + 2;
471 case RR_RESPONSE:
472 break;
473 default:
474 fatal_dump("htcpBuildClrOpData: bad RR value");
475 }
476
477 return 0;
478 }
479
480 static ssize_t
481 htcpBuildOpData(char *buf, size_t buflen, htcpStuff * stuff)
482 {
483 ssize_t off = 0;
484 debugs(31, 3, "htcpBuildOpData: opcode " << htcpOpcodeStr[stuff->op]);
485
486 switch (stuff->op) {
487
488 case HTCP_TST:
489 off = htcpBuildTstOpData(buf + off, buflen, stuff);
490 break;
491
492 case HTCP_CLR:
493 off = htcpBuildClrOpData(buf + off, buflen, stuff);
494 break;
495
496 default:
497 assert(0);
498 break;
499 }
500
501 return off;
502 }
503
504 static ssize_t
505 htcpBuildData(char *buf, size_t buflen, htcpStuff * stuff)
506 {
507 ssize_t off = 0;
508 ssize_t op_data_sz;
509 size_t hdr_sz = sizeof(htcpDataHeader);
510 htcpDataHeader hdr;
511
512 if (buflen < hdr_sz)
513 return -1;
514
515 off += hdr_sz; /* skip! */
516
517 op_data_sz = htcpBuildOpData(buf + off, buflen - off, stuff);
518
519 if (op_data_sz < 0)
520 return op_data_sz;
521
522 off += op_data_sz;
523
524 debugs(31, 3, "htcpBuildData: hdr.length = " << off);
525
526 hdr.length = (uint16_t) off;
527
528 hdr.opcode = stuff->op;
529
530 hdr.response = stuff->response;
531
532 hdr.RR = stuff->rr;
533
534 hdr.F1 = stuff->f1;
535
536 hdr.msg_id = stuff->msg_id;
537
538 /* convert multi-byte fields */
539 hdr.length = htons(hdr.length);
540
541 hdr.msg_id = htonl(hdr.msg_id);
542
543 if (!old_squid_format) {
544 memcpy(buf, &hdr, hdr_sz);
545 } else {
546 htcpDataHeaderSquid hdrSquid;
547 memset(&hdrSquid, 0, sizeof(hdrSquid));
548 hdrSquid.length = hdr.length;
549 hdrSquid.opcode = hdr.opcode;
550 hdrSquid.response = hdr.response;
551 hdrSquid.F1 = hdr.F1;
552 hdrSquid.RR = hdr.RR;
553 memcpy(buf, &hdrSquid, hdr_sz);
554 }
555
556 debugs(31, 3, "htcpBuildData: size " << off);
557
558 return off;
559 }
560
561 /*
562 * Build an HTCP packet into buf, maximum length buflen.
563 * Returns the packet length, or zero on failure.
564 */
565 static ssize_t
566 htcpBuildPacket(char *buf, size_t buflen, htcpStuff * stuff)
567 {
568 ssize_t s;
569 ssize_t off = 0;
570 size_t hdr_sz = sizeof(htcpHeader);
571 htcpHeader hdr;
572 /* skip the header -- we don't know the overall length */
573
574 if (buflen < hdr_sz) {
575 return 0;
576 }
577
578 off += hdr_sz;
579 s = htcpBuildData(buf + off, buflen - off, stuff);
580
581 if (s < 0) {
582 return 0;
583 }
584
585 off += s;
586 s = htcpBuildAuth(buf + off, buflen - off);
587
588 if (s < 0) {
589 return 0;
590 }
591
592 off += s;
593 hdr.length = htons((uint16_t) off);
594 hdr.major = 0;
595
596 if (old_squid_format)
597 hdr.minor = 0;
598 else
599 hdr.minor = 1;
600
601 memcpy(buf, &hdr, hdr_sz);
602
603 debugs(31, 3, "htcpBuildPacket: size " << off);
604
605 return off;
606 }
607
608 static void
609 htcpSend(const char *buf, int len, Ip::Address &to)
610 {
611 debugs(31, 3, HERE << to);
612 htcpHexdump("htcpSend", buf, len);
613
614 if (comm_udp_sendto(htcpOutgoingConn->fd, to, buf, len) < 0)
615 debugs(31, 3, HERE << htcpOutgoingConn << " sendto: " << xstrerror());
616 else
617 ++statCounter.htcp.pkts_sent;
618 }
619
620 /*
621 * STUFF FOR RECEIVING HTCP MESSAGES
622 */
623
624 void
625 htcpSpecifier::setFrom(Ip::Address &aSocket)
626 {
627 from = aSocket;
628 }
629
630 void
631 htcpSpecifier::setDataHeader(htcpDataHeader *aDataHeader)
632 {
633 dhdr = aDataHeader;
634 }
635
636 static void
637 htcpFreeSpecifier(htcpSpecifier * s)
638 {
639 HTTPMSGUNLOCK(s->request);
640
641 delete s;
642 }
643
644 static void
645 htcpFreeDetail(htcpDetail * d)
646 {
647 htcpDetailPool->freeOne(d);
648 }
649
650 /*
651 * Unpack an HTCP SPECIFIER in place
652 * This will overwrite any following AUTH block
653 */
654 static htcpSpecifier *
655 htcpUnpackSpecifier(char *buf, int sz)
656 {
657 htcpSpecifier *s = new htcpSpecifier;
658 HttpRequestMethod method;
659
660 /* Find length of METHOD */
661 uint16_t l = ntohs(*(uint16_t *) buf);
662 sz -= 2;
663 buf += 2;
664
665 if (l > sz) {
666 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack METHOD");
667 htcpFreeSpecifier(s);
668 return NULL;
669 }
670
671 /* Set METHOD */
672 s->method = buf;
673 buf += l;
674 sz -= l;
675 debugs(31, 6, "htcpUnpackSpecifier: METHOD (" << l << "/" << sz << ") '" << s->method << "'");
676
677 /* Find length of URI */
678 l = ntohs(*(uint16_t *) buf);
679 sz -= 2;
680
681 if (l > sz) {
682 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack URI");
683 htcpFreeSpecifier(s);
684 return NULL;
685 }
686
687 /* Add terminating null to METHOD */
688 *buf = '\0';
689 buf += 2;
690
691 /* Set URI */
692 s->uri = buf;
693 buf += l;
694 sz -= l;
695 debugs(31, 6, "htcpUnpackSpecifier: URI (" << l << "/" << sz << ") '" << s->uri << "'");
696
697 /* Find length of VERSION */
698 l = ntohs(*(uint16_t *) buf);
699 sz -= 2;
700
701 if (l > sz) {
702 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack VERSION");
703 htcpFreeSpecifier(s);
704 return NULL;
705 }
706
707 /* Add terminating null to URI */
708 *buf = '\0';
709 buf += 2;
710
711 /* Set VERSION */
712 s->version = buf;
713 buf += l;
714 sz -= l;
715 debugs(31, 6, "htcpUnpackSpecifier: VERSION (" << l << "/" << sz << ") '" << s->version << "'");
716
717 /* Find length of REQ-HDRS */
718 l = ntohs(*(uint16_t *) buf);
719 sz -= 2;
720
721 if (l > sz) {
722 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack REQ-HDRS");
723 htcpFreeSpecifier(s);
724 return NULL;
725 }
726
727 /* Add terminating null to URI */
728 *buf = '\0';
729 buf += 2;
730
731 /* Set REQ-HDRS */
732 s->req_hdrs = buf;
733 buf += l;
734 sz -= l;
735 debugs(31, 6, "htcpUnpackSpecifier: REQ-HDRS (" << l << "/" << sz << ") '" << s->req_hdrs << "'");
736
737 debugs(31, 3, "htcpUnpackSpecifier: " << sz << " bytes left");
738
739 /*
740 * Add terminating null to REQ-HDRS. This is possible because we allocated
741 * an extra byte when we received the packet. This will overwrite any following
742 * AUTH block.
743 */
744 *buf = '\0';
745
746 /*
747 * Parse the request
748 */
749 method = HttpRequestMethod(s->method, NULL);
750
751 s->request = HttpRequest::CreateFromUrlAndMethod(s->uri, method == Http::METHOD_NONE ? HttpRequestMethod(Http::METHOD_GET) : method);
752
753 if (s->request)
754 HTTPMSGLOCK(s->request);
755
756 return s;
757 }
758
759 /*
760 * Unpack an HTCP DETAIL in place
761 * This will overwrite any following AUTH block
762 */
763 static htcpDetail *
764 htcpUnpackDetail(char *buf, int sz)
765 {
766 htcpDetail *d = static_cast<htcpDetail *>(htcpDetailPool->alloc());
767
768 /* Find length of RESP-HDRS */
769 uint16_t l = ntohs(*(uint16_t *) buf);
770 sz -= 2;
771 buf += 2;
772
773 if (l > sz) {
774 debugs(31, 3, "htcpUnpackDetail: failed to unpack RESP_HDRS");
775 htcpFreeDetail(d);
776 return NULL;
777 }
778
779 /* Set RESP-HDRS */
780 d->resp_hdrs = buf;
781
782 buf += l;
783
784 sz -= l;
785
786 /* Find length of ENTITY-HDRS */
787 l = ntohs(*(uint16_t *) buf);
788
789 sz -= 2;
790
791 if (l > sz) {
792 debugs(31, 3, "htcpUnpackDetail: failed to unpack ENTITY_HDRS");
793 htcpFreeDetail(d);
794 return NULL;
795 }
796
797 /* Add terminating null to RESP-HDRS */
798 *buf = '\0';
799
800 /* Set ENTITY-HDRS */
801 buf += 2;
802
803 d->entity_hdrs = buf;
804
805 buf += l;
806
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
828 buf += l;
829
830 sz -= l;
831
832 debugs(31, 3, "htcpUnpackDetail: " << sz << " bytes left");
833
834 /*
835 * Add terminating null to CACHE-HDRS. This is possible because we allocated
836 * an extra byte when we received the packet. This will overwrite any following
837 * AUTH block.
838 */
839 *buf = '\0';
840
841 return d;
842 }
843
844 static bool
845 htcpAccessAllowed(acl_access * acl, htcpSpecifier * s, Ip::Address &from)
846 {
847 /* default deny if no access list present */
848 if (!acl)
849 return false;
850
851 ACLFilledChecklist checklist(acl, s->request, NULL);
852 checklist.src_addr = from;
853 checklist.my_addr.setNoAddr();
854 return (checklist.fastCheck() == ACCESS_ALLOWED);
855 }
856
857 static void
858 htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Address &from)
859 {
860 htcpStuff stuff;
861 static char pkt[8192];
862 HttpHeader hdr(hoHtcpReply);
863 MemBuf mb;
864 Packer p;
865 ssize_t pktlen;
866 memset(&stuff, '\0', sizeof(stuff));
867 stuff.op = HTCP_TST;
868 stuff.rr = RR_RESPONSE;
869 stuff.f1 = 0;
870 stuff.response = e ? 0 : 1;
871 debugs(31, 3, "htcpTstReply: response = " << stuff.response);
872 stuff.msg_id = dhdr->msg_id;
873
874 if (spec) {
875 mb.init();
876 packerToMemInit(&p, &mb);
877 stuff.S.method = spec->method;
878 stuff.S.uri = spec->uri;
879 stuff.S.version = spec->version;
880 stuff.S.req_hdrs = spec->req_hdrs;
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 debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff.D.resp_hdrs << "}");
888 mb.reset();
889 hdr.reset();
890
891 if (e && e->expires > -1)
892 hdr.putTime(HDR_EXPIRES, e->expires);
893
894 if (e && e->lastmod > -1)
895 hdr.putTime(HDR_LAST_MODIFIED, e->lastmod);
896
897 hdr.packInto(&p);
898
899 stuff.D.entity_hdrs = xstrdup(mb.buf);
900
901 debugs(31, 3, "htcpTstReply: entity_hdrs = {" << stuff.D.entity_hdrs << "}");
902
903 mb.reset();
904
905 hdr.reset();
906
907 #if USE_ICMP
908 if (char *host = urlHostname(spec->uri)) {
909 int rtt = 0;
910 int hops = 0;
911 int samp = 0;
912 netdbHostData(host, &samp, &rtt, &hops);
913
914 if (rtt || hops) {
915 char cto_buf[128];
916 snprintf(cto_buf, 128, "%s %d %f %d",
917 host, samp, 0.001 * rtt, hops);
918 hdr.putExt("Cache-to-Origin", cto_buf);
919 }
920 }
921 #endif /* USE_ICMP */
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 debugs(31, 3, "htcpTstReply: htcpBuildPacket() failed");
939 return;
940 }
941
942 htcpSend(pkt, (int) pktlen, from);
943 }
944
945 static void
946
947 htcpClrReply(htcpDataHeader * dhdr, int purgeSucceeded, Ip::Address &from)
948 {
949 htcpStuff stuff;
950 static char pkt[8192];
951 ssize_t pktlen;
952
953 /* If dhdr->F1 == 0, no response desired */
954
955 if (dhdr->F1 == 0)
956 return;
957
958 memset(&stuff, '\0', sizeof(stuff));
959
960 stuff.op = HTCP_CLR;
961
962 stuff.rr = RR_RESPONSE;
963
964 stuff.f1 = 0;
965
966 stuff.response = purgeSucceeded ? 0 : 2;
967
968 debugs(31, 3, "htcpClrReply: response = " << stuff.response);
969
970 stuff.msg_id = dhdr->msg_id;
971
972 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
973
974 if (pktlen == 0) {
975 debugs(31, 3, "htcpClrReply: htcpBuildPacket() failed");
976 return;
977 }
978
979 htcpSend(pkt, (int) pktlen, from);
980 }
981
982 static void
983
984 htcpHandleNop(htcpDataHeader * hdr, char *buf, int sz, Ip::Address &from)
985 {
986 debugs(31, 3, "htcpHandleNop: Unimplemented");
987 }
988
989 void
990 htcpSpecifier::checkHit()
991 {
992 char *blk_end;
993 checkHitRequest = request;
994
995 if (NULL == checkHitRequest) {
996 debugs(31, 3, "htcpCheckHit: NO; failed to parse URL");
997 checkedHit(NullStoreEntry::getInstance());
998 return;
999 }
1000
1001 blk_end = req_hdrs + strlen(req_hdrs);
1002
1003 if (!checkHitRequest->header.parse(req_hdrs, blk_end)) {
1004 debugs(31, 3, "htcpCheckHit: NO; failed to parse request headers");
1005 delete checkHitRequest;
1006 checkHitRequest = NULL;
1007 checkedHit(NullStoreEntry::getInstance());
1008 return;
1009 }
1010
1011 StoreEntry::getPublicByRequest(this, checkHitRequest);
1012 }
1013
1014 void
1015 htcpSpecifier::created (StoreEntry *e)
1016 {
1017 StoreEntry *hit=NULL;
1018 assert (e);
1019
1020 if (e->isNull()) {
1021 debugs(31, 3, "htcpCheckHit: NO; public object not found");
1022 } else if (!e->validToSend()) {
1023 debugs(31, 3, "htcpCheckHit: NO; entry not valid to send" );
1024 } else if (refreshCheckHTCP(e, checkHitRequest)) {
1025 debugs(31, 3, "htcpCheckHit: NO; cached response is stale");
1026 } else {
1027 debugs(31, 3, "htcpCheckHit: YES!?");
1028 hit = e;
1029 }
1030
1031 checkedHit (hit);
1032 }
1033
1034 static void
1035 htcpClrStoreEntry(StoreEntry * e)
1036 {
1037 debugs(31, 4, "htcpClrStoreEntry: Clearing store for entry: " << e->url() );
1038 e->releaseRequest();
1039 }
1040
1041 static int
1042 htcpClrStore(const htcpSpecifier * s)
1043 {
1044 HttpRequest *request = s->request;
1045 char *blk_end;
1046 StoreEntry *e = NULL;
1047 int released = 0;
1048
1049 if (request == NULL) {
1050 debugs(31, 3, "htcpClrStore: failed to parse URL");
1051 return -1;
1052 }
1053
1054 /* Parse request headers */
1055 blk_end = s->req_hdrs + strlen(s->req_hdrs);
1056
1057 if (!request->header.parse(s->req_hdrs, blk_end)) {
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, t + strlen(t));
1151
1152 if ((t = d->entity_hdrs))
1153 htcpReply.hdr.parse(t, t + strlen(t));
1154
1155 if ((t = d->cache_hdrs))
1156 htcpReply.hdr.parse(t, t + strlen(t));
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 stuff.S.method = (char *) RequestMethodStr(req->method);
1581 stuff.S.uri = (char *) e->url();
1582 stuff.S.version = vbuf;
1583 HttpStateData::httpBuildRequestHeader(req, e, NULL, &hdr, flags);
1584 mb.init();
1585 packerToMemInit(&pa, &mb);
1586 hdr.packInto(&pa);
1587 hdr.clean();
1588 packerClean(&pa);
1589 stuff.S.req_hdrs = mb.buf;
1590 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
1591 mb.clean();
1592 if (!pktlen) {
1593 debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
1594 return -1;
1595 }
1596
1597 htcpSend(pkt, (int) pktlen, p->in_addr);
1598
1599 queried_id[stuff.msg_id % N_QUERIED_KEYS] = stuff.msg_id;
1600 save_key = queried_keys[stuff.msg_id % N_QUERIED_KEYS];
1601 storeKeyCopy(save_key, (const cache_key *)e->key);
1602 queried_addr[stuff.msg_id % N_QUERIED_KEYS] = p->in_addr;
1603 debugs(31, 3, "htcpQuery: key (" << save_key << ") " << storeKeyText(save_key));
1604
1605 return 1;
1606 }
1607
1608 /*
1609 * Send an HTCP CLR message for a specified item to a given CachePeer.
1610 */
1611 void
1612 htcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, CachePeer * p, htcp_clr_reason reason)
1613 {
1614 static char pkt[8192];
1615 ssize_t pktlen;
1616 char vbuf[32];
1617 htcpStuff stuff;
1618 HttpHeader hdr(hoRequest);
1619 Packer pa;
1620 MemBuf mb;
1621 HttpStateFlags flags;
1622
1623 if (!Comm::IsConnOpen(htcpIncomingConn))
1624 return;
1625
1626 old_squid_format = p->options.htcp_oldsquid;
1627 memset(&flags, '\0', sizeof(flags));
1628 snprintf(vbuf, sizeof(vbuf), "%d/%d",
1629 req->http_ver.major, req->http_ver.minor);
1630 stuff.op = HTCP_CLR;
1631 stuff.rr = RR_REQUEST;
1632 stuff.f1 = 0;
1633 stuff.response = 0;
1634 stuff.msg_id = ++msg_id_counter;
1635 switch (reason) {
1636 case HTCP_CLR_INVALIDATION:
1637 stuff.reason = 1;
1638 break;
1639 default:
1640 stuff.reason = 0;
1641 break;
1642 }
1643 stuff.S.method = (char *) RequestMethodStr(req->method);
1644 if (e == NULL || e->mem_obj == NULL) {
1645 if (uri == NULL) {
1646 return;
1647 }
1648 stuff.S.uri = xstrdup(uri);
1649 } else {
1650 stuff.S.uri = (char *) e->url();
1651 }
1652 stuff.S.version = vbuf;
1653 if (reason != HTCP_CLR_INVALIDATION) {
1654 HttpStateData::httpBuildRequestHeader(req, e, NULL, &hdr, flags);
1655 mb.init();
1656 packerToMemInit(&pa, &mb);
1657 hdr.packInto(&pa);
1658 hdr.clean();
1659 packerClean(&pa);
1660 stuff.S.req_hdrs = mb.buf;
1661 } else {
1662 stuff.S.req_hdrs = NULL;
1663 }
1664 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
1665 if (reason != HTCP_CLR_INVALIDATION) {
1666 mb.clean();
1667 }
1668 if (e == NULL) {
1669 xfree(stuff.S.uri);
1670 }
1671 if (!pktlen) {
1672 debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
1673 return;
1674 }
1675
1676 htcpSend(pkt, (int) pktlen, p->in_addr);
1677 }
1678
1679 /*
1680 * htcpSocketShutdown only closes the 'in' socket if it is
1681 * different than the 'out' socket.
1682 */
1683 void
1684 htcpSocketShutdown(void)
1685 {
1686 if (!Comm::IsConnOpen(htcpIncomingConn))
1687 return;
1688
1689 debugs(12, DBG_IMPORTANT, "Stop accepting HTCP on " << htcpIncomingConn->local);
1690 /*
1691 * Here we just unlink htcpIncomingConn because the HTCP 'in'
1692 * and 'out' sockets might be just one FD. This prevents this
1693 * function from executing repeatedly. When we are really ready to
1694 * exit or restart, main will comm_close the 'out' descriptor.
1695 */
1696 htcpIncomingConn = NULL;
1697
1698 /*
1699 * Normally we only write to the outgoing HTCP socket, but
1700 * we also have a read handler there to catch messages sent
1701 * to that specific interface. During shutdown, we must
1702 * disable reading on the outgoing socket.
1703 */
1704 /* XXX Don't we need this handler to read replies while shutting down?
1705 * I think there should be a separate hander for reading replies..
1706 */
1707 assert(Comm::IsConnOpen(htcpOutgoingConn));
1708
1709 Comm::SetSelect(htcpOutgoingConn->fd, COMM_SELECT_READ, NULL, NULL, 0);
1710 }
1711
1712 void
1713 htcpClosePorts(void)
1714 {
1715 htcpSocketShutdown();
1716
1717 if (htcpOutgoingConn != NULL) {
1718 debugs(12, DBG_IMPORTANT, "Stop sending HTCP from " << htcpOutgoingConn->local);
1719 htcpOutgoingConn = NULL;
1720 }
1721 }
1722
1723 static void
1724 htcpLogHtcp(Ip::Address &caddr, int opcode, LogTags logcode, const char *url)
1725 {
1726 AccessLogEntry::Pointer al = new AccessLogEntry;
1727 if (LOG_TAG_NONE == logcode)
1728 return;
1729 if (!Config.onoff.log_udp)
1730 return;
1731 al->htcp.opcode = htcpOpcodeStr[opcode];
1732 al->url = url;
1733 al->cache.caddr = caddr;
1734 al->cache.code = logcode;
1735 al->cache.msec = 0;
1736 accessLogLog(al, NULL);
1737 }