]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm/ConnAcceptor.cc
Remove redundant accept params. add costructors for subscriptions
[thirdparty/squid.git] / src / comm / ConnAcceptor.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
35#include "squid.h"
a9870624 36#include "base/TextException.h"
04f55905
AJ
37#include "CommCalls.h"
38#include "comm/AcceptLimiter.h"
cfd66529 39#include "comm/Connection.h"
04f55905 40#include "comm/comm_internal.h"
a9870624 41#include "comm/ConnAcceptor.h"
04f55905 42#include "fde.h"
5511c78a 43#include "protos.h"
04f55905
AJ
44#include "SquidTime.h"
45
a9870624
AJ
46namespace Comm {
47 CBDATA_CLASS_INIT(ConnAcceptor);
48};
49
50Comm::ConnAcceptor::ConnAcceptor(int aFd, bool accept_many) :
51 AsyncJob("Legacy_Comm::ConnAcceptor"),
0ba55a12
AJ
52 errcode(0),
53 isLimited(0),
54 callSection(NULL),
55 callLevel(NULL),
56 callName(NULL),
57 callDialer(NULL),
4c5518e5 58 theCallback(NULL),
0ba55a12
AJ
59 mayAcceptMore(accept_many)
60{
61 assert(aFd >= 0);
62 assert(isOpen(aFd));
a9870624
AJ
63 conn = new Connection;
64 conn->fd = aFd;
65 // TODO: figure out what the new FD local address is/was/should be.
0ba55a12
AJ
66}
67
a9870624
AJ
68Comm::ConnAcceptor::ConnAcceptor(Comm::ConnectionPointer &newConn, bool accept_many, const char *note) :
69 AsyncJob("Comm::ConnAcceptor"),
0ba55a12
AJ
70 errcode(0),
71 isLimited(0),
72 callSection(NULL),
73 callLevel(NULL),
74 callName(NULL),
75 callDialer(NULL),
4c5518e5 76 theCallback(NULL),
0ba55a12
AJ
77 mayAcceptMore(accept_many)
78{
79 /* open the conn if its not already open */
a9870624
AJ
80 if (!IsConnOpen(newConn)) {
81 newConn->fd = comm_open_listener(SOCK_STREAM, IPPROTO_TCP, conn->local, conn->flags, note);
82 debugs(9, 3, HERE << "Unconnected data socket created on FD " << newConn->fd );
83
84 if (!newConn->isOpen()) {
0ba55a12
AJ
85 debugs(5, DBG_CRITICAL, HERE << "comm_open failed");
86 errcode = -1;
87 return;
88 }
89 }
90
a9870624
AJ
91 assert(IsConnOpen(newConn));
92 conn = newConn;
0ba55a12
AJ
93}
94
a9870624 95Comm::ConnAcceptor::~ConnAcceptor()
04f55905 96{
a9870624 97 swanSong();
0ba55a12
AJ
98}
99
100void
a9870624 101Comm::ConnAcceptor::subscribe(int section, int level, const char *name, CommAcceptCbPtrFun *dialer)
0ba55a12 102{
a9870624 103 debugs(5, 5, HERE << "FD " << conn->fd << " AsyncCall: " << name);
0ba55a12
AJ
104
105 // if this is the first subscription. start listening on the socket.
4c5518e5 106 if (callDialer == NULL && theCallback == NULL)
0ba55a12
AJ
107 setListen();
108
4c5518e5
AJ
109 // remove old subscription. if any.
110 unsubscribe();
111
0ba55a12
AJ
112 // store the subscribed handler details.
113 callSection = section;
114 callLevel = level;
115 safe_free(callName);
116 callName = xstrdup(name);
117 callDialer = dialer;
0ba55a12
AJ
118}
119
4c5518e5 120void
a9870624 121Comm::ConnAcceptor::subscribe(const AsyncCall::Pointer &call)
4c5518e5 122{
a9870624 123 debugs(5, 5, HERE << "FD " << conn->fd << " AsyncCall: " << call);
4c5518e5
AJ
124
125 // remove old subscription. if any.
126 unsubscribe();
127
128 // store new callback subscription
129 theCallback = call;
4c5518e5
AJ
130}
131
0ba55a12 132void
a9870624 133Comm::ConnAcceptor::unsubscribe()
0ba55a12
AJ
134{
135 safe_free(callName);
136 delete callDialer;
137 callDialer = NULL;
4c5518e5 138 theCallback = NULL;
0ba55a12
AJ
139}
140
a9870624
AJ
141void
142Comm::ConnAcceptor::start()
143{
144 debugs(5, 5, HERE << "FD " << conn->fd << " AsyncCall: " << callName);
145
146 Must(IsConnOpen(conn));
147
148 setListen();
149
150 // if no error so far start accepting connections.
151 if (errcode == 0)
152 commSetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
153}
154
155bool
156Comm::ConnAcceptor::doneAll() const
157{
158 if (!IsConnOpen(conn)) {
159 debugs(5,5, HERE << "Done? maybe. FD is closed." << (conn==NULL?"conn=NULL":"") << ", FD " << (conn!=NULL?conn->fd:-999));
160 return AsyncJob::doneAll();
161 }
162
163 if (callDialer == NULL && theCallback == NULL) {
164 debugs(5,5, HERE << "Done? maybe: handlers are gone.");
165 return AsyncJob::doneAll();
166 }
167
168 return false;
169}
170
171void
172Comm::ConnAcceptor::swanSong()
173{
174 debugs(5,5, HERE);
175 unsubscribe();
176 conn = NULL;
177 AsyncJob::swanSong();
178}
179
0ba55a12
AJ
180/**
181 * New-style listen and accept routines
182 *
183 * setListen simply registers our interest in an FD for listening.
184 * The constructor takes a callback to call when an FD has been
185 * accept()ed some time later.
186 */
187void
a9870624 188Comm::ConnAcceptor::setListen()
0ba55a12
AJ
189{
190 errcode = 0; // reset local errno copy.
a9870624
AJ
191 if (listen(conn->fd, Squid_MaxFD >> 2) < 0) {
192 debugs(50, 0, HERE << "listen(FD " << conn->fd << ", " << (Squid_MaxFD >> 2) << "): " << xstrerror());
0ba55a12
AJ
193 errcode = errno;
194 return;
195 }
196
197 if (Config.accept_filter && strcmp(Config.accept_filter, "none") != 0) {
198#ifdef SO_ACCEPTFILTER
199 struct accept_filter_arg afa;
200 bzero(&afa, sizeof(afa));
a9870624 201 debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on FD " << conn->fd);
0ba55a12 202 xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name));
a9870624 203 if (setsockopt(conn->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0)
0ba55a12
AJ
204 debugs(5, DBG_CRITICAL, "SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerror());
205#elif defined(TCP_DEFER_ACCEPT)
206 int seconds = 30;
207 if (strncmp(Config.accept_filter, "data=", 5) == 0)
208 seconds = atoi(Config.accept_filter + 5);
a9870624 209 if (setsockopt(conn->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds)) < 0)
0ba55a12
AJ
210 debugs(5, DBG_CRITICAL, "TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerror());
211#else
212 debugs(5, DBG_CRITICAL, "accept_filter not supported on your OS");
213#endif
214 }
04f55905
AJ
215}
216
217/**
218 * This private callback is called whenever a filedescriptor is ready
219 * to dupe itself and fob off an accept()ed connection
220 *
221 * It will either do that accept operation. Or if there are not enough FD
222 * available to do the clone safely will push the listening FD into a list
223 * of deferred operations. The list gets kicked and the dupe/accept() actually
224 * done later when enough sockets become available.
225 */
226void
a9870624 227Comm::ConnAcceptor::doAccept(int fd, void *data)
04f55905
AJ
228{
229 debugs(5, 2, HERE << "New connection on FD " << fd);
230
a9870624
AJ
231 Must(isOpen(fd));
232 ConnAcceptor *afd = static_cast<ConnAcceptor*>(data);
04f55905
AJ
233
234 if (!okToAccept()) {
235 AcceptLimiter::Instance().defer(afd);
97b8ac39 236 } else {
04f55905
AJ
237 afd->acceptNext();
238 }
a9870624 239 commSetSelect(fd, COMM_SELECT_READ, Comm::ConnAcceptor::doAccept, afd, 0);
04f55905
AJ
240}
241
242bool
a9870624 243Comm::ConnAcceptor::okToAccept()
04f55905
AJ
244{
245 static time_t last_warn = 0;
246
247 if (fdNFree() >= RESERVED_FD)
248 return true;
249
250 if (last_warn + 15 < squid_curtime) {
251 debugs(5, DBG_CRITICAL, "WARNING! Your cache is running out of filedescriptors");
252 last_warn = squid_curtime;
253 }
254
255 return false;
256}
257
971581ee 258void
a9870624 259Comm::ConnAcceptor::acceptOne()
04f55905
AJ
260{
261 /*
262 * We don't worry about running low on FDs here. Instead,
263 * doAccept() will use AcceptLimiter if we reach the limit
264 * there.
265 */
266
267 /* Accept a new connection */
a9870624
AJ
268 Connection *newConnDetails = new Connection();
269 int newfd = oldAccept(*newConnDetails);
04f55905
AJ
270
271 /* Check for errors */
272 if (newfd < 0) {
273
274 if (newfd == COMM_NOMESSAGE) {
275 /* register interest again */
a9870624
AJ
276 debugs(5, 5, HERE << "try later: FD " << conn->fd << " handler: " << callName);
277 commSetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
971581ee 278 return;
04f55905
AJ
279 }
280
281 // A non-recoverable error; notify the caller */
a9870624
AJ
282 debugs(5, 5, HERE << "non-recoverable error: FD " << conn->fd << " handler: " << callName);
283 notify(-1, COMM_ERROR, newConnDetails);
971581ee
AR
284 mayAcceptMore = false;
285 return;
04f55905
AJ
286 }
287
a9870624
AJ
288 debugs(5, 5, HERE << "accepted: FD " << conn->fd <<
289 " newfd: " << newfd << " from: " << newConnDetails->remote <<
0ba55a12 290 " handler: " << callName);
a9870624 291 notify(newfd, COMM_OK, newConnDetails);
04f55905
AJ
292}
293
294void
a9870624 295Comm::ConnAcceptor::acceptNext()
04f55905 296{
a9870624
AJ
297 Must(IsConnOpen(conn));
298 debugs(5, 2, HERE << "connection on FD " << conn->fd);
971581ee 299 acceptOne();
04f55905
AJ
300}
301
302void
a9870624 303Comm::ConnAcceptor::notify(int newfd, comm_err_t flag, const Comm::ConnectionPointer &newConnDetails)
04f55905
AJ
304{
305 // listener socket handlers just abandon the port with COMM_ERR_CLOSING
306 // it should only happen when this object is deleted...
1fc32b95 307 if (flag == COMM_ERR_CLOSING) {
04f55905
AJ
308 return;
309 }
310
0ba55a12
AJ
311 if (callDialer != NULL) {
312 AsyncCall::Pointer call = commCbCall(callSection, callLevel, callName, *callDialer);
313 typedef CommAcceptCbParams Params;
314 Params &params = GetCommParams<Params>(call);
a9870624 315 params.fd = conn->fd;
0ba55a12 316 params.nfd = newfd;
a9870624 317 params.details = newConnDetails;
0ba55a12
AJ
318 params.flag = flag;
319 params.xerrno = errcode;
320 ScheduleCallHere(call);
0ba55a12
AJ
321 }
322 else if (theCallback != NULL) {
04f55905
AJ
323 typedef CommAcceptCbParams Params;
324 Params &params = GetCommParams<Params>(theCallback);
a9870624 325 params.fd = conn->fd;
04f55905 326 params.nfd = newfd;
a9870624 327 params.details = newConnDetails;
1fc32b95
AJ
328 params.flag = flag;
329 params.xerrno = errcode;
04f55905 330 ScheduleCallHere(theCallback);
4c5518e5
AJ
331 // only permit the call to be scheduled once.
332 mayAcceptMore = false;
333 theCallback = NULL;
04f55905
AJ
334 }
335}
336
337/**
97b8ac39 338 * accept() and process
273f66c4
AJ
339 * Wait for an incoming connection on FD.
340 */
04f55905 341int
a9870624 342Comm::ConnAcceptor::oldAccept(Comm::Connection &details)
04f55905
AJ
343{
344 PROF_start(comm_accept);
345 statCounter.syscalls.sock.accepts++;
346 int sock;
347 struct addrinfo *gai = NULL;
cfd66529 348 details.local.InitAddrInfo(gai);
04f55905 349
1fc32b95 350 errcode = 0; // reset local errno copy.
a9870624 351 if ((sock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
1fc32b95 352 errcode = errno; // store last accept errno locally.
04f55905 353
cfd66529 354 details.local.FreeAddrInfo(gai);
04f55905
AJ
355
356 PROF_stop(comm_accept);
357
358 if (ignoreErrno(errno)) {
a9870624 359 debugs(50, 5, HERE << "FD " << conn->fd << ": " << xstrerror());
04f55905
AJ
360 return COMM_NOMESSAGE;
361 } else if (ENFILE == errno || EMFILE == errno) {
a9870624 362 debugs(50, 3, HERE << "FD " << conn->fd << ": " << xstrerror());
04f55905
AJ
363 return COMM_ERROR;
364 } else {
a9870624 365 debugs(50, 1, HERE << "FD " << conn->fd << ": " << xstrerror());
04f55905
AJ
366 return COMM_ERROR;
367 }
368 }
369
a9870624 370 Must(sock >= 0);
903198a7 371 details.fd = sock;
cfd66529 372 details.remote = *gai;
04f55905 373
5511c78a 374 if ( Config.client_ip_max_connections >= 0) {
cfd66529
AJ
375 if (clientdbEstablished(details.remote, 0) > Config.client_ip_max_connections) {
376 debugs(50, DBG_IMPORTANT, "WARNING: " << details.remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
377 details.local.FreeAddrInfo(gai);
5511c78a
AJ
378 return COMM_ERROR;
379 }
380 }
381
903198a7 382 // lookup the local-end details of this new connection
cfd66529 383 details.local.InitAddrInfo(gai);
cfd66529 384 details.local.SetEmpty();
04f55905 385 getsockname(sock, gai->ai_addr, &gai->ai_addrlen);
cfd66529 386 details.local = *gai;
903198a7 387 details.local.FreeAddrInfo(gai);
04f55905
AJ
388
389 /* fdstat update */
903198a7
AJ
390 // XXX : these are not all HTTP requests. use a note about type and ip:port details.
391 // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
04f55905
AJ
392 fd_open(sock, FD_SOCKET, "HTTP Request");
393
394 fdd_table[sock].close_file = NULL;
395 fdd_table[sock].close_line = 0;
396
397 fde *F = &fd_table[sock];
cfd66529
AJ
398 details.remote.NtoA(F->ipaddr,MAX_IPSTRLEN);
399 F->remote_port = details.remote.GetPort();
903198a7 400 F->local_addr = details.local;
7803fd54 401 F->sock_family = details.local.IsIPv6()?AF_INET6:AF_INET;
04f55905 402
903198a7
AJ
403 // set socket flags
404 commSetCloseOnExec(sock);
04f55905
AJ
405 commSetNonBlocking(sock);
406
407 /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
a9870624 408 F->flags.transparent = fd_table[conn->fd].flags.transparent;
04f55905
AJ
409
410 PROF_stop(comm_accept);
411 return sock;
412}