TcpAcceptor.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2020 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 "profiler/Profiler.h"
31 #include "SquidConfig.h"
32 #include "SquidTime.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 
59 void
61 {
62  debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << aSub);
63  unsubscribe("subscription change");
64  theCallSub = aSub;
65 }
66 
67 void
68 Comm::TcpAcceptor::unsubscribe(const char *reason)
69 {
70  debugs(5, 5, HERE << status() << " AsyncCall Subscription " << theCallSub << " removed: " << reason);
71  theCallSub = NULL;
72 }
73 
74 void
76 {
77  if (listenPort_)
78  CodeContext::Reset(listenPort_);
79  debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << theCallSub);
80 
82 
83  setListen();
84 
85  conn->noteStart();
86 
87  // if no error so far start accepting connections.
88  if (errcode == 0)
89  SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
90 }
91 
92 bool
94 {
95  // stop when FD is closed
96  if (!IsConnOpen(conn)) {
97  return AsyncJob::doneAll();
98  }
99 
100  // stop when handlers are gone
101  if (theCallSub == NULL) {
102  return AsyncJob::doneAll();
103  }
104 
105  // open FD with handlers...keep accepting.
106  return false;
107 }
108 
109 void
111 {
112  debugs(5,5, HERE);
113  unsubscribe("swanSong");
114  if (IsConnOpen(conn)) {
115  if (closer_ != NULL)
116  comm_remove_close_handler(conn->fd, closer_);
117  conn->close();
118  }
119 
120  conn = NULL;
123 }
124 
125 const char *
127 {
128  if (conn == NULL)
129  return "[nil connection]";
130 
131  static char ipbuf[MAX_IPSTRLEN] = {'\0'};
132  if (ipbuf[0] == '\0')
133  conn->local.toHostStr(ipbuf, MAX_IPSTRLEN);
134 
135  static MemBuf buf;
136  buf.reset();
137  buf.appendf(" FD %d, %s",conn->fd, ipbuf);
138 
139  const char *jobStatus = AsyncJob::status();
140  buf.append(jobStatus, strlen(jobStatus));
141 
142  return buf.content();
143 }
144 
152 void
154 {
155  errcode = errno = 0;
156  if (listen(conn->fd, Squid_MaxFD >> 2) < 0) {
157  errcode = errno;
158  debugs(50, DBG_CRITICAL, "ERROR: listen(..., " << (Squid_MaxFD >> 2) << ") system call failed: " << xstrerr(errcode));
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));
166  debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on " << conn);
167  xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name));
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  }
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);
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  }
180 #else
181  debugs(5, DBG_CRITICAL, "WARNING: accept_filter not supported on your OS");
182 #endif
183  }
184 
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.
190  if (conn->tos)
192 #if SO_MARK
193  if (conn->nfmark)
194  Ip::Qos::setSockNfmark(conn, conn->nfmark);
195 #endif
196 #endif
197 
199  closer_ = JobCallback(5, 4, Dialer, this, Comm::TcpAcceptor::handleClosure);
200  comm_add_close_handler(conn->fd, closer_);
201 }
202 
205 void
207 {
208  closer_ = NULL;
209  conn = NULL;
210  Must(done());
211 }
212 
222 void
224 {
225  try {
226  debugs(5, 2, HERE << "New connection on FD " << fd);
227 
228  Must(isOpen(fd));
229  TcpAcceptor *afd = static_cast<TcpAcceptor*>(data);
230 
231  if (!okToAccept()) {
233  } else {
234  afd->acceptNext();
235  }
236 
237  } catch (const std::exception &e) {
238  fatalf("FATAL: error while accepting new client connection: %s\n", e.what());
239  } catch (...) {
240  fatal("FATAL: error while accepting new client connection: [unknown]\n");
241  }
242 }
243 
244 bool
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 
260 void
262 {
264  CodeContext::Reset(al);
265  al->tcpClient = tcpClient;
266  al->url = "error:accept-client-connection";
268  ACLFilledChecklist ch(nullptr, nullptr, nullptr);
269  ch.src_addr = tcpClient->remote;
270  ch.my_addr = tcpClient->local;
271  ch.al = al;
272  accessLogLog(al, &ch);
273 
274  CodeContext::Reset(listenPort_);
275 }
276 
277 void
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 */
287  ConnectionPointer newConnDetails = new Connection();
288  const Comm::Flag flag = oldAccept(newConnDetails);
289 
290  if (flag == Comm::COMM_ERROR) {
291  // A non-recoverable error; notify the caller */
292  debugs(5, 5, HERE << "non-recoverable error:" << status() << " handler Subscription: " << theCallSub);
293  if (intendedForUserConnections())
294  logAcceptError(newConnDetails);
295  notify(flag, newConnDetails);
296  mustStop("Listener socket closed");
297  return;
298  }
299 
300  if (flag == Comm::NOMESSAGE) {
301  /* register interest again */
302  debugs(5, 5, "try later: " << conn << " handler Subscription: " << theCallSub);
303  } else {
304  // TODO: When ALE, MasterXaction merge, use them or ClientConn instead.
305  CodeContext::Reset(newConnDetails);
306  debugs(5, 5, "Listener: " << conn <<
307  " accepted new connection " << newConnDetails <<
308  " handler Subscription: " << theCallSub);
309  notify(flag, newConnDetails);
310  CodeContext::Reset(listenPort_);
311  }
312 
313  SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
314 }
315 
316 void
318 {
319  Must(IsConnOpen(conn));
320  debugs(5, 2, HERE << "connection on " << conn);
321  acceptOne();
322 }
323 
324 void
325 Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &newConnDetails) const
326 {
327  // listener socket handlers just abandon the port with Comm::ERR_CLOSING
328  // it should only happen when this object is deleted...
329  if (flag == Comm::ERR_CLOSING) {
330  return;
331  }
332 
333  if (theCallSub != NULL) {
334  AsyncCall::Pointer call = theCallSub->callback();
335  CommAcceptCbParams &params = GetCommParams<CommAcceptCbParams>(call);
337  params.xaction->squidPort = listenPort_;
338  params.fd = conn->fd;
339  params.conn = params.xaction->tcpClient = newConnDetails;
340  params.flag = flag;
341  params.xerrno = errcode;
342  ScheduleCallHere(call);
343  }
344 }
345 
357 {
358  PROF_start(comm_accept);
359  ++statCounter.syscalls.sock.accepts;
360  int sock;
361  struct addrinfo *gai = NULL;
363 
364  errcode = 0; // reset local errno copy.
365  if ((sock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
366  errcode = errno; // store last accept errno locally.
367 
369 
370  PROF_stop(comm_accept);
371 
372  if (ignoreErrno(errcode) || errcode == ECONNABORTED) {
373  debugs(50, 5, status() << ": " << xstrerr(errcode));
374  return Comm::NOMESSAGE;
375  } else if (errcode == ENFILE || errcode == EMFILE) {
376  debugs(50, 3, status() << ": " << xstrerr(errcode));
377  return Comm::COMM_ERROR;
378  } else {
379  debugs(50, DBG_IMPORTANT, "ERROR: failed to accept an incoming connection: " << xstrerr(errcode));
380  return Comm::COMM_ERROR;
381  }
382  }
383 
384  Must(sock >= 0);
385  details->fd = sock;
386  details->remote = *gai;
387 
388  // lookup the local-end details of this new connection
390  details->local.setEmpty();
391  if (getsockname(sock, gai->ai_addr, &gai->ai_addrlen) != 0) {
392  int xerrno = errno;
393  debugs(50, DBG_IMPORTANT, "ERROR: getsockname() failed to locate local-IP on " << details << ": " << xstrerr(xerrno));
395  PROF_stop(comm_accept);
396  return Comm::COMM_ERROR;
397  }
398  details->local = *gai;
400 
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)) {
403  debugs(50, DBG_IMPORTANT, "ERROR: NAT/TPROXY lookup failed to locate original IPs on " << details);
404  // Failed.
405  PROF_stop(comm_accept);
406  return Comm::COMM_ERROR;
407  }
408 
409 #if USE_SQUID_EUI
410  if (Eui::TheConfig.euiLookup) {
411  if (details->remote.isIPv4()) {
412  details->remoteEui48.lookup(details->remote);
413  } else if (details->remote.isIPv6()) {
414  details->remoteEui64.lookup(details->remote);
415  }
416  }
417 #endif
418 
420 
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 
447  PROF_stop(comm_accept);
448  return Comm::OK;
449 }
450 
@ initClient
HTTP or FTP client.
void fatal(const char *message)
Definition: fatal.cc:28
const char * xstrerr(int error)
Definition: xstrerror.cc:83
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:983
AnyP::PortCfgPointer squidPort
the listening port which originated this transaction
Definition: MasterXaction.h:50
virtual void start()
called by AsyncStart; do not call directly
Definition: TcpAcceptor.cc:75
struct StatCounters::@137::@142 sock
@ NOMESSAGE
Definition: Flag.h:18
char * accept_filter
Definition: SquidConfig.h:529
Ip::Address src_addr
int setSockNfmark(const Comm::ConnectionPointer &conn, nfmark_t mark)
Definition: QosConfig.cc:586
virtual void swanSong()
Definition: AsyncJob.h:54
Eui::Eui64 remoteEui64
Definition: Connection.h:190
#define ScheduleCallHere(call)
Definition: AsyncCall.h:166
void handleClosure(const CommCloseCbParams &io)
Definition: TcpAcceptor.cc:206
@ FD_SOCKET
Definition: enums.h:16
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
void setVirginUrlForMissingRequest(const SBuf &vu)
Remember Client URI (or equivalent) when there is no HttpRequest.
int clientdbEstablished(const Ip::Address &addr, int delta)
Definition: client_db.cc:183
int commSetNonBlocking(int fd)
Definition: comm.cc:1098
Abstraction layer for TCP, UDP, TLS, UDS and filedescriptor sockets.
Definition: AcceptLimiter.h:16
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:686
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:90
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:26
@ OK
Definition: Flag.h:16
#define DBG_CRITICAL
Definition: Debug.h:45
bool isIPv4() const
Definition: Address.cc:158
@ ERR_CLOSING
Definition: Flag.h:25
#define PROF_stop(probename)
Definition: Profiler.h:63
#define DBG_IMPORTANT
Definition: Debug.h:46
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:88
Definition: fde.h:51
socklen_t ai_addrlen
Comm::Flag oldAccept(Comm::ConnectionPointer &details)
Definition: TcpAcceptor.cc:356
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:802
bool lookup(const Ip::Address &c)
Definition: Eui48.cc:135
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:23
static void Reset()
forgets the current context, setting it to nil/unknown
Definition: CodeContext.cc:75
#define COMM_INTERCEPTION
Definition: Connection.h:51
struct sockaddr * ai_addr
#define COMM_TRANSPARENT
Definition: Connection.h:50
Comm::ConnectionPointer tcpClient
the client TCP connection which originated this transaction
Definition: MasterXaction.h:53
TcpAcceptor(const TcpAcceptor &)
virtual const char * status() const
internal cleanup; do not call directly
Definition: TcpAcceptor.cc:126
#define NULL
Definition: types.h:166
virtual bool doneAll() const
whether positive goal has been reached
Definition: AsyncJob.cc:96
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:128
struct StatCounters::@137 syscalls
unsigned short port() const
Definition: Address.cc:778
bool isIPv6() const
Definition: Address.cc:164
Definition: MemBuf.h:23
Ip::Address local
Definition: Connection.h:156
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:157
Comm::ConnectionPointer tcpClient
TCP/IP level details about the client connection.
void const char HLPCB void * data
Definition: stub_helper.cc:16
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
Ip::Address remote
Definition: Connection.h:159
int conn
the current server connection FD
Definition: Transport.cc:26
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
void commSetCloseOnExec(int fd)
Definition: comm.cc:1159
nfmark_t nfConnmark
Definition: Connection.h:181
void unsubscribe(const char *reason)
Definition: TcpAcceptor.cc:68
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:87
@ COMM_ERROR
Definition: Flag.h:17
EuiConfig TheConfig
Definition: Config.cc:12
#define COMM_SELECT_READ
Definition: defines.h:36
Eui::Eui48 remoteEui48
Definition: Connection.h:189
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
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:325
bool Lookup(const Comm::ConnectionPointer &newConn, const Comm::ConnectionPointer &listenConn)
Definition: Intercept.cc:389
time_t squid_curtime
Definition: stub_time.cc:17
void setEmpty()
Fast reset of the stored content to what would be after default constructor.
Definition: Address.cc:184
int Squid_MaxFD
static AcceptLimiter & Instance()
CBDATA_NAMESPACED_CLASS_INIT(Comm, TcpAcceptor)
virtual const char * status() const
internal cleanup; do not call directly
Definition: AsyncJob.cc:159
void defer(const TcpAcceptor::Pointer &afd)
Flag
Definition: Flag.h:15
static void doAccept(int fd, void *data)
Method callback for whenever an FD is ready to accept a client connection.
Definition: TcpAcceptor.cc:223
int ignoreErrno(int ierrno)
Definition: comm.cc:1507
#define fd_table
Definition: fde.h:189
Intercept Interceptor
Definition: Intercept.h:154
nfmark_t getNfConnmark(const Comm::ConnectionPointer &conn, const ConnectionDirection connDir)
Definition: QosConfig.cc:141
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:225
int fdNFree(void)
Definition: fd.cc:295
int RESERVED_FD
virtual void swanSong()
Definition: TcpAcceptor.cc:110
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
void fd_open(int fd, unsigned int type, const char *desc)
Definition: fd.cc:186
int client_ip_max_connections
Definition: SquidConfig.h:539
int setSockTos(const Comm::ConnectionPointer &conn, tos_t tos)
Definition: QosConfig.cc:558
@ dirAccepted
accepted (from a client by Squid)
Definition: QosConfig.h:67
void removeDead(const TcpAcceptor::Pointer &afd)
bool isOpen(const int fd)
Definition: comm.cc:87
void logAcceptError(const ConnectionPointer &tcpClient) const
Definition: TcpAcceptor.cc:261
static bool okToAccept()
Definition: TcpAcceptor.cc:245
void subscribe(const Subscription::Pointer &aSub)
Definition: TcpAcceptor.cc:60
virtual bool doneAll() const
whether positive goal has been reached
Definition: TcpAcceptor.cc:93
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:1010
MasterXaction::Pointer xaction
Transaction which this call is part of.
Definition: CommCalls.h:105
void const char * buf
Definition: stub_helper.cc:16
#define PROF_start(probename)
Definition: Profiler.h:62
static void InitAddr(struct addrinfo *&ai)
Definition: Address.cc:668
class SquidConfig Config
Definition: SquidConfig.cc:12
void accessLogLog(AccessLogEntry::Pointer &, ACLChecklist *checklist)
Definition: access_log.cc:144
bool lookup(const Ip::Address &c)
Definition: Eui64.cc:47
StatCounters statCounter
Definition: StatCounters.cc:12

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors