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