2 * DEBUG: section 05 Listener Socket Handler
3 * AUTHOR: Harvest Derived
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
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.
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.
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.
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.
32 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
36 #include "CommCalls.h"
37 #include "comm/AcceptLimiter.h"
38 #include "comm/comm_internal.h"
39 #include "comm/ListenStateData.h"
40 #include "ConnectionDetail.h"
43 #include "SquidTime.h"
46 * New-style listen and accept routines
48 * Listen simply registers our interest in an FD for listening,
49 * and accept takes a callback to call when an FD has been
53 Comm::ListenStateData::setListen()
57 if ((x
= listen(fd
, Squid_MaxFD
>> 2)) < 0) {
58 debugs(50, 0, HERE
<< "listen(FD " << fd
<< ", " << (Squid_MaxFD
>> 2) << "): " << xstrerror());
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
));
71 debugs(5, DBG_CRITICAL
, "SO_ACCEPTFILTER '" << Config
.accept_filter
<< "': '" << xstrerror());
72 #elif defined(TCP_DEFER_ACCEPT)
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
));
78 debugs(5, DBG_CRITICAL
, "TCP_DEFER_ACCEPT '" << Config
.accept_filter
<< "': '" << xstrerror());
80 debugs(5, DBG_CRITICAL
, "accept_filter not supported on your OS");
85 Comm::ListenStateData::ListenStateData(int aFd
, AsyncCall::Pointer
&call
, bool accept_many
) :
88 mayAcceptMore(accept_many
)
91 debugs(5, 5, HERE
<< "FD " << fd
<< " AsyncCall: " << call
);
94 commSetSelect(fd
, COMM_SELECT_READ
, doAccept
, this, 0);
97 Comm::ListenStateData::~ListenStateData()
104 * This private callback is called whenever a filedescriptor is ready
105 * to dupe itself and fob off an accept()ed connection
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.
113 Comm::ListenStateData::doAccept(int fd
, void *data
)
115 debugs(5, 2, HERE
<< "New connection on FD " << fd
);
118 ListenStateData
*afd
= static_cast<ListenStateData
*>(data
);
121 AcceptLimiter::Instance().defer(afd
);
125 commSetSelect(fd
, COMM_SELECT_READ
, Comm::ListenStateData::doAccept
, afd
, 0);
129 Comm::ListenStateData::okToAccept()
131 static time_t last_warn
= 0;
133 if (fdNFree() >= RESERVED_FD
)
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
;
145 Comm::ListenStateData::acceptOne()
148 * We don't worry about running low on FDs here. Instead,
149 * doAccept() will use AcceptLimiter if we reach the limit
153 /* Accept a new connection */
154 ConnectionDetail connDetails
;
155 int newfd
= oldAccept(connDetails
);
157 /* Check for errors */
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);
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;
174 debugs(5, 5, HERE
<< "accepted: FD " << fd
<<
175 " newfd: " << newfd
<< " from: " << connDetails
.peer
<<
176 " handler: " << theCallback
);
177 notify(newfd
, COMM_OK
, 0, connDetails
);
181 Comm::ListenStateData::acceptNext()
184 debugs(5, 2, HERE
<< "connection on FD " << fd
);
189 Comm::ListenStateData::notify(int newfd
, comm_err_t errcode
, int xerrno
, const ConnectionDetail
&connDetails
)
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
) {
197 if (theCallback
!= NULL
) {
198 typedef CommAcceptCbParams Params
;
199 Params
¶ms
= GetCommParams
<Params
>(theCallback
);
202 params
.details
= connDetails
;
203 params
.flag
= errcode
;
204 params
.xerrno
= xerrno
;
205 ScheduleCallHere(theCallback
);
212 * accept() and process
213 * Wait for an incoming connection on FD.
216 Comm::ListenStateData::oldAccept(ConnectionDetail
&details
)
218 PROF_start(comm_accept
);
219 statCounter
.syscalls
.sock
.accepts
++;
221 struct addrinfo
*gai
= NULL
;
222 details
.me
.InitAddrInfo(gai
);
224 if ((sock
= accept(fd
, gai
->ai_addr
, &gai
->ai_addrlen
)) < 0) {
226 details
.me
.FreeAddrInfo(gai
);
228 PROF_stop(comm_accept
);
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());
237 debugs(50, 1, HERE
<< "FD " << fd
<< ": " << xstrerror());
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
);
252 details
.me
.InitAddrInfo(gai
);
254 details
.me
.SetEmpty();
255 getsockname(sock
, gai
->ai_addr
, &gai
->ai_addrlen
);
258 commSetCloseOnExec(sock
);
261 fd_open(sock
, FD_SOCKET
, "HTTP Request");
263 fdd_table
[sock
].close_file
= NULL
;
264 fdd_table
[sock
].close_line
= 0;
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());
271 F
->sock_family
= AF_INET
;
273 F
->sock_family
= details
.me
.IsIPv4()?AF_INET
:AF_INET6
;
275 details
.me
.FreeAddrInfo(gai
);
277 commSetNonBlocking(sock
);
279 /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
280 F
->flags
.transparent
= fd_table
[fd
].flags
.transparent
;
282 PROF_stop(comm_accept
);