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