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