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