]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm/TcpAcceptor.cc
Make mk-globals-c.awk more permissive on the input it accepts
[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"
a9870624 36#include "base/TextException.h"
04f55905 37#include "comm/AcceptLimiter.h"
582c2af2 38#include "CommCalls.h"
04f55905 39#include "comm/comm_internal.h"
5b67dfa4 40#include "comm/Connection.h"
d841c88d 41#include "comm/Loops.h"
cbff89ba 42#include "comm/TcpAcceptor.h"
04f55905 43#include "fde.h"
40d34a62 44#include "ip/Intercept.h"
582c2af2 45#include "profiler/Profiler.h"
5511c78a 46#include "protos.h"
04f55905 47#include "SquidTime.h"
e4f1fdae 48#include "StatCounters.h"
04f55905 49
db98b2bd
A
50namespace Comm
51{
52CBDATA_CLASS_INIT(TcpAcceptor);
a9870624
AJ
53};
54
8bbb16e3 55Comm::TcpAcceptor::TcpAcceptor(const Comm::ConnectionPointer &newConn, const char *note, const Subscription::Pointer &aSub) :
cbff89ba 56 AsyncJob("Comm::TcpAcceptor"),
0ba55a12
AJ
57 errcode(0),
58 isLimited(0),
5b67dfa4
AJ
59 theCallSub(aSub),
60 conn(newConn)
cbff89ba 61{}
0ba55a12
AJ
62
63void
cbff89ba 64Comm::TcpAcceptor::subscribe(const Subscription::Pointer &aSub)
0ba55a12 65{
cbff89ba 66 debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << aSub);
5b67dfa4
AJ
67 unsubscribe("subscription change");
68 theCallSub = aSub;
4c5518e5
AJ
69}
70
0ba55a12 71void
cbff89ba 72Comm::TcpAcceptor::unsubscribe(const char *reason)
0ba55a12 73{
cbff89ba 74 debugs(5, 5, HERE << status() << " AsyncCall Subscription " << theCallSub << " removed: " << reason);
5b67dfa4 75 theCallSub = NULL;
0ba55a12
AJ
76}
77
a9870624 78void
cbff89ba 79Comm::TcpAcceptor::start()
a9870624 80{
cbff89ba 81 debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << theCallSub);
a9870624
AJ
82
83 Must(IsConnOpen(conn));
84
85 setListen();
86
87 // if no error so far start accepting connections.
88 if (errcode == 0)
8bbb16e3 89 SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
a9870624
AJ
90}
91
92bool
cbff89ba 93Comm::TcpAcceptor::doneAll() const
a9870624 94{
6f8536c0 95 // stop when FD is closed
a9870624 96 if (!IsConnOpen(conn)) {
a9870624
AJ
97 return AsyncJob::doneAll();
98 }
99
5b67dfa4
AJ
100 // stop when handlers are gone
101 if (theCallSub == NULL) {
a9870624
AJ
102 return AsyncJob::doneAll();
103 }
104
5b67dfa4 105 // open FD with handlers...keep accepting.
a9870624
AJ
106 return false;
107}
108
109void
cbff89ba 110Comm::TcpAcceptor::swanSong()
a9870624
AJ
111{
112 debugs(5,5, HERE);
5b67dfa4 113 unsubscribe("swanSong");
a9870624 114 conn = NULL;
5b67dfa4 115 AcceptLimiter::Instance().removeDead(this);
a9870624
AJ
116 AsyncJob::swanSong();
117}
118
cbff89ba
AJ
119const char *
120Comm::TcpAcceptor::status() const
121{
8bbb16e3
AJ
122 if (conn == NULL)
123 return "[nil connection]";
124
cbff89ba
AJ
125 static char ipbuf[MAX_IPSTRLEN] = {'\0'};
126 if (ipbuf[0] == '\0')
8bbb16e3 127 conn->local.ToHostname(ipbuf, MAX_IPSTRLEN);
cbff89ba
AJ
128
129 static MemBuf buf;
130 buf.reset();
8bbb16e3 131 buf.Printf(" FD %d, %s",conn->fd, ipbuf);
cbff89ba
AJ
132
133 const char *jobStatus = AsyncJob::status();
134 buf.append(jobStatus, strlen(jobStatus));
135
136 return buf.content();
137}
138
0ba55a12
AJ
139/**
140 * New-style listen and accept routines
141 *
142 * setListen simply registers our interest in an FD for listening.
143 * The constructor takes a callback to call when an FD has been
144 * accept()ed some time later.
145 */
146void
cbff89ba 147Comm::TcpAcceptor::setListen()
0ba55a12
AJ
148{
149 errcode = 0; // reset local errno copy.
a9870624 150 if (listen(conn->fd, Squid_MaxFD >> 2) < 0) {
cbff89ba 151 debugs(50, DBG_CRITICAL, "ERROR: listen(" << status() << ", " << (Squid_MaxFD >> 2) << "): " << xstrerror());
0ba55a12
AJ
152 errcode = errno;
153 return;
154 }
155
156 if (Config.accept_filter && strcmp(Config.accept_filter, "none") != 0) {
157#ifdef SO_ACCEPTFILTER
158 struct accept_filter_arg afa;
159 bzero(&afa, sizeof(afa));
5b67dfa4 160 debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on " << conn);
0ba55a12 161 xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name));
a9870624 162 if (setsockopt(conn->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0)
5b67dfa4 163 debugs(5, DBG_CRITICAL, "WARNING: SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerror());
0ba55a12
AJ
164#elif defined(TCP_DEFER_ACCEPT)
165 int seconds = 30;
166 if (strncmp(Config.accept_filter, "data=", 5) == 0)
167 seconds = atoi(Config.accept_filter + 5);
a9870624 168 if (setsockopt(conn->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds)) < 0)
5b67dfa4 169 debugs(5, DBG_CRITICAL, "WARNING: TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerror());
0ba55a12 170#else
5b67dfa4 171 debugs(5, DBG_CRITICAL, "WARNING: accept_filter not supported on your OS");
0ba55a12
AJ
172#endif
173 }
04f55905
AJ
174}
175
176/**
177 * This private callback is called whenever a filedescriptor is ready
178 * to dupe itself and fob off an accept()ed connection
179 *
180 * It will either do that accept operation. Or if there are not enough FD
181 * available to do the clone safely will push the listening FD into a list
182 * of deferred operations. The list gets kicked and the dupe/accept() actually
183 * done later when enough sockets become available.
184 */
185void
cbff89ba 186Comm::TcpAcceptor::doAccept(int fd, void *data)
04f55905 187{
5b67dfa4
AJ
188 try {
189 debugs(5, 2, HERE << "New connection on FD " << fd);
04f55905 190
5b67dfa4 191 Must(isOpen(fd));
cbff89ba 192 TcpAcceptor *afd = static_cast<TcpAcceptor*>(data);
04f55905 193
5b67dfa4
AJ
194 if (!okToAccept()) {
195 AcceptLimiter::Instance().defer(afd);
196 } else {
197 afd->acceptNext();
198 }
cbff89ba 199 SetSelect(fd, COMM_SELECT_READ, Comm::TcpAcceptor::doAccept, afd, 0);
5b67dfa4 200
db98b2bd 201 } catch (const std::exception &e) {
cbff89ba 202 fatalf("FATAL: error while accepting new client connection: %s\n", e.what());
db98b2bd 203 } catch (...) {
5b67dfa4 204 fatal("FATAL: error while accepting new client connection: [unkown]\n");
04f55905 205 }
04f55905
AJ
206}
207
208bool
cbff89ba 209Comm::TcpAcceptor::okToAccept()
04f55905
AJ
210{
211 static time_t last_warn = 0;
212
213 if (fdNFree() >= RESERVED_FD)
214 return true;
215
216 if (last_warn + 15 < squid_curtime) {
217 debugs(5, DBG_CRITICAL, "WARNING! Your cache is running out of filedescriptors");
218 last_warn = squid_curtime;
219 }
220
221 return false;
222}
223
971581ee 224void
cbff89ba 225Comm::TcpAcceptor::acceptOne()
04f55905
AJ
226{
227 /*
228 * We don't worry about running low on FDs here. Instead,
229 * doAccept() will use AcceptLimiter if we reach the limit
230 * there.
231 */
232
233 /* Accept a new connection */
5b67dfa4 234 ConnectionPointer newConnDetails = new Connection();
8bbb16e3 235 const comm_err_t flag = oldAccept(newConnDetails);
04f55905
AJ
236
237 /* Check for errors */
5b67dfa4 238 if (!newConnDetails->isOpen()) {
04f55905 239
cbff89ba 240 if (flag == COMM_NOMESSAGE) {
04f55905 241 /* register interest again */
6f8536c0 242 debugs(5, 5, HERE << "try later: " << conn << " handler Subscription: " << theCallSub);
8bbb16e3 243 SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
971581ee 244 return;
04f55905
AJ
245 }
246
247 // A non-recoverable error; notify the caller */
cbff89ba 248 debugs(5, 5, HERE << "non-recoverable error:" << status() << " handler Subscription: " << theCallSub);
8bbb16e3 249 notify(flag, newConnDetails);
5b67dfa4 250 mustStop("Listener socket closed");
971581ee 251 return;
04f55905
AJ
252 }
253
5b67dfa4
AJ
254 debugs(5, 5, HERE << "Listener: " << conn <<
255 " accepted new connection " << newConnDetails <<
6f8536c0 256 " handler Subscription: " << theCallSub);
8bbb16e3 257 notify(flag, newConnDetails);
04f55905
AJ
258}
259
260void
cbff89ba 261Comm::TcpAcceptor::acceptNext()
04f55905 262{
a9870624 263 Must(IsConnOpen(conn));
5b67dfa4 264 debugs(5, 2, HERE << "connection on " << conn);
971581ee 265 acceptOne();
04f55905
AJ
266}
267
268void
8bbb16e3 269Comm::TcpAcceptor::notify(const comm_err_t flag, const Comm::ConnectionPointer &newConnDetails) const
04f55905
AJ
270{
271 // listener socket handlers just abandon the port with COMM_ERR_CLOSING
272 // it should only happen when this object is deleted...
1fc32b95 273 if (flag == COMM_ERR_CLOSING) {
04f55905
AJ
274 return;
275 }
276
5b67dfa4
AJ
277 if (theCallSub != NULL) {
278 AsyncCall::Pointer call = theCallSub->callback();
279 CommAcceptCbParams &params = GetCommParams<CommAcceptCbParams>(call);
a9870624 280 params.fd = conn->fd;
5b67dfa4 281 params.conn = newConnDetails;
0ba55a12
AJ
282 params.flag = flag;
283 params.xerrno = errcode;
284 ScheduleCallHere(call);
0ba55a12 285 }
04f55905
AJ
286}
287
288/**
97b8ac39 289 * accept() and process
5b67dfa4
AJ
290 * Wait for an incoming connection on our listener socket.
291 *
292 * \retval COMM_OK success. details parameter filled.
293 * \retval COMM_NOMESSAGE attempted accept() but nothing useful came in.
294 * \retval COMM_ERROR an outright failure occured.
295 * Or if this client has too many connections already.
273f66c4 296 */
5b67dfa4 297comm_err_t
8bbb16e3 298Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
04f55905
AJ
299{
300 PROF_start(comm_accept);
e4f1fdae 301 ++statCounter.syscalls.sock.accepts;
04f55905
AJ
302 int sock;
303 struct addrinfo *gai = NULL;
5b67dfa4 304 details->local.InitAddrInfo(gai);
04f55905 305
1fc32b95 306 errcode = 0; // reset local errno copy.
a9870624 307 if ((sock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
1fc32b95 308 errcode = errno; // store last accept errno locally.
04f55905 309
5b67dfa4 310 details->local.FreeAddrInfo(gai);
04f55905
AJ
311
312 PROF_stop(comm_accept);
313
314 if (ignoreErrno(errno)) {
cbff89ba 315 debugs(50, 5, HERE << status() << ": " << xstrerror());
04f55905
AJ
316 return COMM_NOMESSAGE;
317 } else if (ENFILE == errno || EMFILE == errno) {
cbff89ba 318 debugs(50, 3, HERE << status() << ": " << xstrerror());
04f55905
AJ
319 return COMM_ERROR;
320 } else {
e0236918 321 debugs(50, DBG_IMPORTANT, HERE << status() << ": " << xstrerror());
04f55905
AJ
322 return COMM_ERROR;
323 }
324 }
325
a9870624 326 Must(sock >= 0);
5b67dfa4
AJ
327 details->fd = sock;
328 details->remote = *gai;
04f55905 329
5511c78a 330 if ( Config.client_ip_max_connections >= 0) {
5b67dfa4
AJ
331 if (clientdbEstablished(details->remote, 0) > Config.client_ip_max_connections) {
332 debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
333 details->local.FreeAddrInfo(gai);
5511c78a
AJ
334 return COMM_ERROR;
335 }
336 }
337
903198a7 338 // lookup the local-end details of this new connection
5b67dfa4
AJ
339 details->local.InitAddrInfo(gai);
340 details->local.SetEmpty();
04f55905 341 getsockname(sock, gai->ai_addr, &gai->ai_addrlen);
5b67dfa4
AJ
342 details->local = *gai;
343 details->local.FreeAddrInfo(gai);
04f55905
AJ
344
345 /* fdstat update */
5b67dfa4 346 // XXX : these are not all HTTP requests. use a note about type and ip:port details->
903198a7 347 // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
04f55905
AJ
348 fd_open(sock, FD_SOCKET, "HTTP Request");
349
350 fdd_table[sock].close_file = NULL;
351 fdd_table[sock].close_line = 0;
352
353 fde *F = &fd_table[sock];
5b67dfa4
AJ
354 details->remote.NtoA(F->ipaddr,MAX_IPSTRLEN);
355 F->remote_port = details->remote.GetPort();
356 F->local_addr = details->local;
357 F->sock_family = details->local.IsIPv6()?AF_INET6:AF_INET;
04f55905 358
903198a7
AJ
359 // set socket flags
360 commSetCloseOnExec(sock);
04f55905
AJ
361 commSetNonBlocking(sock);
362
363 /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
40d34a62
AJ
364 F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
365
366 // Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
367 if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) {
368 // Failed.
369 return COMM_ERROR;
370 }
04f55905
AJ
371
372 PROF_stop(comm_accept);
5b67dfa4 373 return COMM_OK;
04f55905 374}