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