TcpAcceptor.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2018 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 
41 CBDATA_NAMESPACED_CLASS_INIT(Comm, TcpAcceptor);
42 
44  AsyncJob("Comm::TcpAcceptor"),
45  errcode(0),
46  isLimited(0),
47  theCallSub(aSub),
48  conn(newConn),
49  listenPort_()
50 {}
51 
53  AsyncJob("Comm::TcpAcceptor"),
54  errcode(0),
55  isLimited(0),
56  theCallSub(aSub),
57  conn(p->listenConn),
58  listenPort_(p)
59 {}
60 
61 void
63 {
64  debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << aSub);
65  unsubscribe("subscription change");
66  theCallSub = aSub;
67 }
68 
69 void
70 Comm::TcpAcceptor::unsubscribe(const char *reason)
71 {
72  debugs(5, 5, HERE << status() << " AsyncCall Subscription " << theCallSub << " removed: " << reason);
73  theCallSub = NULL;
74 }
75 
76 void
78 {
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(" << status() << ", " << (Squid_MaxFD >> 2) << "): " << 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  }
237 
238  } catch (const std::exception &e) {
239  fatalf("FATAL: error while accepting new client connection: %s\n", e.what());
240  } catch (...) {
241  fatal("FATAL: error while accepting new client connection: [unknown]\n");
242  }
243 }
244 
245 bool
247 {
248  static time_t last_warn = 0;
249 
250  if (fdNFree() >= RESERVED_FD)
251  return true;
252 
253  if (last_warn + 15 < squid_curtime) {
254  debugs(5, DBG_CRITICAL, "WARNING! Your cache is running out of filedescriptors");
255  last_warn = squid_curtime;
256  }
257 
258  return false;
259 }
260 
261 static void
263 {
265  al->tcpClient = conn;
266  al->url = "error:accept-client-connection";
267  ACLFilledChecklist ch(nullptr, nullptr, nullptr);
268  ch.src_addr = conn->remote;
269  ch.my_addr = conn->local;
270  accessLogLog(al, &ch);
271 }
272 
273 void
275 {
276  /*
277  * We don't worry about running low on FDs here. Instead,
278  * doAccept() will use AcceptLimiter if we reach the limit
279  * there.
280  */
281 
282  /* Accept a new connection */
283  ConnectionPointer newConnDetails = new Connection();
284  const Comm::Flag flag = oldAccept(newConnDetails);
285 
286  /* Check for errors */
287  if (!newConnDetails->isOpen()) {
288 
289  if (flag == Comm::NOMESSAGE) {
290  /* register interest again */
291  debugs(5, 5, HERE << "try later: " << conn << " handler Subscription: " << theCallSub);
292  SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
293  return;
294  }
295 
296  // A non-recoverable error; notify the caller */
297  debugs(5, 5, HERE << "non-recoverable error:" << status() << " handler Subscription: " << theCallSub);
298  if (intendedForUserConnections())
299  logAcceptError(newConnDetails);
300  notify(flag, newConnDetails);
301  mustStop("Listener socket closed");
302  return;
303  }
304 
305  newConnDetails->nfmark = Ip::Qos::getNfmarkFromConnection(newConnDetails, Ip::Qos::dirAccepted);
306 
307  debugs(5, 5, HERE << "Listener: " << conn <<
308  " accepted new connection " << newConnDetails <<
309  " handler Subscription: " << theCallSub);
310  notify(flag, newConnDetails);
311 }
312 
313 void
315 {
316  Must(IsConnOpen(conn));
317  debugs(5, 2, HERE << "connection on " << conn);
318  acceptOne();
319 }
320 
321 void
322 Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &newConnDetails) const
323 {
324  // listener socket handlers just abandon the port with Comm::ERR_CLOSING
325  // it should only happen when this object is deleted...
326  if (flag == Comm::ERR_CLOSING) {
327  return;
328  }
329 
330  if (theCallSub != NULL) {
331  AsyncCall::Pointer call = theCallSub->callback();
332  CommAcceptCbParams &params = GetCommParams<CommAcceptCbParams>(call);
334  params.xaction->squidPort = listenPort_;
335  params.fd = conn->fd;
336  params.conn = params.xaction->tcpClient = newConnDetails;
337  params.flag = flag;
338  params.xerrno = errcode;
339  ScheduleCallHere(call);
340  }
341 }
342 
354 {
355  PROF_start(comm_accept);
356  ++statCounter.syscalls.sock.accepts;
357  int sock;
358  struct addrinfo *gai = NULL;
360 
361  errcode = 0; // reset local errno copy.
362  if ((sock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
363  errcode = errno; // store last accept errno locally.
364 
366 
367  PROF_stop(comm_accept);
368 
369  if (ignoreErrno(errcode)) {
370  debugs(50, 5, status() << ": " << xstrerr(errcode));
371  return Comm::NOMESSAGE;
372  } else if (ENFILE == errno || EMFILE == errno) {
373  debugs(50, 3, status() << ": " << xstrerr(errcode));
374  return Comm::COMM_ERROR;
375  } else {
376  debugs(50, DBG_IMPORTANT, MYNAME << status() << ": " << xstrerr(errcode));
377  return Comm::COMM_ERROR;
378  }
379  }
380 
381  Must(sock >= 0);
382  details->fd = sock;
383  details->remote = *gai;
384 
385  if ( Config.client_ip_max_connections >= 0) {
387  debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
389  PROF_stop(comm_accept);
390  return Comm::COMM_ERROR;
391  }
392  }
393 
394  // lookup the local-end details of this new connection
396  details->local.setEmpty();
397  if (getsockname(sock, gai->ai_addr, &gai->ai_addrlen) != 0) {
398  int xerrno = errno;
399  debugs(50, DBG_IMPORTANT, "ERROR: getsockname() failed to locate local-IP on " << details << ": " << xstrerr(xerrno));
401  PROF_stop(comm_accept);
402  return Comm::COMM_ERROR;
403  }
404  details->local = *gai;
406 
407  /* fdstat update */
408  // XXX : these are not all HTTP requests. use a note about type and ip:port details->
409  // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
410  fd_open(sock, FD_SOCKET, "HTTP Request");
411 
412  fde *F = &fd_table[sock];
413  details->remote.toStr(F->ipaddr,MAX_IPSTRLEN);
414  F->remote_port = details->remote.port();
415  F->local_addr = details->local;
416  F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET;
417 
418  // set socket flags
419  commSetCloseOnExec(sock);
420  commSetNonBlocking(sock);
421 
422  /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
423  F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
424 
425  // Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
426  if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) {
427  debugs(50, DBG_IMPORTANT, "ERROR: NAT/TPROXY lookup failed to locate original IPs on " << details);
428  // Failed.
429  PROF_stop(comm_accept);
430  return Comm::COMM_ERROR;
431  }
432 
433 #if USE_SQUID_EUI
434  if (Eui::TheConfig.euiLookup) {
435  if (details->remote.isIPv4()) {
436  details->remoteEui48.lookup(details->remote);
437  } else if (details->remote.isIPv6()) {
438  details->remoteEui64.lookup(details->remote);
439  }
440  }
441 #endif
442 
443  PROF_stop(comm_accept);
444  return Comm::OK;
445 }
446 
AnyP::PortCfgPointer squidPort
the listening port which originated this transaction
Definition: MasterXaction.h:50
bool Lookup(const Comm::ConnectionPointer &newConn, const Comm::ConnectionPointer &listenConn)
Definition: Intercept.cc:389
virtual bool doneAll() const
whether positive goal has been reached
Definition: AsyncJob.cc:96
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:68
#define fd_table
Definition: fde.h:157
#define COMM_SELECT_READ
Definition: defines.h:36
StatCounters statCounter
Definition: StatCounters.cc:12
bool isOpen(const int fd)
Definition: comm.cc:86
void unsubscribe(const char *reason)
Definition: TcpAcceptor.cc:70
int Squid_MaxFD
virtual void append(const char *c, int sz)
Definition: MemBuf.cc:225
nfmark_t getNfmarkFromConnection(const Comm::ConnectionPointer &conn, const ConnectionDirection connDir)
Definition: QosConfig.cc:81
struct fde::_fde_flags flags
nfmark_t nfmark
Definition: Connection.h:150
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:679
Ip::Address local_addr
Definition: fde.h:86
void accessLogLog(AccessLogEntry::Pointer &, ACLChecklist *checklist)
Definition: access_log.cc:144
struct StatCounters::@136::@140 sock
unsigned short remote_port
Definition: fde.h:84
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 client_ip_max_connections
Definition: SquidConfig.h:528
Ip::Address src_addr
int clientdbEstablished(const Ip::Address &addr, int delta)
Definition: client_db.cc:183
void fd_open(int fd, unsigned int type, const char *desc)
Definition: fd.cc:187
Definition: Flag.h:16
virtual const char * status() const
internal cleanup; do not call directly
Definition: AsyncJob.cc:159
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
#define COMM_INTERCEPTION
Definition: Connection.h:49
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:322
void handleClosure(const CommCloseCbParams &io)
Definition: TcpAcceptor.cc:206
#define DBG_CRITICAL
Definition: Debug.h:45
char * p
Definition: membanger.c:43
int conn
the current server connection FD
Definition: Transport.cc:26
int fdNFree(void)
Definition: fd.cc:300
Eui::Eui64 remoteEui64
Definition: Connection.h:159
time_t squid_curtime
Definition: stub_time.cc:17
Eui::Eui48 remoteEui48
Definition: Connection.h:158
int sock_family
Definition: fde.h:91
void fatalf(const char *fmt,...)
Definition: fatal.cc:79
int RESERVED_FD
bool isIPv6() const
Definition: Address.cc:157
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
const char * xstrerr(int error)
Definition: xstrerror.cc:83
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:24
void const char HLPCB void * data
Definition: stub_helper.cc:16
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:90
bool lookup(const Ip::Address &c)
Definition: Eui48.cc:135
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:121
struct StatCounters::@136 syscalls
#define DBG_IMPORTANT
Definition: Debug.h:46
TcpAcceptor(const TcpAcceptor &)
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:225
virtual void swanSong()
Definition: AsyncJob.h:54
void reset()
Definition: MemBuf.cc:141
static bool okToAccept()
Definition: TcpAcceptor.cc:246
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:88
virtual const char * status() const
internal cleanup; do not call directly
Definition: TcpAcceptor.cc:126
virtual bool doneAll() const
whether positive goal has been reached
Definition: TcpAcceptor.cc:93
static void logAcceptError(const Comm::ConnectionPointer &conn)
Definition: TcpAcceptor.cc:262
void commSetCloseOnExec(int fd)
Definition: comm.cc:1137
static void InitAddr(struct addrinfo *&ai)
Definition: Address.cc:661
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
void fatal(const char *message)
Definition: fatal.cc:39
char * accept_filter
Definition: SquidConfig.h:518
int commSetNonBlocking(int fd)
Definition: comm.cc:1076
void setEmpty()
Fast reset of the stored content to what would be after default constructor.
Definition: Address.cc:177
bool isIPv4() const
Definition: Address.cc:151
void defer(const TcpAcceptor::Pointer &afd)
char * content()
start of the added data
Definition: MemBuf.h:41
void const char * buf
Definition: stub_helper.cc:16
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:145
unsigned short port() const
Definition: Address.cc:771
Flag
Definition: Flag.h:15
Ip::Address local
Definition: Connection.h:135
int ignoreErrno(int ierrno)
Definition: comm.cc:1477
EuiConfig TheConfig
Definition: Config.cc:12
#define MYNAME
Definition: Debug.h:158
void removeDead(const TcpAcceptor::Pointer &afd)
Ip::Address remote
Definition: Connection.h:138
#define COMM_TRANSPARENT
Definition: Connection.h:48
Comm::Flag oldAccept(Comm::ConnectionPointer &details)
Definition: TcpAcceptor.cc:353
MasterXaction::Pointer xaction
Transaction which this call is part of.
Definition: CommCalls.h:105
#define ScheduleCallHere(call)
Definition: AsyncCall.h:166
socklen_t ai_addrlen
#define PROF_start(probename)
Definition: Profiler.h:62
bool isOpen() const
Definition: Connection.h:87
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:87
CBDATA_NAMESPACED_CLASS_INIT(Comm, TcpAcceptor)
struct sockaddr * ai_addr
bool transparent
Definition: fde.h:108
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
accepted (from a client by Squid)
Definition: QosConfig.h:66
Definition: fde.h:49
Definition: MemBuf.h:23
int setSockNfmark(const Comm::ConnectionPointer &conn, nfmark_t mark)
Definition: QosConfig.cc:525
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:961
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:23
bool lookup(const Ip::Address &c)
Definition: Eui64.cc:47
#define PROF_stop(probename)
Definition: Profiler.h:63
virtual void swanSong()
Definition: TcpAcceptor.cc:110
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:795
char ipaddr[MAX_IPSTRLEN]
Definition: fde.h:92
void subscribe(const Subscription::Pointer &aSub)
Definition: TcpAcceptor.cc:62
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:988
static AcceptLimiter & Instance()
int setSockTos(const Comm::ConnectionPointer &conn, tos_t tos)
Definition: QosConfig.cc:497
virtual void start()
called by AsyncStart; do not call directly
Definition: TcpAcceptor.cc:77
Intercept Interceptor
Definition: Intercept.h:154
Comm::ConnectionPointer tcpClient
the client TCP connection which originated this transaction
Definition: MasterXaction.h:53
class SquidConfig Config
Definition: SquidConfig.cc:12
#define NULL
Definition: types.h:166

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors