]>
Commit | Line | Data |
---|---|---|
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 | ||
582c2af2 | 35 | #include "squid.h" |
94bfd31f | 36 | #include "anyp/PortCfg.h" |
a9870624 | 37 | #include "base/TextException.h" |
95e6d864 | 38 | #include "client_db.h" |
04f55905 AJ |
39 | #include "comm/AcceptLimiter.h" |
40 | #include "comm/comm_internal.h" | |
5b67dfa4 | 41 | #include "comm/Connection.h" |
d841c88d | 42 | #include "comm/Loops.h" |
cbff89ba | 43 | #include "comm/TcpAcceptor.h" |
602d9612 | 44 | #include "CommCalls.h" |
83b62d3f | 45 | #include "eui/Config.h" |
c4ad1349 | 46 | #include "fd.h" |
04f55905 | 47 | #include "fde.h" |
67679543 | 48 | #include "globals.h" |
40d34a62 | 49 | #include "ip/Intercept.h" |
94bfd31f | 50 | #include "MasterXaction.h" |
582c2af2 | 51 | #include "profiler/Profiler.h" |
4d5904f7 | 52 | #include "SquidConfig.h" |
04f55905 | 53 | #include "SquidTime.h" |
e4f1fdae | 54 | #include "StatCounters.h" |
04f55905 | 55 | |
21d845b1 FC |
56 | #if HAVE_ERRNO_H |
57 | #include <errno.h> | |
58 | #endif | |
f842580f AJ |
59 | #ifdef HAVE_NETINET_TCP_H |
60 | // required for accept_filter to build. | |
61 | #include <netinet/tcp.h> | |
62 | #endif | |
21d845b1 | 63 | |
f842580f | 64 | CBDATA_NAMESPACED_CLASS_INIT(Comm, TcpAcceptor); |
a9870624 | 65 | |
8bbb16e3 | 66 | Comm::TcpAcceptor::TcpAcceptor(const Comm::ConnectionPointer &newConn, const char *note, const Subscription::Pointer &aSub) : |
cbff89ba | 67 | AsyncJob("Comm::TcpAcceptor"), |
0ba55a12 AJ |
68 | errcode(0), |
69 | isLimited(0), | |
5b67dfa4 AJ |
70 | theCallSub(aSub), |
71 | conn(newConn) | |
cbff89ba | 72 | {} |
0ba55a12 AJ |
73 | |
74 | void | |
cbff89ba | 75 | Comm::TcpAcceptor::subscribe(const Subscription::Pointer &aSub) |
0ba55a12 | 76 | { |
cbff89ba | 77 | debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << aSub); |
5b67dfa4 AJ |
78 | unsubscribe("subscription change"); |
79 | theCallSub = aSub; | |
4c5518e5 AJ |
80 | } |
81 | ||
0ba55a12 | 82 | void |
cbff89ba | 83 | Comm::TcpAcceptor::unsubscribe(const char *reason) |
0ba55a12 | 84 | { |
cbff89ba | 85 | debugs(5, 5, HERE << status() << " AsyncCall Subscription " << theCallSub << " removed: " << reason); |
5b67dfa4 | 86 | theCallSub = NULL; |
0ba55a12 AJ |
87 | } |
88 | ||
a9870624 | 89 | void |
cbff89ba | 90 | Comm::TcpAcceptor::start() |
a9870624 | 91 | { |
cbff89ba | 92 | debugs(5, 5, HERE << status() << " AsyncCall Subscription: " << theCallSub); |
a9870624 AJ |
93 | |
94 | Must(IsConnOpen(conn)); | |
95 | ||
96 | setListen(); | |
97 | ||
98 | // if no error so far start accepting connections. | |
99 | if (errcode == 0) | |
8bbb16e3 | 100 | SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0); |
a9870624 AJ |
101 | } |
102 | ||
103 | bool | |
cbff89ba | 104 | Comm::TcpAcceptor::doneAll() const |
a9870624 | 105 | { |
6f8536c0 | 106 | // stop when FD is closed |
a9870624 | 107 | if (!IsConnOpen(conn)) { |
a9870624 AJ |
108 | return AsyncJob::doneAll(); |
109 | } | |
110 | ||
5b67dfa4 AJ |
111 | // stop when handlers are gone |
112 | if (theCallSub == NULL) { | |
a9870624 AJ |
113 | return AsyncJob::doneAll(); |
114 | } | |
115 | ||
5b67dfa4 | 116 | // open FD with handlers...keep accepting. |
a9870624 AJ |
117 | return false; |
118 | } | |
119 | ||
120 | void | |
cbff89ba | 121 | Comm::TcpAcceptor::swanSong() |
a9870624 AJ |
122 | { |
123 | debugs(5,5, HERE); | |
5b67dfa4 | 124 | unsubscribe("swanSong"); |
a9870624 | 125 | conn = NULL; |
5b67dfa4 | 126 | AcceptLimiter::Instance().removeDead(this); |
a9870624 AJ |
127 | AsyncJob::swanSong(); |
128 | } | |
129 | ||
cbff89ba AJ |
130 | const char * |
131 | Comm::TcpAcceptor::status() const | |
132 | { | |
8bbb16e3 AJ |
133 | if (conn == NULL) |
134 | return "[nil connection]"; | |
135 | ||
cbff89ba AJ |
136 | static char ipbuf[MAX_IPSTRLEN] = {'\0'}; |
137 | if (ipbuf[0] == '\0') | |
4dd643d5 | 138 | conn->local.toHostStr(ipbuf, MAX_IPSTRLEN); |
cbff89ba AJ |
139 | |
140 | static MemBuf buf; | |
141 | buf.reset(); | |
8bbb16e3 | 142 | buf.Printf(" FD %d, %s",conn->fd, ipbuf); |
cbff89ba AJ |
143 | |
144 | const char *jobStatus = AsyncJob::status(); | |
145 | buf.append(jobStatus, strlen(jobStatus)); | |
146 | ||
147 | return buf.content(); | |
148 | } | |
149 | ||
0ba55a12 AJ |
150 | /** |
151 | * New-style listen and accept routines | |
152 | * | |
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. | |
156 | */ | |
157 | void | |
cbff89ba | 158 | Comm::TcpAcceptor::setListen() |
0ba55a12 AJ |
159 | { |
160 | errcode = 0; // reset local errno copy. | |
a9870624 | 161 | if (listen(conn->fd, Squid_MaxFD >> 2) < 0) { |
cbff89ba | 162 | debugs(50, DBG_CRITICAL, "ERROR: listen(" << status() << ", " << (Squid_MaxFD >> 2) << "): " << xstrerror()); |
0ba55a12 AJ |
163 | errcode = errno; |
164 | return; | |
165 | } | |
166 | ||
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)); | |
5b67dfa4 | 171 | debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on " << conn); |
0ba55a12 | 172 | xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name)); |
a9870624 | 173 | if (setsockopt(conn->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0) |
5b67dfa4 | 174 | debugs(5, DBG_CRITICAL, "WARNING: SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerror()); |
0ba55a12 AJ |
175 | #elif defined(TCP_DEFER_ACCEPT) |
176 | int seconds = 30; | |
177 | if (strncmp(Config.accept_filter, "data=", 5) == 0) | |
178 | seconds = atoi(Config.accept_filter + 5); | |
a9870624 | 179 | if (setsockopt(conn->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds)) < 0) |
5b67dfa4 | 180 | debugs(5, DBG_CRITICAL, "WARNING: TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerror()); |
0ba55a12 | 181 | #else |
5b67dfa4 | 182 | debugs(5, DBG_CRITICAL, "WARNING: accept_filter not supported on your OS"); |
0ba55a12 AJ |
183 | #endif |
184 | } | |
04f55905 AJ |
185 | } |
186 | ||
187 | /** | |
188 | * This private callback is called whenever a filedescriptor is ready | |
189 | * to dupe itself and fob off an accept()ed connection | |
190 | * | |
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. | |
195 | */ | |
196 | void | |
cbff89ba | 197 | Comm::TcpAcceptor::doAccept(int fd, void *data) |
04f55905 | 198 | { |
5b67dfa4 AJ |
199 | try { |
200 | debugs(5, 2, HERE << "New connection on FD " << fd); | |
04f55905 | 201 | |
5b67dfa4 | 202 | Must(isOpen(fd)); |
cbff89ba | 203 | TcpAcceptor *afd = static_cast<TcpAcceptor*>(data); |
04f55905 | 204 | |
5b67dfa4 AJ |
205 | if (!okToAccept()) { |
206 | AcceptLimiter::Instance().defer(afd); | |
207 | } else { | |
208 | afd->acceptNext(); | |
209 | } | |
cbff89ba | 210 | SetSelect(fd, COMM_SELECT_READ, Comm::TcpAcceptor::doAccept, afd, 0); |
5b67dfa4 | 211 | |
db98b2bd | 212 | } catch (const std::exception &e) { |
cbff89ba | 213 | fatalf("FATAL: error while accepting new client connection: %s\n", e.what()); |
db98b2bd | 214 | } catch (...) { |
5b67dfa4 | 215 | fatal("FATAL: error while accepting new client connection: [unkown]\n"); |
04f55905 | 216 | } |
04f55905 AJ |
217 | } |
218 | ||
219 | bool | |
cbff89ba | 220 | Comm::TcpAcceptor::okToAccept() |
04f55905 AJ |
221 | { |
222 | static time_t last_warn = 0; | |
223 | ||
224 | if (fdNFree() >= RESERVED_FD) | |
225 | return true; | |
226 | ||
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; | |
230 | } | |
231 | ||
232 | return false; | |
233 | } | |
234 | ||
971581ee | 235 | void |
cbff89ba | 236 | Comm::TcpAcceptor::acceptOne() |
04f55905 AJ |
237 | { |
238 | /* | |
239 | * We don't worry about running low on FDs here. Instead, | |
240 | * doAccept() will use AcceptLimiter if we reach the limit | |
241 | * there. | |
242 | */ | |
243 | ||
244 | /* Accept a new connection */ | |
5b67dfa4 | 245 | ConnectionPointer newConnDetails = new Connection(); |
8bbb16e3 | 246 | const comm_err_t flag = oldAccept(newConnDetails); |
04f55905 AJ |
247 | |
248 | /* Check for errors */ | |
5b67dfa4 | 249 | if (!newConnDetails->isOpen()) { |
04f55905 | 250 | |
cbff89ba | 251 | if (flag == COMM_NOMESSAGE) { |
04f55905 | 252 | /* register interest again */ |
6f8536c0 | 253 | debugs(5, 5, HERE << "try later: " << conn << " handler Subscription: " << theCallSub); |
8bbb16e3 | 254 | SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0); |
971581ee | 255 | return; |
04f55905 AJ |
256 | } |
257 | ||
258 | // A non-recoverable error; notify the caller */ | |
cbff89ba | 259 | debugs(5, 5, HERE << "non-recoverable error:" << status() << " handler Subscription: " << theCallSub); |
8bbb16e3 | 260 | notify(flag, newConnDetails); |
5b67dfa4 | 261 | mustStop("Listener socket closed"); |
971581ee | 262 | return; |
04f55905 AJ |
263 | } |
264 | ||
5b67dfa4 AJ |
265 | debugs(5, 5, HERE << "Listener: " << conn << |
266 | " accepted new connection " << newConnDetails << | |
6f8536c0 | 267 | " handler Subscription: " << theCallSub); |
8bbb16e3 | 268 | notify(flag, newConnDetails); |
04f55905 AJ |
269 | } |
270 | ||
271 | void | |
cbff89ba | 272 | Comm::TcpAcceptor::acceptNext() |
04f55905 | 273 | { |
a9870624 | 274 | Must(IsConnOpen(conn)); |
5b67dfa4 | 275 | debugs(5, 2, HERE << "connection on " << conn); |
971581ee | 276 | acceptOne(); |
04f55905 AJ |
277 | } |
278 | ||
279 | void | |
8bbb16e3 | 280 | Comm::TcpAcceptor::notify(const comm_err_t flag, const Comm::ConnectionPointer &newConnDetails) const |
04f55905 AJ |
281 | { |
282 | // listener socket handlers just abandon the port with COMM_ERR_CLOSING | |
283 | // it should only happen when this object is deleted... | |
1fc32b95 | 284 | if (flag == COMM_ERR_CLOSING) { |
04f55905 AJ |
285 | return; |
286 | } | |
287 | ||
5b67dfa4 AJ |
288 | if (theCallSub != NULL) { |
289 | AsyncCall::Pointer call = theCallSub->callback(); | |
290 | CommAcceptCbParams ¶ms = GetCommParams<CommAcceptCbParams>(call); | |
94bfd31f AJ |
291 | params.xaction = new MasterXaction; |
292 | params.xaction->squidPort = static_cast<AnyP::PortCfg*>(params.data); | |
a9870624 | 293 | params.fd = conn->fd; |
94bfd31f | 294 | params.conn = params.xaction->tcpClient = newConnDetails; |
0ba55a12 AJ |
295 | params.flag = flag; |
296 | params.xerrno = errcode; | |
297 | ScheduleCallHere(call); | |
0ba55a12 | 298 | } |
04f55905 AJ |
299 | } |
300 | ||
301 | /** | |
97b8ac39 | 302 | * accept() and process |
5b67dfa4 AJ |
303 | * Wait for an incoming connection on our listener socket. |
304 | * | |
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. | |
273f66c4 | 309 | */ |
5b67dfa4 | 310 | comm_err_t |
8bbb16e3 | 311 | Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details) |
04f55905 AJ |
312 | { |
313 | PROF_start(comm_accept); | |
e4f1fdae | 314 | ++statCounter.syscalls.sock.accepts; |
04f55905 AJ |
315 | int sock; |
316 | struct addrinfo *gai = NULL; | |
4dd643d5 | 317 | Ip::Address::InitAddrInfo(gai); |
04f55905 | 318 | |
1fc32b95 | 319 | errcode = 0; // reset local errno copy. |
a9870624 | 320 | if ((sock = accept(conn->fd, gai->ai_addr, &gai->ai_addrlen)) < 0) { |
1fc32b95 | 321 | errcode = errno; // store last accept errno locally. |
04f55905 | 322 | |
4dd643d5 | 323 | Ip::Address::FreeAddrInfo(gai); |
04f55905 AJ |
324 | |
325 | PROF_stop(comm_accept); | |
326 | ||
327 | if (ignoreErrno(errno)) { | |
cbff89ba | 328 | debugs(50, 5, HERE << status() << ": " << xstrerror()); |
04f55905 AJ |
329 | return COMM_NOMESSAGE; |
330 | } else if (ENFILE == errno || EMFILE == errno) { | |
cbff89ba | 331 | debugs(50, 3, HERE << status() << ": " << xstrerror()); |
04f55905 AJ |
332 | return COMM_ERROR; |
333 | } else { | |
e0236918 | 334 | debugs(50, DBG_IMPORTANT, HERE << status() << ": " << xstrerror()); |
04f55905 AJ |
335 | return COMM_ERROR; |
336 | } | |
337 | } | |
338 | ||
a9870624 | 339 | Must(sock >= 0); |
5b67dfa4 AJ |
340 | details->fd = sock; |
341 | details->remote = *gai; | |
04f55905 | 342 | |
5511c78a | 343 | if ( Config.client_ip_max_connections >= 0) { |
5b67dfa4 AJ |
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."); | |
4dd643d5 | 346 | Ip::Address::FreeAddrInfo(gai); |
5511c78a AJ |
347 | return COMM_ERROR; |
348 | } | |
349 | } | |
350 | ||
903198a7 | 351 | // lookup the local-end details of this new connection |
4dd643d5 AJ |
352 | Ip::Address::InitAddrInfo(gai); |
353 | details->local.setEmpty(); | |
e42281f9 AJ |
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()); | |
4dd643d5 | 356 | Ip::Address::FreeAddrInfo(gai); |
e42281f9 AJ |
357 | return COMM_ERROR; |
358 | } | |
5b67dfa4 | 359 | details->local = *gai; |
4dd643d5 | 360 | Ip::Address::FreeAddrInfo(gai); |
04f55905 AJ |
361 | |
362 | /* fdstat update */ | |
5b67dfa4 | 363 | // XXX : these are not all HTTP requests. use a note about type and ip:port details-> |
903198a7 | 364 | // so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port" |
04f55905 AJ |
365 | fd_open(sock, FD_SOCKET, "HTTP Request"); |
366 | ||
367 | fdd_table[sock].close_file = NULL; | |
368 | fdd_table[sock].close_line = 0; | |
369 | ||
370 | fde *F = &fd_table[sock]; | |
4dd643d5 AJ |
371 | details->remote.toStr(F->ipaddr,MAX_IPSTRLEN); |
372 | F->remote_port = details->remote.port(); | |
5b67dfa4 | 373 | F->local_addr = details->local; |
4dd643d5 | 374 | F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET; |
04f55905 | 375 | |
903198a7 AJ |
376 | // set socket flags |
377 | commSetCloseOnExec(sock); | |
04f55905 AJ |
378 | commSetNonBlocking(sock); |
379 | ||
380 | /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */ | |
40d34a62 AJ |
381 | F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet? |
382 | ||
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)) { | |
385 | // Failed. | |
386 | return COMM_ERROR; | |
387 | } | |
04f55905 | 388 | |
83b62d3f AJ |
389 | #if USE_SQUID_EUI |
390 | if (Eui::TheConfig.euiLookup) { | |
68e47c3e AJ |
391 | if (details->remote.isIPv4()) { |
392 | details->remoteEui48.lookup(details->remote); | |
393 | } else if (details->remote.isIPv6()) { | |
394 | details->remoteEui64.lookup(details->remote); | |
83b62d3f AJ |
395 | } |
396 | } | |
397 | #endif | |
398 | ||
04f55905 | 399 | PROF_stop(comm_accept); |
5b67dfa4 | 400 | return COMM_OK; |
04f55905 | 401 | } |