2 * DEBUG: section 05 Socket Connection Opener
8 #include "comm/Connection.h"
9 #include "comm/ConnOpener.h"
10 #include "comm/Loops.h"
14 #include "icmp/net_db.h"
17 #include "ip/QosConfig.h"
18 #include "SquidConfig.h"
19 #include "SquidTime.h"
25 CBDATA_NAMESPACED_CLASS_INIT(Comm
, ConnOpener
);
27 Comm::ConnOpener::ConnOpener(Comm::ConnectionPointer
&c
, AsyncCall::Pointer
&handler
, time_t ctimeout
) :
28 AsyncJob("Comm::ConnOpener"),
35 deadline_(squid_curtime
+ static_cast<time_t>(ctimeout
))
38 Comm::ConnOpener::~ConnOpener()
44 Comm::ConnOpener::doneAll() const
46 // is the conn_ to be opened still waiting?
48 return AsyncJob::doneAll();
51 // is the callback still to be called?
52 if (callback_
== NULL
|| callback_
->canceled()) {
53 return AsyncJob::doneAll();
56 // otherwise, we must be waiting for something
57 Must(temporaryFd_
>= 0 || calls_
.sleep_
);
62 Comm::ConnOpener::swanSong()
64 if (callback_
!= NULL
) {
65 // inform the still-waiting caller we are dying
66 sendAnswer(Comm::ERR_CONNECT
, 0, "Comm::ConnOpener::swanSong");
69 // did we abort with a temporary FD assigned?
70 if (temporaryFd_
>= 0)
73 // did we abort while waiting between retries?
81 Comm::ConnOpener::setHost(const char * new_host
)
83 // unset and erase if already set.
87 // set the new one if given.
89 host_
= xstrdup(new_host
);
93 Comm::ConnOpener::getHost() const
99 * Connection attempt are completed. One way or the other.
100 * Pass the results back to the external handler.
103 Comm::ConnOpener::sendAnswer(Comm::Flag errFlag
, int xerrno
, const char *why
)
105 // only mark the address good/bad AFTER connect is finished.
107 if (xerrno
== 0) // XXX: should not we use errFlag instead?
108 ipcacheMarkGoodAddr(host_
, conn_
->remote
);
110 ipcacheMarkBadAddr(host_
, conn_
->remote
);
112 if (Config
.onoff
.test_reachability
)
113 netdbDeleteAddrNetwork(conn_
->remote
);
118 if (callback_
!= NULL
) {
119 // avoid scheduling cancelled callbacks, assuming they are common
120 // enough to make this extra check an optimization
121 if (callback_
->canceled()) {
122 debugs(5, 4, conn_
<< " not calling canceled " << *callback_
<<
123 " [" << callback_
->id
<< ']' );
124 // TODO save the pconn to the pconnPool ?
126 typedef CommConnectCbParams Params
;
127 Params
¶ms
= GetCommParams
<Params
>(callback_
);
129 params
.flag
= errFlag
;
130 params
.xerrno
= xerrno
;
131 ScheduleCallHere(callback_
);
136 // The job will stop without this call because nil callback_ makes
137 // doneAll() true, but this explicit call creates nicer debugging.
141 /// cleans up this job I/O state without closing temporaryFd
142 /// required before closing temporaryFd or keeping it in conn_
143 /// leaves FD bare so must only be called via closeFd() or keepFd()
145 Comm::ConnOpener::cleanFd()
147 debugs(5, 4, HERE
<< conn_
<< " closing temp FD " << temporaryFd_
);
149 Must(temporaryFd_
>= 0);
150 fde
&f
= fd_table
[temporaryFd_
];
152 // Our write_handler was set without using Comm::Write API, so we cannot
153 // use a cancellable Pointer-free job callback and simply cancel it here.
154 if (f
.write_handler
) {
156 /* XXX: We are about to remove write_handler, which was responsible
157 * for deleting write_data, so we have to delete write_data
158 * ourselves. Comm currently calls SetSelect handlers synchronously
159 * so if write_handler is set, we know it has not been called yet.
160 * ConnOpener converts that sync call into an async one, but only
161 * after deleting ptr, so that is not a problem.
164 delete static_cast<Pointer
*>(f
.write_data
);
166 f
.write_handler
= NULL
;
168 // Comm::DoSelect does not do this when calling and resetting write_handler
169 // (because it expects more writes to come?). We could mimic that
170 // optimization by resetting Comm "Select" state only when the FD is
172 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, NULL
, NULL
, 0);
174 if (calls_
.timeout_
!= NULL
) {
175 calls_
.timeout_
->cancel("Comm::ConnOpener::cleanFd");
176 calls_
.timeout_
= NULL
;
178 // Comm checkTimeouts() and commCloseAllSockets() do not clear .timeout
179 // when calling timeoutHandler (XXX fix them), so we clear unconditionally.
180 f
.timeoutHandler
= NULL
;
183 if (calls_
.earlyAbort_
!= NULL
) {
184 comm_remove_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
185 calls_
.earlyAbort_
= NULL
;
189 /// cleans I/O state and ends I/O for temporaryFd_
191 Comm::ConnOpener::closeFd()
193 if (temporaryFd_
< 0)
198 // comm_close() below uses COMMIO_FD_WRITECB(fd)->active() to clear Comm
199 // "Select" state. It will not clear ours. XXX: It should always clear
200 // because a callback may have been active but was called before comm_close
201 // Update: we now do this in cleanFd()
202 // Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, NULL, NULL, 0);
204 comm_close(temporaryFd_
);
208 /// cleans I/O state and moves temporaryFd_ to the conn_ for long-term use
210 Comm::ConnOpener::keepFd()
213 Must(temporaryFd_
>= 0);
217 conn_
->fd
= temporaryFd_
;
222 Comm::ConnOpener::start()
226 /* outbound sockets have no need to be protocol agnostic. */
227 if (!(Ip::EnableIpv6
&IPV6_SPECIAL_V4MAPPING
) && conn_
->remote
.isIPv4()) {
228 conn_
->local
.setIPv4();
236 /// called at the end of Comm::ConnOpener::DelayedConnectRetry event
238 Comm::ConnOpener::restart()
240 debugs(5, 5, conn_
<< " restarting after sleep");
241 calls_
.sleep_
= false;
247 /// Create a socket for the future connection or return false.
248 /// If false is returned, done() is guaranteed to return true and end the job.
250 Comm::ConnOpener::createFd()
252 Must(temporaryFd_
< 0);
254 // our initators signal abort by cancelling their callbacks
255 if (callback_
== NULL
|| callback_
->canceled())
258 temporaryFd_
= comm_openex(SOCK_STREAM
, IPPROTO_TCP
, conn_
->local
, conn_
->flags
, host_
);
259 if (temporaryFd_
< 0) {
260 sendAnswer(Comm::ERR_CONNECT
, 0, "Comm::ConnOpener::createFd");
264 // Set TOS if needed.
266 Ip::Qos::setSockTos(temporaryFd_
, conn_
->tos
, conn_
->remote
.isIPv4() ? AF_INET
: AF_INET6
) < 0)
270 Ip::Qos::setSockNfmark(temporaryFd_
, conn_
->nfmark
) < 0)
274 fd_table
[conn_
->fd
].tosToServer
= conn_
->tos
;
275 fd_table
[conn_
->fd
].nfmarkToServer
= conn_
->nfmark
;
277 typedef CommCbMemFunT
<Comm::ConnOpener
, CommCloseCbParams
> abortDialer
;
278 calls_
.earlyAbort_
= JobCallback(5, 4, abortDialer
, this, Comm::ConnOpener::earlyAbort
);
279 comm_add_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
281 typedef CommCbMemFunT
<Comm::ConnOpener
, CommTimeoutCbParams
> timeoutDialer
;
282 calls_
.timeout_
= JobCallback(5, 4, timeoutDialer
, this, Comm::ConnOpener::timeout
);
283 debugs(5, 3, conn_
<< " will timeout in " << (deadline_
- squid_curtime
));
285 // Update the fd_table directly because commSetConnTimeout() needs open conn_
286 assert(temporaryFd_
< Squid_MaxFD
);
287 assert(fd_table
[temporaryFd_
].flags
.open
);
288 typedef CommTimeoutCbParams Params
;
289 Params
¶ms
= GetCommParams
<Params
>(calls_
.timeout_
);
291 fd_table
[temporaryFd_
].timeoutHandler
= calls_
.timeout_
;
292 fd_table
[temporaryFd_
].timeout
= deadline_
;
298 Comm::ConnOpener::connected()
300 Must(temporaryFd_
>= 0);
304 * stats.conn_open is used to account for the number of
305 * connections that we have open to the CachePeer, so we can limit
306 * based on the max-conn option. We need to increment here,
307 * even if the connection may fail.
309 if (CachePeer
*peer
=(conn_
->getPeer()))
310 ++peer
->stats
.conn_open
;
312 lookupLocalAddress();
314 /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to
315 * indicate the state of a raw fd object being passed around.
316 * Also, legacy code still depends on comm_local_port() with no access to Comm::Connection
317 * when those are done comm_local_port can become one of our member functions to do the below.
319 Must(fd_table
[conn_
->fd
].flags
.open
);
320 fd_table
[conn_
->fd
].local_addr
= conn_
->local
;
322 sendAnswer(Comm::OK
, 0, "Comm::ConnOpener::connected");
325 /// Make an FD connection attempt.
327 Comm::ConnOpener::doConnect()
330 Must(temporaryFd_
>= 0);
334 switch (comm_connect_addr(temporaryFd_
, conn_
->remote
) ) {
336 case Comm::INPROGRESS
:
337 debugs(5, 5, HERE
<< conn_
<< ": Comm::INPROGRESS");
338 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, Comm::ConnOpener::InProgressConnectRetry
, new Pointer(this), 0);
342 debugs(5, 5, HERE
<< conn_
<< ": Comm::OK - connected");
347 const int xerrno
= errno
;
350 debugs(5, 7, conn_
<< ": failure #" << failRetries_
<< " <= " <<
351 Config
.connect_retries
<< ": " << xstrerr(xerrno
));
353 if (failRetries_
< Config
.connect_retries
) {
354 debugs(5, 5, HERE
<< conn_
<< ": * - try again");
358 // send ERROR back to the upper layer.
359 debugs(5, 5, HERE
<< conn_
<< ": * - ERR tried too many times already.");
360 sendAnswer(Comm::ERR_CONNECT
, xerrno
, "Comm::ConnOpener::doConnect");
366 /// Close and wait a little before trying to open and connect again.
368 Comm::ConnOpener::retrySleep()
370 Must(!calls_
.sleep_
);
372 calls_
.sleep_
= true;
373 eventAdd("Comm::ConnOpener::DelayedConnectRetry",
374 Comm::ConnOpener::DelayedConnectRetry
,
375 new Pointer(this), 0.05, 0, false);
378 /// cleans up this job sleep state
380 Comm::ConnOpener::cancelSleep()
383 // It would be nice to delete the sleep event, but it might be out of
384 // the event queue and in the async queue already, so (a) we do not know
385 // whether we can safely delete the call ptr here and (b) eventDelete()
386 // will assert if the event went async. Thus, we let the event run so
387 // that it deletes the call ptr [after this job is gone]. Note that we
388 // are called only when the job ends so this "hanging event" will do
389 // nothing but deleting the call ptr. TODO: Revise eventDelete() API.
390 // eventDelete(Comm::ConnOpener::DelayedConnectRetry, calls_.sleep);
391 calls_
.sleep_
= false;
392 debugs(5, 9, conn_
<< " stops sleeping");
397 * Lookup local-end address and port of the TCP link just opened.
398 * This ensure the connection local details are set correctly
401 Comm::ConnOpener::lookupLocalAddress()
403 struct addrinfo
*addr
= NULL
;
404 Ip::Address::InitAddrInfo(addr
);
406 if (getsockname(conn_
->fd
, addr
->ai_addr
, &(addr
->ai_addrlen
)) != 0) {
407 debugs(50, DBG_IMPORTANT
, "ERROR: Failed to retrieve TCP/UDP details for socket: " << conn_
<< ": " << xstrerror());
408 Ip::Address::FreeAddrInfo(addr
);
412 conn_
->local
= *addr
;
413 Ip::Address::FreeAddrInfo(addr
);
414 debugs(5, 6, HERE
<< conn_
);
417 /** Abort connection attempt.
418 * Handles the case(s) when a partially setup connection gets closed early.
421 Comm::ConnOpener::earlyAbort(const CommCloseCbParams
&io
)
423 debugs(5, 3, HERE
<< io
.conn
);
424 calls_
.earlyAbort_
= NULL
;
425 // NP: is closing or shutdown better?
426 sendAnswer(Comm::ERR_CLOSING
, io
.xerrno
, "Comm::ConnOpener::earlyAbort");
430 * Handles the case(s) when a partially setup connection gets timed out.
431 * NP: When commSetConnTimeout accepts generic CommCommonCbParams this can die.
434 Comm::ConnOpener::timeout(const CommTimeoutCbParams
&)
436 debugs(5, 5, HERE
<< conn_
<< ": * - ERR took too long to receive response.");
437 calls_
.timeout_
= NULL
;
438 sendAnswer(Comm::TIMEOUT
, ETIMEDOUT
, "Comm::ConnOpener::timeout");
441 /* Legacy Wrapper for the retry event after Comm::INPROGRESS
442 * XXX: As soon as Comm::SetSelect() accepts Async calls we can use a ConnOpener::doConnect call
445 Comm::ConnOpener::InProgressConnectRetry(int fd
, void *data
)
447 Pointer
*ptr
= static_cast<Pointer
*>(data
);
449 if (ConnOpener
*cs
= ptr
->valid()) {
450 // Ew. we are now outside the all AsyncJob protections.
451 // get back inside by scheduling another call...
452 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
453 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::doConnect
);
454 ScheduleCallHere(call
);
459 /* Legacy Wrapper for the retry event with small delay after errors.
460 * XXX: As soon as eventAdd() accepts Async calls we can use a ConnOpener::restart call
463 Comm::ConnOpener::DelayedConnectRetry(void *data
)
465 Pointer
*ptr
= static_cast<Pointer
*>(data
);
467 if (ConnOpener
*cs
= ptr
->valid()) {
468 // Ew. we are now outside the all AsyncJob protections.
469 // get back inside by scheduling another call...
470 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
471 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::restart
);
472 ScheduleCallHere(call
);