]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm/ConnOpener.cc
Renamed struct peer to class CachePeer.
[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"
aed188fd
AJ
6#include "comm/ConnOpener.h"
7#include "comm/Connection.h"
8bbb16e3 8#include "comm/Loops.h"
aed188fd 9#include "comm.h"
c4ad1349 10#include "fd.h"
aed188fd 11#include "fde.h"
582c2af2 12#include "globals.h"
aed188fd 13#include "icmp/net_db.h"
714e68b7 14#include "ipcache.h"
4d5904f7 15#include "SquidConfig.h"
aed188fd
AJ
16#include "SquidTime.h"
17
21d845b1
FC
18#if HAVE_ERRNO_H
19#include <errno.h>
20#endif
21
a016163c 22CBDATA_NAMESPACED_CLASS_INIT(Comm, ConnOpener);
aed188fd 23
4accd6d8
AJ
24Comm::ConnOpener::ConnOpener(Comm::ConnectionPointer &c, AsyncCall::Pointer &handler, time_t ctimeout) :
25 AsyncJob("Comm::ConnOpener"),
5229395c 26 host_(NULL),
a95ff429 27 temporaryFd_(-1),
5229395c
AJ
28 conn_(c),
29 callback_(handler),
30 totalTries_(0),
31 failRetries_(0),
32 connectTimeout_(ctimeout),
418b3087 33 connectStart_(0)
5229395c 34{}
aed188fd 35
4accd6d8 36Comm::ConnOpener::~ConnOpener()
aed188fd 37{
5229395c 38 safe_free(host_);
aed188fd
AJ
39}
40
294775b5 41bool
4accd6d8 42Comm::ConnOpener::doneAll() const
294775b5 43{
5229395c 44 // is the conn_ to be opened still waiting?
e3a4aecc
AJ
45 if (conn_ == NULL) {
46 return AsyncJob::doneAll();
40d9f0fc 47 }
294775b5
AJ
48
49 // is the callback still to be called?
e3a4aecc
AJ
50 if (callback_ == NULL || callback_->canceled()) {
51 return AsyncJob::doneAll();
40d9f0fc 52 }
294775b5 53
e3a4aecc 54 return false;
294775b5
AJ
55}
56
e52e78c4 57void
4accd6d8 58Comm::ConnOpener::swanSong()
e52e78c4 59{
e52e78c4 60 // cancel any event watchers
5229395c
AJ
61 // done here to get the "swanSong" mention in cancel debugging.
62 if (calls_.earlyAbort_ != NULL) {
63 calls_.earlyAbort_->cancel("Comm::ConnOpener::swanSong");
64 calls_.earlyAbort_ = NULL;
e52e78c4 65 }
5229395c
AJ
66 if (calls_.timeout_ != NULL) {
67 calls_.timeout_->cancel("Comm::ConnOpener::swanSong");
68 calls_.timeout_ = NULL;
e52e78c4 69 }
4590cc7d 70
418b3087 71 if (callback_ != NULL) {
e3a4aecc
AJ
72 if (callback_->canceled())
73 callback_ = NULL;
74 else
75 // inform the still-waiting caller we are dying
76 doneConnecting(COMM_ERR_CONNECT, 0);
4590cc7d 77 }
d146d17e 78
a95ff429
AJ
79 // rollback what we can from the job state
80 if (temporaryFd_ >= 0) {
81 // doneConnecting() handles partial FD connection cleanup
82 doneConnecting(COMM_ERR_CONNECT, 0);
83 }
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.
a95ff429 109 * NP: on errors the earlyAbort call should be cancelled first with a reason.
5229395c 110 */
aed188fd 111void
5229395c 112Comm::ConnOpener::doneConnecting(comm_err_t status, int xerrno)
aed188fd 113{
e884bbde
AJ
114 // only mark the address good/bad AFTER connect is finished.
115 if (host_ != NULL) {
116 if (xerrno == 0)
117 ipcacheMarkGoodAddr(host_, conn_->remote);
118 else {
119 ipcacheMarkBadAddr(host_, conn_->remote);
120#if USE_ICMP
121 if (Config.onoff.test_reachability)
122 netdbDeleteAddrNetwork(conn_->remote);
123#endif
124 }
125 }
126
5229395c 127 if (callback_ != NULL) {
294775b5 128 typedef CommConnectCbParams Params;
5229395c
AJ
129 Params &params = GetCommParams<Params>(callback_);
130 params.conn = conn_;
294775b5
AJ
131 params.flag = status;
132 params.xerrno = xerrno;
5229395c
AJ
133 ScheduleCallHere(callback_);
134 callback_ = NULL;
294775b5
AJ
135 }
136
a95ff429 137 if (temporaryFd_ >= 0) {
6dd9a2e4 138 debugs(5, 4, HERE << conn_ << " closing temp FD " << temporaryFd_);
a95ff429
AJ
139 // it never reached fully open, so cleanup the FD handlers
140 // Note that comm_close() sequence does not happen for partially open FD
141 Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, NULL, NULL, 0);
142 calls_.earlyAbort_ = NULL;
143 if (calls_.timeout_ != NULL) {
144 calls_.timeout_->cancel("Comm::ConnOpener::doneConnecting");
145 calls_.timeout_ = NULL;
146 }
147 fd_table[temporaryFd_].timeoutHandler = NULL;
148 fd_table[temporaryFd_].timeout = 0;
6dd9a2e4 149 close(temporaryFd_);
a95ff429
AJ
150 fd_close(temporaryFd_);
151 temporaryFd_ = -1;
152 }
153
294775b5 154 /* ensure cleared local state, we are done. */
5229395c 155 conn_ = NULL;
aed188fd
AJ
156}
157
a9870624
AJ
158void
159Comm::ConnOpener::start()
aed188fd 160{
5229395c 161 Must(conn_ != NULL);
482dcd01 162
5229395c 163 /* get a socket open ready for connecting with */
a95ff429 164 if (temporaryFd_ < 0) {
aed188fd
AJ
165#if USE_IPV6
166 /* outbound sockets have no need to be protocol agnostic. */
5229395c
AJ
167 if (conn_->remote.IsIPv4()) {
168 conn_->local.SetIPv4();
aed188fd
AJ
169 }
170#endif
a95ff429
AJ
171 temporaryFd_ = comm_openex(SOCK_STREAM, IPPROTO_TCP, conn_->local, conn_->flags, conn_->tos, conn_->nfmark, host_);
172 if (temporaryFd_ < 0) {
5229395c 173 doneConnecting(COMM_ERR_CONNECT, 0);
aed188fd
AJ
174 return;
175 }
aed188fd
AJ
176 }
177
2832d7c0 178 typedef CommCbMemFunT<Comm::ConnOpener, CommCloseCbParams> abortDialer;
802540f2 179 calls_.earlyAbort_ = JobCallback(5, 4, abortDialer, this, Comm::ConnOpener::earlyAbort);
a95ff429 180 comm_add_close_handler(temporaryFd_, calls_.earlyAbort_);
418b3087 181
802540f2
AJ
182 typedef CommCbMemFunT<Comm::ConnOpener, CommTimeoutCbParams> timeoutDialer;
183 calls_.timeout_ = JobCallback(5, 4, timeoutDialer, this, Comm::ConnOpener::timeout);
418b3087 184 debugs(5, 3, HERE << conn_ << " timeout " << connectTimeout_);
a95ff429
AJ
185
186 // Update the fd_table directly because conn_ is not yet storing the FD
187 assert(temporaryFd_ < Squid_MaxFD);
188 assert(fd_table[temporaryFd_].flags.open);
189 typedef CommTimeoutCbParams Params;
190 Params &params = GetCommParams<Params>(calls_.timeout_);
191 params.conn = conn_;
192 fd_table[temporaryFd_].timeoutHandler = calls_.timeout_;
193 fd_table[temporaryFd_].timeout = squid_curtime + (time_t) connectTimeout_;
418b3087
AJ
194
195 connectStart_ = squid_curtime;
196 connect();
197}
198
199void
200Comm::ConnOpener::connected()
201{
a95ff429
AJ
202 conn_->fd = temporaryFd_;
203 temporaryFd_ = -1;
204
418b3087
AJ
205 /*
206 * stats.conn_open is used to account for the number of
a3c6762c 207 * connections that we have open to the CachePeer, so we can limit
418b3087
AJ
208 * based on the max-conn option. We need to increment here,
209 * even if the connection may fail.
210 */
a3c6762c 211 if (CachePeer *peer=(conn_->getPeer()))
b79bfaae 212 ++peer->stats.conn_open;
418b3087
AJ
213
214 lookupLocalAddress();
215
216 /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to
217 * indicate the state of a raw fd object being passed around.
218 * Also, legacy code still depends on comm_local_port() with no access to Comm::Connection
219 * when those are done comm_local_port can become one of our member functions to do the below.
220 */
221 fd_table[conn_->fd].flags.open = 1;
222 fd_table[conn_->fd].local_addr = conn_->local;
8968fd45
AJ
223}
224
5d779c44
AJ
225/** Make an FD connection attempt.
226 * Handles the case(s) when a partially setup connection gets closed early.
227 */
8968fd45 228void
418b3087 229Comm::ConnOpener::connect()
8968fd45
AJ
230{
231 Must(conn_ != NULL);
232
878bfa63
AJ
233 // our parent Jobs signal abort by cancelling their callbacks.
234 if (callback_ == NULL || callback_->canceled())
235 return;
236
a2f5277a 237 ++ totalTries_;
aed188fd 238
a95ff429 239 switch (comm_connect_addr(temporaryFd_, conn_->remote) ) {
aed188fd
AJ
240
241 case COMM_INPROGRESS:
294775b5 242 // check for timeout FIRST.
418b3087 243 if (squid_curtime - connectStart_ > connectTimeout_) {
5b67dfa4 244 debugs(5, 5, HERE << conn_ << ": * - ERR took too long already.");
e884bbde 245 calls_.earlyAbort_->cancel("Comm::ConnOpener::connect timed out");
5229395c 246 doneConnecting(COMM_TIMEOUT, errno);
294775b5
AJ
247 return;
248 } else {
5b67dfa4 249 debugs(5, 5, HERE << conn_ << ": COMM_INPROGRESS");
9e64d84e 250 Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, Comm::ConnOpener::InProgressConnectRetry, new Pointer(this), 0);
294775b5 251 }
aed188fd
AJ
252 break;
253
254 case COMM_OK:
5b67dfa4 255 debugs(5, 5, HERE << conn_ << ": COMM_OK - connected");
418b3087 256 connected();
5229395c 257 doneConnecting(COMM_OK, 0);
aed188fd
AJ
258 break;
259
260 default:
a2f5277a 261 ++failRetries_;
aed188fd
AJ
262
263 // check for timeout FIRST.
dc49061a 264 if (squid_curtime - connectStart_ > connectTimeout_) {
e884bbde
AJ
265 debugs(5, 5, HERE << conn_ << ": * - ERR took too long to receive response.");
266 calls_.earlyAbort_->cancel("Comm::ConnOpener::connect timed out");
5229395c
AJ
267 doneConnecting(COMM_TIMEOUT, errno);
268 } else if (failRetries_ < Config.connect_retries) {
e884bbde 269 debugs(5, 5, HERE << conn_ << ": * - try again");
38a9f558 270 eventAdd("Comm::ConnOpener::DelayedConnectRetry", Comm::ConnOpener::DelayedConnectRetry, new Pointer(this), 0.05, 0, false);
e884bbde 271 return;
aed188fd
AJ
272 } else {
273 // send ERROR back to the upper layer.
5b67dfa4 274 debugs(5, 5, HERE << conn_ << ": * - ERR tried too many times already.");
e884bbde 275 calls_.earlyAbort_->cancel("Comm::ConnOpener::connect failed");
5229395c 276 doneConnecting(COMM_ERR_CONNECT, errno);
aed188fd
AJ
277 }
278 }
279}
280
dd829807
AJ
281/**
282 * Lookup local-end address and port of the TCP link just opened.
283 * This ensure the connection local details are set correctly
284 */
285void
286Comm::ConnOpener::lookupLocalAddress()
287{
288 struct addrinfo *addr = NULL;
289 conn_->local.InitAddrInfo(addr);
290
291 if (getsockname(conn_->fd, addr->ai_addr, &(addr->ai_addrlen)) != 0) {
5b67dfa4 292 debugs(50, DBG_IMPORTANT, "ERROR: Failed to retrieve TCP/UDP details for socket: " << conn_ << ": " << xstrerror());
dd829807
AJ
293 conn_->local.FreeAddrInfo(addr);
294 return;
295 }
296
297 conn_->local = *addr;
298 conn_->local.FreeAddrInfo(addr);
5b67dfa4 299 debugs(5, 6, HERE << conn_);
dd829807
AJ
300}
301
5229395c
AJ
302/** Abort connection attempt.
303 * Handles the case(s) when a partially setup connection gets closed early.
304 */
aed188fd 305void
2832d7c0 306Comm::ConnOpener::earlyAbort(const CommCloseCbParams &io)
aed188fd 307{
5b67dfa4 308 debugs(5, 3, HERE << io.conn);
5229395c 309 doneConnecting(COMM_ERR_CLOSING, io.xerrno); // NP: is closing or shutdown better?
aed188fd
AJ
310}
311
5229395c
AJ
312/**
313 * Handles the case(s) when a partially setup connection gets timed out.
8d77a37c 314 * NP: When commSetConnTimeout accepts generic CommCommonCbParams this can die.
5229395c 315 */
aed188fd 316void
418b3087 317Comm::ConnOpener::timeout(const CommTimeoutCbParams &)
aed188fd 318{
418b3087 319 connect();
aed188fd
AJ
320}
321
5229395c 322/* Legacy Wrapper for the retry event after COMM_INPROGRESS
8bbb16e3 323 * XXX: As soon as Comm::SetSelect() accepts Async calls we can use a ConnOpener::connect call
5229395c 324 */
aed188fd 325void
418b3087 326Comm::ConnOpener::InProgressConnectRetry(int fd, void *data)
aed188fd 327{
9e64d84e
AR
328 Pointer *ptr = static_cast<Pointer*>(data);
329 assert(ptr);
330 if (ConnOpener *cs = ptr->valid()) {
42628300
A
331 // Ew. we are now outside the all AsyncJob protections.
332 // get back inside by scheduling another call...
333 typedef NullaryMemFunT<Comm::ConnOpener> Dialer;
334 AsyncCall::Pointer call = JobCallback(5, 4, Dialer, cs, Comm::ConnOpener::connect);
335 ScheduleCallHere(call);
9e64d84e
AR
336 }
337 delete ptr;
418b3087
AJ
338}
339
340/* Legacy Wrapper for the retry event with small delay after errors.
341 * XXX: As soon as eventAdd() accepts Async calls we can use a ConnOpener::connect call
342 */
343void
344Comm::ConnOpener::DelayedConnectRetry(void *data)
345{
9e64d84e
AR
346 Pointer *ptr = static_cast<Pointer*>(data);
347 assert(ptr);
348 if (ConnOpener *cs = ptr->valid()) {
42628300
A
349 // Ew. we are now outside the all AsyncJob protections.
350 // get back inside by scheduling another call...
351 typedef NullaryMemFunT<Comm::ConnOpener> Dialer;
352 AsyncCall::Pointer call = JobCallback(5, 4, Dialer, cs, Comm::ConnOpener::connect);
353 ScheduleCallHere(call);
9e64d84e
AR
354 }
355 delete ptr;
aed188fd 356}