]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm/TcpAcceptor.cc
Rename Packable::Printf as Packable::appendf
[thirdparty/squid.git] / src / comm / TcpAcceptor.cc
CommitLineData
04f55905 1/*
bde978a6 2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
04f55905 3 *
bbc27441
AJ
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.
04f55905
AJ
7 */
8
bbc27441
AJ
9/* DEBUG: section 05 Listener Socket Handler */
10
582c2af2 11#include "squid.h"
94bfd31f 12#include "anyp/PortCfg.h"
a9870624 13#include "base/TextException.h"
95e6d864 14#include "client_db.h"
04f55905
AJ
15#include "comm/AcceptLimiter.h"
16#include "comm/comm_internal.h"
5b67dfa4 17#include "comm/Connection.h"
d841c88d 18#include "comm/Loops.h"
cbff89ba 19#include "comm/TcpAcceptor.h"
602d9612 20#include "CommCalls.h"
83b62d3f 21#include "eui/Config.h"
c4ad1349 22#include "fd.h"
04f55905 23#include "fde.h"
67679543 24#include "globals.h"
40d34a62 25#include "ip/Intercept.h"
c6f168c1 26#include "ip/QosConfig.h"
94bfd31f 27#include "MasterXaction.h"
582c2af2 28#include "profiler/Profiler.h"
4d5904f7 29#include "SquidConfig.h"
04f55905 30#include "SquidTime.h"
e4f1fdae 31#include "StatCounters.h"
04f55905 32
1a30fdf5 33#include <cerrno>
f842580f
AJ
34#ifdef HAVE_NETINET_TCP_H
35// required for accept_filter to build.
36#include <netinet/tcp.h>
37#endif
21d845b1 38
f842580f 39CBDATA_NAMESPACED_CLASS_INIT(Comm, TcpAcceptor);
a9870624 40
ced8def3 41Comm::TcpAcceptor::TcpAcceptor(const Comm::ConnectionPointer &newConn, const char *, const Subscription::Pointer &aSub) :
f53969cc
SM
42 AsyncJob("Comm::TcpAcceptor"),
43 errcode(0),
44 isLimited(0),
45 theCallSub(aSub),
46 conn(newConn),
47 listenPort_()
fa720bfb
AJ
48{}
49
ced8def3 50Comm::TcpAcceptor::TcpAcceptor(const AnyP::PortCfgPointer &p, const char *, const Subscription::Pointer &aSub) :
f53969cc
SM
51 AsyncJob("Comm::TcpAcceptor"),
52 errcode(0),
53 isLimited(0),
54 theCallSub(aSub),
55 conn(p->listenConn),
56 listenPort_(p)
cbff89ba 57{}
0ba55a12
AJ
58
59void
cbff89ba 60Comm::TcpAcceptor::subscribe(const Subscription::Pointer &aSub)
0ba55a12 61{
cbff89ba 62 debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << aSub);
5b67dfa4
AJ
63 unsubscribe("subscription change");
64 theCallSub = aSub;
4c5518e5
AJ
65}
66
0ba55a12 67void
cbff89ba 68Comm::TcpAcceptor::unsubscribe(const char *reason)
0ba55a12 69{
cbff89ba 70 debugs(5, 5, HERE << status() << " AsyncCall Subscription " << theCallSub << " removed: " << reason);
5b67dfa4 71 theCallSub = NULL;
0ba55a12
AJ
72}
73
a9870624 74void
cbff89ba 75Comm::TcpAcceptor::start()
a9870624 76{
cbff89ba 77 debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << theCallSub);
a9870624
AJ
78
79 Must(IsConnOpen(conn));
80
81 setListen();
82
8aec3e1b
CT
83 conn->noteStart();
84
a9870624
AJ
85 // if no error so far start accepting connections.
86 if (errcode == 0)
8bbb16e3 87 SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
a9870624
AJ
88}
89
90bool
cbff89ba 91Comm::TcpAcceptor::doneAll() const
a9870624 92{
6f8536c0 93 // stop when FD is closed
a9870624 94 if (!IsConnOpen(conn)) {
a9870624
AJ
95 return AsyncJob::doneAll();
96 }
97
5b67dfa4
AJ
98 // stop when handlers are gone
99 if (theCallSub == NULL) {
a9870624
AJ
100 return AsyncJob::doneAll();
101 }
102
5b67dfa4 103 // open FD with handlers...keep accepting.
a9870624
AJ
104 return false;
105}
106
107void
cbff89ba 108Comm::TcpAcceptor::swanSong()
a9870624
AJ
109{
110 debugs(5,5, HERE);
5b67dfa4 111 unsubscribe("swanSong");
6f09127c
AR
112 if (IsConnOpen(conn)) {
113 if (closer_ != NULL)
114 comm_remove_close_handler(conn->fd, closer_);
115 conn->close();
116 }
117
a9870624 118 conn = NULL;
5b67dfa4 119 AcceptLimiter::Instance().removeDead(this);
a9870624
AJ
120 AsyncJob::swanSong();
121}
122
cbff89ba
AJ
123const char *
124Comm::TcpAcceptor::status() const
125{
8bbb16e3
AJ
126 if (conn == NULL)
127 return "[nil connection]";
128
cbff89ba
AJ
129 static char ipbuf[MAX_IPSTRLEN] = {'\0'};
130 if (ipbuf[0] == '\0')
4dd643d5 131 conn->local.toHostStr(ipbuf, MAX_IPSTRLEN);
cbff89ba
AJ
132
133 static MemBuf buf;
134 buf.reset();
4391cd15 135 buf.appendf(" FD %d, %s",conn->fd, ipbuf);
cbff89ba
AJ
136
137 const char *jobStatus = AsyncJob::status();
138 buf.append(jobStatus, strlen(jobStatus));
139
140 return buf.content();
141}
142
0ba55a12
AJ
143/**
144 * New-style listen and accept routines
145 *
146 * setListen simply registers our interest in an FD for listening.
147 * The constructor takes a callback to call when an FD has been
148 * accept()ed some time later.
149 */
150void
cbff89ba 151Comm::TcpAcceptor::setListen()
0ba55a12
AJ
152{
153 errcode = 0; // reset local errno copy.
a9870624 154 if (listen(conn->fd, Squid_MaxFD >> 2) < 0) {
cbff89ba 155 debugs(50, DBG_CRITICAL, "ERROR: listen(" << status() << ", " << (Squid_MaxFD >> 2) << "): " << xstrerror());
0ba55a12
AJ
156 errcode = errno;
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));
5b67dfa4 164 debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on " << conn);
0ba55a12 165 xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name));
a9870624 166 if (setsockopt(conn->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0)
5b67dfa4 167 debugs(5, DBG_CRITICAL, "WARNING: SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerror());
0ba55a12
AJ
168#elif defined(TCP_DEFER_ACCEPT)
169 int seconds = 30;
170 if (strncmp(Config.accept_filter, "data=", 5) == 0)
171 seconds = atoi(Config.accept_filter + 5);
a9870624 172 if (setsockopt(conn->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds)) < 0)
5b67dfa4 173 debugs(5, DBG_CRITICAL, "WARNING: TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerror());
0ba55a12 174#else
5b67dfa4 175 debugs(5, DBG_CRITICAL, "WARNING: accept_filter not supported on your OS");
0ba55a12
AJ
176#endif
177 }
6f09127c 178
c6f168c1
CT
179#if 0
180 // Untested code.
181 // Set TOS if needed.
182 // To correctly implement TOS values on listening sockets, probably requires
183 // more work to inherit TOS values to created connection objects.
184 if (conn->tos &&
d9cbd42a 185 Ip::Qos::setSockTos(conn->fd, conn->tos, conn->remote.isIPv4() ? AF_INET : AF_INET6) < 0)
c6f168c1
CT
186 conn->tos = 0;
187#if SO_MARK
188 if (conn->nfmark &&
d9cbd42a 189 Ip::Qos::setSockNfmark(conn->fd, conn->nfmark) < 0)
c6f168c1
CT
190 conn->nfmark = 0;
191#endif
192#endif
193
6f09127c
AR
194 typedef CommCbMemFunT<Comm::TcpAcceptor, CommCloseCbParams> Dialer;
195 closer_ = JobCallback(5, 4, Dialer, this, Comm::TcpAcceptor::handleClosure);
196 comm_add_close_handler(conn->fd, closer_);
197}
198
199/// called when listening descriptor is closed by an external force
200/// such as clientHttpConnectionsClose()
201void
ced8def3 202Comm::TcpAcceptor::handleClosure(const CommCloseCbParams &)
6f09127c
AR
203{
204 closer_ = NULL;
205 conn = NULL;
206 Must(done());
04f55905
AJ
207}
208
209/**
210 * This private callback is called whenever a filedescriptor is ready
211 * to dupe itself and fob off an accept()ed connection
212 *
213 * It will either do that accept operation. Or if there are not enough FD
214 * available to do the clone safely will push the listening FD into a list
215 * of deferred operations. The list gets kicked and the dupe/accept() actually
216 * done later when enough sockets become available.
217 */
218void
cbff89ba 219Comm::TcpAcceptor::doAccept(int fd, void *data)
04f55905 220{
5b67dfa4
AJ
221 try {
222 debugs(5, 2, HERE << "New connection on FD " << fd);
04f55905 223
5b67dfa4 224 Must(isOpen(fd));
cbff89ba 225 TcpAcceptor *afd = static_cast<TcpAcceptor*>(data);
04f55905 226
5b67dfa4
AJ
227 if (!okToAccept()) {
228 AcceptLimiter::Instance().defer(afd);
229 } else {
230 afd->acceptNext();
231 }
cbff89ba 232 SetSelect(fd, COMM_SELECT_READ, Comm::TcpAcceptor::doAccept, afd, 0);
5b67dfa4 233
db98b2bd 234 } catch (const std::exception &e) {
cbff89ba 235 fatalf("FATAL: error while accepting new client connection: %s\n", e.what());
db98b2bd 236 } catch (...) {
5b67dfa4 237 fatal("FATAL: error while accepting new client connection: [unkown]\n");
04f55905 238 }
04f55905
AJ
239}
240
241bool
cbff89ba 242Comm::TcpAcceptor::okToAccept()
04f55905
AJ
243{
244 static time_t last_warn = 0;
245
246 if (fdNFree() >= RESERVED_FD)
247 return true;
248
249 if (last_warn + 15 < squid_curtime) {
250 debugs(5, DBG_CRITICAL, "WARNING! Your cache is running out of filedescriptors");
251 last_warn = squid_curtime;
252 }
253
254 return false;
255}
256
971581ee 257void
cbff89ba 258Comm::TcpAcceptor::acceptOne()
04f55905
AJ
259{
260 /*
261 * We don't worry about running low on FDs here. Instead,
262 * doAccept() will use AcceptLimiter if we reach the limit
263 * there.
264 */
265
266 /* Accept a new connection */
5b67dfa4 267 ConnectionPointer newConnDetails = new Connection();
c8407295 268 const Comm::Flag flag = oldAccept(newConnDetails);
04f55905
AJ
269
270 /* Check for errors */
5b67dfa4 271 if (!newConnDetails->isOpen()) {
04f55905 272
c8407295 273 if (flag == Comm::NOMESSAGE) {
04f55905 274 /* register interest again */
6f8536c0 275 debugs(5, 5, HERE << "try later: " << conn << " handler Subscription: " << theCallSub);
8bbb16e3 276 SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
971581ee 277 return;
04f55905
AJ
278 }
279
280 // A non-recoverable error; notify the caller */
cbff89ba 281 debugs(5, 5, HERE << "non-recoverable error:" << status() << " handler Subscription: " << theCallSub);
8bbb16e3 282 notify(flag, newConnDetails);
5b67dfa4 283 mustStop("Listener socket closed");
971581ee 284 return;
04f55905
AJ
285 }
286
5b67dfa4
AJ
287 debugs(5, 5, HERE << "Listener: " << conn <<
288 " accepted new connection " << newConnDetails <<
6f8536c0 289 " handler Subscription: " << theCallSub);
8bbb16e3 290 notify(flag, newConnDetails);
04f55905
AJ
291}
292
293void
cbff89ba 294Comm::TcpAcceptor::acceptNext()
04f55905 295{
a9870624 296 Must(IsConnOpen(conn));
5b67dfa4 297 debugs(5, 2, HERE << "connection on " << conn);
971581ee 298 acceptOne();
04f55905
AJ
299}
300
301void
c8407295 302Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &newConnDetails) const
04f55905 303{
c8407295 304 // listener socket handlers just abandon the port with Comm::ERR_CLOSING
04f55905 305 // it should only happen when this object is deleted...
c8407295 306 if (flag == Comm::ERR_CLOSING) {
04f55905
AJ
307 return;
308 }
309
5b67dfa4
AJ
310 if (theCallSub != NULL) {
311 AsyncCall::Pointer call = theCallSub->callback();
312 CommAcceptCbParams &params = GetCommParams<CommAcceptCbParams>(call);
94bfd31f 313 params.xaction = new MasterXaction;
fa720bfb 314 params.xaction->squidPort = listenPort_;
a9870624 315 params.fd = conn->fd;
94bfd31f 316 params.conn = params.xaction->tcpClient = newConnDetails;
0ba55a12
AJ
317 params.flag = flag;
318 params.xerrno = errcode;
319 ScheduleCallHere(call);
0ba55a12 320 }
04f55905
AJ
321}
322
323/**
97b8ac39 324 * accept() and process
5b67dfa4
AJ
325 * Wait for an incoming connection on our listener socket.
326 *
c8407295
AJ
327 * \retval Comm::OK success. details parameter filled.
328 * \retval Comm::NOMESSAGE attempted accept() but nothing useful came in.
4ee57cbe 329 * \retval Comm::COMM_ERROR an outright failure occured.
5b67dfa4 330 * Or if this client has too many connections already.
273f66c4 331 */
c8407295 332Comm::Flag
8bbb16e3 333Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
04f55905
AJ
334{
335 PROF_start(comm_accept);
e4f1fdae 336 ++statCounter.syscalls.sock.accepts;
04f55905
AJ
337 int sock;
338 struct addrinfo *gai = NULL;
851614a8 339 Ip::Address::InitAddr(gai);
04f55905 340
1fc32b95 341 errcode = 0; // reset local errno copy.
a9870624 342 if ((sock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
1fc32b95 343 errcode = errno; // store last accept errno locally.
04f55905 344
851614a8 345 Ip::Address::FreeAddr(gai);
04f55905
AJ
346
347 PROF_stop(comm_accept);
348
349 if (ignoreErrno(errno)) {
cbff89ba 350 debugs(50, 5, HERE << status() << ": " << xstrerror());
c8407295 351 return Comm::NOMESSAGE;
04f55905 352 } else if (ENFILE == errno || EMFILE == errno) {
cbff89ba 353 debugs(50, 3, HERE << status() << ": " << xstrerror());
4ee57cbe 354 return Comm::COMM_ERROR;
04f55905 355 } else {
e0236918 356 debugs(50, DBG_IMPORTANT, HERE << status() << ": " << xstrerror());
4ee57cbe 357 return Comm::COMM_ERROR;
04f55905
AJ
358 }
359 }
360
a9870624 361 Must(sock >= 0);
5b67dfa4
AJ
362 details->fd = sock;
363 details->remote = *gai;
04f55905 364
5511c78a 365 if ( Config.client_ip_max_connections >= 0) {
5b67dfa4
AJ
366 if (clientdbEstablished(details->remote, 0) > Config.client_ip_max_connections) {
367 debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
851614a8 368 Ip::Address::FreeAddr(gai);
4ee57cbe 369 return Comm::COMM_ERROR;
5511c78a
AJ
370 }
371 }
372
903198a7 373 // lookup the local-end details of this new connection
851614a8 374 Ip::Address::InitAddr(gai);
4dd643d5 375 details->local.setEmpty();
e42281f9
AJ
376 if (getsockname(sock, gai->ai_addr, &gai->ai_addrlen) != 0) {
377 debugs(50, DBG_IMPORTANT, "ERROR: getsockname() failed to locate local-IP on " << details << ": " << xstrerror());
851614a8 378 Ip::Address::FreeAddr(gai);
4ee57cbe 379 return Comm::COMM_ERROR;
e42281f9 380 }
5b67dfa4 381 details->local = *gai;
851614a8 382 Ip::Address::FreeAddr(gai);
04f55905
AJ
383
384 /* fdstat update */
5b67dfa4 385 // XXX : these are not all HTTP requests. use a note about type and ip:port details->
903198a7 386 // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
04f55905
AJ
387 fd_open(sock, FD_SOCKET, "HTTP Request");
388
389 fdd_table[sock].close_file = NULL;
390 fdd_table[sock].close_line = 0;
391
392 fde *F = &fd_table[sock];
4dd643d5
AJ
393 details->remote.toStr(F->ipaddr,MAX_IPSTRLEN);
394 F->remote_port = details->remote.port();
5b67dfa4 395 F->local_addr = details->local;
4dd643d5 396 F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET;
04f55905 397
903198a7
AJ
398 // set socket flags
399 commSetCloseOnExec(sock);
04f55905
AJ
400 commSetNonBlocking(sock);
401
402 /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
40d34a62
AJ
403 F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
404
405 // Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
406 if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) {
407 // Failed.
4ee57cbe 408 return Comm::COMM_ERROR;
40d34a62 409 }
04f55905 410
83b62d3f
AJ
411#if USE_SQUID_EUI
412 if (Eui::TheConfig.euiLookup) {
68e47c3e
AJ
413 if (details->remote.isIPv4()) {
414 details->remoteEui48.lookup(details->remote);
415 } else if (details->remote.isIPv6()) {
416 details->remoteEui64.lookup(details->remote);
83b62d3f
AJ
417 }
418 }
419#endif
420
04f55905 421 PROF_stop(comm_accept);
c8407295 422 return Comm::OK;
04f55905 423}
f53969cc 424