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