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