TcpAcceptor.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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/TextException.h"
15 #include "client_db.h"
16 #include "comm/AcceptLimiter.h"
17 #include "comm/comm_internal.h"
18 #include "comm/Connection.h"
19 #include "comm/Loops.h"
20 #include "comm/TcpAcceptor.h"
21 #include "CommCalls.h"
22 #include "eui/Config.h"
23 #include "fd.h"
24 #include "fde.h"
25 #include "globals.h"
26 #include "ip/Intercept.h"
27 #include "ip/QosConfig.h"
28 #include "log/access_log.h"
29 #include "MasterXaction.h"
30 #include "SquidConfig.h"
31 #include "StatCounters.h"
32 
33 #include <cerrno>
34 #ifdef HAVE_NETINET_TCP_H
35 // required for accept_filter to build.
36 #include <netinet/tcp.h>
37 #endif
38 
40 
42  AsyncJob("Comm::TcpAcceptor"),
43  errcode(0),
44  theCallSub(aSub),
45  conn(newConn),
46  listenPort_()
47 {}
48 
50  AsyncJob("Comm::TcpAcceptor"),
51  errcode(0),
52  theCallSub(aSub),
53  conn(p->listenConn),
54  listenPort_(p)
55 {}
56 
57 void
59 {
60  debugs(5, 5, status() << " AsyncCall Subscription: " << aSub);
61  unsubscribe("subscription change");
62  theCallSub = aSub;
63 }
64 
65 void
66 Comm::TcpAcceptor::unsubscribe(const char *reason)
67 {
68  debugs(5, 5, status() << " AsyncCall Subscription " << theCallSub << " removed: " << reason);
69  theCallSub = NULL;
70 }
71 
72 void
74 {
75  if (listenPort_)
76  CodeContext::Reset(listenPort_);
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 
90 bool
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 == NULL) {
100  return AsyncJob::doneAll();
101  }
102 
103  // open FD with handlers...keep accepting.
104  return false;
105 }
106 
107 void
109 {
110  debugs(5,5, MYNAME);
111  unsubscribe("swanSong");
112  if (IsConnOpen(conn)) {
113  if (closer_ != NULL)
114  comm_remove_close_handler(conn->fd, closer_);
115  conn->close();
116  }
117 
118  conn = NULL;
121 }
122 
123 const char *
125 {
126  if (conn == NULL)
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 
150 void
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 
190 void
192 {
193  closer_ = NULL;
194  if (conn) {
195  conn->noteClosure();
196  conn = nullptr;
197  }
198  Must(done());
199 }
200 
210 void
211 Comm::TcpAcceptor::doAccept(int fd, void *data)
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 
232 bool
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 
248 void
250 {
252  CodeContext::Reset(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  CodeContext::Reset(listenPort_);
263 }
264 
265 void
267 {
268  /*
269  * We don't worry about running low on FDs here. Instead,
270  * doAccept() will use AcceptLimiter if we reach the limit
271  * there.
272  */
273 
274  /* Accept a new connection */
275  ConnectionPointer newConnDetails = new Connection();
276  const Comm::Flag flag = oldAccept(newConnDetails);
277 
278  if (flag == Comm::COMM_ERROR) {
279  // A non-recoverable error; notify the caller */
280  debugs(5, 5, "non-recoverable error:" << status() << " handler Subscription: " << theCallSub);
281  if (intendedForUserConnections())
282  logAcceptError(newConnDetails);
283  notify(flag, newConnDetails);
284  // XXX: not under async job call protections
285  mustStop("Listener socket closed");
286  return;
287  }
288 
289  if (flag == Comm::NOMESSAGE) {
290  /* register interest again */
291  debugs(5, 5, "try later: " << conn << " handler Subscription: " << theCallSub);
292  } else {
293  // TODO: When ALE, MasterXaction merge, use them or ClientConn instead.
294  CodeContext::Reset(newConnDetails);
295  debugs(5, 5, "Listener: " << conn <<
296  " accepted new connection " << newConnDetails <<
297  " handler Subscription: " << theCallSub);
298  notify(flag, newConnDetails);
299  CodeContext::Reset(listenPort_);
300  }
301 
302  SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
303 }
304 
305 void
307 {
308  Must(IsConnOpen(conn));
309  debugs(5, 2, "connection on " << conn);
310  acceptOne();
311 }
312 
313 void
314 Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &newConnDetails) const
315 {
316  // listener socket handlers just abandon the port with Comm::ERR_CLOSING
317  // it should only happen when this object is deleted...
318  if (flag == Comm::ERR_CLOSING) {
319  return;
320  }
321 
322  if (theCallSub != NULL) {
323  AsyncCall::Pointer call = theCallSub->callback();
324  CommAcceptCbParams &params = GetCommParams<CommAcceptCbParams>(call);
325  params.port = listenPort_;
326  params.fd = conn->fd;
327  params.conn = newConnDetails;
328  params.flag = flag;
329  params.xerrno = errcode;
330  ScheduleCallHere(call);
331  }
332 }
333 
345 {
346  ++statCounter.syscalls.sock.accepts;
347  int sock;
348  struct addrinfo *gai = NULL;
350 
351  errcode = 0; // reset local errno copy.
352  if ((sock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
353  errcode = errno; // store last accept errno locally.
354 
356 
357  if (ignoreErrno(errcode) || errcode == ECONNABORTED) {
358  debugs(50, 5, status() << ": " << xstrerr(errcode));
359  return Comm::NOMESSAGE;
360  } else if (errcode == ENFILE || errcode == EMFILE) {
361  debugs(50, 3, status() << ": " << xstrerr(errcode));
362  return Comm::COMM_ERROR;
363  } else {
364  debugs(50, DBG_IMPORTANT, "ERROR: failed to accept an incoming connection: " << xstrerr(errcode));
365  return Comm::COMM_ERROR;
366  }
367  }
368 
369  Must(sock >= 0);
371 
372  // Sync with Comm ASAP so that abandoned details can properly close().
373  // XXX : these are not all HTTP requests. use a note about type and ip:port details->
374  // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
375  fd_open(sock, FD_SOCKET, "HTTP Request");
376  details->fd = sock;
377  details->enterOrphanage();
378 
379  details->remote = *gai;
380 
381  // lookup the local-end details of this new connection
383  details->local.setEmpty();
384  if (getsockname(sock, gai->ai_addr, &gai->ai_addrlen) != 0) {
385  int xerrno = errno;
386  debugs(50, DBG_IMPORTANT, "ERROR: getsockname() failed to locate local-IP on " << details << ": " << xstrerr(xerrno));
388  return Comm::COMM_ERROR;
389  }
390  details->local = *gai;
392 
393  // Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
394  if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) {
395  debugs(50, DBG_IMPORTANT, "ERROR: NAT/TPROXY lookup failed to locate original IPs on " << details);
396  return Comm::NOMESSAGE;
397  }
398 
399 #if USE_SQUID_EUI
400  if (Eui::TheConfig.euiLookup) {
401  if (details->remote.isIPv4()) {
402  details->remoteEui48.lookup(details->remote);
403  } else if (details->remote.isIPv6()) {
404  details->remoteEui64.lookup(details->remote);
405  }
406  }
407 #endif
408 
410 
413  debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
414  return Comm::NOMESSAGE;
415  }
416  }
417 
418  fde *F = &fd_table[sock];
419  details->remote.toStr(F->ipaddr,MAX_IPSTRLEN);
420  F->remote_port = details->remote.port();
421  F->local_addr = details->local;
422  F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET;
423 
424  // set socket flags
425  commSetCloseOnExec(sock);
426  commSetNonBlocking(sock);
427  if (listenPort_)
428  Comm::ApplyTcpKeepAlive(sock, listenPort_->tcp_keepalive);
429 
430  /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
431  F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
432 
433  return Comm::OK;
434 }
435 
void accessLogLog(const AccessLogEntryPointer &, ACLChecklist *)
Definition: access_log.cc:147
#define ScheduleCallHere(call)
Definition: AsyncCall.h:164
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
#define COMM_TRANSPARENT
Definition: Connection.h:50
#define COMM_INTERCEPTION
Definition: Connection.h:51
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)
#define Must(condition)
Definition: TextException.h:71
int conn
the current server connection FD
Definition: Transport.cc:26
Ip::Address src_addr
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:97
virtual void swanSong()
Definition: AsyncJob.h:59
virtual const char * status() const
internal cleanup; do not call directly
Definition: AsyncJob.cc:167
static void Reset()
forgets the current context, setting it to nil/unknown
Definition: CodeContext.cc:75
AnyP::PortCfgPointer port
the configuration listening port this call relates to (may be nil)
Definition: CommCalls.h:105
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:88
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:90
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:87
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
void defer(const TcpAcceptor::Pointer &afd)
void removeDead(const TcpAcceptor::Pointer &afd)
static AcceptLimiter & Instance()
Eui::Eui64 remoteEui64
Definition: Connection.h:178
Ip::Address remote
Definition: Connection.h:147
Ip::Address local
Definition: Connection.h:144
void enterOrphanage()
close the still-open connection when its last reference is gone
Definition: Connection.h:88
nfmark_t nfConnmark
Definition: Connection.h:169
Eui::Eui48 remoteEui48
Definition: Connection.h:177
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
virtual void start()
called by AsyncStart; do not call directly
Definition: TcpAcceptor.cc:73
TcpAcceptor(const TcpAcceptor &)
void unsubscribe(const char *reason)
Definition: TcpAcceptor.cc:66
virtual const char * status() const
internal cleanup; do not call directly
Definition: TcpAcceptor.cc:124
void handleClosure(const CommCloseCbParams &io)
Definition: TcpAcceptor.cc:191
virtual void swanSong()
Definition: TcpAcceptor.cc:108
void subscribe(const Subscription::Pointer &aSub)
Definition: TcpAcceptor.cc:58
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:314
void logAcceptError(const ConnectionPointer &tcpClient) const
Definition: TcpAcceptor.cc:249
Comm::Flag oldAccept(Comm::ConnectionPointer &details)
Definition: TcpAcceptor.cc:344
virtual bool doneAll() const
whether positive goal has been reached
Definition: TcpAcceptor.cc:91
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
bool Lookup(const Comm::ConnectionPointer &newConn, const Comm::ConnectionPointer &listenConn)
Definition: Intercept.cc:380
Definition: MemBuf.h:24
virtual void append(const char *c, int sz)
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:531
int client_ip_max_connections
Definition: SquidConfig.h:541
struct StatCounters::@135 syscalls
struct StatCounters::@135::@140 sock
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:1038
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:921
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:950
void commSetCloseOnExec(int fd)
Definition: comm.cc:1099
int ignoreErrno(int ierrno)
Definition: comm.cc:1412
bool isOpen(const int fd)
Definition: comm.cc:85
#define MYNAME
Definition: Stream.h:238
#define DBG_IMPORTANT
Definition: Stream.h:41
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
#define DBG_CRITICAL
Definition: Stream.h:40
#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:145
#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
@ NOMESSAGE
Definition: Flag.h:18
@ ERR_CLOSING
Definition: Flag.h:25
@ 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:53
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:67
nfmark_t getNfConnmark(const Comm::ConnectionPointer &conn, const ConnectionDirection connDir)
Definition: QosConfig.cc:146
SSL Connection
Definition: Session.h:45
struct sockaddr * ai_addr
socklen_t ai_addrlen
#define NULL
Definition: types.h:166
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