]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm.cc
update
[thirdparty/squid.git] / src / comm.cc
CommitLineData
da2b3a17 1
1afe05c5 2
30a4f2a8 3/*
8b8ffae4 4 * $Id: comm.cc,v 1.277 1998/07/21 17:34:20 wessels Exp $
30a4f2a8 5 *
6 * DEBUG: section 5 Socket Functions
7 * AUTHOR: Harvest Derived
8 *
42c04c16 9 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 10 * --------------------------------------------------------
11 *
12 * Squid is the result of efforts by numerous individuals from the
13 * Internet community. Development is led by Duane Wessels of the
14 * National Laboratory for Applied Network Research and funded by
15 * the National Science Foundation.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
cbdec147 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30a4f2a8 30 *
31 */
d1f14731 32
30a4f2a8 33/*
34 * Copyright (c) 1994, 1995. All rights reserved.
35 *
36 * The Harvest software was developed by the Internet Research Task
37 * Force Research Group on Resource Discovery (IRTF-RD):
38 *
39 * Mic Bowman of Transarc Corporation.
40 * Peter Danzig of the University of Southern California.
41 * Darren R. Hardy of the University of Colorado at Boulder.
42 * Udi Manber of the University of Arizona.
43 * Michael F. Schwartz of the University of Colorado at Boulder.
44 * Duane Wessels of the University of Colorado at Boulder.
45 *
46 * This copyright notice applies to software in the Harvest
47 * ``src/'' directory only. Users should consult the individual
48 * copyright notices in the ``components/'' subdirectories for
49 * copyright information about other software bundled with the
50 * Harvest source code distribution.
51 *
52 * TERMS OF USE
53 *
54 * The Harvest software may be used and re-distributed without
55 * charge, provided that the software origin and research team are
56 * cited in any use of the system. Most commonly this is
57 * accomplished by including a link to the Harvest Home Page
58 * (http://harvest.cs.colorado.edu/) from the query page of any
59 * Broker you deploy, as well as in the query result pages. These
60 * links are generated automatically by the standard Broker
61 * software distribution.
62 *
63 * The Harvest software is provided ``as is'', without express or
64 * implied warranty, and with no support nor obligation to assist
65 * in its use, correction, modification or enhancement. We assume
66 * no liability with respect to the infringement of copyrights,
67 * trade secrets, or any patents, and are not responsible for
68 * consequential damages. Proper use of the Harvest software is
69 * entirely the responsibility of the user.
70 *
71 * DERIVATIVE WORKS
72 *
73 * Users may make derivative works from the Harvest software, subject
74 * to the following constraints:
75 *
76 * - You must include the above copyright notice and these
77 * accompanying paragraphs in all forms of derivative works,
78 * and any documentation and other materials related to such
79 * distribution and use acknowledge that the software was
80 * developed at the above institutions.
81 *
82 * - You must notify IRTF-RD regarding your distribution of
83 * the derivative work.
84 *
85 * - You must clearly notify users that your are distributing
86 * a modified version and not the original Harvest software.
87 *
88 * - Any derivative product is also subject to these copyright
89 * and use restrictions.
90 *
91 * Note that the Harvest software is NOT in the public domain. We
92 * retain copyright, as specified above.
93 *
94 * HISTORY OF FREE SOFTWARE STATUS
95 *
96 * Originally we required sites to license the software in cases
97 * where they were going to build commercial products/services
98 * around Harvest. In June 1995 we changed this policy. We now
99 * allow people to use the core Harvest software (the code found in
100 * the Harvest ``src/'' directory) for free. We made this change
101 * in the interest of encouraging the widest possible deployment of
102 * the technology. The Harvest software is really a reference
103 * implementation of a set of protocols and formats, some of which
104 * we intend to standardize. We encourage commercial
105 * re-implementations of code complying to this set of standards.
106 */
090089c4 107
44a47c6e 108#include "squid.h"
090089c4 109
30a4f2a8 110#ifdef HAVE_NETINET_TCP_H
111#include <netinet/tcp.h>
112#endif
090089c4 113
52040193 114#if USE_ASYNC_IO
6a988308 115#define MAX_POLL_TIME 10
52040193 116#else
117#define MAX_POLL_TIME 1000
118#endif
119
f88211e8 120typedef struct {
121 char *host;
122 u_short port;
123 struct sockaddr_in S;
124 CNCB *callback;
125 void *data;
f88211e8 126 struct in_addr in_addr;
127 int locks;
03a1ee42 128 int fd;
22c653cd 129 int tries;
130 int addrcount;
131 int connstart;
f88211e8 132} ConnectStateData;
133
090089c4 134/* STATIC */
f5b8bbc4 135static int commBind(int s, struct in_addr, u_short port);
f5b8bbc4 136static void commSetReuseAddr(int);
137static void commSetNoLinger(int);
f5b8bbc4 138static void CommWriteStateCallbackAndFree(int fd, int code);
30a4f2a8 139#ifdef TCP_NODELAY
f5b8bbc4 140static void commSetTcpNoDelay(int);
30a4f2a8 141#endif
f5b8bbc4 142static void commSetTcpRcvbuf(int, int);
f88211e8 143static PF commConnectFree;
03a1ee42 144static PF commConnectHandle;
145static PF commHandleWrite;
edeb28fd 146static IPH commConnectDnsHandle;
f5b8bbc4 147static void commConnectCallback(ConnectStateData * cs, int status);
22c653cd 148static int commResetFD(ConnectStateData * cs);
149static int commRetryConnect(ConnectStateData * cs);
309ad3b6 150
b8d8561b 151static void
f17936ab 152CommWriteStateCallbackAndFree(int fd, int code)
9864ee44 153{
f17936ab 154 CommWriteStateData *CommWriteState = fd_table[fd].rwstate;
155 CWCB *callback = NULL;
1a8f5ed6 156 void *data;
a56a3abe 157 fd_table[fd].rwstate = NULL;
f17936ab 158 if (CommWriteState == NULL)
9864ee44 159 return;
c0dec081 160 if (CommWriteState->free_func) {
161 CommWriteState->free_func(CommWriteState->buf);
f17936ab 162 CommWriteState->buf = NULL;
9864ee44 163 }
f17936ab 164 callback = CommWriteState->handler;
1a8f5ed6 165 data = CommWriteState->handler_data;
f17936ab 166 CommWriteState->handler = NULL;
1a8f5ed6 167 if (callback && cbdataValid(data))
168 callback(fd, CommWriteState->buf, CommWriteState->offset, code, data);
169 cbdataUnlock(data);
f17936ab 170 safe_free(CommWriteState);
9864ee44 171}
172
090089c4 173/* Return the local port associated with fd. */
b8d8561b 174u_short
175comm_local_port(int fd)
090089c4 176{
177 struct sockaddr_in addr;
178 int addr_len = 0;
76f87348 179 fde *F = &fd_table[fd];
090089c4 180
090089c4 181 /* If the fd is closed already, just return */
76f87348 182 if (!F->open) {
a3d5953d 183 debug(5, 0) ("comm_local_port: FD %d has been closed.\n", fd);
30a4f2a8 184 return 0;
090089c4 185 }
76f87348 186 if (F->local_port)
187 return F->local_port;
090089c4 188 addr_len = sizeof(addr);
189 if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {
a3d5953d 190 debug(50, 1) ("comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror());
30a4f2a8 191 return 0;
090089c4 192 }
76f87348 193 F->local_port = ntohs(addr.sin_port);
5f6ac48b 194 debug(5, 6) ("comm_local_port: FD %d: port %d\n", fd, (int) F->local_port);
76f87348 195 return F->local_port;
090089c4 196}
197
b8d8561b 198static int
199commBind(int s, struct in_addr in_addr, u_short port)
090089c4 200{
201 struct sockaddr_in S;
090089c4 202
090089c4 203 memset(&S, '\0', sizeof(S));
204 S.sin_family = AF_INET;
205 S.sin_port = htons(port);
30a4f2a8 206 S.sin_addr = in_addr;
090089c4 207 if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0)
208 return COMM_OK;
a3d5953d 209 debug(50, 0) ("commBind: Cannot bind socket FD %d to %s:%d: %s\n",
090089c4 210 s,
30a4f2a8 211 S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr),
44a62238 212 (int) port,
213 xstrerror());
090089c4 214 return COMM_ERROR;
215}
216
217/* Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE
218 * is OR of flags specified in comm.h. */
b8d8561b 219int
16b204c4 220comm_open(int sock_type,
cc6a9d2e 221 int proto,
222 struct in_addr addr,
223 u_short port,
224 int flags,
0ee4272b 225 const char *note)
090089c4 226{
227 int new_socket;
76f87348 228 fde *F = NULL;
b6f794d6 229 int tcp_rcv_bufsz = Config.tcpRcvBufsz;
090089c4 230
231 /* Create socket for accepting new connections. */
16b204c4 232 if ((new_socket = socket(AF_INET, sock_type, proto)) < 0) {
090089c4 233 /* Increase the number of reserved fd's if calls to socket()
234 * are failing because the open file table is full. This
235 * limits the number of simultaneous clients */
236 switch (errno) {
237 case ENFILE:
238 case EMFILE:
a3d5953d 239 debug(50, 1) ("comm_open: socket failure: %s\n", xstrerror());
090089c4 240 break;
241 default:
a3d5953d 242 debug(50, 0) ("comm_open: socket failure: %s\n", xstrerror());
090089c4 243 }
4b23e114 244 fdAdjustReserved();
603a02fd 245 return -1;
090089c4 246 }
247 /* update fdstat */
365e5b34 248 debug(5, 5) ("comm_open: FD %d is a new socket\n", new_socket);
5c5783a2 249 fd_open(new_socket, FD_SOCKET, note);
76f87348 250 F = &fd_table[new_socket];
79a15e0a 251 if (!(flags & COMM_NOCLOEXEC))
3ca60c86 252 commSetCloseOnExec(new_socket);
cdc33f35 253 if ((flags & COMM_REUSEADDR))
254 commSetReuseAddr(new_socket);
7690e8eb 255 if (port > (u_short) 0) {
30a4f2a8 256 commSetNoLinger(new_socket);
3b4be6a6 257 if (opt_reuseaddr)
090089c4 258 commSetReuseAddr(new_socket);
090089c4 259 }
a3724d50 260 if (addr.s_addr != no_addr.s_addr) {
261 if (commBind(new_socket, addr, port) != COMM_OK) {
262 comm_close(new_socket);
603a02fd 263 return -1;
a3724d50 264 }
23ff6968 265 }
76f87348 266 F->local_port = port;
090089c4 267
79a15e0a 268 if (flags & COMM_NONBLOCKING)
30a4f2a8 269 if (commSetNonBlocking(new_socket) == COMM_ERROR)
603a02fd 270 return -1;
30a4f2a8 271#ifdef TCP_NODELAY
272 if (sock_type == SOCK_STREAM)
273 commSetTcpNoDelay(new_socket);
274#endif
f868539a 275 if (tcp_rcv_bufsz > 0 && sock_type == SOCK_STREAM)
276 commSetTcpRcvbuf(new_socket, tcp_rcv_bufsz);
090089c4 277 return new_socket;
278}
279
b4ea1f2e 280/*
281 * NOTE: set the listen queue to Squid_MaxFD/4 and rely on the kernel to
282 * impose an upper limit. Solaris' listen(3n) page says it has
283 * no limit on this parameter, but sys/socket.h sets SOMAXCONN
284 * to 5. HP-UX currently has a limit of 20. SunOS is 5 and
285 * OSF 3.0 is 8.
286 */
b8d8561b 287int
288comm_listen(int sock)
090089c4 289{
290 int x;
e83892e9 291 if ((x = listen(sock, Squid_MaxFD >> 2)) < 0) {
a3d5953d 292 debug(50, 0) ("comm_listen: listen(%d, %d): %s\n",
e83892e9 293 Squid_MaxFD >> 2,
090089c4 294 sock, xstrerror());
295 return x;
296 }
297 return sock;
298}
299
e5f6c5c2 300void
4f92c80c 301commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data)
e924600d 302{
303 ConnectStateData *cs = xcalloc(1, sizeof(ConnectStateData));
6a988308 304 debug(5, 3) ("commConnectStart: FD %d, %s:%d\n", fd, host, (int) port);
3f6c0fb2 305 cbdataAdd(cs, MEM_NONE);
03a1ee42 306 cs->fd = fd;
e924600d 307 cs->host = xstrdup(host);
308 cs->port = port;
309 cs->callback = callback;
310 cs->data = data;
b716a8ad 311 cbdataLock(cs->data);
e924600d 312 comm_add_close_handler(fd, commConnectFree, cs);
f88211e8 313 cs->locks++;
8407afee 314 ipcache_nbgethostbyname(host, commConnectDnsHandle, cs);
edeb28fd 315}
316
317static void
03a1ee42 318commConnectDnsHandle(const ipcache_addrs * ia, void *data)
edeb28fd 319{
320 ConnectStateData *cs = data;
f88211e8 321 assert(cs->locks == 1);
322 cs->locks--;
edeb28fd 323 if (ia == NULL) {
a3d5953d 324 debug(5, 3) ("commConnectDnsHandle: Unknown host: %s\n", cs->host);
6cf028ab 325 if (!dns_error_message) {
326 dns_error_message = "Unknown DNS error";
0e473d70 327 debug(5, 1) ("commConnectDnsHandle: Bad dns_error_message\n");
6cf028ab 328 }
a64c2869 329 assert(dns_error_message != NULL);
03a1ee42 330 commConnectCallback(cs, COMM_ERR_DNS);
edeb28fd 331 return;
332 }
f076b37b 333 assert(ia->cur < ia->count);
edeb28fd 334 cs->in_addr = ia->in_addrs[ia->cur];
52926044 335 ipcacheCycleAddr(cs->host, NULL);
22c653cd 336 cs->addrcount = ia->count;
337 cs->connstart = squid_curtime;
03a1ee42 338 commConnectHandle(cs->fd, cs);
e924600d 339}
340
f88211e8 341static void
03a1ee42 342commConnectCallback(ConnectStateData * cs, int status)
f88211e8 343{
a3d5953d 344 CNCB *callback = cs->callback;
345 void *data = cs->data;
03a1ee42 346 int fd = cs->fd;
a3d5953d 347 comm_remove_close_handler(fd, commConnectFree, cs);
9daca08e 348 cs->callback = NULL;
349 cs->data = NULL;
e1b16349 350 commSetTimeout(fd, -1, NULL, NULL);
a3d5953d 351 commConnectFree(fd, cs);
8407afee 352 if (cbdataValid(data))
365e5b34 353 callback(fd, status, data);
1790d392 354 cbdataUnlock(data);
f88211e8 355}
356
e924600d 357static void
9daca08e 358commConnectFree(int fd, void *data)
e924600d 359{
360 ConnectStateData *cs = data;
9daca08e 361 debug(5, 3) ("commConnectFree: FD %d\n", fd);
8407afee 362 if (cs->locks)
365e5b34 363 ipcacheUnregister(cs->host, cs);
9daca08e 364 if (cs->data)
365 cbdataUnlock(cs->data);
8407afee 366 safe_free(cs->host);
367 cbdataFree(cs);
e924600d 368}
369
22c653cd 370/* Reset FD so that we can connect() again */
edeb28fd 371static int
22c653cd 372commResetFD(ConnectStateData * cs)
edeb28fd 373{
374 int fd2;
7dd44885 375 if (!cbdataValid(cs->data))
376 return 0;
edeb28fd 377 fd2 = socket(AF_INET, SOCK_STREAM, 0);
378 if (fd2 < 0) {
22c653cd 379 debug(5, 0) ("commResetFD: socket: %s\n", xstrerror());
4b23e114 380 fdAdjustReserved();
edeb28fd 381 return 0;
382 }
22c653cd 383 if (dup2(fd2, cs->fd) < 0) {
384 debug(5, 0) ("commResetFD: dup2: %s\n", xstrerror());
4b23e114 385 fdAdjustReserved();
edeb28fd 386 return 0;
387 }
edeb28fd 388 close(fd2);
22c653cd 389 commSetNonBlocking(cs->fd);
edeb28fd 390 return 1;
391}
392
22c653cd 393static int
394commRetryConnect(ConnectStateData * cs)
395{
396 assert(cs->addrcount > 0);
397 if (cs->addrcount == 1) {
398 if (cs->tries >= Config.retry.maxtries)
399 return 0;
400 if (squid_curtime - cs->connstart > Config.Timeout.connect)
401 return 0;
22c653cd 402 } else {
403 if (cs->tries > cs->addrcount)
404 return 0;
405 }
406 return commResetFD(cs);
407}
408
e924600d 409/* Connect SOCK to specified DEST_PORT at DEST_HOST. */
410static void
411commConnectHandle(int fd, void *data)
090089c4 412{
f88211e8 413 ConnectStateData *cs = data;
414 if (cs->S.sin_addr.s_addr == 0) {
415 cs->S.sin_family = AF_INET;
416 cs->S.sin_addr = cs->in_addr;
417 cs->S.sin_port = htons(cs->port);
17a0a4ee 418 if (Config.onoff.log_fqdn)
f88211e8 419 fqdncache_gethostbyaddr(cs->S.sin_addr, FQDN_LOOKUP_IF_MISS);
e5f6c5c2 420 }
f88211e8 421 switch (comm_connect_addr(fd, &cs->S)) {
e5f6c5c2 422 case COMM_INPROGRESS:
11994bb9 423 debug(5, 5) ("commConnectHandle: FD %d: COMM_INPROGRESS\n", fd);
f88211e8 424 commSetSelect(fd, COMM_SELECT_WRITE, commConnectHandle, cs, 0);
e5f6c5c2 425 break;
426 case COMM_OK:
22c653cd 427 ipcacheMarkGoodAddr(cs->host, cs->S.sin_addr);
03a1ee42 428 commConnectCallback(cs, COMM_OK);
e5f6c5c2 429 break;
430 default:
22c653cd 431 cs->tries++;
432 ipcacheMarkBadAddr(cs->host, cs->S.sin_addr);
194dd3b8 433 if (Config.onoff.test_reachability)
434 netdbDeleteAddrNetwork(cs->S.sin_addr);
22c653cd 435 if (commRetryConnect(cs)) {
f88211e8 436 cs->locks++;
8407afee 437 ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs);
edeb28fd 438 } else {
03a1ee42 439 commConnectCallback(cs, COMM_ERR_CONNECT);
edeb28fd 440 }
e5f6c5c2 441 break;
090089c4 442 }
090089c4 443}
22c653cd 444
b8d8561b 445int
4f92c80c 446commSetTimeout(int fd, int timeout, PF * handler, void *data)
090089c4 447{
76f87348 448 fde *F;
a3d5953d 449 debug(5, 3) ("commSetTimeout: FD %d timeout %d\n", fd, timeout);
03eb2f01 450 assert(fd >= 0);
451 assert(fd < Squid_MaxFD);
76f87348 452 F = &fd_table[fd];
0ca54a51 453 assert(F->open);
5c5783a2 454 if (timeout < 0) {
76f87348 455 F->timeout_handler = NULL;
456 F->timeout_data = NULL;
457 return F->timeout = 0;
5c5783a2 458 }
2681d383 459 if (shutting_down) {
4f92c80c 460 /* don't increase the timeout if something pending */
76f87348 461 if (F->timeout > 0 && (int) (F->timeout - squid_curtime) < timeout)
462 return F->timeout;
5c5783a2 463 }
76f87348 464 assert(handler || F->timeout_handler);
5c5783a2 465 if (handler || data) {
76f87348 466 F->timeout_handler = handler;
467 F->timeout_data = data;
30a4f2a8 468 }
76f87348 469 return F->timeout = squid_curtime + (time_t) timeout;
090089c4 470}
471
b8d8561b 472int
0ee4272b 473comm_connect_addr(int sock, const struct sockaddr_in *address)
090089c4 474{
475 int status = COMM_OK;
76f87348 476 fde *F = &fd_table[sock];
090089c4 477 int len;
478 int x;
489b22c1 479 assert(ntohs(address->sin_port) != 0);
090089c4 480 /* Establish connection. */
086bce16 481 if (connect(sock, (struct sockaddr *) address, sizeof(struct sockaddr_in)) < 0) {
365e5b34 482 debug(5, 9) ("connect FD %d: %s\n", sock, xstrerror());
f81b2bec 483#ifdef _SQUID_HPUX_
603500e7 484 if (EALREADY == errno) {
f81b2bec 485 /*
486 * On my HP-UX box (HP-UX tirana B.10.10 A 9000/851),
487 * we get into fast loops on EALREADY. select(2) continually
488 * says the FD is ready for writing, but connect always
489 * returns EALREADY. I applied a patch (PHNE_12906) but
164f7660 490 * it didn't help. -DW Dec 1, 1997
f81b2bec 491 */
492 debug(50, 1) ("connect: %s:%d: %s.\n",
493 fqdnFromAddr(address->sin_addr),
494 ntohs(address->sin_port),
495 xstrerror());
496 return COMM_ERROR;
603500e7 497 } else
30a4f2a8 498#endif
603500e7 499 if (ignoreErrno(errno)) {
e5f6c5c2 500 status = COMM_INPROGRESS;
603500e7 501 } else if (EISCONN == errno) {
090089c4 502 status = COMM_OK;
603500e7 503 } else {
504 if (EINVAL == errno) {
505 len = sizeof(x);
506 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &x, &len) >= 0)
507 errno = x;
508 }
a3d5953d 509 debug(50, 2) ("connect: %s:%d: %s.\n",
28ab0c0a 510 fqdnFromAddr(address->sin_addr),
090089c4 511 ntohs(address->sin_port),
512 xstrerror());
513 return COMM_ERROR;
514 }
e5f6c5c2 515 }
76f87348 516 xstrncpy(F->ipaddr, inet_ntoa(address->sin_addr), 16);
517 F->remote_port = ntohs(address->sin_port);
090089c4 518 if (status == COMM_OK) {
a3d5953d 519 debug(5, 10) ("comm_connect_addr: FD %d connected to %s:%d\n",
76f87348 520 sock, F->ipaddr, F->remote_port);
f21cd581 521 } else if (status == COMM_INPROGRESS) {
a3d5953d 522 debug(5, 10) ("comm_connect_addr: FD %d connection pending\n", sock);
090089c4 523 }
524 /* Add new socket to list of open sockets. */
090089c4 525 return status;
526}
527
528/* Wait for an incoming connection on FD. FD should be a socket returned
529 * from comm_listen. */
b8d8561b 530int
531comm_accept(int fd, struct sockaddr_in *peer, struct sockaddr_in *me)
090089c4 532{
533 int sock;
1f9afe33 534 struct sockaddr_in P;
535 struct sockaddr_in M;
090089c4 536 int Slen;
76f87348 537 fde *F = NULL;
1f9afe33 538 Slen = sizeof(P);
603500e7 539 if ((sock = accept(fd, (struct sockaddr *) &P, &Slen)) < 0) {
540 if (ignoreErrno(errno)) {
541 debug(50, 5) ("comm_accept: FD %d: %s\n", fd, xstrerror());
0a0bf5db 542 return COMM_NOMESSAGE;
603500e7 543 } else if (ENFILE == errno || EMFILE == errno) {
544 debug(50, 3) ("comm_accept: FD %d: %s\n", fd, xstrerror());
090089c4 545 return COMM_ERROR;
603500e7 546 } else {
547 debug(50, 1) ("comm_accept: FD %d: %s\n", fd, xstrerror());
090089c4 548 return COMM_ERROR;
549 }
550 }
090089c4 551 if (peer)
1f9afe33 552 *peer = P;
4053a845 553 Slen = sizeof(M);
554 memset(&M, '\0', Slen);
555 getsockname(sock, (struct sockaddr *) &M, &Slen);
556 if (me)
1f9afe33 557 *me = M;
3ca60c86 558 commSetCloseOnExec(sock);
090089c4 559 /* fdstat update */
5c5783a2 560 fd_open(sock, FD_SOCKET, "HTTP Request");
76f87348 561 F = &fd_table[sock];
c0dec081 562 xstrncpy(F->ipaddr, inet_ntoa(P.sin_addr), 16);
76f87348 563 F->remote_port = htons(P.sin_port);
564 F->local_port = htons(M.sin_port);
090089c4 565 commSetNonBlocking(sock);
090089c4 566 return sock;
567}
568
cb201b7e 569void
570commCallCloseHandlers(int fd)
571{
76f87348 572 fde *F = &fd_table[fd];
f1dc9b30 573 close_handler *ch;
a3d5953d 574 debug(5, 5) ("commCallCloseHandlers: FD %d\n", fd);
76f87348 575 while ((ch = F->close_handler) != NULL) {
576 F->close_handler = ch->next;
9daca08e 577 debug(5, 5) ("commCallCloseHandlers: ch->handler=%p\n", ch->handler);
603a02fd 578 if (cbdataValid(ch->data))
579 ch->handler(fd, ch->data);
580 cbdataUnlock(ch->data);
cb201b7e 581 safe_free(ch);
582 }
583}
584
5492ad1d 585#if LINGERING_CLOSE
586static void
587commLingerClose(int fd, void *unused)
588{
589 LOCAL_ARRAY(char, buf, 1024);
590 int n;
591 n = read(fd, buf, 1024);
592 if (n < 0)
593 debug(5, 3) ("commLingerClose: FD %d read: %s\n", fd, xstrerror());
594 comm_close(fd);
595}
596
597static void
598commLingerTimeout(int fd, void *unused)
599{
600 debug(5, 3) ("commLingerTimeout: FD %d\n", fd);
601 comm_close(fd);
602}
603
604/*
605 * Inspired by apache
606 */
607void
608comm_lingering_close(int fd)
609{
610 if (shutdown(fd, 1) < 0) {
611 comm_close(fd);
612 return;
613 }
614 fd_note(fd, "lingering close");
615 commSetTimeout(fd, 10, commLingerTimeout, NULL);
616 commSetSelect(fd, COMM_SELECT_READ, commLingerClose, NULL, 0);
617}
618#endif
619
b8d8561b 620void
621comm_close(int fd)
090089c4 622{
76f87348 623 fde *F = NULL;
6444c441 624#if USE_ASYNC_IO
c41fa515 625 int doaioclose = 1;
6444c441 626#endif
a3d5953d 627 debug(5, 5) ("comm_close: FD %d\n", fd);
03eb2f01 628 assert(fd >= 0);
629 assert(fd < Squid_MaxFD);
76f87348 630 F = &fd_table[fd];
58a6c186 631 if (F->flags.closing)
e102ebda 632 return;
2681d383 633 if (shutting_down && (!F->open || F->type == FD_FILE))
6cf028ab 634 return;
b716a8ad 635 assert(F->open);
76f87348 636 assert(F->type != FD_FILE);
c41fa515 637#ifdef USE_ASYNC_IO
58a6c186 638 if (F->flags.nolinger && F->flags.nonblocking)
639 doaioclose = 0;
c41fa515 640#endif
58a6c186 641 F->flags.closing = 1;
96f1be5d 642 CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING);
cb201b7e 643 commCallCloseHandlers(fd);
b716a8ad 644 if (F->uses) /* assume persistent connect count */
645 pconnHistCount(1, F->uses);
5c5783a2 646 fd_close(fd); /* update fdstat */
5874bf28 647#if defined(_SQUID_LINUX_)
648 /*
649 * michael@metal.iinet.net.au sez close() on
650 * network sockets never blocks.
651 */
652 close(fd);
56ed7af5 653#elif USE_ASYNC_IO
c41fa515 654 if (doaioclose)
6444c441 655 aioClose(fd);
6444c441 656 else
c41fa515 657 close(fd);
0a0bf5db 658#else
9864ee44 659 close(fd);
0a0bf5db 660#endif
090089c4 661}
662
090089c4 663/* Send a udp datagram to specified TO_ADDR. */
b8d8561b 664int
5df61230 665comm_udp_sendto(int fd,
666 const struct sockaddr_in *to_addr,
667 int addr_len,
17b6e784 668 const void *buf,
5df61230 669 int len)
090089c4 670{
5df61230 671 int x;
672 x = sendto(fd, buf, len, 0, (struct sockaddr *) to_addr, addr_len);
673 if (x < 0) {
17d51783 674#ifdef _SQUID_LINUX_
675 if (ECONNREFUSED != errno)
676#endif
677 debug(50, 1) ("comm_udp_sendto: FD %d, %s, port %d: %s\n",
678 fd,
679 inet_ntoa(to_addr->sin_addr),
680 (int) htons(to_addr->sin_port),
681 xstrerror());
090089c4 682 return COMM_ERROR;
683 }
5df61230 684 return x;
090089c4 685}
686
b8d8561b 687void
70a9dab4 688commSetDefer(int fd, DEFER * func, void *data)
4883993a 689{
da2b3a17 690 fde *F = &fd_table[fd];
691 F->defer_check = func;
70a9dab4 692 F->defer_data = data;
4883993a 693}
694
b8d8561b 695void
582b6456 696commSetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout)
090089c4 697{
89de058c 698 fde *F = &fd_table[fd];
489b22c1 699 assert(fd >= 0);
89de058c 700 assert(F->open == FD_OPEN);
cebe3f19 701 debug(5, 5) ("commSetSelect: FD %d type %d\n", fd, type);
090089c4 702 if (type & COMM_SELECT_READ) {
76f87348 703 F->read_handler = handler;
704 F->read_data = client_data;
090089c4 705 }
706 if (type & COMM_SELECT_WRITE) {
76f87348 707 F->write_handler = handler;
708 F->write_data = client_data;
090089c4 709 }
5c5783a2 710 if (timeout)
76f87348 711 F->timeout = squid_curtime + timeout;
090089c4 712}
713
b8d8561b 714void
582b6456 715comm_add_close_handler(int fd, PF * handler, void *data)
30a4f2a8 716{
f1dc9b30 717 close_handler *new = xmalloc(sizeof(*new));
cddc721b 718 close_handler *c;
a3d5953d 719 debug(5, 5) ("comm_add_close_handler: FD %d, handler=%p, data=%p\n",
e0c42e90 720 fd, handler, data);
6a988308 721 for (c = fd_table[fd].close_handler; c; c = c->next)
aeca2a09 722 assert(c->handler != handler || c->data != data);
30a4f2a8 723 new->handler = handler;
724 new->data = data;
725 new->next = fd_table[fd].close_handler;
726 fd_table[fd].close_handler = new;
603a02fd 727 cbdataLock(data);
30a4f2a8 728}
729
b8d8561b 730void
582b6456 731comm_remove_close_handler(int fd, PF * handler, void *data)
090089c4 732{
f1dc9b30 733 close_handler *p;
734 close_handler *last = NULL;
30a4f2a8 735 /* Find handler in list */
e869f2bd 736 debug(5, 5) ("comm_remove_close_handler: FD %d, handler=%p, data=%p\n",
737 fd, handler, data);
30a4f2a8 738 for (p = fd_table[fd].close_handler; p != NULL; last = p, p = p->next)
739 if (p->handler == handler && p->data == data)
740 break; /* This is our handler */
f88211e8 741 assert(p != NULL);
30a4f2a8 742 /* Remove list entry */
743 if (last)
744 last->next = p->next;
745 else
746 fd_table[fd].close_handler = p->next;
1390960a 747 cbdataUnlock(p->data);
30a4f2a8 748 safe_free(p);
749}
090089c4 750
b8d8561b 751static void
752commSetNoLinger(int fd)
30a4f2a8 753{
754 struct linger L;
090089c4 755 L.l_onoff = 0; /* off */
756 L.l_linger = 0;
30a4f2a8 757 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
a3d5953d 758 debug(50, 0) ("commSetNoLinger: FD %d: %s\n", fd, xstrerror());
58a6c186 759 fd_table[fd].flags.nolinger = 1;
090089c4 760}
761
b8d8561b 762static void
763commSetReuseAddr(int fd)
090089c4 764{
765 int on = 1;
30a4f2a8 766 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0)
a3d5953d 767 debug(50, 1) ("commSetReuseAddr: FD %d: %s\n", fd, xstrerror());
090089c4 768}
769
b8d8561b 770static void
771commSetTcpRcvbuf(int fd, int size)
f868539a 772{
773 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0)
a3d5953d 774 debug(50, 1) ("commSetTcpRcvbuf: FD %d, SIZE %d: %s\n",
b6f794d6 775 fd, size, xstrerror());
f868539a 776}
777
b8d8561b 778int
779commSetNonBlocking(int fd)
30a4f2a8 780{
731e4d49 781 int flags;
9e205701 782 int dummy = 0;
95cf2361 783 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
a3d5953d 784 debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
731e4d49 785 return COMM_ERROR;
786 }
4f92c80c 787 if (fcntl(fd, F_SETFL, flags | SQUID_NONBLOCK) < 0) {
a3d5953d 788 debug(50, 0) ("commSetNonBlocking: FD %d: %s\n", fd, xstrerror());
30a4f2a8 789 return COMM_ERROR;
090089c4 790 }
58a6c186 791 fd_table[fd].flags.nonblocking = 1;
090089c4 792 return 0;
793}
794
b8d8561b 795void
796commSetCloseOnExec(int fd)
3ca60c86 797{
798#ifdef FD_CLOEXEC
731e4d49 799 int flags;
7a18b487 800 int dummy = 0;
c7989865 801 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
a3d5953d 802 debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
24382924 803 return;
3ca60c86 804 }
24382924 805 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
a3d5953d 806 debug(50, 0) ("FD %d: set close-on-exec failed: %s\n", fd, xstrerror());
3ca60c86 807#endif
808}
809
e90100aa 810#ifdef TCP_NODELAY
811static void
812commSetTcpNoDelay(int fd)
813{
814 int on = 1;
815 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0)
a3d5953d 816 debug(50, 1) ("commSetTcpNoDelay: FD %d: %s\n", fd, xstrerror());
e90100aa 817}
818#endif
819
6a988308 820
d86b3703 821void
0673c0ba 822comm_init(void)
090089c4 823{
f1dc9b30 824 fd_table = xcalloc(Squid_MaxFD, sizeof(fde));
59c4d35b 825 /* XXX account fd_table */
090089c4 826 /* Keep a few file descriptors free so that we don't run out of FD's
827 * after accepting a client but before it opens a socket or a file.
e83892e9 828 * Since Squid_MaxFD can be as high as several thousand, don't waste them */
0254ee29 829 RESERVED_FD = XMIN(100, Squid_MaxFD / 4);
090089c4 830}
831
30a4f2a8 832/* Write to FD. */
b8d8561b 833static void
582b6456 834commHandleWrite(int fd, void *data)
30a4f2a8 835{
f17936ab 836 CommWriteStateData *state = data;
30a4f2a8 837 int len = 0;
838 int nleft;
839
cebe3f19 840 debug(5, 5) ("commHandleWrite: FD %d: off %d, sz %d.\n",
5f6ac48b 841 fd, (int) state->offset, state->size);
30a4f2a8 842
843 nleft = state->size - state->offset;
844 len = write(fd, state->buf + state->offset, nleft);
6a988308 845 debug(5, 5) ("commHandleWrite: write() returns %d\n", len);
b69f7771 846 fd_bytes(fd, len, FD_WRITE);
30a4f2a8 847
848 if (len == 0) {
849 /* Note we even call write if nleft == 0 */
850 /* We're done */
851 if (nleft != 0)
02be0294 852 debug(5, 1) ("commHandleWrite: FD %d: write failure: connection closed with %d bytes remaining.\n", fd, nleft);
f17936ab 853 CommWriteStateCallbackAndFree(fd, nleft ? COMM_ERROR : COMM_OK);
30a4f2a8 854 } else if (len < 0) {
855 /* An error */
26a880e2 856 if (ignoreErrno(errno)) {
a3d5953d 857 debug(50, 10) ("commHandleWrite: FD %d: write failure: %s.\n",
30a4f2a8 858 fd, xstrerror());
b177367b 859 commSetSelect(fd,
30a4f2a8 860 COMM_SELECT_WRITE,
cd1fb0eb 861 commHandleWrite,
b177367b 862 state,
85d7ea98 863 0);
9864ee44 864 } else {
a3d5953d 865 debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n",
9864ee44 866 fd, xstrerror());
f17936ab 867 CommWriteStateCallbackAndFree(fd, COMM_ERROR);
30a4f2a8 868 }
30a4f2a8 869 } else {
870 /* A successful write, continue */
871 state->offset += len;
872 if (state->offset < state->size) {
873 /* Not done, reinstall the write handler and write some more */
b177367b 874 commSetSelect(fd,
30a4f2a8 875 COMM_SELECT_WRITE,
cd1fb0eb 876 commHandleWrite,
b177367b 877 state,
85d7ea98 878 0);
9864ee44 879 } else {
f17936ab 880 CommWriteStateCallbackAndFree(fd, COMM_OK);
30a4f2a8 881 }
30a4f2a8 882 }
883}
884
885
886
887/* Select for Writing on FD, until SIZE bytes are sent. Call
888 * * HANDLER when complete. */
b8d8561b 889void
9e4ad609 890comm_write(int fd, char *buf, int size, CWCB * handler, void *handler_data, FREE * free_func)
30a4f2a8 891{
aa9e2cab 892 CommWriteStateData *state = fd_table[fd].rwstate;
a3d5953d 893 debug(5, 5) ("comm_write: FD %d: sz %d: hndl %p: data %p.\n",
787869c5 894 fd, size, handler, handler_data);
aa9e2cab 895 if (NULL != state) {
afde8a9d 896 debug(5, 1) ("comm_write: fd_table[%d].rwstate != NULL\n", fd);
aa9e2cab 897 safe_free(state);
6cf028ab 898 fd_table[fd].rwstate = NULL;
899 }
aa9e2cab 900 assert(state == NULL);
901 fd_table[fd].rwstate = state = xcalloc(1, sizeof(CommWriteStateData));
30a4f2a8 902 state->buf = buf;
903 state->size = size;
904 state->offset = 0;
905 state->handler = handler;
30a4f2a8 906 state->handler_data = handler_data;
c0dec081 907 state->free_func = free_func;
1a8f5ed6 908 cbdataLock(handler_data);
aa9e2cab 909 commSetSelect(fd, COMM_SELECT_WRITE, commHandleWrite, state, 0);
30a4f2a8 910}
26a880e2 911
137ee196 912/* a wrapper around comm_write to allow for MemBuf to be comm_written in a snap */
cb69b4c7 913void
914comm_write_mbuf(int fd, MemBuf mb, CWCB * handler, void *handler_data)
915{
916 comm_write(fd, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb));
917}
918
89924214 919/*
920 * hm, this might be too general-purpose for all the places we'd
921 * like to use it.
922 */
b224ea98 923int
edd2eb63 924ignoreErrno(int ierrno)
26a880e2 925{
603500e7 926 switch (ierrno) {
89924214 927 case EINPROGRESS:
603500e7 928 case EWOULDBLOCK:
26a880e2 929#if EAGAIN != EWOULDBLOCK
603500e7 930 case EAGAIN:
26a880e2 931#endif
603500e7 932 case EALREADY:
933 case EINTR:
db494ab8 934#ifdef ERESTART
935 case ERESTART:
936#endif
26a880e2 937 return 1;
603500e7 938 default:
939 return 0;
940 }
941 /* NOTREACHED */
26a880e2 942}