]> git.ipfire.org Git - thirdparty/squid.git/blame - src/htcp.cc
Updates for running on squid-cache.org
[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
56deee1e 250static void htcpHandleMsg(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)
56deee1e 603 debugs(31, 3, "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) {
56deee1e 655 debugs(31, 3, "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) {
56deee1e 673 debugs(31, 3, "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) {
56deee1e 696 debugs(31, 3, "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) {
56deee1e 719 debugs(31, 3, "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) {
56deee1e 773 debugs(31, 3, "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) {
56deee1e 791 debugs(31, 3, "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) {
56deee1e 814 debugs(31, 3, "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 {
56deee1e 939 debugs(31, 3, "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 {
56deee1e 977 debugs(31, 3, "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 {
56deee1e 1124 debugs(31, 3, "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 {
56deee1e 1132 debugs(31, 3, "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) {
56deee1e 1155 debugs(31, 3, "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
1bd06eff
BR
1309/*
1310 * Forward a CLR request to all peers who have requested that CLRs be
1311 * forwarded to them.
1312 */
4f4fa815
BR
1313static void
1314htcpForwardClr(char *buf, int sz)
1315{
1316 peer *p;
1317
90bd689c 1318 for (p = Config.peers; p; p = p->next) {
4f4fa815
BR
1319 if (!p->options.htcp) {
1320 continue;
1321 }
1322 if (!p->options.htcp_forward_clr) {
1323 continue;
1324 }
1325
1326 htcpSend(buf, sz, p->in_addr);
1327 }
1328}
1329
1bd06eff
BR
1330/*
1331 * Do the first pass of handling an HTCP message. This used to be two
1332 * separate functions, htcpHandle and htcpHandleData. They were merged to
1333 * allow for forwarding HTCP packets easily to other peers if desired.
1334 *
1335 * This function now works out what type of message we have received and then
1336 * hands it off to other functions to break apart message-specific data.
1337 */
5401aa8d 1338static void
56deee1e 1339htcpHandleMsg(char *buf, int sz, IPAddress &from)
4f4fa815
BR
1340{
1341 htcpHeader htcpHdr;
eb9ae2f7 1342 htcpDataHeader hdr;
4f4fa815
BR
1343 char *hbuf;
1344 int hsz;
1345 assert (sz >= 0);
62e76326 1346
4f4fa815
BR
1347 if ((size_t)sz < sizeof(htcpHeader))
1348 {
56deee1e 1349 debugs(31, 3, "htcpHandle: msg size less than htcpHeader size");
4f4fa815
BR
1350 return;
1351 }
1352
1353 htcpHexdump("htcpHandle", buf, sz);
1354 xmemcpy(&htcpHdr, buf, sizeof(htcpHeader));
1355 htcpHdr.length = ntohs(htcpHdr.length);
1356
1357 if (htcpHdr.minor == 0)
1358 old_squid_format = 1;
1359 else
1360 old_squid_format = 0;
1361
1362 debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr.length);
1363 debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr.major);
1364 debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr.minor);
1365
1366 if (sz != htcpHdr.length)
1367 {
56deee1e 1368 debugs(31, 3, "htcpHandle: sz/" << sz << " != htcpHdr.length/" <<
4f4fa815
BR
1369 htcpHdr.length << " from " << from );
1370
1371 return;
1372 }
1373
1374 if (htcpHdr.major != 0)
1375 {
56deee1e 1376 debugs(31, 3, "htcpHandle: Unknown major version " << htcpHdr.major << " from " << from );
4f4fa815
BR
1377
1378 return;
1379 }
1380
90bd689c
BR
1381 hbuf = buf + sizeof(htcpHeader);
1382 hsz = sz - sizeof(htcpHeader);
4f4fa815
BR
1383
1384 if ((size_t)hsz < sizeof(htcpDataHeader))
62e76326 1385 {
56deee1e 1386 debugs(31, 3, "htcpHandleData: msg size less than htcpDataHeader size");
62e76326 1387 return;
eb9ae2f7 1388 }
62e76326 1389
527ee50d 1390 if (!old_squid_format)
1391 {
4f4fa815
BR
1392 xmemcpy(&hdr, hbuf, sizeof(hdr));
1393 } else {
527ee50d 1394 htcpDataHeaderSquid hdrSquid;
4f4fa815 1395 xmemcpy(&hdrSquid, hbuf, sizeof(hdrSquid));
5401aa8d 1396 hdr.length = hdrSquid.length;
527ee50d 1397 hdr.opcode = hdrSquid.opcode;
1398 hdr.response = hdrSquid.response;
1399 hdr.F1 = hdrSquid.F1;
1400 hdr.RR = hdrSquid.RR;
1401 hdr.reserved = 0;
1402 hdr.msg_id = hdrSquid.msg_id;
1403 }
1404
eb9ae2f7 1405 hdr.length = ntohs(hdr.length);
26df9ec6 1406 hdr.msg_id = ntohl(hdr.msg_id);
4f4fa815 1407 debugs(31, 3, "htcpHandleData: hsz = " << hsz);
4a7a3d56 1408 debugs(31, 3, "htcpHandleData: length = " << hdr.length);
62e76326 1409
4f4fa815 1410 if (hdr.opcode >= HTCP_END) {
56deee1e 1411 debugs(31, 3, "htcpHandleData: client " << from << ", opcode " << hdr.opcode << " out of range");
62e76326 1412 return;
eb9ae2f7 1413 }
62e76326 1414
4a7a3d56 1415 debugs(31, 3, "htcpHandleData: opcode = " << hdr.opcode << " " << htcpOpcodeStr[hdr.opcode]);
1416 debugs(31, 3, "htcpHandleData: response = " << hdr.response);
1417 debugs(31, 3, "htcpHandleData: F1 = " << hdr.F1);
1418 debugs(31, 3, "htcpHandleData: RR = " << hdr.RR);
1419 debugs(31, 3, "htcpHandleData: msg_id = " << hdr.msg_id);
62e76326 1420
4f4fa815 1421 if (hsz < hdr.length) {
56deee1e 1422 debugs(31, 3, "htcpHandleData: sz < hdr.length");
62e76326 1423 return;
eb9ae2f7 1424 }
62e76326 1425
60fac9b5 1426 /*
1427 * set sz = hdr.length so we ignore any AUTH fields following
1428 * the DATA.
1429 */
4f4fa815
BR
1430 hsz = (int) hdr.length;
1431 hbuf += sizeof(htcpDataHeader);
1432 hsz -= sizeof(htcpDataHeader);
1433 debugs(31, 3, "htcpHandleData: hsz = " << hsz);
62e76326 1434
4f4fa815 1435 htcpHexdump("htcpHandleData", hbuf, hsz);
62e76326 1436
4f4fa815 1437 switch (hdr.opcode) {
eb9ae2f7 1438 case HTCP_NOP:
4f4fa815 1439 htcpHandleNop(&hdr, hbuf, hsz, from);
62e76326 1440 break;
eb9ae2f7 1441 case HTCP_TST:
4f4fa815 1442 htcpHandleTst(&hdr, hbuf, hsz, from);
62e76326 1443 break;
eb9ae2f7 1444 case HTCP_MON:
4f4fa815 1445 htcpHandleMon(&hdr, hbuf, hsz, from);
62e76326 1446 break;
eb9ae2f7 1447 case HTCP_SET:
4f4fa815 1448 htcpHandleSet(&hdr, hbuf, hsz, from);
62e76326 1449 break;
2caa57ef 1450 case HTCP_CLR:
4f4fa815
BR
1451 htcpHandleClr(&hdr, hbuf, hsz, from);
1452 htcpForwardClr(buf, sz);
62e76326 1453 break;
eb9ae2f7 1454 default:
4f4fa815 1455 break;
527ee50d 1456 }
eb9ae2f7 1457}
1458
56714a1a 1459static void
eb9ae2f7 1460htcpRecv(int fd, void *data)
1461{
1462 static char buf[8192];
1463 int len;
cc192b50 1464 static IPAddress from;
62e76326 1465
5401aa8d 1466 /* Receive up to 8191 bytes, leaving room for a null */
1467
cc192b50 1468 len = comm_udp_recvfrom(fd, buf, sizeof(buf) - 1, 0, from);
1469
1470 debugs(31, 3, "htcpRecv: FD " << fd << ", " << len << " bytes from " << from );
c4ebc830 1471
1472 if (len)
1473 statCounter.htcp.pkts_recv++;
1474
56deee1e 1475 htcpHandleMsg(buf, len, from);
c4ebc830 1476
eb9ae2f7 1477 commSetSelect(fd, COMM_SELECT_READ, htcpRecv, NULL, 0);
1478}
1479
56714a1a 1480/*
1481 * ======================================================================
1482 * PUBLIC FUNCTIONS
1483 * ======================================================================
1484 */
1485
d5d466fc 1486void
1487htcpInit(void)
3340a3e6 1488{
775fa4ba 1489 if (Config.Port.htcp <= 0) {
bf8fe701 1490 debugs(31, 1, "HTCP Disabled.");
62e76326 1491 return;
775fa4ba 1492 }
9e54a52f
AR
1493
1494 IPAddress incomingAddr = Config.Addrs.udp_incoming;
1495 incomingAddr.SetPort(Config.Port.htcp);
62e76326 1496
d5d466fc 1497 enter_suid();
1498 htcpInSocket = comm_open(SOCK_DGRAM,
bdb741f4 1499 IPPROTO_UDP,
9e54a52f 1500 incomingAddr,
62e76326 1501 COMM_NONBLOCKING,
1502 "HTCP Socket");
d5d466fc 1503 leave_suid();
62e76326 1504
d5d466fc 1505 if (htcpInSocket < 0)
62e76326 1506 fatal("Cannot open HTCP Socket");
1507
d5d466fc 1508 commSetSelect(htcpInSocket, COMM_SELECT_READ, htcpRecv, NULL, 0);
62e76326 1509
4a7a3d56 1510 debugs(31, 1, "Accepting HTCP messages on port " << Config.Port.htcp << ", FD " << htcpInSocket << ".");
62e76326 1511
cc192b50 1512 if (!Config.Addrs.udp_outgoing.IsNoAddr()) {
9e54a52f
AR
1513 IPAddress outgoingAddr = Config.Addrs.udp_outgoing;
1514 outgoingAddr.SetPort(Config.Port.htcp);
1515
62e76326 1516 enter_suid();
1517 htcpOutSocket = comm_open(SOCK_DGRAM,
bdb741f4 1518 IPPROTO_UDP,
9e54a52f 1519 outgoingAddr,
62e76326 1520 COMM_NONBLOCKING,
1521 "Outgoing HTCP Socket");
1522 leave_suid();
1523
1524 if (htcpOutSocket < 0)
1525 fatal("Cannot open Outgoing HTCP Socket");
1526
1527 commSetSelect(htcpOutSocket, COMM_SELECT_READ, htcpRecv, NULL, 0);
1528
4a7a3d56 1529 debugs(31, 1, "Outgoing HTCP messages on port " << Config.Port.htcp << ", FD " << htcpOutSocket << ".");
62e76326 1530
1531 fd_note(htcpInSocket, "Incoming HTCP socket");
d5d466fc 1532 } else {
62e76326 1533 htcpOutSocket = htcpInSocket;
d5d466fc 1534 }
62e76326 1535
e6ccf245 1536 if (!htcpDetailPool) {
04eb0689 1537 htcpDetailPool = memPoolCreate("htcpDetail", sizeof(htcpDetail));
675f3dff 1538 }
59c4d35b 1539}
72549e05 1540
56714a1a 1541void
190154cf 1542htcpQuery(StoreEntry * e, HttpRequest * req, peer * p)
56714a1a 1543{
26df9ec6 1544 cache_key *save_key;
5401aa8d 1545 static char pkt[8192];
56714a1a 1546 ssize_t pktlen;
1547 char vbuf[32];
1548 htcpStuff stuff;
75faaa7a 1549 HttpHeader hdr(hoRequest);
56714a1a 1550 Packer pa;
1551 MemBuf mb;
9c48373d 1552 http_state_flags flags;
775fa4ba 1553
1554 if (htcpInSocket < 0)
62e76326 1555 return;
775fa4ba 1556
527ee50d 1557 old_squid_format = p->options.htcp_oldsquid;
9c48373d 1558 memset(&flags, '\0', sizeof(flags));
7af0a8e6 1559 snprintf(vbuf, sizeof(vbuf), "%d/%d",
62e76326 1560 req->http_ver.major, req->http_ver.minor);
56714a1a 1561 stuff.op = HTCP_TST;
1562 stuff.rr = RR_REQUEST;
1563 stuff.f1 = 1;
1564 stuff.response = 0;
26df9ec6 1565 stuff.msg_id = ++msg_id_counter;
60745f24 1566 stuff.S.method = (char *) RequestMethodStr(req->method);
3900307b 1567 stuff.S.uri = (char *) e->url();
a2edf5dc 1568 stuff.S.version = vbuf;
3b0d7130 1569 HttpStateData::httpBuildRequestHeader(req, req, e, &hdr, flags);
2fe7eff9 1570 mb.init();
56714a1a 1571 packerToMemInit(&pa, &mb);
61f4f11b 1572 hdr.packInto(&pa);
519e0948 1573 hdr.clean();
56714a1a 1574 packerClean(&pa);
a2edf5dc 1575 stuff.S.req_hdrs = mb.buf;
5401aa8d 1576 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
2fe7eff9 1577 mb.clean();
5401aa8d 1578 if (!pktlen) {
56deee1e 1579 debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
62e76326 1580 return;
56714a1a 1581 }
4f4fa815 1582
cc192b50 1583 htcpSend(pkt, (int) pktlen, p->in_addr);
1584
5401aa8d 1585 queried_id[stuff.msg_id % N_QUERIED_KEYS] = stuff.msg_id;
26df9ec6 1586 save_key = queried_keys[stuff.msg_id % N_QUERIED_KEYS];
332dafa2 1587 storeKeyCopy(save_key, (const cache_key *)e->key);
5401aa8d 1588 queried_addr[stuff.msg_id % N_QUERIED_KEYS] = p->in_addr;
bf8fe701 1589 debugs(31, 3, "htcpQuery: key (" << save_key << ") " << storeKeyText(save_key));
56714a1a 1590}
1591
1bd06eff
BR
1592/*
1593 * Send an HTCP CLR message for a specified item to a given peer.
1594 */
4f4fa815 1595void
8dceeee3 1596htcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, peer * p, htcp_clr_reason reason)
4f4fa815
BR
1597{
1598 static char pkt[8192];
1599 ssize_t pktlen;
1600 char vbuf[32];
1601 htcpStuff stuff;
bb7c31c8 1602 HttpHeader hdr(hoRequest);
4f4fa815
BR
1603 Packer pa;
1604 MemBuf mb;
1605 http_state_flags flags;
1606
1607 if (htcpInSocket < 0)
1608 return;
1609
1610 old_squid_format = p->options.htcp_oldsquid;
1611 memset(&flags, '\0', sizeof(flags));
1612 snprintf(vbuf, sizeof(vbuf), "%d/%d",
1613 req->http_ver.major, req->http_ver.minor);
1614 stuff.op = HTCP_CLR;
1615 stuff.rr = RR_REQUEST;
1616 stuff.f1 = 0;
1617 stuff.response = 0;
1618 stuff.msg_id = ++msg_id_counter;
1619 switch (reason) {
1620 case HTCP_CLR_INVALIDATION:
1621 stuff.reason = 1;
1622 break;
1623 default:
1624 stuff.reason = 0;
1625 break;
1626 }
1627 stuff.S.method = (char *) RequestMethodStr(req->method);
1628 if (e == NULL || e->mem_obj == NULL) {
1629 if (uri == NULL) {
1630 return;
1631 }
1632 stuff.S.uri = xstrdup(uri);
1633 } else {
1634 stuff.S.uri = (char *) e->url();
1635 }
1636 stuff.S.version = vbuf;
1637 if (reason != HTCP_CLR_INVALIDATION) {
1638 HttpStateData::httpBuildRequestHeader(req, req, e, &hdr, flags);
1639 mb.init();
1640 packerToMemInit(&pa, &mb);
1641 hdr.packInto(&pa);
1642 hdr.clean();
1643 packerClean(&pa);
1644 stuff.S.req_hdrs = mb.buf;
8dceeee3
BR
1645 } else {
1646 stuff.S.req_hdrs = NULL;
4f4fa815
BR
1647 }
1648 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
1649 if (reason != HTCP_CLR_INVALIDATION) {
1650 mb.clean();
1651 }
1652 if (e == NULL) {
1653 xfree(stuff.S.uri);
1654 }
1655 if (!pktlen) {
56deee1e 1656 debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
4f4fa815
BR
1657 return;
1658 }
1659
1660 htcpSend(pkt, (int) pktlen, p->in_addr);
1661}
1662
62e76326 1663/*
72549e05 1664 * htcpSocketShutdown only closes the 'in' socket if it is
1665 * different than the 'out' socket.
1afe05c5 1666 */
72549e05 1667void
1668htcpSocketShutdown(void)
1afe05c5 1669{
72549e05 1670 if (htcpInSocket < 0)
62e76326 1671 return;
1672
1afe05c5 1673 if (htcpInSocket != htcpOutSocket) {
bf8fe701 1674 debugs(12, 1, "FD " << htcpInSocket << " Closing HTCP socket");
62e76326 1675 comm_close(htcpInSocket);
1afe05c5 1676 }
62e76326 1677
1678 /*
72549e05 1679 * Here we set 'htcpInSocket' to -1 even though the HTCP 'in'
1680 * and 'out' sockets might be just one FD. This prevents this
1681 * function from executing repeatedly. When we are really ready to
1682 * exit or restart, main will comm_close the 'out' descriptor.
1afe05c5 1683 */
72549e05 1684 htcpInSocket = -1;
62e76326 1685
1686 /*
72549e05 1687 * Normally we only write to the outgoing HTCP socket, but
1688 * we also have a read handler there to catch messages sent
1689 * to that specific interface. During shutdown, we must
1690 * disable reading on the outgoing socket.
1afe05c5 1691 */
675f3dff 1692 /* XXX Don't we need this handler to read replies while shutting down?
1693 * I think there should be a separate hander for reading replies..
1694 */
72549e05 1695 assert(htcpOutSocket > -1);
62e76326 1696
72549e05 1697 commSetSelect(htcpOutSocket, COMM_SELECT_READ, NULL, NULL, 0);
1698}
1699
1afe05c5 1700void
72549e05 1701htcpSocketClose(void)
1702{
1703 htcpSocketShutdown();
62e76326 1704
72549e05 1705 if (htcpOutSocket > -1) {
bf8fe701 1706 debugs(12, 1, "FD " << htcpOutSocket << " Closing HTCP socket");
62e76326 1707 comm_close(htcpOutSocket);
1708 htcpOutSocket = -1;
1afe05c5 1709 }
1710}