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