]>
Commit | Line | Data |
---|---|---|
9cef6668 | 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. | |
9cef6668 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 12 Internet Cache Protocol (ICP) */ |
10 | ||
63be0a78 | 11 | /** |
12 | \defgroup ServerProtocolICPInternal2 ICPv2 Internals | |
13 | \ingroup ServerProtocolICPAPI | |
14 | */ | |
15 | ||
582c2af2 FC |
16 | #include "squid.h" |
17 | #include "AccessLogEntry.h" | |
18 | #include "acl/Acl.h" | |
19 | #include "acl/FilledChecklist.h" | |
e5ddd4ce | 20 | #include "base/AsyncCallbacks.h" |
95e6d864 | 21 | #include "client_db.h" |
063dc1eb | 22 | #include "comm.h" |
09cee743 | 23 | #include "comm/Connection.h" |
d841c88d | 24 | #include "comm/Loops.h" |
c4ad1349 | 25 | #include "fd.h" |
f9b72e0c | 26 | #include "HttpRequest.h" |
9b5c4a9a | 27 | #include "icmp/net_db.h" |
582c2af2 | 28 | #include "ICP.h" |
96d89ea0 | 29 | #include "ip/Address.h" |
e0f8b709 | 30 | #include "ip/tools.h" |
e5ddd4ce | 31 | #include "ipc/StartListening.h" |
714e68b7 | 32 | #include "ipcache.h" |
582c2af2 | 33 | #include "md5.h" |
afabcc13 | 34 | #include "multicast.h" |
f0ba2534 | 35 | #include "neighbors.h" |
c6f15d40 | 36 | #include "refresh.h" |
1fa9b1a7 | 37 | #include "rfc1738.h" |
4d5904f7 | 38 | #include "SquidConfig.h" |
582c2af2 FC |
39 | #include "StatCounters.h" |
40 | #include "Store.h" | |
fb548aaf | 41 | #include "store_key_md5.h" |
4e540555 | 42 | #include "tools.h" |
582c2af2 | 43 | #include "wordlist.h" |
7a2f978b | 44 | |
1a30fdf5 | 45 | #include <cerrno> |
21d845b1 | 46 | |
d2a6dcba EB |
47 | /// a delayed icpUdpSend() call |
48 | class DelayedUdpSend { | |
49 | public: | |
50 | Ip::Address address; ///< remote peer (which may not be a cache_peer) | |
51 | icp_common_t *msg = nullptr; ///< ICP message with network byte order fields | |
52 | DelayedUdpSend *next = nullptr; ///< invasive FIFO queue of delayed ICP messages | |
53 | AccessLogEntryPointer ale; ///< sender's master transaction summary | |
8b082ed9 | 54 | struct timeval queue_time = {}; ///< queuing timestamp |
d2a6dcba EB |
55 | }; |
56 | ||
e5ddd4ce | 57 | static void icpIncomingConnectionOpened(Ipc::StartListeningAnswer &); |
013e320c | 58 | |
63be0a78 | 59 | /// \ingroup ServerProtocolICPInternal2 |
d2a6dcba | 60 | static void icpLogIcp(const Ip::Address &, const LogTags_ot, int, const char *, const int, AccessLogEntryPointer &); |
62e76326 | 61 | |
63be0a78 | 62 | /// \ingroup ServerProtocolICPInternal2 |
b7ac5457 | 63 | static void icpHandleIcpV2(int, Ip::Address &, char *, int); |
63be0a78 | 64 | |
65 | /// \ingroup ServerProtocolICPInternal2 | |
071a3ae7 | 66 | static void icpCount(void *, int, size_t, int); |
17b6e784 | 67 | |
d2a6dcba EB |
68 | static LogTags_ot icpLogFromICPCode(icp_opcode); |
69 | ||
70 | static int icpUdpSend(int fd, const Ip::Address &to, icp_common_t * msg, int delay, AccessLogEntryPointer al); | |
71 | ||
819be284 | 72 | static void |
d2a6dcba | 73 | icpSyncAle(AccessLogEntryPointer &al, const Ip::Address &caddr, const char *url, int len, int delay) |
819be284 EB |
74 | { |
75 | if (!al) | |
76 | al = new AccessLogEntry(); | |
77 | al->icp.opcode = ICP_QUERY; | |
78 | al->cache.caddr = caddr; | |
819be284 | 79 | al->url = url; |
bec110e4 | 80 | al->setVirginUrlForMissingRequest(al->url); |
819be284 EB |
81 | // XXX: move to use icp.clientReply instead |
82 | al->http.clientReplySz.payloadData = len; | |
83 | al->cache.start_time = current_time; | |
84 | al->cache.start_time.tv_sec -= delay; | |
85 | al->cache.trTime.tv_sec = delay; | |
86 | al->cache.trTime.tv_usec = 0; | |
87 | } | |
88 | ||
63be0a78 | 89 | /** |
90 | \ingroup ServerProtocolICPInternal2 | |
48382032 | 91 | * IcpQueueHead is global so comm_incoming() knows whether or not |
92 | * to call icpUdpSendQueue. | |
93 | */ | |
aee3523a | 94 | static DelayedUdpSend *IcpQueueHead = nullptr; |
63be0a78 | 95 | /// \ingroup ServerProtocolICPInternal2 |
aee3523a | 96 | static DelayedUdpSend *IcpQueueTail = nullptr; |
7a2f978b | 97 | |
e1f7507e | 98 | /// \ingroup ServerProtocolICPInternal2 |
aee3523a | 99 | Comm::ConnectionPointer icpIncomingConn = nullptr; |
e0d28505 | 100 | /// \ingroup ServerProtocolICPInternal2 |
aee3523a | 101 | Comm::ConnectionPointer icpOutgoingConn = nullptr; |
e0d28505 | 102 | |
e6ccf245 | 103 | /* icp_common_t */ |
cb835136 | 104 | icp_common_t::icp_common_t() : |
f53969cc SM |
105 | opcode(ICP_INVALID), version(0), length(0), reqnum(0), |
106 | flags(0), pad(0), shostid(0) | |
62e76326 | 107 | {} |
e6ccf245 | 108 | |
cb835136 | 109 | icp_common_t::icp_common_t(char *buf, unsigned int len) : |
f53969cc | 110 | opcode(ICP_INVALID), version(0), reqnum(0), flags(0), pad(0), shostid(0) |
e6ccf245 | 111 | { |
cb835136 | 112 | if (len < sizeof(icp_common_t)) { |
62e76326 | 113 | /* mark as invalid */ |
114 | length = len + 1; | |
115 | return; | |
e6ccf245 | 116 | } |
62e76326 | 117 | |
41d00cd3 | 118 | memcpy(this, buf, sizeof(icp_common_t)); |
e6ccf245 | 119 | /* |
120 | * Convert network order sensitive fields | |
121 | */ | |
122 | length = ntohs(length); | |
123 | reqnum = ntohl(reqnum); | |
124 | flags = ntohl(flags); | |
125 | pad = ntohl(pad); | |
126 | } | |
127 | ||
128 | icp_opcode | |
cb835136 | 129 | icp_common_t::getOpCode() const |
e6ccf245 | 130 | { |
2fefc43c | 131 | if (opcode > static_cast<char>(icp_opcode::ICP_END)) |
62e76326 | 132 | return ICP_INVALID; |
133 | ||
2fefc43c | 134 | return static_cast<icp_opcode>(opcode); |
e6ccf245 | 135 | } |
136 | ||
137 | /* ICPState */ | |
138 | ||
63be0a78 | 139 | ICPState::ICPState(icp_common_t &aHeader, HttpRequest *aRequest): |
f53969cc SM |
140 | header(aHeader), |
141 | request(aRequest), | |
142 | fd(-1), | |
aee3523a | 143 | url(nullptr) |
b248c2a3 AJ |
144 | { |
145 | HTTPMSGLOCK(request); | |
146 | } | |
e6ccf245 | 147 | |
148 | ICPState::~ICPState() | |
149 | { | |
150 | safe_free(url); | |
f72fb56b | 151 | HTTPMSGUNLOCK(request); |
e6ccf245 | 152 | } |
153 | ||
819be284 | 154 | bool |
7976fed3 EB |
155 | ICPState::isHit() const |
156 | { | |
157 | const auto e = storeGetPublic(url, Http::METHOD_GET); | |
158 | ||
159 | const auto hit = e && confirmAndPrepHit(*e); | |
160 | ||
161 | if (e) | |
d868b138 | 162 | e->abandon(__func__); |
7976fed3 EB |
163 | |
164 | return hit; | |
165 | } | |
166 | ||
167 | bool | |
168 | ICPState::confirmAndPrepHit(const StoreEntry &e) const | |
819be284 | 169 | { |
819be284 EB |
170 | if (!e.validToSend()) |
171 | return false; | |
172 | ||
173 | if (!Config.onoff.icp_hit_stale && refreshCheckICP(&e, request)) | |
174 | return false; | |
175 | ||
d2a6dcba | 176 | if (e.hittingRequiresCollapsing() && !startCollapsingOn(e, false)) |
819be284 EB |
177 | return false; |
178 | ||
179 | return true; | |
180 | } | |
181 | ||
d2a6dcba | 182 | LogTags * |
7976fed3 | 183 | ICPState::loggingTags() const |
d2a6dcba EB |
184 | { |
185 | // calling icpSyncAle(LOG_TAG_NONE) here would not change cache.code | |
186 | if (!al) | |
187 | al = new AccessLogEntry(); | |
188 | return &al->cache.code; | |
189 | } | |
190 | ||
819be284 EB |
191 | void |
192 | ICPState::fillChecklist(ACLFilledChecklist &checklist) const | |
193 | { | |
194 | checklist.setRequest(request); | |
d2a6dcba | 195 | icpSyncAle(al, from, url, 0, 0); |
819be284 EB |
196 | checklist.al = al; |
197 | } | |
198 | ||
e6ccf245 | 199 | /* End ICPState */ |
200 | ||
201 | /* ICP2State */ | |
62e76326 | 202 | |
63be0a78 | 203 | /// \ingroup ServerProtocolICPInternal2 |
819be284 | 204 | class ICP2State: public ICPState |
62e76326 | 205 | { |
206 | ||
207 | public: | |
f72fb56b | 208 | ICP2State(icp_common_t & aHeader, HttpRequest *aRequest): |
f53969cc | 209 | ICPState(aHeader, aRequest),rtt(0),src_rtt(0),flags(0) {} |
62e76326 | 210 | |
337b9aa4 | 211 | ~ICP2State() override; |
e6ccf245 | 212 | |
213 | int rtt; | |
214 | int src_rtt; | |
09aabd84 | 215 | uint32_t flags; |
e6ccf245 | 216 | }; |
217 | ||
63be0a78 | 218 | ICP2State::~ICP2State() |
62e76326 | 219 | {} |
e6ccf245 | 220 | |
e6ccf245 | 221 | /* End ICP2State */ |
222 | ||
d2a6dcba | 223 | /// updates ALE (if any) and logs the transaction (if needed) |
7a2f978b | 224 | static void |
d2a6dcba | 225 | icpLogIcp(const Ip::Address &caddr, const LogTags_ot logcode, const int len, const char *url, int delay, AccessLogEntry::Pointer &al) |
7a2f978b | 226 | { |
d2a6dcba | 227 | assert(logcode != LOG_TAG_NONE); |
62e76326 | 228 | |
d2a6dcba EB |
229 | // Optimization: No premature (ALE creation in) icpSyncAle(). |
230 | if (al) { | |
231 | icpSyncAle(al, caddr, url, len, delay); | |
232 | al->cache.code.update(logcode); | |
233 | } | |
62e76326 | 234 | |
d2a6dcba EB |
235 | if (logcode == LOG_ICP_QUERY) |
236 | return; // we never log queries | |
62e76326 | 237 | |
d2a6dcba EB |
238 | if (!Config.onoff.log_udp) { |
239 | clientdbUpdate(caddr, al ? al->cache.code : LogTags(logcode), AnyP::PROTO_ICP, len); | |
62e76326 | 240 | return; |
d2a6dcba | 241 | } |
62e76326 | 242 | |
d2a6dcba EB |
243 | if (!al) { |
244 | // The above attempt to optimize ALE creation has failed. We do need it. | |
245 | icpSyncAle(al, caddr, url, len, delay); | |
246 | al->cache.code.update(logcode); | |
247 | } | |
248 | clientdbUpdate(caddr, al->cache.code, AnyP::PROTO_ICP, len); | |
aee3523a | 249 | accessLogLog(al, nullptr); |
7a2f978b | 250 | } |
251 | ||
63be0a78 | 252 | /// \ingroup ServerProtocolICPInternal2 |
8b082ed9 | 253 | static void |
ced8def3 | 254 | icpUdpSendQueue(int fd, void *) |
7a2f978b | 255 | { |
d2a6dcba | 256 | DelayedUdpSend *q; |
62e76326 | 257 | |
aee3523a | 258 | while ((q = IcpQueueHead) != nullptr) { |
b115733c | 259 | int delay = tvSubUsec(q->queue_time, current_time); |
62e76326 | 260 | /* increment delay to prevent looping */ |
d2a6dcba | 261 | const int x = icpUdpSend(fd, q->address, q->msg, ++delay, q->ale); |
62e76326 | 262 | IcpQueueHead = q->next; |
d2a6dcba | 263 | delete q; |
62e76326 | 264 | |
265 | if (x < 0) | |
266 | break; | |
7a2f978b | 267 | } |
268 | } | |
269 | ||
cb835136 AR |
270 | icp_common_t * |
271 | icp_common_t::CreateMessage( | |
7a2f978b | 272 | icp_opcode opcode, |
273 | int flags, | |
274 | const char *url, | |
275 | int reqnum, | |
276 | int pad) | |
277 | { | |
aee3523a AR |
278 | char *buf = nullptr; |
279 | icp_common_t *headerp = nullptr; | |
280 | char *urloffset = nullptr; | |
7a2f978b | 281 | int buf_len; |
282 | buf_len = sizeof(icp_common_t) + strlen(url) + 1; | |
62e76326 | 283 | |
27cd7235 | 284 | if (opcode == ICP_QUERY) |
09aabd84 | 285 | buf_len += sizeof(uint32_t); |
62e76326 | 286 | |
e6ccf245 | 287 | buf = (char *) xcalloc(buf_len, 1); |
62e76326 | 288 | |
7a2f978b | 289 | headerp = (icp_common_t *) (void *) buf; |
62e76326 | 290 | |
79d39a72 | 291 | headerp->opcode = (char) opcode; |
62e76326 | 292 | |
7a2f978b | 293 | headerp->version = ICP_VERSION_CURRENT; |
62e76326 | 294 | |
09aabd84 | 295 | headerp->length = (uint16_t) htons(buf_len); |
62e76326 | 296 | |
7a2f978b | 297 | headerp->reqnum = htonl(reqnum); |
62e76326 | 298 | |
7a2f978b | 299 | headerp->flags = htonl(flags); |
62e76326 | 300 | |
7a2f978b | 301 | headerp->pad = htonl(pad); |
62e76326 | 302 | |
9ca56f05 | 303 | headerp->shostid = 0; |
62e76326 | 304 | |
7a2f978b | 305 | urloffset = buf + sizeof(icp_common_t); |
62e76326 | 306 | |
27cd7235 | 307 | if (opcode == ICP_QUERY) |
09aabd84 | 308 | urloffset += sizeof(uint32_t); |
62e76326 | 309 | |
41d00cd3 | 310 | memcpy(urloffset, url, strlen(url)); |
62e76326 | 311 | |
e6ccf245 | 312 | return (icp_common_t *)buf; |
7a2f978b | 313 | } |
314 | ||
d2a6dcba EB |
315 | // TODO: Move retries to icpCreateAndSend(); the other caller does not retry. |
316 | /// writes the given UDP msg to the socket; queues a retry on the first failure | |
317 | /// \returns a negative number on failures | |
318 | static int | |
7a2f978b | 319 | icpUdpSend(int fd, |
b7ac5457 | 320 | const Ip::Address &to, |
62e76326 | 321 | icp_common_t * msg, |
819be284 EB |
322 | int delay, |
323 | AccessLogEntryPointer al) | |
7a2f978b | 324 | { |
8e68922c | 325 | int x; |
17b6e784 | 326 | int len; |
327 | len = (int) ntohs(msg->length); | |
bf8fe701 | 328 | debugs(12, 5, "icpUdpSend: FD " << fd << " sending " << |
cc192b50 | 329 | icp_opcode_str[msg->opcode] << ", " << len << " bytes to " << to); |
bf8fe701 | 330 | |
cc192b50 | 331 | x = comm_udp_sendto(fd, to, msg, len); |
62e76326 | 332 | |
26ac0430 | 333 | if (x >= 0) { |
62e76326 | 334 | /* successfully written */ |
d2a6dcba | 335 | const auto logcode = icpLogFromICPCode(static_cast<icp_opcode>(msg->opcode)); |
819be284 | 336 | icpLogIcp(to, logcode, len, (char *) (msg + 1), delay, al); |
62e76326 | 337 | icpCount(msg, SENT, (size_t) len, delay); |
338 | safe_free(msg); | |
26ac0430 | 339 | } else if (0 == delay) { |
62e76326 | 340 | /* send failed, but queue it */ |
d2a6dcba | 341 | const auto queue = new DelayedUdpSend(); |
cc192b50 | 342 | queue->address = to; |
62e76326 | 343 | queue->msg = msg; |
62e76326 | 344 | queue->queue_time = current_time; |
d2a6dcba | 345 | queue->ale = al; |
62e76326 | 346 | |
aee3523a | 347 | if (IcpQueueHead == nullptr) { |
62e76326 | 348 | IcpQueueHead = queue; |
349 | IcpQueueTail = queue; | |
350 | } else if (IcpQueueTail == IcpQueueHead) { | |
351 | IcpQueueTail = queue; | |
352 | IcpQueueHead->next = queue; | |
353 | } else { | |
354 | IcpQueueTail->next = queue; | |
355 | IcpQueueTail = queue; | |
356 | } | |
357 | ||
aee3523a | 358 | Comm::SetSelect(fd, COMM_SELECT_WRITE, icpUdpSendQueue, nullptr, 0); |
95dc7ff4 | 359 | ++statCounter.icp.replies_queued; |
26ac0430 | 360 | } else { |
62e76326 | 361 | /* don't queue it */ |
d2a6dcba | 362 | // XXX: safe_free(msg) |
95dc7ff4 | 363 | ++statCounter.icp.replies_dropped; |
8e68922c | 364 | } |
62e76326 | 365 | |
17b6e784 | 366 | return x; |
7a2f978b | 367 | } |
368 | ||
63be0a78 | 369 | /** |
bef81ea5 | 370 | * This routine selects an ICP opcode for ICP misses. |
63be0a78 | 371 | * |
372 | \retval ICP_ERR no opcode selected here | |
373 | \retval ICP_MISS_NOFETCH store is rebuilding, no fetch is possible yet | |
bef81ea5 | 374 | */ |
e6ccf245 | 375 | icp_opcode |
376 | icpGetCommonOpcode() | |
377 | { | |
bef81ea5 | 378 | /* if store is rebuilding, return a UDP_MISS_NOFETCH */ |
62e76326 | 379 | |
871899ca | 380 | if ((StoreController::store_dirs_rebuilding && opt_reload_hit_only) || |
62e76326 | 381 | hit_only_mode_until > squid_curtime) { |
382 | return ICP_MISS_NOFETCH; | |
e6ccf245 | 383 | } |
62e76326 | 384 | |
e6ccf245 | 385 | return ICP_ERR; |
386 | } | |
387 | ||
d2a6dcba | 388 | static LogTags_ot |
e6ccf245 | 389 | icpLogFromICPCode(icp_opcode opcode) |
390 | { | |
391 | if (opcode == ICP_ERR) | |
62e76326 | 392 | return LOG_UDP_INVALID; |
393 | ||
e6ccf245 | 394 | if (opcode == ICP_DENIED) |
62e76326 | 395 | return LOG_UDP_DENIED; |
396 | ||
e6ccf245 | 397 | if (opcode == ICP_HIT) |
62e76326 | 398 | return LOG_UDP_HIT; |
399 | ||
e6ccf245 | 400 | if (opcode == ICP_MISS) |
62e76326 | 401 | return LOG_UDP_MISS; |
402 | ||
e6ccf245 | 403 | if (opcode == ICP_MISS_NOFETCH) |
62e76326 | 404 | return LOG_UDP_MISS_NOFETCH; |
405 | ||
d2a6dcba EB |
406 | if (opcode == ICP_DECHO) |
407 | return LOG_ICP_QUERY; | |
408 | ||
409 | if (opcode == ICP_QUERY) | |
410 | return LOG_ICP_QUERY; | |
411 | ||
e6ccf245 | 412 | fatal("expected ICP opcode\n"); |
62e76326 | 413 | |
e6ccf245 | 414 | return LOG_UDP_INVALID; |
415 | } | |
416 | ||
417 | void | |
819be284 | 418 | icpCreateAndSend(icp_opcode opcode, int flags, char const *url, int reqnum, int pad, int fd, const Ip::Address &from, AccessLogEntry::Pointer al) |
e6ccf245 | 419 | { |
d2a6dcba EB |
420 | // update potentially shared ALE ASAP; the ICP query itself may be delayed |
421 | if (al) | |
422 | al->cache.code.update(icpLogFromICPCode(opcode)); | |
cb835136 | 423 | icp_common_t *reply = icp_common_t::CreateMessage(opcode, flags, url, reqnum, pad); |
d2a6dcba | 424 | icpUdpSend(fd, from, reply, 0, al); |
e6ccf245 | 425 | } |
426 | ||
427 | void | |
b7ac5457 | 428 | icpDenyAccess(Ip::Address &from, char *url, int reqnum, int fd) |
e6ccf245 | 429 | { |
26ac0430 | 430 | if (clientdbCutoffDenied(from)) { |
62e76326 | 431 | /* |
432 | * count this DENIED query in the clientdb, even though | |
433 | * we're not sending an ICP reply... | |
434 | */ | |
d2a6dcba | 435 | clientdbUpdate(from, LogTags(LOG_UDP_DENIED), AnyP::PROTO_ICP, 0); |
26ac0430 | 436 | } else { |
819be284 | 437 | icpCreateAndSend(ICP_DENIED, 0, url, reqnum, 0, fd, from, nullptr); |
e6ccf245 | 438 | } |
439 | } | |
440 | ||
2efeb0b7 | 441 | bool |
b7ac5457 | 442 | icpAccessAllowed(Ip::Address &from, HttpRequest * icp_request) |
7a2f978b | 443 | { |
25aa6c9a AR |
444 | if (!Config.accessList.icp) { |
445 | debugs(12, 2, "Access Denied due to lack of ICP access rules."); | |
638402dd | 446 | return false; |
25aa6c9a | 447 | } |
b50e327b | 448 | |
aee3523a | 449 | ACLFilledChecklist checklist(Config.accessList.icp, icp_request, nullptr); |
cc192b50 | 450 | checklist.src_addr = from; |
4dd643d5 | 451 | checklist.my_addr.setNoAddr(); |
25aa6c9a AR |
452 | const auto &answer = checklist.fastCheck(); |
453 | if (answer.allowed()) | |
454 | return true; | |
455 | ||
456 | debugs(12, 2, "Access Denied for " << from << " by " << answer.lastCheckDescription() << "."); | |
457 | return false; | |
e6ccf245 | 458 | } |
459 | ||
190154cf | 460 | HttpRequest * |
b7ac5457 | 461 | icpGetRequest(char *url, int reqnum, int fd, Ip::Address &from) |
e6ccf245 | 462 | { |
26ac0430 | 463 | if (strpbrk(url, w_space)) { |
62e76326 | 464 | url = rfc1738_escape(url); |
819be284 | 465 | icpCreateAndSend(ICP_ERR, 0, rfc1738_escape(url), reqnum, 0, fd, from, nullptr); |
aee3523a | 466 | return nullptr; |
e6ccf245 | 467 | } |
62e76326 | 468 | |
ad05b958 | 469 | const auto mx = MasterXaction::MakePortless<XactionInitiator::initIcp>(); |
6c880a16 AJ |
470 | auto *result = HttpRequest::FromUrlXXX(url, mx); |
471 | if (!result) | |
819be284 | 472 | icpCreateAndSend(ICP_ERR, 0, url, reqnum, 0, fd, from, nullptr); |
62e76326 | 473 | |
e6ccf245 | 474 | return result; |
475 | ||
476 | } | |
477 | ||
478 | static void | |
b7ac5457 | 479 | doV2Query(int fd, Ip::Address &from, char *buf, icp_common_t header) |
e6ccf245 | 480 | { |
481 | int rtt = 0; | |
7a2f978b | 482 | int src_rtt = 0; |
09aabd84 | 483 | uint32_t flags = 0; |
e6ccf245 | 484 | /* We have a valid packet */ |
09aabd84 | 485 | char *url = buf + sizeof(icp_common_t) + sizeof(uint32_t); |
cc192b50 | 486 | HttpRequest *icp_request = icpGetRequest(url, header.reqnum, fd, from); |
62e76326 | 487 | |
e6ccf245 | 488 | if (!icp_request) |
62e76326 | 489 | return; |
490 | ||
6dd9f4bd | 491 | HTTPMSGLOCK(icp_request); |
319bf5a7 | 492 | |
26ac0430 | 493 | if (!icpAccessAllowed(from, icp_request)) { |
cc192b50 | 494 | icpDenyAccess(from, url, header.reqnum, fd); |
6dd9f4bd | 495 | HTTPMSGUNLOCK(icp_request); |
62e76326 | 496 | return; |
e6ccf245 | 497 | } |
9b5c4a9a | 498 | #if USE_ICMP |
26ac0430 | 499 | if (header.flags & ICP_FLAG_SRC_RTT) { |
5c51bffb AJ |
500 | rtt = netdbHostRtt(icp_request->url.host()); |
501 | int hops = netdbHostHops(icp_request->url.host()); | |
62e76326 | 502 | src_rtt = ((hops & 0xFFFF) << 16) | (rtt & 0xFFFF); |
503 | ||
504 | if (rtt) | |
505 | flags |= ICP_FLAG_SRC_RTT; | |
e6ccf245 | 506 | } |
9b5c4a9a | 507 | #endif /* USE_ICMP */ |
62e76326 | 508 | |
e6ccf245 | 509 | /* The peer is allowed to use this cache */ |
7976fed3 EB |
510 | ICP2State state(header, icp_request); |
511 | state.fd = fd; | |
512 | state.from = from; | |
513 | state.url = xstrdup(url); | |
514 | state.flags = flags; | |
515 | state.rtt = rtt; | |
516 | state.src_rtt = src_rtt; | |
62e76326 | 517 | |
7976fed3 EB |
518 | icp_opcode codeToSend; |
519 | ||
520 | if (state.isHit()) { | |
521 | codeToSend = ICP_HIT; | |
522 | } else { | |
523 | #if USE_ICMP | |
524 | if (Config.onoff.test_reachability && state.rtt == 0) { | |
525 | if ((state.rtt = netdbHostRtt(state.request->url.host())) == 0) | |
526 | netdbPingSite(state.request->url.host()); | |
527 | } | |
528 | #endif /* USE_ICMP */ | |
529 | ||
530 | if (icpGetCommonOpcode() != ICP_ERR) | |
531 | codeToSend = icpGetCommonOpcode(); | |
532 | else if (Config.onoff.test_reachability && rtt == 0) | |
533 | codeToSend = ICP_MISS_NOFETCH; | |
534 | else | |
535 | codeToSend = ICP_MISS; | |
536 | } | |
537 | ||
538 | icpCreateAndSend(codeToSend, flags, url, header.reqnum, src_rtt, fd, from, state.al); | |
319bf5a7 | 539 | |
6dd9f4bd | 540 | HTTPMSGUNLOCK(icp_request); |
e6ccf245 | 541 | } |
542 | ||
543 | void | |
cb835136 | 544 | icp_common_t::handleReply(char *buf, Ip::Address &from) |
e6ccf245 | 545 | { |
26ac0430 | 546 | if (neighbors_do_private_keys && reqnum == 0) { |
fa84c01d FC |
547 | debugs(12, DBG_CRITICAL, "icpHandleIcpV2: Neighbor " << from << " returned reqnum = 0"); |
548 | debugs(12, DBG_CRITICAL, "icpHandleIcpV2: Disabling use of private keys"); | |
62e76326 | 549 | neighbors_do_private_keys = 0; |
e6ccf245 | 550 | } |
62e76326 | 551 | |
e6ccf245 | 552 | char *url = buf + sizeof(icp_common_t); |
cc192b50 | 553 | debugs(12, 3, "icpHandleIcpV2: " << icp_opcode_str[opcode] << " from " << from << " for '" << url << "'"); |
bf8fe701 | 554 | |
e6ccf245 | 555 | const cache_key *key = icpGetCacheKey(url, (int) reqnum); |
556 | /* call neighborsUdpAck even if ping_status != PING_WAITING */ | |
557 | neighborsUdpAck(key, this, from); | |
558 | } | |
559 | ||
560 | static void | |
b7ac5457 | 561 | icpHandleIcpV2(int fd, Ip::Address &from, char *buf, int len) |
e6ccf245 | 562 | { |
26ac0430 | 563 | if (len <= 0) { |
bf8fe701 | 564 | debugs(12, 3, "icpHandleIcpV2: ICP message is too small"); |
62e76326 | 565 | return; |
e6ccf245 | 566 | } |
62e76326 | 567 | |
e6ccf245 | 568 | icp_common_t header(buf, len); |
7b83b3d9 | 569 | /* |
570 | * Length field should match the number of bytes read | |
571 | */ | |
62e76326 | 572 | |
26ac0430 | 573 | if (len != header.length) { |
bf8fe701 | 574 | debugs(12, 3, "icpHandleIcpV2: ICP message is too small"); |
62e76326 | 575 | return; |
7b83b3d9 | 576 | } |
62e76326 | 577 | |
7976fed3 EB |
578 | debugs(12, 5, "OPCODE " << icp_opcode_str[header.getOpCode()] << '=' << uint8_t(header.opcode)); |
579 | ||
26ac0430 | 580 | switch (header.opcode) { |
62e76326 | 581 | |
27cd7235 | 582 | case ICP_QUERY: |
62e76326 | 583 | /* We have a valid packet */ |
584 | doV2Query(fd, from, buf, header); | |
585 | break; | |
7a2f978b | 586 | |
27cd7235 | 587 | case ICP_HIT: |
62e76326 | 588 | |
27cd7235 | 589 | case ICP_DECHO: |
62e76326 | 590 | |
27cd7235 | 591 | case ICP_MISS: |
62e76326 | 592 | |
27cd7235 | 593 | case ICP_DENIED: |
62e76326 | 594 | |
27cd7235 | 595 | case ICP_MISS_NOFETCH: |
cc192b50 | 596 | header.handleReply(buf, from); |
62e76326 | 597 | break; |
7a2f978b | 598 | |
27cd7235 | 599 | case ICP_INVALID: |
62e76326 | 600 | |
27cd7235 | 601 | case ICP_ERR: |
62e76326 | 602 | break; |
7a2f978b | 603 | |
604 | default: | |
d816f28d | 605 | debugs(12, DBG_CRITICAL, "ERROR: icpHandleIcpV2: Unknown opcode: " << header.opcode << " from " << from); |
bf8fe701 | 606 | |
62e76326 | 607 | break; |
7a2f978b | 608 | } |
7a2f978b | 609 | } |
610 | ||
7a2f978b | 611 | void |
ced8def3 | 612 | icpHandleUdp(int sock, void *) |
7a2f978b | 613 | { |
d193a436 | 614 | int *N = &incoming_sockets_accepted; |
62e76326 | 615 | |
b7ac5457 | 616 | Ip::Address from; |
7a2f978b | 617 | LOCAL_ARRAY(char, buf, SQUID_UDP_SO_RCVBUF); |
0b6d1955 | 618 | int len; |
7a2f978b | 619 | int icp_version; |
65d448bc | 620 | int max = INCOMING_UDP_MAX; |
aee3523a | 621 | Comm::SetSelect(sock, COMM_SELECT_READ, icpHandleUdp, nullptr, 0); |
62e76326 | 622 | |
5e263176 FC |
623 | while (max) { |
624 | --max; | |
62e76326 | 625 | len = comm_udp_recvfrom(sock, |
626 | buf, | |
627 | SQUID_UDP_SO_RCVBUF - 1, | |
628 | 0, | |
cc192b50 | 629 | from); |
62e76326 | 630 | |
631 | if (len == 0) | |
632 | break; | |
633 | ||
634 | if (len < 0) { | |
b69e9ffa AJ |
635 | int xerrno = errno; |
636 | if (ignoreErrno(xerrno)) | |
62e76326 | 637 | break; |
638 | ||
1191b93b | 639 | #if _SQUID_LINUX_ |
62e76326 | 640 | /* Some Linux systems seem to set the FD for reading and then |
641 | * return ECONNREFUSED when sendto() fails and generates an ICMP | |
642 | * port unreachable message. */ | |
643 | /* or maybe an EHOSTUNREACH "No route to host" message */ | |
b69e9ffa | 644 | if (xerrno != ECONNREFUSED && xerrno != EHOSTUNREACH) |
7a2f978b | 645 | #endif |
b69e9ffa | 646 | debugs(50, DBG_IMPORTANT, "icpHandleUdp: FD " << sock << " recvfrom: " << xstrerr(xerrno)); |
62e76326 | 647 | |
648 | break; | |
649 | } | |
650 | ||
95dc7ff4 | 651 | ++(*N); |
62e76326 | 652 | icpCount(buf, RECV, (size_t) len, 0); |
653 | buf[len] = '\0'; | |
bf8fe701 | 654 | debugs(12, 4, "icpHandleUdp: FD " << sock << ": received " << |
cc192b50 | 655 | (unsigned long int)len << " bytes from " << from); |
bf8fe701 | 656 | |
0b6d1955 | 657 | if ((size_t) len < sizeof(icp_common_t)) { |
bf8fe701 | 658 | debugs(12, 4, "icpHandleUdp: Ignoring too-small UDP packet"); |
62e76326 | 659 | break; |
660 | } | |
661 | ||
f53969cc | 662 | icp_version = (int) buf[1]; /* cheat! */ |
62e76326 | 663 | |
1b76e6c1 | 664 | if (icpOutgoingConn->local == from) |
7c8931a1 AJ |
665 | // ignore ICP packets which loop back (multicast usually) |
666 | debugs(12, 4, "icpHandleUdp: Ignoring UDP packet sent by myself"); | |
667 | else if (icp_version == ICP_VERSION_2) | |
62e76326 | 668 | icpHandleIcpV2(sock, from, buf, len); |
669 | else if (icp_version == ICP_VERSION_3) | |
670 | icpHandleIcpV3(sock, from, buf, len); | |
671 | else | |
e0236918 | 672 | debugs(12, DBG_IMPORTANT, "WARNING: Unused ICP version " << icp_version << |
26ac0430 | 673 | " received from " << from); |
7a2f978b | 674 | } |
7a2f978b | 675 | } |
15df8349 | 676 | |
677 | void | |
65d448bc | 678 | icpOpenPorts(void) |
15df8349 | 679 | { |
09aabd84 | 680 | uint16_t port; |
62e76326 | 681 | |
15df8349 | 682 | if ((port = Config.Port.icp) <= 0) |
62e76326 | 683 | return; |
684 | ||
e0d28505 AJ |
685 | icpIncomingConn = new Comm::Connection; |
686 | icpIncomingConn->local = Config.Addrs.udp_incoming; | |
4dd643d5 | 687 | icpIncomingConn->local.port(port); |
62e76326 | 688 | |
4dd643d5 | 689 | if (!Ip::EnableIpv6 && !icpIncomingConn->local.setIPv4()) { |
e0d28505 | 690 | debugs(12, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << icpIncomingConn->local << " is not an IPv4 address."); |
e0f8b709 AJ |
691 | fatal("ICP port cannot be opened."); |
692 | } | |
e0209dae | 693 | /* split-stack for now requires default IPv4-only ICP */ |
4dd643d5 AJ |
694 | if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && icpIncomingConn->local.isAnyAddr()) { |
695 | icpIncomingConn->local.setIPv4(); | |
e0209dae | 696 | } |
e0f8b709 | 697 | |
e5ddd4ce | 698 | auto call = asyncCallbackFun(12, 2, icpIncomingConnectionOpened); |
013e320c | 699 | Ipc::StartListening(SOCK_DGRAM, |
5667a628 | 700 | IPPROTO_UDP, |
e0d28505 | 701 | icpIncomingConn, |
5667a628 | 702 | Ipc::fdnInIcpSocket, call); |
62e76326 | 703 | |
4dd643d5 | 704 | if ( !Config.Addrs.udp_outgoing.isNoAddr() ) { |
e0d28505 AJ |
705 | icpOutgoingConn = new Comm::Connection; |
706 | icpOutgoingConn->local = Config.Addrs.udp_outgoing; | |
4dd643d5 | 707 | icpOutgoingConn->local.port(port); |
e0f8b709 | 708 | |
4dd643d5 | 709 | if (!Ip::EnableIpv6 && !icpOutgoingConn->local.setIPv4()) { |
e0d28505 | 710 | debugs(49, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << icpOutgoingConn->local << " is not an IPv4 address."); |
e0f8b709 AJ |
711 | fatal("ICP port cannot be opened."); |
712 | } | |
e0209dae | 713 | /* split-stack for now requires default IPv4-only ICP */ |
4dd643d5 AJ |
714 | if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && icpOutgoingConn->local.isAnyAddr()) { |
715 | icpOutgoingConn->local.setIPv4(); | |
e0209dae | 716 | } |
e0f8b709 | 717 | |
e0d28505 AJ |
718 | enter_suid(); |
719 | comm_open_listener(SOCK_DGRAM, IPPROTO_UDP, icpOutgoingConn, "Outgoing ICP Port"); | |
62e76326 | 720 | leave_suid(); |
721 | ||
e0d28505 | 722 | if (!Comm::IsConnOpen(icpOutgoingConn)) |
62e76326 | 723 | fatal("Cannot open Outgoing ICP Port"); |
724 | ||
e0d28505 | 725 | debugs(12, DBG_CRITICAL, "Sending ICP messages from " << icpOutgoingConn->local); |
62e76326 | 726 | |
aee3523a | 727 | Comm::SetSelect(icpOutgoingConn->fd, COMM_SELECT_READ, icpHandleUdp, nullptr, 0); |
25b481e6 | 728 | fd_note(icpOutgoingConn->fd, "Outgoing ICP socket"); |
15df8349 | 729 | } |
236dedc0 | 730 | } |
62e76326 | 731 | |
013e320c | 732 | static void |
e5ddd4ce | 733 | icpIncomingConnectionOpened(Ipc::StartListeningAnswer &answer) |
013e320c | 734 | { |
e5ddd4ce AR |
735 | const auto &conn = answer.conn; |
736 | ||
09cee743 | 737 | if (!Comm::IsConnOpen(conn)) |
013e320c AR |
738 | fatal("Cannot open ICP Port"); |
739 | ||
aee3523a | 740 | Comm::SetSelect(conn->fd, COMM_SELECT_READ, icpHandleUdp, nullptr, 0); |
013e320c AR |
741 | |
742 | for (const wordlist *s = Config.mcast_group_list; s; s = s->next) | |
aee3523a | 743 | ipcache_nbgethostbyname(s->key, mcastJoinGroups, nullptr); // XXX: pass the conn for mcastJoinGroups usage. |
013e320c | 744 | |
09cee743 | 745 | debugs(12, DBG_IMPORTANT, "Accepting ICP messages on " << conn->local); |
013e320c | 746 | |
09cee743 | 747 | fd_note(conn->fd, "Incoming ICP port"); |
013e320c | 748 | |
4dd643d5 | 749 | if (Config.Addrs.udp_outgoing.isNoAddr()) { |
09cee743 | 750 | icpOutgoingConn = conn; |
e0d28505 AJ |
751 | debugs(12, DBG_IMPORTANT, "Sending ICP messages from " << icpOutgoingConn->local); |
752 | } | |
013e320c AR |
753 | } |
754 | ||
63be0a78 | 755 | /** |
26ac0430 | 756 | * icpConnectionShutdown only closes the 'in' socket if it is |
17e6c0a1 | 757 | * different than the 'out' socket. |
758 | */ | |
c0fbae16 | 759 | void |
17e6c0a1 | 760 | icpConnectionShutdown(void) |
c0fbae16 | 761 | { |
e0d28505 | 762 | if (!Comm::IsConnOpen(icpIncomingConn)) |
62e76326 | 763 | return; |
764 | ||
e0d28505 | 765 | debugs(12, DBG_IMPORTANT, "Stop receiving ICP on " << icpIncomingConn->local); |
62e76326 | 766 | |
e0d28505 AJ |
767 | /** Release the 'in' socket for lazy closure. |
768 | * in and out sockets may be sharing one same FD. | |
769 | * This prevents this function from executing repeatedly. | |
c0fbae16 | 770 | */ |
aee3523a | 771 | icpIncomingConn = nullptr; |
62e76326 | 772 | |
63be0a78 | 773 | /** |
c0fbae16 | 774 | * Normally we only write to the outgoing ICP socket, but |
775 | * we also have a read handler there to catch messages sent | |
776 | * to that specific interface. During shutdown, we must | |
777 | * disable reading on the outgoing socket. | |
778 | */ | |
e0d28505 | 779 | assert(Comm::IsConnOpen(icpOutgoingConn)); |
62e76326 | 780 | |
aee3523a | 781 | Comm::SetSelect(icpOutgoingConn->fd, COMM_SELECT_READ, nullptr, nullptr, 0); |
c0fbae16 | 782 | } |
17e6c0a1 | 783 | |
784 | void | |
65d448bc | 785 | icpClosePorts(void) |
17e6c0a1 | 786 | { |
787 | icpConnectionShutdown(); | |
62e76326 | 788 | |
aee3523a | 789 | if (icpOutgoingConn != nullptr) { |
e0d28505 | 790 | debugs(12, DBG_IMPORTANT, "Stop sending ICP from " << icpOutgoingConn->local); |
aee3523a | 791 | icpOutgoingConn = nullptr; |
17e6c0a1 | 792 | } |
793 | } | |
071a3ae7 | 794 | |
795 | static void | |
796 | icpCount(void *buf, int which, size_t len, int delay) | |
797 | { | |
e6ccf245 | 798 | icp_common_t *icp = (icp_common_t *) buf; |
62e76326 | 799 | |
071a3ae7 | 800 | if (len < sizeof(*icp)) |
62e76326 | 801 | return; |
802 | ||
071a3ae7 | 803 | if (SENT == which) { |
95dc7ff4 | 804 | ++statCounter.icp.pkts_sent; |
a0864754 | 805 | statCounter.icp.kbytes_sent += len; |
62e76326 | 806 | |
807 | if (ICP_QUERY == icp->opcode) { | |
95dc7ff4 | 808 | ++statCounter.icp.queries_sent; |
a0864754 | 809 | statCounter.icp.q_kbytes_sent += len; |
62e76326 | 810 | } else { |
95dc7ff4 | 811 | ++statCounter.icp.replies_sent; |
a0864754 | 812 | statCounter.icp.r_kbytes_sent += len; |
62e76326 | 813 | /* this is the sent-reply service time */ |
e8baef82 | 814 | statCounter.icp.replySvcTime.count(delay); |
62e76326 | 815 | } |
816 | ||
817 | if (ICP_HIT == icp->opcode) | |
95dc7ff4 | 818 | ++statCounter.icp.hits_sent; |
071a3ae7 | 819 | } else if (RECV == which) { |
95dc7ff4 | 820 | ++statCounter.icp.pkts_recv; |
a0864754 | 821 | statCounter.icp.kbytes_recv += len; |
62e76326 | 822 | |
823 | if (ICP_QUERY == icp->opcode) { | |
95dc7ff4 | 824 | ++statCounter.icp.queries_recv; |
a0864754 | 825 | statCounter.icp.q_kbytes_recv += len; |
62e76326 | 826 | } else { |
95dc7ff4 | 827 | ++statCounter.icp.replies_recv; |
a0864754 | 828 | statCounter.icp.r_kbytes_recv += len; |
e8baef82 | 829 | /* statCounter.icp.querySvcTime set in clientUpdateCounters */ |
62e76326 | 830 | } |
831 | ||
832 | if (ICP_HIT == icp->opcode) | |
95dc7ff4 | 833 | ++statCounter.icp.hits_recv; |
071a3ae7 | 834 | } |
835 | } | |
007b8be4 | 836 | |
837 | #define N_QUERIED_KEYS 8192 | |
838 | #define N_QUERIED_KEYS_MASK 8191 | |
c3031d67 | 839 | static cache_key queried_keys[N_QUERIED_KEYS][SQUID_MD5_DIGEST_LENGTH]; |
007b8be4 | 840 | |
841 | int | |
5942e8d4 | 842 | icpSetCacheKey(const cache_key * key) |
007b8be4 | 843 | { |
844 | static int reqnum = 0; | |
62e76326 | 845 | |
007b8be4 | 846 | if (++reqnum < 0) |
62e76326 | 847 | reqnum = 1; |
848 | ||
007b8be4 | 849 | storeKeyCopy(queried_keys[reqnum & N_QUERIED_KEYS_MASK], key); |
62e76326 | 850 | |
007b8be4 | 851 | return reqnum; |
852 | } | |
853 | ||
854 | const cache_key * | |
855 | icpGetCacheKey(const char *url, int reqnum) | |
856 | { | |
857 | if (neighbors_do_private_keys && reqnum) | |
62e76326 | 858 | return queried_keys[reqnum & N_QUERIED_KEYS_MASK]; |
859 | ||
c2a7cefd | 860 | return storeKeyPublic(url, Http::METHOD_GET); |
007b8be4 | 861 | } |
f53969cc | 862 |