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