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