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