]> git.ipfire.org Git - thirdparty/squid.git/blob - src/comm.cc
Updated copyright
[thirdparty/squid.git] / src / comm.cc
1
2 /*
3 * $Id: comm.cc,v 1.315 2001/01/12 00:37:16 wessels Exp $
4 *
5 * DEBUG: section 5 Socket Functions
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37
38 #ifdef HAVE_NETINET_TCP_H
39 #include <netinet/tcp.h>
40 #endif
41
42 typedef struct {
43 char *host;
44 u_short port;
45 struct sockaddr_in S;
46 CNCB *callback;
47 void *data;
48 struct in_addr in_addr;
49 int locks;
50 int fd;
51 int tries;
52 int addrcount;
53 int connstart;
54 } ConnectStateData;
55
56 /* STATIC */
57 static int commBind(int s, struct in_addr, u_short port);
58 static void commSetReuseAddr(int);
59 static void commSetNoLinger(int);
60 static void CommWriteStateCallbackAndFree(int fd, int code);
61 #ifdef TCP_NODELAY
62 static void commSetTcpNoDelay(int);
63 #endif
64 static void commSetTcpRcvbuf(int, int);
65 static PF commConnectFree;
66 static PF commConnectHandle;
67 static PF commHandleWrite;
68 static IPH commConnectDnsHandle;
69 static void commConnectCallback(ConnectStateData * cs, int status);
70 static int commResetFD(ConnectStateData * cs);
71 static int commRetryConnect(ConnectStateData * cs);
72 CBDATA_TYPE(ConnectStateData);
73
74 static MemPool *comm_write_pool = NULL;
75 static MemPool *conn_close_pool = NULL;
76
77 static void
78 CommWriteStateCallbackAndFree(int fd, int code)
79 {
80 CommWriteStateData *CommWriteState = fd_table[fd].rwstate;
81 CWCB *callback = NULL;
82 void *data;
83 fd_table[fd].rwstate = NULL;
84 if (CommWriteState == NULL)
85 return;
86 if (CommWriteState->free_func) {
87 FREE *free_func = CommWriteState->free_func;
88 void *free_buf = CommWriteState->buf;
89 CommWriteState->free_func = NULL;
90 CommWriteState->buf = NULL;
91 free_func(free_buf);
92 }
93 callback = CommWriteState->handler;
94 data = CommWriteState->handler_data;
95 CommWriteState->handler = NULL;
96 if (callback && cbdataValid(data))
97 callback(fd, CommWriteState->buf, CommWriteState->offset, code, data);
98 cbdataUnlock(data);
99 memPoolFree(comm_write_pool, CommWriteState);
100 }
101
102 /* Return the local port associated with fd. */
103 u_short
104 comm_local_port(int fd)
105 {
106 struct sockaddr_in addr;
107 socklen_t addr_len = 0;
108 fde *F = &fd_table[fd];
109
110 /* If the fd is closed already, just return */
111 if (!F->flags.open) {
112 debug(5, 0) ("comm_local_port: FD %d has been closed.\n", fd);
113 return 0;
114 }
115 if (F->local_port)
116 return F->local_port;
117 addr_len = sizeof(addr);
118 if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {
119 debug(50, 1) ("comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror());
120 return 0;
121 }
122 F->local_port = ntohs(addr.sin_port);
123 debug(5, 6) ("comm_local_port: FD %d: port %d\n", fd, (int) F->local_port);
124 return F->local_port;
125 }
126
127 static int
128 commBind(int s, struct in_addr in_addr, u_short port)
129 {
130 struct sockaddr_in S;
131
132 memset(&S, '\0', sizeof(S));
133 S.sin_family = AF_INET;
134 S.sin_port = htons(port);
135 S.sin_addr = in_addr;
136 statCounter.syscalls.sock.binds++;
137 if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0)
138 return COMM_OK;
139 debug(50, 0) ("commBind: Cannot bind socket FD %d to %s:%d: %s\n",
140 s,
141 S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr),
142 (int) port,
143 xstrerror());
144 return COMM_ERROR;
145 }
146
147 /* Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE
148 * is OR of flags specified in comm.h. */
149 int
150 comm_open(int sock_type,
151 int proto,
152 struct in_addr addr,
153 u_short port,
154 int flags,
155 const char *note)
156 {
157 int new_socket;
158 fde *F = NULL;
159
160 /* Create socket for accepting new connections. */
161 statCounter.syscalls.sock.sockets++;
162 if ((new_socket = socket(AF_INET, sock_type, proto)) < 0) {
163 /* Increase the number of reserved fd's if calls to socket()
164 * are failing because the open file table is full. This
165 * limits the number of simultaneous clients */
166 switch (errno) {
167 case ENFILE:
168 case EMFILE:
169 debug(50, 1) ("comm_open: socket failure: %s\n", xstrerror());
170 fdAdjustReserved();
171 break;
172 default:
173 debug(50, 0) ("comm_open: socket failure: %s\n", xstrerror());
174 }
175 return -1;
176 }
177 /* update fdstat */
178 debug(5, 5) ("comm_open: FD %d is a new socket\n", new_socket);
179 fd_open(new_socket, FD_SOCKET, note);
180 F = &fd_table[new_socket];
181 if (!(flags & COMM_NOCLOEXEC))
182 commSetCloseOnExec(new_socket);
183 if ((flags & COMM_REUSEADDR))
184 commSetReuseAddr(new_socket);
185 if (port > (u_short) 0) {
186 commSetNoLinger(new_socket);
187 if (opt_reuseaddr)
188 commSetReuseAddr(new_socket);
189 }
190 if (addr.s_addr != no_addr.s_addr) {
191 if (commBind(new_socket, addr, port) != COMM_OK) {
192 comm_close(new_socket);
193 return -1;
194 }
195 }
196 F->local_port = port;
197
198 if (flags & COMM_NONBLOCKING)
199 if (commSetNonBlocking(new_socket) == COMM_ERROR)
200 return -1;
201 #ifdef TCP_NODELAY
202 if (sock_type == SOCK_STREAM)
203 commSetTcpNoDelay(new_socket);
204 #endif
205 if (Config.tcpRcvBufsz > 0 && sock_type == SOCK_STREAM)
206 commSetTcpRcvbuf(new_socket, Config.tcpRcvBufsz);
207 return new_socket;
208 }
209
210 /*
211 * NOTE: set the listen queue to Squid_MaxFD/4 and rely on the kernel to
212 * impose an upper limit. Solaris' listen(3n) page says it has
213 * no limit on this parameter, but sys/socket.h sets SOMAXCONN
214 * to 5. HP-UX currently has a limit of 20. SunOS is 5 and
215 * OSF 3.0 is 8.
216 */
217 int
218 comm_listen(int sock)
219 {
220 int x;
221 if ((x = listen(sock, Squid_MaxFD >> 2)) < 0) {
222 debug(50, 0) ("comm_listen: listen(%d, %d): %s\n",
223 Squid_MaxFD >> 2,
224 sock, xstrerror());
225 return x;
226 }
227 return sock;
228 }
229
230 void
231 commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data)
232 {
233 ConnectStateData *cs;
234 debug(5, 3) ("commConnectStart: FD %d, %s:%d\n", fd, host, (int) port);
235 cs = CBDATA_ALLOC(ConnectStateData, NULL);
236 cs->fd = fd;
237 cs->host = xstrdup(host);
238 cs->port = port;
239 cs->callback = callback;
240 cs->data = data;
241 cbdataLock(cs->data);
242 comm_add_close_handler(fd, commConnectFree, cs);
243 cs->locks++;
244 ipcache_nbgethostbyname(host, commConnectDnsHandle, cs);
245 }
246
247 static void
248 commConnectDnsHandle(const ipcache_addrs * ia, void *data)
249 {
250 ConnectStateData *cs = data;
251 assert(cs->locks == 1);
252 cs->locks--;
253 if (ia == NULL) {
254 debug(5, 3) ("commConnectDnsHandle: Unknown host: %s\n", cs->host);
255 if (!dns_error_message) {
256 dns_error_message = "Unknown DNS error";
257 debug(5, 1) ("commConnectDnsHandle: Bad dns_error_message\n");
258 }
259 assert(dns_error_message != NULL);
260 commConnectCallback(cs, COMM_ERR_DNS);
261 return;
262 }
263 assert(ia->cur < ia->count);
264 cs->in_addr = ia->in_addrs[ia->cur];
265 ipcacheCycleAddr(cs->host, NULL);
266 cs->addrcount = ia->count;
267 cs->connstart = squid_curtime;
268 commConnectHandle(cs->fd, cs);
269 }
270
271 static void
272 commConnectCallback(ConnectStateData * cs, int status)
273 {
274 CNCB *callback = cs->callback;
275 void *data = cs->data;
276 int fd = cs->fd;
277 comm_remove_close_handler(fd, commConnectFree, cs);
278 cs->callback = NULL;
279 cs->data = NULL;
280 commSetTimeout(fd, -1, NULL, NULL);
281 commConnectFree(fd, cs);
282 if (cbdataValid(data))
283 callback(fd, status, data);
284 cbdataUnlock(data);
285 }
286
287 static void
288 commConnectFree(int fd, void *data)
289 {
290 ConnectStateData *cs = data;
291 debug(5, 3) ("commConnectFree: FD %d\n", fd);
292 if (cs->data)
293 cbdataUnlock(cs->data);
294 safe_free(cs->host);
295 cbdataFree(cs);
296 }
297
298 /* Reset FD so that we can connect() again */
299 static int
300 commResetFD(ConnectStateData * cs)
301 {
302 int fd2;
303 if (!cbdataValid(cs->data))
304 return 0;
305 statCounter.syscalls.sock.sockets++;
306 fd2 = socket(AF_INET, SOCK_STREAM, 0);
307 statCounter.syscalls.sock.sockets++;
308 if (fd2 < 0) {
309 debug(5, 0) ("commResetFD: socket: %s\n", xstrerror());
310 if (ENFILE == errno || EMFILE == errno)
311 fdAdjustReserved();
312 return 0;
313 }
314 if (dup2(fd2, cs->fd) < 0) {
315 debug(5, 0) ("commResetFD: dup2: %s\n", xstrerror());
316 if (ENFILE == errno || EMFILE == errno)
317 fdAdjustReserved();
318 return 0;
319 }
320 close(fd2);
321 fd_table[cs->fd].flags.called_connect = 0;
322 /*
323 * yuck, this has assumptions about comm_open() arguments for
324 * the original socket
325 */
326 commSetCloseOnExec(cs->fd);
327 if (Config.Addrs.tcp_outgoing.s_addr != no_addr.s_addr) {
328 if (commBind(cs->fd, Config.Addrs.tcp_outgoing, 0) != COMM_OK) {
329 return 0;
330 }
331 }
332 commSetNonBlocking(cs->fd);
333 #ifdef TCP_NODELAY
334 commSetTcpNoDelay(cs->fd);
335 #endif
336 if (Config.tcpRcvBufsz > 0)
337 commSetTcpRcvbuf(cs->fd, Config.tcpRcvBufsz);
338 return 1;
339 }
340
341 static int
342 commRetryConnect(ConnectStateData * cs)
343 {
344 assert(cs->addrcount > 0);
345 if (cs->addrcount == 1) {
346 if (cs->tries >= Config.retry.maxtries)
347 return 0;
348 if (squid_curtime - cs->connstart > Config.Timeout.connect)
349 return 0;
350 } else {
351 if (cs->tries > cs->addrcount)
352 return 0;
353 }
354 return commResetFD(cs);
355 }
356
357 /* Connect SOCK to specified DEST_PORT at DEST_HOST. */
358 static void
359 commConnectHandle(int fd, void *data)
360 {
361 ConnectStateData *cs = data;
362 if (cs->S.sin_addr.s_addr == 0) {
363 cs->S.sin_family = AF_INET;
364 cs->S.sin_addr = cs->in_addr;
365 cs->S.sin_port = htons(cs->port);
366 if (Config.onoff.log_fqdn)
367 fqdncache_gethostbyaddr(cs->S.sin_addr, FQDN_LOOKUP_IF_MISS);
368 }
369 switch (comm_connect_addr(fd, &cs->S)) {
370 case COMM_INPROGRESS:
371 debug(5, 5) ("commConnectHandle: FD %d: COMM_INPROGRESS\n", fd);
372 commSetSelect(fd, COMM_SELECT_WRITE, commConnectHandle, cs, 0);
373 break;
374 case COMM_OK:
375 ipcacheMarkGoodAddr(cs->host, cs->S.sin_addr);
376 commConnectCallback(cs, COMM_OK);
377 break;
378 default:
379 cs->tries++;
380 ipcacheMarkBadAddr(cs->host, cs->S.sin_addr);
381 if (Config.onoff.test_reachability)
382 netdbDeleteAddrNetwork(cs->S.sin_addr);
383 if (commRetryConnect(cs)) {
384 cs->locks++;
385 ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs);
386 } else {
387 commConnectCallback(cs, COMM_ERR_CONNECT);
388 }
389 break;
390 }
391 }
392
393 int
394 commSetTimeout(int fd, int timeout, PF * handler, void *data)
395 {
396 fde *F;
397 debug(5, 3) ("commSetTimeout: FD %d timeout %d\n", fd, timeout);
398 assert(fd >= 0);
399 assert(fd < Squid_MaxFD);
400 F = &fd_table[fd];
401 assert(F->flags.open);
402 if (timeout < 0) {
403 F->timeout_handler = NULL;
404 F->timeout_data = NULL;
405 return F->timeout = 0;
406 }
407 assert(handler || F->timeout_handler);
408 if (handler || data) {
409 F->timeout_handler = handler;
410 F->timeout_data = data;
411 }
412 return F->timeout = squid_curtime + (time_t) timeout;
413 }
414
415 int
416 comm_connect_addr(int sock, const struct sockaddr_in *address)
417 {
418 int status = COMM_OK;
419 fde *F = &fd_table[sock];
420 int x;
421 int err = 0;
422 socklen_t errlen;
423 assert(ntohs(address->sin_port) != 0);
424 /* Establish connection. */
425 errno = 0;
426 if (!F->flags.called_connect) {
427 F->flags.called_connect = 1;
428 statCounter.syscalls.sock.connects++;
429 x = connect(sock, (struct sockaddr *) address, sizeof(*address));
430 if (x < 0)
431 debug(5, 9) ("connect FD %d: %s\n", sock, xstrerror());
432 } else {
433 #if defined(_SQUID_NEWSOS6_)
434 /* Makoto MATSUSHITA <matusita@ics.es.osaka-u.ac.jp> */
435 connect(sock, (struct sockaddr *) address, sizeof(*address));
436 if (errno == EINVAL) {
437 errlen = sizeof(err);
438 x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
439 if (x >= 0)
440 errno = x;
441 }
442 #else
443 errlen = sizeof(err);
444 x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
445 if (x == 0)
446 errno = err;
447 #if defined(_SQUID_SOLARIS_)
448 /*
449 * Solaris 2.4's socket emulation doesn't allow you
450 * to determine the error from a failed non-blocking
451 * connect and just returns EPIPE. Create a fake
452 * error message for connect. -- fenner@parc.xerox.com
453 */
454 if (x < 0 && errno == EPIPE)
455 errno = ENOTCONN;
456 #endif
457 #endif
458 }
459 if (errno == 0 || errno == EISCONN)
460 status = COMM_OK;
461 else if (ignoreErrno(errno))
462 status = COMM_INPROGRESS;
463 else
464 return COMM_ERROR;
465 xstrncpy(F->ipaddr, inet_ntoa(address->sin_addr), 16);
466 F->remote_port = ntohs(address->sin_port);
467 if (status == COMM_OK) {
468 debug(5, 10) ("comm_connect_addr: FD %d connected to %s:%d\n",
469 sock, F->ipaddr, F->remote_port);
470 } else if (status == COMM_INPROGRESS) {
471 debug(5, 10) ("comm_connect_addr: FD %d connection pending\n", sock);
472 }
473 return status;
474 }
475
476 /* Wait for an incoming connection on FD. FD should be a socket returned
477 * from comm_listen. */
478 int
479 comm_accept(int fd, struct sockaddr_in *pn, struct sockaddr_in *me)
480 {
481 int sock;
482 struct sockaddr_in P;
483 struct sockaddr_in M;
484 socklen_t Slen;
485 fde *F = NULL;
486 Slen = sizeof(P);
487 statCounter.syscalls.sock.accepts++;
488 if ((sock = accept(fd, (struct sockaddr *) &P, &Slen)) < 0) {
489 if (ignoreErrno(errno)) {
490 debug(50, 5) ("comm_accept: FD %d: %s\n", fd, xstrerror());
491 return COMM_NOMESSAGE;
492 } else if (ENFILE == errno || EMFILE == errno) {
493 debug(50, 3) ("comm_accept: FD %d: %s\n", fd, xstrerror());
494 return COMM_ERROR;
495 } else {
496 debug(50, 1) ("comm_accept: FD %d: %s\n", fd, xstrerror());
497 return COMM_ERROR;
498 }
499 }
500 if (pn)
501 *pn = P;
502 Slen = sizeof(M);
503 memset(&M, '\0', Slen);
504 getsockname(sock, (struct sockaddr *) &M, &Slen);
505 if (me)
506 *me = M;
507 commSetCloseOnExec(sock);
508 /* fdstat update */
509 fd_open(sock, FD_SOCKET, "HTTP Request");
510 F = &fd_table[sock];
511 xstrncpy(F->ipaddr, inet_ntoa(P.sin_addr), 16);
512 F->remote_port = htons(P.sin_port);
513 F->local_port = htons(M.sin_port);
514 commSetNonBlocking(sock);
515 return sock;
516 }
517
518 void
519 commCallCloseHandlers(int fd)
520 {
521 fde *F = &fd_table[fd];
522 close_handler *ch;
523 debug(5, 5) ("commCallCloseHandlers: FD %d\n", fd);
524 while ((ch = F->close_handler) != NULL) {
525 F->close_handler = ch->next;
526 debug(5, 5) ("commCallCloseHandlers: ch->handler=%p\n", ch->handler);
527 if (cbdataValid(ch->data))
528 ch->handler(fd, ch->data);
529 cbdataUnlock(ch->data);
530 memPoolFree(conn_close_pool, ch); /* AAA */
531 }
532 }
533
534 #if LINGERING_CLOSE
535 static void
536 commLingerClose(int fd, void *unused)
537 {
538 LOCAL_ARRAY(char, buf, 1024);
539 int n;
540 n = read(fd, buf, 1024);
541 if (n < 0)
542 debug(5, 3) ("commLingerClose: FD %d read: %s\n", fd, xstrerror());
543 comm_close(fd);
544 }
545
546 static void
547 commLingerTimeout(int fd, void *unused)
548 {
549 debug(5, 3) ("commLingerTimeout: FD %d\n", fd);
550 comm_close(fd);
551 }
552
553 /*
554 * Inspired by apache
555 */
556 void
557 comm_lingering_close(int fd)
558 {
559 if (shutdown(fd, 1) < 0) {
560 comm_close(fd);
561 return;
562 }
563 fd_note(fd, "lingering close");
564 commSetTimeout(fd, 10, commLingerTimeout, NULL);
565 commSetSelect(fd, COMM_SELECT_READ, commLingerClose, NULL, 0);
566 }
567 #endif
568
569 void
570 comm_close(int fd)
571 {
572 fde *F = NULL;
573 debug(5, 5) ("comm_close: FD %d\n", fd);
574 assert(fd >= 0);
575 assert(fd < Squid_MaxFD);
576 F = &fd_table[fd];
577 if (F->flags.closing)
578 return;
579 if (shutting_down && (!F->flags.open || F->type == FD_FILE))
580 return;
581 assert(F->flags.open);
582 assert(F->type != FD_FILE);
583 F->flags.closing = 1;
584 CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING);
585 commCallCloseHandlers(fd);
586 if (F->uses) /* assume persistent connect count */
587 pconnHistCount(1, F->uses);
588 fd_close(fd); /* update fdstat */
589 close(fd);
590 statCounter.syscalls.sock.closes++;
591 }
592
593 /* Send a udp datagram to specified TO_ADDR. */
594 int
595 comm_udp_sendto(int fd,
596 const struct sockaddr_in *to_addr,
597 int addr_len,
598 const void *buf,
599 int len)
600 {
601 int x;
602 statCounter.syscalls.sock.sendtos++;
603 x = sendto(fd, buf, len, 0, (struct sockaddr *) to_addr, addr_len);
604 if (x < 0) {
605 #ifdef _SQUID_LINUX_
606 if (ECONNREFUSED != errno)
607 #endif
608 debug(50, 1) ("comm_udp_sendto: FD %d, %s, port %d: %s\n",
609 fd,
610 inet_ntoa(to_addr->sin_addr),
611 (int) htons(to_addr->sin_port),
612 xstrerror());
613 return COMM_ERROR;
614 }
615 return x;
616 }
617
618 void
619 commSetDefer(int fd, DEFER * func, void *data)
620 {
621 fde *F = &fd_table[fd];
622 F->defer_check = func;
623 F->defer_data = data;
624 }
625
626 void
627 commSetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout)
628 {
629 fde *F = &fd_table[fd];
630 assert(fd >= 0);
631 assert(F->flags.open);
632 debug(5, 5) ("commSetSelect: FD %d type %d\n", fd, type);
633 if (type & COMM_SELECT_READ) {
634 F->read_handler = handler;
635 F->read_data = client_data;
636 commUpdateReadBits(fd, handler);
637 }
638 if (type & COMM_SELECT_WRITE) {
639 F->write_handler = handler;
640 F->write_data = client_data;
641 commUpdateWriteBits(fd, handler);
642 }
643 if (timeout)
644 F->timeout = squid_curtime + timeout;
645 }
646
647 void
648 comm_add_close_handler(int fd, PF * handler, void *data)
649 {
650 close_handler *new = memPoolAlloc(conn_close_pool); /* AAA */
651 close_handler *c;
652 debug(5, 5) ("comm_add_close_handler: FD %d, handler=%p, data=%p\n",
653 fd, handler, data);
654 for (c = fd_table[fd].close_handler; c; c = c->next)
655 assert(c->handler != handler || c->data != data);
656 new->handler = handler;
657 new->data = data;
658 new->next = fd_table[fd].close_handler;
659 fd_table[fd].close_handler = new;
660 cbdataLock(data);
661 }
662
663 void
664 comm_remove_close_handler(int fd, PF * handler, void *data)
665 {
666 close_handler *p;
667 close_handler *last = NULL;
668 /* Find handler in list */
669 debug(5, 5) ("comm_remove_close_handler: FD %d, handler=%p, data=%p\n",
670 fd, handler, data);
671 for (p = fd_table[fd].close_handler; p != NULL; last = p, p = p->next)
672 if (p->handler == handler && p->data == data)
673 break; /* This is our handler */
674 assert(p != NULL);
675 /* Remove list entry */
676 if (last)
677 last->next = p->next;
678 else
679 fd_table[fd].close_handler = p->next;
680 cbdataUnlock(p->data);
681 memPoolFree(conn_close_pool, p); /* AAA */
682
683 }
684
685 static void
686 commSetNoLinger(int fd)
687 {
688 struct linger L;
689 L.l_onoff = 0; /* off */
690 L.l_linger = 0;
691 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
692 debug(50, 0) ("commSetNoLinger: FD %d: %s\n", fd, xstrerror());
693 fd_table[fd].flags.nolinger = 1;
694 }
695
696 static void
697 commSetReuseAddr(int fd)
698 {
699 int on = 1;
700 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0)
701 debug(50, 1) ("commSetReuseAddr: FD %d: %s\n", fd, xstrerror());
702 }
703
704 static void
705 commSetTcpRcvbuf(int fd, int size)
706 {
707 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0)
708 debug(50, 1) ("commSetTcpRcvbuf: FD %d, SIZE %d: %s\n",
709 fd, size, xstrerror());
710 }
711
712 int
713 commSetNonBlocking(int fd)
714 {
715 int flags;
716 int dummy = 0;
717 #ifdef _SQUID_CYGWIN_
718 int nonblocking = TRUE;
719 if (fd_table[fd].type != FD_PIPE) {
720 if (ioctl(fd, FIONBIO, &nonblocking) < 0) {
721 debug(50, 0) ("commSetNonBlocking: FD %d: %s %D\n", fd, xstrerror(), fd_table[fd].type);
722 return COMM_ERROR;
723 }
724 } else {
725 #endif
726 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
727 debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
728 return COMM_ERROR;
729 }
730 if (fcntl(fd, F_SETFL, flags | SQUID_NONBLOCK) < 0) {
731 debug(50, 0) ("commSetNonBlocking: FD %d: %s\n", fd, xstrerror());
732 return COMM_ERROR;
733 }
734 #ifdef _SQUID_CYGWIN_
735 }
736 #endif
737 fd_table[fd].flags.nonblocking = 1;
738 return 0;
739 }
740
741 int
742 commUnsetNonBlocking(int fd)
743 {
744 int flags;
745 int dummy = 0;
746 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
747 debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
748 return COMM_ERROR;
749 }
750 if (fcntl(fd, F_SETFL, flags & (~SQUID_NONBLOCK)) < 0) {
751 debug(50, 0) ("commUnsetNonBlocking: FD %d: %s\n", fd, xstrerror());
752 return COMM_ERROR;
753 }
754 fd_table[fd].flags.nonblocking = 0;
755 return 0;
756 }
757
758 void
759 commSetCloseOnExec(int fd)
760 {
761 #ifdef FD_CLOEXEC
762 int flags;
763 int dummy = 0;
764 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
765 debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
766 return;
767 }
768 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
769 debug(50, 0) ("FD %d: set close-on-exec failed: %s\n", fd, xstrerror());
770 #endif
771 }
772
773 #ifdef TCP_NODELAY
774 static void
775 commSetTcpNoDelay(int fd)
776 {
777 int on = 1;
778 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0)
779 debug(50, 1) ("commSetTcpNoDelay: FD %d: %s\n", fd, xstrerror());
780 }
781 #endif
782
783
784 void
785 comm_init(void)
786 {
787 fd_table = xcalloc(Squid_MaxFD, sizeof(fde));
788 /* XXX account fd_table */
789 /* Keep a few file descriptors free so that we don't run out of FD's
790 * after accepting a client but before it opens a socket or a file.
791 * Since Squid_MaxFD can be as high as several thousand, don't waste them */
792 RESERVED_FD = XMIN(100, Squid_MaxFD / 4);
793 CBDATA_INIT_TYPE(ConnectStateData);
794 comm_write_pool = memPoolCreate("CommWriteStateData", sizeof(CommWriteStateData));
795 conn_close_pool = memPoolCreate("close_handler", sizeof(close_handler));
796 }
797
798 /* Write to FD. */
799 static void
800 commHandleWrite(int fd, void *data)
801 {
802 CommWriteStateData *state = data;
803 int len = 0;
804 int nleft;
805
806 debug(5, 5) ("commHandleWrite: FD %d: off %d, sz %d.\n",
807 fd, (int) state->offset, state->size);
808
809 nleft = state->size - state->offset;
810 len = write(fd, state->buf + state->offset, nleft);
811 debug(5, 5) ("commHandleWrite: write() returns %d\n", len);
812 fd_bytes(fd, len, FD_WRITE);
813 statCounter.syscalls.sock.writes++;
814
815 if (len == 0) {
816 /* Note we even call write if nleft == 0 */
817 /* We're done */
818 if (nleft != 0)
819 debug(5, 1) ("commHandleWrite: FD %d: write failure: connection closed with %d bytes remaining.\n", fd, nleft);
820 CommWriteStateCallbackAndFree(fd, nleft ? COMM_ERROR : COMM_OK);
821 } else if (len < 0) {
822 /* An error */
823 if (fd_table[fd].flags.socket_eof) {
824 debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n",
825 fd, xstrerror());
826 CommWriteStateCallbackAndFree(fd, COMM_ERROR);
827 } else if (ignoreErrno(errno)) {
828 debug(50, 10) ("commHandleWrite: FD %d: write failure: %s.\n",
829 fd, xstrerror());
830 commSetSelect(fd,
831 COMM_SELECT_WRITE,
832 commHandleWrite,
833 state,
834 0);
835 } else {
836 debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n",
837 fd, xstrerror());
838 CommWriteStateCallbackAndFree(fd, COMM_ERROR);
839 }
840 } else {
841 /* A successful write, continue */
842 state->offset += len;
843 if (state->offset < state->size) {
844 /* Not done, reinstall the write handler and write some more */
845 commSetSelect(fd,
846 COMM_SELECT_WRITE,
847 commHandleWrite,
848 state,
849 0);
850 } else {
851 CommWriteStateCallbackAndFree(fd, COMM_OK);
852 }
853 }
854 }
855
856
857
858 /* Select for Writing on FD, until SIZE bytes are sent. Call
859 * *HANDLER when complete. */
860 void
861 comm_write(int fd, char *buf, int size, CWCB * handler, void *handler_data, FREE * free_func)
862 {
863 CommWriteStateData *state = fd_table[fd].rwstate;
864 debug(5, 5) ("comm_write: FD %d: sz %d: hndl %p: data %p.\n",
865 fd, size, handler, handler_data);
866 if (NULL != state) {
867 debug(5, 1) ("comm_write: fd_table[%d].rwstate != NULL\n", fd);
868 memPoolFree(comm_write_pool, state);
869 fd_table[fd].rwstate = NULL;
870 }
871 assert(state == NULL);
872 fd_table[fd].rwstate = state = memPoolAlloc(comm_write_pool);
873 state->buf = buf;
874 state->size = size;
875 state->offset = 0;
876 state->handler = handler;
877 state->handler_data = handler_data;
878 state->free_func = free_func;
879 cbdataLock(handler_data);
880 commSetSelect(fd, COMM_SELECT_WRITE, commHandleWrite, state, 0);
881 }
882
883 /* a wrapper around comm_write to allow for MemBuf to be comm_written in a snap */
884 void
885 comm_write_mbuf(int fd, MemBuf mb, CWCB * handler, void *handler_data)
886 {
887 comm_write(fd, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb));
888 }
889
890 /*
891 * hm, this might be too general-purpose for all the places we'd
892 * like to use it.
893 */
894 int
895 ignoreErrno(int ierrno)
896 {
897 switch (ierrno) {
898 case EINPROGRESS:
899 case EWOULDBLOCK:
900 #if EAGAIN != EWOULDBLOCK
901 case EAGAIN:
902 #endif
903 case EALREADY:
904 case EINTR:
905 #ifdef ERESTART
906 case ERESTART:
907 #endif
908 return 1;
909 default:
910 return 0;
911 }
912 /* NOTREACHED */
913 }
914
915 void
916 commCloseAllSockets(void)
917 {
918 int fd;
919 fde *F = NULL;
920 PF *callback;
921 for (fd = 0; fd <= Biggest_FD; fd++) {
922 F = &fd_table[fd];
923 if (!F->flags.open)
924 continue;
925 if (F->type != FD_SOCKET)
926 continue;
927 if (F->flags.ipc) /* don't close inter-process sockets */
928 continue;
929 if (F->timeout_handler) {
930 debug(5, 5) ("commCloseAllSockets: FD %d: Calling timeout handler\n",
931 fd);
932 callback = F->timeout_handler;
933 F->timeout_handler = NULL;
934 callback(fd, F->timeout_data);
935 } else {
936 debug(5, 5) ("commCloseAllSockets: FD %d: calling comm_close()\n", fd);
937 comm_close(fd);
938 }
939 }
940 }