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