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 "anyp/PortCfg.h"
37 #include "base/TextException.h"
38 #include "client_db.h"
39 #include "comm/AcceptLimiter.h"
40 #include "comm/comm_internal.h"
41 #include "comm/Connection.h"
42 #include "comm/Loops.h"
43 #include "comm/TcpAcceptor.h"
44 #include "CommCalls.h"
45 #include "eui/Config.h"
49 #include "ip/Intercept.h"
50 #include "MasterXaction.h"
51 #include "profiler/Profiler.h"
52 #include "SquidConfig.h"
53 #include "SquidTime.h"
54 #include "StatCounters.h"
59 #ifdef HAVE_NETINET_TCP_H
60 // required for accept_filter to build.
61 #include <netinet/tcp.h>
64 CBDATA_NAMESPACED_CLASS_INIT(Comm
, TcpAcceptor
);
66 Comm::TcpAcceptor::TcpAcceptor(const Comm::ConnectionPointer
&newConn
, const char *note
, const Subscription::Pointer
&aSub
) :
67 AsyncJob("Comm::TcpAcceptor"),
75 Comm::TcpAcceptor::subscribe(const Subscription::Pointer
&aSub
)
77 debugs(5, 5, HERE
<< status() << " AsyncCall Subscription: " << aSub
);
78 unsubscribe("subscription change");
83 Comm::TcpAcceptor::unsubscribe(const char *reason
)
85 debugs(5, 5, HERE
<< status() << " AsyncCall Subscription " << theCallSub
<< " removed: " << reason
);
90 Comm::TcpAcceptor::start()
92 debugs(5, 5, HERE
<< status() << " AsyncCall Subscription: " << theCallSub
);
94 Must(IsConnOpen(conn
));
98 // if no error so far start accepting connections.
100 SetSelect(conn
->fd
, COMM_SELECT_READ
, doAccept
, this, 0);
104 Comm::TcpAcceptor::doneAll() const
106 // stop when FD is closed
107 if (!IsConnOpen(conn
)) {
108 return AsyncJob::doneAll();
111 // stop when handlers are gone
112 if (theCallSub
== NULL
) {
113 return AsyncJob::doneAll();
116 // open FD with handlers...keep accepting.
121 Comm::TcpAcceptor::swanSong()
124 unsubscribe("swanSong");
126 AcceptLimiter::Instance().removeDead(this);
127 AsyncJob::swanSong();
131 Comm::TcpAcceptor::status() const
134 return "[nil connection]";
136 static char ipbuf
[MAX_IPSTRLEN
] = {'\0'};
137 if (ipbuf
[0] == '\0')
138 conn
->local
.toHostStr(ipbuf
, MAX_IPSTRLEN
);
142 buf
.Printf(" FD %d, %s",conn
->fd
, ipbuf
);
144 const char *jobStatus
= AsyncJob::status();
145 buf
.append(jobStatus
, strlen(jobStatus
));
147 return buf
.content();
151 * New-style listen and accept routines
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.
158 Comm::TcpAcceptor::setListen()
160 errcode
= 0; // reset local errno copy.
161 if (listen(conn
->fd
, Squid_MaxFD
>> 2) < 0) {
162 debugs(50, DBG_CRITICAL
, "ERROR: listen(" << status() << ", " << (Squid_MaxFD
>> 2) << "): " << xstrerror());
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 " << conn
);
172 xstrncpy(afa
.af_name
, Config
.accept_filter
, sizeof(afa
.af_name
));
173 if (setsockopt(conn
->fd
, SOL_SOCKET
, SO_ACCEPTFILTER
, &afa
, sizeof(afa
)) < 0)
174 debugs(5, DBG_CRITICAL
, "WARNING: SO_ACCEPTFILTER '" << Config
.accept_filter
<< "': '" << xstrerror());
175 #elif defined(TCP_DEFER_ACCEPT)
177 if (strncmp(Config
.accept_filter
, "data=", 5) == 0)
178 seconds
= atoi(Config
.accept_filter
+ 5);
179 if (setsockopt(conn
->fd
, IPPROTO_TCP
, TCP_DEFER_ACCEPT
, &seconds
, sizeof(seconds
)) < 0)
180 debugs(5, DBG_CRITICAL
, "WARNING: TCP_DEFER_ACCEPT '" << Config
.accept_filter
<< "': '" << xstrerror());
182 debugs(5, DBG_CRITICAL
, "WARNING: accept_filter not supported on your OS");
188 * This private callback is called whenever a filedescriptor is ready
189 * to dupe itself and fob off an accept()ed connection
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.
197 Comm::TcpAcceptor::doAccept(int fd
, void *data
)
200 debugs(5, 2, HERE
<< "New connection on FD " << fd
);
203 TcpAcceptor
*afd
= static_cast<TcpAcceptor
*>(data
);
206 AcceptLimiter::Instance().defer(afd
);
210 SetSelect(fd
, COMM_SELECT_READ
, Comm::TcpAcceptor::doAccept
, afd
, 0);
212 } catch (const std::exception
&e
) {
213 fatalf("FATAL: error while accepting new client connection: %s\n", e
.what());
215 fatal("FATAL: error while accepting new client connection: [unkown]\n");
220 Comm::TcpAcceptor::okToAccept()
222 static time_t last_warn
= 0;
224 if (fdNFree() >= RESERVED_FD
)
227 if (last_warn
+ 15 < squid_curtime
) {
228 debugs(5, DBG_CRITICAL
, "WARNING! Your cache is running out of filedescriptors");
229 last_warn
= squid_curtime
;
236 Comm::TcpAcceptor::acceptOne()
239 * We don't worry about running low on FDs here. Instead,
240 * doAccept() will use AcceptLimiter if we reach the limit
244 /* Accept a new connection */
245 ConnectionPointer newConnDetails
= new Connection();
246 const comm_err_t flag
= oldAccept(newConnDetails
);
248 /* Check for errors */
249 if (!newConnDetails
->isOpen()) {
251 if (flag
== COMM_NOMESSAGE
) {
252 /* register interest again */
253 debugs(5, 5, HERE
<< "try later: " << conn
<< " handler Subscription: " << theCallSub
);
254 SetSelect(conn
->fd
, COMM_SELECT_READ
, doAccept
, this, 0);
258 // A non-recoverable error; notify the caller */
259 debugs(5, 5, HERE
<< "non-recoverable error:" << status() << " handler Subscription: " << theCallSub
);
260 notify(flag
, newConnDetails
);
261 mustStop("Listener socket closed");
265 debugs(5, 5, HERE
<< "Listener: " << conn
<<
266 " accepted new connection " << newConnDetails
<<
267 " handler Subscription: " << theCallSub
);
268 notify(flag
, newConnDetails
);
272 Comm::TcpAcceptor::acceptNext()
274 Must(IsConnOpen(conn
));
275 debugs(5, 2, HERE
<< "connection on " << conn
);
280 Comm::TcpAcceptor::notify(const comm_err_t flag
, const Comm::ConnectionPointer
&newConnDetails
) const
282 // listener socket handlers just abandon the port with COMM_ERR_CLOSING
283 // it should only happen when this object is deleted...
284 if (flag
== COMM_ERR_CLOSING
) {
288 if (theCallSub
!= NULL
) {
289 AsyncCall::Pointer call
= theCallSub
->callback();
290 CommAcceptCbParams
¶ms
= GetCommParams
<CommAcceptCbParams
>(call
);
291 params
.xaction
= new MasterXaction
;
292 params
.xaction
->squidPort
= static_cast<AnyP::PortCfg
*>(params
.data
);
293 params
.fd
= conn
->fd
;
294 params
.conn
= params
.xaction
->tcpClient
= newConnDetails
;
296 params
.xerrno
= errcode
;
297 ScheduleCallHere(call
);
302 * accept() and process
303 * Wait for an incoming connection on our listener socket.
305 * \retval COMM_OK success. details parameter filled.
306 * \retval COMM_NOMESSAGE attempted accept() but nothing useful came in.
307 * \retval COMM_ERROR an outright failure occured.
308 * Or if this client has too many connections already.
311 Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer
&details
)
313 PROF_start(comm_accept
);
314 ++statCounter
.syscalls
.sock
.accepts
;
316 struct addrinfo
*gai
= NULL
;
317 Ip::Address::InitAddrInfo(gai
);
319 errcode
= 0; // reset local errno copy.
320 if ((sock
= accept(conn
->fd
, gai
->ai_addr
, &gai
->ai_addrlen
)) < 0) {
321 errcode
= errno
; // store last accept errno locally.
323 Ip::Address::FreeAddrInfo(gai
);
325 PROF_stop(comm_accept
);
327 if (ignoreErrno(errno
)) {
328 debugs(50, 5, HERE
<< status() << ": " << xstrerror());
329 return COMM_NOMESSAGE
;
330 } else if (ENFILE
== errno
|| EMFILE
== errno
) {
331 debugs(50, 3, HERE
<< status() << ": " << xstrerror());
334 debugs(50, DBG_IMPORTANT
, HERE
<< status() << ": " << xstrerror());
341 details
->remote
= *gai
;
343 if ( Config
.client_ip_max_connections
>= 0) {
344 if (clientdbEstablished(details
->remote
, 0) > Config
.client_ip_max_connections
) {
345 debugs(50, DBG_IMPORTANT
, "WARNING: " << details
->remote
<< " attempting more than " << Config
.client_ip_max_connections
<< " connections.");
346 Ip::Address::FreeAddrInfo(gai
);
351 // lookup the local-end details of this new connection
352 Ip::Address::InitAddrInfo(gai
);
353 details
->local
.setEmpty();
354 if (getsockname(sock
, gai
->ai_addr
, &gai
->ai_addrlen
) != 0) {
355 debugs(50, DBG_IMPORTANT
, "ERROR: getsockname() failed to locate local-IP on " << details
<< ": " << xstrerror());
356 Ip::Address::FreeAddrInfo(gai
);
359 details
->local
= *gai
;
360 Ip::Address::FreeAddrInfo(gai
);
363 // XXX : these are not all HTTP requests. use a note about type and ip:port details->
364 // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
365 fd_open(sock
, FD_SOCKET
, "HTTP Request");
367 fdd_table
[sock
].close_file
= NULL
;
368 fdd_table
[sock
].close_line
= 0;
370 fde
*F
= &fd_table
[sock
];
371 details
->remote
.toStr(F
->ipaddr
,MAX_IPSTRLEN
);
372 F
->remote_port
= details
->remote
.port();
373 F
->local_addr
= details
->local
;
374 F
->sock_family
= details
->local
.isIPv6()?AF_INET6
:AF_INET
;
377 commSetCloseOnExec(sock
);
378 commSetNonBlocking(sock
);
380 /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
381 F
->flags
.transparent
= fd_table
[conn
->fd
].flags
.transparent
; // XXX: can we remove this line yet?
383 // Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
384 if (conn
->flags
&(COMM_TRANSPARENT
|COMM_INTERCEPTION
) && !Ip::Interceptor
.Lookup(details
, conn
)) {
390 if (Eui::TheConfig
.euiLookup
) {
391 if (conn
->remote
.isIPv4()) {
392 conn
->remoteEui48
.lookup(conn
->remote
);
393 } else if (conn
->remote
.isIPv6()) {
394 conn
->remoteEui64
.lookup(conn
->remote
);
399 PROF_stop(comm_accept
);