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