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