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