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