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