]> git.ipfire.org Git - thirdparty/squid.git/blame - src/htcp.cc
Synced #includes after moving files around.
[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"
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"
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
63be0a78 170MEMPROXY_CLASS_INLINE(htcpSpecifier) /**DOCS_NOSEMI*/
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
5401aa8d 853 ACLChecklist checklist;
cc192b50 854 checklist.src_addr = from;
855 checklist.my_addr.SetNoAddr();
b8def95f 856 checklist.request = HTTPMSGLOCK(s->request);
5401aa8d 857 checklist.accessList = cbdataReference(acl);
858 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
859 int result = checklist.fastCheck();
860 return result;
861}
862
eb9ae2f7 863static void
ad61a2b4 864htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, IpAddress &from)
60fac9b5 865{
866 htcpStuff stuff;
5401aa8d 867 static char pkt[8192];
75faaa7a 868 HttpHeader hdr(hoHtcpReply);
2dcc81d4 869 MemBuf mb;
870 Packer p;
60fac9b5 871 ssize_t pktlen;
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
26ac0430 880 if (spec) {
2fe7eff9 881 mb.init();
62e76326 882 packerToMemInit(&p, &mb);
62e76326 883 stuff.S.method = spec->method;
884 stuff.S.uri = spec->uri;
885 stuff.S.version = spec->version;
886 stuff.S.req_hdrs = spec->req_hdrs;
61f4f11b 887 hdr.putInt(HDR_AGE,
888 e->timestamp <= squid_curtime ?
889 squid_curtime - e->timestamp : 0);
890 hdr.packInto(&p);
62e76326 891 stuff.D.resp_hdrs = xstrdup(mb.buf);
bf8fe701 892 debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff.D.resp_hdrs << "}");
2fe7eff9 893 mb.reset();
61f4f11b 894 hdr.reset();
62e76326 895
896 if (e->expires > -1)
61f4f11b 897 hdr.putTime(HDR_EXPIRES, e->expires);
62e76326 898
899 if (e->lastmod > -1)
61f4f11b 900 hdr.putTime(HDR_LAST_MODIFIED, e->lastmod);
62e76326 901
61f4f11b 902 hdr.packInto(&p);
62e76326 903
904 stuff.D.entity_hdrs = xstrdup(mb.buf);
905
bf8fe701 906 debugs(31, 3, "htcpTstReply: entity_hdrs = {" << stuff.D.entity_hdrs << "}");
62e76326 907
2fe7eff9 908 mb.reset();
62e76326 909
61f4f11b 910 hdr.reset();
62e76326 911
9b5c4a9a 912#if USE_ICMP
c517f84a 913 if (char *host = urlHostname(spec->uri)) {
9e008dda
AJ
914 int rtt = 0;
915 int hops = 0;
9b5c4a9a 916 int samp = 0;
62e76326 917 netdbHostData(host, &samp, &rtt, &hops);
918
919 if (rtt || hops) {
c517f84a 920 char cto_buf[128];
62e76326 921 snprintf(cto_buf, 128, "%s %d %f %d",
922 host, samp, 0.001 * rtt, hops);
61f4f11b 923 hdr.putExt("Cache-to-Origin", cto_buf);
62e76326 924 }
925 }
9b5c4a9a 926#endif /* USE_ICMP */
62e76326 927
61f4f11b 928 hdr.packInto(&p);
62e76326 929 stuff.D.cache_hdrs = xstrdup(mb.buf);
bf8fe701 930 debugs(31, 3, "htcpTstReply: cache_hdrs = {" << stuff.D.cache_hdrs << "}");
2fe7eff9 931 mb.clean();
519e0948 932 hdr.clean();
62e76326 933 packerClean(&p);
60fac9b5 934 }
62e76326 935
5401aa8d 936 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
62e76326 937
0203ca6d 938 safe_free(stuff.D.resp_hdrs);
939 safe_free(stuff.D.entity_hdrs);
940 safe_free(stuff.D.cache_hdrs);
941
26ac0430 942 if (!pktlen) {
56deee1e 943 debugs(31, 3, "htcpTstReply: htcpBuildPacket() failed");
5401aa8d 944 return;
945 }
946
947 htcpSend(pkt, (int) pktlen, from);
948}
949
950static void
951
ad61a2b4 952htcpClrReply(htcpDataHeader * dhdr, int purgeSucceeded, IpAddress &from)
5401aa8d 953{
954 htcpStuff stuff;
955 static char pkt[8192];
956 ssize_t pktlen;
957
958 /* If dhdr->F1 == 0, no response desired */
959
960 if (dhdr->F1 == 0)
961 return;
962
963 memset(&stuff, '\0', sizeof(stuff));
964
965 stuff.op = HTCP_CLR;
966
967 stuff.rr = RR_RESPONSE;
968
969 stuff.f1 = 0;
970
971 stuff.response = purgeSucceeded ? 0 : 2;
972
bf8fe701 973 debugs(31, 3, "htcpClrReply: response = " << stuff.response);
5401aa8d 974
975 stuff.msg_id = dhdr->msg_id;
976
977 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
978
26ac0430 979 if (pktlen == 0) {
56deee1e 980 debugs(31, 3, "htcpClrReply: htcpBuildPacket() failed");
62e76326 981 return;
60fac9b5 982 }
62e76326 983
60fac9b5 984 htcpSend(pkt, (int) pktlen, from);
60fac9b5 985}
986
987static void
62e76326 988
ad61a2b4 989htcpHandleNop(htcpDataHeader * hdr, char *buf, int sz, IpAddress &from)
eb9ae2f7 990{
bf8fe701 991 debugs(31, 3, "htcpHandleNop: Unimplemented");
eb9ae2f7 992}
993
e6ccf245 994void
995htcpSpecifier::checkHit()
32b3cf93 996{
7e3ce7b9 997 char *blk_end;
5401aa8d 998 checkHitRequest = request;
62e76326 999
e6ccf245 1000 if (NULL == checkHitRequest) {
bf8fe701 1001 debugs(31, 3, "htcpCheckHit: NO; failed to parse URL");
62e76326 1002 checkedHit(NullStoreEntry::getInstance());
1003 return;
7e3ce7b9 1004 }
62e76326 1005
e6ccf245 1006 blk_end = req_hdrs + strlen(req_hdrs);
62e76326 1007
61f4f11b 1008 if (!checkHitRequest->header.parse(req_hdrs, blk_end)) {
bf8fe701 1009 debugs(31, 3, "htcpCheckHit: NO; failed to parse request headers");
5cafad19 1010 delete checkHitRequest;
62e76326 1011 checkHitRequest = NULL;
1012 checkedHit(NullStoreEntry::getInstance());
1013 return;
f66a9ef4 1014 }
62e76326 1015
3b13a8fd 1016 StoreEntry::getPublicByRequest(this, checkHitRequest);
e6ccf245 1017}
1018
1019void
3b13a8fd 1020htcpSpecifier::created (StoreEntry *e)
e6ccf245 1021{
1022 StoreEntry *hit=NULL;
1023 assert (e);
62e76326 1024
e6ccf245 1025 if (e->isNull()) {
bf8fe701 1026 debugs(31, 3, "htcpCheckHit: NO; public object not found");
26ac0430 1027 } else if (!e->validToSend()) {
bf8fe701 1028 debugs(31, 3, "htcpCheckHit: NO; entry not valid to send" );
26ac0430 1029 } else if (refreshCheckHTCP(e, checkHitRequest)) {
bf8fe701 1030 debugs(31, 3, "htcpCheckHit: NO; cached response is stale");
26ac0430 1031 } else {
cc192b50 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
ad61a2b4 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
ad61a2b4 1101htcpHandleTstResponse(htcpDataHeader * hdr, char *buf, int sz, IpAddress &from)
60fac9b5 1102{
a2edf5dc 1103 htcpReplyData htcpReply;
1104 cache_key *key = NULL;
5401aa8d 1105
ad61a2b4 1106 IpAddress *peer;
26df9ec6 1107 htcpDetail *d = NULL;
a2edf5dc 1108 char *t;
62e76326 1109
26ac0430 1110 if (queried_id[hdr->msg_id % N_QUERIED_KEYS] != hdr->msg_id) {
bf8fe701 1111 debugs(31, 2, "htcpHandleTstResponse: No matching query id '" <<
1112 hdr->msg_id << "' (expected " <<
1113 queried_id[hdr->msg_id % N_QUERIED_KEYS] << ") from '" <<
cc192b50 1114 from << "'");
bf8fe701 1115
5401aa8d 1116 return;
1117 }
1118
1119 key = queried_keys[hdr->msg_id % N_QUERIED_KEYS];
1120
26ac0430 1121 if (!key) {
56deee1e 1122 debugs(31, 3, "htcpHandleTstResponse: No query key for response id '" << hdr->msg_id << "' from '" << from << "'");
5401aa8d 1123 return;
1124 }
1125
1126 peer = &queried_addr[hdr->msg_id % N_QUERIED_KEYS];
1127
26ac0430 1128 if ( *peer != from || peer->GetPort() != from.GetPort() ) {
56deee1e 1129 debugs(31, 3, "htcpHandleTstResponse: Unexpected response source " << from );
5401aa8d 1130 return;
1131 }
1132
26ac0430 1133 if (hdr->F1 == 1) {
bf8fe701 1134 debugs(31, 2, "htcpHandleTstResponse: error condition, F1/MO == 1");
62e76326 1135 return;
44e237d0 1136 }
62e76326 1137
26df9ec6 1138 htcpReply.msg_id = hdr->msg_id;
4a7a3d56 1139 debugs(31, 3, "htcpHandleTstResponse: msg_id = " << htcpReply.msg_id);
44e237d0 1140 htcpReply.hit = hdr->response ? 0 : 1;
62e76326 1141
26ac0430 1142 if (hdr->F1) {
bf8fe701 1143 debugs(31, 3, "htcpHandleTstResponse: MISS");
26ac0430 1144 } else {
bf8fe701 1145 debugs(31, 3, "htcpHandleTstResponse: HIT");
62e76326 1146 d = htcpUnpackDetail(buf, sz);
1147
1148 if (d == NULL) {
56deee1e 1149 debugs(31, 3, "htcpHandleTstResponse: bad DETAIL");
62e76326 1150 return;
1151 }
1152
1153 if ((t = d->resp_hdrs))
61f4f11b 1154 htcpReply.hdr.parse(t, t + strlen(t));
62e76326 1155
1156 if ((t = d->entity_hdrs))
61f4f11b 1157 htcpReply.hdr.parse(t, t + strlen(t));
62e76326 1158
1159 if ((t = d->cache_hdrs))
61f4f11b 1160 htcpReply.hdr.parse(t, t + strlen(t));
a2edf5dc 1161 }
62e76326 1162
bf8fe701 1163 debugs(31, 3, "htcpHandleTstResponse: key (" << key << ") " << storeKeyText(key));
a2edf5dc 1164 neighborsHtcpReply(key, &htcpReply, from);
519e0948 1165 htcpReply.hdr.clean();
62e76326 1166
26df9ec6 1167 if (d)
62e76326 1168 htcpFreeDetail(d);
60fac9b5 1169}
1170
1171static void
62e76326 1172
ad61a2b4 1173htcpHandleTstRequest(htcpDataHeader * dhdr, char *buf, int sz, IpAddress &from)
60fac9b5 1174{
1175 /* buf should be a SPECIFIER */
1176 htcpSpecifier *s;
62e76326 1177
26ac0430 1178 if (sz == 0) {
bf8fe701 1179 debugs(31, 3, "htcpHandleTst: nothing to do");
62e76326 1180 return;
60fac9b5 1181 }
62e76326 1182
44e237d0 1183 if (dhdr->F1 == 0)
62e76326 1184 return;
1185
e6ccf245 1186 /* s is a new object */
60fac9b5 1187 s = htcpUnpackSpecifier(buf, sz);
62e76326 1188
e6ccf245 1189 s->setFrom (from);
62e76326 1190
e6ccf245 1191 s->setDataHeader (dhdr);
62e76326 1192
26ac0430 1193 if (NULL == s) {
bf8fe701 1194 debugs(31, 2, "htcpHandleTstRequest: htcpUnpackSpecifier failed");
5401aa8d 1195 return;
1196 }
1197
26ac0430 1198 if (!s->request) {
bf8fe701 1199 debugs(31, 2, "htcpHandleTstRequest: failed to parse request");
5401aa8d 1200 htcpFreeSpecifier(s);
1201 return;
1202 }
1203
26ac0430 1204 if (!htcpAccessCheck(Config.accessList.htcp, s, from)) {
bf8fe701 1205 debugs(31, 2, "htcpHandleTstRequest: Access denied");
5401aa8d 1206 htcpFreeSpecifier(s);
62e76326 1207 return;
1afe05c5 1208 }
62e76326 1209
bf8fe701 1210 debugs(31, 3, "htcpHandleTstRequest: " << s->method << " " << s->uri << " " << s->version);
1211 debugs(31, 3, "htcpHandleTstRequest: " << s->req_hdrs);
e6ccf245 1212 s->checkHit();
1213}
1214
1215void
1216htcpSpecifier::checkedHit(StoreEntry *e)
1217{
1218 if (e)
62e76326 1219 htcpTstReply(dhdr, e, this, from); /* hit */
32b3cf93 1220 else
62e76326 1221 htcpTstReply(dhdr, NULL, NULL, from); /* cache miss */
1222
e6ccf245 1223 htcpFreeSpecifier(this);
d9f9d78b 1224}
1225
1226static void
62e76326 1227
ad61a2b4 1228htcpHandleMon(htcpDataHeader * hdr, char *buf, int sz, IpAddress &from)
eb9ae2f7 1229{
bf8fe701 1230 debugs(31, 3, "htcpHandleMon: Unimplemented");
eb9ae2f7 1231}
1232
1233static void
62e76326 1234
ad61a2b4 1235htcpHandleSet(htcpDataHeader * hdr, char *buf, int sz, IpAddress &from)
eb9ae2f7 1236{
bf8fe701 1237 debugs(31, 3, "htcpHandleSet: Unimplemented");
eb9ae2f7 1238}
1239
1240static void
62e76326 1241
ad61a2b4 1242htcpHandleClr(htcpDataHeader * hdr, char *buf, int sz, IpAddress &from)
5401aa8d 1243{
1244 htcpSpecifier *s;
1245 /* buf[0/1] is reserved and reason */
1246 int reason = buf[1] << 4;
bf8fe701 1247 debugs(31, 3, "htcpHandleClr: reason=" << reason);
5401aa8d 1248 buf += 2;
1249 sz -= 2;
1250
1251 /* buf should be a SPECIFIER */
1252
26ac0430 1253 if (sz == 0) {
bf8fe701 1254 debugs(31, 4, "htcpHandleClr: nothing to do");
5401aa8d 1255 return;
1256 }
1257
1258 s = htcpUnpackSpecifier(buf, sz);
1259
26ac0430 1260 if (NULL == s) {
bf8fe701 1261 debugs(31, 3, "htcpHandleClr: htcpUnpackSpecifier failed");
5401aa8d 1262 return;
1263 }
1264
26ac0430 1265 if (!htcpAccessCheck(Config.accessList.htcp_clr, s, from)) {
bf8fe701 1266 debugs(31, 2, "htcpHandleClr: Access denied");
5401aa8d 1267 htcpFreeSpecifier(s);
1268 return;
1269 }
1270
bf8fe701 1271 debugs(31, 5, "htcpHandleClr: " << s->method << " " << s->uri << " " << s->version);
1272 debugs(31, 5, "htcpHandleClr: request headers: " << s->req_hdrs);
5401aa8d 1273
1274 /* Release objects from cache
1275 * analog to clientPurgeRequest in client_side.c
1276 */
1277
26ac0430 1278 switch (htcpClrStore(s)) {
5401aa8d 1279
1280 case 1:
1281 htcpClrReply(hdr, 1, from); /* hit */
1282 break;
1283
1284 case 0:
1285 htcpClrReply(hdr, 0, from); /* miss */
1286 break;
1287
1288 default:
1289 break;
1290 }
1291
1292 htcpFreeSpecifier(s);
1293}
1294
1bd06eff
BR
1295/*
1296 * Forward a CLR request to all peers who have requested that CLRs be
1297 * forwarded to them.
1298 */
4f4fa815
BR
1299static void
1300htcpForwardClr(char *buf, int sz)
1301{
1302 peer *p;
26ac0430 1303
90bd689c 1304 for (p = Config.peers; p; p = p->next) {
4f4fa815
BR
1305 if (!p->options.htcp) {
1306 continue;
1307 }
1308 if (!p->options.htcp_forward_clr) {
1309 continue;
1310 }
26ac0430 1311
4f4fa815
BR
1312 htcpSend(buf, sz, p->in_addr);
1313 }
1314}
1315
1bd06eff
BR
1316/*
1317 * Do the first pass of handling an HTCP message. This used to be two
1318 * separate functions, htcpHandle and htcpHandleData. They were merged to
1319 * allow for forwarding HTCP packets easily to other peers if desired.
1320 *
1321 * This function now works out what type of message we have received and then
1322 * hands it off to other functions to break apart message-specific data.
1323 */
5401aa8d 1324static void
ad61a2b4 1325htcpHandleMsg(char *buf, int sz, IpAddress &from)
4f4fa815
BR
1326{
1327 htcpHeader htcpHdr;
eb9ae2f7 1328 htcpDataHeader hdr;
4f4fa815
BR
1329 char *hbuf;
1330 int hsz;
1331 assert (sz >= 0);
62e76326 1332
26ac0430 1333 if ((size_t)sz < sizeof(htcpHeader)) {
56deee1e 1334 debugs(31, 3, "htcpHandle: msg size less than htcpHeader size");
4f4fa815
BR
1335 return;
1336 }
1337
1338 htcpHexdump("htcpHandle", buf, sz);
1339 xmemcpy(&htcpHdr, buf, sizeof(htcpHeader));
1340 htcpHdr.length = ntohs(htcpHdr.length);
1341
1342 if (htcpHdr.minor == 0)
1343 old_squid_format = 1;
1344 else
1345 old_squid_format = 0;
1346
1347 debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr.length);
1348 debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr.major);
1349 debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr.minor);
1350
26ac0430 1351 if (sz != htcpHdr.length) {
56deee1e 1352 debugs(31, 3, "htcpHandle: sz/" << sz << " != htcpHdr.length/" <<
4f4fa815
BR
1353 htcpHdr.length << " from " << from );
1354
1355 return;
1356 }
1357
26ac0430 1358 if (htcpHdr.major != 0) {
56deee1e 1359 debugs(31, 3, "htcpHandle: Unknown major version " << htcpHdr.major << " from " << from );
4f4fa815
BR
1360
1361 return;
1362 }
1363
90bd689c
BR
1364 hbuf = buf + sizeof(htcpHeader);
1365 hsz = sz - sizeof(htcpHeader);
4f4fa815 1366
26ac0430 1367 if ((size_t)hsz < sizeof(htcpDataHeader)) {
56deee1e 1368 debugs(31, 3, "htcpHandleData: msg size less than htcpDataHeader size");
62e76326 1369 return;
eb9ae2f7 1370 }
62e76326 1371
26ac0430 1372 if (!old_squid_format) {
4f4fa815
BR
1373 xmemcpy(&hdr, hbuf, sizeof(hdr));
1374 } else {
527ee50d 1375 htcpDataHeaderSquid hdrSquid;
4f4fa815 1376 xmemcpy(&hdrSquid, hbuf, sizeof(hdrSquid));
5401aa8d 1377 hdr.length = hdrSquid.length;
527ee50d 1378 hdr.opcode = hdrSquid.opcode;
1379 hdr.response = hdrSquid.response;
1380 hdr.F1 = hdrSquid.F1;
1381 hdr.RR = hdrSquid.RR;
1382 hdr.reserved = 0;
1383 hdr.msg_id = hdrSquid.msg_id;
1384 }
1385
eb9ae2f7 1386 hdr.length = ntohs(hdr.length);
26df9ec6 1387 hdr.msg_id = ntohl(hdr.msg_id);
4f4fa815 1388 debugs(31, 3, "htcpHandleData: hsz = " << hsz);
4a7a3d56 1389 debugs(31, 3, "htcpHandleData: length = " << hdr.length);
62e76326 1390
4f4fa815 1391 if (hdr.opcode >= HTCP_END) {
56deee1e 1392 debugs(31, 3, "htcpHandleData: client " << from << ", opcode " << hdr.opcode << " out of range");
62e76326 1393 return;
eb9ae2f7 1394 }
62e76326 1395
4a7a3d56 1396 debugs(31, 3, "htcpHandleData: opcode = " << hdr.opcode << " " << htcpOpcodeStr[hdr.opcode]);
1397 debugs(31, 3, "htcpHandleData: response = " << hdr.response);
1398 debugs(31, 3, "htcpHandleData: F1 = " << hdr.F1);
1399 debugs(31, 3, "htcpHandleData: RR = " << hdr.RR);
1400 debugs(31, 3, "htcpHandleData: msg_id = " << hdr.msg_id);
62e76326 1401
4f4fa815 1402 if (hsz < hdr.length) {
56deee1e 1403 debugs(31, 3, "htcpHandleData: sz < hdr.length");
62e76326 1404 return;
eb9ae2f7 1405 }
62e76326 1406
60fac9b5 1407 /*
1408 * set sz = hdr.length so we ignore any AUTH fields following
1409 * the DATA.
1410 */
4f4fa815
BR
1411 hsz = (int) hdr.length;
1412 hbuf += sizeof(htcpDataHeader);
1413 hsz -= sizeof(htcpDataHeader);
1414 debugs(31, 3, "htcpHandleData: hsz = " << hsz);
62e76326 1415
4f4fa815 1416 htcpHexdump("htcpHandleData", hbuf, hsz);
62e76326 1417
4f4fa815 1418 switch (hdr.opcode) {
eb9ae2f7 1419 case HTCP_NOP:
4f4fa815 1420 htcpHandleNop(&hdr, hbuf, hsz, from);
62e76326 1421 break;
eb9ae2f7 1422 case HTCP_TST:
4f4fa815 1423 htcpHandleTst(&hdr, hbuf, hsz, from);
62e76326 1424 break;
eb9ae2f7 1425 case HTCP_MON:
4f4fa815 1426 htcpHandleMon(&hdr, hbuf, hsz, from);
62e76326 1427 break;
eb9ae2f7 1428 case HTCP_SET:
4f4fa815 1429 htcpHandleSet(&hdr, hbuf, hsz, from);
62e76326 1430 break;
2caa57ef 1431 case HTCP_CLR:
4f4fa815
BR
1432 htcpHandleClr(&hdr, hbuf, hsz, from);
1433 htcpForwardClr(buf, sz);
62e76326 1434 break;
eb9ae2f7 1435 default:
4f4fa815 1436 break;
527ee50d 1437 }
eb9ae2f7 1438}
1439
56714a1a 1440static void
eb9ae2f7 1441htcpRecv(int fd, void *data)
1442{
1443 static char buf[8192];
1444 int len;
ad61a2b4 1445 static IpAddress from;
62e76326 1446
5401aa8d 1447 /* Receive up to 8191 bytes, leaving room for a null */
1448
cc192b50 1449 len = comm_udp_recvfrom(fd, buf, sizeof(buf) - 1, 0, from);
1450
1451 debugs(31, 3, "htcpRecv: FD " << fd << ", " << len << " bytes from " << from );
c4ebc830 1452
1453 if (len)
1454 statCounter.htcp.pkts_recv++;
1455
56deee1e 1456 htcpHandleMsg(buf, len, from);
c4ebc830 1457
eb9ae2f7 1458 commSetSelect(fd, COMM_SELECT_READ, htcpRecv, NULL, 0);
1459}
1460
56714a1a 1461/*
1462 * ======================================================================
1463 * PUBLIC FUNCTIONS
1464 * ======================================================================
1465 */
1466
d5d466fc 1467void
1468htcpInit(void)
3340a3e6 1469{
775fa4ba 1470 if (Config.Port.htcp <= 0) {
bf8fe701 1471 debugs(31, 1, "HTCP Disabled.");
62e76326 1472 return;
775fa4ba 1473 }
9e54a52f 1474
ad61a2b4 1475 IpAddress incomingAddr = Config.Addrs.udp_incoming;
9e54a52f 1476 incomingAddr.SetPort(Config.Port.htcp);
62e76326 1477
d5d466fc 1478 enter_suid();
1479 htcpInSocket = comm_open(SOCK_DGRAM,
bdb741f4 1480 IPPROTO_UDP,
9e54a52f 1481 incomingAddr,
62e76326 1482 COMM_NONBLOCKING,
1483 "HTCP Socket");
d5d466fc 1484 leave_suid();
62e76326 1485
d5d466fc 1486 if (htcpInSocket < 0)
62e76326 1487 fatal("Cannot open HTCP Socket");
1488
d5d466fc 1489 commSetSelect(htcpInSocket, COMM_SELECT_READ, htcpRecv, NULL, 0);
62e76326 1490
4a7a3d56 1491 debugs(31, 1, "Accepting HTCP messages on port " << Config.Port.htcp << ", FD " << htcpInSocket << ".");
62e76326 1492
cc192b50 1493 if (!Config.Addrs.udp_outgoing.IsNoAddr()) {
ad61a2b4 1494 IpAddress outgoingAddr = Config.Addrs.udp_outgoing;
9e54a52f
AR
1495 outgoingAddr.SetPort(Config.Port.htcp);
1496
62e76326 1497 enter_suid();
1498 htcpOutSocket = comm_open(SOCK_DGRAM,
bdb741f4 1499 IPPROTO_UDP,
9e54a52f 1500 outgoingAddr,
62e76326 1501 COMM_NONBLOCKING,
1502 "Outgoing HTCP Socket");
1503 leave_suid();
1504
1505 if (htcpOutSocket < 0)
1506 fatal("Cannot open Outgoing HTCP Socket");
1507
1508 commSetSelect(htcpOutSocket, COMM_SELECT_READ, htcpRecv, NULL, 0);
1509
4a7a3d56 1510 debugs(31, 1, "Outgoing HTCP messages on port " << Config.Port.htcp << ", FD " << htcpOutSocket << ".");
62e76326 1511
1512 fd_note(htcpInSocket, "Incoming HTCP socket");
d5d466fc 1513 } else {
62e76326 1514 htcpOutSocket = htcpInSocket;
d5d466fc 1515 }
62e76326 1516
e6ccf245 1517 if (!htcpDetailPool) {
04eb0689 1518 htcpDetailPool = memPoolCreate("htcpDetail", sizeof(htcpDetail));
675f3dff 1519 }
59c4d35b 1520}
72549e05 1521
bebf08ff 1522int
190154cf 1523htcpQuery(StoreEntry * e, HttpRequest * req, peer * p)
56714a1a 1524{
26df9ec6 1525 cache_key *save_key;
5401aa8d 1526 static char pkt[8192];
56714a1a 1527 ssize_t pktlen;
1528 char vbuf[32];
1529 htcpStuff stuff;
75faaa7a 1530 HttpHeader hdr(hoRequest);
56714a1a 1531 Packer pa;
1532 MemBuf mb;
9c48373d 1533 http_state_flags flags;
775fa4ba 1534
1535 if (htcpInSocket < 0)
bebf08ff 1536 return 0;
775fa4ba 1537
527ee50d 1538 old_squid_format = p->options.htcp_oldsquid;
9c48373d 1539 memset(&flags, '\0', sizeof(flags));
7af0a8e6 1540 snprintf(vbuf, sizeof(vbuf), "%d/%d",
62e76326 1541 req->http_ver.major, req->http_ver.minor);
56714a1a 1542 stuff.op = HTCP_TST;
1543 stuff.rr = RR_REQUEST;
1544 stuff.f1 = 1;
1545 stuff.response = 0;
26df9ec6 1546 stuff.msg_id = ++msg_id_counter;
60745f24 1547 stuff.S.method = (char *) RequestMethodStr(req->method);
3900307b 1548 stuff.S.uri = (char *) e->url();
a2edf5dc 1549 stuff.S.version = vbuf;
3b0d7130 1550 HttpStateData::httpBuildRequestHeader(req, req, e, &hdr, flags);
2fe7eff9 1551 mb.init();
56714a1a 1552 packerToMemInit(&pa, &mb);
61f4f11b 1553 hdr.packInto(&pa);
519e0948 1554 hdr.clean();
56714a1a 1555 packerClean(&pa);
a2edf5dc 1556 stuff.S.req_hdrs = mb.buf;
5401aa8d 1557 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
2fe7eff9 1558 mb.clean();
5401aa8d 1559 if (!pktlen) {
56deee1e 1560 debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
bebf08ff 1561 return -1;
56714a1a 1562 }
26ac0430 1563
cc192b50 1564 htcpSend(pkt, (int) pktlen, p->in_addr);
1565
5401aa8d 1566 queried_id[stuff.msg_id % N_QUERIED_KEYS] = stuff.msg_id;
26df9ec6 1567 save_key = queried_keys[stuff.msg_id % N_QUERIED_KEYS];
332dafa2 1568 storeKeyCopy(save_key, (const cache_key *)e->key);
5401aa8d 1569 queried_addr[stuff.msg_id % N_QUERIED_KEYS] = p->in_addr;
bf8fe701 1570 debugs(31, 3, "htcpQuery: key (" << save_key << ") " << storeKeyText(save_key));
bebf08ff
AJ
1571
1572 return 1;
56714a1a 1573}
1574
1bd06eff
BR
1575/*
1576 * Send an HTCP CLR message for a specified item to a given peer.
1577 */
4f4fa815 1578void
8dceeee3 1579htcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, peer * p, htcp_clr_reason reason)
4f4fa815
BR
1580{
1581 static char pkt[8192];
1582 ssize_t pktlen;
1583 char vbuf[32];
1584 htcpStuff stuff;
bb7c31c8 1585 HttpHeader hdr(hoRequest);
4f4fa815
BR
1586 Packer pa;
1587 MemBuf mb;
1588 http_state_flags flags;
1589
1590 if (htcpInSocket < 0)
30fca662 1591 return;
4f4fa815
BR
1592
1593 old_squid_format = p->options.htcp_oldsquid;
1594 memset(&flags, '\0', sizeof(flags));
1595 snprintf(vbuf, sizeof(vbuf), "%d/%d",
26ac0430 1596 req->http_ver.major, req->http_ver.minor);
4f4fa815
BR
1597 stuff.op = HTCP_CLR;
1598 stuff.rr = RR_REQUEST;
1599 stuff.f1 = 0;
1600 stuff.response = 0;
1601 stuff.msg_id = ++msg_id_counter;
1602 switch (reason) {
1603 case HTCP_CLR_INVALIDATION:
26ac0430
AJ
1604 stuff.reason = 1;
1605 break;
4f4fa815 1606 default:
26ac0430
AJ
1607 stuff.reason = 0;
1608 break;
4f4fa815
BR
1609 }
1610 stuff.S.method = (char *) RequestMethodStr(req->method);
1611 if (e == NULL || e->mem_obj == NULL) {
26ac0430 1612 if (uri == NULL) {
4f4fa815 1613 return;
26ac0430
AJ
1614 }
1615 stuff.S.uri = xstrdup(uri);
4f4fa815 1616 } else {
26ac0430 1617 stuff.S.uri = (char *) e->url();
4f4fa815
BR
1618 }
1619 stuff.S.version = vbuf;
1620 if (reason != HTCP_CLR_INVALIDATION) {
1621 HttpStateData::httpBuildRequestHeader(req, req, e, &hdr, flags);
1622 mb.init();
1623 packerToMemInit(&pa, &mb);
1624 hdr.packInto(&pa);
1625 hdr.clean();
1626 packerClean(&pa);
26ac0430 1627 stuff.S.req_hdrs = mb.buf;
8dceeee3
BR
1628 } else {
1629 stuff.S.req_hdrs = NULL;
4f4fa815
BR
1630 }
1631 pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
1632 if (reason != HTCP_CLR_INVALIDATION) {
1633 mb.clean();
1634 }
1635 if (e == NULL) {
26ac0430 1636 xfree(stuff.S.uri);
4f4fa815
BR
1637 }
1638 if (!pktlen) {
26ac0430
AJ
1639 debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
1640 return;
4f4fa815 1641 }
26ac0430 1642
4f4fa815
BR
1643 htcpSend(pkt, (int) pktlen, p->in_addr);
1644}
1645
62e76326 1646/*
72549e05 1647 * htcpSocketShutdown only closes the 'in' socket if it is
1648 * different than the 'out' socket.
1afe05c5 1649 */
72549e05 1650void
1651htcpSocketShutdown(void)
1afe05c5 1652{
72549e05 1653 if (htcpInSocket < 0)
62e76326 1654 return;
1655
1afe05c5 1656 if (htcpInSocket != htcpOutSocket) {
bf8fe701 1657 debugs(12, 1, "FD " << htcpInSocket << " Closing HTCP socket");
62e76326 1658 comm_close(htcpInSocket);
1afe05c5 1659 }
62e76326 1660
1661 /*
72549e05 1662 * Here we set 'htcpInSocket' to -1 even though the HTCP 'in'
1663 * and 'out' sockets might be just one FD. This prevents this
1664 * function from executing repeatedly. When we are really ready to
1665 * exit or restart, main will comm_close the 'out' descriptor.
1afe05c5 1666 */
72549e05 1667 htcpInSocket = -1;
62e76326 1668
1669 /*
72549e05 1670 * Normally we only write to the outgoing HTCP socket, but
1671 * we also have a read handler there to catch messages sent
1672 * to that specific interface. During shutdown, we must
1673 * disable reading on the outgoing socket.
1afe05c5 1674 */
675f3dff 1675 /* XXX Don't we need this handler to read replies while shutting down?
1676 * I think there should be a separate hander for reading replies..
1677 */
72549e05 1678 assert(htcpOutSocket > -1);
62e76326 1679
72549e05 1680 commSetSelect(htcpOutSocket, COMM_SELECT_READ, NULL, NULL, 0);
1681}
1682
1afe05c5 1683void
72549e05 1684htcpSocketClose(void)
1685{
1686 htcpSocketShutdown();
62e76326 1687
72549e05 1688 if (htcpOutSocket > -1) {
bf8fe701 1689 debugs(12, 1, "FD " << htcpOutSocket << " Closing HTCP socket");
62e76326 1690 comm_close(htcpOutSocket);
1691 htcpOutSocket = -1;
1afe05c5 1692 }
1693}