]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm/ConnOpener.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / comm / ConnOpener.cc
CommitLineData
482dcd01 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
bbc27441
AJ
3 *
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.
482dcd01
AJ
7 */
8
bbc27441
AJ
9/* DEBUG: section 05 Socket Connection Opener */
10
f7f3304a 11#include "squid.h"
a011edee 12#include "CachePeer.h"
602d9612 13#include "comm.h"
aed188fd 14#include "comm/Connection.h"
602d9612 15#include "comm/ConnOpener.h"
8bbb16e3 16#include "comm/Loops.h"
c4ad1349 17#include "fd.h"
aed188fd 18#include "fde.h"
582c2af2 19#include "globals.h"
aed188fd 20#include "icmp/net_db.h"
d9cbd42a 21#include "ip/QosConfig.h"
37abc165 22#include "ip/tools.h"
602d9612 23#include "ipcache.h"
4d5904f7 24#include "SquidConfig.h"
aed188fd 25
1a30fdf5 26#include <cerrno>
21d845b1 27
a011edee
FC
28class CachePeer;
29
a016163c 30CBDATA_NAMESPACED_CLASS_INIT(Comm, ConnOpener);
aed188fd 31
9b7992d9 32Comm::ConnOpener::ConnOpener(const Comm::ConnectionPointer &c, const AsyncCall::Pointer &handler, time_t ctimeout) :
f53969cc 33 AsyncJob("Comm::ConnOpener"),
aee3523a 34 host_(nullptr),
f53969cc
SM
35 temporaryFd_(-1),
36 conn_(c),
37 callback_(handler),
38 totalTries_(0),
39 failRetries_(0),
40 deadline_(squid_curtime + static_cast<time_t>(ctimeout))
0ce8e93b
EB
41{
42 debugs(5, 3, "will connect to " << c << " with " << ctimeout << " timeout");
2b6b1bcb
AR
43 assert(conn_); // we know where to go
44
45 // Sharing a being-modified Connection object with the caller is dangerous,
46 // but we cannot ban (or even check for) that using existing APIs. We do not
47 // want to clone "just in case" because cloning is a bit expensive, and most
48 // callers already have a non-owned Connection object to give us. Until the
49 // APIs improve, we can only check that the connection is not open.
50 assert(!conn_->isOpen());
0ce8e93b 51}
aed188fd 52
4accd6d8 53Comm::ConnOpener::~ConnOpener()
aed188fd 54{
5229395c 55 safe_free(host_);
aed188fd
AJ
56}
57
294775b5 58bool
4accd6d8 59Comm::ConnOpener::doneAll() const
294775b5 60{
5229395c 61 // is the conn_ to be opened still waiting?
aee3523a 62 if (conn_ == nullptr) {
e3a4aecc 63 return AsyncJob::doneAll();
40d9f0fc 64 }
294775b5
AJ
65
66 // is the callback still to be called?
aee3523a 67 if (callback_ == nullptr || callback_->canceled()) {
e3a4aecc 68 return AsyncJob::doneAll();
40d9f0fc 69 }
294775b5 70
923b75ce
AR
71 // otherwise, we must be waiting for something
72 Must(temporaryFd_ >= 0 || calls_.sleep_);
e3a4aecc 73 return false;
294775b5
AJ
74}
75
e52e78c4 76void
4accd6d8 77Comm::ConnOpener::swanSong()
e52e78c4 78{
aee3523a 79 if (callback_ != nullptr) {
923b75ce 80 // inform the still-waiting caller we are dying
c8407295 81 sendAnswer(Comm::ERR_CONNECT, 0, "Comm::ConnOpener::swanSong");
4590cc7d 82 }
d146d17e 83
6ca3e20d 84 // did we abort with a temporary FD assigned?
923b75ce
AR
85 if (temporaryFd_ >= 0)
86 closeFd();
87
2b6b1bcb
AR
88 // did we abort while owning an open connection?
89 if (conn_ && conn_->isOpen())
90 conn_->close();
91
6ca3e20d 92 // did we abort while waiting between retries?
923b75ce
AR
93 if (calls_.sleep_)
94 cancelSleep();
a95ff429 95
d146d17e 96 AsyncJob::swanSong();
e52e78c4
AJ
97}
98
aed188fd 99void
4accd6d8 100Comm::ConnOpener::setHost(const char * new_host)
aed188fd 101{
85d2870d 102 // unset and erase if already set.
aee3523a 103 if (host_ != nullptr)
5229395c 104 safe_free(host_);
85d2870d
AJ
105
106 // set the new one if given.
aee3523a 107 if (new_host != nullptr)
5229395c 108 host_ = xstrdup(new_host);
aed188fd
AJ
109}
110
111const char *
4accd6d8 112Comm::ConnOpener::getHost() const
aed188fd 113{
5229395c 114 return host_;
aed188fd
AJ
115}
116
5229395c
AJ
117/**
118 * Connection attempt are completed. One way or the other.
119 * Pass the results back to the external handler.
120 */
aed188fd 121void
c8407295 122Comm::ConnOpener::sendAnswer(Comm::Flag errFlag, int xerrno, const char *why)
aed188fd 123{
e884bbde 124 // only mark the address good/bad AFTER connect is finished.
aee3523a 125 if (host_ != nullptr) {
923b75ce 126 if (xerrno == 0) // XXX: should not we use errFlag instead?
e884bbde
AJ
127 ipcacheMarkGoodAddr(host_, conn_->remote);
128 else {
129 ipcacheMarkBadAddr(host_, conn_->remote);
130#if USE_ICMP
131 if (Config.onoff.test_reachability)
132 netdbDeleteAddrNetwork(conn_->remote);
133#endif
134 }
135 }
136
aee3523a 137 if (callback_ != nullptr) {
923b75ce
AR
138 // avoid scheduling cancelled callbacks, assuming they are common
139 // enough to make this extra check an optimization
140 if (callback_->canceled()) {
141 debugs(5, 4, conn_ << " not calling canceled " << *callback_ <<
142 " [" << callback_->id << ']' );
6ca3e20d 143 // TODO save the pconn to the pconnPool ?
923b75ce 144 } else {
2b6b1bcb
AR
145 assert(conn_);
146
147 // free resources earlier and simplify recipients
148 if (errFlag != Comm::OK)
149 conn_->close(); // may not be opened
150 else
151 assert(conn_->isOpen());
152
923b75ce
AR
153 typedef CommConnectCbParams Params;
154 Params &params = GetCommParams<Params>(callback_);
155 params.conn = conn_;
2b6b1bcb 156 conn_ = nullptr; // release ownership; prevent closure by us
923b75ce
AR
157 params.flag = errFlag;
158 params.xerrno = xerrno;
159 ScheduleCallHere(callback_);
160 }
aee3523a 161 callback_ = nullptr;
294775b5
AJ
162 }
163
923b75ce
AR
164 // The job will stop without this call because nil callback_ makes
165 // doneAll() true, but this explicit call creates nicer debugging.
166 mustStop(why);
167}
168
169/// cleans up this job I/O state without closing temporaryFd
170/// required before closing temporaryFd or keeping it in conn_
171/// leaves FD bare so must only be called via closeFd() or keepFd()
172void
173Comm::ConnOpener::cleanFd()
174{
2b6b1bcb 175 debugs(5, 4, conn_ << "; temp FD " << temporaryFd_);
923b75ce
AR
176
177 Must(temporaryFd_ >= 0);
178 fde &f = fd_table[temporaryFd_];
179
180 // Our write_handler was set without using Comm::Write API, so we cannot
181 // use a cancellable Pointer-free job callback and simply cancel it here.
182 if (f.write_handler) {
183
184 /* XXX: We are about to remove write_handler, which was responsible
185 * for deleting write_data, so we have to delete write_data
186 * ourselves. Comm currently calls SetSelect handlers synchronously
187 * so if write_handler is set, we know it has not been called yet.
188 * ConnOpener converts that sync call into an async one, but only
189 * after deleting ptr, so that is not a problem.
190 */
191
192 delete static_cast<Pointer*>(f.write_data);
aee3523a
AR
193 f.write_data = nullptr;
194 f.write_handler = nullptr;
923b75ce
AR
195 }
196 // Comm::DoSelect does not do this when calling and resetting write_handler
3c862035 197 // (because it expects more writes to come?). We could mimic that
923b75ce
AR
198 // optimization by resetting Comm "Select" state only when the FD is
199 // actually closed.
aee3523a 200 Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, nullptr, nullptr, 0);
3c862035 201
aee3523a 202 if (calls_.timeout_ != nullptr) {
923b75ce 203 calls_.timeout_->cancel("Comm::ConnOpener::cleanFd");
aee3523a 204 calls_.timeout_ = nullptr;
923b75ce 205 }
3c862035 206 // Comm checkTimeouts() and commCloseAllSockets() do not clear .timeout
923b75ce 207 // when calling timeoutHandler (XXX fix them), so we clear unconditionally.
aee3523a 208 f.timeoutHandler = nullptr;
923b75ce
AR
209 f.timeout = 0;
210
aee3523a 211 if (calls_.earlyAbort_ != nullptr) {
923b75ce 212 comm_remove_close_handler(temporaryFd_, calls_.earlyAbort_);
aee3523a 213 calls_.earlyAbort_ = nullptr;
a95ff429 214 }
923b75ce
AR
215}
216
217/// cleans I/O state and ends I/O for temporaryFd_
218void
219Comm::ConnOpener::closeFd()
220{
221 if (temporaryFd_ < 0)
222 return;
223
224 cleanFd();
a95ff429 225
923b75ce
AR
226 // comm_close() below uses COMMIO_FD_WRITECB(fd)->active() to clear Comm
227 // "Select" state. It will not clear ours. XXX: It should always clear
228 // because a callback may have been active but was called before comm_close
229 // Update: we now do this in cleanFd()
230 // Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, NULL, NULL, 0);
231
232 comm_close(temporaryFd_);
233 temporaryFd_ = -1;
234}
235
236/// cleans I/O state and moves temporaryFd_ to the conn_ for long-term use
237void
238Comm::ConnOpener::keepFd()
239{
aee3523a 240 Must(conn_ != nullptr);
923b75ce
AR
241 Must(temporaryFd_ >= 0);
242
243 cleanFd();
244
245 conn_->fd = temporaryFd_;
246 temporaryFd_ = -1;
aed188fd
AJ
247}
248
a9870624
AJ
249void
250Comm::ConnOpener::start()
aed188fd 251{
aee3523a 252 Must(conn_ != nullptr);
482dcd01 253
923b75ce 254 /* outbound sockets have no need to be protocol agnostic. */
4dd643d5
AJ
255 if (!(Ip::EnableIpv6&IPV6_SPECIAL_V4MAPPING) && conn_->remote.isIPv4()) {
256 conn_->local.setIPv4();
923b75ce
AR
257 }
258
8aec3e1b 259 conn_->noteStart();
923b75ce 260 if (createFd())
3d7ecaff 261 doConnect();
923b75ce
AR
262}
263
264/// called at the end of Comm::ConnOpener::DelayedConnectRetry event
265void
3c862035
A
266Comm::ConnOpener::restart()
267{
923b75ce
AR
268 debugs(5, 5, conn_ << " restarting after sleep");
269 calls_.sleep_ = false;
270
271 if (createFd())
3d7ecaff 272 doConnect();
923b75ce
AR
273}
274
3c862035 275/// Create a socket for the future connection or return false.
923b75ce
AR
276/// If false is returned, done() is guaranteed to return true and end the job.
277bool
278Comm::ConnOpener::createFd()
279{
280 Must(temporaryFd_ < 0);
2b6b1bcb 281 assert(conn_);
923b75ce 282
2f8abb64 283 // our initiators signal abort by cancelling their callbacks
aee3523a 284 if (callback_ == nullptr || callback_->canceled())
923b75ce
AR
285 return false;
286
74d7d196 287 temporaryFd_ = comm_open(SOCK_STREAM, IPPROTO_TCP, conn_->local, conn_->flags, host_);
a95ff429 288 if (temporaryFd_ < 0) {
c8407295 289 sendAnswer(Comm::ERR_CONNECT, 0, "Comm::ConnOpener::createFd");
923b75ce 290 return false;
aed188fd
AJ
291 }
292
c6f168c1
CT
293 // Set TOS if needed.
294 if (conn_->tos &&
d9cbd42a 295 Ip::Qos::setSockTos(temporaryFd_, conn_->tos, conn_->remote.isIPv4() ? AF_INET : AF_INET6) < 0)
c6f168c1
CT
296 conn_->tos = 0;
297#if SO_MARK
298 if (conn_->nfmark &&
d9cbd42a 299 Ip::Qos::setSockNfmark(temporaryFd_, conn_->nfmark) < 0)
c6f168c1
CT
300 conn_->nfmark = 0;
301#endif
302
2d1bd082
CT
303 fd_table[temporaryFd_].tosToServer = conn_->tos;
304 fd_table[temporaryFd_].nfmarkToServer = conn_->nfmark;
c6f168c1 305
2832d7c0 306 typedef CommCbMemFunT<Comm::ConnOpener, CommCloseCbParams> abortDialer;
802540f2 307 calls_.earlyAbort_ = JobCallback(5, 4, abortDialer, this, Comm::ConnOpener::earlyAbort);
a95ff429 308 comm_add_close_handler(temporaryFd_, calls_.earlyAbort_);
418b3087 309
802540f2
AJ
310 typedef CommCbMemFunT<Comm::ConnOpener, CommTimeoutCbParams> timeoutDialer;
311 calls_.timeout_ = JobCallback(5, 4, timeoutDialer, this, Comm::ConnOpener::timeout);
923b75ce 312 debugs(5, 3, conn_ << " will timeout in " << (deadline_ - squid_curtime));
a95ff429 313
923b75ce 314 // Update the fd_table directly because commSetConnTimeout() needs open conn_
a95ff429
AJ
315 assert(temporaryFd_ < Squid_MaxFD);
316 assert(fd_table[temporaryFd_].flags.open);
317 typedef CommTimeoutCbParams Params;
318 Params &params = GetCommParams<Params>(calls_.timeout_);
319 params.conn = conn_;
320 fd_table[temporaryFd_].timeoutHandler = calls_.timeout_;
923b75ce 321 fd_table[temporaryFd_].timeout = deadline_;
418b3087 322
923b75ce 323 return true;
418b3087
AJ
324}
325
326void
327Comm::ConnOpener::connected()
328{
923b75ce
AR
329 Must(temporaryFd_ >= 0);
330 keepFd();
a95ff429 331
418b3087
AJ
332 /*
333 * stats.conn_open is used to account for the number of
a3c6762c 334 * connections that we have open to the CachePeer, so we can limit
418b3087
AJ
335 * based on the max-conn option. We need to increment here,
336 * even if the connection may fail.
337 */
a3c6762c 338 if (CachePeer *peer=(conn_->getPeer()))
b79bfaae 339 ++peer->stats.conn_open;
418b3087
AJ
340
341 lookupLocalAddress();
342
343 /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to
344 * indicate the state of a raw fd object being passed around.
345 * Also, legacy code still depends on comm_local_port() with no access to Comm::Connection
346 * when those are done comm_local_port can become one of our member functions to do the below.
347 */
923b75ce 348 Must(fd_table[conn_->fd].flags.open);
418b3087 349 fd_table[conn_->fd].local_addr = conn_->local;
923b75ce 350
c8407295 351 sendAnswer(Comm::OK, 0, "Comm::ConnOpener::connected");
8968fd45
AJ
352}
353
923b75ce 354/// Make an FD connection attempt.
8968fd45 355void
3d7ecaff 356Comm::ConnOpener::doConnect()
8968fd45 357{
aee3523a 358 Must(conn_ != nullptr);
923b75ce 359 Must(temporaryFd_ >= 0);
878bfa63 360
a2f5277a 361 ++ totalTries_;
aed188fd 362
a95ff429 363 switch (comm_connect_addr(temporaryFd_, conn_->remote) ) {
aed188fd 364
c8407295 365 case Comm::INPROGRESS:
bf95c10a 366 debugs(5, 5, conn_ << ": Comm::INPROGRESS");
923b75ce 367 Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, Comm::ConnOpener::InProgressConnectRetry, new Pointer(this), 0);
aed188fd
AJ
368 break;
369
c8407295 370 case Comm::OK:
bf95c10a 371 debugs(5, 5, conn_ << ": Comm::OK - connected");
418b3087 372 connected();
aed188fd
AJ
373 break;
374
923b75ce
AR
375 default: {
376 const int xerrno = errno;
377
a2f5277a 378 ++failRetries_;
923b75ce
AR
379 debugs(5, 7, conn_ << ": failure #" << failRetries_ << " <= " <<
380 Config.connect_retries << ": " << xstrerr(xerrno));
aed188fd 381
923b75ce 382 if (failRetries_ < Config.connect_retries) {
bf95c10a 383 debugs(5, 5, conn_ << ": * - try again");
f6c0d1ab 384 retrySleep();
e884bbde 385 return;
aed188fd
AJ
386 } else {
387 // send ERROR back to the upper layer.
bf95c10a 388 debugs(5, 5, conn_ << ": * - ERR tried too many times already.");
294e1994 389 sendAnswer(Comm::ERR_CONNECT, xerrno, "Comm::ConnOpener::doConnect");
aed188fd
AJ
390 }
391 }
923b75ce
AR
392 }
393}
394
395/// Close and wait a little before trying to open and connect again.
396void
f6c0d1ab 397Comm::ConnOpener::retrySleep()
3c862035 398{
923b75ce
AR
399 Must(!calls_.sleep_);
400 closeFd();
401 calls_.sleep_ = true;
402 eventAdd("Comm::ConnOpener::DelayedConnectRetry",
403 Comm::ConnOpener::DelayedConnectRetry,
404 new Pointer(this), 0.05, 0, false);
405}
406
407/// cleans up this job sleep state
408void
409Comm::ConnOpener::cancelSleep()
410{
411 if (calls_.sleep_) {
3c862035
A
412 // It would be nice to delete the sleep event, but it might be out of
413 // the event queue and in the async queue already, so (a) we do not know
414 // whether we can safely delete the call ptr here and (b) eventDelete()
415 // will assert if the event went async. Thus, we let the event run so
416 // that it deletes the call ptr [after this job is gone]. Note that we
417 // are called only when the job ends so this "hanging event" will do
418 // nothing but deleting the call ptr. TODO: Revise eventDelete() API.
419 // eventDelete(Comm::ConnOpener::DelayedConnectRetry, calls_.sleep);
420 calls_.sleep_ = false;
421 debugs(5, 9, conn_ << " stops sleeping");
923b75ce 422 }
aed188fd
AJ
423}
424
dd829807
AJ
425/**
426 * Lookup local-end address and port of the TCP link just opened.
427 * This ensure the connection local details are set correctly
428 */
429void
430Comm::ConnOpener::lookupLocalAddress()
431{
aee3523a 432 struct addrinfo *addr = nullptr;
851614a8 433 Ip::Address::InitAddr(addr);
dd829807
AJ
434
435 if (getsockname(conn_->fd, addr->ai_addr, &(addr->ai_addrlen)) != 0) {
b69e9ffa
AJ
436 int xerrno = errno;
437 debugs(50, DBG_IMPORTANT, "ERROR: Failed to retrieve TCP/UDP details for socket: " << conn_ << ": " << xstrerr(xerrno));
851614a8 438 Ip::Address::FreeAddr(addr);
dd829807
AJ
439 return;
440 }
441
442 conn_->local = *addr;
851614a8 443 Ip::Address::FreeAddr(addr);
bf95c10a 444 debugs(5, 6, conn_);
dd829807
AJ
445}
446
5229395c
AJ
447/** Abort connection attempt.
448 * Handles the case(s) when a partially setup connection gets closed early.
449 */
aed188fd 450void
2832d7c0 451Comm::ConnOpener::earlyAbort(const CommCloseCbParams &io)
aed188fd 452{
bf95c10a 453 debugs(5, 3, io.conn);
aee3523a 454 calls_.earlyAbort_ = nullptr;
923b75ce 455 // NP: is closing or shutdown better?
c8407295 456 sendAnswer(Comm::ERR_CLOSING, io.xerrno, "Comm::ConnOpener::earlyAbort");
aed188fd
AJ
457}
458
5229395c
AJ
459/**
460 * Handles the case(s) when a partially setup connection gets timed out.
8d77a37c 461 * NP: When commSetConnTimeout accepts generic CommCommonCbParams this can die.
5229395c 462 */
aed188fd 463void
418b3087 464Comm::ConnOpener::timeout(const CommTimeoutCbParams &)
aed188fd 465{
bf95c10a 466 debugs(5, 5, conn_ << ": * - ERR took too long to receive response.");
aee3523a 467 calls_.timeout_ = nullptr;
c8407295 468 sendAnswer(Comm::TIMEOUT, ETIMEDOUT, "Comm::ConnOpener::timeout");
aed188fd
AJ
469}
470
c8407295 471/* Legacy Wrapper for the retry event after Comm::INPROGRESS
294e1994 472 * XXX: As soon as Comm::SetSelect() accepts Async calls we can use a ConnOpener::doConnect call
5229395c 473 */
aed188fd 474void
ced8def3 475Comm::ConnOpener::InProgressConnectRetry(int, void *data)
aed188fd 476{
9e64d84e
AR
477 Pointer *ptr = static_cast<Pointer*>(data);
478 assert(ptr);
479 if (ConnOpener *cs = ptr->valid()) {
42628300
A
480 // Ew. we are now outside the all AsyncJob protections.
481 // get back inside by scheduling another call...
482 typedef NullaryMemFunT<Comm::ConnOpener> Dialer;
294e1994 483 AsyncCall::Pointer call = JobCallback(5, 4, Dialer, cs, Comm::ConnOpener::doConnect);
42628300 484 ScheduleCallHere(call);
9e64d84e
AR
485 }
486 delete ptr;
418b3087
AJ
487}
488
489/* Legacy Wrapper for the retry event with small delay after errors.
923b75ce 490 * XXX: As soon as eventAdd() accepts Async calls we can use a ConnOpener::restart call
418b3087
AJ
491 */
492void
493Comm::ConnOpener::DelayedConnectRetry(void *data)
494{
9e64d84e
AR
495 Pointer *ptr = static_cast<Pointer*>(data);
496 assert(ptr);
497 if (ConnOpener *cs = ptr->valid()) {
42628300
A
498 // Ew. we are now outside the all AsyncJob protections.
499 // get back inside by scheduling another call...
500 typedef NullaryMemFunT<Comm::ConnOpener> Dialer;
923b75ce 501 AsyncCall::Pointer call = JobCallback(5, 4, Dialer, cs, Comm::ConnOpener::restart);
42628300 502 ScheduleCallHere(call);
9e64d84e
AR
503 }
504 delete ptr;
aed188fd 505}
f53969cc 506