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