]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm.cc
bug #2578: squid fails to resume dowload from FTP
[thirdparty/squid.git] / src / comm.cc
CommitLineData
30a4f2a8 1/*
30a4f2a8 2 * DEBUG: section 5 Socket Functions
3 * AUTHOR: Harvest Derived
4 *
2b6662ba 5 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 6 * ----------------------------------------------------------
30a4f2a8 7 *
2b6662ba 8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
30a4f2a8 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.
9e008dda 21 *
30a4f2a8 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.
9e008dda 26 *
30a4f2a8 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.
e25c139f 30 *
2d8c0b1a 31 *
32 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
30a4f2a8 33 */
090089c4 34
44a47c6e 35#include "squid.h"
c4b7a5a9 36#include "StoreIOBuffer.h"
37#include "comm.h"
a553a5a3 38#include "event.h"
528b2c61 39#include "fde.h"
56410c89 40#include "CommIO.h"
a553a5a3 41#include "CommRead.h"
ee0989f2 42#include "ConnectionDetail.h"
0eb49b6d 43#include "MemBuf.h"
781ce8ff 44#include "pconn.h"
985c86bc 45#include "SquidTime.h"
b0469965 46#include "CommCalls.h"
74257126 47#include "DescriptorSet.h"
9b5c4a9a 48#include "icmp/net_db.h"
9837e5f0 49#include "ip/IpAddress.h"
85944c1c 50#include "ip/IpIntercept.h"
090089c4 51
b671cc68 52#if defined(_SQUID_CYGWIN_)
53#include <sys/ioctl.h>
54#endif
30a4f2a8 55#ifdef HAVE_NETINET_TCP_H
56#include <netinet/tcp.h>
57#endif
090089c4 58
2b663917 59/*
60 * New C-like simple comm code. This stuff is a mess and doesn't really buy us anything.
61 */
62
63typedef enum {
9e008dda
AJ
64 IOCB_NONE,
65 IOCB_READ,
66 IOCB_WRITE
2b663917 67} iocb_type;
68
82ec8dfc
AR
69static void commStopHalfClosedMonitor(int fd);
70static IOCB commHalfClosedReader;
71
72
b0469965 73struct comm_io_callback_t {
9e008dda
AJ
74 iocb_type type;
75 int fd;
76 AsyncCall::Pointer callback;
77 char *buf;
78 FREE *freefunc;
79 int size;
80 int offset;
81 comm_err_t errcode;
82 int xerrno;
83
84 bool active() const { return callback != NULL; }
2b663917 85};
2b663917 86
87struct _comm_fd {
9e008dda
AJ
88 int fd;
89 comm_io_callback_t readcb;
90 comm_io_callback_t writecb;
2b663917 91};
92typedef struct _comm_fd comm_fd_t;
93comm_fd_t *commfd_table;
94
b0469965 95// TODO: make this a comm_io_callback_t method?
2b663917 96bool
97commio_has_callback(int fd, iocb_type type, comm_io_callback_t *ccb)
98{
9e008dda
AJ
99 assert(ccb->fd == fd);
100 assert(ccb->type == type);
101 return ccb->active();
2b663917 102}
103
104/*
b0469965 105 * Configure comm_io_callback_t for I/O
2b663917 106 *
107 * @param fd filedescriptor
108 * @param ccb comm io callback
109 * @param cb callback
110 * @param cbdata callback data (must be cbdata'ed)
111 * @param buf buffer, if applicable
112 * @param freefunc freefunc, if applicable
113 * @param size buffer size
114 */
b0469965 115static void
116commio_set_callback(int fd, iocb_type type, comm_io_callback_t *ccb,
9e008dda 117 AsyncCall::Pointer &cb, char *buf, FREE *freefunc, int size)
2b663917 118{
9e008dda
AJ
119 assert(!ccb->active());
120 assert(ccb->type == type);
121 assert(cb != NULL);
122 ccb->fd = fd;
123 ccb->callback = cb;
124 ccb->buf = buf;
125 ccb->freefunc = freefunc;
126 ccb->size = size;
127 ccb->offset = 0;
2b663917 128}
129
130
b0469965 131// Schedule the callback call and clear the callback
132static void
133commio_finish_callback(int fd, comm_io_callback_t *ccb, comm_err_t code, int xerrno)
2b663917 134{
b0469965 135 debugs(5, 3, "commio_finish_callback: called for FD " << fd << " (" <<
9e008dda
AJ
136 code << ", " << xerrno << ")");
137 assert(ccb->active());
138 assert(ccb->fd == fd);
139 ccb->errcode = code;
140 ccb->xerrno = xerrno;
141
142 comm_io_callback_t cb = *ccb;
143
144 /* We've got a copy; blow away the real one */
145 /* XXX duplicate code from commio_cancel_callback! */
146 ccb->xerrno = 0;
147 ccb->callback = NULL; // cb has it
148
149 /* free data */
150 if (cb.freefunc) {
151 cb.freefunc(cb.buf);
152 cb.buf = NULL;
153 }
154
155 if (cb.callback != NULL) {
b0469965 156 typedef CommIoCbParams Params;
157 Params &params = GetCommParams<Params>(cb.callback);
158 params.fd = cb.fd;
159 params.buf = cb.buf;
160 params.size = cb.offset;
161 params.flag = cb.errcode;
162 params.xerrno = cb.xerrno;
163 ScheduleCallHere(cb.callback);
9e008dda 164 }
2b663917 165}
166
167
168/*
169 * Cancel the given callback
170 *
171 * Remember that the data is cbdataRef'ed.
172 */
b0469965 173// TODO: make this a comm_io_callback_t method
174static void
2b663917 175commio_cancel_callback(int fd, comm_io_callback_t *ccb)
176{
b0469965 177 debugs(5, 3, "commio_cancel_callback: called for FD " << fd);
9e008dda
AJ
178 assert(ccb->fd == fd);
179 assert(ccb->active());
2b663917 180
9e008dda
AJ
181 ccb->xerrno = 0;
182 ccb->callback = NULL;
2b663917 183}
184
185/*
186 * Call the given comm callback; assumes the callback is valid.
9e008dda 187 *
2b663917 188 * @param ccb io completion callback
189 */
190void
191commio_call_callback(comm_io_callback_t *ccb)
192{
2b663917 193}
194
2d8c0b1a 195class ConnectStateData
62e76326 196{
2d8c0b1a 197
198public:
d2d59a68 199 void *operator new (size_t);
200 void operator delete (void *);
2d8c0b1a 201 static void Connect (int fd, void *me);
202 void connect();
203 void callCallback(comm_err_t status, int xerrno);
204 void defaults();
cc192b50 205
206// defaults given by client
f88211e8 207 char *host;
cc192b50 208 u_short default_port;
ad61a2b4 209 IpAddress default_addr;
cc192b50 210 // NP: CANNOT store the default addr:port together as it gets set/reset differently.
62e76326 211
ad61a2b4 212 IpAddress S;
b0469965 213 AsyncCall::Pointer callback;
62e76326 214
03a1ee42 215 int fd;
22c653cd 216 int tries;
217 int addrcount;
218 int connstart;
d2d59a68 219
220private:
0b77ecd8 221 int commResetFD();
222 int commRetryConnect();
d2d59a68 223 CBDATA_CLASS(ConnectStateData);
2d8c0b1a 224};
f88211e8 225
090089c4 226/* STATIC */
62e76326 227
74257126
AR
228static DescriptorSet *TheHalfClosed = NULL; /// the set of half-closed FDs
229static bool WillCheckHalfClosed = false; /// true if check is scheduled
230static EVH commHalfClosedCheck;
231static void commPlanHalfClosedCheck();
232
cc192b50 233static comm_err_t commBind(int s, struct addrinfo &);
f5b8bbc4 234static void commSetReuseAddr(int);
235static void commSetNoLinger(int);
30a4f2a8 236#ifdef TCP_NODELAY
f5b8bbc4 237static void commSetTcpNoDelay(int);
30a4f2a8 238#endif
f5b8bbc4 239static void commSetTcpRcvbuf(int, int);
f88211e8 240static PF commConnectFree;
03a1ee42 241static PF commHandleWrite;
edeb28fd 242static IPH commConnectDnsHandle;
723123a9 243
3c1a197f 244static PF comm_accept_try;
c4b7a5a9 245
62e76326 246class AcceptFD
247{
248
249public:
b0469965 250 AcceptFD(int aFd = -1): fd(aFd), theCallback(0), mayAcceptMore(false) {}
62e76326 251
b0469965 252 void subscribe(AsyncCall::Pointer &call);
253 void acceptNext();
254 void notify(int newfd, comm_err_t, int xerrno, const ConnectionDetail &);
62e76326 255
2d8c0b1a 256 int fd;
545d554b 257
b0469965 258private:
259 bool acceptOne();
62e76326 260
b0469965 261 AsyncCall::Pointer theCallback;
262 bool mayAcceptMore;
c4b7a5a9 263};
62e76326 264
c4b7a5a9 265typedef enum {
62e76326 266 COMM_CB_READ = 1,
2d8c0b1a 267 COMM_CB_DERIVED,
c4b7a5a9 268} comm_callback_t;
269
9e008dda 270struct _fd_debug_t {
43ae1d95 271 char const *close_file;
62e76326 272 int close_line;
c4b7a5a9 273};
62e76326 274
c4b7a5a9 275typedef struct _fd_debug_t fd_debug_t;
276
b001e822 277static MemAllocator *conn_close_pool = NULL;
b0469965 278AcceptFD *fdc_table = NULL; // TODO: rename. And use Vector<>?
c4b7a5a9 279fd_debug_t *fdd_table = NULL;
62e76326 280
b0469965 281static bool
282isOpen(const int fd)
b300c36d 283{
9e008dda 284 return fd_table[fd].flags.open != 0;
b300c36d 285}
286
e1a88700 287/**
c4b7a5a9 288 * Attempt a read
289 *
290 * If the read attempt succeeds or fails, call the callback.
291 * Else, wait for another IO notification.
292 */
2d8c0b1a 293void
2b663917 294commHandleRead(int fd, void *data)
2d8c0b1a 295{
2b663917 296 comm_io_callback_t *ccb = (comm_io_callback_t *) data;
9e008dda 297
2b663917 298 assert(data == COMMIO_FD_READCB(fd));
299 assert(commio_has_callback(fd, IOCB_READ, ccb));
62e76326 300 /* Attempt a read */
301 statCounter.syscalls.sock.reads++;
302 errno = 0;
2d8c0b1a 303 int retval;
2b663917 304 retval = FD_READ_METHOD(fd, ccb->buf, ccb->size);
bf8fe701 305 debugs(5, 3, "comm_read_try: FD " << fd << ", size " << ccb->size << ", retval " << retval << ", errno " << errno);
62e76326 306
307 if (retval < 0 && !ignoreErrno(errno)) {
bf8fe701 308 debugs(5, 3, "comm_read_try: scheduling COMM_ERROR");
9e008dda
AJ
309 ccb->offset = 0;
310 commio_finish_callback(fd, ccb, COMM_ERROR, errno);
62e76326 311 return;
312 };
313
314 /* See if we read anything */
315 /* Note - read 0 == socket EOF, which is a valid read */
316 if (retval >= 0) {
317 fd_bytes(fd, retval, FD_READ);
9e008dda
AJ
318 ccb->offset = retval;
319 commio_finish_callback(fd, ccb, COMM_OK, errno);
62e76326 320 return;
321 }
c4b7a5a9 322
62e76326 323 /* Nope, register for some more IO */
2b663917 324 commSetSelect(fd, COMM_SELECT_READ, commHandleRead, data, 0);
c4b7a5a9 325}
326
e1a88700 327/**
c4b7a5a9 328 * Queue a read. handler/handler_data are called when the read
329 * completes, on error, or on file descriptor close.
330 */
331void
332comm_read(int fd, char *buf, int size, IOCB *handler, void *handler_data)
b0469965 333{
334 AsyncCall::Pointer call = commCbCall(5,4, "SomeCommReadHandler",
9e008dda 335 CommIoCbPtrFun(handler, handler_data));
b0469965 336 comm_read(fd, buf, size, call);
337}
338
339void
340comm_read(int fd, char *buf, int size, AsyncCall::Pointer &callback)
c4b7a5a9 341{
82ec8dfc 342 debugs(5, 5, "comm_read, queueing read for FD " << fd << "; asynCall " << callback);
c4b7a5a9 343
82ec8dfc
AR
344 /* Make sure we are open and not closing */
345 assert(isOpen(fd));
346 assert(!fd_table[fd].closing());
347 comm_io_callback_t *ccb = COMMIO_FD_READCB(fd);
348
349 // Make sure we are either not reading or just passively monitoring.
350 // Active/passive conflicts are OK and simply cancel passive monitoring.
351 if (ccb->active()) {
352 // if the assertion below fails, we have an active comm_read conflict
74257126 353 assert(fd_table[fd].halfClosedReader != NULL);
82ec8dfc
AR
354 commStopHalfClosedMonitor(fd);
355 assert(!ccb->active());
356 }
528b2c61 357
2b663917 358 /* Queue the read */
82ec8dfc
AR
359 commio_set_callback(fd, IOCB_READ, ccb, callback, (char *)buf, NULL, size);
360 commSetSelect(fd, COMM_SELECT_READ, commHandleRead, ccb, 0);
c4b7a5a9 361}
362
e1a88700 363/**
c4b7a5a9 364 * Empty the read buffers
365 *
366 * This is a magical routine that empties the read buffers.
367 * Under some platforms (Linux) if a buffer has data in it before
368 * you call close(), the socket will hang and take quite a while
369 * to timeout.
370 */
371static void
372comm_empty_os_read_buffers(int fd)
373{
a42d5c25 374#ifdef _SQUID_LINUX_
c4b7a5a9 375 /* prevent those nasty RST packets */
376 char buf[SQUID_TCP_SO_RCVBUF];
62e76326 377
cc192b50 378 if (fd_table[fd].flags.nonblocking == 1) {
379 while (FD_READ_METHOD(fd, buf, SQUID_TCP_SO_RCVBUF) > 0) {};
380 }
c4b7a5a9 381#endif
382}
383
384
e1a88700 385/**
2b663917 386 * Return whether the FD has a pending completed callback.
c4b7a5a9 387 */
388int
389comm_has_pending_read_callback(int fd)
390{
b0469965 391 assert(isOpen(fd));
392 // XXX: We do not know whether there is a read callback scheduled.
393 // This is used for pconn management that should probably be more
394 // tightly integrated into comm to minimize the chance that a
395 // closing pconn socket will be used for a new transaction.
545d554b 396 return false;
c4b7a5a9 397}
398
b0469965 399// Does comm check this fd for read readiness?
400// Note that when comm is not monitoring, there can be a pending callback
401// call, which may resume comm monitoring once fired.
528b2c61 402bool
b0469965 403comm_monitors_read(int fd)
c4b7a5a9 404{
b0469965 405 assert(isOpen(fd));
406 // Being active is usually the same as monitoring because we always
407 // start monitoring the FD when we configure comm_io_callback_t for I/O
408 // and we usually configure comm_io_callback_t for I/O when we starting
409 // monitoring a FD for reading. TODO: replace with commio_has_callback
410 return COMMIO_FD_READCB(fd)->active();
c4b7a5a9 411}
412
e1a88700 413/**
c4b7a5a9 414 * Cancel a pending read. Assert that we have the right parameters,
415 * and that there are no pending read events!
2b663917 416 *
b0469965 417 * XXX: We do not assert that there are no pending read events and
418 * with async calls it becomes even more difficult.
419 * The whole interface should be reworked to do callback->cancel()
420 * instead of searching for places where the callback may be stored and
421 * updating the state of those places.
422 *
2b663917 423 * AHC Don't call the comm handlers?
c4b7a5a9 424 */
425void
426comm_read_cancel(int fd, IOCB *callback, void *data)
427{
b0469965 428 if (!isOpen(fd)) {
429 debugs(5, 4, "comm_read_cancel fails: FD " << fd << " closed");
430 return;
9e008dda 431 }
b0469965 432
433 comm_io_callback_t *cb = COMMIO_FD_READCB(fd);
434 // TODO: is "active" == "monitors FD"?
435 if (!cb->active()) {
436 debugs(5, 4, "comm_read_cancel fails: FD " << fd << " inactive");
437 return;
9e008dda 438 }
b0469965 439
440 typedef CommCbFunPtrCallT<CommIoCbPtrFun> Call;
441 Call *call = dynamic_cast<Call*>(cb->callback.getRaw());
442 if (!call) {
443 debugs(5, 4, "comm_read_cancel fails: FD " << fd << " lacks callback");
444 return;
9e008dda 445 }
b0469965 446
82ec8dfc
AR
447 call->cancel("old comm_read_cancel");
448
b0469965 449 typedef CommIoCbParams Params;
450 const Params &params = GetCommParams<Params>(cb->callback);
c4b7a5a9 451
c4b7a5a9 452 /* Ok, we can be reasonably sure we won't lose any data here! */
b0469965 453 assert(call->dialer.handler == callback);
454 assert(params.data == data);
c4b7a5a9 455
456 /* Delete the callback */
b0469965 457 commio_cancel_callback(fd, cb);
420f2ac8 458
459 /* And the IO event */
62e76326 460 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
c4b7a5a9 461}
462
c4b7a5a9 463void
b0469965 464comm_read_cancel(int fd, AsyncCall::Pointer &callback)
c4b7a5a9 465{
b0469965 466 callback->cancel("comm_read_cancel");
9e008dda 467
b0469965 468 if (!isOpen(fd)) {
469 debugs(5, 4, "comm_read_cancel fails: FD " << fd << " closed");
470 return;
471 }
472
473 comm_io_callback_t *cb = COMMIO_FD_READCB(fd);
474
475 if (!cb->active()) {
476 debugs(5, 4, "comm_read_cancel fails: FD " << fd << " inactive");
477 return;
478 }
479
480 AsyncCall::Pointer call = cb->callback;
82ec8dfc 481 assert(call != NULL); // XXX: should never fail (active() checks for callback==NULL)
9e008dda 482
b0469965 483 /* Ok, we can be reasonably sure we won't lose any data here! */
484 assert(call == callback);
c4b7a5a9 485
b0469965 486 /* Delete the callback */
487 commio_cancel_callback(fd, cb);
488
489 /* And the IO event */
490 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
c4b7a5a9 491}
492
493
e1a88700 494/**
ce767c23 495 * synchronous wrapper around udp socket functions
496 */
ce767c23 497int
ad61a2b4 498comm_udp_recvfrom(int fd, void *buf, size_t len, int flags, IpAddress &from)
ce767c23 499{
62e76326 500 statCounter.syscalls.sock.recvfroms++;
cc192b50 501 int x = 0;
502 struct addrinfo *AI = NULL;
503
504 debugs(5,8, "comm_udp_recvfrom: FD " << fd << " from " << from);
505
506 assert( NULL == AI );
507
508 from.InitAddrInfo(AI);
509
510 x = recvfrom(fd, buf, len, flags, AI->ai_addr, &AI->ai_addrlen);
511
512 from = *AI;
513
514 from.FreeAddrInfo(AI);
515
516 return x;
ce767c23 517}
518
365f12a9 519int
7d21986b 520comm_udp_recv(int fd, void *buf, size_t len, int flags)
365f12a9 521{
ad61a2b4 522 IpAddress nul;
cc192b50 523 return comm_udp_recvfrom(fd, buf, len, flags, nul);
365f12a9 524}
525
f71da12c 526ssize_t
7d21986b 527comm_udp_send(int s, const void *buf, size_t len, int flags)
f71da12c 528{
62e76326 529 return send(s, buf, len, flags);
f71da12c 530}
ce767c23 531
532
545d554b 533bool
534comm_has_incomplete_write(int fd)
535{
b0469965 536 assert(isOpen(fd));
537 return COMMIO_FD_WRITECB(fd)->active();
d4cb310b 538}
539
e1a88700 540/**
cf3c0ee3 541 * Queue a write. handler/handler_data are called when the write fully
542 * completes, on error, or on file descriptor close.
543 */
9864ee44 544
090089c4 545/* Return the local port associated with fd. */
b8d8561b 546u_short
547comm_local_port(int fd)
090089c4 548{
ad61a2b4 549 IpAddress temp;
cc192b50 550 struct addrinfo *addr = NULL;
76f87348 551 fde *F = &fd_table[fd];
090089c4 552
090089c4 553 /* If the fd is closed already, just return */
62e76326 554
60c0b5a2 555 if (!F->flags.open) {
bf8fe701 556 debugs(5, 0, "comm_local_port: FD " << fd << " has been closed.");
62e76326 557 return 0;
090089c4 558 }
62e76326 559
cc192b50 560 if (F->local_addr.GetPort())
561 return F->local_addr.GetPort();
62e76326 562
cc192b50 563 temp.InitAddrInfo(addr);
62e76326 564
cc192b50 565 if (getsockname(fd, addr->ai_addr, &(addr->ai_addrlen)) ) {
bf8fe701 566 debugs(50, 1, "comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD " << fd << ": " << xstrerror());
cc192b50 567 temp.FreeAddrInfo(addr);
62e76326 568 return 0;
090089c4 569 }
cc192b50 570 temp = *addr;
571
572 temp.FreeAddrInfo(addr);
573
574 F->local_addr.SetPort(temp.GetPort());
575
576 // grab default socket information for this address
577 temp.GetAddrInfo(addr);
578
579 F->sock_family = addr->ai_family;
580
581 temp.FreeAddrInfo(addr);
62e76326 582
cc192b50 583 debugs(5, 6, "comm_local_port: FD " << fd << ": port " << F->local_addr.GetPort());
584 return F->local_addr.GetPort();
090089c4 585}
586
3d7e9d7c 587static comm_err_t
cc192b50 588commBind(int s, struct addrinfo &inaddr)
090089c4 589{
83704487 590 statCounter.syscalls.sock.binds++;
62e76326 591
cc192b50 592 if (bind(s, inaddr.ai_addr, inaddr.ai_addrlen) == 0)
62e76326 593 return COMM_OK;
594
cc192b50 595 debugs(50, 0, "commBind: Cannot bind socket FD " << s << " to " << fd_table[s].local_addr << ": " << xstrerror());
62e76326 596
090089c4 597 return COMM_ERROR;
598}
599
e1a88700 600/**
601 * Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE
602 * is OR of flags specified in comm.h. Defaults TOS
603 */
b8d8561b 604int
16b204c4 605comm_open(int sock_type,
62e76326 606 int proto,
ad61a2b4 607 IpAddress &addr,
62e76326 608 int flags,
609 const char *note)
d6827718 610{
cc192b50 611 return comm_openex(sock_type, proto, addr, flags, 0, note);
d6827718 612}
613
2d8c0b1a 614static bool
615limitError(int const anErrno)
616{
617 return anErrno == ENFILE || anErrno == EMFILE;
618}
d6827718 619
057f5854 620int
621comm_set_tos(int fd, int tos)
622{
623#ifdef IP_TOS
9e008dda
AJ
624 int x = setsockopt(fd, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int));
625 if (x < 0)
626 debugs(50, 1, "comm_set_tos: setsockopt(IP_TOS) on FD " << fd << ": " << xstrerror());
627 return x;
057f5854 628#else
9e008dda
AJ
629 debugs(50, 0, "WARNING: setsockopt(IP_TOS) not supported on this platform");
630 return -1;
057f5854 631#endif
632}
633
cc192b50 634void
635comm_set_v6only(int fd, int tos)
636{
637#ifdef IPV6_V6ONLY
638 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &tos, sizeof(int)) < 0) {
639 debugs(50, 1, "comm_open: setsockopt(IPV6_V6ONLY) on FD " << fd << ": " << xstrerror());
640 }
641#else
642 debugs(50, 0, "WARNING: comm_open: setsockopt(IPV6_V6ONLY) not supported on this platform");
643#endif /* sockopt */
644}
057f5854 645
40d6264d
AJ
646/**
647 * Set the socket IP_TRANSPARENT option for Linux TPROXY v4 support.
648 */
f1e0717c 649void
e950e673 650comm_set_transparent(int fd)
f1e0717c 651{
2ad20b4f 652#if defined(IP_TRANSPARENT)
e950e673 653 int tos = 1;
ef88b51d
AJ
654 if (setsockopt(fd, SOL_IP, IP_TRANSPARENT, (char *) &tos, sizeof(int)) < 0) {
655 debugs(50, DBG_IMPORTANT, "comm_open: setsockopt(IP_TRANSPARENT) on FD " << fd << ": " << xstrerror());
9e008dda 656 } else {
3949d8b7
AJ
657 /* mark the socket as having transparent options */
658 fd_table[fd].flags.transparent = 1;
659 }
f1e0717c 660#else
ef88b51d 661 debugs(50, DBG_CRITICAL, "WARNING: comm_open: setsockopt(IP_TRANSPARENT) not supported on this platform");
f1e0717c
AJ
662#endif /* sockopt */
663}
664
e1a88700 665/**
666 * Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE
667 * is OR of flags specified in defines.h:COMM_*
668 */
d6827718 669int
670comm_openex(int sock_type,
62e76326 671 int proto,
ad61a2b4 672 IpAddress &addr,
62e76326 673 int flags,
674 unsigned char TOS,
675 const char *note)
090089c4 676{
677 int new_socket;
76f87348 678 fde *F = NULL;
cc192b50 679 int tos = 0;
680 struct addrinfo *AI = NULL;
090089c4 681
88bfe092 682 PROF_start(comm_open);
090089c4 683 /* Create socket for accepting new connections. */
83704487 684 statCounter.syscalls.sock.sockets++;
62e76326 685
cc192b50 686 /* Setup the socket addrinfo details for use */
687 addr.GetAddrInfo(AI);
688 AI->ai_socktype = sock_type;
689 AI->ai_protocol = proto;
cc192b50 690
691 debugs(50, 3, "comm_openex: Attempt open socket for: " << addr );
692
9e008dda 693 if ((new_socket = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) {
62e76326 694 /* Increase the number of reserved fd's if calls to socket()
695 * are failing because the open file table is full. This
696 * limits the number of simultaneous clients */
697
2d8c0b1a 698 if (limitError(errno)) {
dd4e714f 699 debugs(50, DBG_IMPORTANT, "comm_open: socket failure: " << xstrerror());
62e76326 700 fdAdjustReserved();
2d8c0b1a 701 } else {
8ca8bd92 702 debugs(50, DBG_CRITICAL, "comm_open: socket failure: " << xstrerror());
62e76326 703 }
704
cc192b50 705 addr.FreeAddrInfo(AI);
706
62e76326 707 PROF_stop(comm_open);
708 return -1;
090089c4 709 }
62e76326 710
cc192b50 711 debugs(50, 3, "comm_openex: Opened socket FD " << new_socket << " : family=" << AI->ai_family << ", type=" << AI->ai_socktype << ", protocol=" << AI->ai_protocol );
712
d6827718 713 /* set TOS if needed */
cc192b50 714 if (TOS && comm_set_tos(new_socket, TOS) ) {
62e76326 715 tos = TOS;
cc192b50 716 }
62e76326 717
cc192b50 718#if IPV6_SPECIAL_SPLITSTACK
62e76326 719
9e008dda 720 if ( addr.IsIPv6() )
cc192b50 721 comm_set_v6only(new_socket, tos);
62e76326 722
d6827718 723#endif
62e76326 724
9b1f7ee8 725#if IPV6_SPECIAL_V4MAPPED
cc192b50 726
727 /* Windows Vista supports Dual-Sockets. BUT defaults them to V6ONLY. Turn it OFF. */
9b1f7ee8 728 /* Other OS may have this administratively disabled for general use. Same deal. */
9e008dda 729 if ( addr.IsIPv6() )
cc192b50 730 comm_set_v6only(new_socket, 0);
731
732#endif
62e76326 733
090089c4 734 /* update fdstat */
bf8fe701 735 debugs(5, 5, "comm_open: FD " << new_socket << " is a new socket");
62e76326 736
b0469965 737 assert(!isOpen(new_socket));
5c5783a2 738 fd_open(new_socket, FD_SOCKET, note);
62e76326 739
c4b7a5a9 740 fdd_table[new_socket].close_file = NULL;
62e76326 741
c4b7a5a9 742 fdd_table[new_socket].close_line = 0;
62e76326 743
76f87348 744 F = &fd_table[new_socket];
62e76326 745
d6827718 746 F->local_addr = addr;
62e76326 747
cc192b50 748 F->tos = TOS;
749
750 F->sock_family = AI->ai_family;
62e76326 751
79a15e0a 752 if (!(flags & COMM_NOCLOEXEC))
62e76326 753 commSetCloseOnExec(new_socket);
754
cdc33f35 755 if ((flags & COMM_REUSEADDR))
62e76326 756 commSetReuseAddr(new_socket);
757
9e008dda 758 if (addr.GetPort() > (u_short) 0) {
a50bfe93 759#ifdef _SQUID_MSWIN_
760
761 if (sock_type != SOCK_DGRAM)
762#endif
763
764 commSetNoLinger(new_socket);
62e76326 765
766 if (opt_reuseaddr)
767 commSetReuseAddr(new_socket);
090089c4 768 }
62e76326 769
a35595cd 770 /* MUST be done before binding or face OS Error: "(99) Cannot assign requested address"... */
9e008dda 771 if ((flags & COMM_TRANSPARENT)) {
a35595cd
AJ
772 comm_set_transparent(new_socket);
773 }
a35595cd 774
9e008dda 775 if (!addr.IsNoAddr()) {
cc192b50 776 if (commBind(new_socket, *AI) != COMM_OK) {
62e76326 777 comm_close(new_socket);
cc192b50 778 addr.FreeAddrInfo(AI);
62e76326 779 return -1;
780 PROF_stop(comm_open);
781 }
23ff6968 782 }
62e76326 783
cc192b50 784 addr.FreeAddrInfo(AI);
090089c4 785
79a15e0a 786 if (flags & COMM_NONBLOCKING)
9e008dda 787 if (commSetNonBlocking(new_socket) == COMM_ERROR) {
62e76326 788 return -1;
789 PROF_stop(comm_open);
790 }
791
30a4f2a8 792#ifdef TCP_NODELAY
793 if (sock_type == SOCK_STREAM)
62e76326 794 commSetTcpNoDelay(new_socket);
795
30a4f2a8 796#endif
62e76326 797
1241e63e 798 if (Config.tcpRcvBufsz > 0 && sock_type == SOCK_STREAM)
62e76326 799 commSetTcpRcvbuf(new_socket, Config.tcpRcvBufsz);
800
88bfe092 801 PROF_stop(comm_open);
62e76326 802
090089c4 803 return new_socket;
804}
805
d2d59a68 806CBDATA_CLASS_INIT(ConnectStateData);
807
808void *
809ConnectStateData::operator new (size_t size)
810{
811 CBDATA_INIT_TYPE(ConnectStateData);
812 return cbdataAlloc(ConnectStateData);
813}
814
815void
816ConnectStateData::operator delete (void *address)
817{
818 cbdataFree(address);
819}
820
b0469965 821
822
e5f6c5c2 823void
b0469965 824commConnectStart(int fd, const char *host, u_short port, AsyncCall::Pointer &cb)
e924600d 825{
b0469965 826 debugs(cb->debugSection, cb->debugLevel, "commConnectStart: FD " << fd <<
9e008dda 827 ", cb " << cb << ", " << host << ":" << port); // TODO: just print *cb
b0469965 828
28c60158 829 ConnectStateData *cs;
d2d59a68 830 cs = new ConnectStateData;
03a1ee42 831 cs->fd = fd;
e924600d 832 cs->host = xstrdup(host);
cc192b50 833 cs->default_port = port;
b0469965 834 cs->callback = cb;
835
e924600d 836 comm_add_close_handler(fd, commConnectFree, cs);
8407afee 837 ipcache_nbgethostbyname(host, commConnectDnsHandle, cs);
edeb28fd 838}
839
b0469965 840// TODO: Remove this and similar callback registration functions by replacing
841// (callback,data) parameters with an AsyncCall so that we do not have to use
842// a generic call name and debug level when creating an AsyncCall. This will
843// also cut the number of callback registration routines in half.
844void
845commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data)
846{
847 debugs(5, 5, "commConnectStart: FD " << fd << ", data " << data << ", " << host << ":" << port);
848 AsyncCall::Pointer call = commCbCall(5,3,
9e008dda 849 "SomeCommConnectHandler", CommConnectCbPtrFun(callback, data));
b0469965 850 commConnectStart(fd, host, port, call);
851}
852
edeb28fd 853static void
03a1ee42 854commConnectDnsHandle(const ipcache_addrs * ia, void *data)
edeb28fd 855{
e6ccf245 856 ConnectStateData *cs = (ConnectStateData *)data;
62e76326 857
edeb28fd 858 if (ia == NULL) {
bf8fe701 859 debugs(5, 3, "commConnectDnsHandle: Unknown host: " << cs->host);
62e76326 860
861 if (!dns_error_message) {
862 dns_error_message = "Unknown DNS error";
bf8fe701 863 debugs(5, 1, "commConnectDnsHandle: Bad dns_error_message");
62e76326 864 }
865
866 assert(dns_error_message != NULL);
2d8c0b1a 867 cs->callCallback(COMM_ERR_DNS, 0);
62e76326 868 return;
edeb28fd 869 }
62e76326 870
f076b37b 871 assert(ia->cur < ia->count);
cc192b50 872
873 cs->default_addr = ia->in_addrs[ia->cur];
a12a049a 874
875 if (Config.onoff.balance_on_multiple_ip)
876 ipcacheCycleAddr(cs->host, NULL);
877
22c653cd 878 cs->addrcount = ia->count;
a12a049a 879
22c653cd 880 cs->connstart = squid_curtime;
a12a049a 881
2d8c0b1a 882 cs->connect();
e924600d 883}
884
2d8c0b1a 885void
886ConnectStateData::callCallback(comm_err_t status, int xerrno)
887{
b0469965 888 debugs(5, 3, "commConnectCallback: FD " << fd);
bf8fe701 889
2d8c0b1a 890 comm_remove_close_handler(fd, commConnectFree, this);
e1b16349 891 commSetTimeout(fd, -1, NULL, NULL);
62e76326 892
b0469965 893 typedef CommConnectCbParams Params;
894 Params &params = GetCommParams<Params>(callback);
895 params.fd = fd;
896 params.flag = status;
897 params.xerrno = xerrno;
898 ScheduleCallHere(callback);
899 callback = NULL;
62e76326 900
744c68f5 901 commConnectFree(fd, this);
f88211e8 902}
903
e924600d 904static void
9daca08e 905commConnectFree(int fd, void *data)
e924600d 906{
e6ccf245 907 ConnectStateData *cs = (ConnectStateData *)data;
bf8fe701 908 debugs(5, 3, "commConnectFree: FD " << fd);
b0469965 909// delete cs->callback;
910 cs->callback = NULL;
8407afee 911 safe_free(cs->host);
00d77d6b 912 delete cs;
e924600d 913}
914
2d8c0b1a 915static void
916copyFDFlags(int to, fde *F)
917{
918 if (F->flags.close_on_exec)
919 commSetCloseOnExec(to);
920
921 if (F->flags.nonblocking)
922 commSetNonBlocking(to);
923
924#ifdef TCP_NODELAY
925
926 if (F->flags.nodelay)
927 commSetTcpNoDelay(to);
928
929#endif
930
931 if (Config.tcpRcvBufsz > 0)
932 commSetTcpRcvbuf(to, Config.tcpRcvBufsz);
933}
934
22c653cd 935/* Reset FD so that we can connect() again */
0b77ecd8 936int
937ConnectStateData::commResetFD()
edeb28fd 938{
cc192b50 939 struct addrinfo *AI = NULL;
ad61a2b4 940 IpAddress nul;
9d92af86 941 int new_family = AF_UNSPEC;
cc192b50 942
b0469965 943// XXX: do we have to check this?
944//
945// if (!cbdataReferenceValid(callback.data))
946// return 0;
62e76326 947
83704487 948 statCounter.syscalls.sock.sockets++;
62e76326 949
cc192b50 950 /* setup a bare-bones addrinfo */
9d92af86 951 /* TODO INET6: for WinXP we may need to check the local_addr type and setup the family properly. */
cc192b50 952 nul.GetAddrInfo(AI);
9d92af86 953 new_family = AI->ai_family;
cc192b50 954
955 int fd2 = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);
956
957 nul.FreeAddrInfo(AI);
62e76326 958
edeb28fd 959 if (fd2 < 0) {
8ca8bd92 960 debugs(5, DBG_CRITICAL, HERE << "WARNING: FD " << fd2 << " socket failed to allocate: " << xstrerror());
62e76326 961
962 if (ENFILE == errno || EMFILE == errno)
963 fdAdjustReserved();
964
965 return 0;
edeb28fd 966 }
62e76326 967
68aa4272 968#ifdef _SQUID_MSWIN_
969
970 /* On Windows dup2() can't work correctly on Sockets, the */
971 /* workaround is to close the destination Socket before call them. */
0b77ecd8 972 close(fd);
68aa4272 973
974#endif
975
0b77ecd8 976 if (dup2(fd2, fd) < 0) {
8ca8bd92 977 debugs(5, DBG_CRITICAL, HERE << "WARNING: dup2(FD " << fd2 << ", FD " << fd << ") failed: " << xstrerror());
62e76326 978
979 if (ENFILE == errno || EMFILE == errno)
980 fdAdjustReserved();
981
982 close(fd2);
983
984 return 0;
edeb28fd 985 }
3a5a4930 986 commResetSelect(fd);
62e76326 987
edeb28fd 988 close(fd2);
0b77ecd8 989 fde *F = &fd_table[fd];
9d92af86
AJ
990
991 /* INET6: copy the new sockets family type to the FDE table */
992 fd_table[fd].sock_family = new_family;
993
0b77ecd8 994 fd_table[fd].flags.called_connect = 0;
09544acc 995 /*
996 * yuck, this has assumptions about comm_open() arguments for
997 * the original socket
998 */
62e76326 999
e17b4929 1000 /* MUST be done before binding or face OS Error: "(99) Cannot assign requested address"... */
9e008dda 1001 if ( F->flags.transparent ) {
e17b4929
AJ
1002 comm_set_transparent(fd);
1003 }
1004
cc192b50 1005 AI = NULL;
1006 F->local_addr.GetAddrInfo(AI);
1007
1008 if (commBind(fd, *AI) != COMM_OK) {
68d57d84 1009 debugs(5, DBG_CRITICAL, "WARNING: Reset of FD " << fd << " for " << F->local_addr << " failed to bind: " << xstrerror());
cc192b50 1010 F->local_addr.FreeAddrInfo(AI);
62e76326 1011 return 0;
09544acc 1012 }
cc192b50 1013 F->local_addr.FreeAddrInfo(AI);
62e76326 1014
cc192b50 1015 if (F->tos)
1016 comm_set_tos(fd, F->tos);
1017
1018#if IPV6_SPECIAL_SPLITSTACK
1019
9e008dda 1020 if ( F->local_addr.IsIPv6() )
cc192b50 1021 comm_set_v6only(fd, F->tos);
62e76326 1022
d6827718 1023#endif
cc192b50 1024
f3767a6a 1025 copyFDFlags(fd, F);
62e76326 1026
edeb28fd 1027 return 1;
1028}
1029
0b77ecd8 1030int
1031ConnectStateData::commRetryConnect()
22c653cd 1032{
0b77ecd8 1033 assert(addrcount > 0);
62e76326 1034
0b77ecd8 1035 if (addrcount == 1) {
1036 if (tries >= Config.retry.maxtries)
62e76326 1037 return 0;
1038
0b77ecd8 1039 if (squid_curtime - connstart > Config.Timeout.connect)
62e76326 1040 return 0;
22c653cd 1041 } else {
0b77ecd8 1042 if (tries > addrcount)
62e76326 1043 return 0;
22c653cd 1044 }
62e76326 1045
0b77ecd8 1046 return commResetFD();
22c653cd 1047}
1048
4ed0e075 1049static void
1050commReconnect(void *data)
1051{
1052 ConnectStateData *cs = (ConnectStateData *)data;
1053 ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs);
1054}
1055
f3767a6a 1056/** Connect SOCK to specified DEST_PORT at DEST_HOST. */
2d8c0b1a 1057void
f3767a6a 1058ConnectStateData::Connect(int fd, void *me)
090089c4 1059{
2d8c0b1a 1060 ConnectStateData *cs = (ConnectStateData *)me;
1061 assert (cs->fd == fd);
1062 cs->connect();
1063}
1064
1065void
1066ConnectStateData::defaults()
1067{
cc192b50 1068 S = default_addr;
1069 S.SetPort(default_port);
2d8c0b1a 1070}
62e76326 1071
2d8c0b1a 1072void
1073ConnectStateData::connect()
1074{
cc192b50 1075 if (S.IsAnyAddr())
2d8c0b1a 1076 defaults();
62e76326 1077
f3767a6a 1078 debugs(5,5, HERE << "to " << S);
cc192b50 1079
1080 switch (comm_connect_addr(fd, S) ) {
62e76326 1081
e5f6c5c2 1082 case COMM_INPROGRESS:
f3767a6a 1083 debugs(5, 5, HERE << "FD " << fd << ": COMM_INPROGRESS");
2d8c0b1a 1084 commSetSelect(fd, COMM_SELECT_WRITE, ConnectStateData::Connect, this, 0);
62e76326 1085 break;
1086
e5f6c5c2 1087 case COMM_OK:
f3767a6a 1088 debugs(5, 5, HERE << "FD " << fd << ": COMM_OK - connected");
cc192b50 1089 ipcacheMarkGoodAddr(host, S);
2d8c0b1a 1090 callCallback(COMM_OK, 0);
62e76326 1091 break;
1092
9d92af86
AJ
1093#if USE_IPV6
1094 case COMM_ERR_PROTOCOL:
1095 /* problem using the desired protocol over this socket.
1096 * count the connection attempt, reset the socket, and immediately try again */
1097 tries++;
1098 commResetFD();
1099 connect();
1100 break;
1101#endif
1102
e5f6c5c2 1103 default:
f3767a6a 1104 debugs(5, 5, HERE "FD " << fd << ": * - try again");
2d8c0b1a 1105 tries++;
cc192b50 1106 ipcacheMarkBadAddr(host, S);
62e76326 1107
9b5c4a9a 1108#if USE_ICMP
62e76326 1109 if (Config.onoff.test_reachability)
cc192b50 1110 netdbDeleteAddrNetwork(S);
9b5c4a9a 1111#endif
62e76326 1112
0b77ecd8 1113 if (commRetryConnect()) {
4ed0e075 1114 eventAdd("commReconnect", commReconnect, this, this->addrcount == 1 ? 0.05 : 0.0, 0);
62e76326 1115 } else {
f3767a6a 1116 debugs(5, 5, HERE << "FD " << fd << ": * - ERR tried too many times already.");
2d8c0b1a 1117 callCallback(COMM_ERR_CONNECT, errno);
62e76326 1118 }
090089c4 1119 }
090089c4 1120}
b0469965 1121/*
b8d8561b 1122int
b0469965 1123commSetTimeout_old(int fd, int timeout, PF * handler, void *data)
090089c4 1124{
f3767a6a 1125 debugs(5, 3, HERE << "FD " << fd << " timeout " << timeout);
03eb2f01 1126 assert(fd >= 0);
1127 assert(fd < Squid_MaxFD);
2d8c0b1a 1128 fde *F = &fd_table[fd];
60c0b5a2 1129 assert(F->flags.open);
62e76326 1130
5c5783a2 1131 if (timeout < 0) {
62e76326 1132 cbdataReferenceDone(F->timeout_data);
1133 F->timeout_handler = NULL;
1134 F->timeout = 0;
5849612f 1135 } else {
62e76326 1136 if (handler) {
1137 cbdataReferenceDone(F->timeout_data);
1138 F->timeout_handler = handler;
1139 F->timeout_data = cbdataReference(data);
1140 }
1141
1142 F->timeout = squid_curtime + (time_t) timeout;
30a4f2a8 1143 }
62e76326 1144
a3fa14bf 1145 return F->timeout;
090089c4 1146}
b0469965 1147*/
1148
1149int
1150commSetTimeout(int fd, int timeout, PF * handler, void *data)
1151{
1152 AsyncCall::Pointer call;
f3767a6a 1153 debugs(5, 3, HERE << "FD " << fd << " timeout " << timeout);
9e008dda
AJ
1154 if (handler != NULL)
1155 call=commCbCall(5,4, "SomeTimeoutHandler", CommTimeoutCbPtrFun(handler, data));
b0469965 1156 else
9e008dda 1157 call = NULL;
b0469965 1158 return commSetTimeout(fd, timeout, call);
1159}
1160
1161
1162int commSetTimeout(int fd, int timeout, AsyncCall::Pointer &callback)
1163{
f3767a6a 1164 debugs(5, 3, HERE << "FD " << fd << " timeout " << timeout);
b0469965 1165 assert(fd >= 0);
1166 assert(fd < Squid_MaxFD);
1167 fde *F = &fd_table[fd];
1168 assert(F->flags.open);
1169
1170 if (timeout < 0) {
1171 F->timeoutHandler = NULL;
1172 F->timeout = 0;
1173 } else {
1174 if (callback != NULL) {
9e008dda
AJ
1175 typedef CommTimeoutCbParams Params;
1176 Params &params = GetCommParams<Params>(callback);
1177 params.fd = fd;
b0469965 1178 F->timeoutHandler = callback;
1179 }
1180
1181 F->timeout = squid_curtime + (time_t) timeout;
1182 }
1183
1184 return F->timeout;
1185
1186}
090089c4 1187
b8d8561b 1188int
ad61a2b4 1189comm_connect_addr(int sock, const IpAddress &address)
090089c4 1190{
3d7e9d7c 1191 comm_err_t status = COMM_OK;
76f87348 1192 fde *F = &fd_table[sock];
cc192b50 1193 int x = 0;
b5568a61 1194 int err = 0;
9689d97c 1195 socklen_t errlen;
feca3b9a 1196 struct addrinfo *AI = NULL;
88bfe092 1197 PROF_start(comm_connect_addr);
cc192b50 1198
1199 assert(address.GetPort() != 0);
1200
b0469965 1201 debugs(5, 9, "comm_connect_addr: connecting socket " << sock << " to " << address << " (want family: " << F->sock_family << ")");
cc192b50 1202
9d92af86
AJ
1203 /* BUG 2222 FIX: reset the FD when its found to be IPv4 in IPv6 mode */
1204 /* inverse case of IPv4 failing to connect on IPv6 socket is handeld post-connect.
1205 * this case must presently be handled here since the GetAddrInfo asserts on bad mappings.
1206 * eventually we want it to throw a Must() that gets handled there instead of this if.
1207 * NP: because commresetFD is private to ConnStateData we have to return an error and
1208 * trust its handled properly.
1209 */
1210#if USE_IPV6
9e008dda 1211 if (F->sock_family == AF_INET && !address.IsIPv4()) {
9d92af86
AJ
1212 return COMM_ERR_PROTOCOL;
1213 }
1214#endif
1215
feca3b9a 1216 address.GetAddrInfo(AI, F->sock_family);
cc192b50 1217
090089c4 1218 /* Establish connection. */
b5568a61 1219 errno = 0;
62e76326 1220
9e008dda 1221 if (!F->flags.called_connect) {
62e76326 1222 F->flags.called_connect = 1;
1223 statCounter.syscalls.sock.connects++;
1224
feca3b9a 1225 x = connect(sock, AI->ai_addr, AI->ai_addrlen);
62e76326 1226
5a33a66a 1227 // XXX: ICAP code refuses callbacks during a pending comm_ call
1228 // Async calls development will fix this.
1229 if (x == 0) {
1230 x = -1;
1231 errno = EINPROGRESS;
1232 }
1233
9e008dda 1234 if (x < 0) {
cc192b50 1235 debugs(5,5, "comm_connect_addr: sock=" << sock << ", addrinfo( " <<
9e008dda
AJ
1236 " flags=" << AI->ai_flags <<
1237 ", family=" << AI->ai_family <<
1238 ", socktype=" << AI->ai_socktype <<
1239 ", protocol=" << AI->ai_protocol <<
1240 ", &addr=" << AI->ai_addr <<
1241 ", addrlen=" << AI->ai_addrlen <<
1242 " )" );
cc192b50 1243 debugs(5, 9, "connect FD " << sock << ": (" << x << ") " << xstrerror());
1244 debugs(14,9, "connecting to: " << address );
1245 }
9e008dda 1246 } else {
140e2c0b 1247#if defined(_SQUID_NEWSOS6_)
62e76326 1248 /* Makoto MATSUSHITA <matusita@ics.es.osaka-u.ac.jp> */
1249
feca3b9a 1250 connect(sock, AI->ai_addr, AI->ai_addrlen);
62e76326 1251
1252 if (errno == EINVAL) {
1253 errlen = sizeof(err);
1254 x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
1255
1256 if (x >= 0)
1257 errno = x;
1258 }
1259
33ac9442 1260#else
62e76326 1261 errlen = sizeof(err);
1262
1263 x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
1264
1265 if (x == 0)
1266 errno = err;
1267
b5568a61 1268#if defined(_SQUID_SOLARIS_)
62e76326 1269 /*
1270 * Solaris 2.4's socket emulation doesn't allow you
1271 * to determine the error from a failed non-blocking
1272 * connect and just returns EPIPE. Create a fake
1273 * error message for connect. -- fenner@parc.xerox.com
1274 */
1275 if (x < 0 && errno == EPIPE)
1276 errno = ENOTCONN;
1277
33ac9442 1278#endif
30a4f2a8 1279#endif
62e76326 1280
e5f6c5c2 1281 }
62e76326 1282
9e008dda
AJ
1283 /* Squid seems to be working fine without this code. With this code,
1284 * we leak memory on many connect requests because of EINPROGRESS.
1285 * If you find that this code is needed, please file a bug report. */
82ec8dfc 1286#if 0
feca3b9a
AJ
1287#ifdef _SQUID_LINUX_
1288 /* 2007-11-27:
9e008dda 1289 * Linux Debian replaces our allocated AI pointer with garbage when
feca3b9a
AJ
1290 * connect() fails. This leads to segmentation faults deallocating
1291 * the system-allocated memory when we go to clean up our pointer.
1292 * HACK: is to leak the memory returned since we can't deallocate.
1293 */
9e008dda 1294 if (errno != 0) {
feca3b9a
AJ
1295 AI = NULL;
1296 }
82ec8dfc 1297#endif
feca3b9a
AJ
1298#endif
1299
1300 address.FreeAddrInfo(AI);
1301
88bfe092 1302 PROF_stop(comm_connect_addr);
62e76326 1303
b5568a61 1304 if (errno == 0 || errno == EISCONN)
62e76326 1305 status = COMM_OK;
b5568a61 1306 else if (ignoreErrno(errno))
62e76326 1307 status = COMM_INPROGRESS;
b5568a61 1308 else
cc192b50 1309#if USE_IPV6
9e008dda 1310 if ( address.IsIPv4() && F->sock_family == AF_INET6 ) {
cc192b50 1311
1312 /* failover to trying IPv4-only link if an IPv6 one fails */
1313 /* to catch the edge case of apps listening on IPv4-localhost */
9e008dda
AJ
1314 F->sock_family = AF_INET;
1315 int res = comm_connect_addr(sock, address);
cc192b50 1316
9e008dda
AJ
1317 /* if that fails too, undo our temporary socktype hack so the repeat works properly. */
1318 if (res == COMM_ERROR)
1319 F->sock_family = AF_INET6;
cc192b50 1320
9e008dda
AJ
1321 return res;
1322 } else
cc192b50 1323#endif
9e008dda 1324 return COMM_ERROR;
62e76326 1325
cc192b50 1326 address.NtoA(F->ipaddr, MAX_IPSTRLEN);
62e76326 1327
cc192b50 1328 F->remote_port = address.GetPort(); /* remote_port is HS */
62e76326 1329
9e008dda 1330 if (status == COMM_OK) {
cc192b50 1331 debugs(5, 10, "comm_connect_addr: FD " << sock << " connected to " << address);
9e008dda 1332 } else if (status == COMM_INPROGRESS) {
bf8fe701 1333 debugs(5, 10, "comm_connect_addr: FD " << sock << " connection pending");
090089c4 1334 }
62e76326 1335
090089c4 1336 return status;
1337}
1338
1339/* Wait for an incoming connection on FD. FD should be a socket returned
1340 * from comm_listen. */
ee0989f2 1341static int
1342comm_old_accept(int fd, ConnectionDetail &details)
090089c4 1343{
88bfe092 1344 PROF_start(comm_accept);
ee0989f2 1345 statCounter.syscalls.sock.accepts++;
1346 int sock;
cc192b50 1347 struct addrinfo *gai = NULL;
1348 details.me.InitAddrInfo(gai);
1349
1350 if ((sock = accept(fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
1351
1352 details.me.FreeAddrInfo(gai);
62e76326 1353
62e76326 1354 PROF_stop(comm_accept);
1355
9e008dda 1356 if (ignoreErrno(errno)) {
bf8fe701 1357 debugs(50, 5, "comm_old_accept: FD " << fd << ": " << xstrerror());
62e76326 1358 return COMM_NOMESSAGE;
9e008dda 1359 } else if (ENFILE == errno || EMFILE == errno) {
bf8fe701 1360 debugs(50, 3, "comm_old_accept: FD " << fd << ": " << xstrerror());
62e76326 1361 return COMM_ERROR;
9e008dda 1362 } else {
bf8fe701 1363 debugs(50, 1, "comm_old_accept: FD " << fd << ": " << xstrerror());
62e76326 1364 return COMM_ERROR;
1365 }
090089c4 1366 }
62e76326 1367
cc192b50 1368 details.peer = *gai;
1369
3be4d5d1 1370 details.me.InitAddrInfo(gai);
1371
cc192b50 1372 details.me.SetEmpty();
1373 getsockname(sock, gai->ai_addr, &gai->ai_addrlen);
1374 details.me = *gai;
62e76326 1375
3ca60c86 1376 commSetCloseOnExec(sock);
cc192b50 1377
090089c4 1378 /* fdstat update */
5c5783a2 1379 fd_open(sock, FD_SOCKET, "HTTP Request");
c4b7a5a9 1380 fdd_table[sock].close_file = NULL;
1381 fdd_table[sock].close_line = 0;
ee0989f2 1382 fde *F = &fd_table[sock];
cc192b50 1383 details.peer.NtoA(F->ipaddr,MAX_IPSTRLEN);
1384 F->remote_port = details.peer.GetPort();
1385 F->local_addr.SetPort(details.me.GetPort());
3be4d5d1 1386#if USE_IPV6
1387 F->sock_family = AF_INET;
1388#else
1389 F->sock_family = details.me.IsIPv4()?AF_INET:AF_INET6;
1390#endif
1391 details.me.FreeAddrInfo(gai);
cc192b50 1392
090089c4 1393 commSetNonBlocking(sock);
cc192b50 1394
acaa7194
AJ
1395 /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
1396 F->flags.transparent = fd_table[fd].flags.transparent;
72d57dea 1397
88bfe092 1398 PROF_stop(comm_accept);
090089c4 1399 return sock;
1400}
1401
cb201b7e 1402void
1403commCallCloseHandlers(int fd)
1404{
76f87348 1405 fde *F = &fd_table[fd];
bf8fe701 1406 debugs(5, 5, "commCallCloseHandlers: FD " << fd);
62e76326 1407
8000a965 1408 while (F->closeHandler != NULL) {
b0469965 1409 AsyncCall::Pointer call = F->closeHandler;
9e008dda
AJ
1410 F->closeHandler = call->Next();
1411 call->setNext(NULL);
1412 // If call is not canceled schedule it for execution else ignore it
1413 if (!call->canceled()) {
1414 debugs(5, 5, "commCallCloseHandlers: ch->handler=" << call);
1415 typedef CommCloseCbParams Params;
1416 Params &params = GetCommParams<Params>(call);
1417 params.fd = fd;
1418 ScheduleCallHere(call);
1419 }
cb201b7e 1420 }
1421}
1422
5492ad1d 1423#if LINGERING_CLOSE
1424static void
1425commLingerClose(int fd, void *unused)
1426{
1427 LOCAL_ARRAY(char, buf, 1024);
1428 int n;
1f7c9178 1429 n = FD_READ_METHOD(fd, buf, 1024);
62e76326 1430
5492ad1d 1431 if (n < 0)
bf8fe701 1432 debugs(5, 3, "commLingerClose: FD " << fd << " read: " << xstrerror());
62e76326 1433
5492ad1d 1434 comm_close(fd);
1435}
1436
1437static void
1438commLingerTimeout(int fd, void *unused)
1439{
bf8fe701 1440 debugs(5, 3, "commLingerTimeout: FD " << fd);
5492ad1d 1441 comm_close(fd);
1442}
1443
1444/*
1445 * Inspired by apache
1446 */
1447void
1448comm_lingering_close(int fd)
1449{
d4c19b39 1450#if USE_SSL
62e76326 1451
d4c19b39 1452 if (fd_table[fd].ssl)
62e76326 1453 ssl_shutdown_method(fd);
1454
d4c19b39 1455#endif
62e76326 1456
5492ad1d 1457 if (shutdown(fd, 1) < 0) {
62e76326 1458 comm_close(fd);
1459 return;
5492ad1d 1460 }
62e76326 1461
5492ad1d 1462 fd_note(fd, "lingering close");
1463 commSetTimeout(fd, 10, commLingerTimeout, NULL);
1464 commSetSelect(fd, COMM_SELECT_READ, commLingerClose, NULL, 0);
1465}
62e76326 1466
5492ad1d 1467#endif
1468
98264874 1469/*
1470 * enable linger with time of 0 so that when the socket is
1471 * closed, TCP generates a RESET
1472 */
1473void
1474comm_reset_close(int fd)
1475{
62e76326 1476
98264874 1477 struct linger L;
1478 L.l_onoff = 1;
1479 L.l_linger = 0;
62e76326 1480
98264874 1481 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
bf8fe701 1482 debugs(50, 0, "commResetTCPClose: FD " << fd << ": " << xstrerror());
62e76326 1483
98264874 1484 comm_close(fd);
1485}
1486
9e008dda 1487void
10b06767
AJ
1488comm_close_start(int fd, void *data)
1489{
10b06767 1490#if USE_SSL
86dedcc1 1491 fde *F = &fd_table[fd];
10b06767
AJ
1492 if (F->ssl)
1493 ssl_shutdown_method(fd);
1494
1495#endif
1496
1497}
1498
1499
9e008dda 1500void
b0469965 1501comm_close_complete(int fd, void *data)
2d8c0b1a 1502{
b0469965 1503#if USE_SSL
1504 fde *F = &fd_table[fd];
2d8c0b1a 1505
b0469965 1506 if (F->ssl) {
1507 SSL_free(F->ssl);
1508 F->ssl = NULL;
1509 }
2d8c0b1a 1510
b0469965 1511#endif
1512 fd_close(fd); /* update fdstat */
1513
1514 close(fd);
1515
b0469965 1516 fdc_table[fd] = AcceptFD(fd);
1517
1518 statCounter.syscalls.sock.closes++;
1519
1520 /* When an fd closes, give accept() a chance, if need be */
1521
1522 if (fdNFree() >= RESERVED_FD)
1523 AcceptLimiter::Instance().kick();
2d8c0b1a 1524
2d8c0b1a 1525}
c4b7a5a9 1526
1527/*
1528 * Close the socket fd.
1529 *
1530 * + call write handlers with ERR_CLOSING
1531 * + call read handlers with ERR_CLOSING
1532 * + call closing handlers
a46d2c0e 1533 *
9e008dda 1534 * NOTE: COMM_ERR_CLOSING will NOT be called for CommReads' sitting in a
a46d2c0e 1535 * DeferredReadManager.
c4b7a5a9 1536 */
b8d8561b 1537void
43ae1d95 1538_comm_close(int fd, char const *file, int line)
090089c4 1539{
82ec8dfc 1540 debugs(5, 3, "comm_close: start closing FD " << fd);
03eb2f01 1541 assert(fd >= 0);
1542 assert(fd < Squid_MaxFD);
82ec8dfc
AR
1543
1544 fde *F = &fd_table[fd];
c4b7a5a9 1545 fdd_table[fd].close_file = file;
1546 fdd_table[fd].close_line = line;
1f7c9178 1547
82ec8dfc 1548 if (F->closing())
62e76326 1549 return;
1550
36afac26 1551 /* XXX: is this obsolete behind F->closing() ? */
b8869bcf 1552 if ( (shutting_down || reconfiguring) && (!F->flags.open || F->type == FD_FILE))
62e76326 1553 return;
1554
c4b7a5a9 1555 /* The following fails because ipc.c is doing calls to pipe() to create sockets! */
b0469965 1556 assert(isOpen(fd));
62e76326 1557
76f87348 1558 assert(F->type != FD_FILE);
62e76326 1559
88bfe092 1560 PROF_start(comm_close);
62e76326 1561
10b06767 1562 F->flags.close_request = 1;
62e76326 1563
10b06767 1564 AsyncCall::Pointer startCall=commCbCall(5,4, "comm_close_start",
9e008dda 1565 CommCloseCbPtrFun(comm_close_start, NULL));
10b06767
AJ
1566 typedef CommCloseCbParams Params;
1567 Params &startParams = GetCommParams<Params>(startCall);
1568 startParams.fd = fd;
1569 ScheduleCallHere(startCall);
62e76326 1570
74257126
AR
1571 // a half-closed fd may lack a reader, so we stop monitoring explicitly
1572 if (commHasHalfClosedMonitor(fd))
1573 commStopHalfClosedMonitor(fd);
fa80a8ef 1574 commSetTimeout(fd, -1, NULL, NULL);
62e76326 1575
82ec8dfc 1576 // notify read/write handlers
2b663917 1577 if (commio_has_callback(fd, IOCB_WRITE, COMMIO_FD_WRITECB(fd))) {
b0469965 1578 commio_finish_callback(fd, COMMIO_FD_WRITECB(fd), COMM_ERR_CLOSING, errno);
2b663917 1579 }
1580 if (commio_has_callback(fd, IOCB_READ, COMMIO_FD_READCB(fd))) {
b0469965 1581 commio_finish_callback(fd, COMMIO_FD_READCB(fd), COMM_ERR_CLOSING, errno);
2b663917 1582 }
2d8c0b1a 1583
82ec8dfc 1584 // notify accept handlers
b0469965 1585 fdc_table[fd].notify(-1, COMM_ERR_CLOSING, 0, ConnectionDetail());
c4b7a5a9 1586
cb201b7e 1587 commCallCloseHandlers(fd);
62e76326 1588
781ce8ff 1589 if (F->pconn.uses)
1590 F->pconn.pool->count(F->pconn.uses);
62e76326 1591
a7ad6e4e 1592 comm_empty_os_read_buffers(fd);
9e008dda 1593
62e76326 1594
10b06767 1595 AsyncCall::Pointer completeCall=commCbCall(5,4, "comm_close_complete",
9e008dda 1596 CommCloseCbPtrFun(comm_close_complete, NULL));
10b06767
AJ
1597 Params &completeParams = GetCommParams<Params>(completeCall);
1598 completeParams.fd = fd;
9e008dda 1599 // must use async call to wait for all callbacks
82ec8dfc 1600 // scheduled before comm_close() to finish
10b06767 1601 ScheduleCallHere(completeCall);
62e76326 1602
88bfe092 1603 PROF_stop(comm_close);
090089c4 1604}
1605
090089c4 1606/* Send a udp datagram to specified TO_ADDR. */
b8d8561b 1607int
5df61230 1608comm_udp_sendto(int fd,
ad61a2b4 1609 const IpAddress &to_addr,
62e76326 1610 const void *buf,
1611 int len)
090089c4 1612{
cc192b50 1613 int x = 0;
1614 struct addrinfo *AI = NULL;
1615
88bfe092 1616 PROF_start(comm_udp_sendto);
83704487 1617 statCounter.syscalls.sock.sendtos++;
62e76326 1618
cc192b50 1619 debugs(50, 3, "comm_udp_sendto: Attempt to send UDP packet to " << to_addr <<
9e008dda 1620 " using FD " << fd << " using Port " << comm_local_port(fd) );
cc192b50 1621
1622 /* BUG: something in the above macro appears to occasionally be setting AI to garbage. */
1623 /* AYJ: 2007-08-27 : or was it because I wasn't then setting 'fd_table[fd].sock_family' to fill properly. */
1624 assert( NULL == AI );
1625
1626 to_addr.GetAddrInfo(AI, fd_table[fd].sock_family);
1627
1628 x = sendto(fd, buf, len, 0, AI->ai_addr, AI->ai_addrlen);
1629
1630 to_addr.FreeAddrInfo(AI);
1631
88bfe092 1632 PROF_stop(comm_udp_sendto);
62e76326 1633
2d8c0b1a 1634 if (x >= 0)
1635 return x;
1636
17d51783 1637#ifdef _SQUID_LINUX_
62e76326 1638
2d8c0b1a 1639 if (ECONNREFUSED != errno)
17d51783 1640#endif
62e76326 1641
cc192b50 1642 debugs(50, 1, "comm_udp_sendto: FD " << fd << ", (family=" << fd_table[fd].sock_family << ") " << to_addr << ": " << xstrerror());
62e76326 1643
2d8c0b1a 1644 return COMM_ERROR;
090089c4 1645}
1646
b8d8561b 1647void
582b6456 1648comm_add_close_handler(int fd, PF * handler, void *data)
30a4f2a8 1649{
bf8fe701 1650 debugs(5, 5, "comm_add_close_handler: FD " << fd << ", handler=" <<
1651 handler << ", data=" << data);
62e76326 1652
b0469965 1653 AsyncCall::Pointer call=commCbCall(5,4, "SomeCloseHandler",
9e008dda 1654 CommCloseCbPtrFun(handler, data));
b0469965 1655 comm_add_close_handler(fd, call);
1656}
62e76326 1657
b0469965 1658void
1659comm_add_close_handler(int fd, AsyncCall::Pointer &call)
1660{
1661 debugs(5, 5, "comm_add_close_handler: FD " << fd << ", AsyncCall=" << call);
62e76326 1662
b0469965 1663 /*TODO:Check for a similar scheduled AsyncCall*/
1664// for (c = fd_table[fd].closeHandler; c; c = c->next)
1665// assert(c->handler != handler || c->data != data);
62e76326 1666
b0469965 1667 call->setNext(fd_table[fd].closeHandler);
62e76326 1668
b0469965 1669 fd_table[fd].closeHandler = call;
30a4f2a8 1670}
1671
b0469965 1672
1673// remove function-based close handler
b8d8561b 1674void
582b6456 1675comm_remove_close_handler(int fd, PF * handler, void *data)
090089c4 1676{
b0469965 1677 assert (isOpen(fd));
30a4f2a8 1678 /* Find handler in list */
bf8fe701 1679 debugs(5, 5, "comm_remove_close_handler: FD " << fd << ", handler=" <<
1680 handler << ", data=" << data);
62e76326 1681
b0469965 1682 AsyncCall::Pointer p;
9e008dda 1683 for (p = fd_table[fd].closeHandler; p != NULL; p = p->Next()) {
b0469965 1684 typedef CommCbFunPtrCallT<CommCloseCbPtrFun> Call;
1685 const Call *call = dynamic_cast<const Call*>(p.getRaw());
1686 if (!call) // method callbacks have their own comm_remove_close_handler
1687 continue;
62e76326 1688
b0469965 1689 typedef CommCloseCbParams Params;
1690 const Params &params = GetCommParams<Params>(p);
1691 if (call->dialer.handler == handler && params.data == data)
1692 break; /* This is our handler */
1693 }
7828df5b
CT
1694
1695 // comm_close removes all close handlers so our handler may be gone
1696 if (p != NULL)
1697 p->cancel("comm_remove_close_handler");
1698 // TODO: should we remove the handler from the close handlers list?
b0469965 1699}
62e76326 1700
b0469965 1701// remove method-based close handler
1702void
1703comm_remove_close_handler(int fd, AsyncCall::Pointer &call)
1704{
1705 assert (isOpen(fd));
b0469965 1706 debugs(5, 5, "comm_remove_close_handler: FD " << fd << ", AsyncCall=" << call);
62e76326 1707
7828df5b
CT
1708 // comm_close removes all close handlers so our handler may be gone
1709 // TODO: should we remove the handler from the close handlers list?
1710#if 0
b0469965 1711 // Check to see if really exist the given AsyncCall in comm_close handlers
1712 // TODO: optimize: this slow code is only needed for the assert() below
1713 AsyncCall::Pointer p;
1714 for (p = fd_table[fd].closeHandler; p != NULL && p != call; p = p->Next());
1715 assert(p == call);
7828df5b 1716#endif
62e76326 1717
b0469965 1718 call->cancel("comm_remove_close_handler");
30a4f2a8 1719}
090089c4 1720
b8d8561b 1721static void
1722commSetNoLinger(int fd)
30a4f2a8 1723{
62e76326 1724
30a4f2a8 1725 struct linger L;
090089c4 1726 L.l_onoff = 0; /* off */
1727 L.l_linger = 0;
62e76326 1728
30a4f2a8 1729 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
bf8fe701 1730 debugs(50, 0, "commSetNoLinger: FD " << fd << ": " << xstrerror());
62e76326 1731
58a6c186 1732 fd_table[fd].flags.nolinger = 1;
090089c4 1733}
1734
b8d8561b 1735static void
1736commSetReuseAddr(int fd)
090089c4 1737{
1738 int on = 1;
62e76326 1739
30a4f2a8 1740 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0)
bf8fe701 1741 debugs(50, 1, "commSetReuseAddr: FD " << fd << ": " << xstrerror());
090089c4 1742}
1743
b8d8561b 1744static void
1745commSetTcpRcvbuf(int fd, int size)
f868539a 1746{
1747 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0)
bf8fe701 1748 debugs(50, 1, "commSetTcpRcvbuf: FD " << fd << ", SIZE " << size << ": " << xstrerror());
8f0d53ef 1749 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof(size)) < 0)
1750 debugs(50, 1, "commSetTcpRcvbuf: FD " << fd << ", SIZE " << size << ": " << xstrerror());
1751#ifdef TCP_WINDOW_CLAMP
1752 if (setsockopt(fd, SOL_TCP, TCP_WINDOW_CLAMP, (char *) &size, sizeof(size)) < 0)
1753 debugs(50, 1, "commSetTcpRcvbuf: FD " << fd << ", SIZE " << size << ": " << xstrerror());
1754#endif
f868539a 1755}
1756
b8d8561b 1757int
1758commSetNonBlocking(int fd)
30a4f2a8 1759{
a50bfe93 1760#ifndef _SQUID_MSWIN_
731e4d49 1761 int flags;
9e205701 1762 int dummy = 0;
a50bfe93 1763#endif
ec4daaa5 1764#ifdef _SQUID_WIN32_
62e76326 1765
b05490a8 1766 int nonblocking = TRUE;
62e76326 1767
629b5f75 1768#ifdef _SQUID_CYGWIN_
1769
7f6ffd15 1770 if (fd_table[fd].type != FD_PIPE) {
629b5f75 1771#endif
1772
62e76326 1773 if (ioctl(fd, FIONBIO, &nonblocking) < 0) {
bf8fe701 1774 debugs(50, 0, "commSetNonBlocking: FD " << fd << ": " << xstrerror() << " " << fd_table[fd].type);
62e76326 1775 return COMM_ERROR;
1776 }
629b5f75 1777
1778#ifdef _SQUID_CYGWIN_
1779
7f6ffd15 1780 } else {
1781#endif
629b5f75 1782#endif
a50bfe93 1783#ifndef _SQUID_MSWIN_
62e76326 1784
1785 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
bf8fe701 1786 debugs(50, 0, "FD " << fd << ": fcntl F_GETFL: " << xstrerror());
62e76326 1787 return COMM_ERROR;
1788 }
1789
1790 if (fcntl(fd, F_SETFL, flags | SQUID_NONBLOCK) < 0) {
bf8fe701 1791 debugs(50, 0, "commSetNonBlocking: FD " << fd << ": " << xstrerror());
62e76326 1792 return COMM_ERROR;
1793 }
1794
a50bfe93 1795#endif
629b5f75 1796#ifdef _SQUID_CYGWIN_
62e76326 1797
090089c4 1798 }
62e76326 1799
7f6ffd15 1800#endif
58a6c186 1801 fd_table[fd].flags.nonblocking = 1;
62e76326 1802
090089c4 1803 return 0;
1804}
1805
7e3ce7b9 1806int
1807commUnsetNonBlocking(int fd)
1808{
a50bfe93 1809#ifdef _SQUID_MSWIN_
1810 int nonblocking = FALSE;
1811
1812 if (ioctlsocket(fd, FIONBIO, (unsigned long *) &nonblocking) < 0) {
1813#else
7e3ce7b9 1814 int flags;
1815 int dummy = 0;
62e76326 1816
7e3ce7b9 1817 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
bf8fe701 1818 debugs(50, 0, "FD " << fd << ": fcntl F_GETFL: " << xstrerror());
62e76326 1819 return COMM_ERROR;
7e3ce7b9 1820 }
62e76326 1821
7e3ce7b9 1822 if (fcntl(fd, F_SETFL, flags & (~SQUID_NONBLOCK)) < 0) {
a50bfe93 1823#endif
bf8fe701 1824 debugs(50, 0, "commUnsetNonBlocking: FD " << fd << ": " << xstrerror());
62e76326 1825 return COMM_ERROR;
7e3ce7b9 1826 }
62e76326 1827
7e3ce7b9 1828 fd_table[fd].flags.nonblocking = 0;
1829 return 0;
1830}
1831
b8d8561b 1832void
a50bfe93 1833commSetCloseOnExec(int fd) {
3ca60c86 1834#ifdef FD_CLOEXEC
731e4d49 1835 int flags;
7a18b487 1836 int dummy = 0;
62e76326 1837
c7989865 1838 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
bf8fe701 1839 debugs(50, 0, "FD " << fd << ": fcntl F_GETFL: " << xstrerror());
62e76326 1840 return;
3ca60c86 1841 }
62e76326 1842
24382924 1843 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
bf8fe701 1844 debugs(50, 0, "FD " << fd << ": set close-on-exec failed: " << xstrerror());
62e76326 1845
d6827718 1846 fd_table[fd].flags.close_on_exec = 1;
62e76326 1847
3ca60c86 1848#endif
1849}
1850
e90100aa 1851#ifdef TCP_NODELAY
1852static void
a50bfe93 1853commSetTcpNoDelay(int fd) {
e90100aa 1854 int on = 1;
62e76326 1855
e90100aa 1856 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0)
bf8fe701 1857 debugs(50, 1, "commSetTcpNoDelay: FD " << fd << ": " << xstrerror());
62e76326 1858
d6827718 1859 fd_table[fd].flags.nodelay = 1;
e90100aa 1860}
62e76326 1861
e90100aa 1862#endif
1863
b2130d58 1864void
9e008dda 1865commSetTcpKeepalive(int fd, int idle, int interval, int timeout) {
b2130d58 1866 int on = 1;
1867#ifdef TCP_KEEPCNT
1868 if (timeout && interval) {
9e008dda
AJ
1869 int count = (timeout + interval - 1) / interval;
1870 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(on)) < 0)
1871 debugs(5, 1, "commSetKeepalive: FD " << fd << ": " << xstrerror());
b2130d58 1872 }
1873#endif
1874#ifdef TCP_KEEPIDLE
1875 if (idle) {
9e008dda
AJ
1876 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(on)) < 0)
1877 debugs(5, 1, "commSetKeepalive: FD " << fd << ": " << xstrerror());
b2130d58 1878 }
1879#endif
1880#ifdef TCP_KEEPINTVL
1881 if (interval) {
9e008dda
AJ
1882 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(on)) < 0)
1883 debugs(5, 1, "commSetKeepalive: FD " << fd << ": " << xstrerror());
b2130d58 1884 }
1885#endif
1886 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0)
9e008dda 1887 debugs(5, 1, "commSetKeepalive: FD " << fd << ": " << xstrerror());
b2130d58 1888}
6a988308 1889
d86b3703 1890void
a50bfe93 1891comm_init(void) {
c4b7a5a9 1892 fd_table =(fde *) xcalloc(Squid_MaxFD, sizeof(fde));
1893 fdd_table = (fd_debug_t *)xcalloc(Squid_MaxFD, sizeof(fd_debug_t));
2d8c0b1a 1894
b0469965 1895 fdc_table = new AcceptFD[Squid_MaxFD];
2b663917 1896 for (int pos = 0; pos < Squid_MaxFD; ++pos) {
b0469965 1897 fdc_table[pos] = AcceptFD(pos);
2b663917 1898 }
b0469965 1899
1900 commfd_table = (comm_fd_t *) xcalloc(Squid_MaxFD, sizeof(comm_fd_t));
2b663917 1901 for (int pos = 0; pos < Squid_MaxFD; pos++) {
9e008dda
AJ
1902 commfd_table[pos].fd = pos;
1903 commfd_table[pos].readcb.fd = pos;
1904 commfd_table[pos].readcb.type = IOCB_READ;
1905 commfd_table[pos].writecb.fd = pos;
1906 commfd_table[pos].writecb.type = IOCB_WRITE;
2b663917 1907 }
2d8c0b1a 1908
59c4d35b 1909 /* XXX account fd_table */
090089c4 1910 /* Keep a few file descriptors free so that we don't run out of FD's
1911 * after accepting a client but before it opens a socket or a file.
e83892e9 1912 * Since Squid_MaxFD can be as high as several thousand, don't waste them */
d85c3078 1913 RESERVED_FD = min(100, Squid_MaxFD / 4);
2d8c0b1a 1914
04eb0689 1915 conn_close_pool = memPoolCreate("close_handler", sizeof(close_handler));
74257126
AR
1916
1917 TheHalfClosed = new DescriptorSet;
090089c4 1918}
1919
236d1779 1920void
1921comm_exit(void) {
74257126
AR
1922 delete TheHalfClosed;
1923 TheHalfClosed = NULL;
1924
236d1779 1925 safe_free(fd_table);
1926 safe_free(fdd_table);
1927 if (fdc_table) {
9e008dda
AJ
1928 delete[] fdc_table;
1929 fdc_table = NULL;
236d1779 1930 }
1931 safe_free(commfd_table);
1932}
1933
30a4f2a8 1934/* Write to FD. */
b8d8561b 1935static void
a50bfe93 1936commHandleWrite(int fd, void *data) {
2b663917 1937 comm_io_callback_t *state = (comm_io_callback_t *)data;
30a4f2a8 1938 int len = 0;
1939 int nleft;
1940
2b663917 1941 assert(state == COMMIO_FD_WRITECB(fd));
1942
88bfe092 1943 PROF_start(commHandleWrite);
bf8fe701 1944 debugs(5, 5, "commHandleWrite: FD " << fd << ": off " <<
1945 (long int) state->offset << ", sz " << (long int) state->size << ".");
30a4f2a8 1946
1947 nleft = state->size - state->offset;
1f7c9178 1948 len = FD_WRITE_METHOD(fd, state->buf + state->offset, nleft);
bf8fe701 1949 debugs(5, 5, "commHandleWrite: write() returns " << len);
b69f7771 1950 fd_bytes(fd, len, FD_WRITE);
83704487 1951 statCounter.syscalls.sock.writes++;
30a4f2a8 1952
1953 if (len == 0) {
62e76326 1954 /* Note we even call write if nleft == 0 */
1955 /* We're done */
1956
1957 if (nleft != 0)
bf8fe701 1958 debugs(5, 1, "commHandleWrite: FD " << fd << ": write failure: connection closed with " << nleft << " bytes remaining.");
62e76326 1959
b0469965 1960 commio_finish_callback(fd, COMMIO_FD_WRITECB(fd), nleft ? COMM_ERROR : COMM_OK, errno);
30a4f2a8 1961 } else if (len < 0) {
62e76326 1962 /* An error */
1963
1964 if (fd_table[fd].flags.socket_eof) {
bf8fe701 1965 debugs(50, 2, "commHandleWrite: FD " << fd << ": write failure: " << xstrerror() << ".");
b0469965 1966 commio_finish_callback(fd, COMMIO_FD_WRITECB(fd), nleft ? COMM_ERROR : COMM_OK, errno);
62e76326 1967 } else if (ignoreErrno(errno)) {
bf8fe701 1968 debugs(50, 10, "commHandleWrite: FD " << fd << ": write failure: " << xstrerror() << ".");
62e76326 1969 commSetSelect(fd,
1970 COMM_SELECT_WRITE,
1971 commHandleWrite,
1972 state,
1973 0);
1974 } else {
bf8fe701 1975 debugs(50, 2, "commHandleWrite: FD " << fd << ": write failure: " << xstrerror() << ".");
b0469965 1976 commio_finish_callback(fd, COMMIO_FD_WRITECB(fd), nleft ? COMM_ERROR : COMM_OK, errno);
62e76326 1977 }
30a4f2a8 1978 } else {
62e76326 1979 /* A successful write, continue */
1980 state->offset += len;
1981
57d55dfa 1982 if (state->offset < state->size) {
62e76326 1983 /* Not done, reinstall the write handler and write some more */
1984 commSetSelect(fd,
1985 COMM_SELECT_WRITE,
1986 commHandleWrite,
1987 state,
1988 0);
1989 } else {
b0469965 1990 commio_finish_callback(fd, COMMIO_FD_WRITECB(fd), nleft ? COMM_OK : COMM_ERROR, errno);
62e76326 1991 }
30a4f2a8 1992 }
62e76326 1993
88bfe092 1994 PROF_stop(commHandleWrite);
30a4f2a8 1995}
1996
7cd8c414 1997/*
1998 * Queue a write. handler/handler_data are called when the write
1999 * completes, on error, or on file descriptor close.
2000 *
2001 * free_func is used to free the passed buffer when the write has completed.
2002 */
b8d8561b 2003void
9e008dda 2004comm_write(int fd, const char *buf, int size, IOCB * handler, void *handler_data, FREE * free_func) {
b0469965 2005 AsyncCall::Pointer call = commCbCall(5,5, "SomeCommWriteHander",
9e008dda 2006 CommIoCbPtrFun(handler, handler_data));
b0469965 2007
2008 comm_write(fd, buf, size, call, free_func);
2009}
2010
2011void
9e008dda 2012comm_write(int fd, const char *buf, int size, AsyncCall::Pointer &callback, FREE * free_func) {
82ec8dfc 2013 debugs(5, 5, "comm_write: FD " << fd << ": sz " << size << ": asynCall " << callback);
62e76326 2014
82ec8dfc
AR
2015 /* Make sure we are open, not closing, and not writing */
2016 assert(isOpen(fd));
2017 assert(!fd_table[fd].closing());
2018 comm_io_callback_t *ccb = COMMIO_FD_WRITECB(fd);
2019 assert(!ccb->active());
b0469965 2020
82ec8dfc
AR
2021 /* Queue the write */
2022 commio_set_callback(fd, IOCB_WRITE, ccb, callback,
9e008dda 2023 (char *)buf, free_func, size);
82ec8dfc 2024 commSetSelect(fd, COMM_SELECT_WRITE, commHandleWrite, ccb, 0);
30a4f2a8 2025}
26a880e2 2026
b0469965 2027
137ee196 2028/* a wrapper around comm_write to allow for MemBuf to be comm_written in a snap */
cb69b4c7 2029void
2b663917 2030comm_write_mbuf(int fd, MemBuf *mb, IOCB * handler, void *handler_data) {
2031 comm_write(fd, mb->buf, mb->size, handler, handler_data, mb->freeFunc());
cb69b4c7 2032}
2033
b0469965 2034void
2035comm_write_mbuf(int fd, MemBuf *mb, AsyncCall::Pointer &callback) {
2036 comm_write(fd, mb->buf, mb->size, callback, mb->freeFunc());
2037}
2038
c4b7a5a9 2039
89924214 2040/*
2041 * hm, this might be too general-purpose for all the places we'd
2042 * like to use it.
2043 */
b224ea98 2044int
a50bfe93 2045ignoreErrno(int ierrno) {
603500e7 2046 switch (ierrno) {
62e76326 2047
89924214 2048 case EINPROGRESS:
62e76326 2049
603500e7 2050 case EWOULDBLOCK:
26a880e2 2051#if EAGAIN != EWOULDBLOCK
62e76326 2052
603500e7 2053 case EAGAIN:
26a880e2 2054#endif
62e76326 2055
603500e7 2056 case EALREADY:
62e76326 2057
603500e7 2058 case EINTR:
db494ab8 2059#ifdef ERESTART
62e76326 2060
db494ab8 2061 case ERESTART:
2062#endif
62e76326 2063
2064 return 1;
2065
603500e7 2066 default:
62e76326 2067 return 0;
603500e7 2068 }
62e76326 2069
603500e7 2070 /* NOTREACHED */
26a880e2 2071}
d723bf6b 2072
2073void
a50bfe93 2074commCloseAllSockets(void) {
d723bf6b 2075 int fd;
2076 fde *F = NULL;
62e76326 2077
d723bf6b 2078 for (fd = 0; fd <= Biggest_FD; fd++) {
62e76326 2079 F = &fd_table[fd];
2080
2081 if (!F->flags.open)
2082 continue;
2083
2084 if (F->type != FD_SOCKET)
2085 continue;
2086
2087 if (F->flags.ipc) /* don't close inter-process sockets */
2088 continue;
2089
b0469965 2090 if (F->timeoutHandler != NULL) {
2091 AsyncCall::Pointer callback = F->timeoutHandler;
2092 F->timeoutHandler = NULL;
bf8fe701 2093 debugs(5, 5, "commCloseAllSockets: FD " << fd << ": Calling timeout handler");
9e008dda 2094 ScheduleCallHere(callback);
62e76326 2095 } else {
bf8fe701 2096 debugs(5, 5, "commCloseAllSockets: FD " << fd << ": calling comm_close()");
62e76326 2097 comm_close(fd);
2098 }
d723bf6b 2099 }
2100}
1b3db6d9 2101
2d8c0b1a 2102static bool
a50bfe93 2103AlreadyTimedOut(fde *F) {
2d8c0b1a 2104 if (!F->flags.open)
2105 return true;
2106
2107 if (F->timeout == 0)
2108 return true;
2109
2110 if (F->timeout > squid_curtime)
2111 return true;
2112
2113 return false;
2114}
2115
1b3db6d9 2116void
a50bfe93 2117checkTimeouts(void) {
1b3db6d9 2118 int fd;
2119 fde *F = NULL;
b0469965 2120 AsyncCall::Pointer callback;
62e76326 2121
1b3db6d9 2122 for (fd = 0; fd <= Biggest_FD; fd++) {
62e76326 2123 F = &fd_table[fd];
2124
2d8c0b1a 2125 if (AlreadyTimedOut(F))
62e76326 2126 continue;
2127
9e008dda 2128 debugs(5, 5, "checkTimeouts: FD " << fd << " Expired");
62e76326 2129
b0469965 2130 if (F->timeoutHandler != NULL) {
bf8fe701 2131 debugs(5, 5, "checkTimeouts: FD " << fd << ": Call timeout handler");
b0469965 2132 callback = F->timeoutHandler;
2133 F->timeoutHandler = NULL;
9e008dda 2134 ScheduleCallHere(callback);
62e76326 2135 } else {
bf8fe701 2136 debugs(5, 5, "checkTimeouts: FD " << fd << ": Forcing comm_close()");
62e76326 2137 comm_close(fd);
2138 }
b5443c04 2139 }
2140}
2141
c4b7a5a9 2142/*
2143 * New-style listen and accept routines
2144 *
2145 * Listen simply registers our interest in an FD for listening,
2146 * and accept takes a callback to call when an FD has been
2147 * accept()ed.
2148 */
2149int
a50bfe93 2150comm_listen(int sock) {
c4b7a5a9 2151 int x;
62e76326 2152
c4b7a5a9 2153 if ((x = listen(sock, Squid_MaxFD >> 2)) < 0) {
bf8fe701 2154 debugs(50, 0, "comm_listen: listen(" << (Squid_MaxFD >> 2) << ", " << sock << "): " << xstrerror());
62e76326 2155 return x;
c4b7a5a9 2156 }
62e76326 2157
0b4d4be5 2158 if (Config.accept_filter && strcmp(Config.accept_filter, "none") != 0) {
cc9f92d4 2159#ifdef SO_ACCEPTFILTER
9e008dda
AJ
2160 struct accept_filter_arg afa;
2161 bzero(&afa, sizeof(afa));
2162 debugs(5, DBG_CRITICAL, "Installing accept filter '" << Config.accept_filter << "' on FD " << sock);
2163 xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name));
2164 x = setsockopt(sock, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
2165 if (x < 0)
2166 debugs(5, 0, "SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerror());
0b4d4be5 2167#elif defined(TCP_DEFER_ACCEPT)
9e008dda
AJ
2168 int seconds = 30;
2169 if (strncmp(Config.accept_filter, "data=", 5) == 0)
2170 seconds = atoi(Config.accept_filter + 5);
2171 x = setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds));
2172 if (x < 0)
2173 debugs(5, 0, "TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerror());
0b4d4be5 2174#else
9e008dda 2175 debugs(5, 0, "accept_filter not supported on your OS");
cc9f92d4 2176#endif
0b4d4be5 2177 }
cc9f92d4 2178
c4b7a5a9 2179 return sock;
2180}
2181
2d8c0b1a 2182void
b0469965 2183comm_accept(int fd, IOACB *handler, void *handler_data) {
2184 debugs(5, 5, "comm_accept: FD " << fd << " handler: " << (void*)handler);
2185 assert(isOpen(fd));
2186
2187 AsyncCall::Pointer call = commCbCall(5,5, "SomeCommAcceptHandler",
9e008dda 2188 CommAcceptCbPtrFun(handler, handler_data));
b0469965 2189 fdc_table[fd].subscribe(call);
2d8c0b1a 2190}
c4b7a5a9 2191
b0469965 2192void
2193comm_accept(int fd, AsyncCall::Pointer &call) {
2194 debugs(5, 5, "comm_accept: FD " << fd << " AsyncCall: " << call);
2195 assert(isOpen(fd));
9e008dda 2196
b0469965 2197 fdc_table[fd].subscribe(call);
2d8c0b1a 2198}
62e76326 2199
9e008dda 2200// Called when somebody wants to be notified when our socket accepts new
b0469965 2201// connection. We do not probe the FD until there is such interest.
2d8c0b1a 2202void
b0469965 2203AcceptFD::subscribe(AsyncCall::Pointer &call) {
2204 /* make sure we're not pending! */
2205 assert(!theCallback);
2206 theCallback = call;
2207
2208#if OPTIMISTIC_IO
2209 mayAcceptMore = true; // even if we failed to accept last time
2210#endif
2211
2212 if (mayAcceptMore)
2213 acceptNext();
2214 else
2215 commSetSelect(fd, COMM_SELECT_READ, comm_accept_try, NULL, 0);
2216}
2217
2218bool
2219AcceptFD::acceptOne() {
c99de607 2220 // If there is no callback and we accept, we will leak the accepted FD.
2221 // When we are running out of FDs, there is often no callback.
b0469965 2222 if (!theCallback) {
2223 debugs(5, 5, "AcceptFD::acceptOne orphaned: FD " << fd);
9e008dda 2224 // XXX: can we remove this and similar "just in case" calls and
c99de607 2225 // either listen always or listen only when there is a callback?
2226 if (!AcceptLimiter::Instance().deferring())
2227 commSetSelect(fd, COMM_SELECT_READ, comm_accept_try, NULL, 0);
b0469965 2228 return false;
c99de607 2229 }
2230
bdd8c442 2231 /*
2232 * We don't worry about running low on FDs here. Instead,
2233 * httpAccept() will use AcceptLimiter if we reach the limit
2234 * there.
2235 */
62e76326 2236
2d8c0b1a 2237 /* Accept a new connection */
b0469965 2238 ConnectionDetail connDetails;
2239 int newfd = comm_old_accept(fd, connDetails);
62e76326 2240
2d8c0b1a 2241 /* Check for errors */
bdd8c442 2242
2d8c0b1a 2243 if (newfd < 0) {
b0469965 2244 assert(theCallback != NULL);
2245
2d8c0b1a 2246 if (newfd == COMM_NOMESSAGE) {
2247 /* register interest again */
10b06767 2248 debugs(5, 5, HERE << "try later: FD " << fd <<
9e008dda 2249 " handler: " << *theCallback);
2d8c0b1a 2250 commSetSelect(fd, COMM_SELECT_READ, comm_accept_try, NULL, 0);
b0469965 2251 return false;
62e76326 2252 }
2253
b0469965 2254 // A non-recoverable error; notify the caller */
2255 notify(-1, COMM_ERROR, errno, connDetails);
2256 return false;
2d8c0b1a 2257 }
62e76326 2258
b0469965 2259 assert(theCallback != NULL);
2260 debugs(5, 5, "AcceptFD::acceptOne accepted: FD " << fd <<
9e008dda
AJ
2261 " newfd: " << newfd << " from: " << connDetails.peer <<
2262 " handler: " << *theCallback);
b0469965 2263 notify(newfd, COMM_OK, 0, connDetails);
2264 return true;
2d8c0b1a 2265}
62e76326 2266
2d8c0b1a 2267void
b0469965 2268AcceptFD::acceptNext() {
2269 mayAcceptMore = acceptOne();
2d8c0b1a 2270}
62e76326 2271
b0469965 2272void
9e008dda 2273AcceptFD::notify(int newfd, comm_err_t errcode, int xerrno, const ConnectionDetail &connDetails) {
b0469965 2274 if (theCallback != NULL) {
2275 typedef CommAcceptCbParams Params;
2276 Params &params = GetCommParams<Params>(theCallback);
2277 params.fd = fd;
2278 params.nfd = newfd;
2279 params.details = connDetails;
2280 params.flag = errcode;
2281 params.xerrno = xerrno;
2282 ScheduleCallHere(theCallback);
2283 theCallback = NULL;
2284 }
c4b7a5a9 2285}
2286
2d8c0b1a 2287/*
2288 * This callback is called whenever a filedescriptor is ready
2289 * to dupe itself and fob off an accept()ed connection
2290 */
2291static void
b0469965 2292comm_accept_try(int fd, void *) {
2293 assert(isOpen(fd));
2294 fdc_table[fd].acceptNext();
c4b7a5a9 2295}
6cce2334 2296
a50bfe93 2297void CommIO::Initialise() {
6cce2334 2298 /* Initialize done pipe signal */
2299 int DonePipe[2];
9e008dda 2300 if (pipe(DonePipe)) {}
6cce2334 2301 DoneFD = DonePipe[1];
2302 DoneReadFD = DonePipe[0];
d06925a4 2303 fd_open(DoneReadFD, FD_PIPE, "async-io completetion event: main");
2304 fd_open(DoneFD, FD_PIPE, "async-io completetion event: threads");
2305 commSetNonBlocking(DoneReadFD);
2306 commSetNonBlocking(DoneFD);
2307 commSetSelect(DoneReadFD, COMM_SELECT_READ, NULLFDHandler, NULL, 0);
6cce2334 2308 Initialised = true;
2309}
2310
d06925a4 2311void CommIO::NotifyIOClose() {
2312 /* Close done pipe signal */
2313 FlushPipe();
2314 close(DoneFD);
2315 close(DoneReadFD);
2316 fd_close(DoneFD);
2317 fd_close(DoneReadFD);
2318 Initialised = false;
2319}
2320
6cce2334 2321bool CommIO::Initialised = false;
2322bool CommIO::DoneSignalled = false;
2323int CommIO::DoneFD = -1;
2324int CommIO::DoneReadFD = -1;
2325
2326void
a50bfe93 2327CommIO::FlushPipe() {
6cce2334 2328 char buf[256];
56410c89 2329 FD_READ_METHOD(DoneReadFD, buf, sizeof(buf));
6cce2334 2330}
2331
2332void
a50bfe93 2333CommIO::NULLFDHandler(int fd, void *data) {
6cce2334 2334 FlushPipe();
2335 commSetSelect(fd, COMM_SELECT_READ, NULLFDHandler, NULL, 0);
2336}
2337
2338void
a50bfe93 2339CommIO::ResetNotifications() {
6cce2334 2340 if (DoneSignalled) {
62e76326 2341 FlushPipe();
2342 DoneSignalled = false;
6cce2334 2343 }
2344}
a46d2c0e 2345
2346AcceptLimiter AcceptLimiter::Instance_;
2347
a50bfe93 2348AcceptLimiter &AcceptLimiter::Instance() {
a46d2c0e 2349 return Instance_;
2350}
2351
c99de607 2352bool
2353AcceptLimiter::deferring() const {
2354 return deferred.size() > 0;
2355}
2356
a46d2c0e 2357void
a50bfe93 2358AcceptLimiter::defer (int fd, Acceptor::AcceptorFunction *aFunc, void *data) {
bf8fe701 2359 debugs(5, 5, "AcceptLimiter::defer: FD " << fd << " handler: " << (void*)aFunc);
a46d2c0e 2360 Acceptor temp;
2361 temp.theFunction = aFunc;
2362 temp.acceptFD = fd;
2363 temp.theData = data;
2364 deferred.push_back(temp);
2365}
2366
2367void
a50bfe93 2368AcceptLimiter::kick() {
c99de607 2369 if (!deferring())
a46d2c0e 2370 return;
2371
2372 /* Yes, this means the first on is the last off....
2373 * If the list container was a little more friendly, we could sensibly us it.
2374 */
2375 Acceptor temp = deferred.pop_back();
2376
2377 comm_accept (temp.acceptFD, temp.theFunction, temp.theData);
2378}
2379
9e008dda
AJ
2380/// Start waiting for a possibly half-closed connection to close
2381// by scheduling a read callback to a monitoring handler that
82ec8dfc 2382// will close the connection on read errors.
a46d2c0e 2383void
82ec8dfc 2384commStartHalfClosedMonitor(int fd) {
74257126 2385 debugs(5, 5, HERE << "adding FD " << fd << " to " << *TheHalfClosed);
82ec8dfc
AR
2386 assert(isOpen(fd));
2387 assert(!commHasHalfClosedMonitor(fd));
74257126
AR
2388 (void)TheHalfClosed->add(fd); // could also assert the result
2389 commPlanHalfClosedCheck(); // may schedule check if we added the first FD
2390}
2391
2392static
2393void
9e008dda 2394commPlanHalfClosedCheck() {
74257126
AR
2395 if (!WillCheckHalfClosed && !TheHalfClosed->empty()) {
2396 eventAdd("commHalfClosedCheck", &commHalfClosedCheck, NULL, 1.0, 1);
2397 WillCheckHalfClosed = true;
2398 }
2399}
2400
2401/// iterates over all descriptors that may need half-closed tests and
2402/// calls comm_read for those that do; re-schedules the check if needed
2403static
2404void
2405commHalfClosedCheck(void *) {
2406 debugs(5, 5, HERE << "checking " << *TheHalfClosed);
2407
2408 typedef DescriptorSet::const_iterator DSCI;
2409 const DSCI end = TheHalfClosed->end();
2410 for (DSCI i = TheHalfClosed->begin(); i != end; ++i) {
2411 const int fd = *i;
2412 if (!fd_table[fd].halfClosedReader) { // not reading already
2413 AsyncCall::Pointer call = commCbCall(5,4, "commHalfClosedReader",
9e008dda 2414 CommIoCbPtrFun(&commHalfClosedReader, NULL));
74257126
AR
2415 comm_read(fd, NULL, 0, call);
2416 fd_table[fd].halfClosedReader = call;
2417 }
2418 }
f900210a 2419
74257126
AR
2420 WillCheckHalfClosed = false; // as far as we know
2421 commPlanHalfClosedCheck(); // may need to check again
f900210a 2422}
2423
82ec8dfc
AR
2424/// checks whether we are waiting for possibly half-closed connection to close
2425// We are monitoring if the read handler for the fd is the monitoring handler.
2426bool
2427commHasHalfClosedMonitor(int fd) {
74257126 2428 return TheHalfClosed->has(fd);
a46d2c0e 2429}
2430
82ec8dfc
AR
2431/// stop waiting for possibly half-closed connection to close
2432static void
2433commStopHalfClosedMonitor(int const fd) {
74257126
AR
2434 debugs(5, 5, HERE << "removing FD " << fd << " from " << *TheHalfClosed);
2435
2436 // cancel the read if one was scheduled
2437 AsyncCall::Pointer reader = fd_table[fd].halfClosedReader;
2438 if (reader != NULL)
2439 comm_read_cancel(fd, reader);
2440 fd_table[fd].halfClosedReader = NULL;
2441
2442 TheHalfClosed->del(fd);
a46d2c0e 2443}
2444
82ec8dfc
AR
2445/// I/O handler for the possibly half-closed connection monitoring code
2446static void
2447commHalfClosedReader(int fd, char *, size_t size, comm_err_t flag, int, void *) {
2448 // there cannot be more data coming in on half-closed connections
9e008dda 2449 assert(size == 0);
74257126
AR
2450 assert(commHasHalfClosedMonitor(fd)); // or we would have canceled the read
2451
2452 fd_table[fd].halfClosedReader = NULL; // done reading, for now
a46d2c0e 2453
82ec8dfc
AR
2454 // nothing to do if fd is being closed
2455 if (flag == COMM_ERR_CLOSING)
2456 return;
a46d2c0e 2457
82ec8dfc
AR
2458 // if read failed, close the connection
2459 if (flag != COMM_OK) {
2460 debugs(5, 3, "commHalfClosedReader: closing FD " << fd);
2461 comm_close(fd);
2462 return;
2463 }
a46d2c0e 2464
82ec8dfc 2465 // continue waiting for close or error
74257126 2466 commPlanHalfClosedCheck(); // make sure this fd will be checked again
a46d2c0e 2467}
2468
a46d2c0e 2469
b0469965 2470CommRead::CommRead() : fd(-1), buf(NULL), len(0), callback(NULL) {}
a46d2c0e 2471
b0469965 2472CommRead::CommRead(int fd_, char *buf_, int len_, AsyncCall::Pointer &callback_)
2473 : fd(fd_), buf(buf_), len(len_), callback(callback_) {}
a46d2c0e 2474
a50bfe93 2475DeferredRead::DeferredRead () : theReader(NULL), theContext(NULL), theRead(), cancelled(false) {}
a46d2c0e 2476
a50bfe93 2477DeferredRead::DeferredRead (DeferrableRead *aReader, void *data, CommRead const &aRead) : theReader(aReader), theContext (data), theRead(aRead), cancelled(false) {}
a46d2c0e 2478
a50bfe93 2479DeferredReadManager::~DeferredReadManager() {
a46d2c0e 2480 flushReads();
2481 assert (deferredReads.empty());
2482}
2483
97427e90 2484/* explicit instantiation required for some systems */
2485
63be0a78 2486/// \cond AUTODOCS-IGNORE
2236466c 2487template cbdata_type CbDataList<DeferredRead>::CBDATA_CbDataList;
63be0a78 2488/// \endcond
97427e90 2489
a46d2c0e 2490void
a50bfe93 2491DeferredReadManager::delayRead(DeferredRead const &aRead) {
bf8fe701 2492 debugs(5, 3, "Adding deferred read on FD " << aRead.theRead.fd);
2236466c 2493 CbDataList<DeferredRead> *temp = deferredReads.push_back(aRead);
2796c0d7 2494
9e008dda 2495 // We have to use a global function as a closer and point to temp
2796c0d7
AR
2496 // instead of "this" because DeferredReadManager is not a job and
2497 // is not even cbdata protected
2498 AsyncCall::Pointer closer = commCbCall(5,4,
9e008dda
AJ
2499 "DeferredReadManager::CloseHandler",
2500 CommCloseCbPtrFun(&CloseHandler, temp));
2796c0d7
AR
2501 comm_add_close_handler(aRead.theRead.fd, closer);
2502 temp->element.closer = closer; // remeber so that we can cancel
a46d2c0e 2503}
2504
2505void
a50bfe93 2506DeferredReadManager::CloseHandler(int fd, void *thecbdata) {
a46d2c0e 2507 if (!cbdataReferenceValid (thecbdata))
2508 return;
2509
2236466c 2510 CbDataList<DeferredRead> *temp = (CbDataList<DeferredRead> *)thecbdata;
a46d2c0e 2511
2796c0d7 2512 temp->element.closer = NULL;
a46d2c0e 2513 temp->element.markCancelled();
2514}
2515
2516DeferredRead
2236466c 2517DeferredReadManager::popHead(CbDataListContainer<DeferredRead> &deferredReads) {
a46d2c0e 2518 assert (!deferredReads.empty());
2519
2796c0d7
AR
2520 DeferredRead &read = deferredReads.head->element;
2521 if (!read.cancelled) {
2522 comm_remove_close_handler(read.theRead.fd, read.closer);
2523 read.closer = NULL;
2524 }
a46d2c0e 2525
2526 DeferredRead result = deferredReads.pop_front();
2527
2528 return result;
2529}
2530
2531void
a50bfe93 2532DeferredReadManager::kickReads(int const count) {
2236466c 2533 /* if we had CbDataList::size() we could consolidate this and flushReads */
a46d2c0e 2534
33cea91c 2535 if (count < 1) {
a46d2c0e 2536 flushReads();
33cea91c 2537 return;
2538 }
a46d2c0e 2539
2540 size_t remaining = count;
2541
2542 while (!deferredReads.empty() && remaining) {
2543 DeferredRead aRead = popHead(deferredReads);
2544 kickARead(aRead);
2545
2546 if (!aRead.cancelled)
2547 --remaining;
2548 }
2549}
2550
2551void
a50bfe93 2552DeferredReadManager::flushReads() {
2236466c 2553 CbDataListContainer<DeferredRead> reads;
a46d2c0e 2554 reads = deferredReads;
2236466c 2555 deferredReads = CbDataListContainer<DeferredRead>();
a46d2c0e 2556
27b24462 2557 // XXX: For fairness this SHOULD randomize the order
a46d2c0e 2558 while (!reads.empty()) {
2559 DeferredRead aRead = popHead(reads);
2560 kickARead(aRead);
2561 }
2562}
2563
2564void
a50bfe93 2565DeferredReadManager::kickARead(DeferredRead const &aRead) {
a46d2c0e 2566 if (aRead.cancelled)
2567 return;
2568
af6a12ee
AJ
2569 if (aRead.theRead.fd>=0 && fd_table[aRead.theRead.fd].closing())
2570 return;
ed2c738b 2571
bf8fe701 2572 debugs(5, 3, "Kicking deferred read on FD " << aRead.theRead.fd);
a46d2c0e 2573
2574 aRead.theReader(aRead.theContext, aRead.theRead);
2575}
2576
2577void
a50bfe93 2578DeferredRead::markCancelled() {
a46d2c0e 2579 cancelled = true;
2580}
2d8c0b1a 2581
cc192b50 2582ConnectionDetail::ConnectionDetail() : me(), peer() {
2d8c0b1a 2583}
8ff3fa2e 2584
8ff3fa2e 2585int
2586CommSelectEngine::checkEvents(int timeout) {
fa3f745b 2587 static time_t last_timeout = 0;
2588
2589 /* No, this shouldn't be here. But it shouldn't be in each comm handler. -adrian */
2590 if (squid_curtime > last_timeout) {
2591 last_timeout = squid_curtime;
2592 checkTimeouts();
2593 }
2594
8ff3fa2e 2595 switch (comm_select(timeout)) {
2596
2597 case COMM_OK:
2598
2599 case COMM_TIMEOUT:
2600 return 0;
2601
2602 case COMM_IDLE:
2603
2604 case COMM_SHUTDOWN:
2605 return EVENT_IDLE;
2606
2607 case COMM_ERROR:
2608 return EVENT_ERROR;
2609
2610 default:
2611 fatal_dump("comm.cc: Internal error -- this should never happen.");
2612 return EVENT_ERROR;
2613 };
2614}