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