]> git.ipfire.org Git - thirdparty/squid.git/blob - src/comm/ListenStateData.cc
4e5a304831b5c74806dde63863c3f5ada73f5b95
[thirdparty/squid.git] / src / comm / ListenStateData.cc
1 /*
2 * DEBUG: section 05 Listener Socket Handler
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"
38 #include "comm/comm_internal.h"
39 #include "comm/ListenStateData.h"
40 #include "ConnectionDetail.h"
41 #include "fde.h"
42 #include "protos.h"
43 #include "SquidTime.h"
44
45 /**
46 * New-style listen and accept routines
47 *
48 * Listen simply registers our interest in an FD for listening,
49 * and accept takes a callback to call when an FD has been
50 * accept()ed.
51 */
52 void
53 Comm::ListenStateData::setListen()
54 {
55 int x;
56
57 if ((x = listen(fd, Squid_MaxFD >> 2)) < 0) {
58 debugs(50, 0, HERE << "listen(FD " << fd << ", " << (Squid_MaxFD >> 2) << "): " << xstrerror());
59 errcode = x;
60 return;
61 }
62
63 if (Config.accept_filter && strcmp(Config.accept_filter, "none") != 0) {
64 #ifdef SO_ACCEPTFILTER
65 struct accept_filter_arg afa;
66 bzero(&afa, sizeof(afa));
67 debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on FD " << fd);
68 xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name));
69 x = setsockopt(fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
70 if (x < 0)
71 debugs(5, DBG_CRITICAL, "SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerror());
72 #elif defined(TCP_DEFER_ACCEPT)
73 int seconds = 30;
74 if (strncmp(Config.accept_filter, "data=", 5) == 0)
75 seconds = atoi(Config.accept_filter + 5);
76 x = setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds));
77 if (x < 0)
78 debugs(5, DBG_CRITICAL, "TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerror());
79 #else
80 debugs(5, DBG_CRITICAL, "accept_filter not supported on your OS");
81 #endif
82 }
83 }
84
85 Comm::ListenStateData::ListenStateData(int aFd, AsyncCall::Pointer &call, bool accept_many) :
86 fd(aFd),
87 theCallback(call),
88 mayAcceptMore(accept_many)
89 {
90 assert(aFd >= 0);
91 debugs(5, 5, HERE << "FD " << fd << " AsyncCall: " << call);
92 assert(isOpen(aFd));
93 setListen();
94 commSetSelect(fd, COMM_SELECT_READ, doAccept, this, 0);
95 }
96
97 Comm::ListenStateData::~ListenStateData()
98 {
99 comm_close(fd);
100 fd = -1;
101 }
102
103 /**
104 * This private callback is called whenever a filedescriptor is ready
105 * to dupe itself and fob off an accept()ed connection
106 *
107 * It will either do that accept operation. Or if there are not enough FD
108 * available to do the clone safely will push the listening FD into a list
109 * of deferred operations. The list gets kicked and the dupe/accept() actually
110 * done later when enough sockets become available.
111 */
112 void
113 Comm::ListenStateData::doAccept(int fd, void *data)
114 {
115 debugs(5, 2, HERE << "New connection on FD " << fd);
116
117 assert(isOpen(fd));
118 ListenStateData *afd = static_cast<ListenStateData*>(data);
119
120 if (!okToAccept()) {
121 AcceptLimiter::Instance().defer(afd);
122 } else {
123 afd->acceptNext();
124 }
125 commSetSelect(fd, COMM_SELECT_READ, Comm::ListenStateData::doAccept, afd, 0);
126 }
127
128 bool
129 Comm::ListenStateData::okToAccept()
130 {
131 static time_t last_warn = 0;
132
133 if (fdNFree() >= RESERVED_FD)
134 return true;
135
136 if (last_warn + 15 < squid_curtime) {
137 debugs(5, DBG_CRITICAL, "WARNING! Your cache is running out of filedescriptors");
138 last_warn = squid_curtime;
139 }
140
141 return false;
142 }
143
144 void
145 Comm::ListenStateData::acceptOne()
146 {
147 /*
148 * We don't worry about running low on FDs here. Instead,
149 * doAccept() will use AcceptLimiter if we reach the limit
150 * there.
151 */
152
153 /* Accept a new connection */
154 ConnectionDetail connDetails;
155 int newfd = oldAccept(connDetails);
156
157 /* Check for errors */
158 if (newfd < 0) {
159
160 if (newfd == COMM_NOMESSAGE) {
161 /* register interest again */
162 debugs(5, 5, HERE << "try later: FD " << fd << " handler: " << theCallback);
163 commSetSelect(fd, COMM_SELECT_READ, doAccept, this, 0);
164 return;
165 }
166
167 // A non-recoverable error; notify the caller */
168 debugs(5, 5, HERE << "non-recoverable error: FD " << fd << " handler: " << theCallback);
169 notify(-1, COMM_ERROR, errno, connDetails);
170 mayAcceptMore = false;
171 return;
172 }
173
174 debugs(5, 5, HERE << "accepted: FD " << fd <<
175 " newfd: " << newfd << " from: " << connDetails.peer <<
176 " handler: " << theCallback);
177 notify(newfd, COMM_OK, 0, connDetails);
178 }
179
180 void
181 Comm::ListenStateData::acceptNext()
182 {
183 assert(isOpen(fd));
184 debugs(5, 2, HERE << "connection on FD " << fd);
185 acceptOne();
186 }
187
188 void
189 Comm::ListenStateData::notify(int newfd, comm_err_t errcode, int xerrno, const ConnectionDetail &connDetails)
190 {
191 // listener socket handlers just abandon the port with COMM_ERR_CLOSING
192 // it should only happen when this object is deleted...
193 if (errcode == COMM_ERR_CLOSING) {
194 return;
195 }
196
197 if (theCallback != NULL) {
198 typedef CommAcceptCbParams Params;
199 Params &params = GetCommParams<Params>(theCallback);
200 params.fd = fd;
201 params.nfd = newfd;
202 params.details = connDetails;
203 params.flag = errcode;
204 params.xerrno = xerrno;
205 ScheduleCallHere(theCallback);
206 if (!mayAcceptMore)
207 theCallback = NULL;
208 }
209 }
210
211 /**
212 * accept() and process
213 * Wait for an incoming connection on FD.
214 */
215 int
216 Comm::ListenStateData::oldAccept(ConnectionDetail &details)
217 {
218 PROF_start(comm_accept);
219 statCounter.syscalls.sock.accepts++;
220 int sock;
221 struct addrinfo *gai = NULL;
222 details.me.InitAddrInfo(gai);
223
224 if ((sock = accept(fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
225
226 details.me.FreeAddrInfo(gai);
227
228 PROF_stop(comm_accept);
229
230 if (ignoreErrno(errno)) {
231 debugs(50, 5, HERE << "FD " << fd << ": " << xstrerror());
232 return COMM_NOMESSAGE;
233 } else if (ENFILE == errno || EMFILE == errno) {
234 debugs(50, 3, HERE << "FD " << fd << ": " << xstrerror());
235 return COMM_ERROR;
236 } else {
237 debugs(50, 1, HERE << "FD " << fd << ": " << xstrerror());
238 return COMM_ERROR;
239 }
240 }
241
242 details.peer = *gai;
243
244 if ( Config.client_ip_max_connections >= 0) {
245 if (clientdbEstablished(details.peer, 0) > Config.client_ip_max_connections) {
246 debugs(50, DBG_IMPORTANT, "WARNING: " << details.peer << " attempting more than " << Config.client_ip_max_connections << " connections.");
247 details.me.FreeAddrInfo(gai);
248 return COMM_ERROR;
249 }
250 }
251
252 details.me.InitAddrInfo(gai);
253
254 details.me.SetEmpty();
255 getsockname(sock, gai->ai_addr, &gai->ai_addrlen);
256 details.me = *gai;
257
258 commSetCloseOnExec(sock);
259
260 /* fdstat update */
261 fd_open(sock, FD_SOCKET, "HTTP Request");
262
263 fdd_table[sock].close_file = NULL;
264 fdd_table[sock].close_line = 0;
265
266 fde *F = &fd_table[sock];
267 details.peer.NtoA(F->ipaddr,MAX_IPSTRLEN);
268 F->remote_port = details.peer.GetPort();
269 F->local_addr.SetPort(details.me.GetPort());
270 #if USE_IPV6
271 F->sock_family = AF_INET;
272 #else
273 F->sock_family = details.me.IsIPv4()?AF_INET:AF_INET6;
274 #endif
275 details.me.FreeAddrInfo(gai);
276
277 commSetNonBlocking(sock);
278
279 /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
280 F->flags.transparent = fd_table[fd].flags.transparent;
281
282 PROF_stop(comm_accept);
283 return sock;
284 }