2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 05 Socket Connection Opener */
12 #include "CachePeer.h"
14 #include "comm/Connection.h"
15 #include "comm/ConnOpener.h"
16 #include "comm/Loops.h"
20 #include "icmp/net_db.h"
21 #include "ip/QosConfig.h"
24 #include "SquidConfig.h"
25 #include "SquidTime.h"
31 CBDATA_NAMESPACED_CLASS_INIT(Comm
, ConnOpener
);
33 Comm::ConnOpener::ConnOpener(Comm::ConnectionPointer
&c
, const AsyncCall::Pointer
&handler
, time_t ctimeout
) :
34 AsyncJob("Comm::ConnOpener"),
41 deadline_(squid_curtime
+ static_cast<time_t>(ctimeout
))
43 debugs(5, 3, "will connect to " << c
<< " with " << ctimeout
<< " timeout");
46 Comm::ConnOpener::~ConnOpener()
52 Comm::ConnOpener::doneAll() const
54 // is the conn_ to be opened still waiting?
56 return AsyncJob::doneAll();
59 // is the callback still to be called?
60 if (callback_
== NULL
|| callback_
->canceled()) {
61 return AsyncJob::doneAll();
64 // otherwise, we must be waiting for something
65 Must(temporaryFd_
>= 0 || calls_
.sleep_
);
70 Comm::ConnOpener::swanSong()
72 if (callback_
!= NULL
) {
73 // inform the still-waiting caller we are dying
74 sendAnswer(Comm::ERR_CONNECT
, 0, "Comm::ConnOpener::swanSong");
77 // did we abort with a temporary FD assigned?
78 if (temporaryFd_
>= 0)
81 // did we abort while waiting between retries?
89 Comm::ConnOpener::setHost(const char * new_host
)
91 // unset and erase if already set.
95 // set the new one if given.
97 host_
= xstrdup(new_host
);
101 Comm::ConnOpener::getHost() const
107 * Connection attempt are completed. One way or the other.
108 * Pass the results back to the external handler.
111 Comm::ConnOpener::sendAnswer(Comm::Flag errFlag
, int xerrno
, const char *why
)
113 // only mark the address good/bad AFTER connect is finished.
115 if (xerrno
== 0) // XXX: should not we use errFlag instead?
116 ipcacheMarkGoodAddr(host_
, conn_
->remote
);
118 ipcacheMarkBadAddr(host_
, conn_
->remote
);
120 if (Config
.onoff
.test_reachability
)
121 netdbDeleteAddrNetwork(conn_
->remote
);
126 if (callback_
!= NULL
) {
127 // avoid scheduling cancelled callbacks, assuming they are common
128 // enough to make this extra check an optimization
129 if (callback_
->canceled()) {
130 debugs(5, 4, conn_
<< " not calling canceled " << *callback_
<<
131 " [" << callback_
->id
<< ']' );
132 // TODO save the pconn to the pconnPool ?
134 typedef CommConnectCbParams Params
;
135 Params
¶ms
= GetCommParams
<Params
>(callback_
);
137 params
.flag
= errFlag
;
138 params
.xerrno
= xerrno
;
139 ScheduleCallHere(callback_
);
144 // The job will stop without this call because nil callback_ makes
145 // doneAll() true, but this explicit call creates nicer debugging.
149 /// cleans up this job I/O state without closing temporaryFd
150 /// required before closing temporaryFd or keeping it in conn_
151 /// leaves FD bare so must only be called via closeFd() or keepFd()
153 Comm::ConnOpener::cleanFd()
155 debugs(5, 4, HERE
<< conn_
<< " closing temp FD " << temporaryFd_
);
157 Must(temporaryFd_
>= 0);
158 fde
&f
= fd_table
[temporaryFd_
];
160 // Our write_handler was set without using Comm::Write API, so we cannot
161 // use a cancellable Pointer-free job callback and simply cancel it here.
162 if (f
.write_handler
) {
164 /* XXX: We are about to remove write_handler, which was responsible
165 * for deleting write_data, so we have to delete write_data
166 * ourselves. Comm currently calls SetSelect handlers synchronously
167 * so if write_handler is set, we know it has not been called yet.
168 * ConnOpener converts that sync call into an async one, but only
169 * after deleting ptr, so that is not a problem.
172 delete static_cast<Pointer
*>(f
.write_data
);
174 f
.write_handler
= NULL
;
176 // Comm::DoSelect does not do this when calling and resetting write_handler
177 // (because it expects more writes to come?). We could mimic that
178 // optimization by resetting Comm "Select" state only when the FD is
180 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, NULL
, NULL
, 0);
182 if (calls_
.timeout_
!= NULL
) {
183 calls_
.timeout_
->cancel("Comm::ConnOpener::cleanFd");
184 calls_
.timeout_
= NULL
;
186 // Comm checkTimeouts() and commCloseAllSockets() do not clear .timeout
187 // when calling timeoutHandler (XXX fix them), so we clear unconditionally.
188 f
.timeoutHandler
= NULL
;
191 if (calls_
.earlyAbort_
!= NULL
) {
192 comm_remove_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
193 calls_
.earlyAbort_
= NULL
;
197 /// cleans I/O state and ends I/O for temporaryFd_
199 Comm::ConnOpener::closeFd()
201 if (temporaryFd_
< 0)
206 // comm_close() below uses COMMIO_FD_WRITECB(fd)->active() to clear Comm
207 // "Select" state. It will not clear ours. XXX: It should always clear
208 // because a callback may have been active but was called before comm_close
209 // Update: we now do this in cleanFd()
210 // Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, NULL, NULL, 0);
212 comm_close(temporaryFd_
);
216 /// cleans I/O state and moves temporaryFd_ to the conn_ for long-term use
218 Comm::ConnOpener::keepFd()
221 Must(temporaryFd_
>= 0);
225 conn_
->fd
= temporaryFd_
;
230 Comm::ConnOpener::start()
234 /* outbound sockets have no need to be protocol agnostic. */
235 if (!(Ip::EnableIpv6
&IPV6_SPECIAL_V4MAPPING
) && conn_
->remote
.isIPv4()) {
236 conn_
->local
.setIPv4();
244 /// called at the end of Comm::ConnOpener::DelayedConnectRetry event
246 Comm::ConnOpener::restart()
248 debugs(5, 5, conn_
<< " restarting after sleep");
249 calls_
.sleep_
= false;
255 /// Create a socket for the future connection or return false.
256 /// If false is returned, done() is guaranteed to return true and end the job.
258 Comm::ConnOpener::createFd()
260 Must(temporaryFd_
< 0);
262 // our initators signal abort by cancelling their callbacks
263 if (callback_
== NULL
|| callback_
->canceled())
266 temporaryFd_
= comm_openex(SOCK_STREAM
, IPPROTO_TCP
, conn_
->local
, conn_
->flags
, host_
);
267 if (temporaryFd_
< 0) {
268 sendAnswer(Comm::ERR_CONNECT
, 0, "Comm::ConnOpener::createFd");
272 // Set TOS if needed.
274 Ip::Qos::setSockTos(temporaryFd_
, conn_
->tos
, conn_
->remote
.isIPv4() ? AF_INET
: AF_INET6
) < 0)
278 Ip::Qos::setSockNfmark(temporaryFd_
, conn_
->nfmark
) < 0)
282 fd_table
[temporaryFd_
].tosToServer
= conn_
->tos
;
283 fd_table
[temporaryFd_
].nfmarkToServer
= conn_
->nfmark
;
285 typedef CommCbMemFunT
<Comm::ConnOpener
, CommCloseCbParams
> abortDialer
;
286 calls_
.earlyAbort_
= JobCallback(5, 4, abortDialer
, this, Comm::ConnOpener::earlyAbort
);
287 comm_add_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
289 typedef CommCbMemFunT
<Comm::ConnOpener
, CommTimeoutCbParams
> timeoutDialer
;
290 calls_
.timeout_
= JobCallback(5, 4, timeoutDialer
, this, Comm::ConnOpener::timeout
);
291 debugs(5, 3, conn_
<< " will timeout in " << (deadline_
- squid_curtime
));
293 // Update the fd_table directly because commSetConnTimeout() needs open conn_
294 assert(temporaryFd_
< Squid_MaxFD
);
295 assert(fd_table
[temporaryFd_
].flags
.open
);
296 typedef CommTimeoutCbParams Params
;
297 Params
¶ms
= GetCommParams
<Params
>(calls_
.timeout_
);
299 fd_table
[temporaryFd_
].timeoutHandler
= calls_
.timeout_
;
300 fd_table
[temporaryFd_
].timeout
= deadline_
;
306 Comm::ConnOpener::connected()
308 Must(temporaryFd_
>= 0);
312 * stats.conn_open is used to account for the number of
313 * connections that we have open to the CachePeer, so we can limit
314 * based on the max-conn option. We need to increment here,
315 * even if the connection may fail.
317 if (CachePeer
*peer
=(conn_
->getPeer()))
318 ++peer
->stats
.conn_open
;
320 lookupLocalAddress();
322 /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to
323 * indicate the state of a raw fd object being passed around.
324 * Also, legacy code still depends on comm_local_port() with no access to Comm::Connection
325 * when those are done comm_local_port can become one of our member functions to do the below.
327 Must(fd_table
[conn_
->fd
].flags
.open
);
328 fd_table
[conn_
->fd
].local_addr
= conn_
->local
;
330 sendAnswer(Comm::OK
, 0, "Comm::ConnOpener::connected");
333 /// Make an FD connection attempt.
335 Comm::ConnOpener::doConnect()
338 Must(temporaryFd_
>= 0);
342 switch (comm_connect_addr(temporaryFd_
, conn_
->remote
) ) {
344 case Comm::INPROGRESS
:
345 debugs(5, 5, HERE
<< conn_
<< ": Comm::INPROGRESS");
346 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, Comm::ConnOpener::InProgressConnectRetry
, new Pointer(this), 0);
350 debugs(5, 5, HERE
<< conn_
<< ": Comm::OK - connected");
355 const int xerrno
= errno
;
358 debugs(5, 7, conn_
<< ": failure #" << failRetries_
<< " <= " <<
359 Config
.connect_retries
<< ": " << xstrerr(xerrno
));
361 if (failRetries_
< Config
.connect_retries
) {
362 debugs(5, 5, HERE
<< conn_
<< ": * - try again");
366 // send ERROR back to the upper layer.
367 debugs(5, 5, HERE
<< conn_
<< ": * - ERR tried too many times already.");
368 sendAnswer(Comm::ERR_CONNECT
, xerrno
, "Comm::ConnOpener::doConnect");
374 /// Close and wait a little before trying to open and connect again.
376 Comm::ConnOpener::retrySleep()
378 Must(!calls_
.sleep_
);
380 calls_
.sleep_
= true;
381 eventAdd("Comm::ConnOpener::DelayedConnectRetry",
382 Comm::ConnOpener::DelayedConnectRetry
,
383 new Pointer(this), 0.05, 0, false);
386 /// cleans up this job sleep state
388 Comm::ConnOpener::cancelSleep()
391 // It would be nice to delete the sleep event, but it might be out of
392 // the event queue and in the async queue already, so (a) we do not know
393 // whether we can safely delete the call ptr here and (b) eventDelete()
394 // will assert if the event went async. Thus, we let the event run so
395 // that it deletes the call ptr [after this job is gone]. Note that we
396 // are called only when the job ends so this "hanging event" will do
397 // nothing but deleting the call ptr. TODO: Revise eventDelete() API.
398 // eventDelete(Comm::ConnOpener::DelayedConnectRetry, calls_.sleep);
399 calls_
.sleep_
= false;
400 debugs(5, 9, conn_
<< " stops sleeping");
405 * Lookup local-end address and port of the TCP link just opened.
406 * This ensure the connection local details are set correctly
409 Comm::ConnOpener::lookupLocalAddress()
411 struct addrinfo
*addr
= NULL
;
412 Ip::Address::InitAddr(addr
);
414 if (getsockname(conn_
->fd
, addr
->ai_addr
, &(addr
->ai_addrlen
)) != 0) {
416 debugs(50, DBG_IMPORTANT
, "ERROR: Failed to retrieve TCP/UDP details for socket: " << conn_
<< ": " << xstrerr(xerrno
));
417 Ip::Address::FreeAddr(addr
);
421 conn_
->local
= *addr
;
422 Ip::Address::FreeAddr(addr
);
423 debugs(5, 6, HERE
<< conn_
);
426 /** Abort connection attempt.
427 * Handles the case(s) when a partially setup connection gets closed early.
430 Comm::ConnOpener::earlyAbort(const CommCloseCbParams
&io
)
432 debugs(5, 3, HERE
<< io
.conn
);
433 calls_
.earlyAbort_
= NULL
;
434 // NP: is closing or shutdown better?
435 sendAnswer(Comm::ERR_CLOSING
, io
.xerrno
, "Comm::ConnOpener::earlyAbort");
439 * Handles the case(s) when a partially setup connection gets timed out.
440 * NP: When commSetConnTimeout accepts generic CommCommonCbParams this can die.
443 Comm::ConnOpener::timeout(const CommTimeoutCbParams
&)
445 debugs(5, 5, HERE
<< conn_
<< ": * - ERR took too long to receive response.");
446 calls_
.timeout_
= NULL
;
447 sendAnswer(Comm::TIMEOUT
, ETIMEDOUT
, "Comm::ConnOpener::timeout");
450 /* Legacy Wrapper for the retry event after Comm::INPROGRESS
451 * XXX: As soon as Comm::SetSelect() accepts Async calls we can use a ConnOpener::doConnect call
454 Comm::ConnOpener::InProgressConnectRetry(int, void *data
)
456 Pointer
*ptr
= static_cast<Pointer
*>(data
);
458 if (ConnOpener
*cs
= ptr
->valid()) {
459 // Ew. we are now outside the all AsyncJob protections.
460 // get back inside by scheduling another call...
461 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
462 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::doConnect
);
463 ScheduleCallHere(call
);
468 /* Legacy Wrapper for the retry event with small delay after errors.
469 * XXX: As soon as eventAdd() accepts Async calls we can use a ConnOpener::restart call
472 Comm::ConnOpener::DelayedConnectRetry(void *data
)
474 Pointer
*ptr
= static_cast<Pointer
*>(data
);
476 if (ConnOpener
*cs
= ptr
->valid()) {
477 // Ew. we are now outside the all AsyncJob protections.
478 // get back inside by scheduling another call...
479 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
480 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::restart
);
481 ScheduleCallHere(call
);