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