]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm/TcpAcceptor.cc
Fix tcp outgoing tos bugs
[thirdparty/squid.git] / src / comm / TcpAcceptor.cc
CommitLineData
04f55905 1/*
b510f3a1 2 * DEBUG: section 05 Listener Socket Handler
04f55905
AJ
3 * AUTHOR: Harvest Derived
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 *
32 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
33 */
34
582c2af2 35#include "squid.h"
94bfd31f 36#include "anyp/PortCfg.h"
a9870624 37#include "base/TextException.h"
95e6d864 38#include "client_db.h"
04f55905
AJ
39#include "comm/AcceptLimiter.h"
40#include "comm/comm_internal.h"
5b67dfa4 41#include "comm/Connection.h"
d841c88d 42#include "comm/Loops.h"
cbff89ba 43#include "comm/TcpAcceptor.h"
602d9612 44#include "CommCalls.h"
83b62d3f 45#include "eui/Config.h"
c4ad1349 46#include "fd.h"
04f55905 47#include "fde.h"
67679543 48#include "globals.h"
40d34a62 49#include "ip/Intercept.h"
c6f168c1 50#include "ip/QosConfig.h"
94bfd31f 51#include "MasterXaction.h"
582c2af2 52#include "profiler/Profiler.h"
4d5904f7 53#include "SquidConfig.h"
04f55905 54#include "SquidTime.h"
e4f1fdae 55#include "StatCounters.h"
04f55905 56
1a30fdf5 57#include <cerrno>
f842580f
AJ
58#ifdef HAVE_NETINET_TCP_H
59// required for accept_filter to build.
60#include <netinet/tcp.h>
61#endif
21d845b1 62
f842580f 63CBDATA_NAMESPACED_CLASS_INIT(Comm, TcpAcceptor);
a9870624 64
8bbb16e3 65Comm::TcpAcceptor::TcpAcceptor(const Comm::ConnectionPointer &newConn, const char *note, const Subscription::Pointer &aSub) :
cbff89ba 66 AsyncJob("Comm::TcpAcceptor"),
0ba55a12
AJ
67 errcode(0),
68 isLimited(0),
5b67dfa4 69 theCallSub(aSub),
fa720bfb
AJ
70 conn(newConn),
71 listenPort_()
72{}
73
74Comm::TcpAcceptor::TcpAcceptor(const AnyP::PortCfgPointer &p, const char *note, const Subscription::Pointer &aSub) :
75 AsyncJob("Comm::TcpAcceptor"),
76 errcode(0),
77 isLimited(0),
78 theCallSub(aSub),
79 conn(p->listenConn),
80 listenPort_(p)
cbff89ba 81{}
0ba55a12
AJ
82
83void
cbff89ba 84Comm::TcpAcceptor::subscribe(const Subscription::Pointer &aSub)
0ba55a12 85{
cbff89ba 86 debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << aSub);
5b67dfa4
AJ
87 unsubscribe("subscription change");
88 theCallSub = aSub;
4c5518e5
AJ
89}
90
0ba55a12 91void
cbff89ba 92Comm::TcpAcceptor::unsubscribe(const char *reason)
0ba55a12 93{
cbff89ba 94 debugs(5, 5, HERE << status() << " AsyncCall Subscription " << theCallSub << " removed: " << reason);
5b67dfa4 95 theCallSub = NULL;
0ba55a12
AJ
96}
97
a9870624 98void
cbff89ba 99Comm::TcpAcceptor::start()
a9870624 100{
cbff89ba 101 debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << theCallSub);
a9870624
AJ
102
103 Must(IsConnOpen(conn));
104
105 setListen();
106
8aec3e1b
CT
107 conn->noteStart();
108
a9870624
AJ
109 // if no error so far start accepting connections.
110 if (errcode == 0)
8bbb16e3 111 SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
a9870624
AJ
112}
113
114bool
cbff89ba 115Comm::TcpAcceptor::doneAll() const
a9870624 116{
6f8536c0 117 // stop when FD is closed
a9870624 118 if (!IsConnOpen(conn)) {
a9870624
AJ
119 return AsyncJob::doneAll();
120 }
121
5b67dfa4
AJ
122 // stop when handlers are gone
123 if (theCallSub == NULL) {
a9870624
AJ
124 return AsyncJob::doneAll();
125 }
126
5b67dfa4 127 // open FD with handlers...keep accepting.
a9870624
AJ
128 return false;
129}
130
131void
cbff89ba 132Comm::TcpAcceptor::swanSong()
a9870624
AJ
133{
134 debugs(5,5, HERE);
5b67dfa4 135 unsubscribe("swanSong");
6f09127c
AR
136 if (IsConnOpen(conn)) {
137 if (closer_ != NULL)
138 comm_remove_close_handler(conn->fd, closer_);
139 conn->close();
140 }
141
a9870624 142 conn = NULL;
5b67dfa4 143 AcceptLimiter::Instance().removeDead(this);
a9870624
AJ
144 AsyncJob::swanSong();
145}
146
cbff89ba
AJ
147const char *
148Comm::TcpAcceptor::status() const
149{
8bbb16e3
AJ
150 if (conn == NULL)
151 return "[nil connection]";
152
cbff89ba
AJ
153 static char ipbuf[MAX_IPSTRLEN] = {'\0'};
154 if (ipbuf[0] == '\0')
4dd643d5 155 conn->local.toHostStr(ipbuf, MAX_IPSTRLEN);
cbff89ba
AJ
156
157 static MemBuf buf;
158 buf.reset();
8bbb16e3 159 buf.Printf(" FD %d, %s",conn->fd, ipbuf);
cbff89ba
AJ
160
161 const char *jobStatus = AsyncJob::status();
162 buf.append(jobStatus, strlen(jobStatus));
163
164 return buf.content();
165}
166
0ba55a12
AJ
167/**
168 * New-style listen and accept routines
169 *
170 * setListen simply registers our interest in an FD for listening.
171 * The constructor takes a callback to call when an FD has been
172 * accept()ed some time later.
173 */
174void
cbff89ba 175Comm::TcpAcceptor::setListen()
0ba55a12
AJ
176{
177 errcode = 0; // reset local errno copy.
a9870624 178 if (listen(conn->fd, Squid_MaxFD >> 2) < 0) {
cbff89ba 179 debugs(50, DBG_CRITICAL, "ERROR: listen(" << status() << ", " << (Squid_MaxFD >> 2) << "): " << xstrerror());
0ba55a12
AJ
180 errcode = errno;
181 return;
182 }
183
184 if (Config.accept_filter && strcmp(Config.accept_filter, "none") != 0) {
185#ifdef SO_ACCEPTFILTER
186 struct accept_filter_arg afa;
187 bzero(&afa, sizeof(afa));
5b67dfa4 188 debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on " << conn);
0ba55a12 189 xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name));
a9870624 190 if (setsockopt(conn->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0)
5b67dfa4 191 debugs(5, DBG_CRITICAL, "WARNING: SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerror());
0ba55a12
AJ
192#elif defined(TCP_DEFER_ACCEPT)
193 int seconds = 30;
194 if (strncmp(Config.accept_filter, "data=", 5) == 0)
195 seconds = atoi(Config.accept_filter + 5);
a9870624 196 if (setsockopt(conn->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds)) < 0)
5b67dfa4 197 debugs(5, DBG_CRITICAL, "WARNING: TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerror());
0ba55a12 198#else
5b67dfa4 199 debugs(5, DBG_CRITICAL, "WARNING: accept_filter not supported on your OS");
0ba55a12
AJ
200#endif
201 }
6f09127c 202
c6f168c1
CT
203#if 0
204 // Untested code.
205 // Set TOS if needed.
206 // To correctly implement TOS values on listening sockets, probably requires
207 // more work to inherit TOS values to created connection objects.
208 if (conn->tos &&
209 Ip::Qos::setSockTos(conn->fd, conn->tos, conn->remote.isIPv4() ? AF_INET : AF_INET6) < 0)
210 conn->tos = 0;
211#if SO_MARK
212 if (conn->nfmark &&
213 Ip::Qos::setSockNfmark(conn->fd, conn->nfmark) < 0)
214 conn->nfmark = 0;
215#endif
216#endif
217
6f09127c
AR
218 typedef CommCbMemFunT<Comm::TcpAcceptor, CommCloseCbParams> Dialer;
219 closer_ = JobCallback(5, 4, Dialer, this, Comm::TcpAcceptor::handleClosure);
220 comm_add_close_handler(conn->fd, closer_);
221}
222
223/// called when listening descriptor is closed by an external force
224/// such as clientHttpConnectionsClose()
225void
226Comm::TcpAcceptor::handleClosure(const CommCloseCbParams &io)
227{
228 closer_ = NULL;
229 conn = NULL;
230 Must(done());
04f55905
AJ
231}
232
233/**
234 * This private callback is called whenever a filedescriptor is ready
235 * to dupe itself and fob off an accept()ed connection
236 *
237 * It will either do that accept operation. Or if there are not enough FD
238 * available to do the clone safely will push the listening FD into a list
239 * of deferred operations. The list gets kicked and the dupe/accept() actually
240 * done later when enough sockets become available.
241 */
242void
cbff89ba 243Comm::TcpAcceptor::doAccept(int fd, void *data)
04f55905 244{
5b67dfa4
AJ
245 try {
246 debugs(5, 2, HERE << "New connection on FD " << fd);
04f55905 247
5b67dfa4 248 Must(isOpen(fd));
cbff89ba 249 TcpAcceptor *afd = static_cast<TcpAcceptor*>(data);
04f55905 250
5b67dfa4
AJ
251 if (!okToAccept()) {
252 AcceptLimiter::Instance().defer(afd);
253 } else {
254 afd->acceptNext();
255 }
cbff89ba 256 SetSelect(fd, COMM_SELECT_READ, Comm::TcpAcceptor::doAccept, afd, 0);
5b67dfa4 257
db98b2bd 258 } catch (const std::exception &e) {
cbff89ba 259 fatalf("FATAL: error while accepting new client connection: %s\n", e.what());
db98b2bd 260 } catch (...) {
5b67dfa4 261 fatal("FATAL: error while accepting new client connection: [unkown]\n");
04f55905 262 }
04f55905
AJ
263}
264
265bool
cbff89ba 266Comm::TcpAcceptor::okToAccept()
04f55905
AJ
267{
268 static time_t last_warn = 0;
269
270 if (fdNFree() >= RESERVED_FD)
271 return true;
272
273 if (last_warn + 15 < squid_curtime) {
274 debugs(5, DBG_CRITICAL, "WARNING! Your cache is running out of filedescriptors");
275 last_warn = squid_curtime;
276 }
277
278 return false;
279}
280
971581ee 281void
cbff89ba 282Comm::TcpAcceptor::acceptOne()
04f55905
AJ
283{
284 /*
285 * We don't worry about running low on FDs here. Instead,
286 * doAccept() will use AcceptLimiter if we reach the limit
287 * there.
288 */
289
290 /* Accept a new connection */
5b67dfa4 291 ConnectionPointer newConnDetails = new Connection();
c8407295 292 const Comm::Flag flag = oldAccept(newConnDetails);
04f55905
AJ
293
294 /* Check for errors */
5b67dfa4 295 if (!newConnDetails->isOpen()) {
04f55905 296
c8407295 297 if (flag == Comm::NOMESSAGE) {
04f55905 298 /* register interest again */
6f8536c0 299 debugs(5, 5, HERE << "try later: " << conn << " handler Subscription: " << theCallSub);
8bbb16e3 300 SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
971581ee 301 return;
04f55905
AJ
302 }
303
304 // A non-recoverable error; notify the caller */
cbff89ba 305 debugs(5, 5, HERE << "non-recoverable error:" << status() << " handler Subscription: " << theCallSub);
8bbb16e3 306 notify(flag, newConnDetails);
5b67dfa4 307 mustStop("Listener socket closed");
971581ee 308 return;
04f55905
AJ
309 }
310
5b67dfa4
AJ
311 debugs(5, 5, HERE << "Listener: " << conn <<
312 " accepted new connection " << newConnDetails <<
6f8536c0 313 " handler Subscription: " << theCallSub);
8bbb16e3 314 notify(flag, newConnDetails);
04f55905
AJ
315}
316
317void
cbff89ba 318Comm::TcpAcceptor::acceptNext()
04f55905 319{
a9870624 320 Must(IsConnOpen(conn));
5b67dfa4 321 debugs(5, 2, HERE << "connection on " << conn);
971581ee 322 acceptOne();
04f55905
AJ
323}
324
325void
c8407295 326Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &newConnDetails) const
04f55905 327{
c8407295 328 // listener socket handlers just abandon the port with Comm::ERR_CLOSING
04f55905 329 // it should only happen when this object is deleted...
c8407295 330 if (flag == Comm::ERR_CLOSING) {
04f55905
AJ
331 return;
332 }
333
5b67dfa4
AJ
334 if (theCallSub != NULL) {
335 AsyncCall::Pointer call = theCallSub->callback();
336 CommAcceptCbParams &params = GetCommParams<CommAcceptCbParams>(call);
94bfd31f 337 params.xaction = new MasterXaction;
fa720bfb 338 params.xaction->squidPort = listenPort_;
a9870624 339 params.fd = conn->fd;
94bfd31f 340 params.conn = params.xaction->tcpClient = newConnDetails;
0ba55a12
AJ
341 params.flag = flag;
342 params.xerrno = errcode;
343 ScheduleCallHere(call);
0ba55a12 344 }
04f55905
AJ
345}
346
347/**
97b8ac39 348 * accept() and process
5b67dfa4
AJ
349 * Wait for an incoming connection on our listener socket.
350 *
c8407295
AJ
351 * \retval Comm::OK success. details parameter filled.
352 * \retval Comm::NOMESSAGE attempted accept() but nothing useful came in.
4ee57cbe 353 * \retval Comm::COMM_ERROR an outright failure occured.
5b67dfa4 354 * Or if this client has too many connections already.
273f66c4 355 */
c8407295 356Comm::Flag
8bbb16e3 357Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
04f55905
AJ
358{
359 PROF_start(comm_accept);
e4f1fdae 360 ++statCounter.syscalls.sock.accepts;
04f55905
AJ
361 int sock;
362 struct addrinfo *gai = NULL;
4dd643d5 363 Ip::Address::InitAddrInfo(gai);
04f55905 364
1fc32b95 365 errcode = 0; // reset local errno copy.
a9870624 366 if ((sock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
1fc32b95 367 errcode = errno; // store last accept errno locally.
04f55905 368
4dd643d5 369 Ip::Address::FreeAddrInfo(gai);
04f55905
AJ
370
371 PROF_stop(comm_accept);
372
373 if (ignoreErrno(errno)) {
cbff89ba 374 debugs(50, 5, HERE << status() << ": " << xstrerror());
c8407295 375 return Comm::NOMESSAGE;
04f55905 376 } else if (ENFILE == errno || EMFILE == errno) {
cbff89ba 377 debugs(50, 3, HERE << status() << ": " << xstrerror());
4ee57cbe 378 return Comm::COMM_ERROR;
04f55905 379 } else {
e0236918 380 debugs(50, DBG_IMPORTANT, HERE << status() << ": " << xstrerror());
4ee57cbe 381 return Comm::COMM_ERROR;
04f55905
AJ
382 }
383 }
384
a9870624 385 Must(sock >= 0);
5b67dfa4
AJ
386 details->fd = sock;
387 details->remote = *gai;
04f55905 388
5511c78a 389 if ( Config.client_ip_max_connections >= 0) {
5b67dfa4
AJ
390 if (clientdbEstablished(details->remote, 0) > Config.client_ip_max_connections) {
391 debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
4dd643d5 392 Ip::Address::FreeAddrInfo(gai);
4ee57cbe 393 return Comm::COMM_ERROR;
5511c78a
AJ
394 }
395 }
396
903198a7 397 // lookup the local-end details of this new connection
4dd643d5
AJ
398 Ip::Address::InitAddrInfo(gai);
399 details->local.setEmpty();
e42281f9
AJ
400 if (getsockname(sock, gai->ai_addr, &gai->ai_addrlen) != 0) {
401 debugs(50, DBG_IMPORTANT, "ERROR: getsockname() failed to locate local-IP on " << details << ": " << xstrerror());
4dd643d5 402 Ip::Address::FreeAddrInfo(gai);
4ee57cbe 403 return Comm::COMM_ERROR;
e42281f9 404 }
5b67dfa4 405 details->local = *gai;
4dd643d5 406 Ip::Address::FreeAddrInfo(gai);
04f55905
AJ
407
408 /* fdstat update */
5b67dfa4 409 // XXX : these are not all HTTP requests. use a note about type and ip:port details->
903198a7 410 // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
04f55905
AJ
411 fd_open(sock, FD_SOCKET, "HTTP Request");
412
413 fdd_table[sock].close_file = NULL;
414 fdd_table[sock].close_line = 0;
415
416 fde *F = &fd_table[sock];
4dd643d5
AJ
417 details->remote.toStr(F->ipaddr,MAX_IPSTRLEN);
418 F->remote_port = details->remote.port();
5b67dfa4 419 F->local_addr = details->local;
4dd643d5 420 F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET;
04f55905 421
903198a7
AJ
422 // set socket flags
423 commSetCloseOnExec(sock);
04f55905
AJ
424 commSetNonBlocking(sock);
425
426 /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
40d34a62
AJ
427 F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
428
429 // Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
430 if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) {
431 // Failed.
4ee57cbe 432 return Comm::COMM_ERROR;
40d34a62 433 }
04f55905 434
83b62d3f
AJ
435#if USE_SQUID_EUI
436 if (Eui::TheConfig.euiLookup) {
68e47c3e
AJ
437 if (details->remote.isIPv4()) {
438 details->remoteEui48.lookup(details->remote);
439 } else if (details->remote.isIPv6()) {
440 details->remoteEui64.lookup(details->remote);
83b62d3f
AJ
441 }
442 }
443#endif
444
04f55905 445 PROF_stop(comm_accept);
c8407295 446 return Comm::OK;
04f55905 447}