]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm.cc
fix headers to allow inclusion into C++ source
[thirdparty/squid.git] / src / comm.cc
CommitLineData
da2b3a17 1
30a4f2a8 2/*
29b8d8d6 3 * $Id: comm.cc,v 1.334 2002/09/15 06:40:57 robertc Exp $
30a4f2a8 4 *
5 * DEBUG: section 5 Socket Functions
6 * AUTHOR: Harvest Derived
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
2b6662ba 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.
30a4f2a8 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
30a4f2a8 34 */
090089c4 35
44a47c6e 36#include "squid.h"
090089c4 37
b671cc68 38#if defined(_SQUID_CYGWIN_)
39#include <sys/ioctl.h>
40#endif
30a4f2a8 41#ifdef HAVE_NETINET_TCP_H
42#include <netinet/tcp.h>
43#endif
090089c4 44
f88211e8 45typedef struct {
46 char *host;
47 u_short port;
48 struct sockaddr_in S;
49 CNCB *callback;
50 void *data;
f88211e8 51 struct in_addr in_addr;
52 int locks;
03a1ee42 53 int fd;
22c653cd 54 int tries;
55 int addrcount;
56 int connstart;
f88211e8 57} ConnectStateData;
58
090089c4 59/* STATIC */
f5b8bbc4 60static int commBind(int s, struct in_addr, u_short port);
f5b8bbc4 61static void commSetReuseAddr(int);
62static void commSetNoLinger(int);
3d7e9d7c 63static void CommWriteStateCallbackAndFree(int fd, comm_err_t code);
30a4f2a8 64#ifdef TCP_NODELAY
f5b8bbc4 65static void commSetTcpNoDelay(int);
30a4f2a8 66#endif
f5b8bbc4 67static void commSetTcpRcvbuf(int, int);
f88211e8 68static PF commConnectFree;
03a1ee42 69static PF commConnectHandle;
70static PF commHandleWrite;
edeb28fd 71static IPH commConnectDnsHandle;
3d7e9d7c 72static void commConnectCallback(ConnectStateData * cs, comm_err_t status);
22c653cd 73static int commResetFD(ConnectStateData * cs);
74static int commRetryConnect(ConnectStateData * cs);
28c60158 75CBDATA_TYPE(ConnectStateData);
723123a9 76
77static MemPool *comm_write_pool = NULL;
58cd5bbd 78static MemPool *conn_close_pool = NULL;
309ad3b6 79
b8d8561b 80static void
3d7e9d7c 81CommWriteStateCallbackAndFree(int fd, comm_err_t code)
9864ee44 82{
f17936ab 83 CommWriteStateData *CommWriteState = fd_table[fd].rwstate;
84 CWCB *callback = NULL;
fa80a8ef 85 void *cbdata;
a56a3abe 86 fd_table[fd].rwstate = NULL;
f17936ab 87 if (CommWriteState == NULL)
9864ee44 88 return;
c0dec081 89 if (CommWriteState->free_func) {
729dd65c 90 FREE *free_func = CommWriteState->free_func;
91 void *free_buf = CommWriteState->buf;
92 CommWriteState->free_func = NULL;
f17936ab 93 CommWriteState->buf = NULL;
729dd65c 94 free_func(free_buf);
9864ee44 95 }
f17936ab 96 callback = CommWriteState->handler;
97 CommWriteState->handler = NULL;
fa80a8ef 98 if (callback && cbdataReferenceValidDone(CommWriteState->handler_data, &cbdata))
99 callback(fd, CommWriteState->buf, CommWriteState->offset, code, cbdata);
723123a9 100 memPoolFree(comm_write_pool, CommWriteState);
9864ee44 101}
102
090089c4 103/* Return the local port associated with fd. */
b8d8561b 104u_short
105comm_local_port(int fd)
090089c4 106{
107 struct sockaddr_in addr;
6637e3a5 108 socklen_t addr_len = 0;
76f87348 109 fde *F = &fd_table[fd];
090089c4 110
090089c4 111 /* If the fd is closed already, just return */
60c0b5a2 112 if (!F->flags.open) {
a3d5953d 113 debug(5, 0) ("comm_local_port: FD %d has been closed.\n", fd);
30a4f2a8 114 return 0;
090089c4 115 }
76f87348 116 if (F->local_port)
117 return F->local_port;
090089c4 118 addr_len = sizeof(addr);
119 if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {
a3d5953d 120 debug(50, 1) ("comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror());
30a4f2a8 121 return 0;
090089c4 122 }
76f87348 123 F->local_port = ntohs(addr.sin_port);
5f6ac48b 124 debug(5, 6) ("comm_local_port: FD %d: port %d\n", fd, (int) F->local_port);
76f87348 125 return F->local_port;
090089c4 126}
127
3d7e9d7c 128static comm_err_t
b8d8561b 129commBind(int s, struct in_addr in_addr, u_short port)
090089c4 130{
131 struct sockaddr_in S;
090089c4 132
090089c4 133 memset(&S, '\0', sizeof(S));
134 S.sin_family = AF_INET;
135 S.sin_port = htons(port);
30a4f2a8 136 S.sin_addr = in_addr;
83704487 137 statCounter.syscalls.sock.binds++;
090089c4 138 if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0)
139 return COMM_OK;
a3d5953d 140 debug(50, 0) ("commBind: Cannot bind socket FD %d to %s:%d: %s\n",
090089c4 141 s,
30a4f2a8 142 S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr),
44a62238 143 (int) port,
144 xstrerror());
090089c4 145 return COMM_ERROR;
146}
147
148/* Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE
d6827718 149 * is OR of flags specified in comm.h. Defaults TOS */
b8d8561b 150int
16b204c4 151comm_open(int sock_type,
cc6a9d2e 152 int proto,
153 struct in_addr addr,
154 u_short port,
155 int flags,
0ee4272b 156 const char *note)
d6827718 157{
158 return comm_openex(sock_type, proto, addr, port, flags, 0, note);
159}
160
161
162/* Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE
163 * is OR of flags specified in defines.h:COMM_* */
164int
165comm_openex(int sock_type,
166 int proto,
167 struct in_addr addr,
168 u_short port,
169 int flags,
170 unsigned char TOS,
171 const char *note)
090089c4 172{
173 int new_socket;
9056f553 174 int tos = 0;
76f87348 175 fde *F = NULL;
090089c4 176
177 /* Create socket for accepting new connections. */
83704487 178 statCounter.syscalls.sock.sockets++;
16b204c4 179 if ((new_socket = socket(AF_INET, sock_type, proto)) < 0) {
090089c4 180 /* Increase the number of reserved fd's if calls to socket()
181 * are failing because the open file table is full. This
182 * limits the number of simultaneous clients */
183 switch (errno) {
184 case ENFILE:
185 case EMFILE:
a3d5953d 186 debug(50, 1) ("comm_open: socket failure: %s\n", xstrerror());
9bc73deb 187 fdAdjustReserved();
090089c4 188 break;
189 default:
a3d5953d 190 debug(50, 0) ("comm_open: socket failure: %s\n", xstrerror());
090089c4 191 }
603a02fd 192 return -1;
090089c4 193 }
d6827718 194 /* set TOS if needed */
195 if (TOS) {
196#ifdef IP_TOS
197 tos = TOS;
198 if (setsockopt(new_socket, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
199 debug(50, 1) ("comm_open: setsockopt(IP_TOS) on FD %d: %s\n",
200 new_socket, xstrerror());
201#else
202 debug(50, 0) ("comm_open: setsockopt(IP_TOS) not supported on this platform\n");
203#endif
204 }
090089c4 205 /* update fdstat */
365e5b34 206 debug(5, 5) ("comm_open: FD %d is a new socket\n", new_socket);
5c5783a2 207 fd_open(new_socket, FD_SOCKET, note);
76f87348 208 F = &fd_table[new_socket];
d6827718 209 F->local_addr = addr;
210 F->tos = tos;
79a15e0a 211 if (!(flags & COMM_NOCLOEXEC))
3ca60c86 212 commSetCloseOnExec(new_socket);
cdc33f35 213 if ((flags & COMM_REUSEADDR))
214 commSetReuseAddr(new_socket);
7690e8eb 215 if (port > (u_short) 0) {
30a4f2a8 216 commSetNoLinger(new_socket);
3b4be6a6 217 if (opt_reuseaddr)
090089c4 218 commSetReuseAddr(new_socket);
090089c4 219 }
a3724d50 220 if (addr.s_addr != no_addr.s_addr) {
221 if (commBind(new_socket, addr, port) != COMM_OK) {
222 comm_close(new_socket);
603a02fd 223 return -1;
a3724d50 224 }
23ff6968 225 }
76f87348 226 F->local_port = port;
090089c4 227
79a15e0a 228 if (flags & COMM_NONBLOCKING)
30a4f2a8 229 if (commSetNonBlocking(new_socket) == COMM_ERROR)
603a02fd 230 return -1;
30a4f2a8 231#ifdef TCP_NODELAY
232 if (sock_type == SOCK_STREAM)
233 commSetTcpNoDelay(new_socket);
234#endif
1241e63e 235 if (Config.tcpRcvBufsz > 0 && sock_type == SOCK_STREAM)
236 commSetTcpRcvbuf(new_socket, Config.tcpRcvBufsz);
090089c4 237 return new_socket;
238}
239
b4ea1f2e 240/*
241 * NOTE: set the listen queue to Squid_MaxFD/4 and rely on the kernel to
242 * impose an upper limit. Solaris' listen(3n) page says it has
243 * no limit on this parameter, but sys/socket.h sets SOMAXCONN
244 * to 5. HP-UX currently has a limit of 20. SunOS is 5 and
245 * OSF 3.0 is 8.
246 */
b8d8561b 247int
248comm_listen(int sock)
090089c4 249{
250 int x;
e83892e9 251 if ((x = listen(sock, Squid_MaxFD >> 2)) < 0) {
a3d5953d 252 debug(50, 0) ("comm_listen: listen(%d, %d): %s\n",
e83892e9 253 Squid_MaxFD >> 2,
090089c4 254 sock, xstrerror());
255 return x;
256 }
257 return sock;
258}
259
e5f6c5c2 260void
4f92c80c 261commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data)
e924600d 262{
28c60158 263 ConnectStateData *cs;
6a988308 264 debug(5, 3) ("commConnectStart: FD %d, %s:%d\n", fd, host, (int) port);
72711e31 265 cs = cbdataAlloc(ConnectStateData);
03a1ee42 266 cs->fd = fd;
e924600d 267 cs->host = xstrdup(host);
268 cs->port = port;
269 cs->callback = callback;
fa80a8ef 270 cs->data = cbdataReference(data);
e924600d 271 comm_add_close_handler(fd, commConnectFree, cs);
f88211e8 272 cs->locks++;
8407afee 273 ipcache_nbgethostbyname(host, commConnectDnsHandle, cs);
edeb28fd 274}
275
276static void
03a1ee42 277commConnectDnsHandle(const ipcache_addrs * ia, void *data)
edeb28fd 278{
279 ConnectStateData *cs = data;
f88211e8 280 assert(cs->locks == 1);
281 cs->locks--;
edeb28fd 282 if (ia == NULL) {
a3d5953d 283 debug(5, 3) ("commConnectDnsHandle: Unknown host: %s\n", cs->host);
6cf028ab 284 if (!dns_error_message) {
285 dns_error_message = "Unknown DNS error";
0e473d70 286 debug(5, 1) ("commConnectDnsHandle: Bad dns_error_message\n");
6cf028ab 287 }
a64c2869 288 assert(dns_error_message != NULL);
03a1ee42 289 commConnectCallback(cs, COMM_ERR_DNS);
edeb28fd 290 return;
291 }
f076b37b 292 assert(ia->cur < ia->count);
edeb28fd 293 cs->in_addr = ia->in_addrs[ia->cur];
52926044 294 ipcacheCycleAddr(cs->host, NULL);
22c653cd 295 cs->addrcount = ia->count;
296 cs->connstart = squid_curtime;
03a1ee42 297 commConnectHandle(cs->fd, cs);
e924600d 298}
299
f88211e8 300static void
3d7e9d7c 301commConnectCallback(ConnectStateData * cs, comm_err_t status)
f88211e8 302{
a3d5953d 303 CNCB *callback = cs->callback;
fa80a8ef 304 void *cbdata = cs->data;
03a1ee42 305 int fd = cs->fd;
a3d5953d 306 comm_remove_close_handler(fd, commConnectFree, cs);
9daca08e 307 cs->callback = NULL;
308 cs->data = NULL;
e1b16349 309 commSetTimeout(fd, -1, NULL, NULL);
a3d5953d 310 commConnectFree(fd, cs);
fa80a8ef 311 if (cbdataReferenceValid(cbdata))
312 callback(fd, status, cbdata);
f88211e8 313}
314
e924600d 315static void
9daca08e 316commConnectFree(int fd, void *data)
e924600d 317{
318 ConnectStateData *cs = data;
9daca08e 319 debug(5, 3) ("commConnectFree: FD %d\n", fd);
fa80a8ef 320 cbdataReferenceDone(cs->data);
8407afee 321 safe_free(cs->host);
322 cbdataFree(cs);
e924600d 323}
324
22c653cd 325/* Reset FD so that we can connect() again */
edeb28fd 326static int
22c653cd 327commResetFD(ConnectStateData * cs)
edeb28fd 328{
329 int fd2;
d6827718 330 fde *F;
fa80a8ef 331 if (!cbdataReferenceValid(cs->data))
7dd44885 332 return 0;
83704487 333 statCounter.syscalls.sock.sockets++;
edeb28fd 334 fd2 = socket(AF_INET, SOCK_STREAM, 0);
83704487 335 statCounter.syscalls.sock.sockets++;
edeb28fd 336 if (fd2 < 0) {
22c653cd 337 debug(5, 0) ("commResetFD: socket: %s\n", xstrerror());
9bc73deb 338 if (ENFILE == errno || EMFILE == errno)
339 fdAdjustReserved();
edeb28fd 340 return 0;
341 }
22c653cd 342 if (dup2(fd2, cs->fd) < 0) {
343 debug(5, 0) ("commResetFD: dup2: %s\n", xstrerror());
9bc73deb 344 if (ENFILE == errno || EMFILE == errno)
345 fdAdjustReserved();
d6827718 346 close(fd2);
edeb28fd 347 return 0;
348 }
edeb28fd 349 close(fd2);
d6827718 350 F = &fd_table[cs->fd];
b5568a61 351 fd_table[cs->fd].flags.called_connect = 0;
09544acc 352 /*
353 * yuck, this has assumptions about comm_open() arguments for
354 * the original socket
355 */
d6827718 356 if (commBind(cs->fd, F->local_addr, F->local_port) != COMM_OK) {
357 debug(5, 0) ("commResetFD: bind: %s\n", xstrerror());
358 return 0;
09544acc 359 }
d6827718 360#ifdef IP_TOS
361 if (F->tos) {
362 int tos = F->tos;
363 if (setsockopt(cs->fd, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
364 debug(50, 1) ("commResetFD: setsockopt(IP_TOS) on FD %d: %s\n", cs->fd, xstrerror());
365 }
366#endif
367 if (F->flags.close_on_exec)
368 commSetCloseOnExec(cs->fd);
369 if (F->flags.nonblocking)
370 commSetNonBlocking(cs->fd);
09544acc 371#ifdef TCP_NODELAY
d6827718 372 if (F->flags.nodelay)
373 commSetTcpNoDelay(cs->fd);
09544acc 374#endif
375 if (Config.tcpRcvBufsz > 0)
376 commSetTcpRcvbuf(cs->fd, Config.tcpRcvBufsz);
edeb28fd 377 return 1;
378}
379
22c653cd 380static int
381commRetryConnect(ConnectStateData * cs)
382{
383 assert(cs->addrcount > 0);
384 if (cs->addrcount == 1) {
385 if (cs->tries >= Config.retry.maxtries)
386 return 0;
387 if (squid_curtime - cs->connstart > Config.Timeout.connect)
388 return 0;
22c653cd 389 } else {
390 if (cs->tries > cs->addrcount)
391 return 0;
392 }
393 return commResetFD(cs);
394}
395
e924600d 396/* Connect SOCK to specified DEST_PORT at DEST_HOST. */
397static void
398commConnectHandle(int fd, void *data)
090089c4 399{
f88211e8 400 ConnectStateData *cs = data;
401 if (cs->S.sin_addr.s_addr == 0) {
402 cs->S.sin_family = AF_INET;
403 cs->S.sin_addr = cs->in_addr;
404 cs->S.sin_port = htons(cs->port);
17a0a4ee 405 if (Config.onoff.log_fqdn)
f88211e8 406 fqdncache_gethostbyaddr(cs->S.sin_addr, FQDN_LOOKUP_IF_MISS);
e5f6c5c2 407 }
f88211e8 408 switch (comm_connect_addr(fd, &cs->S)) {
e5f6c5c2 409 case COMM_INPROGRESS:
11994bb9 410 debug(5, 5) ("commConnectHandle: FD %d: COMM_INPROGRESS\n", fd);
f88211e8 411 commSetSelect(fd, COMM_SELECT_WRITE, commConnectHandle, cs, 0);
e5f6c5c2 412 break;
413 case COMM_OK:
22c653cd 414 ipcacheMarkGoodAddr(cs->host, cs->S.sin_addr);
03a1ee42 415 commConnectCallback(cs, COMM_OK);
e5f6c5c2 416 break;
417 default:
22c653cd 418 cs->tries++;
419 ipcacheMarkBadAddr(cs->host, cs->S.sin_addr);
194dd3b8 420 if (Config.onoff.test_reachability)
421 netdbDeleteAddrNetwork(cs->S.sin_addr);
22c653cd 422 if (commRetryConnect(cs)) {
f88211e8 423 cs->locks++;
8407afee 424 ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs);
edeb28fd 425 } else {
03a1ee42 426 commConnectCallback(cs, COMM_ERR_CONNECT);
edeb28fd 427 }
e5f6c5c2 428 break;
090089c4 429 }
090089c4 430}
22c653cd 431
b8d8561b 432int
4f92c80c 433commSetTimeout(int fd, int timeout, PF * handler, void *data)
090089c4 434{
76f87348 435 fde *F;
a3d5953d 436 debug(5, 3) ("commSetTimeout: FD %d timeout %d\n", fd, timeout);
03eb2f01 437 assert(fd >= 0);
438 assert(fd < Squid_MaxFD);
76f87348 439 F = &fd_table[fd];
60c0b5a2 440 assert(F->flags.open);
5c5783a2 441 if (timeout < 0) {
a3fa14bf 442 cbdataReferenceDone(F->timeout_data);
76f87348 443 F->timeout_handler = NULL;
a3fa14bf 444 F->timeout = 0;
5849612f 445 } else {
446 assert(handler || F->timeout_handler);
447 if (handler) {
448 cbdataReferenceDone(F->timeout_data);
449 F->timeout_handler = handler;
450 F->timeout_data = cbdataReference(data);
451 }
452 F->timeout = squid_curtime + (time_t) timeout;
30a4f2a8 453 }
a3fa14bf 454 return F->timeout;
090089c4 455}
456
b8d8561b 457int
0ee4272b 458comm_connect_addr(int sock, const struct sockaddr_in *address)
090089c4 459{
3d7e9d7c 460 comm_err_t status = COMM_OK;
76f87348 461 fde *F = &fd_table[sock];
090089c4 462 int x;
b5568a61 463 int err = 0;
9689d97c 464 socklen_t errlen;
489b22c1 465 assert(ntohs(address->sin_port) != 0);
090089c4 466 /* Establish connection. */
b5568a61 467 errno = 0;
468 if (!F->flags.called_connect) {
469 F->flags.called_connect = 1;
83704487 470 statCounter.syscalls.sock.connects++;
b5568a61 471 x = connect(sock, (struct sockaddr *) address, sizeof(*address));
54f742e7 472 if (x < 0)
473 debug(5, 9) ("connect FD %d: %s\n", sock, xstrerror());
b5568a61 474 } else {
140e2c0b 475#if defined(_SQUID_NEWSOS6_)
33ac9442 476 /* Makoto MATSUSHITA <matusita@ics.es.osaka-u.ac.jp> */
477 connect(sock, (struct sockaddr *) address, sizeof(*address));
478 if (errno == EINVAL) {
479 errlen = sizeof(err);
480 x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
481 if (x >= 0)
482 errno = x;
483 }
484#else
b5568a61 485 errlen = sizeof(err);
486 x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
487 if (x == 0)
488 errno = err;
489#if defined(_SQUID_SOLARIS_)
490 /*
491 * Solaris 2.4's socket emulation doesn't allow you
492 * to determine the error from a failed non-blocking
493 * connect and just returns EPIPE. Create a fake
494 * error message for connect. -- fenner@parc.xerox.com
495 */
496 if (x < 0 && errno == EPIPE)
497 errno = ENOTCONN;
33ac9442 498#endif
30a4f2a8 499#endif
e5f6c5c2 500 }
b5568a61 501 if (errno == 0 || errno == EISCONN)
502 status = COMM_OK;
503 else if (ignoreErrno(errno))
504 status = COMM_INPROGRESS;
505 else
506 return COMM_ERROR;
76f87348 507 xstrncpy(F->ipaddr, inet_ntoa(address->sin_addr), 16);
508 F->remote_port = ntohs(address->sin_port);
090089c4 509 if (status == COMM_OK) {
a3d5953d 510 debug(5, 10) ("comm_connect_addr: FD %d connected to %s:%d\n",
76f87348 511 sock, F->ipaddr, F->remote_port);
f21cd581 512 } else if (status == COMM_INPROGRESS) {
a3d5953d 513 debug(5, 10) ("comm_connect_addr: FD %d connection pending\n", sock);
090089c4 514 }
090089c4 515 return status;
516}
517
518/* Wait for an incoming connection on FD. FD should be a socket returned
519 * from comm_listen. */
b8d8561b 520int
9ef28b60 521comm_accept(int fd, struct sockaddr_in *pn, struct sockaddr_in *me)
090089c4 522{
523 int sock;
1f9afe33 524 struct sockaddr_in P;
525 struct sockaddr_in M;
6637e3a5 526 socklen_t Slen;
76f87348 527 fde *F = NULL;
1f9afe33 528 Slen = sizeof(P);
83704487 529 statCounter.syscalls.sock.accepts++;
603500e7 530 if ((sock = accept(fd, (struct sockaddr *) &P, &Slen)) < 0) {
531 if (ignoreErrno(errno)) {
532 debug(50, 5) ("comm_accept: FD %d: %s\n", fd, xstrerror());
0a0bf5db 533 return COMM_NOMESSAGE;
603500e7 534 } else if (ENFILE == errno || EMFILE == errno) {
535 debug(50, 3) ("comm_accept: FD %d: %s\n", fd, xstrerror());
090089c4 536 return COMM_ERROR;
603500e7 537 } else {
538 debug(50, 1) ("comm_accept: FD %d: %s\n", fd, xstrerror());
090089c4 539 return COMM_ERROR;
540 }
541 }
9ef28b60 542 if (pn)
543 *pn = P;
4053a845 544 Slen = sizeof(M);
545 memset(&M, '\0', Slen);
546 getsockname(sock, (struct sockaddr *) &M, &Slen);
547 if (me)
1f9afe33 548 *me = M;
3ca60c86 549 commSetCloseOnExec(sock);
090089c4 550 /* fdstat update */
5c5783a2 551 fd_open(sock, FD_SOCKET, "HTTP Request");
76f87348 552 F = &fd_table[sock];
c0dec081 553 xstrncpy(F->ipaddr, inet_ntoa(P.sin_addr), 16);
76f87348 554 F->remote_port = htons(P.sin_port);
555 F->local_port = htons(M.sin_port);
090089c4 556 commSetNonBlocking(sock);
090089c4 557 return sock;
558}
559
cb201b7e 560void
561commCallCloseHandlers(int fd)
562{
76f87348 563 fde *F = &fd_table[fd];
f1dc9b30 564 close_handler *ch;
a3d5953d 565 debug(5, 5) ("commCallCloseHandlers: FD %d\n", fd);
29b8d8d6 566 while ((ch = F->closeHandler) != NULL) {
567 F->closeHandler = ch->next;
9daca08e 568 debug(5, 5) ("commCallCloseHandlers: ch->handler=%p\n", ch->handler);
fa80a8ef 569 if (cbdataReferenceValid(ch->data))
603a02fd 570 ch->handler(fd, ch->data);
fa80a8ef 571 cbdataReferenceDone(ch->data);
7f6ffd15 572 memPoolFree(conn_close_pool, ch); /* AAA */
cb201b7e 573 }
574}
575
5492ad1d 576#if LINGERING_CLOSE
577static void
578commLingerClose(int fd, void *unused)
579{
580 LOCAL_ARRAY(char, buf, 1024);
581 int n;
1f7c9178 582 n = FD_READ_METHOD(fd, buf, 1024);
5492ad1d 583 if (n < 0)
584 debug(5, 3) ("commLingerClose: FD %d read: %s\n", fd, xstrerror());
585 comm_close(fd);
586}
587
588static void
589commLingerTimeout(int fd, void *unused)
590{
591 debug(5, 3) ("commLingerTimeout: FD %d\n", fd);
592 comm_close(fd);
593}
594
595/*
596 * Inspired by apache
597 */
598void
599comm_lingering_close(int fd)
600{
d4c19b39 601#if USE_SSL
602 if (fd_table[fd].ssl)
79d4ccdf 603 ssl_shutdown_method(fd);
d4c19b39 604#endif
5492ad1d 605 if (shutdown(fd, 1) < 0) {
606 comm_close(fd);
607 return;
608 }
609 fd_note(fd, "lingering close");
610 commSetTimeout(fd, 10, commLingerTimeout, NULL);
611 commSetSelect(fd, COMM_SELECT_READ, commLingerClose, NULL, 0);
612}
613#endif
614
98264874 615/*
616 * enable linger with time of 0 so that when the socket is
617 * closed, TCP generates a RESET
618 */
619void
620comm_reset_close(int fd)
621{
622 struct linger L;
623 L.l_onoff = 1;
624 L.l_linger = 0;
625 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
626 debug(50, 0) ("commResetTCPClose: FD %d: %s\n", fd, xstrerror());
627 comm_close(fd);
628}
629
b8d8561b 630void
631comm_close(int fd)
090089c4 632{
76f87348 633 fde *F = NULL;
1f7c9178 634
a3d5953d 635 debug(5, 5) ("comm_close: FD %d\n", fd);
03eb2f01 636 assert(fd >= 0);
637 assert(fd < Squid_MaxFD);
76f87348 638 F = &fd_table[fd];
1f7c9178 639
58a6c186 640 if (F->flags.closing)
e102ebda 641 return;
60c0b5a2 642 if (shutting_down && (!F->flags.open || F->type == FD_FILE))
6cf028ab 643 return;
60c0b5a2 644 assert(F->flags.open);
76f87348 645 assert(F->type != FD_FILE);
58a6c186 646 F->flags.closing = 1;
d4c19b39 647#if USE_SSL
648 if (F->ssl)
79d4ccdf 649 ssl_shutdown_method(fd);
d4c19b39 650#endif
fa80a8ef 651 commSetTimeout(fd, -1, NULL, NULL);
96f1be5d 652 CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING);
cb201b7e 653 commCallCloseHandlers(fd);
b716a8ad 654 if (F->uses) /* assume persistent connect count */
655 pconnHistCount(1, F->uses);
d4c19b39 656#if USE_SSL
657 if (F->ssl) {
658 SSL_free(F->ssl);
659 F->ssl = NULL;
660 }
661#endif
5c5783a2 662 fd_close(fd); /* update fdstat */
5874bf28 663 close(fd);
83704487 664 statCounter.syscalls.sock.closes++;
090089c4 665}
666
090089c4 667/* Send a udp datagram to specified TO_ADDR. */
b8d8561b 668int
5df61230 669comm_udp_sendto(int fd,
670 const struct sockaddr_in *to_addr,
671 int addr_len,
17b6e784 672 const void *buf,
5df61230 673 int len)
090089c4 674{
5df61230 675 int x;
83704487 676 statCounter.syscalls.sock.sendtos++;
5df61230 677 x = sendto(fd, buf, len, 0, (struct sockaddr *) to_addr, addr_len);
678 if (x < 0) {
17d51783 679#ifdef _SQUID_LINUX_
680 if (ECONNREFUSED != errno)
681#endif
682 debug(50, 1) ("comm_udp_sendto: FD %d, %s, port %d: %s\n",
683 fd,
684 inet_ntoa(to_addr->sin_addr),
685 (int) htons(to_addr->sin_port),
686 xstrerror());
090089c4 687 return COMM_ERROR;
688 }
5df61230 689 return x;
090089c4 690}
691
b8d8561b 692void
70a9dab4 693commSetDefer(int fd, DEFER * func, void *data)
4883993a 694{
da2b3a17 695 fde *F = &fd_table[fd];
696 F->defer_check = func;
70a9dab4 697 F->defer_data = data;
4883993a 698}
699
b8d8561b 700void
582b6456 701comm_add_close_handler(int fd, PF * handler, void *data)
30a4f2a8 702{
7f6ffd15 703 close_handler *new = memPoolAlloc(conn_close_pool); /* AAA */
cddc721b 704 close_handler *c;
a3d5953d 705 debug(5, 5) ("comm_add_close_handler: FD %d, handler=%p, data=%p\n",
e0c42e90 706 fd, handler, data);
29b8d8d6 707 for (c = fd_table[fd].closeHandler; c; c = c->next)
aeca2a09 708 assert(c->handler != handler || c->data != data);
30a4f2a8 709 new->handler = handler;
fa80a8ef 710 new->data = cbdataReference(data);
29b8d8d6 711 new->next = fd_table[fd].closeHandler;
712 fd_table[fd].closeHandler = new;
30a4f2a8 713}
714
b8d8561b 715void
582b6456 716comm_remove_close_handler(int fd, PF * handler, void *data)
090089c4 717{
f1dc9b30 718 close_handler *p;
719 close_handler *last = NULL;
30a4f2a8 720 /* Find handler in list */
e869f2bd 721 debug(5, 5) ("comm_remove_close_handler: FD %d, handler=%p, data=%p\n",
722 fd, handler, data);
29b8d8d6 723 for (p = fd_table[fd].closeHandler; p != NULL; last = p, p = p->next)
30a4f2a8 724 if (p->handler == handler && p->data == data)
725 break; /* This is our handler */
f88211e8 726 assert(p != NULL);
30a4f2a8 727 /* Remove list entry */
728 if (last)
729 last->next = p->next;
730 else
29b8d8d6 731 fd_table[fd].closeHandler = p->next;
fa80a8ef 732 cbdataReferenceDone(p->data);
733 memPoolFree(conn_close_pool, p);
30a4f2a8 734}
090089c4 735
b8d8561b 736static void
737commSetNoLinger(int fd)
30a4f2a8 738{
739 struct linger L;
090089c4 740 L.l_onoff = 0; /* off */
741 L.l_linger = 0;
30a4f2a8 742 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
a3d5953d 743 debug(50, 0) ("commSetNoLinger: FD %d: %s\n", fd, xstrerror());
58a6c186 744 fd_table[fd].flags.nolinger = 1;
090089c4 745}
746
b8d8561b 747static void
748commSetReuseAddr(int fd)
090089c4 749{
750 int on = 1;
30a4f2a8 751 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0)
a3d5953d 752 debug(50, 1) ("commSetReuseAddr: FD %d: %s\n", fd, xstrerror());
090089c4 753}
754
b8d8561b 755static void
756commSetTcpRcvbuf(int fd, int size)
f868539a 757{
758 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0)
a3d5953d 759 debug(50, 1) ("commSetTcpRcvbuf: FD %d, SIZE %d: %s\n",
b6f794d6 760 fd, size, xstrerror());
f868539a 761}
762
b8d8561b 763int
764commSetNonBlocking(int fd)
30a4f2a8 765{
731e4d49 766 int flags;
9e205701 767 int dummy = 0;
7f6ffd15 768#ifdef _SQUID_CYGWIN_
b05490a8 769 int nonblocking = TRUE;
7f6ffd15 770 if (fd_table[fd].type != FD_PIPE) {
771 if (ioctl(fd, FIONBIO, &nonblocking) < 0) {
772 debug(50, 0) ("commSetNonBlocking: FD %d: %s %D\n", fd, xstrerror(), fd_table[fd].type);
773 return COMM_ERROR;
774 }
775 } else {
776#endif
777 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
778 debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
779 return COMM_ERROR;
780 }
781 if (fcntl(fd, F_SETFL, flags | SQUID_NONBLOCK) < 0) {
782 debug(50, 0) ("commSetNonBlocking: FD %d: %s\n", fd, xstrerror());
783 return COMM_ERROR;
784 }
785#ifdef _SQUID_CYGWIN_
090089c4 786 }
7f6ffd15 787#endif
58a6c186 788 fd_table[fd].flags.nonblocking = 1;
090089c4 789 return 0;
790}
791
7e3ce7b9 792int
793commUnsetNonBlocking(int fd)
794{
795 int flags;
796 int dummy = 0;
797 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
798 debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
799 return COMM_ERROR;
800 }
801 if (fcntl(fd, F_SETFL, flags & (~SQUID_NONBLOCK)) < 0) {
802 debug(50, 0) ("commUnsetNonBlocking: FD %d: %s\n", fd, xstrerror());
803 return COMM_ERROR;
804 }
805 fd_table[fd].flags.nonblocking = 0;
806 return 0;
807}
808
b8d8561b 809void
810commSetCloseOnExec(int fd)
3ca60c86 811{
812#ifdef FD_CLOEXEC
731e4d49 813 int flags;
7a18b487 814 int dummy = 0;
c7989865 815 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
a3d5953d 816 debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
24382924 817 return;
3ca60c86 818 }
24382924 819 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
a3d5953d 820 debug(50, 0) ("FD %d: set close-on-exec failed: %s\n", fd, xstrerror());
d6827718 821 fd_table[fd].flags.close_on_exec = 1;
3ca60c86 822#endif
823}
824
e90100aa 825#ifdef TCP_NODELAY
826static void
827commSetTcpNoDelay(int fd)
828{
829 int on = 1;
830 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0)
a3d5953d 831 debug(50, 1) ("commSetTcpNoDelay: FD %d: %s\n", fd, xstrerror());
d6827718 832 fd_table[fd].flags.nodelay = 1;
e90100aa 833}
834#endif
835
6a988308 836
d86b3703 837void
0673c0ba 838comm_init(void)
090089c4 839{
f1dc9b30 840 fd_table = xcalloc(Squid_MaxFD, sizeof(fde));
59c4d35b 841 /* XXX account fd_table */
090089c4 842 /* Keep a few file descriptors free so that we don't run out of FD's
843 * after accepting a client but before it opens a socket or a file.
e83892e9 844 * Since Squid_MaxFD can be as high as several thousand, don't waste them */
0254ee29 845 RESERVED_FD = XMIN(100, Squid_MaxFD / 4);
28c60158 846 CBDATA_INIT_TYPE(ConnectStateData);
723123a9 847 comm_write_pool = memPoolCreate("CommWriteStateData", sizeof(CommWriteStateData));
58cd5bbd 848 conn_close_pool = memPoolCreate("close_handler", sizeof(close_handler));
090089c4 849}
850
30a4f2a8 851/* Write to FD. */
b8d8561b 852static void
582b6456 853commHandleWrite(int fd, void *data)
30a4f2a8 854{
f17936ab 855 CommWriteStateData *state = data;
30a4f2a8 856 int len = 0;
857 int nleft;
858
32754419 859 debug(5, 5) ("commHandleWrite: FD %d: off %ld, sz %ld.\n",
860 fd, (long int) state->offset, (long int) state->size);
30a4f2a8 861
862 nleft = state->size - state->offset;
1f7c9178 863 len = FD_WRITE_METHOD(fd, state->buf + state->offset, nleft);
6a988308 864 debug(5, 5) ("commHandleWrite: write() returns %d\n", len);
b69f7771 865 fd_bytes(fd, len, FD_WRITE);
83704487 866 statCounter.syscalls.sock.writes++;
30a4f2a8 867
868 if (len == 0) {
869 /* Note we even call write if nleft == 0 */
870 /* We're done */
871 if (nleft != 0)
02be0294 872 debug(5, 1) ("commHandleWrite: FD %d: write failure: connection closed with %d bytes remaining.\n", fd, nleft);
f17936ab 873 CommWriteStateCallbackAndFree(fd, nleft ? COMM_ERROR : COMM_OK);
30a4f2a8 874 } else if (len < 0) {
875 /* An error */
e8d6569c 876 if (fd_table[fd].flags.socket_eof) {
877 debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n",
878 fd, xstrerror());
879 CommWriteStateCallbackAndFree(fd, COMM_ERROR);
880 } else if (ignoreErrno(errno)) {
a3d5953d 881 debug(50, 10) ("commHandleWrite: FD %d: write failure: %s.\n",
30a4f2a8 882 fd, xstrerror());
b177367b 883 commSetSelect(fd,
30a4f2a8 884 COMM_SELECT_WRITE,
cd1fb0eb 885 commHandleWrite,
b177367b 886 state,
85d7ea98 887 0);
9864ee44 888 } else {
a3d5953d 889 debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n",
9864ee44 890 fd, xstrerror());
f17936ab 891 CommWriteStateCallbackAndFree(fd, COMM_ERROR);
30a4f2a8 892 }
30a4f2a8 893 } else {
894 /* A successful write, continue */
895 state->offset += len;
896 if (state->offset < state->size) {
897 /* Not done, reinstall the write handler and write some more */
b177367b 898 commSetSelect(fd,
30a4f2a8 899 COMM_SELECT_WRITE,
cd1fb0eb 900 commHandleWrite,
b177367b 901 state,
85d7ea98 902 0);
9864ee44 903 } else {
f17936ab 904 CommWriteStateCallbackAndFree(fd, COMM_OK);
30a4f2a8 905 }
30a4f2a8 906 }
907}
908
909
910
7cd8c414 911/*
912 * Queue a write. handler/handler_data are called when the write
913 * completes, on error, or on file descriptor close.
914 *
915 * free_func is used to free the passed buffer when the write has completed.
916 */
b8d8561b 917void
a2c963ae 918comm_write(int fd, const char *buf, int size, CWCB * handler, void *handler_data, FREE * free_func)
30a4f2a8 919{
aa9e2cab 920 CommWriteStateData *state = fd_table[fd].rwstate;
a3d5953d 921 debug(5, 5) ("comm_write: FD %d: sz %d: hndl %p: data %p.\n",
787869c5 922 fd, size, handler, handler_data);
aa9e2cab 923 if (NULL != state) {
afde8a9d 924 debug(5, 1) ("comm_write: fd_table[%d].rwstate != NULL\n", fd);
723123a9 925 memPoolFree(comm_write_pool, state);
6cf028ab 926 fd_table[fd].rwstate = NULL;
927 }
723123a9 928 fd_table[fd].rwstate = state = memPoolAlloc(comm_write_pool);
a2c963ae 929 state->buf = (char *) buf;
30a4f2a8 930 state->size = size;
931 state->offset = 0;
932 state->handler = handler;
fa80a8ef 933 state->handler_data = cbdataReference(handler_data);
c0dec081 934 state->free_func = free_func;
aa9e2cab 935 commSetSelect(fd, COMM_SELECT_WRITE, commHandleWrite, state, 0);
30a4f2a8 936}
26a880e2 937
137ee196 938/* a wrapper around comm_write to allow for MemBuf to be comm_written in a snap */
cb69b4c7 939void
940comm_write_mbuf(int fd, MemBuf mb, CWCB * handler, void *handler_data)
941{
942 comm_write(fd, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb));
943}
944
89924214 945/*
946 * hm, this might be too general-purpose for all the places we'd
947 * like to use it.
948 */
b224ea98 949int
edd2eb63 950ignoreErrno(int ierrno)
26a880e2 951{
603500e7 952 switch (ierrno) {
89924214 953 case EINPROGRESS:
603500e7 954 case EWOULDBLOCK:
26a880e2 955#if EAGAIN != EWOULDBLOCK
603500e7 956 case EAGAIN:
26a880e2 957#endif
603500e7 958 case EALREADY:
959 case EINTR:
db494ab8 960#ifdef ERESTART
961 case ERESTART:
962#endif
26a880e2 963 return 1;
603500e7 964 default:
965 return 0;
966 }
967 /* NOTREACHED */
26a880e2 968}
d723bf6b 969
970void
971commCloseAllSockets(void)
972{
973 int fd;
974 fde *F = NULL;
d723bf6b 975 for (fd = 0; fd <= Biggest_FD; fd++) {
976 F = &fd_table[fd];
60c0b5a2 977 if (!F->flags.open)
d723bf6b 978 continue;
979 if (F->type != FD_SOCKET)
980 continue;
de718ec4 981 if (F->flags.ipc) /* don't close inter-process sockets */
982 continue;
d723bf6b 983 if (F->timeout_handler) {
fa80a8ef 984 PF *callback = F->timeout_handler;
985 void *cbdata = NULL;
986 F->timeout_handler = NULL;
d723bf6b 987 debug(5, 5) ("commCloseAllSockets: FD %d: Calling timeout handler\n",
988 fd);
fa80a8ef 989 if (cbdataReferenceValidDone(F->timeout_data, &cbdata))
990 callback(fd, cbdata);
d723bf6b 991 } else {
992 debug(5, 5) ("commCloseAllSockets: FD %d: calling comm_close()\n", fd);
993 comm_close(fd);
994 }
995 }
996}
1b3db6d9 997
998void
999checkTimeouts(void)
1000{
1001 int fd;
1002 fde *F = NULL;
1003 PF *callback;
1004 for (fd = 0; fd <= Biggest_FD; fd++) {
b5443c04 1005 F = &fd_table[fd];
1006 if (!F->flags.open)
1007 continue;
1008 if (F->timeout == 0)
1009 continue;
1010 if (F->timeout > squid_curtime)
1011 continue;
1012 debug(5, 5) ("checkTimeouts: FD %d Expired\n", fd);
1013 if (F->timeout_handler) {
1014 debug(5, 5) ("checkTimeouts: FD %d: Call timeout handler\n", fd);
1015 callback = F->timeout_handler;
1016 F->timeout_handler = NULL;
1017 callback(fd, F->timeout_data);
1018 } else {
1019 debug(5, 5) ("checkTimeouts: FD %d: Forcing comm_close()\n", fd);
1020 comm_close(fd);
1021 }
1022 }
1023}
1024
1025
1026int
1b3db6d9 1027commDeferRead(int fd)
1028{
1029 fde *F = &fd_table[fd];
1030 if (F->defer_check == NULL)
b5443c04 1031 return 0;
1b3db6d9 1032 return F->defer_check(fd, F->defer_data);
1033}