]>
Commit | Line | Data |
---|---|---|
52e1d7e2 | 1 | |
30a4f2a8 | 2 | /* |
431d84c5 | 3 | * $Id: comm.cc,v 1.107 1996/11/22 05:07:12 wessels Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 5 Socket Functions | |
6 | * AUTHOR: Harvest Derived | |
7 | * | |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
30a4f2a8 | 9 | * -------------------------------------------------------- |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by | |
14 | * the National Science Foundation. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | */ | |
d1f14731 | 31 | |
30a4f2a8 | 32 | /* |
33 | * Copyright (c) 1994, 1995. All rights reserved. | |
34 | * | |
35 | * The Harvest software was developed by the Internet Research Task | |
36 | * Force Research Group on Resource Discovery (IRTF-RD): | |
37 | * | |
38 | * Mic Bowman of Transarc Corporation. | |
39 | * Peter Danzig of the University of Southern California. | |
40 | * Darren R. Hardy of the University of Colorado at Boulder. | |
41 | * Udi Manber of the University of Arizona. | |
42 | * Michael F. Schwartz of the University of Colorado at Boulder. | |
43 | * Duane Wessels of the University of Colorado at Boulder. | |
44 | * | |
45 | * This copyright notice applies to software in the Harvest | |
46 | * ``src/'' directory only. Users should consult the individual | |
47 | * copyright notices in the ``components/'' subdirectories for | |
48 | * copyright information about other software bundled with the | |
49 | * Harvest source code distribution. | |
50 | * | |
51 | * TERMS OF USE | |
52 | * | |
53 | * The Harvest software may be used and re-distributed without | |
54 | * charge, provided that the software origin and research team are | |
55 | * cited in any use of the system. Most commonly this is | |
56 | * accomplished by including a link to the Harvest Home Page | |
57 | * (http://harvest.cs.colorado.edu/) from the query page of any | |
58 | * Broker you deploy, as well as in the query result pages. These | |
59 | * links are generated automatically by the standard Broker | |
60 | * software distribution. | |
61 | * | |
62 | * The Harvest software is provided ``as is'', without express or | |
63 | * implied warranty, and with no support nor obligation to assist | |
64 | * in its use, correction, modification or enhancement. We assume | |
65 | * no liability with respect to the infringement of copyrights, | |
66 | * trade secrets, or any patents, and are not responsible for | |
67 | * consequential damages. Proper use of the Harvest software is | |
68 | * entirely the responsibility of the user. | |
69 | * | |
70 | * DERIVATIVE WORKS | |
71 | * | |
72 | * Users may make derivative works from the Harvest software, subject | |
73 | * to the following constraints: | |
74 | * | |
75 | * - You must include the above copyright notice and these | |
76 | * accompanying paragraphs in all forms of derivative works, | |
77 | * and any documentation and other materials related to such | |
78 | * distribution and use acknowledge that the software was | |
79 | * developed at the above institutions. | |
80 | * | |
81 | * - You must notify IRTF-RD regarding your distribution of | |
82 | * the derivative work. | |
83 | * | |
84 | * - You must clearly notify users that your are distributing | |
85 | * a modified version and not the original Harvest software. | |
86 | * | |
87 | * - Any derivative product is also subject to these copyright | |
88 | * and use restrictions. | |
89 | * | |
90 | * Note that the Harvest software is NOT in the public domain. We | |
91 | * retain copyright, as specified above. | |
92 | * | |
93 | * HISTORY OF FREE SOFTWARE STATUS | |
94 | * | |
95 | * Originally we required sites to license the software in cases | |
96 | * where they were going to build commercial products/services | |
97 | * around Harvest. In June 1995 we changed this policy. We now | |
98 | * allow people to use the core Harvest software (the code found in | |
99 | * the Harvest ``src/'' directory) for free. We made this change | |
100 | * in the interest of encouraging the widest possible deployment of | |
101 | * the technology. The Harvest software is really a reference | |
102 | * implementation of a set of protocols and formats, some of which | |
103 | * we intend to standardize. We encourage commercial | |
104 | * re-implementations of code complying to this set of standards. | |
105 | */ | |
090089c4 | 106 | |
44a47c6e | 107 | #include "squid.h" |
090089c4 | 108 | |
30a4f2a8 | 109 | #ifdef HAVE_NETINET_TCP_H |
110 | #include <netinet/tcp.h> | |
111 | #endif | |
090089c4 | 112 | |
113 | /* Block processing new client requests (accepts on ascii port) when we start | |
114 | * running shy of free file descriptors. For example, under SunOS, we'll keep | |
115 | * 64 file descriptors free for disk-i/o and connections to remote servers */ | |
116 | ||
da22ac20 | 117 | int RESERVED_FD = 64; |
090089c4 | 118 | |
119 | #define min(x,y) ((x)<(y)? (x) : (y)) | |
120 | #define max(a,b) ((a)>(b)? (a) : (b)) | |
121 | ||
30a4f2a8 | 122 | struct _RWStateData { |
123 | char *buf; | |
124 | long size; | |
125 | long offset; | |
126 | int timeout; /* XXX Not used at present. */ | |
127 | time_t time; /* XXX Not used at present. */ | |
128 | rw_complete_handler *handler; | |
129 | void *handler_data; | |
130 | int handle_immed; | |
4a63c85f | 131 | void (*free) (void *); |
30a4f2a8 | 132 | }; |
090089c4 | 133 | |
134 | /* GLOBAL */ | |
090089c4 | 135 | FD_ENTRY *fd_table = NULL; /* also used in disk.c */ |
136 | ||
137 | /* STATIC */ | |
24382924 | 138 | static int commBind _PARAMS((int s, struct in_addr, u_short port)); |
139 | static int comm_cleanup_fd_entry _PARAMS((int)); | |
5742d7c9 | 140 | static int examine_select _PARAMS((fd_set *, fd_set *)); |
67508012 | 141 | static void checkTimeouts _PARAMS((void)); |
142 | static void checkLifetimes _PARAMS((void)); | |
143 | static void Reserve_More_FDs _PARAMS((void)); | |
144 | static void commSetReuseAddr _PARAMS((int)); | |
67508012 | 145 | static void commSetNoLinger _PARAMS((int)); |
146 | static void comm_select_incoming _PARAMS((void)); | |
67508012 | 147 | static void RWStateCallbackAndFree _PARAMS((int fd, int code)); |
30a4f2a8 | 148 | #ifdef TCP_NODELAY |
67508012 | 149 | static void commSetTcpNoDelay _PARAMS((int)); |
30a4f2a8 | 150 | #endif |
67508012 | 151 | static void commSetTcpRcvbuf _PARAMS((int, int)); |
30a4f2a8 | 152 | |
153 | static int *fd_lifetime = NULL; | |
154 | static struct timeval zero_tv; | |
090089c4 | 155 | |
81f754fa | 156 | void |
157 | commCancelRWHandler(int fd) | |
158 | { | |
159 | RWStateData *RWState = fd_table[fd].rwstate; | |
160 | if (RWState) { | |
38cac3c8 | 161 | RWState->handler = NULL; |
162 | RWState->handler_data = NULL; | |
81f754fa | 163 | } |
164 | } | |
165 | ||
166 | ||
b8d8561b | 167 | static void |
168 | RWStateCallbackAndFree(int fd, int code) | |
9864ee44 | 169 | { |
a56a3abe | 170 | RWStateData *RWState = fd_table[fd].rwstate; |
9864ee44 | 171 | rw_complete_handler *callback = NULL; |
a56a3abe | 172 | fd_table[fd].rwstate = NULL; |
9864ee44 | 173 | if (RWState == NULL) |
174 | return; | |
175 | if (RWState->free) { | |
176 | RWState->free(RWState->buf); | |
177 | RWState->buf = NULL; | |
178 | } | |
179 | callback = RWState->handler; | |
180 | RWState->handler = NULL; | |
181 | if (callback) { | |
182 | callback(fd, | |
183 | RWState->buf, | |
184 | RWState->offset, | |
185 | code, | |
186 | RWState->handler_data); | |
187 | } | |
188 | safe_free(RWState); | |
189 | } | |
190 | ||
090089c4 | 191 | /* Return the local port associated with fd. */ |
b8d8561b | 192 | u_short |
193 | comm_local_port(int fd) | |
090089c4 | 194 | { |
195 | struct sockaddr_in addr; | |
196 | int addr_len = 0; | |
9864ee44 | 197 | FD_ENTRY *fde = &fd_table[fd]; |
090089c4 | 198 | |
090089c4 | 199 | /* If the fd is closed already, just return */ |
9864ee44 | 200 | if (!fde->openned) { |
30a4f2a8 | 201 | debug(5, 0, "comm_local_port: FD %d has been closed.\n", fd); |
202 | return 0; | |
090089c4 | 203 | } |
9864ee44 | 204 | if (fde->local_port) |
205 | return fde->local_port; | |
090089c4 | 206 | addr_len = sizeof(addr); |
207 | if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) { | |
881f7a6c | 208 | debug(50, 1, "comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror()); |
30a4f2a8 | 209 | return 0; |
090089c4 | 210 | } |
30a4f2a8 | 211 | debug(5, 6, "comm_local_port: FD %d: sockaddr %u.\n", fd, addr.sin_addr.s_addr); |
9864ee44 | 212 | fde->local_port = ntohs(addr.sin_port); |
213 | return fde->local_port; | |
090089c4 | 214 | } |
215 | ||
b8d8561b | 216 | static int |
217 | commBind(int s, struct in_addr in_addr, u_short port) | |
090089c4 | 218 | { |
219 | struct sockaddr_in S; | |
090089c4 | 220 | |
090089c4 | 221 | memset(&S, '\0', sizeof(S)); |
222 | S.sin_family = AF_INET; | |
223 | S.sin_port = htons(port); | |
30a4f2a8 | 224 | S.sin_addr = in_addr; |
090089c4 | 225 | if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0) |
226 | return COMM_OK; | |
881f7a6c | 227 | debug(50, 0, "commBind: Cannot bind socket FD %d to %s:%d: %s\n", |
090089c4 | 228 | s, |
30a4f2a8 | 229 | S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr), |
090089c4 | 230 | port, xstrerror()); |
231 | return COMM_ERROR; | |
232 | } | |
233 | ||
234 | /* Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE | |
235 | * is OR of flags specified in comm.h. */ | |
b8d8561b | 236 | int |
16b204c4 | 237 | comm_open(int sock_type, |
cc6a9d2e | 238 | int proto, |
239 | struct in_addr addr, | |
240 | u_short port, | |
241 | int flags, | |
0ee4272b | 242 | const char *note) |
090089c4 | 243 | { |
244 | int new_socket; | |
245 | FD_ENTRY *conn = NULL; | |
b6f794d6 | 246 | int tcp_rcv_bufsz = Config.tcpRcvBufsz; |
090089c4 | 247 | |
248 | /* Create socket for accepting new connections. */ | |
16b204c4 | 249 | if ((new_socket = socket(AF_INET, sock_type, proto)) < 0) { |
090089c4 | 250 | /* Increase the number of reserved fd's if calls to socket() |
251 | * are failing because the open file table is full. This | |
252 | * limits the number of simultaneous clients */ | |
253 | switch (errno) { | |
254 | case ENFILE: | |
255 | case EMFILE: | |
881f7a6c | 256 | debug(50, 1, "comm_open: socket failure: %s\n", xstrerror()); |
090089c4 | 257 | Reserve_More_FDs(); |
258 | break; | |
259 | default: | |
881f7a6c | 260 | debug(50, 0, "comm_open: socket failure: %s\n", xstrerror()); |
090089c4 | 261 | } |
262 | return (COMM_ERROR); | |
263 | } | |
264 | /* update fdstat */ | |
30a4f2a8 | 265 | fdstat_open(new_socket, FD_SOCKET); |
090089c4 | 266 | |
267 | conn = &fd_table[new_socket]; | |
268 | memset(conn, '\0', sizeof(FD_ENTRY)); | |
30a4f2a8 | 269 | if (note) |
270 | fd_note(new_socket, note); | |
090089c4 | 271 | conn->openned = 1; |
272 | ||
16b204c4 | 273 | if (!BIT_TEST(flags, COMM_NOCLOEXEC)) |
3ca60c86 | 274 | commSetCloseOnExec(new_socket); |
7690e8eb | 275 | if (port > (u_short) 0) { |
30a4f2a8 | 276 | commSetNoLinger(new_socket); |
277 | if (do_reuse) | |
090089c4 | 278 | commSetReuseAddr(new_socket); |
090089c4 | 279 | } |
30a4f2a8 | 280 | if (addr.s_addr != INADDR_NONE) |
281 | if (commBind(new_socket, addr, port) != COMM_OK) | |
282 | return COMM_ERROR; | |
283 | conn->local_port = port; | |
090089c4 | 284 | |
16b204c4 | 285 | if (BIT_TEST(flags, COMM_NONBLOCKING)) |
30a4f2a8 | 286 | if (commSetNonBlocking(new_socket) == COMM_ERROR) |
287 | return COMM_ERROR; | |
288 | #ifdef TCP_NODELAY | |
289 | if (sock_type == SOCK_STREAM) | |
290 | commSetTcpNoDelay(new_socket); | |
291 | #endif | |
f868539a | 292 | if (tcp_rcv_bufsz > 0 && sock_type == SOCK_STREAM) |
293 | commSetTcpRcvbuf(new_socket, tcp_rcv_bufsz); | |
16b204c4 | 294 | conn->comm_type = sock_type; |
090089c4 | 295 | return new_socket; |
296 | } | |
297 | ||
298 | /* | |
431d84c5 | 299 | * NOTE: set the listen queue to SQUID_MAXFD/4 and rely on the kernel to |
090089c4 | 300 | * impose an upper limit. Solaris' listen(3n) page says it has |
301 | * no limit on this parameter, but sys/socket.h sets SOMAXCONN | |
302 | * to 5. HP-UX currently has a limit of 20. SunOS is 5 and | |
303 | * OSF 3.0 is 8. | |
304 | */ | |
b8d8561b | 305 | int |
306 | comm_listen(int sock) | |
090089c4 | 307 | { |
308 | int x; | |
431d84c5 | 309 | if ((x = listen(sock, SQUID_MAXFD >> 2)) < 0) { |
881f7a6c | 310 | debug(50, 0, "comm_listen: listen(%d, %d): %s\n", |
431d84c5 | 311 | SQUID_MAXFD >> 2, |
090089c4 | 312 | sock, xstrerror()); |
313 | return x; | |
314 | } | |
315 | return sock; | |
316 | } | |
317 | ||
090089c4 | 318 | /* Connect SOCK to specified DEST_PORT at DEST_HOST. */ |
e5f6c5c2 | 319 | void |
320 | comm_nbconnect(int fd, void *data) | |
090089c4 | 321 | { |
e5f6c5c2 | 322 | ConnectStateData *connectState = data; |
0ee4272b | 323 | const ipcache_addrs *ia = NULL; |
e5f6c5c2 | 324 | if (connectState->S.sin_addr.s_addr == 0) { |
325 | ia = ipcache_gethostbyname(connectState->host, IP_BLOCKING_LOOKUP); | |
326 | if (ia == NULL) { | |
327 | debug(5, 3, "comm_nbconnect: Unknown host: %s\n", | |
328 | connectState->host); | |
329 | connectState->handler(fd, | |
330 | COMM_ERROR, | |
331 | connectState->data); | |
332 | return; | |
333 | } | |
334 | connectState->S.sin_family = AF_INET; | |
335 | connectState->S.sin_addr = ia->in_addrs[ia->cur]; | |
336 | connectState->S.sin_port = htons(connectState->port); | |
337 | if (Config.Log.log_fqdn) | |
338 | fqdncache_gethostbyaddr(connectState->S.sin_addr, FQDN_LOOKUP_IF_MISS); | |
339 | } | |
340 | switch (comm_connect_addr(fd, &connectState->S)) { | |
341 | case COMM_INPROGRESS: | |
b177367b | 342 | commSetSelect(fd, |
e5f6c5c2 | 343 | COMM_SELECT_WRITE, |
344 | comm_nbconnect, | |
b177367b | 345 | (void *) connectState, |
85d7ea98 | 346 | 0); |
e5f6c5c2 | 347 | break; |
348 | case COMM_OK: | |
349 | connectState->handler(fd, COMM_OK, connectState->data); | |
350 | ipcacheCycleAddr(connectState->host); | |
351 | break; | |
352 | default: | |
353 | ipcacheRemoveBadAddr(connectState->host, connectState->S.sin_addr); | |
354 | connectState->handler(fd, COMM_ERROR, connectState->data); | |
355 | break; | |
090089c4 | 356 | } |
090089c4 | 357 | } |
358 | ||
b8d8561b | 359 | int |
360 | comm_set_fd_lifetime(int fd, int lifetime) | |
090089c4 | 361 | { |
30a4f2a8 | 362 | debug(5, 3, "comm_set_fd_lifetime: FD %d lft %d\n", fd, lifetime); |
431d84c5 | 363 | if (fd < 0 || fd > SQUID_MAXFD) |
090089c4 | 364 | return 0; |
365 | if (lifetime < 0) | |
366 | return fd_lifetime[fd] = -1; | |
30a4f2a8 | 367 | if (shutdown_pending || reread_pending) { |
368 | /* don't increase the lifetime if something pending */ | |
369 | if (fd_lifetime[fd] > -1 && (fd_lifetime[fd] - squid_curtime) < lifetime) | |
370 | return fd_lifetime[fd]; | |
371 | } | |
b8de7ebe | 372 | return fd_lifetime[fd] = (int) squid_curtime + lifetime; |
090089c4 | 373 | } |
374 | ||
b8d8561b | 375 | int |
376 | comm_get_fd_lifetime(int fd) | |
090089c4 | 377 | { |
378 | if (fd < 0) | |
379 | return 0; | |
380 | return fd_lifetime[fd]; | |
381 | } | |
382 | ||
b8d8561b | 383 | int |
384 | comm_get_fd_timeout(int fd) | |
090089c4 | 385 | { |
386 | if (fd < 0) | |
387 | return 0; | |
388 | return fd_table[fd].timeout_time; | |
389 | } | |
390 | ||
b8d8561b | 391 | int |
0ee4272b | 392 | comm_connect_addr(int sock, const struct sockaddr_in *address) |
090089c4 | 393 | { |
394 | int status = COMM_OK; | |
395 | FD_ENTRY *conn = &fd_table[sock]; | |
396 | int len; | |
397 | int x; | |
398 | int lft; | |
399 | ||
400 | /* sanity check */ | |
401 | if (ntohs(address->sin_port) == 0) { | |
d1f14731 | 402 | debug(5, 10, "comm_connect_addr: %s:%d: URL uses port 0?\n", |
090089c4 | 403 | inet_ntoa(address->sin_addr), ntohs(address->sin_port)); |
404 | errno = 0; | |
405 | return COMM_ERROR; | |
406 | } | |
407 | /* Establish connection. */ | |
0ee4272b | 408 | if (connect(sock, (const struct sockaddr *) address, sizeof(struct sockaddr_in)) < 0) { |
090089c4 | 409 | switch (errno) { |
410 | case EALREADY: | |
411 | return COMM_ERROR; | |
412 | /* NOTREACHED */ | |
30a4f2a8 | 413 | #if EAGAIN != EWOULDBLOCK |
414 | case EAGAIN: | |
415 | #endif | |
416 | case EWOULDBLOCK: | |
090089c4 | 417 | case EINPROGRESS: |
e5f6c5c2 | 418 | status = COMM_INPROGRESS; |
090089c4 | 419 | break; |
420 | case EISCONN: | |
421 | status = COMM_OK; | |
422 | break; | |
423 | case EINVAL: | |
424 | len = sizeof(x); | |
425 | if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &x, &len) >= 0) | |
426 | errno = x; | |
427 | default: | |
881f7a6c | 428 | debug(50, 2, "connect: %s:%d: %s.\n", |
28ab0c0a | 429 | fqdnFromAddr(address->sin_addr), |
090089c4 | 430 | ntohs(address->sin_port), |
431 | xstrerror()); | |
432 | return COMM_ERROR; | |
433 | } | |
e5f6c5c2 | 434 | } |
7450ce42 | 435 | strcpy(conn->ipaddr, inet_ntoa(address->sin_addr)); |
436 | conn->remote_port = ntohs(address->sin_port); | |
090089c4 | 437 | /* set the lifetime for this client */ |
438 | if (status == COMM_OK) { | |
b6f794d6 | 439 | lft = comm_set_fd_lifetime(sock, Config.lifetimeDefault); |
7450ce42 | 440 | debug(5, 10, "comm_connect_addr: FD %d connected to %s:%d, lifetime %d.\n", |
441 | sock, conn->ipaddr, conn->remote_port, lft); | |
090089c4 | 442 | } else if (status == EINPROGRESS) { |
b6f794d6 | 443 | lft = comm_set_fd_lifetime(sock, Config.connectTimeout); |
d1f14731 | 444 | debug(5, 10, "comm_connect_addr: FD %d connection pending, lifetime %d\n", |
090089c4 | 445 | sock, lft); |
446 | } | |
447 | /* Add new socket to list of open sockets. */ | |
090089c4 | 448 | conn->sender = 1; |
090089c4 | 449 | return status; |
450 | } | |
451 | ||
452 | /* Wait for an incoming connection on FD. FD should be a socket returned | |
453 | * from comm_listen. */ | |
b8d8561b | 454 | int |
455 | comm_accept(int fd, struct sockaddr_in *peer, struct sockaddr_in *me) | |
090089c4 | 456 | { |
457 | int sock; | |
1f9afe33 | 458 | struct sockaddr_in P; |
459 | struct sockaddr_in M; | |
090089c4 | 460 | int Slen; |
1f9afe33 | 461 | FD_ENTRY *conn = NULL; |
090089c4 | 462 | FD_ENTRY *listener = &fd_table[fd]; |
463 | ||
1f9afe33 | 464 | Slen = sizeof(P); |
465 | while ((sock = accept(fd, (struct sockaddr *) &P, &Slen)) < 0) { | |
090089c4 | 466 | switch (errno) { |
467 | #if EAGAIN != EWOULDBLOCK | |
468 | case EAGAIN: | |
469 | #endif | |
470 | case EWOULDBLOCK: | |
471 | return COMM_NOMESSAGE; | |
472 | case EINTR: | |
473 | break; /* if accept interrupted, try again */ | |
474 | case ENFILE: | |
475 | case EMFILE: | |
476 | Reserve_More_FDs(); | |
477 | return COMM_ERROR; | |
478 | default: | |
881f7a6c | 479 | debug(50, 1, "comm_accept: FD %d: accept failure: %s\n", |
090089c4 | 480 | fd, xstrerror()); |
481 | return COMM_ERROR; | |
482 | } | |
483 | } | |
484 | ||
485 | if (peer) | |
1f9afe33 | 486 | *peer = P; |
4053a845 | 487 | Slen = sizeof(M); |
488 | memset(&M, '\0', Slen); | |
489 | getsockname(sock, (struct sockaddr *) &M, &Slen); | |
490 | if (me) | |
1f9afe33 | 491 | *me = M; |
3ca60c86 | 492 | commSetCloseOnExec(sock); |
090089c4 | 493 | /* fdstat update */ |
30a4f2a8 | 494 | fdstat_open(sock, FD_SOCKET); |
090089c4 | 495 | conn = &fd_table[sock]; |
496 | conn->openned = 1; | |
497 | conn->sender = 0; /* This is an accept, therefore receiver. */ | |
498 | conn->comm_type = listener->comm_type; | |
1f9afe33 | 499 | strcpy(conn->ipaddr, inet_ntoa(P.sin_addr)); |
30a4f2a8 | 500 | conn->remote_port = htons(P.sin_port); |
501 | conn->local_port = htons(M.sin_port); | |
090089c4 | 502 | commSetNonBlocking(sock); |
090089c4 | 503 | return sock; |
504 | } | |
505 | ||
cb201b7e | 506 | void |
507 | commCallCloseHandlers(int fd) | |
508 | { | |
509 | FD_ENTRY *conn = &fd_table[fd]; | |
510 | struct close_handler *ch; | |
511 | while ((ch = conn->close_handler) != NULL) { | |
512 | conn->close_handler = ch->next; | |
513 | ch->handler(fd, ch->data); | |
514 | safe_free(ch); | |
515 | } | |
516 | } | |
517 | ||
b8d8561b | 518 | void |
519 | comm_close(int fd) | |
090089c4 | 520 | { |
521 | FD_ENTRY *conn = NULL; | |
d9ec4825 | 522 | debug(5, 5, "comm_close: FD %d\n", fd); |
431d84c5 | 523 | if (fd < 0 || fd >= SQUID_MAXFD) |
9864ee44 | 524 | return; |
525 | conn = &fd_table[fd]; | |
526 | if (!conn->openned) | |
527 | return; | |
7450ce42 | 528 | if (fdstatGetType(fd) == FD_FILE) { |
d1f14731 | 529 | debug(5, 0, "FD %d: Someone called comm_close() on a File\n", fd); |
090089c4 | 530 | fatal_dump(NULL); |
531 | } | |
9864ee44 | 532 | conn->openned = 0; |
a56a3abe | 533 | RWStateCallbackAndFree(fd, COMM_ERROR); |
090089c4 | 534 | comm_set_fd_lifetime(fd, -1); /* invalidate the lifetime */ |
4a63c85f | 535 | fdstat_close(fd); /* update fdstat */ |
cb201b7e | 536 | commCallCloseHandlers(fd); |
090089c4 | 537 | memset(conn, '\0', sizeof(FD_ENTRY)); |
9864ee44 | 538 | close(fd); |
090089c4 | 539 | } |
540 | ||
541 | /* use to clean up fdtable when socket is closed without | |
542 | * using comm_close */ | |
24382924 | 543 | static int |
b8d8561b | 544 | comm_cleanup_fd_entry(int fd) |
090089c4 | 545 | { |
546 | FD_ENTRY *conn = &fd_table[fd]; | |
a56a3abe | 547 | RWStateCallbackAndFree(fd, COMM_ERROR); |
090089c4 | 548 | memset(conn, 0, sizeof(FD_ENTRY)); |
549 | return 0; | |
550 | } | |
551 | ||
552 | ||
553 | /* Send a udp datagram to specified PORT at HOST. */ | |
b8d8561b | 554 | int |
0ee4272b | 555 | comm_udp_send(int fd, const char *host, u_short port, const char *buf, int len) |
090089c4 | 556 | { |
0ee4272b | 557 | const ipcache_addrs *ia = NULL; |
090089c4 | 558 | static struct sockaddr_in to_addr; |
559 | int bytes_sent; | |
560 | ||
561 | /* Set up the destination socket address for message to send to. */ | |
562 | to_addr.sin_family = AF_INET; | |
563 | ||
e5f6c5c2 | 564 | if ((ia = ipcache_gethostbyname(host, IP_BLOCKING_LOOKUP)) == 0) { |
881f7a6c | 565 | debug(50, 1, "comm_udp_send: gethostbyname failure: %s: %s\n", |
090089c4 | 566 | host, xstrerror()); |
567 | return (COMM_ERROR); | |
568 | } | |
e5f6c5c2 | 569 | to_addr.sin_addr = ia->in_addrs[ia->cur]; |
090089c4 | 570 | to_addr.sin_port = htons(port); |
571 | if ((bytes_sent = sendto(fd, buf, len, 0, (struct sockaddr *) &to_addr, | |
572 | sizeof(to_addr))) < 0) { | |
881f7a6c | 573 | debug(50, 1, "comm_udp_send: sendto failure: FD %d: %s\n", |
090089c4 | 574 | fd, xstrerror()); |
575 | return COMM_ERROR; | |
576 | } | |
577 | return bytes_sent; | |
578 | } | |
579 | ||
580 | /* Send a udp datagram to specified TO_ADDR. */ | |
b8d8561b | 581 | int |
5df61230 | 582 | comm_udp_sendto(int fd, |
583 | const struct sockaddr_in *to_addr, | |
584 | int addr_len, | |
585 | const char *buf, | |
586 | int len) | |
090089c4 | 587 | { |
5df61230 | 588 | int x; |
589 | x = sendto(fd, buf, len, 0, (struct sockaddr *) to_addr, addr_len); | |
590 | if (x < 0) { | |
881f7a6c | 591 | debug(50, 1, "comm_udp_sendto: FD %d, %s, port %d: %s\n", |
5df61230 | 592 | fd, |
593 | inet_ntoa(to_addr->sin_addr), | |
594 | (int) htons(to_addr->sin_port), | |
595 | xstrerror()); | |
090089c4 | 596 | return COMM_ERROR; |
597 | } | |
5df61230 | 598 | return x; |
090089c4 | 599 | } |
600 | ||
b8d8561b | 601 | void |
602 | comm_set_stall(int fd, int delta) | |
4883993a | 603 | { |
604 | if (fd < 0) | |
605 | return; | |
b8de7ebe | 606 | fd_table[fd].stall_until = squid_curtime + delta; |
4883993a | 607 | } |
608 | ||
b8d8561b | 609 | static void |
0673c0ba | 610 | comm_select_incoming(void) |
055f4d4d | 611 | { |
612 | fd_set read_mask; | |
613 | fd_set write_mask; | |
614 | int maxfd = 0; | |
615 | int fd = 0; | |
616 | int fds[3]; | |
617 | int N = 0; | |
618 | int i = 0; | |
ff8d0ea6 | 619 | PF hdl = NULL; |
055f4d4d | 620 | |
621 | FD_ZERO(&read_mask); | |
622 | FD_ZERO(&write_mask); | |
623 | ||
30a4f2a8 | 624 | if (theHttpConnection >= 0 && fdstat_are_n_free_fd(RESERVED_FD)) |
625 | fds[N++] = theHttpConnection; | |
626 | if (theInIcpConnection >= 0) | |
627 | fds[N++] = theInIcpConnection; | |
055f4d4d | 628 | fds[N++] = 0; |
629 | ||
630 | for (i = 0; i < N; i++) { | |
631 | fd = fds[i]; | |
632 | if (fd_table[fd].read_handler) { | |
633 | FD_SET(fd, &read_mask); | |
634 | if (fd > maxfd) | |
635 | maxfd = fd; | |
636 | } | |
637 | if (fd_table[fd].write_handler) { | |
638 | FD_SET(fd, &write_mask); | |
639 | if (fd > maxfd) | |
640 | maxfd = fd; | |
641 | } | |
642 | } | |
643 | ||
644 | if (maxfd++ == 0) | |
645 | return; | |
646 | if (select(maxfd, &read_mask, &write_mask, NULL, &zero_tv) > 0) { | |
67508012 | 647 | getCurrentTime(); |
31feba03 | 648 | for (i = 0; i < N; i++) { |
055f4d4d | 649 | fd = fds[i]; |
650 | if (FD_ISSET(fd, &read_mask)) { | |
ff8d0ea6 | 651 | hdl = fd_table[fd].read_handler; |
055f4d4d | 652 | fd_table[fd].read_handler = 0; |
ff8d0ea6 | 653 | hdl(fd, fd_table[fd].read_data); |
055f4d4d | 654 | } |
655 | if (FD_ISSET(fd, &write_mask)) { | |
ff8d0ea6 | 656 | hdl = fd_table[fd].write_handler; |
055f4d4d | 657 | fd_table[fd].write_handler = 0; |
ff8d0ea6 | 658 | hdl(fd, fd_table[fd].write_data); |
055f4d4d | 659 | } |
660 | } | |
661 | } | |
662 | } | |
090089c4 | 663 | |
664 | ||
665 | /* Select on all sockets; call handlers for those that are ready. */ | |
b8d8561b | 666 | int |
667 | comm_select(time_t sec) | |
090089c4 | 668 | { |
090089c4 | 669 | fd_set readfds; |
670 | fd_set writefds; | |
ff8d0ea6 | 671 | PF hdl = NULL; |
7d49daab | 672 | int fd; |
673 | int i; | |
674 | int maxfd; | |
675 | int nfds; | |
090089c4 | 676 | int num; |
090089c4 | 677 | static time_t last_timeout = 0; |
678 | struct timeval poll_time; | |
7d49daab | 679 | time_t timeout; |
090089c4 | 680 | |
681 | /* assume all process are very fast (less than 1 second). Call | |
682 | * time() only once */ | |
e069e535 | 683 | getCurrentTime(); |
090089c4 | 684 | /* use only 1 second granularity */ |
b8de7ebe | 685 | timeout = squid_curtime + sec; |
090089c4 | 686 | |
f7361640 | 687 | do { |
4610e312 | 688 | if (sec > 60) |
d556a268 | 689 | fatal_dump(NULL); |
090089c4 | 690 | FD_ZERO(&readfds); |
691 | FD_ZERO(&writefds); | |
090089c4 | 692 | |
30a4f2a8 | 693 | if (shutdown_pending || reread_pending) { |
694 | serverConnectionsClose(); | |
695 | ftpServerClose(); | |
f88bb09c | 696 | dnsShutdownServers(); |
d2af9477 | 697 | redirectShutdownServers(); |
f3753518 | 698 | if (shutdown_pending > 0) |
6759a5fb | 699 | setSocketShutdownLifetimes(Config.lifetimeShutdown); |
f3753518 | 700 | else |
701 | setSocketShutdownLifetimes(0); | |
30a4f2a8 | 702 | } |
4d64d74a | 703 | nfds = 0; |
704 | maxfd = fdstat_biggest_fd() + 1; | |
705 | for (i = 0; i < maxfd; i++) { | |
898f5d1d | 706 | #if USE_ASYNC_IO |
b560dd20 | 707 | /* Using async IO for disk handle, so don't select on them */ |
7450ce42 | 708 | if (fdstatGetType(i) == FD_FILE) |
b560dd20 | 709 | continue; |
898f5d1d | 710 | #endif |
090089c4 | 711 | /* Check each open socket for a handler. */ |
b8de7ebe | 712 | if (fd_table[i].read_handler && fd_table[i].stall_until <= squid_curtime) { |
4d64d74a | 713 | nfds++; |
090089c4 | 714 | FD_SET(i, &readfds); |
4d64d74a | 715 | } |
716 | if (fd_table[i].write_handler) { | |
717 | nfds++; | |
090089c4 | 718 | FD_SET(i, &writefds); |
4d64d74a | 719 | } |
090089c4 | 720 | } |
721 | if (!fdstat_are_n_free_fd(RESERVED_FD)) { | |
30a4f2a8 | 722 | FD_CLR(theHttpConnection, &readfds); |
090089c4 | 723 | } |
234967c9 | 724 | if (shutdown_pending || reread_pending) |
983061ed | 725 | debug(5, 2, "comm_select: Still waiting on %d FDs\n", nfds); |
4d64d74a | 726 | if (nfds == 0) |
727 | return COMM_SHUTDOWN; | |
7690e8eb | 728 | for (;;) { |
898f5d1d | 729 | #if USE_ASYNC_IO |
b560dd20 | 730 | /* Another CPU vs latency tradeoff for async IO */ |
731 | poll_time.tv_sec = 0; | |
732 | poll_time.tv_usec = 250000; | |
733 | #else | |
89fb2544 | 734 | poll_time.tv_sec = sec > 0 ? 1 : 0; |
090089c4 | 735 | poll_time.tv_usec = 0; |
898f5d1d | 736 | #endif |
d0217c9b | 737 | num = select(maxfd, &readfds, &writefds, NULL, &poll_time); |
67508012 | 738 | getCurrentTime(); |
090089c4 | 739 | if (num >= 0) |
740 | break; | |
4d64d74a | 741 | if (errno == EINTR) |
742 | break; | |
881f7a6c | 743 | debug(50, 0, "comm_select: select failure: %s\n", |
30a4f2a8 | 744 | xstrerror()); |
d0217c9b | 745 | examine_select(&readfds, &writefds); |
bf9f8f2b | 746 | return COMM_ERROR; |
30a4f2a8 | 747 | /* NOTREACHED */ |
090089c4 | 748 | } |
898f5d1d | 749 | #if USE_ASYNC_IO |
b560dd20 | 750 | aioExamine(); /* See if any IO completed */ |
898f5d1d | 751 | #endif |
4d64d74a | 752 | if (num < 0) |
753 | continue; | |
d1f14731 | 754 | debug(5, num ? 5 : 8, "comm_select: %d sockets ready at %d\n", |
30a4f2a8 | 755 | num, (int) squid_curtime); |
090089c4 | 756 | |
757 | /* Check lifetime and timeout handlers ONCE each second. | |
758 | * Replaces brain-dead check every time through the loop! */ | |
b8de7ebe | 759 | if (squid_curtime > last_timeout) { |
760 | last_timeout = squid_curtime; | |
090089c4 | 761 | checkTimeouts(); |
762 | checkLifetimes(); | |
763 | } | |
7d49daab | 764 | if (num == 0) |
765 | continue; | |
766 | ||
090089c4 | 767 | /* scan each socket but the accept socket. Poll this |
768 | * more frequently to minimiize losses due to the 5 connect | |
769 | * limit in SunOS */ | |
770 | ||
5742d7c9 | 771 | for (fd = 0; fd < maxfd; fd++) { |
d0217c9b | 772 | if (!FD_ISSET(fd, &readfds) && !FD_ISSET(fd, &writefds)) |
7d49daab | 773 | continue; |
7d49daab | 774 | |
775 | /* | |
776 | * Admit more connections quickly until we hit the hard limit. | |
777 | * Don't forget to keep the UDP acks coming and going. | |
778 | */ | |
055f4d4d | 779 | comm_select_incoming(); |
7d49daab | 780 | |
30a4f2a8 | 781 | if ((fd == theInIcpConnection) || (fd == theHttpConnection)) |
7d49daab | 782 | continue; |
783 | ||
784 | if (FD_ISSET(fd, &readfds)) { | |
785 | debug(5, 6, "comm_select: FD %d ready for reading\n", fd); | |
786 | if (fd_table[fd].read_handler) { | |
ff8d0ea6 | 787 | hdl = fd_table[fd].read_handler; |
7d49daab | 788 | fd_table[fd].read_handler = 0; |
ff8d0ea6 | 789 | hdl(fd, fd_table[fd].read_data); |
090089c4 | 790 | } |
7d49daab | 791 | } |
792 | if (FD_ISSET(fd, &writefds)) { | |
793 | debug(5, 5, "comm_select: FD %d ready for writing\n", fd); | |
794 | if (fd_table[fd].write_handler) { | |
ff8d0ea6 | 795 | hdl = fd_table[fd].write_handler; |
7d49daab | 796 | fd_table[fd].write_handler = 0; |
ff8d0ea6 | 797 | hdl(fd, fd_table[fd].write_data); |
090089c4 | 798 | } |
7d49daab | 799 | } |
090089c4 | 800 | } |
7d49daab | 801 | return COMM_OK; |
f7361640 | 802 | } while (timeout > getCurrentTime()); |
090089c4 | 803 | |
b8de7ebe | 804 | debug(5, 8, "comm_select: time out: %d.\n", squid_curtime); |
090089c4 | 805 | return COMM_TIMEOUT; |
806 | } | |
807 | ||
b8d8561b | 808 | void |
b177367b | 809 | commSetSelect(int fd, unsigned int type, PF handler, void *client_data, time_t timeout) |
090089c4 | 810 | { |
811 | if (type & COMM_SELECT_TIMEOUT) { | |
e069e535 | 812 | fd_table[fd].timeout_time = (getCurrentTime() + timeout); |
090089c4 | 813 | fd_table[fd].timeout_delta = timeout; |
814 | fd_table[fd].timeout_handler = handler; | |
815 | fd_table[fd].timeout_data = client_data; | |
816 | if ((timeout <= 0) && handler) { | |
b177367b | 817 | debug(5, 2, "commSetSelect: Zero timeout doesn't make sense\n"); |
090089c4 | 818 | } |
819 | } | |
820 | if (type & COMM_SELECT_READ) { | |
821 | fd_table[fd].read_handler = handler; | |
822 | fd_table[fd].read_data = client_data; | |
823 | } | |
824 | if (type & COMM_SELECT_WRITE) { | |
825 | fd_table[fd].write_handler = handler; | |
826 | fd_table[fd].write_data = client_data; | |
827 | } | |
090089c4 | 828 | if (type & COMM_SELECT_LIFETIME) { |
829 | fd_table[fd].lifetime_handler = handler; | |
830 | fd_table[fd].lifetime_data = client_data; | |
831 | } | |
090089c4 | 832 | } |
833 | ||
b8d8561b | 834 | int |
67508012 | 835 | comm_get_select_handler(int fd, |
836 | unsigned int type, | |
e5f6c5c2 | 837 | void (**handler_ptr) _PARAMS((int, void *)), |
67508012 | 838 | void **client_data_ptr) |
090089c4 | 839 | { |
840 | if (type & COMM_SELECT_TIMEOUT) { | |
841 | *handler_ptr = fd_table[fd].timeout_handler; | |
842 | *client_data_ptr = fd_table[fd].timeout_data; | |
843 | } | |
844 | if (type & COMM_SELECT_READ) { | |
845 | *handler_ptr = fd_table[fd].read_handler; | |
846 | *client_data_ptr = fd_table[fd].read_data; | |
847 | } | |
848 | if (type & COMM_SELECT_WRITE) { | |
849 | *handler_ptr = fd_table[fd].write_handler; | |
850 | *client_data_ptr = fd_table[fd].write_data; | |
851 | } | |
090089c4 | 852 | if (type & COMM_SELECT_LIFETIME) { |
853 | *handler_ptr = fd_table[fd].lifetime_handler; | |
854 | *client_data_ptr = fd_table[fd].lifetime_data; | |
855 | } | |
856 | return 0; /* XXX What is meaningful? */ | |
857 | } | |
858 | ||
b8d8561b | 859 | void |
860 | comm_add_close_handler(int fd, PF handler, void *data) | |
30a4f2a8 | 861 | { |
862 | struct close_handler *new = xmalloc(sizeof(*new)); | |
863 | ||
44e323cc | 864 | debug(5, 5, "comm_add_close_handler: fd=%d handler=%p data=%p\n", fd, handler, data); |
090089c4 | 865 | |
30a4f2a8 | 866 | new->handler = handler; |
867 | new->data = data; | |
868 | new->next = fd_table[fd].close_handler; | |
869 | fd_table[fd].close_handler = new; | |
870 | } | |
871 | ||
b8d8561b | 872 | void |
873 | comm_remove_close_handler(int fd, PF handler, void *data) | |
090089c4 | 874 | { |
30a4f2a8 | 875 | struct close_handler *p, *last = NULL; |
876 | ||
877 | /* Find handler in list */ | |
878 | for (p = fd_table[fd].close_handler; p != NULL; last = p, p = p->next) | |
879 | if (p->handler == handler && p->data == data) | |
880 | break; /* This is our handler */ | |
881 | if (!p) | |
882 | fatal_dump("comm_remove_close_handler: Handler not found!\n"); | |
883 | ||
884 | /* Remove list entry */ | |
885 | if (last) | |
886 | last->next = p->next; | |
887 | else | |
888 | fd_table[fd].close_handler = p->next; | |
889 | safe_free(p); | |
890 | } | |
090089c4 | 891 | |
e90100aa | 892 | int |
893 | comm_set_mcast_ttl(int fd, int mcast_ttl) | |
894 | { | |
895 | #ifdef IP_MULTICAST_TTL | |
896 | debug(5, 10, "comm_set_mcast_ttl: setting multicast TTL %d on FD %d\n", | |
897 | mcast_ttl, fd); | |
898 | if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, | |
899 | (char *) &mcast_ttl, sizeof(char)) < 0) | |
881f7a6c | 900 | debug(50, 1, "comm_set_mcast_ttl: FD %d, TTL: %d: %s\n", |
e90100aa | 901 | fd, mcast_ttl, xstrerror()); |
902 | #endif | |
903 | return 0; | |
904 | } | |
905 | ||
906 | int | |
907 | comm_join_mcast_groups(int fd) | |
908 | { | |
909 | #ifdef IP_MULTICAST_TTL | |
910 | struct ip_mreq mr; | |
911 | wordlist *s = NULL; | |
912 | ||
913 | for (s = Config.mcast_group_list; s; s = s->next) { | |
ff8d0ea6 | 914 | debug(5, 10, "comm_join_mcast_groups: joining group %s on FD %d\n", |
8eb58c9c | 915 | s->key, fd); |
e90100aa | 916 | mr.imr_multiaddr.s_addr = inet_addr(s->key); |
917 | mr.imr_interface.s_addr = INADDR_ANY; | |
918 | if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, | |
919 | (char *) &mr, sizeof(struct ip_mreq)) < 0) | |
920 | debug(5, 1, "comm_join_mcast_groups: FD %d, addr: %s\n", | |
921 | fd, s->key); | |
922 | } | |
923 | #endif | |
924 | return 0; | |
925 | } | |
926 | ||
b8d8561b | 927 | static void |
928 | commSetNoLinger(int fd) | |
30a4f2a8 | 929 | { |
930 | struct linger L; | |
090089c4 | 931 | L.l_onoff = 0; /* off */ |
932 | L.l_linger = 0; | |
30a4f2a8 | 933 | if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0) |
881f7a6c | 934 | debug(50, 0, "commSetNoLinger: FD %d: %s\n", fd, xstrerror()); |
090089c4 | 935 | } |
936 | ||
b8d8561b | 937 | static void |
938 | commSetReuseAddr(int fd) | |
090089c4 | 939 | { |
940 | int on = 1; | |
30a4f2a8 | 941 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) |
881f7a6c | 942 | debug(50, 1, "commSetReuseAddr: FD %d: %s\n", fd, xstrerror()); |
090089c4 | 943 | } |
944 | ||
b8d8561b | 945 | static void |
946 | commSetTcpRcvbuf(int fd, int size) | |
f868539a | 947 | { |
948 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0) | |
881f7a6c | 949 | debug(50, 1, "commSetTcpRcvbuf: FD %d, SIZE %d: %s\n", |
b6f794d6 | 950 | fd, size, xstrerror()); |
f868539a | 951 | } |
952 | ||
b8d8561b | 953 | int |
954 | commSetNonBlocking(int fd) | |
30a4f2a8 | 955 | { |
731e4d49 | 956 | int flags; |
957 | if ((flags = fcntl(fd, F_GETFL)) < 0) { | |
881f7a6c | 958 | debug(50, 0, "FD %d: fcntl F_GETFL: %s\n", fd, xstrerror()); |
731e4d49 | 959 | return COMM_ERROR; |
960 | } | |
ed43818f | 961 | #if defined(O_NONBLOCK) && !defined(_SQUID_SUNOS_) && !defined(_SQUID_SOLARIS_) |
731e4d49 | 962 | if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { |
881f7a6c | 963 | debug(50, 0, "FD %d: error setting O_NONBLOCK: %s\n", fd, xstrerror()); |
30a4f2a8 | 964 | return COMM_ERROR; |
090089c4 | 965 | } |
966 | #else | |
731e4d49 | 967 | if (fcntl(fd, F_SETFL, flags | O_NDELAY) < 0) { |
881f7a6c | 968 | debug(50, 0, "FD %d: error setting O_NDELAY: %s\n", fd, xstrerror()); |
30a4f2a8 | 969 | return COMM_ERROR; |
090089c4 | 970 | } |
30a4f2a8 | 971 | #endif |
090089c4 | 972 | return 0; |
973 | } | |
974 | ||
b8d8561b | 975 | void |
976 | commSetCloseOnExec(int fd) | |
3ca60c86 | 977 | { |
978 | #ifdef FD_CLOEXEC | |
731e4d49 | 979 | int flags; |
980 | if ((flags = fcntl(fd, F_GETFL)) < 0) { | |
881f7a6c | 981 | debug(50, 0, "FD %d: fcntl F_GETFL: %s\n", fd, xstrerror()); |
24382924 | 982 | return; |
3ca60c86 | 983 | } |
24382924 | 984 | if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) |
881f7a6c | 985 | debug(50, 0, "FD %d: set close-on-exec failed: %s\n", fd, xstrerror()); |
3ca60c86 | 986 | #endif |
987 | } | |
988 | ||
e90100aa | 989 | #ifdef TCP_NODELAY |
990 | static void | |
991 | commSetTcpNoDelay(int fd) | |
992 | { | |
993 | int on = 1; | |
994 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0) | |
881f7a6c | 995 | debug(50, 1, "commSetTcpNoDelay: FD %d: %s\n", fd, xstrerror()); |
e90100aa | 996 | } |
997 | #endif | |
998 | ||
090089c4 | 999 | /* |
1000 | * the fd_lifetime is used as a hardlimit to timeout dead sockets. | |
1001 | * The basic problem is that many WWW clients are abusive and | |
b8de7ebe | 1002 | * it results in squid having lots of CLOSE_WAIT states. Until |
090089c4 | 1003 | * we can find a better solution, we give all asciiPort or |
b8de7ebe | 1004 | * squid initiated clients a maximum lifetime. |
090089c4 | 1005 | */ |
b8d8561b | 1006 | int |
0673c0ba | 1007 | comm_init(void) |
090089c4 | 1008 | { |
30a4f2a8 | 1009 | int i; |
090089c4 | 1010 | |
431d84c5 | 1011 | fd_table = xcalloc(SQUID_MAXFD, sizeof(FD_ENTRY)); |
1012 | meta_data.misc += SQUID_MAXFD * sizeof(FD_ENTRY); | |
090089c4 | 1013 | /* Keep a few file descriptors free so that we don't run out of FD's |
1014 | * after accepting a client but before it opens a socket or a file. | |
431d84c5 | 1015 | * Since SQUID_MAXFD can be as high as several thousand, don't waste them */ |
1016 | RESERVED_FD = min(100, SQUID_MAXFD / 4); | |
090089c4 | 1017 | /* hardwired lifetimes */ |
431d84c5 | 1018 | fd_lifetime = xmalloc(sizeof(int) * SQUID_MAXFD); |
1019 | for (i = 0; i < SQUID_MAXFD; i++) | |
090089c4 | 1020 | comm_set_fd_lifetime(i, -1); /* denotes invalid */ |
431d84c5 | 1021 | meta_data.misc += SQUID_MAXFD * sizeof(int); |
055f4d4d | 1022 | zero_tv.tv_sec = 0; |
1023 | zero_tv.tv_usec = 0; | |
090089c4 | 1024 | return 0; |
1025 | } | |
1026 | ||
1027 | ||
1028 | /* | |
1029 | * examine_select - debug routine. | |
1030 | * | |
1031 | * I spend the day chasing this core dump that occurs when both the client | |
1032 | * and the server side of a cache fetch simultaneoulsy abort the | |
1033 | * connection. While I haven't really studied the code to figure out how | |
1034 | * it happens, the snippet below may prevent the cache from exitting: | |
1035 | * | |
1036 | * Call this from where the select loop fails. | |
1037 | */ | |
b8d8561b | 1038 | static int |
5742d7c9 | 1039 | examine_select(fd_set * readfds, fd_set * writefds) |
090089c4 | 1040 | { |
1041 | int fd = 0; | |
bbc5ea8f | 1042 | fd_set read_x; |
1043 | fd_set write_x; | |
090089c4 | 1044 | int num; |
1045 | struct timeval tv; | |
30a4f2a8 | 1046 | struct close_handler *ch = NULL; |
1047 | struct close_handler *next = NULL; | |
117531df | 1048 | FD_ENTRY *f = NULL; |
090089c4 | 1049 | |
d1f14731 | 1050 | debug(5, 0, "examine_select: Examining open file descriptors...\n"); |
431d84c5 | 1051 | for (fd = 0; fd < SQUID_MAXFD; fd++) { |
090089c4 | 1052 | FD_ZERO(&read_x); |
1053 | FD_ZERO(&write_x); | |
090089c4 | 1054 | tv.tv_sec = tv.tv_usec = 0; |
af00901c | 1055 | if (FD_ISSET(fd, readfds)) |
090089c4 | 1056 | FD_SET(fd, &read_x); |
af00901c | 1057 | else if (FD_ISSET(fd, writefds)) |
1058 | FD_SET(fd, &write_x); | |
af00901c | 1059 | else |
1060 | continue; | |
431d84c5 | 1061 | num = select(SQUID_MAXFD, &read_x, &write_x, NULL, &tv); |
af00901c | 1062 | if (num > -1) { |
1063 | debug(5, 5, "FD %d is valid.\n", fd); | |
1064 | continue; | |
1065 | } | |
1066 | f = &fd_table[fd]; | |
1067 | debug(5, 0, "WARNING: FD %d has handlers, but it's invalid.\n", fd); | |
1068 | debug(5, 0, "FD %d is a %s\n", fd, fdstatTypeStr[fdstatGetType(fd)]); | |
1069 | debug(5, 0, "--> %s\n", fd_note(fd, NULL)); | |
d0217c9b | 1070 | debug(5, 0, "lifetm:%p tmout:%p read:%p write:%p\n", |
af00901c | 1071 | f->lifetime_handler, |
1072 | f->timeout_handler, | |
1073 | f->read_handler, | |
d0217c9b | 1074 | f->write_handler); |
af00901c | 1075 | for (ch = f->close_handler; ch; ch = ch->next) |
1076 | debug(5, 0, " close handler: %p\n", ch->handler); | |
1077 | if (f->close_handler) { | |
1078 | for (ch = f->close_handler; ch; ch = next) { | |
1079 | next = ch->next; | |
1080 | ch->handler(fd, ch->data); | |
1081 | safe_free(ch); | |
090089c4 | 1082 | } |
af00901c | 1083 | } else if (f->lifetime_handler) { |
1084 | debug(5, 0, "examine_select: Calling Lifetime Handler\n"); | |
1085 | f->lifetime_handler(fd, f->lifetime_data); | |
1086 | } else if (f->timeout_handler) { | |
1087 | debug(5, 0, "examine_select: Calling Timeout Handler\n"); | |
1088 | f->timeout_handler(fd, f->timeout_data); | |
090089c4 | 1089 | } |
af00901c | 1090 | f->close_handler = NULL; |
1091 | f->lifetime_handler = NULL; | |
1092 | f->timeout_handler = NULL; | |
1093 | f->read_handler = NULL; | |
1094 | f->write_handler = NULL; | |
af00901c | 1095 | FD_CLR(fd, readfds); |
1096 | FD_CLR(fd, writefds); | |
090089c4 | 1097 | } |
090089c4 | 1098 | return 0; |
1099 | } | |
1100 | ||
b8d8561b | 1101 | char * |
0ee4272b | 1102 | fd_note(int fd, const char *s) |
090089c4 | 1103 | { |
1104 | if (s == NULL) | |
1105 | return (fd_table[fd].ascii_note); | |
f2052513 | 1106 | xstrncpy(fd_table[fd].ascii_note, s, FD_ASCII_NOTE_SZ); |
090089c4 | 1107 | return (NULL); |
1108 | } | |
1109 | ||
b8d8561b | 1110 | static void |
0673c0ba | 1111 | checkTimeouts(void) |
090089c4 | 1112 | { |
1113 | int fd; | |
ff8d0ea6 | 1114 | PF hdl = NULL; |
4d64d74a | 1115 | FD_ENTRY *f = NULL; |
56c6c1f6 | 1116 | void *data; |
090089c4 | 1117 | /* scan for timeout */ |
431d84c5 | 1118 | for (fd = 0; fd < SQUID_MAXFD; ++fd) { |
4d64d74a | 1119 | f = &fd_table[fd]; |
56c6c1f6 | 1120 | if ((hdl = f->timeout_handler) == NULL) |
1121 | continue; | |
1122 | if (f->timeout_time > squid_curtime) | |
1123 | continue; | |
1124 | debug(5, 5, "checkTimeouts: FD %d timeout at %d\n", fd, squid_curtime); | |
1125 | data = f->timeout_data; | |
1126 | f->timeout_handler = NULL; | |
1127 | f->timeout_data = NULL; | |
1128 | hdl(fd, data); | |
090089c4 | 1129 | } |
1130 | } | |
1131 | ||
b8d8561b | 1132 | static void |
0673c0ba | 1133 | checkLifetimes(void) |
090089c4 | 1134 | { |
1135 | int fd; | |
090089c4 | 1136 | time_t lft; |
9864ee44 | 1137 | FD_ENTRY *fde = NULL; |
090089c4 | 1138 | |
ff8d0ea6 | 1139 | PF hdl = NULL; |
30a4f2a8 | 1140 | |
431d84c5 | 1141 | for (fd = 0; fd < SQUID_MAXFD; fd++) { |
30a4f2a8 | 1142 | if ((lft = comm_get_fd_lifetime(fd)) == -1) |
1143 | continue; | |
1144 | if (lft > squid_curtime) | |
1145 | continue; | |
1146 | debug(5, 5, "checkLifetimes: FD %d Expired\n", fd); | |
9864ee44 | 1147 | fde = &fd_table[fd]; |
ff8d0ea6 | 1148 | if ((hdl = fde->lifetime_handler) != NULL) { |
30a4f2a8 | 1149 | debug(5, 5, "checkLifetimes: FD %d: Calling lifetime handler\n", fd); |
ff8d0ea6 | 1150 | hdl(fd, fde->lifetime_data); |
9864ee44 | 1151 | fde->lifetime_handler = NULL; |
ff8d0ea6 | 1152 | } else if ((hdl = fde->read_handler) != NULL) { |
30a4f2a8 | 1153 | debug(5, 5, "checkLifetimes: FD %d: Calling read handler\n", fd); |
ff8d0ea6 | 1154 | hdl(fd, fd_table[fd].read_data); |
caebbe00 | 1155 | fd_table[fd].read_handler = NULL; |
ff8d0ea6 | 1156 | } else if ((hdl = fd_table[fd].write_handler)) { |
30a4f2a8 | 1157 | debug(5, 5, "checkLifetimes: FD %d: Calling write handler\n", fd); |
ff8d0ea6 | 1158 | hdl(fd, fde->write_data); |
9864ee44 | 1159 | fde->write_handler = NULL; |
30a4f2a8 | 1160 | } else { |
1161 | debug(5, 5, "checkLifetimes: FD %d: No handlers, calling comm_close()\n", fd); | |
1162 | comm_close(fd); | |
1163 | comm_cleanup_fd_entry(fd); | |
1164 | } | |
9864ee44 | 1165 | if (fde->openned) { |
30a4f2a8 | 1166 | /* still opened */ |
1167 | debug(5, 5, "checkLifetimes: FD %d: Forcing comm_close()\n", fd); | |
1168 | comm_close(fd); | |
1169 | comm_cleanup_fd_entry(fd); | |
090089c4 | 1170 | } |
1171 | } | |
1172 | } | |
1173 | ||
1174 | /* | |
1175 | * Reserve_More_FDs() called when acceopt(), open(), or socket is failing | |
1176 | */ | |
b8d8561b | 1177 | static void |
0673c0ba | 1178 | Reserve_More_FDs(void) |
090089c4 | 1179 | { |
431d84c5 | 1180 | if (RESERVED_FD < SQUID_MAXFD - 64) { |
090089c4 | 1181 | RESERVED_FD = RESERVED_FD + 1; |
431d84c5 | 1182 | } else if (RESERVED_FD == SQUID_MAXFD - 64) { |
090089c4 | 1183 | RESERVED_FD = RESERVED_FD + 1; |
d1f14731 | 1184 | debug(5, 0, "Don't you have a tiny open-file table size of %d\n", |
431d84c5 | 1185 | SQUID_MAXFD - RESERVED_FD); |
090089c4 | 1186 | } |
1187 | } | |
1188 | ||
30a4f2a8 | 1189 | /* Read from FD. */ |
b8d8561b | 1190 | static int |
1191 | commHandleRead(int fd, RWStateData * state) | |
30a4f2a8 | 1192 | { |
1193 | int len; | |
1194 | ||
1195 | len = read(fd, state->buf + state->offset, state->size - state->offset); | |
1196 | debug(5, 5, "commHandleRead: FD %d: read %d bytes\n", fd, len); | |
1197 | ||
1198 | if (len <= 0) { | |
9864ee44 | 1199 | if (errno == EWOULDBLOCK || errno == EAGAIN) { |
30a4f2a8 | 1200 | /* reschedule self */ |
b177367b | 1201 | commSetSelect(fd, |
30a4f2a8 | 1202 | COMM_SELECT_READ, |
1203 | (PF) commHandleRead, | |
b177367b | 1204 | state, |
85d7ea98 | 1205 | 0); |
30a4f2a8 | 1206 | return COMM_OK; |
9864ee44 | 1207 | } else { |
30a4f2a8 | 1208 | /* Len == 0 means connection closed; otherwise would not have been |
1209 | * called by comm_select(). */ | |
881f7a6c | 1210 | debug(50, len == 0 ? 2 : 1, |
a56a3abe | 1211 | "commHandleRead: FD %d: read failure: %s\n", |
1212 | fd, | |
1213 | len == 0 ? "connection closed" : xstrerror()); | |
1214 | RWStateCallbackAndFree(fd, COMM_ERROR); | |
30a4f2a8 | 1215 | return COMM_ERROR; |
1216 | } | |
1217 | } | |
1218 | state->offset += len; | |
1219 | ||
1220 | /* Call handler if we have read enough */ | |
1221 | if (state->offset >= state->size || state->handle_immed) { | |
a56a3abe | 1222 | RWStateCallbackAndFree(fd, COMM_OK); |
30a4f2a8 | 1223 | } else { |
1224 | /* Reschedule until we are done */ | |
b177367b | 1225 | commSetSelect(fd, |
30a4f2a8 | 1226 | COMM_SELECT_READ, |
1227 | (PF) commHandleRead, | |
b177367b | 1228 | state, |
85d7ea98 | 1229 | 0); |
30a4f2a8 | 1230 | } |
1231 | return COMM_OK; | |
1232 | } | |
1233 | ||
1234 | /* Select for reading on FD, until SIZE bytes are received. Call | |
1235 | * HANDLER when complete. */ | |
b8d8561b | 1236 | void |
1237 | comm_read(int fd, | |
1238 | char *buf, | |
1239 | int size, | |
1240 | int timeout, | |
1241 | int immed, | |
1242 | rw_complete_handler * handler, | |
1243 | void *handler_data) | |
30a4f2a8 | 1244 | { |
1245 | RWStateData *state = NULL; | |
1246 | ||
1247 | debug(5, 5, "comm_read: FD %d: sz %d: tout %d: hndl %p: data %p.\n", | |
1248 | fd, size, timeout, handler, handler_data); | |
1249 | ||
0757f0b5 | 1250 | if (fd_table[fd].rwstate) { |
1251 | debug(5, 1, "comm_read: WARNING! FD %d: A comm_read/comm_write is already active.\n", fd); | |
a56a3abe | 1252 | RWStateCallbackAndFree(fd, COMM_ERROR); |
30a4f2a8 | 1253 | } |
1254 | state = xcalloc(1, sizeof(RWStateData)); | |
30a4f2a8 | 1255 | state->buf = buf; |
1256 | state->size = size; | |
1257 | state->offset = 0; | |
1258 | state->handler = handler; | |
1259 | state->timeout = timeout; | |
1260 | state->handle_immed = immed; | |
1261 | state->time = squid_curtime; | |
1262 | state->handler_data = handler_data; | |
9864ee44 | 1263 | state->free = NULL; |
a56a3abe | 1264 | fd_table[fd].rwstate = state; |
b177367b | 1265 | commSetSelect(fd, |
30a4f2a8 | 1266 | COMM_SELECT_READ, |
1267 | (PF) commHandleRead, | |
b177367b | 1268 | state, |
1269 | 0); | |
30a4f2a8 | 1270 | } |
1271 | ||
1272 | /* Write to FD. */ | |
b8d8561b | 1273 | static void |
1274 | commHandleWrite(int fd, RWStateData * state) | |
30a4f2a8 | 1275 | { |
1276 | int len = 0; | |
1277 | int nleft; | |
1278 | ||
1279 | debug(5, 5, "commHandleWrite: FD %d: state=%p, off %d, sz %d.\n", | |
1280 | fd, state, state->offset, state->size); | |
1281 | ||
1282 | nleft = state->size - state->offset; | |
1283 | len = write(fd, state->buf + state->offset, nleft); | |
1284 | ||
1285 | if (len == 0) { | |
1286 | /* Note we even call write if nleft == 0 */ | |
1287 | /* We're done */ | |
1288 | if (nleft != 0) | |
1289 | debug(5, 2, "commHandleWrite: FD %d: write failure: connection closed with %d bytes remaining.\n", fd, nleft); | |
a56a3abe | 1290 | RWStateCallbackAndFree(fd, nleft ? COMM_ERROR : COMM_OK); |
30a4f2a8 | 1291 | } else if (len < 0) { |
1292 | /* An error */ | |
1293 | if (errno == EWOULDBLOCK || errno == EAGAIN) { | |
881f7a6c | 1294 | debug(50, 10, "commHandleWrite: FD %d: write failure: %s.\n", |
30a4f2a8 | 1295 | fd, xstrerror()); |
b177367b | 1296 | commSetSelect(fd, |
30a4f2a8 | 1297 | COMM_SELECT_WRITE, |
1298 | (PF) commHandleWrite, | |
b177367b | 1299 | state, |
85d7ea98 | 1300 | 0); |
9864ee44 | 1301 | } else { |
881f7a6c | 1302 | debug(50, 2, "commHandleWrite: FD %d: write failure: %s.\n", |
9864ee44 | 1303 | fd, xstrerror()); |
a56a3abe | 1304 | RWStateCallbackAndFree(fd, COMM_ERROR); |
30a4f2a8 | 1305 | } |
30a4f2a8 | 1306 | } else { |
1307 | /* A successful write, continue */ | |
1308 | state->offset += len; | |
1309 | if (state->offset < state->size) { | |
1310 | /* Not done, reinstall the write handler and write some more */ | |
b177367b | 1311 | commSetSelect(fd, |
30a4f2a8 | 1312 | COMM_SELECT_WRITE, |
1313 | (PF) commHandleWrite, | |
b177367b | 1314 | state, |
85d7ea98 | 1315 | 0); |
9864ee44 | 1316 | } else { |
a56a3abe | 1317 | RWStateCallbackAndFree(fd, COMM_OK); |
30a4f2a8 | 1318 | } |
30a4f2a8 | 1319 | } |
1320 | } | |
1321 | ||
1322 | ||
1323 | ||
1324 | /* Select for Writing on FD, until SIZE bytes are sent. Call | |
1325 | * * HANDLER when complete. */ | |
b8d8561b | 1326 | void |
86ee2017 | 1327 | comm_write(int fd, char *buf, int size, int timeout, rw_complete_handler * handler, void *handler_data, void (*free_func) (void *)) |
30a4f2a8 | 1328 | { |
1329 | RWStateData *state = NULL; | |
1330 | ||
1331 | debug(5, 5, "comm_write: FD %d: sz %d: tout %d: hndl %p: data %p.\n", | |
1332 | fd, size, timeout, handler, handler_data); | |
1333 | ||
0757f0b5 | 1334 | if (fd_table[fd].rwstate) { |
1e72c32c | 1335 | debug(5, 1, "WARNING! FD %d: A comm_read/comm_write is already active.\n", fd); |
a56a3abe | 1336 | RWStateCallbackAndFree(fd, COMM_ERROR); |
30a4f2a8 | 1337 | } |
1338 | state = xcalloc(1, sizeof(RWStateData)); | |
1339 | state->buf = buf; | |
1340 | state->size = size; | |
1341 | state->offset = 0; | |
1342 | state->handler = handler; | |
1343 | state->timeout = timeout; | |
1344 | state->time = squid_curtime; | |
1345 | state->handler_data = handler_data; | |
86ee2017 | 1346 | state->free = free_func; |
a56a3abe | 1347 | fd_table[fd].rwstate = state; |
b177367b | 1348 | commSetSelect(fd, |
30a4f2a8 | 1349 | COMM_SELECT_WRITE, |
1350 | (PF) commHandleWrite, | |
b177367b | 1351 | fd_table[fd].rwstate, |
1352 | 0); | |
30a4f2a8 | 1353 | } |
0a21bd84 | 1354 | |
1355 | void | |
1356 | commFreeMemory(void) | |
1357 | { | |
3ba50d75 | 1358 | safe_free(fd_table); |
df92bd2a | 1359 | safe_free(fd_lifetime); |
0a21bd84 | 1360 | } |