ConnOpener.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 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 {
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)
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?
117  else {
119 #if USE_ICMP
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;
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.
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) {
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 
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. */
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 &&
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);
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_
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 
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 
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 
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;
412  Ip::Address::InitAddr(addr);
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));
417  Ip::Address::FreeAddr(addr);
418  return;
419  }
420 
421  conn_->local = *addr;
422  Ip::Address::FreeAddr(addr);
423  debugs(5, 6, HERE << conn_);
424 }
425 
429 void
431 {
432  debugs(5, 3, HERE << io.conn);
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 ipcacheMarkBadAddr(const char *name, const Ip::Address &addr)
Definition: ipcache.cc:1058
bool setIPv4()
Definition: Address.cc:224
AsyncCall::Pointer callback_
handler to be called on connection completion.
Definition: ConnOpener.h:74
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
#define fd_table
Definition: fde.h:174
Comm::ConnectionPointer conn_
single connection currently to be opened.
Definition: ConnOpener.h:73
struct Comm::ConnOpener::Calls calls_
virtual void start()
called by AsyncStart; do not call directly
Definition: ConnOpener.cc:230
#define assert(EX)
Definition: assert.h:17
CbcPointer< ConnOpener > Pointer
Definition: ConnOpener.h:32
int Squid_MaxFD
AsyncCall::Pointer timeout_
Definition: ConnOpener.h:85
int comm_connect_addr(int sock, const Ip::Address &address)
Definition: comm.cc:591
AsyncCall::Pointer earlyAbort_
Definition: ConnOpener.h:84
CBDATA_NAMESPACED_CLASS_INIT(Comm, ConnOpener)
nfmark_t nfmark
Definition: Connection.h:152
ConnOpener(Comm::ConnectionPointer &, const AsyncCall::Pointer &handler, time_t connect_timeout)
Definition: ConnOpener.cc:33
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:686
Abstraction layer for TCP, UDP, TLS, UDS and filedescriptor sockets.
Definition: AcceptLimiter.h:16
void mustStop(const char *aReason)
Definition: AsyncJob.cc:69
void setHost(const char *)
set the hostname note for this connection
Definition: ConnOpener.cc:89
#define xstrdup
const InstanceId< AsyncCall > id
Definition: AsyncCall.h:79
#define safe_free(x)
Definition: xalloc.h:73
Definition: Flag.h:16
CachePeer * getPeer() const
Definition: Connection.cc:98
#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
bool cancel(const char *reason)
Definition: AsyncCall.cc:52
const char * getHost() const
get the hostname noted for this connection
Definition: ConnOpener.cc:101
void earlyAbort(const CommCloseCbParams &)
Definition: ConnOpener.cc:430
void doConnect()
Make an FD connection attempt.
Definition: ConnOpener.cc:335
void cancelSleep()
cleans up this job sleep state
Definition: ConnOpener.cc:388
PF * write_handler
Definition: fde.h:142
time_t squid_curtime
Definition: stub_time.cc:17
bool isIPv4() const
Definition: Address.cc:158
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
const char * xstrerr(int error)
Definition: xstrerror.cc:83
void const char HLPCB void * data
Definition: stub_helper.cc:16
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:124
virtual bool doneAll() const
whether positive goal has been reached
Definition: ConnOpener.cc:52
#define DBG_IMPORTANT
Definition: Debug.h:46
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:225
Cbc * valid() const
was set and is valid
Definition: CbcPointer.h:41
virtual void swanSong()
Definition: AsyncJob.h:54
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:88
#define COMM_SELECT_WRITE
Definition: defines.h:37
static void InitAddr(struct addrinfo *&ai)
Definition: Address.cc:668
void ipcacheMarkGoodAddr(const char *name, const Ip::Address &addr)
Definition: ipcache.cc:1066
void * addr
Definition: membanger.c:46
void * write_data
Definition: fde.h:143
void sendAnswer(Comm::Flag errFlag, int xerrno, const char *why)
Definition: ConnOpener.cc:111
static void InProgressConnectRetry(int fd, void *data)
Definition: ConnOpener.cc:454
void retrySleep()
Close and wait a little before trying to open and connect again.
Definition: ConnOpener.cc:376
void keepFd()
cleans I/O state and moves temporaryFd_ to the conn_ for long-term use
Definition: ConnOpener.cc:218
void closeFd()
cleans I/O state and ends I/O for temporaryFd_
Definition: ConnOpener.cc:199
int test_reachability
Definition: SquidConfig.h:290
static void handler(int signo)
Definition: purge.cc:860
struct CachePeer::@32::@38 flags
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:153
static void DelayedConnectRetry(void *data)
Definition: ConnOpener.cc:472
struct SquidConfig::@112 onoff
Flag
Definition: Flag.h:15
Ip::Address local
Definition: Connection.h:135
time_t deadline_
if we are not done by then, we will call back with Comm::TIMEOUT
Definition: ConnOpener.h:80
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:109
void timeout(const CommTimeoutCbParams &)
Definition: ConnOpener.cc:443
virtual void swanSong()
Definition: ConnOpener.cc:70
int totalTries_
total number of connection attempts over all destinations so far.
Definition: ConnOpener.h:76
Ip::Address remote
Definition: Connection.h:138
#define ScheduleCallHere(call)
Definition: AsyncCall.h:166
int temporaryFd_
the FD being opened. Do NOT set conn_->fd until it is fully open.
Definition: ConnOpener.h:72
socklen_t ai_addrlen
virtual bool doneAll() const
whether positive goal has been reached
Definition: AsyncJob.cc:96
int comm_openex(int sock_type, int proto, Ip::Address &addr, int flags, const char *note)
Definition: comm.cc:330
bool params
Definition: CachePeer.h:108
AsyncCall::Pointer timeoutHandler
Definition: fde.h:144
bool canceled()
Definition: AsyncCall.h:58
struct sockaddr * ai_addr
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
Definition: fde.h:49
int setSockNfmark(const Comm::ConnectionPointer &conn, nfmark_t mark)
Definition: QosConfig.cc:586
time_t timeout
Definition: fde.h:145
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:961
#define IPV6_SPECIAL_V4MAPPING
Definition: tools.h:21
void netdbDeleteAddrNetwork(Ip::Address &addr)
Definition: net_db.cc:1165
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:988
void lookupLocalAddress()
Definition: ConnOpener.cc:409
int setSockTos(const Comm::ConnectionPointer &conn, tos_t tos)
Definition: QosConfig.cc:558
int connect_retries
Definition: SquidConfig.h:353
class SquidConfig Config
Definition: SquidConfig.cc:12
#define comm_close(x)
Definition: comm.h:28
#define NULL
Definition: types.h:166
char * host_
domain name we are trying to connect to.
Definition: ConnOpener.h:71
int failRetries_
number of retries current destination has been tried.
Definition: ConnOpener.h:77

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors