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