]>
Commit | Line | Data |
---|---|---|
52e1d7e2 | 1 | |
30a4f2a8 | 2 | /* |
3ca60c86 | 3 | * $Id: comm.cc,v 1.61 1996/08/30 22:36:21 wessels Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 5 Socket Functions | |
6 | * AUTHOR: Harvest Derived | |
7 | * | |
8 | * SQUID Internet Object Cache http://www.nlanr.net/Squid/ | |
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; |
30a4f2a8 | 118 | struct in_addr any_addr; |
a1ab1e71 | 119 | struct in_addr no_addr; |
090089c4 | 120 | |
121 | #define min(x,y) ((x)<(y)? (x) : (y)) | |
122 | #define max(a,b) ((a)>(b)? (a) : (b)) | |
123 | ||
30a4f2a8 | 124 | struct _RWStateData { |
125 | char *buf; | |
126 | long size; | |
127 | long offset; | |
128 | int timeout; /* XXX Not used at present. */ | |
129 | time_t time; /* XXX Not used at present. */ | |
130 | rw_complete_handler *handler; | |
131 | void *handler_data; | |
132 | int handle_immed; | |
4a63c85f | 133 | void (*free) (void *); |
30a4f2a8 | 134 | }; |
090089c4 | 135 | |
136 | /* GLOBAL */ | |
090089c4 | 137 | FD_ENTRY *fd_table = NULL; /* also used in disk.c */ |
138 | ||
139 | /* STATIC */ | |
31feba03 | 140 | static void checkTimeouts _PARAMS((void)); |
055f4d4d | 141 | static void checkLifetimes _PARAMS((void)); |
090089c4 | 142 | static void Reserve_More_FDs _PARAMS((void)); |
30a4f2a8 | 143 | static void commSetReuseAddr _PARAMS((int)); |
090089c4 | 144 | static int examine_select _PARAMS((fd_set *, fd_set *, fd_set *)); |
30a4f2a8 | 145 | static void commSetNoLinger _PARAMS((int)); |
055f4d4d | 146 | static void comm_select_incoming _PARAMS((void)); |
30a4f2a8 | 147 | static int commBind _PARAMS((int s, struct in_addr, u_short port)); |
0757f0b5 | 148 | static void RWStateCallbackAndFree _PARAMS((int fd, RWStateData *, int code)); |
30a4f2a8 | 149 | #ifdef TCP_NODELAY |
150 | static void commSetTcpNoDelay _PARAMS((int)); | |
151 | #endif | |
f868539a | 152 | static void commSetTcpRcvbuf _PARAMS((int, int)); |
30a4f2a8 | 153 | |
154 | static int *fd_lifetime = NULL; | |
155 | static struct timeval zero_tv; | |
090089c4 | 156 | |
0757f0b5 | 157 | static void RWStateCallbackAndFree(fd, RWState, code) |
9864ee44 | 158 | int fd; |
159 | RWStateData *RWState; | |
160 | int code; | |
161 | { | |
162 | rw_complete_handler *callback = NULL; | |
163 | if (RWState == NULL) | |
164 | return; | |
165 | if (RWState->free) { | |
166 | RWState->free(RWState->buf); | |
167 | RWState->buf = NULL; | |
168 | } | |
169 | callback = RWState->handler; | |
170 | RWState->handler = NULL; | |
171 | if (callback) { | |
172 | callback(fd, | |
173 | RWState->buf, | |
174 | RWState->offset, | |
175 | code, | |
176 | RWState->handler_data); | |
177 | } | |
178 | safe_free(RWState); | |
179 | } | |
180 | ||
090089c4 | 181 | /* Return the local port associated with fd. */ |
30a4f2a8 | 182 | u_short comm_local_port(fd) |
090089c4 | 183 | int fd; |
184 | { | |
185 | struct sockaddr_in addr; | |
186 | int addr_len = 0; | |
9864ee44 | 187 | FD_ENTRY *fde = &fd_table[fd]; |
090089c4 | 188 | |
090089c4 | 189 | /* If the fd is closed already, just return */ |
9864ee44 | 190 | if (!fde->openned) { |
30a4f2a8 | 191 | debug(5, 0, "comm_local_port: FD %d has been closed.\n", fd); |
192 | return 0; | |
090089c4 | 193 | } |
9864ee44 | 194 | if (fde->local_port) |
195 | return fde->local_port; | |
090089c4 | 196 | addr_len = sizeof(addr); |
197 | if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) { | |
30a4f2a8 | 198 | debug(5, 1, "comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror()); |
199 | return 0; | |
090089c4 | 200 | } |
30a4f2a8 | 201 | debug(5, 6, "comm_local_port: FD %d: sockaddr %u.\n", fd, addr.sin_addr.s_addr); |
9864ee44 | 202 | fde->local_port = ntohs(addr.sin_port); |
203 | return fde->local_port; | |
090089c4 | 204 | } |
205 | ||
30a4f2a8 | 206 | static int commBind(s, in_addr, port) |
090089c4 | 207 | int s; |
30a4f2a8 | 208 | struct in_addr in_addr; |
209 | u_short port; | |
090089c4 | 210 | { |
211 | struct sockaddr_in S; | |
090089c4 | 212 | |
090089c4 | 213 | memset(&S, '\0', sizeof(S)); |
214 | S.sin_family = AF_INET; | |
215 | S.sin_port = htons(port); | |
30a4f2a8 | 216 | S.sin_addr = in_addr; |
090089c4 | 217 | if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0) |
218 | return COMM_OK; | |
30a4f2a8 | 219 | debug(5, 0, "commBind: Cannot bind socket FD %d to %s:%d: %s\n", |
090089c4 | 220 | s, |
30a4f2a8 | 221 | S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr), |
090089c4 | 222 | port, xstrerror()); |
223 | return COMM_ERROR; | |
224 | } | |
225 | ||
226 | /* Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE | |
227 | * is OR of flags specified in comm.h. */ | |
30a4f2a8 | 228 | int comm_open(io_type, addr, port, note) |
090089c4 | 229 | unsigned int io_type; |
30a4f2a8 | 230 | struct in_addr addr; |
231 | u_short port; | |
090089c4 | 232 | char *note; |
233 | { | |
234 | int new_socket; | |
235 | FD_ENTRY *conn = NULL; | |
236 | int sock_type = io_type & COMM_DGRAM ? SOCK_DGRAM : SOCK_STREAM; | |
b6f794d6 | 237 | int tcp_rcv_bufsz = Config.tcpRcvBufsz; |
090089c4 | 238 | |
239 | /* Create socket for accepting new connections. */ | |
240 | if ((new_socket = socket(AF_INET, sock_type, 0)) < 0) { | |
241 | /* Increase the number of reserved fd's if calls to socket() | |
242 | * are failing because the open file table is full. This | |
243 | * limits the number of simultaneous clients */ | |
244 | switch (errno) { | |
245 | case ENFILE: | |
246 | case EMFILE: | |
d1f14731 | 247 | debug(5, 1, "comm_open: socket failure: %s\n", xstrerror()); |
090089c4 | 248 | Reserve_More_FDs(); |
249 | break; | |
250 | default: | |
d1f14731 | 251 | debug(5, 0, "comm_open: socket failure: %s\n", xstrerror()); |
090089c4 | 252 | } |
253 | return (COMM_ERROR); | |
254 | } | |
255 | /* update fdstat */ | |
30a4f2a8 | 256 | fdstat_open(new_socket, FD_SOCKET); |
090089c4 | 257 | |
258 | conn = &fd_table[new_socket]; | |
259 | memset(conn, '\0', sizeof(FD_ENTRY)); | |
30a4f2a8 | 260 | if (note) |
261 | fd_note(new_socket, note); | |
090089c4 | 262 | conn->openned = 1; |
263 | ||
3ca60c86 | 264 | if (!(io_type & COMM_NOCLOEXEC)) |
265 | commSetCloseOnExec(new_socket); | |
090089c4 | 266 | if (port > 0) { |
30a4f2a8 | 267 | commSetNoLinger(new_socket); |
268 | if (do_reuse) | |
090089c4 | 269 | commSetReuseAddr(new_socket); |
090089c4 | 270 | } |
30a4f2a8 | 271 | if (addr.s_addr != INADDR_NONE) |
272 | if (commBind(new_socket, addr, port) != COMM_OK) | |
273 | return COMM_ERROR; | |
274 | conn->local_port = port; | |
090089c4 | 275 | |
30a4f2a8 | 276 | if (io_type & COMM_NONBLOCKING) |
277 | if (commSetNonBlocking(new_socket) == COMM_ERROR) | |
278 | return COMM_ERROR; | |
279 | #ifdef TCP_NODELAY | |
280 | if (sock_type == SOCK_STREAM) | |
281 | commSetTcpNoDelay(new_socket); | |
282 | #endif | |
f868539a | 283 | if (tcp_rcv_bufsz > 0 && sock_type == SOCK_STREAM) |
284 | commSetTcpRcvbuf(new_socket, tcp_rcv_bufsz); | |
090089c4 | 285 | conn->comm_type = io_type; |
286 | return new_socket; | |
287 | } | |
288 | ||
289 | /* | |
30a4f2a8 | 290 | * NOTE: set the listen queue to FD_SETSIZE/4 and rely on the kernel to |
090089c4 | 291 | * impose an upper limit. Solaris' listen(3n) page says it has |
292 | * no limit on this parameter, but sys/socket.h sets SOMAXCONN | |
293 | * to 5. HP-UX currently has a limit of 20. SunOS is 5 and | |
294 | * OSF 3.0 is 8. | |
295 | */ | |
296 | int comm_listen(sock) | |
297 | int sock; | |
298 | { | |
299 | int x; | |
30a4f2a8 | 300 | if ((x = listen(sock, FD_SETSIZE >> 2)) < 0) { |
983061ed | 301 | debug(5, 0, "comm_listen: listen(%d, %d): %s\n", |
30a4f2a8 | 302 | FD_SETSIZE >> 2, |
090089c4 | 303 | sock, xstrerror()); |
304 | return x; | |
305 | } | |
306 | return sock; | |
307 | } | |
308 | ||
090089c4 | 309 | /* Connect SOCK to specified DEST_PORT at DEST_HOST. */ |
310 | int comm_connect(sock, dest_host, dest_port) | |
311 | int sock; /* Type of communication to use. */ | |
312 | char *dest_host; /* Server's host name. */ | |
30a4f2a8 | 313 | u_short dest_port; /* Server's port. */ |
090089c4 | 314 | { |
315 | struct hostent *hp = NULL; | |
316 | static struct sockaddr_in to_addr; | |
317 | ||
318 | /* Set up the destination socket address for message to send to. */ | |
319 | to_addr.sin_family = AF_INET; | |
320 | ||
30a4f2a8 | 321 | if ((hp = ipcache_gethostbyname(dest_host, IP_BLOCKING_LOOKUP)) == 0) { |
723965e4 | 322 | debug(5, 3, "comm_connect: Failure to lookup host: %s.\n", dest_host); |
090089c4 | 323 | return (COMM_ERROR); |
324 | } | |
30a4f2a8 | 325 | xmemcpy(&to_addr.sin_addr, hp->h_addr, hp->h_length); |
090089c4 | 326 | to_addr.sin_port = htons(dest_port); |
81e07ca4 | 327 | if (Config.Log.log_fqdn) |
e62d2dea | 328 | fqdncache_gethostbyaddr(to_addr.sin_addr, FQDN_LOOKUP_IF_MISS); |
090089c4 | 329 | return comm_connect_addr(sock, &to_addr); |
330 | } | |
331 | ||
332 | int comm_set_fd_lifetime(fd, lifetime) | |
333 | int fd; | |
334 | int lifetime; | |
335 | { | |
30a4f2a8 | 336 | debug(5, 3, "comm_set_fd_lifetime: FD %d lft %d\n", fd, lifetime); |
337 | if (fd < 0 || fd > FD_SETSIZE) | |
090089c4 | 338 | return 0; |
339 | if (lifetime < 0) | |
340 | return fd_lifetime[fd] = -1; | |
30a4f2a8 | 341 | if (shutdown_pending || reread_pending) { |
342 | /* don't increase the lifetime if something pending */ | |
343 | if (fd_lifetime[fd] > -1 && (fd_lifetime[fd] - squid_curtime) < lifetime) | |
344 | return fd_lifetime[fd]; | |
345 | } | |
b8de7ebe | 346 | return fd_lifetime[fd] = (int) squid_curtime + lifetime; |
090089c4 | 347 | } |
348 | ||
349 | int comm_get_fd_lifetime(fd) | |
350 | int fd; | |
351 | { | |
352 | if (fd < 0) | |
353 | return 0; | |
354 | return fd_lifetime[fd]; | |
355 | } | |
356 | ||
357 | int comm_get_fd_timeout(fd) | |
358 | int fd; | |
359 | { | |
360 | if (fd < 0) | |
361 | return 0; | |
362 | return fd_table[fd].timeout_time; | |
363 | } | |
364 | ||
365 | int comm_connect_addr(sock, address) | |
366 | int sock; | |
367 | struct sockaddr_in *address; | |
368 | { | |
369 | int status = COMM_OK; | |
370 | FD_ENTRY *conn = &fd_table[sock]; | |
371 | int len; | |
372 | int x; | |
373 | int lft; | |
374 | ||
375 | /* sanity check */ | |
376 | if (ntohs(address->sin_port) == 0) { | |
d1f14731 | 377 | debug(5, 10, "comm_connect_addr: %s:%d: URL uses port 0?\n", |
090089c4 | 378 | inet_ntoa(address->sin_addr), ntohs(address->sin_port)); |
379 | errno = 0; | |
380 | return COMM_ERROR; | |
381 | } | |
382 | /* Establish connection. */ | |
383 | if (connect(sock, (struct sockaddr *) address, sizeof(struct sockaddr_in)) < 0) | |
384 | switch (errno) { | |
385 | case EALREADY: | |
386 | return COMM_ERROR; | |
387 | /* NOTREACHED */ | |
30a4f2a8 | 388 | #if EAGAIN != EWOULDBLOCK |
389 | case EAGAIN: | |
390 | #endif | |
391 | case EWOULDBLOCK: | |
090089c4 | 392 | case EINPROGRESS: |
393 | status = EINPROGRESS; | |
394 | break; | |
395 | case EISCONN: | |
396 | status = COMM_OK; | |
397 | break; | |
398 | case EINVAL: | |
399 | len = sizeof(x); | |
400 | if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &x, &len) >= 0) | |
401 | errno = x; | |
402 | default: | |
723965e4 | 403 | debug(5, 3, "connect: %s:%d: %s.\n", |
28ab0c0a | 404 | fqdnFromAddr(address->sin_addr), |
090089c4 | 405 | ntohs(address->sin_port), |
406 | xstrerror()); | |
407 | return COMM_ERROR; | |
408 | } | |
7450ce42 | 409 | strcpy(conn->ipaddr, inet_ntoa(address->sin_addr)); |
410 | conn->remote_port = ntohs(address->sin_port); | |
090089c4 | 411 | /* set the lifetime for this client */ |
412 | if (status == COMM_OK) { | |
b6f794d6 | 413 | lft = comm_set_fd_lifetime(sock, Config.lifetimeDefault); |
7450ce42 | 414 | debug(5, 10, "comm_connect_addr: FD %d connected to %s:%d, lifetime %d.\n", |
415 | sock, conn->ipaddr, conn->remote_port, lft); | |
090089c4 | 416 | } else if (status == EINPROGRESS) { |
b6f794d6 | 417 | lft = comm_set_fd_lifetime(sock, Config.connectTimeout); |
d1f14731 | 418 | debug(5, 10, "comm_connect_addr: FD %d connection pending, lifetime %d\n", |
090089c4 | 419 | sock, lft); |
420 | } | |
421 | /* Add new socket to list of open sockets. */ | |
090089c4 | 422 | conn->sender = 1; |
090089c4 | 423 | return status; |
424 | } | |
425 | ||
426 | /* Wait for an incoming connection on FD. FD should be a socket returned | |
427 | * from comm_listen. */ | |
428 | int comm_accept(fd, peer, me) | |
429 | int fd; | |
430 | struct sockaddr_in *peer; | |
431 | struct sockaddr_in *me; | |
432 | { | |
433 | int sock; | |
1f9afe33 | 434 | struct sockaddr_in P; |
435 | struct sockaddr_in M; | |
090089c4 | 436 | int Slen; |
1f9afe33 | 437 | FD_ENTRY *conn = NULL; |
090089c4 | 438 | FD_ENTRY *listener = &fd_table[fd]; |
439 | ||
1f9afe33 | 440 | Slen = sizeof(P); |
441 | while ((sock = accept(fd, (struct sockaddr *) &P, &Slen)) < 0) { | |
090089c4 | 442 | switch (errno) { |
443 | #if EAGAIN != EWOULDBLOCK | |
444 | case EAGAIN: | |
445 | #endif | |
446 | case EWOULDBLOCK: | |
447 | return COMM_NOMESSAGE; | |
448 | case EINTR: | |
449 | break; /* if accept interrupted, try again */ | |
450 | case ENFILE: | |
451 | case EMFILE: | |
452 | Reserve_More_FDs(); | |
453 | return COMM_ERROR; | |
454 | default: | |
d1f14731 | 455 | debug(5, 1, "comm_accept: FD %d: accept failure: %s\n", |
090089c4 | 456 | fd, xstrerror()); |
457 | return COMM_ERROR; | |
458 | } | |
459 | } | |
460 | ||
461 | if (peer) | |
1f9afe33 | 462 | *peer = P; |
090089c4 | 463 | |
464 | if (me) { | |
1f9afe33 | 465 | Slen = sizeof(M); |
466 | memset(&M, '\0', Slen); | |
467 | getsockname(sock, (struct sockaddr *) &M, &Slen); | |
468 | *me = M; | |
090089c4 | 469 | } |
3ca60c86 | 470 | commSetCloseOnExec(sock); |
090089c4 | 471 | /* fdstat update */ |
30a4f2a8 | 472 | fdstat_open(sock, FD_SOCKET); |
090089c4 | 473 | conn = &fd_table[sock]; |
474 | conn->openned = 1; | |
475 | conn->sender = 0; /* This is an accept, therefore receiver. */ | |
476 | conn->comm_type = listener->comm_type; | |
1f9afe33 | 477 | strcpy(conn->ipaddr, inet_ntoa(P.sin_addr)); |
30a4f2a8 | 478 | conn->remote_port = htons(P.sin_port); |
479 | conn->local_port = htons(M.sin_port); | |
090089c4 | 480 | commSetNonBlocking(sock); |
090089c4 | 481 | return sock; |
482 | } | |
483 | ||
9864ee44 | 484 | void comm_close(fd) |
090089c4 | 485 | int fd; |
486 | { | |
487 | FD_ENTRY *conn = NULL; | |
30a4f2a8 | 488 | struct close_handler *ch = NULL; |
d9ec4825 | 489 | debug(5, 5, "comm_close: FD %d\n", fd); |
9864ee44 | 490 | if (fd < 0 || fd >= FD_SETSIZE) |
491 | return; | |
492 | conn = &fd_table[fd]; | |
493 | if (!conn->openned) | |
494 | return; | |
7450ce42 | 495 | if (fdstatGetType(fd) == FD_FILE) { |
d1f14731 | 496 | debug(5, 0, "FD %d: Someone called comm_close() on a File\n", fd); |
090089c4 | 497 | fatal_dump(NULL); |
498 | } | |
9864ee44 | 499 | conn->openned = 0; |
0757f0b5 | 500 | RWStateCallbackAndFree(fd, conn->rwstate, COMM_ERROR); |
090089c4 | 501 | comm_set_fd_lifetime(fd, -1); /* invalidate the lifetime */ |
4a63c85f | 502 | fdstat_close(fd); /* update fdstat */ |
503 | while ((ch = conn->close_handler)) { /* Call close handlers */ | |
30a4f2a8 | 504 | conn->close_handler = ch->next; |
505 | ch->handler(fd, ch->data); | |
506 | safe_free(ch); | |
507 | } | |
090089c4 | 508 | memset(conn, '\0', sizeof(FD_ENTRY)); |
9864ee44 | 509 | close(fd); |
090089c4 | 510 | } |
511 | ||
512 | /* use to clean up fdtable when socket is closed without | |
513 | * using comm_close */ | |
514 | int comm_cleanup_fd_entry(fd) | |
515 | int fd; | |
516 | { | |
517 | FD_ENTRY *conn = &fd_table[fd]; | |
0757f0b5 | 518 | RWStateCallbackAndFree(fd, conn->rwstate, COMM_ERROR); |
090089c4 | 519 | memset(conn, 0, sizeof(FD_ENTRY)); |
520 | return 0; | |
521 | } | |
522 | ||
523 | ||
524 | /* Send a udp datagram to specified PORT at HOST. */ | |
525 | int comm_udp_send(fd, host, port, buf, len) | |
526 | int fd; | |
527 | char *host; | |
30a4f2a8 | 528 | u_short port; |
090089c4 | 529 | char *buf; |
530 | int len; | |
531 | { | |
532 | struct hostent *hp = NULL; | |
533 | static struct sockaddr_in to_addr; | |
534 | int bytes_sent; | |
535 | ||
536 | /* Set up the destination socket address for message to send to. */ | |
537 | to_addr.sin_family = AF_INET; | |
538 | ||
30a4f2a8 | 539 | if ((hp = ipcache_gethostbyname(host, IP_BLOCKING_LOOKUP)) == 0) { |
d1f14731 | 540 | debug(5, 1, "comm_udp_send: gethostbyname failure: %s: %s\n", |
090089c4 | 541 | host, xstrerror()); |
542 | return (COMM_ERROR); | |
543 | } | |
30a4f2a8 | 544 | xmemcpy(&to_addr.sin_addr, hp->h_addr, hp->h_length); |
090089c4 | 545 | to_addr.sin_port = htons(port); |
546 | if ((bytes_sent = sendto(fd, buf, len, 0, (struct sockaddr *) &to_addr, | |
547 | sizeof(to_addr))) < 0) { | |
d1f14731 | 548 | debug(5, 1, "comm_udp_send: sendto failure: FD %d: %s\n", |
090089c4 | 549 | fd, xstrerror()); |
550 | return COMM_ERROR; | |
551 | } | |
552 | return bytes_sent; | |
553 | } | |
554 | ||
555 | /* Send a udp datagram to specified TO_ADDR. */ | |
556 | int comm_udp_sendto(fd, to_addr, addr_len, buf, len) | |
557 | int fd; | |
558 | struct sockaddr_in *to_addr; | |
559 | int addr_len; | |
560 | char *buf; | |
561 | int len; | |
562 | { | |
563 | int bytes_sent; | |
564 | ||
565 | if ((bytes_sent = sendto(fd, buf, len, 0, (struct sockaddr *) to_addr, addr_len)) < 0) { | |
d1f14731 | 566 | debug(5, 1, "comm_udp_sendto: sendto failure: FD %d: %s\n", fd, xstrerror()); |
567 | debug(5, 1, "comm_udp_sendto: --> sin_family = %d\n", to_addr->sin_family); | |
568 | debug(5, 1, "comm_udp_sendto: --> sin_port = %d\n", htons(to_addr->sin_port)); | |
569 | debug(5, 1, "comm_udp_sendto: --> sin_addr = %s\n", inet_ntoa(to_addr->sin_addr)); | |
30a4f2a8 | 570 | debug(5, 1, "comm_udp_sendto: --> length = %d\n", len); |
090089c4 | 571 | return COMM_ERROR; |
572 | } | |
573 | return bytes_sent; | |
574 | } | |
575 | ||
576 | int comm_udp_recv(fd, buf, size, from_addr, from_size) | |
577 | int fd; | |
578 | char *buf; | |
579 | int size; | |
580 | struct sockaddr_in *from_addr; | |
581 | int *from_size; /* in: size of from_addr; out: size filled in. */ | |
582 | { | |
583 | int len = recvfrom(fd, buf, size, 0, (struct sockaddr *) from_addr, | |
584 | from_size); | |
585 | if (len < 0) { | |
d1f14731 | 586 | debug(5, 1, "comm_udp_recv: recvfrom failure: FD %d: %s\n", fd, |
090089c4 | 587 | xstrerror()); |
588 | return COMM_ERROR; | |
589 | } | |
590 | return len; | |
591 | } | |
592 | ||
4883993a | 593 | void comm_set_stall(fd, delta) |
594 | int fd; | |
595 | int delta; | |
596 | { | |
597 | if (fd < 0) | |
598 | return; | |
b8de7ebe | 599 | fd_table[fd].stall_until = squid_curtime + delta; |
4883993a | 600 | } |
601 | ||
055f4d4d | 602 | static void comm_select_incoming() |
603 | { | |
604 | fd_set read_mask; | |
605 | fd_set write_mask; | |
606 | int maxfd = 0; | |
607 | int fd = 0; | |
608 | int fds[3]; | |
609 | int N = 0; | |
610 | int i = 0; | |
611 | int (*tmp) () = NULL; | |
612 | ||
613 | FD_ZERO(&read_mask); | |
614 | FD_ZERO(&write_mask); | |
615 | ||
30a4f2a8 | 616 | if (theHttpConnection >= 0 && fdstat_are_n_free_fd(RESERVED_FD)) |
617 | fds[N++] = theHttpConnection; | |
618 | if (theInIcpConnection >= 0) | |
619 | fds[N++] = theInIcpConnection; | |
055f4d4d | 620 | fds[N++] = 0; |
621 | ||
622 | for (i = 0; i < N; i++) { | |
623 | fd = fds[i]; | |
624 | if (fd_table[fd].read_handler) { | |
625 | FD_SET(fd, &read_mask); | |
626 | if (fd > maxfd) | |
627 | maxfd = fd; | |
628 | } | |
629 | if (fd_table[fd].write_handler) { | |
630 | FD_SET(fd, &write_mask); | |
631 | if (fd > maxfd) | |
632 | maxfd = fd; | |
633 | } | |
634 | } | |
635 | ||
636 | if (maxfd++ == 0) | |
637 | return; | |
638 | if (select(maxfd, &read_mask, &write_mask, NULL, &zero_tv) > 0) { | |
31feba03 | 639 | for (i = 0; i < N; i++) { |
055f4d4d | 640 | fd = fds[i]; |
641 | if (FD_ISSET(fd, &read_mask)) { | |
642 | tmp = fd_table[fd].read_handler; | |
643 | fd_table[fd].read_handler = 0; | |
644 | tmp(fd, fd_table[fd].read_data); | |
645 | } | |
646 | if (FD_ISSET(fd, &write_mask)) { | |
647 | tmp = fd_table[fd].write_handler; | |
648 | fd_table[fd].write_handler = 0; | |
649 | tmp(fd, fd_table[fd].write_data); | |
650 | } | |
651 | } | |
652 | } | |
653 | } | |
090089c4 | 654 | |
655 | ||
656 | /* Select on all sockets; call handlers for those that are ready. */ | |
9ca89c5a | 657 | int comm_select(sec) |
7d49daab | 658 | time_t sec; |
090089c4 | 659 | { |
7d49daab | 660 | fd_set exceptfds; |
090089c4 | 661 | fd_set readfds; |
662 | fd_set writefds; | |
7d49daab | 663 | int (*tmp) () = NULL; |
664 | int fd; | |
665 | int i; | |
666 | int maxfd; | |
667 | int nfds; | |
090089c4 | 668 | int num; |
090089c4 | 669 | static time_t last_timeout = 0; |
670 | struct timeval poll_time; | |
7d49daab | 671 | time_t timeout; |
090089c4 | 672 | |
673 | /* assume all process are very fast (less than 1 second). Call | |
674 | * time() only once */ | |
e069e535 | 675 | getCurrentTime(); |
090089c4 | 676 | /* use only 1 second granularity */ |
b8de7ebe | 677 | timeout = squid_curtime + sec; |
090089c4 | 678 | |
f7361640 | 679 | do { |
4610e312 | 680 | if (sec > 60) |
d556a268 | 681 | fatal_dump(NULL); |
090089c4 | 682 | FD_ZERO(&readfds); |
683 | FD_ZERO(&writefds); | |
684 | FD_ZERO(&exceptfds); | |
685 | ||
30a4f2a8 | 686 | if (shutdown_pending || reread_pending) { |
687 | serverConnectionsClose(); | |
688 | ftpServerClose(); | |
f88bb09c | 689 | dnsShutdownServers(); |
d2af9477 | 690 | redirectShutdownServers(); |
30a4f2a8 | 691 | setSocketShutdownLifetimes(); |
692 | } | |
4d64d74a | 693 | nfds = 0; |
694 | maxfd = fdstat_biggest_fd() + 1; | |
695 | for (i = 0; i < maxfd; i++) { | |
898f5d1d | 696 | #if USE_ASYNC_IO |
b560dd20 | 697 | /* Using async IO for disk handle, so don't select on them */ |
7450ce42 | 698 | if (fdstatGetType(i) == FD_FILE) |
b560dd20 | 699 | continue; |
898f5d1d | 700 | #endif |
090089c4 | 701 | /* Check each open socket for a handler. */ |
b8de7ebe | 702 | if (fd_table[i].read_handler && fd_table[i].stall_until <= squid_curtime) { |
4d64d74a | 703 | nfds++; |
090089c4 | 704 | FD_SET(i, &readfds); |
4d64d74a | 705 | } |
706 | if (fd_table[i].write_handler) { | |
707 | nfds++; | |
090089c4 | 708 | FD_SET(i, &writefds); |
4d64d74a | 709 | } |
710 | if (fd_table[i].except_handler) { | |
711 | nfds++; | |
090089c4 | 712 | FD_SET(i, &exceptfds); |
4d64d74a | 713 | } |
090089c4 | 714 | } |
715 | if (!fdstat_are_n_free_fd(RESERVED_FD)) { | |
30a4f2a8 | 716 | FD_CLR(theHttpConnection, &readfds); |
090089c4 | 717 | } |
234967c9 | 718 | if (shutdown_pending || reread_pending) |
983061ed | 719 | debug(5, 2, "comm_select: Still waiting on %d FDs\n", nfds); |
4d64d74a | 720 | if (nfds == 0) |
721 | return COMM_SHUTDOWN; | |
30a4f2a8 | 722 | if (shutdown_pending || reread_pending) |
723 | debug(5, 2, "comm_select: Still waiting on %d FDs\n", nfds); | |
090089c4 | 724 | while (1) { |
898f5d1d | 725 | #if USE_ASYNC_IO |
b560dd20 | 726 | /* Another CPU vs latency tradeoff for async IO */ |
727 | poll_time.tv_sec = 0; | |
728 | poll_time.tv_usec = 250000; | |
729 | #else | |
89fb2544 | 730 | poll_time.tv_sec = sec > 0 ? 1 : 0; |
090089c4 | 731 | poll_time.tv_usec = 0; |
898f5d1d | 732 | #endif |
7d49daab | 733 | num = select(maxfd, &readfds, &writefds, &exceptfds, &poll_time); |
090089c4 | 734 | if (num >= 0) |
735 | break; | |
4d64d74a | 736 | if (errno == EINTR) |
737 | break; | |
30a4f2a8 | 738 | debug(5, 0, "comm_select: select failure: %s\n", |
739 | xstrerror()); | |
bf9f8f2b | 740 | examine_select(&readfds, &writefds, &exceptfds); |
741 | return COMM_ERROR; | |
30a4f2a8 | 742 | /* NOTREACHED */ |
090089c4 | 743 | } |
898f5d1d | 744 | #if USE_ASYNC_IO |
b560dd20 | 745 | aioExamine(); /* See if any IO completed */ |
898f5d1d | 746 | #endif |
4d64d74a | 747 | if (num < 0) |
748 | continue; | |
d1f14731 | 749 | debug(5, num ? 5 : 8, "comm_select: %d sockets ready at %d\n", |
30a4f2a8 | 750 | num, (int) squid_curtime); |
090089c4 | 751 | |
752 | /* Check lifetime and timeout handlers ONCE each second. | |
753 | * Replaces brain-dead check every time through the loop! */ | |
b8de7ebe | 754 | if (squid_curtime > last_timeout) { |
755 | last_timeout = squid_curtime; | |
090089c4 | 756 | checkTimeouts(); |
757 | checkLifetimes(); | |
758 | } | |
7d49daab | 759 | if (num == 0) |
760 | continue; | |
761 | ||
090089c4 | 762 | /* scan each socket but the accept socket. Poll this |
763 | * more frequently to minimiize losses due to the 5 connect | |
764 | * limit in SunOS */ | |
765 | ||
7d49daab | 766 | maxfd = fdstat_biggest_fd() + 1; |
767 | for (fd = 0; fd < maxfd && num > 0; fd++) { | |
768 | ||
769 | if (!(FD_ISSET(fd, &readfds) || FD_ISSET(fd, &writefds) || | |
770 | FD_ISSET(fd, &exceptfds))) | |
771 | continue; | |
772 | else | |
773 | --num; | |
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) { | |
787 | tmp = fd_table[fd].read_handler; | |
788 | fd_table[fd].read_handler = 0; | |
789 | debug(5, 10, "calling read handler %p(%d,%p)\n", | |
790 | tmp, fd, fd_table[fd].read_data); | |
791 | tmp(fd, fd_table[fd].read_data); | |
090089c4 | 792 | } |
7d49daab | 793 | } |
794 | if (FD_ISSET(fd, &writefds)) { | |
795 | debug(5, 5, "comm_select: FD %d ready for writing\n", fd); | |
796 | if (fd_table[fd].write_handler) { | |
797 | tmp = fd_table[fd].write_handler; | |
798 | fd_table[fd].write_handler = 0; | |
799 | debug(5, 10, "calling write handler %p(%d,%p)\n", | |
800 | tmp, fd, fd_table[fd].write_data); | |
801 | tmp(fd, fd_table[fd].write_data); | |
090089c4 | 802 | } |
7d49daab | 803 | } |
804 | if (FD_ISSET(fd, &exceptfds)) { | |
805 | debug(5, 5, "comm_select: FD %d has an exception\n", fd); | |
806 | if (fd_table[fd].except_handler) { | |
807 | tmp = fd_table[fd].except_handler; | |
808 | fd_table[fd].except_handler = 0; | |
809 | debug(5, 10, "calling except handler %p(%d,%p)\n", | |
810 | tmp, fd, fd_table[fd].except_data); | |
811 | tmp(fd, fd_table[fd].except_data); | |
090089c4 | 812 | } |
813 | } | |
090089c4 | 814 | } |
7d49daab | 815 | return COMM_OK; |
f7361640 | 816 | } while (timeout > getCurrentTime()); |
090089c4 | 817 | |
b8de7ebe | 818 | debug(5, 8, "comm_select: time out: %d.\n", squid_curtime); |
090089c4 | 819 | return COMM_TIMEOUT; |
820 | } | |
821 | ||
117531df | 822 | void comm_set_select_handler(fd, type, handler, client_data) |
090089c4 | 823 | int fd; |
824 | unsigned int type; | |
0a5b9b32 | 825 | PF handler; |
2318883b | 826 | void *client_data; |
090089c4 | 827 | { |
117531df | 828 | comm_set_select_handler_plus_timeout(fd, type, handler, client_data, 0); |
090089c4 | 829 | } |
830 | ||
117531df | 831 | void comm_set_select_handler_plus_timeout(fd, type, handler, client_data, timeout) |
090089c4 | 832 | int fd; |
833 | unsigned int type; | |
0a5b9b32 | 834 | PF handler; |
2318883b | 835 | void *client_data; |
090089c4 | 836 | time_t timeout; |
837 | { | |
838 | if (type & COMM_SELECT_TIMEOUT) { | |
e069e535 | 839 | fd_table[fd].timeout_time = (getCurrentTime() + timeout); |
090089c4 | 840 | fd_table[fd].timeout_delta = timeout; |
841 | fd_table[fd].timeout_handler = handler; | |
842 | fd_table[fd].timeout_data = client_data; | |
843 | if ((timeout <= 0) && handler) { | |
d1f14731 | 844 | debug(5, 2, "comm_set_select_handler_plus_timeout: Zero timeout doesn't make sense\n"); |
090089c4 | 845 | } |
846 | } | |
847 | if (type & COMM_SELECT_READ) { | |
848 | fd_table[fd].read_handler = handler; | |
849 | fd_table[fd].read_data = client_data; | |
850 | } | |
851 | if (type & COMM_SELECT_WRITE) { | |
852 | fd_table[fd].write_handler = handler; | |
853 | fd_table[fd].write_data = client_data; | |
854 | } | |
855 | if (type & COMM_SELECT_EXCEPT) { | |
856 | fd_table[fd].except_handler = handler; | |
857 | fd_table[fd].except_data = client_data; | |
858 | } | |
859 | if (type & COMM_SELECT_LIFETIME) { | |
860 | fd_table[fd].lifetime_handler = handler; | |
861 | fd_table[fd].lifetime_data = client_data; | |
862 | } | |
090089c4 | 863 | } |
864 | ||
865 | int comm_get_select_handler(fd, type, handler_ptr, client_data_ptr) | |
866 | int fd; | |
867 | unsigned int type; | |
868 | int (**handler_ptr) (); | |
51496678 | 869 | void **client_data_ptr; |
090089c4 | 870 | { |
871 | if (type & COMM_SELECT_TIMEOUT) { | |
872 | *handler_ptr = fd_table[fd].timeout_handler; | |
873 | *client_data_ptr = fd_table[fd].timeout_data; | |
874 | } | |
875 | if (type & COMM_SELECT_READ) { | |
876 | *handler_ptr = fd_table[fd].read_handler; | |
877 | *client_data_ptr = fd_table[fd].read_data; | |
878 | } | |
879 | if (type & COMM_SELECT_WRITE) { | |
880 | *handler_ptr = fd_table[fd].write_handler; | |
881 | *client_data_ptr = fd_table[fd].write_data; | |
882 | } | |
883 | if (type & COMM_SELECT_EXCEPT) { | |
884 | *handler_ptr = fd_table[fd].except_handler; | |
885 | *client_data_ptr = fd_table[fd].except_data; | |
886 | } | |
887 | if (type & COMM_SELECT_LIFETIME) { | |
888 | *handler_ptr = fd_table[fd].lifetime_handler; | |
889 | *client_data_ptr = fd_table[fd].lifetime_data; | |
890 | } | |
891 | return 0; /* XXX What is meaningful? */ | |
892 | } | |
893 | ||
30a4f2a8 | 894 | void comm_add_close_handler(fd, handler, data) |
895 | int fd; | |
0a5b9b32 | 896 | PF handler; |
30a4f2a8 | 897 | void *data; |
898 | { | |
899 | struct close_handler *new = xmalloc(sizeof(*new)); | |
900 | ||
901 | debug(5, 5, "comm_add_close_handler: fd=%d handler=0x%p data=0x%p\n", fd, handler, data); | |
090089c4 | 902 | |
30a4f2a8 | 903 | new->handler = handler; |
904 | new->data = data; | |
905 | new->next = fd_table[fd].close_handler; | |
906 | fd_table[fd].close_handler = new; | |
907 | } | |
908 | ||
909 | void comm_remove_close_handler(fd, handler, data) | |
090089c4 | 910 | int fd; |
0a5b9b32 | 911 | PF handler; |
30a4f2a8 | 912 | void *data; |
090089c4 | 913 | { |
30a4f2a8 | 914 | struct close_handler *p, *last = NULL; |
915 | ||
916 | /* Find handler in list */ | |
917 | for (p = fd_table[fd].close_handler; p != NULL; last = p, p = p->next) | |
918 | if (p->handler == handler && p->data == data) | |
919 | break; /* This is our handler */ | |
920 | if (!p) | |
921 | fatal_dump("comm_remove_close_handler: Handler not found!\n"); | |
922 | ||
923 | /* Remove list entry */ | |
924 | if (last) | |
925 | last->next = p->next; | |
926 | else | |
927 | fd_table[fd].close_handler = p->next; | |
928 | safe_free(p); | |
929 | } | |
090089c4 | 930 | |
30a4f2a8 | 931 | static void commSetNoLinger(fd) |
932 | int fd; | |
933 | { | |
934 | struct linger L; | |
090089c4 | 935 | L.l_onoff = 0; /* off */ |
936 | L.l_linger = 0; | |
d1f14731 | 937 | debug(5, 10, "commSetNoLinger: turning off SO_LINGER on FD %d\n", fd); |
30a4f2a8 | 938 | if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0) |
939 | debug(5, 0, "commSetNoLinger: FD %d: %s\n", fd, xstrerror()); | |
090089c4 | 940 | } |
941 | ||
30a4f2a8 | 942 | static void commSetReuseAddr(fd) |
090089c4 | 943 | int fd; |
944 | { | |
945 | int on = 1; | |
d1f14731 | 946 | debug(5, 10, "commSetReuseAddr: turning on SO_REUSEADDR on FD %d\n", fd); |
30a4f2a8 | 947 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) |
948 | debug(5, 1, "commSetReuseAddr: FD %d: %s\n", fd, xstrerror()); | |
090089c4 | 949 | } |
950 | ||
30a4f2a8 | 951 | #ifdef TCP_NODELAY |
952 | static void commSetTcpNoDelay(fd) | |
090089c4 | 953 | int fd; |
954 | { | |
30a4f2a8 | 955 | int on = 1; |
956 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0) | |
957 | debug(5, 1, "commSetTcpNoDelay: FD %d: %s\n", fd, xstrerror()); | |
958 | } | |
959 | #endif | |
090089c4 | 960 | |
f868539a | 961 | static void commSetTcpRcvbuf(fd, size) |
962 | int fd; | |
963 | int size; | |
964 | { | |
965 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0) | |
966 | debug(5, 1, "commSetTcpRcvbuf: FD %d, SIZE %d: %s\n", | |
b6f794d6 | 967 | fd, size, xstrerror()); |
f868539a | 968 | } |
969 | ||
30a4f2a8 | 970 | int commSetNonBlocking(fd) |
971 | int fd; | |
972 | { | |
ed43818f | 973 | #if defined(O_NONBLOCK) && !defined(_SQUID_SUNOS_) && !defined(_SQUID_SOLARIS_) |
090089c4 | 974 | if (fcntl(fd, F_SETFL, O_NONBLOCK)) { |
d1f14731 | 975 | debug(5, 0, "comm_open: FD %d: error setting O_NONBLOCK: %s\n", |
090089c4 | 976 | fd, xstrerror()); |
30a4f2a8 | 977 | return COMM_ERROR; |
090089c4 | 978 | } |
979 | #else | |
d519f482 | 980 | if (fcntl(fd, F_SETFL, O_NDELAY)) { |
d1f14731 | 981 | debug(5, 0, "comm_open: FD %d: error setting O_NDELAY: %s\n", |
090089c4 | 982 | fd, xstrerror()); |
30a4f2a8 | 983 | return COMM_ERROR; |
090089c4 | 984 | } |
30a4f2a8 | 985 | #endif |
090089c4 | 986 | return 0; |
987 | } | |
988 | ||
3ca60c86 | 989 | void commSetCloseOnExec(fd) |
990 | int fd; | |
991 | { | |
992 | #ifdef FD_CLOEXEC | |
993 | if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { | |
994 | debug(5, 0, "comm_open: FD %d: set close-on-exec failed: %s\n", | |
995 | fd, xstrerror()); | |
996 | } | |
997 | #endif | |
998 | } | |
999 | ||
090089c4 | 1000 | char **getAddressList(name) |
1001 | char *name; | |
1002 | { | |
1003 | struct hostent *hp = NULL; | |
1004 | if (name == NULL) | |
1005 | return NULL; | |
30a4f2a8 | 1006 | if ((hp = ipcache_gethostbyname(name, IP_BLOCKING_LOOKUP))) |
090089c4 | 1007 | return hp->h_addr_list; |
d1f14731 | 1008 | debug(5, 0, "getAddress: gethostbyname failure: %s: %s\n", |
090089c4 | 1009 | name, xstrerror()); |
1010 | return NULL; | |
1011 | } | |
1012 | ||
1013 | struct in_addr *getAddress(name) | |
1014 | char *name; | |
1015 | { | |
1016 | static struct in_addr first; | |
1017 | char **list = NULL; | |
1018 | if (name == NULL) | |
1019 | return NULL; | |
1020 | if ((list = getAddressList(name))) { | |
30a4f2a8 | 1021 | xmemcpy(&first.s_addr, *list, 4); |
090089c4 | 1022 | return (&first); |
1023 | } | |
d1f14731 | 1024 | debug(5, 0, "getAddress: gethostbyname failure: %s: %s\n", |
090089c4 | 1025 | name, xstrerror()); |
1026 | return NULL; | |
1027 | } | |
1028 | ||
1029 | /* | |
1030 | * the fd_lifetime is used as a hardlimit to timeout dead sockets. | |
1031 | * The basic problem is that many WWW clients are abusive and | |
b8de7ebe | 1032 | * it results in squid having lots of CLOSE_WAIT states. Until |
090089c4 | 1033 | * we can find a better solution, we give all asciiPort or |
b8de7ebe | 1034 | * squid initiated clients a maximum lifetime. |
090089c4 | 1035 | */ |
1036 | int comm_init() | |
1037 | { | |
30a4f2a8 | 1038 | int i; |
090089c4 | 1039 | |
30a4f2a8 | 1040 | fd_table = xcalloc(FD_SETSIZE, sizeof(FD_ENTRY)); |
1041 | meta_data.misc += FD_SETSIZE * sizeof(FD_ENTRY); | |
090089c4 | 1042 | /* Keep a few file descriptors free so that we don't run out of FD's |
1043 | * after accepting a client but before it opens a socket or a file. | |
30a4f2a8 | 1044 | * Since FD_SETSIZE can be as high as several thousand, don't waste them */ |
1045 | RESERVED_FD = min(100, FD_SETSIZE / 4); | |
090089c4 | 1046 | /* hardwired lifetimes */ |
30a4f2a8 | 1047 | fd_lifetime = xmalloc(sizeof(int) * FD_SETSIZE); |
1048 | for (i = 0; i < FD_SETSIZE; i++) | |
090089c4 | 1049 | comm_set_fd_lifetime(i, -1); /* denotes invalid */ |
30a4f2a8 | 1050 | meta_data.misc += FD_SETSIZE * sizeof(int); |
055f4d4d | 1051 | zero_tv.tv_sec = 0; |
1052 | zero_tv.tv_usec = 0; | |
a1ab1e71 | 1053 | any_addr.s_addr = INADDR_ANY; |
1054 | no_addr.s_addr = INADDR_NONE; | |
090089c4 | 1055 | return 0; |
1056 | } | |
1057 | ||
1058 | ||
1059 | /* | |
1060 | * examine_select - debug routine. | |
1061 | * | |
1062 | * I spend the day chasing this core dump that occurs when both the client | |
1063 | * and the server side of a cache fetch simultaneoulsy abort the | |
1064 | * connection. While I haven't really studied the code to figure out how | |
1065 | * it happens, the snippet below may prevent the cache from exitting: | |
1066 | * | |
1067 | * Call this from where the select loop fails. | |
1068 | */ | |
1069 | static int examine_select(readfds, writefds, exceptfds) | |
1070 | fd_set *readfds, *writefds, *exceptfds; | |
1071 | { | |
1072 | int fd = 0; | |
bbc5ea8f | 1073 | fd_set read_x; |
1074 | fd_set write_x; | |
1075 | fd_set except_x; | |
090089c4 | 1076 | int num; |
1077 | struct timeval tv; | |
30a4f2a8 | 1078 | struct close_handler *ch = NULL; |
1079 | struct close_handler *next = NULL; | |
117531df | 1080 | FD_ENTRY *f = NULL; |
090089c4 | 1081 | |
d1f14731 | 1082 | debug(5, 0, "examine_select: Examining open file descriptors...\n"); |
30a4f2a8 | 1083 | for (fd = 0; fd < FD_SETSIZE; fd++) { |
090089c4 | 1084 | FD_ZERO(&read_x); |
1085 | FD_ZERO(&write_x); | |
1086 | FD_ZERO(&except_x); | |
1087 | tv.tv_sec = tv.tv_usec = 0; | |
af00901c | 1088 | if (FD_ISSET(fd, readfds)) |
090089c4 | 1089 | FD_SET(fd, &read_x); |
af00901c | 1090 | else if (FD_ISSET(fd, writefds)) |
1091 | FD_SET(fd, &write_x); | |
1092 | else if (FD_ISSET(fd, exceptfds)) | |
1093 | FD_SET(fd, &except_x); | |
1094 | else | |
1095 | continue; | |
1096 | num = select(FD_SETSIZE, &read_x, &write_x, &except_x, &tv); | |
1097 | if (num > -1) { | |
1098 | debug(5, 5, "FD %d is valid.\n", fd); | |
1099 | continue; | |
1100 | } | |
1101 | f = &fd_table[fd]; | |
1102 | debug(5, 0, "WARNING: FD %d has handlers, but it's invalid.\n", fd); | |
1103 | debug(5, 0, "FD %d is a %s\n", fd, fdstatTypeStr[fdstatGetType(fd)]); | |
1104 | debug(5, 0, "--> %s\n", fd_note(fd, NULL)); | |
1105 | debug(5, 0, "lifetm:%p tmout:%p read:%p write:%p expt:%p\n", | |
1106 | f->lifetime_handler, | |
1107 | f->timeout_handler, | |
1108 | f->read_handler, | |
1109 | f->write_handler, | |
1110 | f->except_handler); | |
1111 | for (ch = f->close_handler; ch; ch = ch->next) | |
1112 | debug(5, 0, " close handler: %p\n", ch->handler); | |
1113 | if (f->close_handler) { | |
1114 | for (ch = f->close_handler; ch; ch = next) { | |
1115 | next = ch->next; | |
1116 | ch->handler(fd, ch->data); | |
1117 | safe_free(ch); | |
090089c4 | 1118 | } |
af00901c | 1119 | } else if (f->lifetime_handler) { |
1120 | debug(5, 0, "examine_select: Calling Lifetime Handler\n"); | |
1121 | f->lifetime_handler(fd, f->lifetime_data); | |
1122 | } else if (f->timeout_handler) { | |
1123 | debug(5, 0, "examine_select: Calling Timeout Handler\n"); | |
1124 | f->timeout_handler(fd, f->timeout_data); | |
090089c4 | 1125 | } |
af00901c | 1126 | f->close_handler = NULL; |
1127 | f->lifetime_handler = NULL; | |
1128 | f->timeout_handler = NULL; | |
1129 | f->read_handler = NULL; | |
1130 | f->write_handler = NULL; | |
1131 | f->except_handler = NULL; | |
1132 | FD_CLR(fd, readfds); | |
1133 | FD_CLR(fd, writefds); | |
1134 | FD_CLR(fd, exceptfds); | |
090089c4 | 1135 | } |
090089c4 | 1136 | return 0; |
1137 | } | |
1138 | ||
1139 | char *fd_note(fd, s) | |
1140 | int fd; | |
1141 | char *s; | |
1142 | { | |
1143 | if (s == NULL) | |
1144 | return (fd_table[fd].ascii_note); | |
1145 | strncpy(fd_table[fd].ascii_note, s, FD_ASCII_NOTE_SZ - 1); | |
1146 | return (NULL); | |
1147 | } | |
1148 | ||
1149 | static void checkTimeouts() | |
1150 | { | |
1151 | int fd; | |
4d64d74a | 1152 | int (*tmp) () = NULL; |
1153 | FD_ENTRY *f = NULL; | |
4d64d74a | 1154 | |
090089c4 | 1155 | /* scan for timeout */ |
30a4f2a8 | 1156 | for (fd = 0; fd < FD_SETSIZE; ++fd) { |
4d64d74a | 1157 | f = &fd_table[fd]; |
1158 | if ((f->timeout_handler) && | |
b8de7ebe | 1159 | (f->timeout_time <= squid_curtime)) { |
4d64d74a | 1160 | tmp = f->timeout_handler; |
d1f14731 | 1161 | debug(5, 5, "comm_select: timeout on socket %d at %d\n", |
b8de7ebe | 1162 | fd, squid_curtime); |
4d64d74a | 1163 | f->timeout_handler = 0; |
1164 | tmp(fd, f->timeout_data); | |
090089c4 | 1165 | } |
1166 | } | |
1167 | } | |
1168 | ||
1169 | static void checkLifetimes() | |
1170 | { | |
1171 | int fd; | |
090089c4 | 1172 | time_t lft; |
9864ee44 | 1173 | FD_ENTRY *fde = NULL; |
090089c4 | 1174 | |
30a4f2a8 | 1175 | int (*func) () = NULL; |
1176 | ||
1177 | for (fd = 0; fd < FD_SETSIZE; fd++) { | |
1178 | if ((lft = comm_get_fd_lifetime(fd)) == -1) | |
1179 | continue; | |
1180 | if (lft > squid_curtime) | |
1181 | continue; | |
1182 | debug(5, 5, "checkLifetimes: FD %d Expired\n", fd); | |
9864ee44 | 1183 | fde = &fd_table[fd]; |
1184 | if ((func = fde->lifetime_handler)) { | |
30a4f2a8 | 1185 | debug(5, 5, "checkLifetimes: FD %d: Calling lifetime handler\n", fd); |
9864ee44 | 1186 | func(fd, fde->lifetime_data); |
1187 | fde->lifetime_handler = NULL; | |
1188 | } else if ((func = fde->read_handler)) { | |
30a4f2a8 | 1189 | debug(5, 5, "checkLifetimes: FD %d: Calling read handler\n", fd); |
9864ee44 | 1190 | func(fd, fde->read_data); |
1191 | fde->read_handler = NULL; | |
1192 | } else if ((func = fde->read_handler)) { | |
30a4f2a8 | 1193 | debug(5, 5, "checkLifetimes: FD %d: Calling read handler\n", fd); |
9864ee44 | 1194 | func(fd, fde->read_data); |
1195 | fde->read_handler = NULL; | |
1196 | } else if ((func = fde->write_handler)) { | |
30a4f2a8 | 1197 | debug(5, 5, "checkLifetimes: FD %d: Calling write handler\n", fd); |
9864ee44 | 1198 | func(fd, fde->write_data); |
1199 | fde->write_handler = NULL; | |
30a4f2a8 | 1200 | } else { |
1201 | debug(5, 5, "checkLifetimes: FD %d: No handlers, calling comm_close()\n", fd); | |
1202 | comm_close(fd); | |
1203 | comm_cleanup_fd_entry(fd); | |
1204 | } | |
9864ee44 | 1205 | if (fde->openned) { |
30a4f2a8 | 1206 | /* still opened */ |
1207 | debug(5, 5, "checkLifetimes: FD %d: Forcing comm_close()\n", fd); | |
1208 | comm_close(fd); | |
1209 | comm_cleanup_fd_entry(fd); | |
090089c4 | 1210 | } |
1211 | } | |
1212 | } | |
1213 | ||
1214 | /* | |
1215 | * Reserve_More_FDs() called when acceopt(), open(), or socket is failing | |
1216 | */ | |
1217 | static void Reserve_More_FDs() | |
1218 | { | |
30a4f2a8 | 1219 | if (RESERVED_FD < FD_SETSIZE - 64) { |
090089c4 | 1220 | RESERVED_FD = RESERVED_FD + 1; |
30a4f2a8 | 1221 | } else if (RESERVED_FD == FD_SETSIZE - 64) { |
090089c4 | 1222 | RESERVED_FD = RESERVED_FD + 1; |
d1f14731 | 1223 | debug(5, 0, "Don't you have a tiny open-file table size of %d\n", |
30a4f2a8 | 1224 | FD_SETSIZE - RESERVED_FD); |
090089c4 | 1225 | } |
1226 | } | |
1227 | ||
30a4f2a8 | 1228 | /* Read from FD. */ |
1229 | static int commHandleRead(fd, state) | |
1230 | int fd; | |
1231 | RWStateData *state; | |
1232 | { | |
1233 | int len; | |
1234 | ||
1235 | len = read(fd, state->buf + state->offset, state->size - state->offset); | |
1236 | debug(5, 5, "commHandleRead: FD %d: read %d bytes\n", fd, len); | |
1237 | ||
1238 | if (len <= 0) { | |
9864ee44 | 1239 | if (errno == EWOULDBLOCK || errno == EAGAIN) { |
30a4f2a8 | 1240 | /* reschedule self */ |
1241 | comm_set_select_handler(fd, | |
1242 | COMM_SELECT_READ, | |
1243 | (PF) commHandleRead, | |
1244 | state); | |
1245 | return COMM_OK; | |
9864ee44 | 1246 | } else { |
30a4f2a8 | 1247 | /* Len == 0 means connection closed; otherwise would not have been |
1248 | * called by comm_select(). */ | |
1249 | debug(5, len == 0 ? 2 : 1, "commHandleRead: FD %d: read failure: %s\n", | |
1250 | fd, len == 0 ? "connection closed" : xstrerror()); | |
4a63c85f | 1251 | fd_table[fd].rwstate = NULL; /* handler may issue new read */ |
0757f0b5 | 1252 | RWStateCallbackAndFree(fd, state, COMM_ERROR); |
30a4f2a8 | 1253 | return COMM_ERROR; |
1254 | } | |
1255 | } | |
1256 | state->offset += len; | |
1257 | ||
1258 | /* Call handler if we have read enough */ | |
1259 | if (state->offset >= state->size || state->handle_immed) { | |
0757f0b5 | 1260 | fd_table[fd].rwstate = NULL; /* handler may issue new read */ |
1261 | RWStateCallbackAndFree(fd, state, COMM_OK); | |
30a4f2a8 | 1262 | } else { |
1263 | /* Reschedule until we are done */ | |
1264 | comm_set_select_handler(fd, | |
1265 | COMM_SELECT_READ, | |
1266 | (PF) commHandleRead, | |
1267 | state); | |
1268 | } | |
1269 | return COMM_OK; | |
1270 | } | |
1271 | ||
1272 | /* Select for reading on FD, until SIZE bytes are received. Call | |
1273 | * HANDLER when complete. */ | |
1274 | void comm_read(fd, buf, size, timeout, immed, handler, handler_data) | |
1275 | int fd; | |
1276 | char *buf; | |
1277 | int size; | |
1278 | int timeout; | |
1279 | int immed; /* Call handler immediately when data available */ | |
1280 | rw_complete_handler *handler; | |
1281 | void *handler_data; | |
1282 | { | |
1283 | RWStateData *state = NULL; | |
1284 | ||
1285 | debug(5, 5, "comm_read: FD %d: sz %d: tout %d: hndl %p: data %p.\n", | |
1286 | fd, size, timeout, handler, handler_data); | |
1287 | ||
0757f0b5 | 1288 | if (fd_table[fd].rwstate) { |
1289 | debug(5, 1, "comm_read: WARNING! FD %d: A comm_read/comm_write is already active.\n", fd); | |
1290 | RWStateCallbackAndFree(fd, fd_table[fd].rwstate, COMM_ERROR); | |
30a4f2a8 | 1291 | } |
1292 | state = xcalloc(1, sizeof(RWStateData)); | |
0757f0b5 | 1293 | fd_table[fd].rwstate = state; |
30a4f2a8 | 1294 | state->buf = buf; |
1295 | state->size = size; | |
1296 | state->offset = 0; | |
1297 | state->handler = handler; | |
1298 | state->timeout = timeout; | |
1299 | state->handle_immed = immed; | |
1300 | state->time = squid_curtime; | |
1301 | state->handler_data = handler_data; | |
9864ee44 | 1302 | state->free = NULL; |
30a4f2a8 | 1303 | comm_set_select_handler(fd, |
1304 | COMM_SELECT_READ, | |
1305 | (PF) commHandleRead, | |
1306 | state); | |
1307 | } | |
1308 | ||
1309 | /* Write to FD. */ | |
1310 | static void commHandleWrite(fd, state) | |
1311 | int fd; | |
1312 | RWStateData *state; | |
1313 | { | |
1314 | int len = 0; | |
1315 | int nleft; | |
1316 | ||
1317 | debug(5, 5, "commHandleWrite: FD %d: state=%p, off %d, sz %d.\n", | |
1318 | fd, state, state->offset, state->size); | |
1319 | ||
1320 | nleft = state->size - state->offset; | |
1321 | len = write(fd, state->buf + state->offset, nleft); | |
1322 | ||
1323 | if (len == 0) { | |
1324 | /* Note we even call write if nleft == 0 */ | |
1325 | /* We're done */ | |
1326 | if (nleft != 0) | |
1327 | debug(5, 2, "commHandleWrite: FD %d: write failure: connection closed with %d bytes remaining.\n", fd, nleft); | |
0757f0b5 | 1328 | fd_table[fd].rwstate = NULL; |
1329 | RWStateCallbackAndFree(fd, state, nleft ? COMM_ERROR : COMM_OK); | |
30a4f2a8 | 1330 | } else if (len < 0) { |
1331 | /* An error */ | |
1332 | if (errno == EWOULDBLOCK || errno == EAGAIN) { | |
30a4f2a8 | 1333 | debug(5, 10, "commHandleWrite: FD %d: write failure: %s.\n", |
1334 | fd, xstrerror()); | |
1335 | comm_set_select_handler(fd, | |
1336 | COMM_SELECT_WRITE, | |
1337 | (PF) commHandleWrite, | |
1338 | state); | |
9864ee44 | 1339 | } else { |
1340 | debug(5, 2, "commHandleWrite: FD %d: write failure: %s.\n", | |
1341 | fd, xstrerror()); | |
0757f0b5 | 1342 | fd_table[fd].rwstate = NULL; |
1343 | RWStateCallbackAndFree(fd, state, COMM_ERROR); | |
30a4f2a8 | 1344 | } |
30a4f2a8 | 1345 | } else { |
1346 | /* A successful write, continue */ | |
1347 | state->offset += len; | |
1348 | if (state->offset < state->size) { | |
1349 | /* Not done, reinstall the write handler and write some more */ | |
1350 | comm_set_select_handler(fd, | |
1351 | COMM_SELECT_WRITE, | |
1352 | (PF) commHandleWrite, | |
1353 | state); | |
9864ee44 | 1354 | } else { |
0757f0b5 | 1355 | fd_table[fd].rwstate = NULL; |
1356 | RWStateCallbackAndFree(fd, state, COMM_OK); | |
30a4f2a8 | 1357 | } |
30a4f2a8 | 1358 | } |
1359 | } | |
1360 | ||
1361 | ||
1362 | ||
1363 | /* Select for Writing on FD, until SIZE bytes are sent. Call | |
1364 | * * HANDLER when complete. */ | |
9864ee44 | 1365 | void comm_write(fd, buf, size, timeout, handler, handler_data, free) |
30a4f2a8 | 1366 | int fd; |
1367 | char *buf; | |
1368 | int size; | |
1369 | int timeout; | |
1370 | rw_complete_handler *handler; | |
1371 | void *handler_data; | |
4a63c85f | 1372 | void (*free) (void *); |
30a4f2a8 | 1373 | { |
1374 | RWStateData *state = NULL; | |
1375 | ||
1376 | debug(5, 5, "comm_write: FD %d: sz %d: tout %d: hndl %p: data %p.\n", | |
1377 | fd, size, timeout, handler, handler_data); | |
1378 | ||
0757f0b5 | 1379 | if (fd_table[fd].rwstate) { |
1380 | debug(5, 1, "comm_write: WARNING! FD %d: A comm_read/comm_write is already active.\n", fd); | |
1381 | RWStateCallbackAndFree(fd, fd_table[fd].rwstate, COMM_ERROR); | |
30a4f2a8 | 1382 | } |
1383 | state = xcalloc(1, sizeof(RWStateData)); | |
1384 | state->buf = buf; | |
1385 | state->size = size; | |
1386 | state->offset = 0; | |
1387 | state->handler = handler; | |
1388 | state->timeout = timeout; | |
1389 | state->time = squid_curtime; | |
1390 | state->handler_data = handler_data; | |
9864ee44 | 1391 | state->free = free; |
30a4f2a8 | 1392 | comm_set_select_handler(fd, |
1393 | COMM_SELECT_WRITE, | |
1394 | (PF) commHandleWrite, | |
0757f0b5 | 1395 | fd_table[fd].rwstate = state); |
30a4f2a8 | 1396 | } |