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