TcpAcceptor.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 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 "eui/Config.h"
24#include "fd.h"
25#include "fde.h"
26#include "globals.h"
27#include "ip/Intercept.h"
28#include "ip/QosConfig.h"
29#include "log/access_log.h"
30#include "MasterXaction.h"
31#include "sbuf/Stream.h"
32#include "SquidConfig.h"
33#include "StatCounters.h"
34
35#include <cerrno>
36#ifdef HAVE_NETINET_TCP_H
37// required for accept_filter to build.
38#include <netinet/tcp.h>
39#endif
40
42
44 AsyncJob("Comm::TcpAcceptor"),
45 errcode(0),
46 theCallSub(aSub),
47 conn(newConn),
48 listenPort_()
49{}
50
52 AsyncJob("Comm::TcpAcceptor"),
53 errcode(0),
54 theCallSub(aSub),
55 conn(p->listenConn),
56 listenPort_(p)
57{}
58
59void
61{
62 debugs(5, 5, status() << " AsyncCall Subscription: " << aSub);
63 unsubscribe("subscription change");
64 theCallSub = aSub;
65}
66
67void
69{
70 debugs(5, 5, status() << " AsyncCall Subscription " << theCallSub << " removed: " << reason);
71 theCallSub = nullptr;
72}
73
74void
76{
77 debugs(5, 5, status() << " AsyncCall Subscription: " << theCallSub);
78
80
81 setListen();
82
83 conn->noteStart();
84
85 // if no error so far start accepting connections.
86 if (errcode == 0)
87 SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
88}
89
90bool
92{
93 // stop when FD is closed
94 if (!IsConnOpen(conn)) {
95 return AsyncJob::doneAll();
96 }
97
98 // stop when handlers are gone
99 if (theCallSub == nullptr) {
100 return AsyncJob::doneAll();
101 }
102
103 // open FD with handlers...keep accepting.
104 return false;
105}
106
107void
109{
110 debugs(5,5, MYNAME);
111 unsubscribe("swanSong");
112 if (IsConnOpen(conn)) {
113 if (closer_ != nullptr)
114 comm_remove_close_handler(conn->fd, closer_);
115 conn->close();
116 }
117
118 conn = nullptr;
121}
122
123const char *
125{
126 if (conn == nullptr)
127 return "[nil connection]";
128
129 static char ipbuf[MAX_IPSTRLEN] = {'\0'};
130 if (ipbuf[0] == '\0')
131 conn->local.toHostStr(ipbuf, MAX_IPSTRLEN);
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 (listen(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 (setsockopt(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 (setsockopt(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, 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{
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 struct addrinfo *gai = nullptr;
350
351 errcode = 0; // reset local errno copy.
352 const auto rawSock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen);
353 if (rawSock < 0) {
354 errcode = errno; // store last accept errno locally.
355
357
358 if (ignoreErrno(errcode) || errcode == ECONNABORTED) {
359 debugs(50, 5, status() << ": " << xstrerr(errcode));
360 return false;
361 } else {
362 throw TextException(ToSBuf("Failed to accept an incoming connection: ", xstrerr(errcode)), Here());
363 }
364 }
365
367
368 // Sync with Comm ASAP so that abandoned details can properly close().
369 // XXX : these are not all HTTP requests. use a note about type and ip:port details->
370 // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
371 const auto sock = rawSock;
372 fd_open(sock, FD_SOCKET, "HTTP Request");
373 details->fd = sock;
374 details->enterOrphanage();
375
376 details->remote = *gai;
377
378 // lookup the local-end details of this new connection
380 details->local.setEmpty();
381 if (getsockname(sock, gai->ai_addr, &gai->ai_addrlen) != 0) {
382 int xerrno = errno;
384 throw TextException(ToSBuf("getsockname() failed to locate local-IP on ", details, ": ", xstrerr(xerrno)), Here());
385 }
386 details->local = *gai;
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 *)
Definition: access_log.cc:136
#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.
Definition: AsyncJobCalls.h:69
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
Definition: CodeContext.h:116
#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
Definition: stub_libtime.cc:20
class SquidConfig Config
Definition: SquidConfig.cc:12
StatCounters statCounter
Definition: StatCounters.cc:12
CBDATA_NAMESPACED_CLASS_INIT(Comm, TcpAcceptor)
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define Must(condition)
Definition: TextException.h:75
int conn
the current server connection FD
Definition: Transport.cc:26
Ip::Address src_addr
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:180
bool isOpen() const
Definition: Connection.h:101
Ip::Address remote
Definition: Connection.h:149
Ip::Address local
Definition: Connection.h:146
void enterOrphanage()
close the still-open connection when its last reference is gone
Definition: Connection.h:90
nfmark_t nfConnmark
Definition: Connection.h:171
Eui::Eui48 remoteEui48
Definition: Connection.h:179
static bool okToAccept()
Definition: TcpAcceptor.cc:233
static void doAccept(int fd, void *data)
Method callback for whenever an FD is ready to accept a client connection.
Definition: TcpAcceptor.cc:211
void start() override
called by AsyncStart; do not call directly
Definition: TcpAcceptor.cc:75
TcpAcceptor(const TcpAcceptor &)
void unsubscribe(const char *reason)
Definition: TcpAcceptor.cc:68
bool doneAll() const override
whether positive goal has been reached
Definition: TcpAcceptor.cc:91
void handleClosure(const CommCloseCbParams &io)
Definition: TcpAcceptor.cc:191
void swanSong() override
Definition: TcpAcceptor.cc:108
const char * status() const override
internal cleanup; do not call directly
Definition: TcpAcceptor.cc:124
bool acceptInto(Comm::ConnectionPointer &)
Definition: TcpAcceptor.cc:345
void subscribe(const Subscription::Pointer &aSub)
Definition: TcpAcceptor.cc:60
void notify(const Comm::Flag flag, const Comm::ConnectionPointer &details) const
Call the subscribed callback handler with details about a new connection.
Definition: TcpAcceptor.cc:321
void logAcceptError(const ConnectionPointer &tcpClient) const
Definition: TcpAcceptor.cc:249
static std::ostream & Extra(std::ostream &)
Definition: debug.cc:1313
bool lookup(const Ip::Address &c)
Definition: Eui48.cc:135
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:792
void setEmpty()
Fast reset of the stored content to what would be after default constructor.
Definition: Address.cc:184
static void InitAddr(struct addrinfo *&ai)
Definition: Address.cc:668
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:686
bool isIPv4() const
Definition: Address.cc:158
bool isIPv6() const
Definition: Address.cc:164
unsigned short port() const
Definition: Address.cc:778
Definition: MemBuf.h:24
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
Definition: SquidConfig.h:515
int client_ip_max_connections
Definition: SquidConfig.h:525
struct StatCounters::@130 syscalls
struct StatCounters::@130::@135 sock
an std::runtime_error with thrower location info
Definition: TextException.h:21
Definition: fde.h:52
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:14
int commSetNonBlocking(int fd)
Definition: comm.cc:1066
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:949
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:978
void commSetCloseOnExec(int fd)
Definition: comm.cc:1127
int ignoreErrno(int ierrno)
Definition: comm.cc:1440
bool isOpen(const int fd)
Definition: comm.cc:88
#define MYNAME
Definition: Stream.h:236
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#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:265
#define fd_table
Definition: fde.h:189
int Squid_MaxFD
int RESERVED_FD
int incoming_sockets_accepted
Intercept Interceptor
Definition: Intercept.h:130
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
Abstraction layer for TCP, UDP, TLS, UDS and filedescriptor sockets.
Definition: AcceptLimiter.h:17
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:51
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:223
EuiConfig TheConfig
Definition: Config.cc:12
@ dirAccepted
accepted (from a client by Squid)
Definition: QosConfig.h:69
nfmark_t getNfConnmark(const Comm::ConnectionPointer &conn, const ConnectionDirection connDir)
Definition: QosConfig.cc:146
SSL Connection
Definition: Session.h:45
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
const char * xstrerr(int error)
Definition: xstrerror.cc:83
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors