2 * Copyright (C) 1996-2017 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
, AsyncCall::Pointer
&handler
, time_t ctimeout
) :
34 AsyncJob("Comm::ConnOpener"),
41 deadline_(squid_curtime
+ static_cast<time_t>(ctimeout
))
44 Comm::ConnOpener::~ConnOpener()
50 Comm::ConnOpener::doneAll() const
52 // is the conn_ to be opened still waiting?
54 return AsyncJob::doneAll();
57 // is the callback still to be called?
58 if (callback_
== NULL
|| callback_
->canceled()) {
59 return AsyncJob::doneAll();
62 // otherwise, we must be waiting for something
63 Must(temporaryFd_
>= 0 || calls_
.sleep_
);
68 Comm::ConnOpener::swanSong()
70 if (callback_
!= NULL
) {
71 // inform the still-waiting caller we are dying
72 sendAnswer(Comm::ERR_CONNECT
, 0, "Comm::ConnOpener::swanSong");
75 // did we abort with a temporary FD assigned?
76 if (temporaryFd_
>= 0)
79 // did we abort while waiting between retries?
87 Comm::ConnOpener::setHost(const char * new_host
)
89 // unset and erase if already set.
93 // set the new one if given.
95 host_
= xstrdup(new_host
);
99 Comm::ConnOpener::getHost() const
105 * Connection attempt are completed. One way or the other.
106 * Pass the results back to the external handler.
109 Comm::ConnOpener::sendAnswer(Comm::Flag errFlag
, int xerrno
, const char *why
)
111 // only mark the address good/bad AFTER connect is finished.
113 if (xerrno
== 0) // XXX: should not we use errFlag instead?
114 ipcacheMarkGoodAddr(host_
, conn_
->remote
);
116 ipcacheMarkBadAddr(host_
, conn_
->remote
);
118 if (Config
.onoff
.test_reachability
)
119 netdbDeleteAddrNetwork(conn_
->remote
);
124 if (callback_
!= NULL
) {
125 // avoid scheduling cancelled callbacks, assuming they are common
126 // enough to make this extra check an optimization
127 if (callback_
->canceled()) {
128 debugs(5, 4, conn_
<< " not calling canceled " << *callback_
<<
129 " [" << callback_
->id
<< ']' );
130 // TODO save the pconn to the pconnPool ?
132 typedef CommConnectCbParams Params
;
133 Params
¶ms
= GetCommParams
<Params
>(callback_
);
135 params
.flag
= errFlag
;
136 params
.xerrno
= xerrno
;
137 ScheduleCallHere(callback_
);
142 // The job will stop without this call because nil callback_ makes
143 // doneAll() true, but this explicit call creates nicer debugging.
147 /// cleans up this job I/O state without closing temporaryFd
148 /// required before closing temporaryFd or keeping it in conn_
149 /// leaves FD bare so must only be called via closeFd() or keepFd()
151 Comm::ConnOpener::cleanFd()
153 debugs(5, 4, HERE
<< conn_
<< " closing temp FD " << temporaryFd_
);
155 Must(temporaryFd_
>= 0);
156 fde
&f
= fd_table
[temporaryFd_
];
158 // Our write_handler was set without using Comm::Write API, so we cannot
159 // use a cancellable Pointer-free job callback and simply cancel it here.
160 if (f
.write_handler
) {
162 /* XXX: We are about to remove write_handler, which was responsible
163 * for deleting write_data, so we have to delete write_data
164 * ourselves. Comm currently calls SetSelect handlers synchronously
165 * so if write_handler is set, we know it has not been called yet.
166 * ConnOpener converts that sync call into an async one, but only
167 * after deleting ptr, so that is not a problem.
170 delete static_cast<Pointer
*>(f
.write_data
);
172 f
.write_handler
= NULL
;
174 // Comm::DoSelect does not do this when calling and resetting write_handler
175 // (because it expects more writes to come?). We could mimic that
176 // optimization by resetting Comm "Select" state only when the FD is
178 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, NULL
, NULL
, 0);
180 if (calls_
.timeout_
!= NULL
) {
181 calls_
.timeout_
->cancel("Comm::ConnOpener::cleanFd");
182 calls_
.timeout_
= NULL
;
184 // Comm checkTimeouts() and commCloseAllSockets() do not clear .timeout
185 // when calling timeoutHandler (XXX fix them), so we clear unconditionally.
186 f
.timeoutHandler
= NULL
;
189 if (calls_
.earlyAbort_
!= NULL
) {
190 comm_remove_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
191 calls_
.earlyAbort_
= NULL
;
195 /// cleans I/O state and ends I/O for temporaryFd_
197 Comm::ConnOpener::closeFd()
199 if (temporaryFd_
< 0)
204 // comm_close() below uses COMMIO_FD_WRITECB(fd)->active() to clear Comm
205 // "Select" state. It will not clear ours. XXX: It should always clear
206 // because a callback may have been active but was called before comm_close
207 // Update: we now do this in cleanFd()
208 // Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, NULL, NULL, 0);
210 comm_close(temporaryFd_
);
214 /// cleans I/O state and moves temporaryFd_ to the conn_ for long-term use
216 Comm::ConnOpener::keepFd()
219 Must(temporaryFd_
>= 0);
223 conn_
->fd
= temporaryFd_
;
228 Comm::ConnOpener::start()
232 /* outbound sockets have no need to be protocol agnostic. */
233 if (!(Ip::EnableIpv6
&IPV6_SPECIAL_V4MAPPING
) && conn_
->remote
.isIPv4()) {
234 conn_
->local
.setIPv4();
242 /// called at the end of Comm::ConnOpener::DelayedConnectRetry event
244 Comm::ConnOpener::restart()
246 debugs(5, 5, conn_
<< " restarting after sleep");
247 calls_
.sleep_
= false;
253 /// Create a socket for the future connection or return false.
254 /// If false is returned, done() is guaranteed to return true and end the job.
256 Comm::ConnOpener::createFd()
258 Must(temporaryFd_
< 0);
260 // our initators signal abort by cancelling their callbacks
261 if (callback_
== NULL
|| callback_
->canceled())
264 temporaryFd_
= comm_openex(SOCK_STREAM
, IPPROTO_TCP
, conn_
->local
, conn_
->flags
, host_
);
265 if (temporaryFd_
< 0) {
266 sendAnswer(Comm::ERR_CONNECT
, 0, "Comm::ConnOpener::createFd");
270 // Set TOS if needed.
272 Ip::Qos::setSockTos(temporaryFd_
, conn_
->tos
, conn_
->remote
.isIPv4() ? AF_INET
: AF_INET6
) < 0)
276 Ip::Qos::setSockNfmark(temporaryFd_
, conn_
->nfmark
) < 0)
280 fd_table
[temporaryFd_
].tosToServer
= conn_
->tos
;
281 fd_table
[temporaryFd_
].nfmarkToServer
= conn_
->nfmark
;
283 typedef CommCbMemFunT
<Comm::ConnOpener
, CommCloseCbParams
> abortDialer
;
284 calls_
.earlyAbort_
= JobCallback(5, 4, abortDialer
, this, Comm::ConnOpener::earlyAbort
);
285 comm_add_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
287 typedef CommCbMemFunT
<Comm::ConnOpener
, CommTimeoutCbParams
> timeoutDialer
;
288 calls_
.timeout_
= JobCallback(5, 4, timeoutDialer
, this, Comm::ConnOpener::timeout
);
289 debugs(5, 3, conn_
<< " will timeout in " << (deadline_
- squid_curtime
));
291 // Update the fd_table directly because commSetConnTimeout() needs open conn_
292 assert(temporaryFd_
< Squid_MaxFD
);
293 assert(fd_table
[temporaryFd_
].flags
.open
);
294 typedef CommTimeoutCbParams Params
;
295 Params
¶ms
= GetCommParams
<Params
>(calls_
.timeout_
);
297 fd_table
[temporaryFd_
].timeoutHandler
= calls_
.timeout_
;
298 fd_table
[temporaryFd_
].timeout
= deadline_
;
304 Comm::ConnOpener::connected()
306 Must(temporaryFd_
>= 0);
310 * stats.conn_open is used to account for the number of
311 * connections that we have open to the CachePeer, so we can limit
312 * based on the max-conn option. We need to increment here,
313 * even if the connection may fail.
315 if (CachePeer
*peer
=(conn_
->getPeer()))
316 ++peer
->stats
.conn_open
;
318 lookupLocalAddress();
320 /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to
321 * indicate the state of a raw fd object being passed around.
322 * Also, legacy code still depends on comm_local_port() with no access to Comm::Connection
323 * when those are done comm_local_port can become one of our member functions to do the below.
325 Must(fd_table
[conn_
->fd
].flags
.open
);
326 fd_table
[conn_
->fd
].local_addr
= conn_
->local
;
328 sendAnswer(Comm::OK
, 0, "Comm::ConnOpener::connected");
331 /// Make an FD connection attempt.
333 Comm::ConnOpener::doConnect()
336 Must(temporaryFd_
>= 0);
340 switch (comm_connect_addr(temporaryFd_
, conn_
->remote
) ) {
342 case Comm::INPROGRESS
:
343 debugs(5, 5, HERE
<< conn_
<< ": Comm::INPROGRESS");
344 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, Comm::ConnOpener::InProgressConnectRetry
, new Pointer(this), 0);
348 debugs(5, 5, HERE
<< conn_
<< ": Comm::OK - connected");
353 const int xerrno
= errno
;
356 debugs(5, 7, conn_
<< ": failure #" << failRetries_
<< " <= " <<
357 Config
.connect_retries
<< ": " << xstrerr(xerrno
));
359 if (failRetries_
< Config
.connect_retries
) {
360 debugs(5, 5, HERE
<< conn_
<< ": * - try again");
364 // send ERROR back to the upper layer.
365 debugs(5, 5, HERE
<< conn_
<< ": * - ERR tried too many times already.");
366 sendAnswer(Comm::ERR_CONNECT
, xerrno
, "Comm::ConnOpener::doConnect");
372 /// Close and wait a little before trying to open and connect again.
374 Comm::ConnOpener::retrySleep()
376 Must(!calls_
.sleep_
);
378 calls_
.sleep_
= true;
379 eventAdd("Comm::ConnOpener::DelayedConnectRetry",
380 Comm::ConnOpener::DelayedConnectRetry
,
381 new Pointer(this), 0.05, 0, false);
384 /// cleans up this job sleep state
386 Comm::ConnOpener::cancelSleep()
389 // It would be nice to delete the sleep event, but it might be out of
390 // the event queue and in the async queue already, so (a) we do not know
391 // whether we can safely delete the call ptr here and (b) eventDelete()
392 // will assert if the event went async. Thus, we let the event run so
393 // that it deletes the call ptr [after this job is gone]. Note that we
394 // are called only when the job ends so this "hanging event" will do
395 // nothing but deleting the call ptr. TODO: Revise eventDelete() API.
396 // eventDelete(Comm::ConnOpener::DelayedConnectRetry, calls_.sleep);
397 calls_
.sleep_
= false;
398 debugs(5, 9, conn_
<< " stops sleeping");
403 * Lookup local-end address and port of the TCP link just opened.
404 * This ensure the connection local details are set correctly
407 Comm::ConnOpener::lookupLocalAddress()
409 struct addrinfo
*addr
= NULL
;
410 Ip::Address::InitAddr(addr
);
412 if (getsockname(conn_
->fd
, addr
->ai_addr
, &(addr
->ai_addrlen
)) != 0) {
414 debugs(50, DBG_IMPORTANT
, "ERROR: Failed to retrieve TCP/UDP details for socket: " << conn_
<< ": " << xstrerr(xerrno
));
415 Ip::Address::FreeAddr(addr
);
419 conn_
->local
= *addr
;
420 Ip::Address::FreeAddr(addr
);
421 debugs(5, 6, HERE
<< conn_
);
424 /** Abort connection attempt.
425 * Handles the case(s) when a partially setup connection gets closed early.
428 Comm::ConnOpener::earlyAbort(const CommCloseCbParams
&io
)
430 debugs(5, 3, HERE
<< io
.conn
);
431 calls_
.earlyAbort_
= NULL
;
432 // NP: is closing or shutdown better?
433 sendAnswer(Comm::ERR_CLOSING
, io
.xerrno
, "Comm::ConnOpener::earlyAbort");
437 * Handles the case(s) when a partially setup connection gets timed out.
438 * NP: When commSetConnTimeout accepts generic CommCommonCbParams this can die.
441 Comm::ConnOpener::timeout(const CommTimeoutCbParams
&)
443 debugs(5, 5, HERE
<< conn_
<< ": * - ERR took too long to receive response.");
444 calls_
.timeout_
= NULL
;
445 sendAnswer(Comm::TIMEOUT
, ETIMEDOUT
, "Comm::ConnOpener::timeout");
448 /* Legacy Wrapper for the retry event after Comm::INPROGRESS
449 * XXX: As soon as Comm::SetSelect() accepts Async calls we can use a ConnOpener::doConnect call
452 Comm::ConnOpener::InProgressConnectRetry(int, void *data
)
454 Pointer
*ptr
= static_cast<Pointer
*>(data
);
456 if (ConnOpener
*cs
= ptr
->valid()) {
457 // Ew. we are now outside the all AsyncJob protections.
458 // get back inside by scheduling another call...
459 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
460 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::doConnect
);
461 ScheduleCallHere(call
);
466 /* Legacy Wrapper for the retry event with small delay after errors.
467 * XXX: As soon as eventAdd() accepts Async calls we can use a ConnOpener::restart call
470 Comm::ConnOpener::DelayedConnectRetry(void *data
)
472 Pointer
*ptr
= static_cast<Pointer
*>(data
);
474 if (ConnOpener
*cs
= ptr
->valid()) {
475 // Ew. we are now outside the all AsyncJob protections.
476 // get back inside by scheduling another call...
477 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
478 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::restart
);
479 ScheduleCallHere(call
);