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