ConnOpener.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
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.
7  */
8 
9 /* DEBUG: section 05 Socket Connection Opener */
10 
11 #include "squid.h"
12 #include "CachePeer.h"
13 #include "comm.h"
14 #include "comm/Connection.h"
15 #include "comm/ConnOpener.h"
16 #include "comm/Loops.h"
17 #include "fd.h"
18 #include "fde.h"
19 #include "globals.h"
20 #include "icmp/net_db.h"
21 #include "ip/QosConfig.h"
22 #include "ip/tools.h"
23 #include "ipcache.h"
24 #include "SquidConfig.h"
25 #include "SquidTime.h"
26 
27 #include <cerrno>
28 
29 class CachePeer;
30 
32 
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))
42 {
43  debugs(5, 3, "will connect to " << c << " with " << ctimeout << " timeout");
44 }
45 
47 {
48  safe_free(host_);
49 }
50 
51 bool
53 {
54  // is the conn_ to be opened still waiting?
55  if (conn_ == NULL) {
56  return AsyncJob::doneAll();
57  }
58 
59  // is the callback still to be called?
60  if (callback_ == NULL || callback_->canceled()) {
61  return AsyncJob::doneAll();
62  }
63 
64  // otherwise, we must be waiting for something
65  Must(temporaryFd_ >= 0 || calls_.sleep_);
66  return false;
67 }
68 
69 void
71 {
72  if (callback_ != NULL) {
73  // inform the still-waiting caller we are dying
74  sendAnswer(Comm::ERR_CONNECT, 0, "Comm::ConnOpener::swanSong");
75  }
76 
77  // did we abort with a temporary FD assigned?
78  if (temporaryFd_ >= 0)
79  closeFd();
80 
81  // did we abort while waiting between retries?
82  if (calls_.sleep_)
83  cancelSleep();
84 
86 }
87 
88 void
89 Comm::ConnOpener::setHost(const char * new_host)
90 {
91  // unset and erase if already set.
92  if (host_ != NULL)
93  safe_free(host_);
94 
95  // set the new one if given.
96  if (new_host != NULL)
97  host_ = xstrdup(new_host);
98 }
99 
100 const char *
102 {
103  return host_;
104 }
105 
110 void
111 Comm::ConnOpener::sendAnswer(Comm::Flag errFlag, int xerrno, const char *why)
112 {
113  // only mark the address good/bad AFTER connect is finished.
114  if (host_ != NULL) {
115  if (xerrno == 0) // XXX: should not we use errFlag instead?
116  ipcacheMarkGoodAddr(host_, conn_->remote);
117  else {
118  ipcacheMarkBadAddr(host_, conn_->remote);
119 #if USE_ICMP
121  netdbDeleteAddrNetwork(conn_->remote);
122 #endif
123  }
124  }
125 
126  if (callback_ != NULL) {
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 << ']' );
132  // TODO save the pconn to the pconnPool ?
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  }
141  callback_ = NULL;
142  }
143 
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 
152 void
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
177  // (because it expects more writes to come?). We could mimic that
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);
181 
182  if (calls_.timeout_ != NULL) {
183  calls_.timeout_->cancel("Comm::ConnOpener::cleanFd");
184  calls_.timeout_ = NULL;
185  }
186  // Comm checkTimeouts() and commCloseAllSockets() do not clear .timeout
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_);
193  calls_.earlyAbort_ = NULL;
194  }
195 }
196 
198 void
200 {
201  if (temporaryFd_ < 0)
202  return;
203 
204  cleanFd();
205 
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 
217 void
219 {
220  Must(conn_ != NULL);
221  Must(temporaryFd_ >= 0);
222 
223  cleanFd();
224 
225  conn_->fd = temporaryFd_;
226  temporaryFd_ = -1;
227 }
228 
229 void
231 {
232  Must(conn_ != NULL);
233 
234  /* outbound sockets have no need to be protocol agnostic. */
235  if (!(Ip::EnableIpv6&IPV6_SPECIAL_V4MAPPING) && conn_->remote.isIPv4()) {
236  conn_->local.setIPv4();
237  }
238 
239  conn_->noteStart();
240  if (createFd())
241  doConnect();
242 }
243 
245 void
247 {
248  debugs(5, 5, conn_ << " restarting after sleep");
249  calls_.sleep_ = false;
250 
251  if (createFd())
252  doConnect();
253 }
254 
257 bool
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 
266  temporaryFd_ = comm_openex(SOCK_STREAM, IPPROTO_TCP, conn_->local, conn_->flags, host_);
267  if (temporaryFd_ < 0) {
268  sendAnswer(Comm::ERR_CONNECT, 0, "Comm::ConnOpener::createFd");
269  return false;
270  }
271 
272  // Set TOS if needed.
273  if (conn_->tos &&
274  Ip::Qos::setSockTos(temporaryFd_, conn_->tos, conn_->remote.isIPv4() ? AF_INET : AF_INET6) < 0)
275  conn_->tos = 0;
276 #if SO_MARK
277  if (conn_->nfmark &&
278  Ip::Qos::setSockNfmark(temporaryFd_, conn_->nfmark) < 0)
279  conn_->nfmark = 0;
280 #endif
281 
282  fd_table[temporaryFd_].tosToServer = conn_->tos;
283  fd_table[temporaryFd_].nfmarkToServer = conn_->nfmark;
284 
286  calls_.earlyAbort_ = JobCallback(5, 4, abortDialer, this, Comm::ConnOpener::earlyAbort);
287  comm_add_close_handler(temporaryFd_, calls_.earlyAbort_);
288 
290  calls_.timeout_ = JobCallback(5, 4, timeoutDialer, this, Comm::ConnOpener::timeout);
291  debugs(5, 3, conn_ << " will timeout in " << (deadline_ - squid_curtime));
292 
293  // Update the fd_table directly because commSetConnTimeout() needs open conn_
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_;
300  fd_table[temporaryFd_].timeout = deadline_;
301 
302  return true;
303 }
304 
305 void
307 {
308  Must(temporaryFd_ >= 0);
309  keepFd();
310 
311  /*
312  * stats.conn_open is used to account for the number of
313  * connections that we have open to the CachePeer, so we can limit
314  * based on the max-conn option. We need to increment here,
315  * even if the connection may fail.
316  */
317  if (CachePeer *peer=(conn_->getPeer()))
318  ++peer->stats.conn_open;
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  */
327  Must(fd_table[conn_->fd].flags.open);
328  fd_table[conn_->fd].local_addr = conn_->local;
329 
330  sendAnswer(Comm::OK, 0, "Comm::ConnOpener::connected");
331 }
332 
334 void
336 {
337  Must(conn_ != NULL);
338  Must(temporaryFd_ >= 0);
339 
340  ++ totalTries_;
341 
342  switch (comm_connect_addr(temporaryFd_, conn_->remote) ) {
343 
344  case Comm::INPROGRESS:
345  debugs(5, 5, HERE << conn_ << ": Comm::INPROGRESS");
347  break;
348 
349  case Comm::OK:
350  debugs(5, 5, HERE << conn_ << ": Comm::OK - connected");
351  connected();
352  break;
353 
354  default: {
355  const int xerrno = errno;
356 
357  ++failRetries_;
358  debugs(5, 7, conn_ << ": failure #" << failRetries_ << " <= " <<
359  Config.connect_retries << ": " << xstrerr(xerrno));
360 
361  if (failRetries_ < Config.connect_retries) {
362  debugs(5, 5, HERE << conn_ << ": * - try again");
363  retrySleep();
364  return;
365  } else {
366  // send ERROR back to the upper layer.
367  debugs(5, 5, HERE << conn_ << ": * - ERR tried too many times already.");
368  sendAnswer(Comm::ERR_CONNECT, xerrno, "Comm::ConnOpener::doConnect");
369  }
370  }
371  }
372 }
373 
375 void
377 {
378  Must(!calls_.sleep_);
379  closeFd();
380  calls_.sleep_ = true;
381  eventAdd("Comm::ConnOpener::DelayedConnectRetry",
383  new Pointer(this), 0.05, 0, false);
384 }
385 
387 void
389 {
390  if (calls_.sleep_) {
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");
401  }
402 }
403 
408 void
410 {
411  struct addrinfo *addr = NULL;
413 
414  if (getsockname(conn_->fd, addr->ai_addr, &(addr->ai_addrlen)) != 0) {
415  int xerrno = errno;
416  debugs(50, DBG_IMPORTANT, "ERROR: Failed to retrieve TCP/UDP details for socket: " << conn_ << ": " << xstrerr(xerrno));
418  return;
419  }
420 
421  conn_->local = *addr;
423  debugs(5, 6, HERE << conn_);
424 }
425 
429 void
431 {
432  debugs(5, 3, HERE << io.conn);
433  calls_.earlyAbort_ = NULL;
434  // NP: is closing or shutdown better?
435  sendAnswer(Comm::ERR_CLOSING, io.xerrno, "Comm::ConnOpener::earlyAbort");
436 }
437 
442 void
444 {
445  debugs(5, 5, HERE << conn_ << ": * - ERR took too long to receive response.");
446  calls_.timeout_ = NULL;
447  sendAnswer(Comm::TIMEOUT, ETIMEDOUT, "Comm::ConnOpener::timeout");
448 }
449 
450 /* Legacy Wrapper for the retry event after Comm::INPROGRESS
451  * XXX: As soon as Comm::SetSelect() accepts Async calls we can use a ConnOpener::doConnect call
452  */
453 void
455 {
456  Pointer *ptr = static_cast<Pointer*>(data);
457  assert(ptr);
458  if (ConnOpener *cs = ptr->valid()) {
459  // Ew. we are now outside the all AsyncJob protections.
460  // get back inside by scheduling another call...
461  typedef NullaryMemFunT<Comm::ConnOpener> Dialer;
463  ScheduleCallHere(call);
464  }
465  delete ptr;
466 }
467 
468 /* Legacy Wrapper for the retry event with small delay after errors.
469  * XXX: As soon as eventAdd() accepts Async calls we can use a ConnOpener::restart call
470  */
471 void
473 {
474  Pointer *ptr = static_cast<Pointer*>(data);
475  assert(ptr);
476  if (ConnOpener *cs = ptr->valid()) {
477  // Ew. we are now outside the all AsyncJob protections.
478  // get back inside by scheduling another call...
479  typedef NullaryMemFunT<Comm::ConnOpener> Dialer;
480  AsyncCall::Pointer call = JobCallback(5, 4, Dialer, cs, Comm::ConnOpener::restart);
481  ScheduleCallHere(call);
482  }
483  delete ptr;
484 }
485 
void timeout(const CommTimeoutCbParams &)
Definition: ConnOpener.cc:443
void retrySleep()
Close and wait a little before trying to open and connect again.
Definition: ConnOpener.cc:376
const char * xstrerr(int error)
Definition: xstrerror.cc:83
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:968
void sendAnswer(Comm::Flag errFlag, int xerrno, const char *why)
Definition: ConnOpener.cc:111
void lookupLocalAddress()
Definition: ConnOpener.cc:409
Cbc * valid() const
was set and is valid
Definition: CbcPointer.h:41
int setSockNfmark(const Comm::ConnectionPointer &conn, nfmark_t mark)
Definition: QosConfig.cc:586
virtual void swanSong()
Definition: AsyncJob.h:54
void closeFd()
cleans I/O state and ends I/O for temporaryFd_
Definition: ConnOpener.cc:199
ConnOpener(Comm::ConnectionPointer &, const AsyncCall::Pointer &handler, time_t connect_timeout)
Definition: ConnOpener.cc:33
static void handler(int signo)
Definition: purge.cc:860
int comm_connect_addr(int sock, const Ip::Address &address)
Definition: comm.cc:591
#define ScheduleCallHere(call)
Definition: AsyncCall.h:171
virtual void start()
called by AsyncStart; do not call directly
Definition: ConnOpener.cc:230
const char * getHost() const
get the hostname noted for this connection
Definition: ConnOpener.cc:101
#define xstrdup
Abstraction layer for TCP, UDP, TLS, UDS and filedescriptor sockets.
Definition: AcceptLimiter.h:16
int test_reachability
Definition: SquidConfig.h:290
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:686
#define comm_close(x)
Definition: comm.h:28
Definition: Flag.h:16
virtual bool doneAll() const
whether positive goal has been reached
Definition: ConnOpener.cc:52
CBDATA_NAMESPACED_CLASS_INIT(Comm, ConnOpener)
#define DBG_IMPORTANT
Definition: Debug.h:46
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:88
Definition: fde.h:51
void setHost(const char *)
set the hostname note for this connection
Definition: ConnOpener.cc:89
void cancelSleep()
cleans up this job sleep state
Definition: ConnOpener.cc:388
static void DelayedConnectRetry(void *data)
Definition: ConnOpener.cc:472
int connect_retries
Definition: SquidConfig.h:353
virtual void swanSong()
Definition: ConnOpener.cc:70
#define NULL
Definition: types.h:166
virtual bool doneAll() const
whether positive goal has been reached
Definition: AsyncJob.cc:96
PF * write_handler
Definition: fde.h:144
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:128
time_t timeout
Definition: fde.h:147
#define IPV6_SPECIAL_V4MAPPING
Definition: tools.h:21
int comm_openex(int sock_type, int proto, Ip::Address &addr, int flags, const char *note)
Definition: comm.cc:330
void * write_data
Definition: fde.h:145
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:157
void const char HLPCB void * data
Definition: stub_helper.cc:16
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
#define safe_free(x)
Definition: xalloc.h:73
void doConnect()
Make an FD connection attempt.
Definition: ConnOpener.cc:335
#define assert(EX)
Definition: assert.h:19
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
time_t squid_curtime
Definition: stub_time.cc:17
void ipcacheMarkGoodAddr(const char *name, const Ip::Address &addr)
Definition: ipcache.cc:1066
int Squid_MaxFD
Flag
Definition: Flag.h:15
struct CachePeer::@32::@38 flags
#define fd_table
Definition: fde.h:182
bool params
Definition: CachePeer.h:108
void earlyAbort(const CommCloseCbParams &)
Definition: ConnOpener.cc:430
void ipcacheMarkBadAddr(const char *name, const Ip::Address &addr)
Definition: ipcache.cc:1058
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:225
void netdbDeleteAddrNetwork(Ip::Address &addr)
Definition: net_db.cc:1164
void * addr
Definition: membanger.c:46
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
void restart()
called at the end of Comm::ConnOpener::DelayedConnectRetry event
Definition: ConnOpener.cc:246
int setSockTos(const Comm::ConnectionPointer &conn, tos_t tos)
Definition: QosConfig.cc:558
AsyncCall::Pointer timeoutHandler
Definition: fde.h:146
static void InProgressConnectRetry(int fd, void *data)
Definition: ConnOpener.cc:454
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
void keepFd()
cleans I/O state and moves temporaryFd_ to the conn_ for long-term use
Definition: ConnOpener.cc:218
#define COMM_SELECT_WRITE
Definition: defines.h:37
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:995
struct SquidConfig::@112 onoff
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:109
static void InitAddr(struct addrinfo *&ai)
Definition: Address.cc:668
class SquidConfig Config
Definition: SquidConfig.cc:12

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors