TcpAcceptor.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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  debugs(5, 5, HERE << "Listener: " << conn <<
306  " accepted new connection " << newConnDetails <<
307  " handler Subscription: " << theCallSub);
308  notify(flag, newConnDetails);
309 }
310 
311 void
313 {
314  Must(IsConnOpen(conn));
315  debugs(5, 2, HERE << "connection on " << conn);
316  acceptOne();
317 }
318 
319 void
320 Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &newConnDetails) const
321 {
322  // listener socket handlers just abandon the port with Comm::ERR_CLOSING
323  // it should only happen when this object is deleted...
324  if (flag == Comm::ERR_CLOSING) {
325  return;
326  }
327 
328  if (theCallSub != NULL) {
329  AsyncCall::Pointer call = theCallSub->callback();
330  CommAcceptCbParams &params = GetCommParams<CommAcceptCbParams>(call);
332  params.xaction->squidPort = listenPort_;
333  params.fd = conn->fd;
334  params.conn = params.xaction->tcpClient = newConnDetails;
335  params.flag = flag;
336  params.xerrno = errcode;
337  ScheduleCallHere(call);
338  }
339 }
340 
352 {
353  PROF_start(comm_accept);
354  ++statCounter.syscalls.sock.accepts;
355  int sock;
356  struct addrinfo *gai = NULL;
358 
359  errcode = 0; // reset local errno copy.
360  if ((sock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
361  errcode = errno; // store last accept errno locally.
362 
364 
365  PROF_stop(comm_accept);
366 
367  if (ignoreErrno(errcode)) {
368  debugs(50, 5, status() << ": " << xstrerr(errcode));
369  return Comm::NOMESSAGE;
370  } else if (ENFILE == errno || EMFILE == errno) {
371  debugs(50, 3, status() << ": " << xstrerr(errcode));
372  return Comm::COMM_ERROR;
373  } else {
374  debugs(50, DBG_IMPORTANT, MYNAME << status() << ": " << xstrerr(errcode));
375  return Comm::COMM_ERROR;
376  }
377  }
378 
379  Must(sock >= 0);
380  details->fd = sock;
381  details->remote = *gai;
382 
383  if ( Config.client_ip_max_connections >= 0) {
385  debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
387  PROF_stop(comm_accept);
388  return Comm::COMM_ERROR;
389  }
390  }
391 
392  // lookup the local-end details of this new connection
394  details->local.setEmpty();
395  if (getsockname(sock, gai->ai_addr, &gai->ai_addrlen) != 0) {
396  int xerrno = errno;
397  debugs(50, DBG_IMPORTANT, "ERROR: getsockname() failed to locate local-IP on " << details << ": " << xstrerr(xerrno));
399  PROF_stop(comm_accept);
400  return Comm::COMM_ERROR;
401  }
402  details->local = *gai;
404 
405  /* fdstat update */
406  // XXX : these are not all HTTP requests. use a note about type and ip:port details->
407  // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
408  fd_open(sock, FD_SOCKET, "HTTP Request");
409 
410  fde *F = &fd_table[sock];
411  details->remote.toStr(F->ipaddr,MAX_IPSTRLEN);
412  F->remote_port = details->remote.port();
413  F->local_addr = details->local;
414  F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET;
415 
416  // set socket flags
417  commSetCloseOnExec(sock);
418  commSetNonBlocking(sock);
419 
420  /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
421  F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
422 
423  // Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
424  if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) {
425  debugs(50, DBG_IMPORTANT, "ERROR: NAT/TPROXY lookup failed to locate original IPs on " << details);
426  // Failed.
427  PROF_stop(comm_accept);
428  return Comm::COMM_ERROR;
429  }
430 
431 #if USE_SQUID_EUI
432  if (Eui::TheConfig.euiLookup) {
433  if (details->remote.isIPv4()) {
434  details->remoteEui48.lookup(details->remote);
435  } else if (details->remote.isIPv6()) {
436  details->remoteEui64.lookup(details->remote);
437  }
438  }
439 #endif
440 
441  PROF_stop(comm_accept);
442  return Comm::OK;
443 }
444 
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:390
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
struct fde::_fde_flags flags
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:694
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 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:320
void handleClosure(const CommCloseCbParams &io)
Definition: TcpAcceptor.cc:206
#define DBG_CRITICAL
Definition: Debug.h:44
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:123
struct StatCounters::@136 syscalls
#define DBG_IMPORTANT
Definition: Debug.h:45
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:55
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:676
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:147
unsigned short port() const
Definition: Address.cc:786
Flag
Definition: Flag.h:15
#define Must(cond)
Definition: TextException.h:89
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:160
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:351
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
Definition: fde.h:49
Definition: MemBuf.h:23
int setSockNfmark(const Comm::ConnectionPointer &conn, nfmark_t mark)
Definition: QosConfig.cc:522
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:810
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:494
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