]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm/ConnOpener.cc
Fix tcp outgoing tos bugs
[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"
a011edee 6#include "CachePeer.h"
602d9612 7#include "comm.h"
aed188fd 8#include "comm/Connection.h"
602d9612 9#include "comm/ConnOpener.h"
8bbb16e3 10#include "comm/Loops.h"
c4ad1349 11#include "fd.h"
aed188fd 12#include "fde.h"
582c2af2 13#include "globals.h"
aed188fd 14#include "icmp/net_db.h"
37abc165 15#include "ip/tools.h"
602d9612 16#include "ipcache.h"
c6f168c1 17#include "ip/QosConfig.h"
4d5904f7 18#include "SquidConfig.h"
aed188fd
AJ
19#include "SquidTime.h"
20
1a30fdf5 21#include <cerrno>
21d845b1 22
a011edee
FC
23class CachePeer;
24
a016163c 25CBDATA_NAMESPACED_CLASS_INIT(Comm, ConnOpener);
aed188fd 26
4accd6d8
AJ
27Comm::ConnOpener::ConnOpener(Comm::ConnectionPointer &c, AsyncCall::Pointer &handler, time_t ctimeout) :
28 AsyncJob("Comm::ConnOpener"),
5229395c 29 host_(NULL),
a95ff429 30 temporaryFd_(-1),
5229395c
AJ
31 conn_(c),
32 callback_(handler),
33 totalTries_(0),
34 failRetries_(0),
923b75ce 35 deadline_(squid_curtime + static_cast<time_t>(ctimeout))
5229395c 36{}
aed188fd 37
4accd6d8 38Comm::ConnOpener::~ConnOpener()
aed188fd 39{
5229395c 40 safe_free(host_);
aed188fd
AJ
41}
42
294775b5 43bool
4accd6d8 44Comm::ConnOpener::doneAll() const
294775b5 45{
5229395c 46 // is the conn_ to be opened still waiting?
e3a4aecc
AJ
47 if (conn_ == NULL) {
48 return AsyncJob::doneAll();
40d9f0fc 49 }
294775b5
AJ
50
51 // is the callback still to be called?
e3a4aecc
AJ
52 if (callback_ == NULL || callback_->canceled()) {
53 return AsyncJob::doneAll();
40d9f0fc 54 }
294775b5 55
923b75ce
AR
56 // otherwise, we must be waiting for something
57 Must(temporaryFd_ >= 0 || calls_.sleep_);
e3a4aecc 58 return false;
294775b5
AJ
59}
60
e52e78c4 61void
4accd6d8 62Comm::ConnOpener::swanSong()
e52e78c4 63{
418b3087 64 if (callback_ != NULL) {
923b75ce 65 // inform the still-waiting caller we are dying
c8407295 66 sendAnswer(Comm::ERR_CONNECT, 0, "Comm::ConnOpener::swanSong");
4590cc7d 67 }
d146d17e 68
6ca3e20d 69 // did we abort with a temporary FD assigned?
923b75ce
AR
70 if (temporaryFd_ >= 0)
71 closeFd();
72
6ca3e20d 73 // did we abort while waiting between retries?
923b75ce
AR
74 if (calls_.sleep_)
75 cancelSleep();
a95ff429 76
d146d17e 77 AsyncJob::swanSong();
e52e78c4
AJ
78}
79
aed188fd 80void
4accd6d8 81Comm::ConnOpener::setHost(const char * new_host)
aed188fd 82{
85d2870d 83 // unset and erase if already set.
5229395c
AJ
84 if (host_ != NULL)
85 safe_free(host_);
85d2870d
AJ
86
87 // set the new one if given.
88 if (new_host != NULL)
5229395c 89 host_ = xstrdup(new_host);
aed188fd
AJ
90}
91
92const char *
4accd6d8 93Comm::ConnOpener::getHost() const
aed188fd 94{
5229395c 95 return host_;
aed188fd
AJ
96}
97
5229395c
AJ
98/**
99 * Connection attempt are completed. One way or the other.
100 * Pass the results back to the external handler.
101 */
aed188fd 102void
c8407295 103Comm::ConnOpener::sendAnswer(Comm::Flag errFlag, int xerrno, const char *why)
aed188fd 104{
e884bbde
AJ
105 // only mark the address good/bad AFTER connect is finished.
106 if (host_ != NULL) {
923b75ce 107 if (xerrno == 0) // XXX: should not we use errFlag instead?
e884bbde
AJ
108 ipcacheMarkGoodAddr(host_, conn_->remote);
109 else {
110 ipcacheMarkBadAddr(host_, conn_->remote);
111#if USE_ICMP
112 if (Config.onoff.test_reachability)
113 netdbDeleteAddrNetwork(conn_->remote);
114#endif
115 }
116 }
117
5229395c 118 if (callback_ != NULL) {
923b75ce
AR
119 // avoid scheduling cancelled callbacks, assuming they are common
120 // enough to make this extra check an optimization
121 if (callback_->canceled()) {
122 debugs(5, 4, conn_ << " not calling canceled " << *callback_ <<
123 " [" << callback_->id << ']' );
6ca3e20d 124 // TODO save the pconn to the pconnPool ?
923b75ce
AR
125 } else {
126 typedef CommConnectCbParams Params;
127 Params &params = GetCommParams<Params>(callback_);
128 params.conn = conn_;
129 params.flag = errFlag;
130 params.xerrno = xerrno;
131 ScheduleCallHere(callback_);
132 }
5229395c 133 callback_ = NULL;
294775b5
AJ
134 }
135
923b75ce
AR
136 // The job will stop without this call because nil callback_ makes
137 // doneAll() true, but this explicit call creates nicer debugging.
138 mustStop(why);
139}
140
141/// cleans up this job I/O state without closing temporaryFd
142/// required before closing temporaryFd or keeping it in conn_
143/// leaves FD bare so must only be called via closeFd() or keepFd()
144void
145Comm::ConnOpener::cleanFd()
146{
147 debugs(5, 4, HERE << conn_ << " closing temp FD " << temporaryFd_);
148
149 Must(temporaryFd_ >= 0);
150 fde &f = fd_table[temporaryFd_];
151
152 // Our write_handler was set without using Comm::Write API, so we cannot
153 // use a cancellable Pointer-free job callback and simply cancel it here.
154 if (f.write_handler) {
155
156 /* XXX: We are about to remove write_handler, which was responsible
157 * for deleting write_data, so we have to delete write_data
158 * ourselves. Comm currently calls SetSelect handlers synchronously
159 * so if write_handler is set, we know it has not been called yet.
160 * ConnOpener converts that sync call into an async one, but only
161 * after deleting ptr, so that is not a problem.
162 */
163
164 delete static_cast<Pointer*>(f.write_data);
165 f.write_data = NULL;
166 f.write_handler = NULL;
167 }
168 // Comm::DoSelect does not do this when calling and resetting write_handler
3c862035 169 // (because it expects more writes to come?). We could mimic that
923b75ce
AR
170 // optimization by resetting Comm "Select" state only when the FD is
171 // actually closed.
172 Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, NULL, NULL, 0);
3c862035 173
923b75ce
AR
174 if (calls_.timeout_ != NULL) {
175 calls_.timeout_->cancel("Comm::ConnOpener::cleanFd");
176 calls_.timeout_ = NULL;
177 }
3c862035 178 // Comm checkTimeouts() and commCloseAllSockets() do not clear .timeout
923b75ce
AR
179 // when calling timeoutHandler (XXX fix them), so we clear unconditionally.
180 f.timeoutHandler = NULL;
181 f.timeout = 0;
182
183 if (calls_.earlyAbort_ != NULL) {
184 comm_remove_close_handler(temporaryFd_, calls_.earlyAbort_);
a95ff429 185 calls_.earlyAbort_ = NULL;
a95ff429 186 }
923b75ce
AR
187}
188
189/// cleans I/O state and ends I/O for temporaryFd_
190void
191Comm::ConnOpener::closeFd()
192{
193 if (temporaryFd_ < 0)
194 return;
195
196 cleanFd();
a95ff429 197
923b75ce
AR
198 // comm_close() below uses COMMIO_FD_WRITECB(fd)->active() to clear Comm
199 // "Select" state. It will not clear ours. XXX: It should always clear
200 // because a callback may have been active but was called before comm_close
201 // Update: we now do this in cleanFd()
202 // Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, NULL, NULL, 0);
203
204 comm_close(temporaryFd_);
205 temporaryFd_ = -1;
206}
207
208/// cleans I/O state and moves temporaryFd_ to the conn_ for long-term use
209void
210Comm::ConnOpener::keepFd()
211{
212 Must(conn_ != NULL);
213 Must(temporaryFd_ >= 0);
214
215 cleanFd();
216
217 conn_->fd = temporaryFd_;
218 temporaryFd_ = -1;
aed188fd
AJ
219}
220
a9870624
AJ
221void
222Comm::ConnOpener::start()
aed188fd 223{
5229395c 224 Must(conn_ != NULL);
482dcd01 225
923b75ce 226 /* outbound sockets have no need to be protocol agnostic. */
4dd643d5
AJ
227 if (!(Ip::EnableIpv6&IPV6_SPECIAL_V4MAPPING) && conn_->remote.isIPv4()) {
228 conn_->local.setIPv4();
923b75ce
AR
229 }
230
8aec3e1b 231 conn_->noteStart();
923b75ce 232 if (createFd())
3d7ecaff 233 doConnect();
923b75ce
AR
234}
235
236/// called at the end of Comm::ConnOpener::DelayedConnectRetry event
237void
3c862035
A
238Comm::ConnOpener::restart()
239{
923b75ce
AR
240 debugs(5, 5, conn_ << " restarting after sleep");
241 calls_.sleep_ = false;
242
243 if (createFd())
3d7ecaff 244 doConnect();
923b75ce
AR
245}
246
3c862035 247/// Create a socket for the future connection or return false.
923b75ce
AR
248/// If false is returned, done() is guaranteed to return true and end the job.
249bool
250Comm::ConnOpener::createFd()
251{
252 Must(temporaryFd_ < 0);
253
254 // our initators signal abort by cancelling their callbacks
255 if (callback_ == NULL || callback_->canceled())
256 return false;
257
c6f168c1 258 temporaryFd_ = comm_openex(SOCK_STREAM, IPPROTO_TCP, conn_->local, conn_->flags, host_);
a95ff429 259 if (temporaryFd_ < 0) {
c8407295 260 sendAnswer(Comm::ERR_CONNECT, 0, "Comm::ConnOpener::createFd");
923b75ce 261 return false;
aed188fd
AJ
262 }
263
c6f168c1
CT
264 // Set TOS if needed.
265 if (conn_->tos &&
266 Ip::Qos::setSockTos(temporaryFd_, conn_->tos, conn_->remote.isIPv4() ? AF_INET : AF_INET6) < 0)
267 conn_->tos = 0;
268#if SO_MARK
269 if (conn_->nfmark &&
270 Ip::Qos::setSockNfmark(temporaryFd_, conn_->nfmark) < 0)
271 conn_->nfmark = 0;
272#endif
273
274 fd_table[conn_->fd].tosToServer = conn_->tos;
275 fd_table[conn_->fd].nfmarkToServer = conn_->nfmark;
276
2832d7c0 277 typedef CommCbMemFunT<Comm::ConnOpener, CommCloseCbParams> abortDialer;
802540f2 278 calls_.earlyAbort_ = JobCallback(5, 4, abortDialer, this, Comm::ConnOpener::earlyAbort);
a95ff429 279 comm_add_close_handler(temporaryFd_, calls_.earlyAbort_);
418b3087 280
802540f2
AJ
281 typedef CommCbMemFunT<Comm::ConnOpener, CommTimeoutCbParams> timeoutDialer;
282 calls_.timeout_ = JobCallback(5, 4, timeoutDialer, this, Comm::ConnOpener::timeout);
923b75ce 283 debugs(5, 3, conn_ << " will timeout in " << (deadline_ - squid_curtime));
a95ff429 284
923b75ce 285 // Update the fd_table directly because commSetConnTimeout() needs open conn_
a95ff429
AJ
286 assert(temporaryFd_ < Squid_MaxFD);
287 assert(fd_table[temporaryFd_].flags.open);
288 typedef CommTimeoutCbParams Params;
289 Params &params = GetCommParams<Params>(calls_.timeout_);
290 params.conn = conn_;
291 fd_table[temporaryFd_].timeoutHandler = calls_.timeout_;
923b75ce 292 fd_table[temporaryFd_].timeout = deadline_;
418b3087 293
923b75ce 294 return true;
418b3087
AJ
295}
296
297void
298Comm::ConnOpener::connected()
299{
923b75ce
AR
300 Must(temporaryFd_ >= 0);
301 keepFd();
a95ff429 302
418b3087
AJ
303 /*
304 * stats.conn_open is used to account for the number of
a3c6762c 305 * connections that we have open to the CachePeer, so we can limit
418b3087
AJ
306 * based on the max-conn option. We need to increment here,
307 * even if the connection may fail.
308 */
a3c6762c 309 if (CachePeer *peer=(conn_->getPeer()))
b79bfaae 310 ++peer->stats.conn_open;
418b3087
AJ
311
312 lookupLocalAddress();
313
314 /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to
315 * indicate the state of a raw fd object being passed around.
316 * Also, legacy code still depends on comm_local_port() with no access to Comm::Connection
317 * when those are done comm_local_port can become one of our member functions to do the below.
318 */
923b75ce 319 Must(fd_table[conn_->fd].flags.open);
418b3087 320 fd_table[conn_->fd].local_addr = conn_->local;
923b75ce 321
c8407295 322 sendAnswer(Comm::OK, 0, "Comm::ConnOpener::connected");
8968fd45
AJ
323}
324
923b75ce 325/// Make an FD connection attempt.
8968fd45 326void
3d7ecaff 327Comm::ConnOpener::doConnect()
8968fd45
AJ
328{
329 Must(conn_ != NULL);
923b75ce 330 Must(temporaryFd_ >= 0);
878bfa63 331
a2f5277a 332 ++ totalTries_;
aed188fd 333
a95ff429 334 switch (comm_connect_addr(temporaryFd_, conn_->remote) ) {
aed188fd 335
c8407295
AJ
336 case Comm::INPROGRESS:
337 debugs(5, 5, HERE << conn_ << ": Comm::INPROGRESS");
923b75ce 338 Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, Comm::ConnOpener::InProgressConnectRetry, new Pointer(this), 0);
aed188fd
AJ
339 break;
340
c8407295
AJ
341 case Comm::OK:
342 debugs(5, 5, HERE << conn_ << ": Comm::OK - connected");
418b3087 343 connected();
aed188fd
AJ
344 break;
345
923b75ce
AR
346 default: {
347 const int xerrno = errno;
348
a2f5277a 349 ++failRetries_;
923b75ce
AR
350 debugs(5, 7, conn_ << ": failure #" << failRetries_ << " <= " <<
351 Config.connect_retries << ": " << xstrerr(xerrno));
aed188fd 352
923b75ce 353 if (failRetries_ < Config.connect_retries) {
e884bbde 354 debugs(5, 5, HERE << conn_ << ": * - try again");
f6c0d1ab 355 retrySleep();
e884bbde 356 return;
aed188fd
AJ
357 } else {
358 // send ERROR back to the upper layer.
5b67dfa4 359 debugs(5, 5, HERE << conn_ << ": * - ERR tried too many times already.");
294e1994 360 sendAnswer(Comm::ERR_CONNECT, xerrno, "Comm::ConnOpener::doConnect");
aed188fd
AJ
361 }
362 }
923b75ce
AR
363 }
364}
365
366/// Close and wait a little before trying to open and connect again.
367void
f6c0d1ab 368Comm::ConnOpener::retrySleep()
3c862035 369{
923b75ce
AR
370 Must(!calls_.sleep_);
371 closeFd();
372 calls_.sleep_ = true;
373 eventAdd("Comm::ConnOpener::DelayedConnectRetry",
374 Comm::ConnOpener::DelayedConnectRetry,
375 new Pointer(this), 0.05, 0, false);
376}
377
378/// cleans up this job sleep state
379void
380Comm::ConnOpener::cancelSleep()
381{
382 if (calls_.sleep_) {
3c862035
A
383 // It would be nice to delete the sleep event, but it might be out of
384 // the event queue and in the async queue already, so (a) we do not know
385 // whether we can safely delete the call ptr here and (b) eventDelete()
386 // will assert if the event went async. Thus, we let the event run so
387 // that it deletes the call ptr [after this job is gone]. Note that we
388 // are called only when the job ends so this "hanging event" will do
389 // nothing but deleting the call ptr. TODO: Revise eventDelete() API.
390 // eventDelete(Comm::ConnOpener::DelayedConnectRetry, calls_.sleep);
391 calls_.sleep_ = false;
392 debugs(5, 9, conn_ << " stops sleeping");
923b75ce 393 }
aed188fd
AJ
394}
395
dd829807
AJ
396/**
397 * Lookup local-end address and port of the TCP link just opened.
398 * This ensure the connection local details are set correctly
399 */
400void
401Comm::ConnOpener::lookupLocalAddress()
402{
403 struct addrinfo *addr = NULL;
4dd643d5 404 Ip::Address::InitAddrInfo(addr);
dd829807
AJ
405
406 if (getsockname(conn_->fd, addr->ai_addr, &(addr->ai_addrlen)) != 0) {
5b67dfa4 407 debugs(50, DBG_IMPORTANT, "ERROR: Failed to retrieve TCP/UDP details for socket: " << conn_ << ": " << xstrerror());
4dd643d5 408 Ip::Address::FreeAddrInfo(addr);
dd829807
AJ
409 return;
410 }
411
412 conn_->local = *addr;
4dd643d5 413 Ip::Address::FreeAddrInfo(addr);
5b67dfa4 414 debugs(5, 6, HERE << conn_);
dd829807
AJ
415}
416
5229395c
AJ
417/** Abort connection attempt.
418 * Handles the case(s) when a partially setup connection gets closed early.
419 */
aed188fd 420void
2832d7c0 421Comm::ConnOpener::earlyAbort(const CommCloseCbParams &io)
aed188fd 422{
5b67dfa4 423 debugs(5, 3, HERE << io.conn);
923b75ce
AR
424 calls_.earlyAbort_ = NULL;
425 // NP: is closing or shutdown better?
c8407295 426 sendAnswer(Comm::ERR_CLOSING, io.xerrno, "Comm::ConnOpener::earlyAbort");
aed188fd
AJ
427}
428
5229395c
AJ
429/**
430 * Handles the case(s) when a partially setup connection gets timed out.
8d77a37c 431 * NP: When commSetConnTimeout accepts generic CommCommonCbParams this can die.
5229395c 432 */
aed188fd 433void
418b3087 434Comm::ConnOpener::timeout(const CommTimeoutCbParams &)
aed188fd 435{
923b75ce
AR
436 debugs(5, 5, HERE << conn_ << ": * - ERR took too long to receive response.");
437 calls_.timeout_ = NULL;
c8407295 438 sendAnswer(Comm::TIMEOUT, ETIMEDOUT, "Comm::ConnOpener::timeout");
aed188fd
AJ
439}
440
c8407295 441/* Legacy Wrapper for the retry event after Comm::INPROGRESS
294e1994 442 * XXX: As soon as Comm::SetSelect() accepts Async calls we can use a ConnOpener::doConnect call
5229395c 443 */
aed188fd 444void
418b3087 445Comm::ConnOpener::InProgressConnectRetry(int fd, void *data)
aed188fd 446{
9e64d84e
AR
447 Pointer *ptr = static_cast<Pointer*>(data);
448 assert(ptr);
449 if (ConnOpener *cs = ptr->valid()) {
42628300
A
450 // Ew. we are now outside the all AsyncJob protections.
451 // get back inside by scheduling another call...
452 typedef NullaryMemFunT<Comm::ConnOpener> Dialer;
294e1994 453 AsyncCall::Pointer call = JobCallback(5, 4, Dialer, cs, Comm::ConnOpener::doConnect);
42628300 454 ScheduleCallHere(call);
9e64d84e
AR
455 }
456 delete ptr;
418b3087
AJ
457}
458
459/* Legacy Wrapper for the retry event with small delay after errors.
923b75ce 460 * XXX: As soon as eventAdd() accepts Async calls we can use a ConnOpener::restart call
418b3087
AJ
461 */
462void
463Comm::ConnOpener::DelayedConnectRetry(void *data)
464{
9e64d84e
AR
465 Pointer *ptr = static_cast<Pointer*>(data);
466 assert(ptr);
467 if (ConnOpener *cs = ptr->valid()) {
42628300
A
468 // Ew. we are now outside the all AsyncJob protections.
469 // get back inside by scheduling another call...
470 typedef NullaryMemFunT<Comm::ConnOpener> Dialer;
923b75ce 471 AsyncCall::Pointer call = JobCallback(5, 4, Dialer, cs, Comm::ConnOpener::restart);
42628300 472 ScheduleCallHere(call);
9e64d84e
AR
473 }
474 delete ptr;
aed188fd 475}