]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm/ConnOpener.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / comm / ConnOpener.cc
CommitLineData
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
24class CachePeer;
25
a016163c 26CBDATA_NAMESPACED_CLASS_INIT(Comm, ConnOpener);
aed188fd 27
4accd6d8
AJ
28Comm::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 39Comm::ConnOpener::~ConnOpener()
aed188fd 40{
5229395c 41 safe_free(host_);
aed188fd
AJ
42}
43
294775b5 44bool
4accd6d8 45Comm::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 62void
4accd6d8 63Comm::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 81void
4accd6d8 82Comm::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
93const char *
4accd6d8 94Comm::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 103void
923b75ce 104Comm::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 &params = 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()
145void
146Comm::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_
191void
192Comm::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
210void
211Comm::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
222void
223Comm::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
237void
3c862035
A
238Comm::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.
249bool
250Comm::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 &params = 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
284void
285Comm::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 313void
418b3087 314Comm::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.
354void
f6c0d1ab 355Comm::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
366void
367Comm::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 */
387void
388Comm::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 407void
2832d7c0 408Comm::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 420void
418b3087 421Comm::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 431void
418b3087 432Comm::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 */
449void
450Comm::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}