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