Squid Web Cache master
Loading...
Searching...
No Matches
TcpAcceptor.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9/* DEBUG: section 05 Listener Socket Handler */
10
11#include "squid.h"
12#include "acl/FilledChecklist.h"
13#include "anyp/PortCfg.h"
14#include "base/CodeContext.h"
15#include "base/TextException.h"
16#include "client_db.h"
17#include "comm/AcceptLimiter.h"
18#include "comm/comm_internal.h"
19#include "comm/Connection.h"
20#include "comm/Loops.h"
21#include "comm/TcpAcceptor.h"
22#include "CommCalls.h"
23#include "compat/socket.h"
24#include "eui/Config.h"
25#include "fd.h"
26#include "fde.h"
27#include "globals.h"
28#include "ip/Intercept.h"
29#include "ip/QosConfig.h"
30#include "log/access_log.h"
31#include "MasterXaction.h"
32#include "sbuf/Stream.h"
33#include "SquidConfig.h"
34#include "StatCounters.h"
35
36#include <cerrno>
37#ifdef HAVE_NETINET_TCP_H
38// required for accept_filter to build.
39#include <netinet/tcp.h>
40#endif
41
43
45 AsyncJob("Comm::TcpAcceptor"),
46 errcode(0),
47 theCallSub(aSub),
48 conn(newConn),
49 listenPort_()
50{}
51
53 AsyncJob("Comm::TcpAcceptor"),
54 errcode(0),
55 theCallSub(aSub),
56 conn(p->listenConn),
57 listenPort_(p)
58{}
59
60void
62{
63 debugs(5, 5, status() << " AsyncCall Subscription: " << aSub);
64 unsubscribe("subscription change");
65 theCallSub = aSub;
66}
67
68void
70{
71 debugs(5, 5, status() << " AsyncCall Subscription " << theCallSub << " removed: " << reason);
72 theCallSub = nullptr;
73}
74
75void
77{
78 debugs(5, 5, status() << " AsyncCall Subscription: " << theCallSub);
79
80 Must(IsConnOpen(conn));
81
82 setListen();
83
84 conn->noteStart();
85
86 // if no error so far start accepting connections.
87 if (errcode == 0)
88 SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
89}
90
91bool
93{
94 // stop when FD is closed
95 if (!IsConnOpen(conn)) {
96 return AsyncJob::doneAll();
97 }
98
99 // stop when handlers are gone
100 if (theCallSub == nullptr) {
101 return AsyncJob::doneAll();
102 }
103
104 // open FD with handlers...keep accepting.
105 return false;
106}
107
108void
110{
111 debugs(5,5, MYNAME);
112 unsubscribe("swanSong");
113 if (IsConnOpen(conn)) {
114 if (closer_ != nullptr)
115 comm_remove_close_handler(conn->fd, closer_);
116 conn->close();
117 }
118
119 conn = nullptr;
122}
123
124const char *
126{
127 if (conn == nullptr)
128 return "[nil connection]";
129
130 char ipbuf[MAX_IPSTRLEN];
131 conn->local.toHostStr(ipbuf, MAX_IPSTRLEN); // XXX: report port using toUrl()
132
133 static MemBuf buf;
134 buf.reset();
135 buf.appendf(" FD %d, %s",conn->fd, ipbuf);
136
137 const char *jobStatus = AsyncJob::status();
138 buf.append(jobStatus, strlen(jobStatus));
139
140 return buf.content();
141}
142
150void
152{
153 errcode = errno = 0;
154 if (xlisten(conn->fd, Squid_MaxFD >> 2) < 0) {
155 errcode = errno;
156 debugs(50, DBG_CRITICAL, "ERROR: listen(..., " << (Squid_MaxFD >> 2) << ") system call failed: " << xstrerr(errcode));
157 return;
158 }
159
160 if (Config.accept_filter && strcmp(Config.accept_filter, "none") != 0) {
161#ifdef SO_ACCEPTFILTER
162 struct accept_filter_arg afa;
163 bzero(&afa, sizeof(afa));
164 debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on " << conn);
165 xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name));
166 if (xsetsockopt(conn->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0) {
167 int xerrno = errno;
168 debugs(5, DBG_CRITICAL, "WARNING: SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerr(xerrno));
169 }
170#elif defined(TCP_DEFER_ACCEPT)
171 int seconds = 30;
172 if (strncmp(Config.accept_filter, "data=", 5) == 0)
173 seconds = atoi(Config.accept_filter + 5);
174 if (xsetsockopt(conn->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds)) < 0) {
175 int xerrno = errno;
176 debugs(5, DBG_CRITICAL, "WARNING: TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerr(xerrno));
177 }
178#else
179 debugs(5, DBG_CRITICAL, "WARNING: accept_filter not supported on your OS");
180#endif
181 }
182
184 closer_ = JobCallback(5, 4, Dialer, this, Comm::TcpAcceptor::handleClosure);
185 comm_add_close_handler(conn->fd, closer_);
186}
187
190void
192{
193 closer_ = nullptr;
194 if (conn) {
195 conn->noteClosure();
196 conn = nullptr;
197 }
198 Must(done());
199}
200
210void
212{
213 try {
214 debugs(5, 2, "New connection on FD " << fd);
215
216 Must(isOpen(fd));
217 TcpAcceptor *afd = static_cast<TcpAcceptor*>(data);
218
219 if (!okToAccept()) {
221 } else {
222 afd->acceptNext();
223 }
224
225 } catch (const std::exception &e) {
226 fatalf("FATAL: error while accepting new client connection: %s\n", e.what());
227 } catch (...) {
228 fatal("FATAL: error while accepting new client connection: [unknown]\n");
229 }
230}
231
232bool
234{
235 static time_t last_warn = 0;
236
237 if (fdNFree() >= RESERVED_FD)
238 return true;
239
240 if (last_warn + 15 < squid_curtime) {
241 debugs(5, DBG_CRITICAL, "WARNING: Your cache is running out of filedescriptors");
242 last_warn = squid_curtime;
243 }
244
245 return false;
246}
247
248void
250{
252 CallBack(al, [&] {
253 al->tcpClient = tcpClient;
254 al->url = "error:accept-client-connection";
256 ACLFilledChecklist ch(nullptr, nullptr);
257 ch.src_addr = tcpClient->remote;
258 ch.my_addr = tcpClient->local;
259 ch.al = al;
260 accessLogLog(al, &ch);
261 });
262}
263
264void
266{
267 /*
268 * We don't worry about running low on FDs here. Instead,
269 * doAccept() will use AcceptLimiter if we reach the limit
270 * there.
271 */
272
273 /* Accept a new connection */
274 ConnectionPointer newConnDetails = new Connection();
275 try {
276 if (acceptInto(newConnDetails)) {
277 Assure(newConnDetails->isOpen());
278 CallBack(newConnDetails, [&] {
279 debugs(5, 5, "Listener: " << conn <<
280 " accepted new connection " << newConnDetails <<
281 " handler Subscription: " << theCallSub);
282 notify(Comm::OK, newConnDetails);
283 });
284 } else {
285 debugs(5, 5, "try later: " << conn << " handler Subscription: " << theCallSub);
286 newConnDetails->close(); // paranoid manual closure (and may already be closed)
287 }
288
289 SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
290 return;
291 } catch (...) {
292 const auto debugLevel = intendedForUserConnections() ? DBG_CRITICAL : 3;
293 debugs(5, debugLevel, "ERROR: Stopped accepting connections:" <<
294 Debug::Extra << "error: " << CurrentException);
295 }
296
297 if (intendedForUserConnections())
298 logAcceptError(newConnDetails);
299
300 // do not expose subscribers to a "usable" descriptor of a failed connection
301 newConnDetails->close(); // may already be closed
302
303 CallBack(newConnDetails, [&] {
304 notify(Comm::COMM_ERROR, newConnDetails);
305 });
306
307 // XXX: Not under AsyncJob call protections but, if placed there, may cause
308 // problems like making the corresponding HttpSockets entry (if any) stale.
309 mustStop("unrecoverable accept failure");
310}
311
312void
314{
315 Must(IsConnOpen(conn));
316 debugs(5, 2, "connection on " << conn);
317 acceptOne();
318}
319
320void
321Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &newConnDetails) const
322{
323 // listener socket handlers just abandon the port with Comm::ERR_CLOSING
324 // it should only happen when this object is deleted...
325 if (flag == Comm::ERR_CLOSING) {
326 return;
327 }
328
329 if (theCallSub != nullptr) {
330 AsyncCall::Pointer call = theCallSub->callback();
331 CommAcceptCbParams &params = GetCommParams<CommAcceptCbParams>(call);
332 params.port = listenPort_;
333 params.fd = conn->fd;
334 params.conn = newConnDetails;
335 params.flag = flag;
336 params.xerrno = errcode;
337 ScheduleCallHere(call);
338 }
339}
340
344bool
346{
348
349 errcode = 0; // reset local errno copy.
350 struct sockaddr_storage remoteAddress = {};
351 socklen_t remoteAddressSize = sizeof(remoteAddress);
352 const auto rawSock = xaccept(conn->fd, reinterpret_cast<struct sockaddr *>(&remoteAddress), &remoteAddressSize);
353 if (rawSock < 0) {
354 errcode = errno; // store last accept errno locally.
355 if (ignoreErrno(errcode) || errcode == ECONNABORTED) {
356 debugs(50, 5, status() << ": " << xstrerr(errcode));
357 return false;
358 } else {
359 throw TextException(ToSBuf("Failed to accept an incoming connection: ", xstrerr(errcode)), Here());
360 }
361 }
362
364
365 // Sync with Comm ASAP so that abandoned details can properly close().
366 // XXX : these are not all HTTP requests. use a note about type and ip:port details->
367 // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
368 const auto sock = rawSock;
369 fd_open(sock, FD_SOCKET, "HTTP Request");
370 details->fd = sock;
371 details->enterOrphanage();
372
373 Assure(remoteAddressSize <= socklen_t(sizeof(remoteAddress)));
374 details->remote = remoteAddress;
375
376 // lookup the local-end details of this new connection
377 struct sockaddr_storage localAddress = {};
378 socklen_t localAddressSize = sizeof(localAddress);
379 if (xgetsockname(sock, reinterpret_cast<struct sockaddr *>(&localAddress), &localAddressSize) != 0) {
380 int xerrno = errno;
381 debugs(50, DBG_IMPORTANT, "ERROR: Closing accepted TCP connection after failing to obtain its local IP address" <<
382 Debug::Extra << "accepted connection: " << details <<
383 Debug::Extra << "getsockname(2) error: " << xstrerr(xerrno));
384 return false;
385 }
386 Assure(localAddressSize <= socklen_t(sizeof(localAddress)));
387 details->local = localAddress;
388
389 if (conn->flags & COMM_TRANSPARENT) { // the real client/dest IP address must be already available via getsockname()
390 details->flags |= COMM_TRANSPARENT;
391 if (!Ip::Interceptor.TransparentActive()) {
392 debugs(50, DBG_IMPORTANT, "ERROR: Cannot use transparent " << details << " because TPROXY mode became inactive");
393 // TODO: consider throwing instead
394 return false;
395 }
396 } else if (conn->flags & COMM_INTERCEPTION) { // request the real client/dest IP address from NAT
397 details->flags |= COMM_INTERCEPTION;
398 if (!Ip::Interceptor.LookupNat(*details)) {
399 debugs(50, DBG_IMPORTANT, "ERROR: NAT lookup failed to locate original IPs on " << details);
400 return false;
401 }
402 }
403
404#if USE_SQUID_EUI
405 if (Eui::TheConfig.euiLookup) {
406 if (details->remote.isIPv4()) {
407 details->remoteEui48.lookup(details->remote);
408 } else if (details->remote.isIPv6()) {
409 details->remoteEui64.lookup(details->remote);
410 }
411 }
412#endif
413
415
418 debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
419 return false;
420 }
421 }
422
423 fde *F = &fd_table[sock];
424 details->remote.toStr(F->ipaddr,MAX_IPSTRLEN);
425 F->remote_port = details->remote.port();
426 F->local_addr = details->local;
427 F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET;
428
429 // set socket flags
430 commSetCloseOnExec(sock);
431 commSetNonBlocking(sock);
432 if (listenPort_)
433 Comm::ApplyTcpKeepAlive(sock, listenPort_->tcp_keepalive);
434
435 /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
436 F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
437
438 return true;
439}
440
void accessLogLog(const AccessLogEntryPointer &, ACLChecklist *)
#define Assure(condition)
Definition Assure.h:35
#define ScheduleCallHere(call)
Definition AsyncCall.h:166
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
#define COMM_TRANSPARENT
Definition Connection.h:50
#define COMM_INTERCEPTION
Definition Connection.h:51
#define Here()
source code location of the caller
Definition Here.h:15
time_t squid_curtime
class SquidConfig Config
StatCounters statCounter
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define Must(condition)
#define CBDATA_NAMESPACED_CLASS_INIT(namespace, type)
Definition cbdata.h:333
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
Comm::ConnectionPointer tcpClient
TCP/IP level details about the client connection.
void setVirginUrlForMissingRequest(const SBuf &vu)
Remember Client URI (or equivalent) when there is no HttpRequest.
virtual bool doneAll() const
whether positive goal has been reached
Definition AsyncJob.cc:112
virtual void swanSong()
Definition AsyncJob.h:61
virtual const char * status() const
internal cleanup; do not call directly
Definition AsyncJob.cc:182
AnyP::PortCfgPointer port
the configuration listening port this call relates to (may be nil)
Definition CommCalls.h:100
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition CommCalls.h:83
int fd
FD which the call was about. Set by the async call creator.
Definition CommCalls.h:85
Comm::Flag flag
comm layer result status.
Definition CommCalls.h:82
Comm::ConnectionPointer conn
Definition CommCalls.h:80
void defer(const TcpAcceptor::Pointer &afd)
void removeDead(const TcpAcceptor::Pointer &afd)
static AcceptLimiter & Instance()
Eui::Eui64 remoteEui64
Definition Connection.h:181
bool isOpen() const
Definition Connection.h:101
Ip::Address remote
Definition Connection.h:152
Ip::Address local
Definition Connection.h:149
void enterOrphanage()
close the still-open connection when its last reference is gone
Definition Connection.h:90
nfmark_t nfConnmark
Definition Connection.h:174
Eui::Eui48 remoteEui48
Definition Connection.h:180
static bool okToAccept()
static void doAccept(int fd, void *data)
Method callback for whenever an FD is ready to accept a client connection.
void start() override
called by AsyncStart; do not call directly
TcpAcceptor(const TcpAcceptor &)
void unsubscribe(const char *reason)
bool doneAll() const override
whether positive goal has been reached
void handleClosure(const CommCloseCbParams &io)
void swanSong() override
const char * status() const override
internal cleanup; do not call directly
bool acceptInto(Comm::ConnectionPointer &)
void subscribe(const Subscription::Pointer &aSub)
void notify(const Comm::Flag flag, const Comm::ConnectionPointer &details) const
Call the subscribed callback handler with details about a new connection.
void logAcceptError(const ConnectionPointer &tcpClient) const
static std::ostream & Extra(std::ostream &)
Definition debug.cc:1316
bool lookup(const Ip::Address &c)
Definition Eui48.cc:162
bool lookup(const Ip::Address &c)
Definition Eui64.cc:47
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition Address.cc:804
bool isIPv4() const
Definition Address.cc:178
bool isIPv6() const
Definition Address.cc:184
unsigned short port() const
Definition Address.cc:790
void append(const char *c, int sz) override
Definition MemBuf.cc:209
char * content()
start of the added data
Definition MemBuf.h:41
void reset()
Definition MemBuf.cc:129
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition Packable.h:61
char * accept_filter
int client_ip_max_connections
struct StatCounters::@112 syscalls
struct StatCounters::@112::@117 sock
an std::runtime_error with thrower location info
Definition fde.h:52
struct fde::_fde_flags flags
unsigned short remote_port
Definition fde.h:106
char ipaddr[MAX_IPSTRLEN]
Definition fde.h:114
Ip::Address local_addr
Definition fde.h:108
int sock_family
Definition fde.h:113
int clientdbEstablished(const Ip::Address &addr, int delta)
Definition client_db.cc:182
void fd_open(const int fd, unsigned int, const char *description)
Definition minimal.cc:15
int commSetNonBlocking(int fd)
Definition comm.cc:1044
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition comm.cc:942
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition comm.cc:971
void commSetCloseOnExec(int fd)
Definition comm.cc:1105
int ignoreErrno(int ierrno)
Definition comm.cc:1407
bool isOpen(const int fd)
Definition comm.cc:91
#define MYNAME
Definition Stream.h:219
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
#define COMM_SELECT_READ
Definition defines.h:24
@ FD_SOCKET
Definition enums.h:16
void fatal(const char *message)
Definition fatal.cc:28
void fatalf(const char *fmt,...)
Definition fatal.cc:68
int fdNFree(void)
Definition fd.cc:262
#define fd_table
Definition fde.h:189
int Squid_MaxFD
int RESERVED_FD
int incoming_sockets_accepted
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition forward.h:25
Abstraction layer for TCP, UDP, TLS, UDS and filedescriptor sockets.
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition Connection.cc:27
Flag
Definition Flag.h:15
@ OK
Definition Flag.h:16
@ ERR_CLOSING
Definition Flag.h:24
@ COMM_ERROR
Definition Flag.h:17
void ApplyTcpKeepAlive(int fd, const TcpKeepAlive &)
apply configured TCP keep-alive settings to the given FD socket
Definition Tcp.cc:49
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
EuiConfig TheConfig
Definition Config.cc:12
@ dirAccepted
accepted (from a client by Squid)
Definition QosConfig.h:71
nfmark_t getNfConnmark(const Comm::ConnectionPointer &conn, const ConnectionDirection connDir)
Definition QosConfig.cc:151
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition Stream.h:63
int xgetsockname(int socketFd, struct sockaddr *sa, socklen_t *saLength)
POSIX getsockname(2) equivalent.
Definition socket.h:80
int xaccept(int socketFd, struct sockaddr *sa, socklen_t *saLength)
POSIX accept(2) equivalent.
Definition socket.h:62
int xsetsockopt(int socketFd, int level, int option, const void *value, socklen_t valueLength)
POSIX setsockopt(2) equivalent.
Definition socket.h:122
int xlisten(int socketFd, int backlog)
POSIX listen(2) equivalent.
Definition socket.h:86
bool transparent
Definition fde.h:129
int socklen_t
Definition types.h:137
const char * xstrerr(int error)
Definition xstrerror.cc:83
char * xstrncpy(char *dst, const char *src, size_t n)
Definition xstring.cc:37