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