comm.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 05 Socket Functions */
10
11#include "squid.h"
12#include "ClientInfo.h"
13#include "comm/AcceptLimiter.h"
14#include "comm/comm_internal.h"
15#include "comm/Connection.h"
16#include "comm/IoCallback.h"
17#include "comm/Loops.h"
18#include "comm/Read.h"
19#include "comm/TcpAcceptor.h"
20#include "comm/Write.h"
21#include "compat/cmsg.h"
22#include "DescriptorSet.h"
23#include "event.h"
24#include "fd.h"
25#include "fde.h"
26#include "globals.h"
27#include "icmp/net_db.h"
28#include "ip/Intercept.h"
29#include "ip/QosConfig.h"
30#include "ip/tools.h"
31#include "pconn.h"
32#include "sbuf/SBuf.h"
33#include "sbuf/Stream.h"
34#include "SquidConfig.h"
35#include "StatCounters.h"
36#include "StoreIOBuffer.h"
37#include "tools.h"
38
39#if USE_OPENSSL
40#include "ssl/support.h"
41#endif
42
43#include <cerrno>
44#include <cmath>
45#if _SQUID_CYGWIN_
46#include <sys/ioctl.h>
47#endif
48#ifdef HAVE_NETINET_TCP_H
49#include <netinet/tcp.h>
50#endif
51#if HAVE_SYS_UN_H
52#include <sys/un.h>
53#endif
54
55/*
56 * New C-like simple comm code. This stuff is a mess and doesn't really buy us anything.
57 */
58
60static void comm_init_opened(const Comm::ConnectionPointer &conn, const char *note, struct addrinfo *AI);
61static int comm_apply_flags(int new_socket, Ip::Address &addr, int flags, struct addrinfo *AI);
62
63#if USE_DELAY_POOLS
65
66static void commHandleWriteHelper(void * data);
67#endif
68
69/* STATIC */
70
71static DescriptorSet *TheHalfClosed = nullptr;
72static bool WillCheckHalfClosed = false;
74static void commPlanHalfClosedCheck();
75
76static Comm::Flag commBind(int s, struct addrinfo &);
77static void commSetReuseAddr(int);
78static void commSetNoLinger(int);
79#ifdef TCP_NODELAY
80static void commSetTcpNoDelay(int);
81#endif
82static void commSetTcpRcvbuf(int, int);
83
84bool
85isOpen(const int fd)
86{
87 return fd >= 0 && fd_table && fd_table[fd].flags.open != 0;
88}
89
98static void
100{
101#if _SQUID_LINUX_
102#if USE_OPENSSL
103 // Bug 4146: SSL-Bump BIO does not release sockets on close.
104 if (fd_table[fd].ssl)
105 return;
106#endif
107
108 /* prevent those nasty RST packets */
109 char buf[SQUID_TCP_SO_RCVBUF];
110 if (fd_table[fd].flags.nonblocking && fd_table[fd].type != FD_MSGHDR) {
111 while (FD_READ_METHOD(fd, buf, SQUID_TCP_SO_RCVBUF) > 0) {};
112 }
113#else
114 (void)fd;
115#endif
116}
117
121int
122comm_udp_recvfrom(int fd, void *buf, size_t len, int flags, Ip::Address &from)
123{
125 debugs(5,8, "comm_udp_recvfrom: FD " << fd << " from " << from);
126 struct addrinfo *AI = nullptr;
128 int x = recvfrom(fd, buf, len, flags, AI->ai_addr, &AI->ai_addrlen);
129 from = *AI;
131 return x;
132}
133
134int
135comm_udp_recv(int fd, void *buf, size_t len, int flags)
136{
137 Ip::Address nul;
138 return comm_udp_recvfrom(fd, buf, len, flags, nul);
139}
140
141ssize_t
142comm_udp_send(int s, const void *buf, size_t len, int flags)
143{
144 return send(s, buf, len, flags);
145}
146
147bool
149{
150 assert(isOpen(fd) && COMMIO_FD_WRITECB(fd) != nullptr);
151 return COMMIO_FD_WRITECB(fd)->active();
152}
153
159/* Return the local port associated with fd. */
160unsigned short
162{
163 Ip::Address temp;
164 struct addrinfo *addr = nullptr;
165 fde *F = &fd_table[fd];
166
167 /* If the fd is closed already, just return */
168
169 if (!F->flags.open) {
170 debugs(5, 0, "comm_local_port: FD " << fd << " has been closed.");
171 return 0;
172 }
173
174 if (F->local_addr.port())
175 return F->local_addr.port();
176
177 if (F->sock_family == AF_INET)
178 temp.setIPv4();
179
181
182 if (getsockname(fd, addr->ai_addr, &(addr->ai_addrlen)) ) {
183 int xerrno = errno;
184 debugs(50, DBG_IMPORTANT, "ERROR: " << MYNAME << "Failed to retrieve TCP/UDP port number for socket: FD " << fd << ": " << xstrerr(xerrno));
186 return 0;
187 }
188 temp = *addr;
189
191
192 if (F->local_addr.isAnyAddr()) {
193 /* save the whole local address, not just the port. */
194 F->local_addr = temp;
195 } else {
196 F->local_addr.port(temp.port());
197 }
198
199 debugs(5, 6, "comm_local_port: FD " << fd << ": port " << F->local_addr.port() << "(family=" << F->sock_family << ")");
200 return F->local_addr.port();
201}
202
203static Comm::Flag
204commBind(int s, struct addrinfo &inaddr)
205{
207
208 if (bind(s, inaddr.ai_addr, inaddr.ai_addrlen) == 0) {
209 debugs(50, 6, "bind socket FD " << s << " to " << fd_table[s].local_addr);
210 return Comm::OK;
211 }
212 int xerrno = errno;
213 debugs(50, DBG_CRITICAL, "ERROR: " << MYNAME << "Cannot bind socket FD " << s << " to " << fd_table[s].local_addr << ": " << xstrerr(xerrno));
214
215 return Comm::COMM_ERROR;
216}
217
222int
223comm_open(int sock_type,
224 int proto,
225 Ip::Address &addr,
226 int flags,
227 const char *note)
228{
229 return comm_openex(sock_type, proto, addr, flags, note);
230}
231
232void
233comm_open_listener(int sock_type,
234 int proto,
236 const char *note)
237{
238 /* all listener sockets require bind() */
239 conn->flags |= COMM_DOBIND;
240
241 /* attempt native enabled port. */
242 conn->fd = comm_openex(sock_type, proto, conn->local, conn->flags, note);
243}
244
245int
246comm_open_listener(int sock_type,
247 int proto,
248 Ip::Address &addr,
249 int flags,
250 const char *note)
251{
252 int sock = -1;
253
254 /* all listener sockets require bind() */
255 flags |= COMM_DOBIND;
256
257 /* attempt native enabled port. */
258 sock = comm_openex(sock_type, proto, addr, flags, note);
259
260 return sock;
261}
262
263static bool
264limitError(int const anErrno)
265{
266 return anErrno == ENFILE || anErrno == EMFILE;
267}
268
269static void
270comm_set_v6only(int fd, int tos)
271{
272#ifdef IPV6_V6ONLY
273 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &tos, sizeof(int)) < 0) {
274 int xerrno = errno;
275 debugs(50, DBG_IMPORTANT, MYNAME << "setsockopt(IPV6_V6ONLY) " << (tos?"ON":"OFF") << " for FD " << fd << ": " << xstrerr(xerrno));
276 }
277#else
278 debugs(50, DBG_CRITICAL, MYNAME << "WARNING: setsockopt(IPV6_V6ONLY) not supported on this platform");
279#endif /* sockopt */
280}
281
288static void
290{
291#if _SQUID_LINUX_ && defined(IP_TRANSPARENT) // Linux
292# define soLevel SOL_IP
293# define soFlag IP_TRANSPARENT
294 bool doneSuid = false;
295
296#elif defined(SO_BINDANY) // OpenBSD 4.7+ and NetBSD with PF
297# define soLevel SOL_SOCKET
298# define soFlag SO_BINDANY
299 enter_suid();
300 bool doneSuid = true;
301
302#elif defined(IP_BINDANY) // FreeBSD with IPFW
303# define soLevel IPPROTO_IP
304# define soFlag IP_BINDANY
305 enter_suid();
306 bool doneSuid = true;
307
308#else
309 debugs(50, DBG_CRITICAL, "WARNING: comm_open: setsockopt(TPROXY) not supported on this platform");
310#endif /* sockopt */
311
312#if defined(soLevel) && defined(soFlag)
313 int tos = 1;
314 if (setsockopt(fd, soLevel, soFlag, (char *) &tos, sizeof(int)) < 0) {
315 int xerrno = errno;
316 debugs(50, DBG_IMPORTANT, MYNAME << "setsockopt(TPROXY) on FD " << fd << ": " << xstrerr(xerrno));
317 } else {
318 /* mark the socket as having transparent options */
319 fd_table[fd].flags.transparent = true;
320 }
321 if (doneSuid)
322 leave_suid();
323#endif
324}
325
330int
331comm_openex(int sock_type,
332 int proto,
333 Ip::Address &addr,
334 int flags,
335 const char *note)
336{
337 int new_socket;
338 struct addrinfo *AI = nullptr;
339
340 /* Create socket for accepting new connections. */
342
343 /* Setup the socket addrinfo details for use */
344 addr.getAddrInfo(AI);
345 AI->ai_socktype = sock_type;
346 AI->ai_protocol = proto;
347
348 debugs(50, 3, "comm_openex: Attempt open socket for: " << addr );
349
350 new_socket = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);
351 int xerrno = errno;
352
353 /* under IPv6 there is the possibility IPv6 is present but disabled. */
354 /* try again as IPv4-native if possible */
355 if ( new_socket < 0 && Ip::EnableIpv6 && addr.isIPv6() && addr.setIPv4() ) {
356 /* attempt to open this IPv4-only. */
358 /* Setup the socket addrinfo details for use */
359 addr.getAddrInfo(AI);
360 AI->ai_socktype = sock_type;
361 AI->ai_protocol = proto;
362 debugs(50, 3, "Attempt fallback open socket for: " << addr );
363 new_socket = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);
364 debugs(50, 2, "attempt open " << note << " socket on: " << addr);
365 }
366
367 if (new_socket < 0) {
368 /* Increase the number of reserved fd's if calls to socket()
369 * are failing because the open file table is full. This
370 * limits the number of simultaneous clients */
371
372 if (limitError(errno)) {
373 debugs(50, DBG_IMPORTANT, MYNAME << "socket failure: " << xstrerr(xerrno));
375 } else {
376 debugs(50, DBG_CRITICAL, MYNAME << "socket failure: " << xstrerr(xerrno));
377 }
378
380
381 errno = xerrno; // restore for caller
382 return -1;
383 }
384
385 // XXX: temporary for the transition. comm_openex will eventually have a conn to play with.
387 conn->local = addr;
388 conn->fd = new_socket;
389
390 debugs(50, 3, "comm_openex: Opened socket " << conn << " : family=" << AI->ai_family << ", type=" << AI->ai_socktype << ", protocol=" << AI->ai_protocol );
391
393 comm_set_v6only(conn->fd, 1);
394
395 /* Windows Vista supports Dual-Sockets. BUT defaults them to V6ONLY. Turn it OFF. */
396 /* Other OS may have this administratively disabled for general use. Same deal. */
398 comm_set_v6only(conn->fd, 0);
399
400 comm_init_opened(conn, note, AI);
401 new_socket = comm_apply_flags(conn->fd, addr, flags, AI);
402
404
405 // XXX transition only. prevent conn from closing the new FD on function exit.
406 conn->fd = -1;
407 errno = xerrno; // restore for caller
408 return new_socket;
409}
410
412void
414 const char *note,
415 struct addrinfo *AI)
416{
418 assert(AI);
419
420 /* update fdstat */
421 debugs(5, 5, conn << " is a new socket");
422
423 assert(!isOpen(conn->fd)); // NP: global isOpen checks the fde entry for openness not the Comm::Connection
424 fd_open(conn->fd, FD_SOCKET, note);
425
426 fde *F = &fd_table[conn->fd];
427 F->local_addr = conn->local;
428
429 F->sock_family = AI->ai_family;
430}
431
434static int
435comm_apply_flags(int new_socket,
436 Ip::Address &addr,
437 int flags,
438 struct addrinfo *AI)
439{
440 assert(new_socket >= 0);
441 assert(AI);
442 const int sock_type = AI->ai_socktype;
443
444 if (!(flags & COMM_NOCLOEXEC))
445 commSetCloseOnExec(new_socket);
446
447 if ((flags & COMM_REUSEADDR))
448 commSetReuseAddr(new_socket);
449
450 if (addr.port() > (unsigned short) 0) {
451#if _SQUID_WINDOWS_
452 if (sock_type != SOCK_DGRAM)
453#endif
454 commSetNoLinger(new_socket);
455
456 if (opt_reuseaddr)
457 commSetReuseAddr(new_socket);
458 }
459
460 /* MUST be done before binding or face OS Error: "(99) Cannot assign requested address"... */
461 if ((flags & COMM_TRANSPARENT)) {
462 comm_set_transparent(new_socket);
463 }
464
465 if ( (flags & COMM_DOBIND) || addr.port() > 0 || !addr.isAnyAddr() ) {
466 if ( !(flags & COMM_DOBIND) && addr.isAnyAddr() )
467 debugs(5, DBG_IMPORTANT,"WARNING: Squid is attempting to bind() port " << addr << " without being a listener.");
468 if ( addr.isNoAddr() )
469 debugs(5, DBG_CRITICAL, "ERROR: Squid is attempting to bind() port " << addr << "!!");
470
471#if defined(SO_REUSEPORT)
472 if (flags & COMM_REUSEPORT) {
473 int on = 1;
474 if (setsockopt(new_socket, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char*>(&on), sizeof(on)) < 0) {
475 const auto savedErrno = errno;
476 const auto errorMessage = ToSBuf("cannot enable SO_REUSEPORT socket option when binding to ",
477 addr, ": ", xstrerr(savedErrno));
478 if (reconfiguring)
479 debugs(5, DBG_IMPORTANT, "ERROR: " << errorMessage);
480 else
481 throw TexcHere(errorMessage);
482 }
483 }
484#endif
485 if (commBind(new_socket, *AI) != Comm::OK) {
486 comm_close(new_socket);
487 return -1;
488 }
489 }
490
491 if (flags & COMM_NONBLOCKING)
492 if (commSetNonBlocking(new_socket) == Comm::COMM_ERROR) {
493 comm_close(new_socket);
494 return -1;
495 }
496
497#ifdef TCP_NODELAY
498 if (sock_type == SOCK_STREAM)
499 commSetTcpNoDelay(new_socket);
500
501#endif
502
503 if (Config.tcpRcvBufsz > 0 && sock_type == SOCK_STREAM)
505
506 return new_socket;
507}
508
509void
511 const char *note,
512 struct addrinfo *AI)
513{
514 debugs(5, 2, conn);
516 assert(AI);
517
518 comm_init_opened(conn, note, AI);
519
520 if (!(conn->flags & COMM_NOCLOEXEC))
521 fd_table[conn->fd].flags.close_on_exec = true;
522
523 if (conn->local.port() > (unsigned short) 0) {
524#if _SQUID_WINDOWS_
525 if (AI->ai_socktype != SOCK_DGRAM)
526#endif
527 fd_table[conn->fd].flags.nolinger = true;
528 }
529
530 if ((conn->flags & COMM_TRANSPARENT))
531 fd_table[conn->fd].flags.transparent = true;
532
533 if (conn->flags & COMM_NONBLOCKING)
534 fd_table[conn->fd].flags.nonblocking = true;
535
536#ifdef TCP_NODELAY
537 if (AI->ai_socktype == SOCK_STREAM)
538 fd_table[conn->fd].flags.nodelay = true;
539#endif
540
541 /* no fd_table[fd].flags. updates needed for these conditions:
542 * if ((flags & COMM_REUSEADDR)) ...
543 * if ((flags & COMM_DOBIND) ...) ...
544 */
545}
546
547// XXX: now that raw-FD timeouts are only unset for pipes and files this SHOULD be a no-op.
548// With handler already unset. Leaving this present until that can be verified for all code paths.
549void
551{
552 debugs(5, 3, "Remove timeout for FD " << fd);
553 assert(fd >= 0);
554 assert(fd < Squid_MaxFD);
555 fde *F = &fd_table[fd];
556 assert(F->flags.open);
557
558 F->timeoutHandler = nullptr;
559 F->timeout = 0;
560}
561
562int
564{
565 debugs(5, 3, conn << " timeout " << timeout);
567 assert(conn->fd < Squid_MaxFD);
568 fde *F = &fd_table[conn->fd];
569 assert(F->flags.open);
570
571 if (timeout < 0) {
572 F->timeoutHandler = nullptr;
573 F->timeout = 0;
574 } else {
575 if (callback != nullptr) {
576 typedef CommTimeoutCbParams Params;
577 Params &params = GetCommParams<Params>(callback);
578 params.conn = conn;
579 F->timeoutHandler = callback;
580 }
581
582 F->timeout = squid_curtime + (time_t) timeout;
583 }
584
585 return F->timeout;
586}
587
588int
590{
591 debugs(5, 3, "Remove timeout for " << conn);
593 return commSetConnTimeout(conn, -1, nil);
594}
595
601int
602comm_connect_addr(int sock, const Ip::Address &address)
603{
604 Comm::Flag status = Comm::OK;
605 fde *F = &fd_table[sock];
606 int x = 0;
607 int err = 0;
608 socklen_t errlen;
609 struct addrinfo *AI = nullptr;
610
611 assert(address.port() != 0);
612
613 debugs(5, 9, "connecting socket FD " << sock << " to " << address << " (want family: " << F->sock_family << ")");
614
615 /* Handle IPv6 over IPv4-only socket case.
616 * this case must presently be handled here since the getAddrInfo asserts on bad mappings.
617 * NP: because commResetFD is private to ConnStateData we have to return an error and
618 * trust its handled properly.
619 */
620 if (F->sock_family == AF_INET && !address.isIPv4()) {
621 errno = ENETUNREACH;
622 return Comm::ERR_PROTOCOL;
623 }
624
625 /* Handle IPv4 over IPv6-only socket case.
626 * This case is presently handled here as it's both a known case and it's
627 * uncertain what error will be returned by the IPv6 stack in such case. It's
628 * possible this will also be handled by the errno checks below after connect()
629 * but needs careful cross-platform verification, and verifying the address
630 * condition here is simple.
631 */
632 if (!F->local_addr.isIPv4() && address.isIPv4()) {
633 errno = ENETUNREACH;
634 return Comm::ERR_PROTOCOL;
635 }
636
637 address.getAddrInfo(AI, F->sock_family);
638
639 /* Establish connection. */
640 int xerrno = 0;
641
642 if (!F->flags.called_connect) {
643 F->flags.called_connect = true;
645
646 errno = 0;
647 if ((x = connect(sock, AI->ai_addr, AI->ai_addrlen)) < 0) {
648 xerrno = errno;
649 debugs(5,5, "sock=" << sock << ", addrinfo(" <<
650 " flags=" << AI->ai_flags <<
651 ", family=" << AI->ai_family <<
652 ", socktype=" << AI->ai_socktype <<
653 ", protocol=" << AI->ai_protocol <<
654 ", &addr=" << AI->ai_addr <<
655 ", addrlen=" << AI->ai_addrlen << " )");
656 debugs(5, 9, "connect FD " << sock << ": (" << x << ") " << xstrerr(xerrno));
657 debugs(14,9, "connecting to: " << address);
658
659 } else if (x == 0) {
660 // XXX: ICAP code refuses callbacks during a pending comm_ call
661 // Async calls development will fix this.
662 x = -1;
663 xerrno = EINPROGRESS;
664 }
665
666 } else {
667 errno = 0;
668#if _SQUID_NEWSOS6_
669 /* Makoto MATSUSHITA <matusita@ics.es.osaka-u.ac.jp> */
670 if (connect(sock, AI->ai_addr, AI->ai_addrlen) < 0)
671 xerrno = errno;
672
673 if (xerrno == EINVAL) {
674 errlen = sizeof(err);
675 x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
676 if (x >= 0)
677 xerrno = x;
678 }
679#else
680 errlen = sizeof(err);
681 x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
682 if (x == 0)
683 xerrno = err;
684
685#if _SQUID_SOLARIS_
686 /*
687 * Solaris 2.4's socket emulation doesn't allow you
688 * to determine the error from a failed non-blocking
689 * connect and just returns EPIPE. Create a fake
690 * error message for connect. -- fenner@parc.xerox.com
691 */
692 if (x < 0 && xerrno == EPIPE)
693 xerrno = ENOTCONN;
694 else
695 xerrno = errno;
696#endif
697#endif
698 }
699
701
702 errno = xerrno;
703 if (xerrno == 0 || xerrno == EISCONN)
704 status = Comm::OK;
705 else if (ignoreErrno(xerrno))
706 status = Comm::INPROGRESS;
707 else if (xerrno == EAFNOSUPPORT || xerrno == EINVAL)
708 return Comm::ERR_PROTOCOL;
709 else
710 return Comm::COMM_ERROR;
711
712 address.toStr(F->ipaddr, MAX_IPSTRLEN);
713
714 F->remote_port = address.port(); /* remote_port is HS */
715
716 if (status == Comm::OK) {
717 debugs(5, DBG_DATA, "comm_connect_addr: FD " << sock << " connected to " << address);
718 } else if (status == Comm::INPROGRESS) {
719 debugs(5, DBG_DATA, "comm_connect_addr: FD " << sock << " connection pending");
720 }
721
722 errno = xerrno;
723 return status;
724}
725
726void
728{
729 fde *F = &fd_table[fd];
730 debugs(5, 5, "commCallCloseHandlers: FD " << fd);
731
732 while (F->closeHandler != nullptr) {
733 AsyncCall::Pointer call = F->closeHandler;
734 F->closeHandler = call->Next();
735 call->setNext(nullptr);
736 // If call is not canceled schedule it for execution else ignore it
737 if (!call->canceled()) {
738 debugs(5, 5, "commCallCloseHandlers: ch->handler=" << call);
739 // XXX: Without the following code, callback fd may be -1.
740 // typedef CommCloseCbParams Params;
741 // auto &params = GetCommParams<Params>(call);
742 // params.fd = fd;
743 ScheduleCallHere(call);
744 }
745 }
746}
747
752void
754{
755 struct linger L;
756 L.l_onoff = 1;
757 L.l_linger = 0;
758
759 if (setsockopt(conn->fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0) {
760 int xerrno = errno;
761 debugs(50, DBG_CRITICAL, "ERROR: Closing " << conn << " with TCP RST: " << xstrerr(xerrno));
762 }
763 conn->close();
764}
765
766// Legacy close function.
767void
769{
770 struct linger L;
771 L.l_onoff = 1;
772 L.l_linger = 0;
773
774 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0) {
775 int xerrno = errno;
776 debugs(50, DBG_CRITICAL, "ERROR: Closing FD " << fd << " with TCP RST: " << xstrerr(xerrno));
777 }
778 comm_close(fd);
779}
780
781static void
783{
785}
786
787static void
789{
790 fde *F = &fd_table[params.fd];
791 F->ssl.reset();
792 F->dynamicTlsContext.reset();
793 fd_close(params.fd); /* update fdstat */
794 close(params.fd);
795
797
798 /* When one connection closes, give accept() a chance, if need be */
799 CodeContext::Reset(); // exit FD-specific context
801}
802
803/*
804 * Close the socket fd.
805 *
806 * + call write handlers with ERR_CLOSING
807 * + call read handlers with ERR_CLOSING
808 * + call closing handlers
809 *
810 * A deferred reader has no Comm read handler mentioned above. To stay in sync,
811 * such a reader must register a Comm closing handler.
812 */
813void
814_comm_close(int fd, char const *file, int line)
815{
816 debugs(5, 3, "start closing FD " << fd << " by " << file << ":" << line);
817 assert(fd >= 0);
818 assert(fd < Squid_MaxFD);
819
820 fde *F = &fd_table[fd];
821
822 if (F->closing())
823 return;
824
825 /* XXX: is this obsolete behind F->closing() ? */
826 if ( (shutting_down || reconfiguring) && (!F->flags.open || F->type == FD_FILE))
827 return;
828
829 /* The following fails because ipc.c is doing calls to pipe() to create sockets! */
830 if (!isOpen(fd)) {
831 debugs(50, DBG_IMPORTANT, "ERROR: Squid BUG #3556: FD " << fd << " is not an open socket.");
832 // XXX: do we need to run close(fd) or fd_close(fd) here?
833 return;
834 }
835
836 assert(F->type != FD_FILE);
837
838 F->flags.close_request = true;
839
840 // We have caller's context and fde::codeContext. In the unlikely event they
841 // differ, it is not clear which context is more applicable to this closure.
842 // For simplicity sake, we remain in the caller's context while still
843 // allowing individual advanced callbacks to overwrite it.
844
845 if (F->ssl) {
846 AsyncCall::Pointer startCall=commCbCall(5,4, "commStartTlsClose",
848 FdeCbParams &startParams = GetCommParams<FdeCbParams>(startCall);
849 startParams.fd = fd;
850 ScheduleCallHere(startCall);
851 }
852
853 // a half-closed fd may lack a reader, so we stop monitoring explicitly
857
858 // notify read/write handlers after canceling select reservations, if any
859 if (COMMIO_FD_WRITECB(fd)->active()) {
860 Comm::SetSelect(fd, COMM_SELECT_WRITE, nullptr, nullptr, 0);
861 COMMIO_FD_WRITECB(fd)->finish(Comm::ERR_CLOSING, errno);
862 }
863 if (COMMIO_FD_READCB(fd)->active()) {
864 Comm::SetSelect(fd, COMM_SELECT_READ, nullptr, nullptr, 0);
865 COMMIO_FD_READCB(fd)->finish(Comm::ERR_CLOSING, errno);
866 }
867
868#if USE_DELAY_POOLS
870 if (bucket->selectWaiting)
871 bucket->onFdClosed();
872 }
873#endif
874
876
878
879 AsyncCall::Pointer completeCall=commCbCall(5,4, "comm_close_complete",
881 FdeCbParams &completeParams = GetCommParams<FdeCbParams>(completeCall);
882 completeParams.fd = fd;
883 // must use async call to wait for all callbacks
884 // scheduled before comm_close() to finish
885 ScheduleCallHere(completeCall);
886}
887
888/* Send a udp datagram to specified TO_ADDR. */
889int
891 const Ip::Address &to_addr,
892 const void *buf,
893 int len)
894{
896
897 debugs(50, 3, "comm_udp_sendto: Attempt to send UDP packet to " << to_addr <<
898 " using FD " << fd << " using Port " << comm_local_port(fd) );
899
900 struct addrinfo *AI = nullptr;
901 to_addr.getAddrInfo(AI, fd_table[fd].sock_family);
902 int x = sendto(fd, buf, len, 0, AI->ai_addr, AI->ai_addrlen);
903 int xerrno = errno;
905
906 if (x >= 0) {
907 errno = xerrno; // restore for caller to use
908 return x;
909 }
910
911#if _SQUID_LINUX_
912 if (ECONNREFUSED != xerrno)
913#endif
914 debugs(50, DBG_IMPORTANT, MYNAME << "FD " << fd << ", (family=" << fd_table[fd].sock_family << ") " << to_addr << ": " << xstrerr(xerrno));
915
916 errno = xerrno; // restore for caller to use
917 return Comm::COMM_ERROR;
918}
919
921comm_add_close_handler(int fd, CLCB * handler, void *data)
922{
923 debugs(5, 5, "comm_add_close_handler: FD " << fd << ", handler=" <<
924 handler << ", data=" << data);
925
926 AsyncCall::Pointer call=commCbCall(5,4, "SomeCloseHandler",
928 comm_add_close_handler(fd, call);
929 return call;
930}
931
932void
934{
935 debugs(5, 5, "comm_add_close_handler: FD " << fd << ", AsyncCall=" << call);
936
937 /*TODO:Check for a similar scheduled AsyncCall*/
938// for (c = fd_table[fd].closeHandler; c; c = c->next)
939// assert(c->handler != handler || c->data != data);
940
941 // TODO: Consider enhancing AsyncCallList to support random-access close
942 // handlers, perhaps after upgrading the remaining legacy CLCB handlers.
943 call->setNext(fd_table[fd].closeHandler);
944
945 fd_table[fd].closeHandler = call;
946}
947
948// remove function-based close handler
949void
951{
952 assert(isOpen(fd));
953 /* Find handler in list */
954 debugs(5, 5, "comm_remove_close_handler: FD " << fd << ", handler=" <<
955 handler << ", data=" << data);
956
957 AsyncCall::Pointer p, prev = nullptr;
958 for (p = fd_table[fd].closeHandler; p != nullptr; prev = p, p = p->Next()) {
960 const Call *call = dynamic_cast<const Call*>(p.getRaw());
961 if (!call) // method callbacks have their own comm_remove_close_handler
962 continue;
963
964 typedef CommCloseCbParams Params;
965 const Params &params = GetCommParams<Params>(p);
966 if (call->dialer.handler == handler && params.data == data)
967 break; /* This is our handler */
968 }
969
970 // comm_close removes all close handlers so our handler may be gone
971 if (p != nullptr) {
972 p->dequeue(fd_table[fd].closeHandler, prev);
973 p->cancel("comm_remove_close_handler");
974 }
975}
976
977// remove method-based close handler
978void
980{
981 assert(isOpen(fd));
982 debugs(5, 5, "comm_remove_close_handler: FD " << fd << ", AsyncCall=" << call);
983
984 // comm_close removes all close handlers so our handler may be gone
985 AsyncCall::Pointer p, prev = nullptr;
986 for (p = fd_table[fd].closeHandler; p != nullptr && p != call; prev = p, p = p->Next());
987
988 if (p != nullptr)
989 p->dequeue(fd_table[fd].closeHandler, prev);
990 call->cancel("comm_remove_close_handler");
991}
992
993static void
995{
996
997 struct linger L;
998 L.l_onoff = 0; /* off */
999 L.l_linger = 0;
1000
1001 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0) {
1002 int xerrno = errno;
1003 debugs(50, DBG_CRITICAL, MYNAME << "FD " << fd << ": " << xstrerr(xerrno));
1004 }
1005 fd_table[fd].flags.nolinger = true;
1006}
1007
1008static void
1010{
1011 int on = 1;
1012 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) {
1013 int xerrno = errno;
1014 debugs(50, DBG_IMPORTANT, MYNAME << "FD " << fd << ": " << xstrerr(xerrno));
1015 }
1016}
1017
1018static void
1020{
1021 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0) {
1022 int xerrno = errno;
1023 debugs(50, DBG_IMPORTANT, MYNAME << "FD " << fd << ", SIZE " << size << ": " << xstrerr(xerrno));
1024 }
1025 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof(size)) < 0) {
1026 int xerrno = errno;
1027 debugs(50, DBG_IMPORTANT, MYNAME << "FD " << fd << ", SIZE " << size << ": " << xstrerr(xerrno));
1028 }
1029#ifdef TCP_WINDOW_CLAMP
1030 if (setsockopt(fd, SOL_TCP, TCP_WINDOW_CLAMP, (char *) &size, sizeof(size)) < 0) {
1031 int xerrno = errno;
1032 debugs(50, DBG_IMPORTANT, MYNAME << "FD " << fd << ", SIZE " << size << ": " << xstrerr(xerrno));
1033 }
1034#endif
1035}
1036
1037int
1039{
1040#if _SQUID_WINDOWS_
1041 int nonblocking = TRUE;
1042
1043 if (ioctl(fd, FIONBIO, &nonblocking) < 0) {
1044 int xerrno = errno;
1045 debugs(50, DBG_CRITICAL, MYNAME << "FD " << fd << ": " << xstrerr(xerrno) << " " << fd_table[fd].type);
1046 return Comm::COMM_ERROR;
1047 }
1048
1049#else
1050 int flags;
1051 int dummy = 0;
1052
1053 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
1054 int xerrno = errno;
1055 debugs(50, DBG_CRITICAL, MYNAME << "FD " << fd << ": fcntl F_GETFL: " << xstrerr(xerrno));
1056 return Comm::COMM_ERROR;
1057 }
1058
1059 if (fcntl(fd, F_SETFL, flags | SQUID_NONBLOCK) < 0) {
1060 int xerrno = errno;
1061 debugs(50, DBG_CRITICAL, MYNAME << "FD " << fd << ": " << xstrerr(xerrno));
1062 return Comm::COMM_ERROR;
1063 }
1064#endif
1065
1066 fd_table[fd].flags.nonblocking = true;
1067 return 0;
1068}
1069
1070int
1072{
1073#if _SQUID_WINDOWS_
1074 int nonblocking = FALSE;
1075
1076 if (ioctlsocket(fd, FIONBIO, (unsigned long *) &nonblocking) < 0) {
1077#else
1078 int flags;
1079 int dummy = 0;
1080
1081 if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
1082 int xerrno = errno;
1083 debugs(50, DBG_CRITICAL, MYNAME << "FD " << fd << ": fcntl F_GETFL: " << xstrerr(xerrno));
1084 return Comm::COMM_ERROR;
1085 }
1086
1087 if (fcntl(fd, F_SETFL, flags & (~SQUID_NONBLOCK)) < 0) {
1088#endif
1089 int xerrno = errno;
1090 debugs(50, DBG_CRITICAL, MYNAME << "FD " << fd << ": " << xstrerr(xerrno));
1091 return Comm::COMM_ERROR;
1092 }
1093
1094 fd_table[fd].flags.nonblocking = false;
1095 return 0;
1096}
1097
1098void
1100{
1101#ifdef FD_CLOEXEC
1102 int flags;
1103 int dummy = 0;
1104
1105 if ((flags = fcntl(fd, F_GETFD, dummy)) < 0) {
1106 int xerrno = errno;
1107 debugs(50, DBG_CRITICAL, MYNAME << "FD " << fd << ": fcntl F_GETFD: " << xstrerr(xerrno));
1108 return;
1109 }
1110
1111 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
1112 int xerrno = errno;
1113 debugs(50, DBG_CRITICAL, "ERROR: " << MYNAME << "FD " << fd << ": set close-on-exec failed: " << xstrerr(xerrno));
1114 }
1115
1116 fd_table[fd].flags.close_on_exec = true;
1117
1118#endif
1119}
1120
1121#ifdef TCP_NODELAY
1122static void
1123commSetTcpNoDelay(int fd)
1124{
1125 int on = 1;
1126
1127 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0) {
1128 int xerrno = errno;
1129 debugs(50, DBG_IMPORTANT, MYNAME << "FD " << fd << ": " << xstrerr(xerrno));
1130 }
1131
1132 fd_table[fd].flags.nodelay = true;
1133}
1134
1135#endif
1136
1137void
1139{
1141
1142 /* make sure the accept() socket FIFO delay queue exists */
1144
1145 // make sure the IO pending callback table exists
1147
1148 /* XXX account fd_table */
1149 /* Keep a few file descriptors free so that we don't run out of FD's
1150 * after accepting a client but before it opens a socket or a file.
1151 * Since Squid_MaxFD can be as high as several thousand, don't waste them */
1152 RESERVED_FD = min(100, Squid_MaxFD / 4);
1153
1155
1156 /* setup the select loop module */
1158}
1159
1160void
1162{
1163 delete TheHalfClosed;
1164 TheHalfClosed = nullptr;
1165
1167}
1168
1169#if USE_DELAY_POOLS
1170// called when the queue is done waiting for the client bucket to fill
1171void
1173{
1174 CommQuotaQueue *queue = static_cast<CommQuotaQueue*>(data);
1175 assert(queue);
1176
1177 ClientInfo *clientInfo = queue->clientInfo;
1178 // ClientInfo invalidates queue if freed, so if we got here through,
1179 // evenAdd cbdata protections, everything should be valid and consistent
1180 assert(clientInfo);
1181 assert(clientInfo->hasQueue());
1182 assert(clientInfo->hasQueue(queue));
1183 assert(clientInfo->eventWaiting);
1184 clientInfo->eventWaiting = false;
1185
1186 do {
1187 clientInfo->writeOrDequeue();
1188 if (clientInfo->selectWaiting)
1189 return;
1190 } while (clientInfo->hasQueue());
1191
1192 debugs(77, 3, "emptied queue");
1193}
1194
1195void
1197{
1199 const auto head = quotaPeekFd();
1200 const auto &headFde = fd_table[head];
1201 CallBack(headFde.codeContext, [&] {
1202 const auto ccb = COMMIO_FD_WRITECB(head);
1203 // check that the head descriptor is still relevant
1204 if (headFde.clientInfo == this &&
1205 quotaPeekReserv() == ccb->quotaQueueReserv &&
1206 !headFde.closing()) {
1207
1208 // wait for the head descriptor to become ready for writing
1209 Comm::SetSelect(head, COMM_SELECT_WRITE, Comm::HandleWrite, ccb, 0);
1210 selectWaiting = true;
1211 } else {
1212 quotaDequeue(); // remove the no longer relevant descriptor
1213 }
1214 });
1215}
1216
1217bool
1219{
1221 return !quotaQueue->empty();
1222}
1223
1224bool
1226{
1228 return quotaQueue == q;
1229}
1230
1232int
1234{
1236 return quotaQueue->front();
1237}
1238
1240unsigned int
1242{
1244 return quotaQueue->outs + 1;
1245}
1246
1248unsigned int
1250{
1252 return quotaQueue->enqueue(fd);
1253}
1254
1256void
1258{
1261}
1262
1263void
1265{
1266 if (!eventWaiting && !selectWaiting && hasQueue()) {
1267 // wait at least a second if the bucket is empty
1268 const double delay = (bucketLevel < 1.0) ? 1.0 : 0.0;
1269 eventAdd("commHandleWriteHelper", &commHandleWriteHelper,
1270 quotaQueue, delay, 0, true);
1271 eventWaiting = true;
1272 }
1273}
1274
1276int
1278{
1279 /* If we have multiple clients and give full bucketSize to each client then
1280 * clt1 may often get a lot more because clt1->clt2 time distance in the
1281 * select(2) callback order may be a lot smaller than cltN->clt1 distance.
1282 * We divide quota evenly to be more fair. */
1283
1284 if (!rationedCount) {
1285 rationedCount = quotaQueue->size() + 1;
1286
1287 // The delay in ration recalculation _temporary_ deprives clients from
1288 // bytes that should have trickled in while rationedCount was positive.
1289 refillBucket();
1290
1291 // Rounding errors do not accumulate here, but we round down to avoid
1292 // negative bucket sizes after write with rationedCount=1.
1293 rationedQuota = static_cast<int>(floor(bucketLevel/rationedCount));
1294 debugs(77,5, "new rationedQuota: " << rationedQuota <<
1295 '*' << rationedCount);
1296 }
1297
1298 --rationedCount;
1299 debugs(77,7, "rationedQuota: " << rationedQuota <<
1300 " rations remaining: " << rationedCount);
1301
1302 // update 'last seen' time to prevent clientdb GC from dropping us
1304 return rationedQuota;
1305}
1306
1307bool
1309{
1310 assert(hasQueue());
1311 assert(quotaPeekFd() == state->conn->fd);
1312 quotaDequeue(); // we will write or requeue below
1313 if (nleft > 0 && !BandwidthBucket::applyQuota(nleft, state)) {
1314 state->quotaQueueReserv = quotaEnqueue(state->conn->fd);
1316 return false;
1317 }
1318 return true;
1319}
1320
1321void
1323{
1324 if (writeLimitingActive) {
1325 state->quotaQueueReserv = quotaEnqueue(state->conn->fd);
1327 }
1328}
1329
1330void
1332{
1334 // kick queue or it will get stuck as commWriteHandle is not called
1336}
1337
1338void
1340{
1341 if (len > 0)
1343 // even if we wrote nothing, we were served; give others a chance
1345}
1346
1347void
1348ClientInfo::setWriteLimiter(const int aWriteSpeedLimit, const double anInitialBurst, const double aHighWatermark)
1349{
1350 debugs(77,5, "Write limits for " << (const char*)key <<
1351 " speed=" << aWriteSpeedLimit << " burst=" << anInitialBurst <<
1352 " highwatermark=" << aHighWatermark);
1353
1354 // set or possibly update traffic shaping parameters
1355 writeLimitingActive = true;
1356 writeSpeedLimit = aWriteSpeedLimit;
1357 bucketSizeLimit = aHighWatermark;
1358
1359 // but some members should only be set once for a newly activated bucket
1360 if (firstTimeConnection) {
1361 firstTimeConnection = false;
1362
1365 quotaQueue = new CommQuotaQueue(this);
1366
1367 bucketLevel = anInitialBurst;
1369 }
1370}
1371
1373 ins(0), outs(0)
1374{
1376}
1377
1379{
1380 assert(!clientInfo); // ClientInfo should clear this before destroying us
1381}
1382
1384unsigned int
1386{
1387 debugs(77,5, "clt" << (const char*)clientInfo->key <<
1388 ": FD " << fd << " with qqid" << (ins+1) << ' ' << fds.size());
1389 fds.push_back(fd);
1390 fd_table[fd].codeContext = CodeContext::Current();
1391 return ++ins;
1392}
1393
1395void
1397{
1398 assert(!fds.empty());
1399 debugs(77,5, "clt" << (const char*)clientInfo->key <<
1400 ": FD " << fds.front() << " with qqid" << (outs+1) << ' ' <<
1401 fds.size());
1402 fds.pop_front();
1403 ++outs;
1404}
1405#endif /* USE_DELAY_POOLS */
1406
1407/*
1408 * hm, this might be too general-purpose for all the places we'd
1409 * like to use it.
1410 */
1411int
1412ignoreErrno(int ierrno)
1413{
1414 switch (ierrno) {
1415
1416 case EINPROGRESS:
1417
1418 case EWOULDBLOCK:
1419#if EAGAIN != EWOULDBLOCK
1420
1421 case EAGAIN:
1422#endif
1423
1424 case EALREADY:
1425
1426 case EINTR:
1427#ifdef ERESTART
1428
1429 case ERESTART:
1430#endif
1431
1432 return 1;
1433
1434 default:
1435 return 0;
1436 }
1437
1438 /* NOTREACHED */
1439}
1440
1441void
1443{
1444 int fd;
1445 fde *F = nullptr;
1446
1447 for (fd = 0; fd <= Biggest_FD; ++fd) {
1448 F = &fd_table[fd];
1449
1450 if (!F->flags.open)
1451 continue;
1452
1453 if (F->type != FD_SOCKET)
1454 continue;
1455
1456 if (F->flags.ipc) /* don't close inter-process sockets */
1457 continue;
1458
1459 if (F->timeoutHandler != nullptr) {
1460 AsyncCall::Pointer callback = F->timeoutHandler;
1461 F->timeoutHandler = nullptr;
1462 debugs(5, 5, "commCloseAllSockets: FD " << fd << ": Calling timeout handler");
1463 ScheduleCallHere(callback);
1464 } else {
1465 debugs(5, 5, "commCloseAllSockets: FD " << fd << ": calling comm_reset_close()");
1467 }
1468 }
1469}
1470
1471static bool
1473{
1474 if (!F->flags.open)
1475 return true;
1476
1477 if (F->timeout == 0)
1478 return true;
1479
1480 if (F->timeout > squid_curtime)
1481 return true;
1482
1483 return false;
1484}
1485
1486static bool
1488{
1489 if (!COMMIO_FD_WRITECB(fd)->active())
1490 return false;
1491
1492 if ((squid_curtime - fd_table[fd].writeStart) < Config.Timeout.write)
1493 return false;
1494
1495 return true;
1496}
1497
1498void
1500{
1501 int fd;
1502 fde *F = nullptr;
1503 AsyncCall::Pointer callback;
1504
1505 for (fd = 0; fd <= Biggest_FD; ++fd) {
1506 F = &fd_table[fd];
1507
1508 if (writeTimedOut(fd)) {
1509 // We have an active write callback and we are timed out
1510 CodeContext::Reset(F->codeContext);
1511 debugs(5, 5, "checkTimeouts: FD " << fd << " auto write timeout");
1512 Comm::SetSelect(fd, COMM_SELECT_WRITE, nullptr, nullptr, 0);
1513 COMMIO_FD_WRITECB(fd)->finish(Comm::COMM_ERROR, ETIMEDOUT);
1515 continue;
1516#if USE_DELAY_POOLS
1517 } else if (F->writeQuotaHandler != nullptr && COMMIO_FD_WRITECB(fd)->conn != nullptr) {
1518 // TODO: Move and extract quota() call to place it inside F->codeContext.
1519 if (!F->writeQuotaHandler->selectWaiting && F->writeQuotaHandler->quota() && !F->closing()) {
1520 CodeContext::Reset(F->codeContext);
1521 F->writeQuotaHandler->selectWaiting = true;
1524 }
1525 continue;
1526#endif
1527 }
1528 else if (AlreadyTimedOut(F))
1529 continue;
1530
1531 CodeContext::Reset(F->codeContext);
1532 debugs(5, 5, "checkTimeouts: FD " << fd << " Expired");
1533
1534 if (F->timeoutHandler != nullptr) {
1535 debugs(5, 5, "checkTimeouts: FD " << fd << ": Call timeout handler");
1536 callback = F->timeoutHandler;
1537 F->timeoutHandler = nullptr;
1538 ScheduleCallHere(callback);
1539 } else {
1540 debugs(5, 5, "checkTimeouts: FD " << fd << ": Forcing comm_close()");
1541 comm_close(fd);
1542 }
1543
1545 }
1546}
1547
1549// by scheduling a read callback to a monitoring handler that
1550// will close the connection on read errors.
1551void
1553{
1554 debugs(5, 5, "adding FD " << fd << " to " << *TheHalfClosed);
1556 (void)TheHalfClosed->add(fd); // could also assert the result
1557 fd_table[fd].codeContext = CodeContext::Current();
1558 commPlanHalfClosedCheck(); // may schedule check if we added the first FD
1559}
1560
1561static
1562void
1564{
1566 eventAdd("commHalfClosedCheck", &commHalfClosedCheck, nullptr, 1.0, 1);
1567 WillCheckHalfClosed = true;
1568 }
1569}
1570
1573static
1574void
1576{
1577 debugs(5, 5, "checking " << *TheHalfClosed);
1578
1579 typedef DescriptorSet::const_iterator DSCI;
1580 const DSCI end = TheHalfClosed->end();
1581 for (DSCI i = TheHalfClosed->begin(); i != end; ++i) {
1582 Comm::ConnectionPointer c = new Comm::Connection; // XXX: temporary. make HalfClosed a list of these.
1583 c->fd = *i;
1584 if (!fd_table[c->fd].halfClosedReader) { // not reading already
1585 CallBack(fd_table[c->fd].codeContext, [&c] {
1586 AsyncCall::Pointer call = commCbCall(5,4, "commHalfClosedReader",
1587 CommIoCbPtrFun(&commHalfClosedReader, nullptr));
1588 Comm::Read(c, call);
1589 fd_table[c->fd].halfClosedReader = call;
1590 });
1591 } else
1592 c->fd = -1; // XXX: temporary. prevent c replacement erase closing listed FD
1593 }
1594
1595 WillCheckHalfClosed = false; // as far as we know
1596 commPlanHalfClosedCheck(); // may need to check again
1597}
1598
1600// We are monitoring if the read handler for the fd is the monitoring handler.
1601bool
1603{
1604 return TheHalfClosed->has(fd);
1605}
1606
1608void
1610{
1611 debugs(5, 5, "removing FD " << fd << " from " << *TheHalfClosed);
1612
1613 // cancel the read if one was scheduled
1614 AsyncCall::Pointer reader = fd_table[fd].halfClosedReader;
1615 if (reader != nullptr)
1616 Comm::ReadCancel(fd, reader);
1617 fd_table[fd].halfClosedReader = nullptr;
1618
1619 TheHalfClosed->del(fd);
1620}
1621
1623static void
1624commHalfClosedReader(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag flag, int, void *)
1625{
1626 // there cannot be more data coming in on half-closed connections
1627 assert(size == 0);
1628 assert(conn != nullptr);
1629 assert(commHasHalfClosedMonitor(conn->fd)); // or we would have canceled the read
1630
1631 fd_table[conn->fd].halfClosedReader = nullptr; // done reading, for now
1632
1633 // nothing to do if fd is being closed
1634 if (flag == Comm::ERR_CLOSING)
1635 return;
1636
1637 // if read failed, close the connection
1638 if (flag != Comm::OK) {
1639 debugs(5, 3, "closing " << conn);
1640 conn->close();
1641 return;
1642 }
1643
1644 // continue waiting for close or error
1645 commPlanHalfClosedCheck(); // make sure this fd will be checked again
1646}
1647
1648int
1650{
1651 static time_t last_timeout = 0;
1652
1653 /* No, this shouldn't be here. But it shouldn't be in each comm handler. -adrian */
1654 if (squid_curtime > last_timeout) {
1655 last_timeout = squid_curtime;
1656 checkTimeouts();
1657 }
1658
1659 switch (Comm::DoSelect(timeout)) {
1660
1661 case Comm::OK:
1662
1663 case Comm::TIMEOUT:
1664 return 0;
1665
1666 case Comm::IDLE:
1667
1668 case Comm::SHUTDOWN:
1669 return EVENT_IDLE;
1670
1671 case Comm::COMM_ERROR:
1672 return EVENT_ERROR;
1673
1674 default:
1675 fatal_dump("comm.cc: Internal error -- this should never happen.");
1676 return EVENT_ERROR;
1677 };
1678}
1679
1681int
1682comm_open_uds(int sock_type,
1683 int proto,
1684 struct sockaddr_un* addr,
1685 int flags)
1686{
1687 // TODO: merge with comm_openex() when Ip::Address becomes NetAddress
1688
1689 int new_socket;
1690
1691 /* Create socket for accepting new connections. */
1693
1694 /* Setup the socket addrinfo details for use */
1695 struct addrinfo AI;
1696 AI.ai_flags = 0;
1697 AI.ai_family = PF_UNIX;
1698 AI.ai_socktype = sock_type;
1699 AI.ai_protocol = proto;
1700 AI.ai_addrlen = SUN_LEN(addr);
1701 AI.ai_addr = (sockaddr*)addr;
1702 AI.ai_canonname = nullptr;
1703 AI.ai_next = nullptr;
1704
1705 debugs(50, 3, "Attempt open socket for: " << addr->sun_path);
1706
1707 if ((new_socket = socket(AI.ai_family, AI.ai_socktype, AI.ai_protocol)) < 0) {
1708 int xerrno = errno;
1709 /* Increase the number of reserved fd's if calls to socket()
1710 * are failing because the open file table is full. This
1711 * limits the number of simultaneous clients */
1712
1713 if (limitError(xerrno)) {
1714 debugs(50, DBG_IMPORTANT, MYNAME << "socket failure: " << xstrerr(xerrno));
1716 } else {
1717 debugs(50, DBG_CRITICAL, MYNAME << "socket failure: " << xstrerr(xerrno));
1718 }
1719 return -1;
1720 }
1721
1722 debugs(50, 3, "Opened UDS FD " << new_socket << " : family=" << AI.ai_family << ", type=" << AI.ai_socktype << ", protocol=" << AI.ai_protocol);
1723
1724 /* update fdstat */
1725 debugs(50, 5, "FD " << new_socket << " is a new socket");
1726
1727 assert(!isOpen(new_socket));
1728 fd_open(new_socket, FD_MSGHDR, addr->sun_path);
1729
1730 fd_table[new_socket].sock_family = AI.ai_family;
1731
1732 if (!(flags & COMM_NOCLOEXEC))
1733 commSetCloseOnExec(new_socket);
1734
1735 if (flags & COMM_REUSEADDR)
1736 commSetReuseAddr(new_socket);
1737
1738 if (flags & COMM_NONBLOCKING) {
1739 if (commSetNonBlocking(new_socket) != Comm::OK) {
1740 comm_close(new_socket);
1741 return -1;
1742 }
1743 }
1744
1745 if (flags & COMM_DOBIND) {
1746 if (commBind(new_socket, AI) != Comm::OK) {
1747 comm_close(new_socket);
1748 return -1;
1749 }
1750 }
1751
1752#ifdef TCP_NODELAY
1753 if (sock_type == SOCK_STREAM)
1754 commSetTcpNoDelay(new_socket);
1755
1756#endif
1757
1758 if (Config.tcpRcvBufsz > 0 && sock_type == SOCK_STREAM)
1759 commSetTcpRcvbuf(new_socket, Config.tcpRcvBufsz);
1760
1761 return new_socket;
1762}
1763
#define ScheduleCallHere(call)
Definition: AsyncCall.h:164
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
Definition: CodeContext.h:112
CommCbFunPtrCallT< Dialer > * commCbCall(int debugSection, int debugLevel, const char *callName, const Dialer &dialer)
Definition: CommCalls.h:342
void IOCB(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag flag, int xerrno, void *data)
Definition: CommCalls.h:36
void CLCB(const CommCloseCbParams &params)
Definition: CommCalls.h:42
#define COMM_TRANSPARENT
Definition: Connection.h:50
#define COMM_NOCLOEXEC
Definition: Connection.h:47
#define COMM_REUSEPORT
Definition: Connection.h:52
#define COMM_DOBIND
Definition: Connection.h:49
#define COMM_NONBLOCKING
Definition: Connection.h:46
#define COMM_REUSEADDR
Definition: Connection.h:48
#define COMMIO_FD_WRITECB(fd)
Definition: IoCallback.h:79
#define COMMIO_FD_READCB(fd)
Definition: IoCallback.h:78
int size
Definition: ModDevPoll.cc:75
time_t squid_curtime
Definition: stub_libtime.cc:20
class SquidConfig Config
Definition: SquidConfig.cc:12
StatCounters statCounter
Definition: StatCounters.cc:12
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:59
int conn
the current server connection FD
Definition: Transport.cc:26
squidaio_request_t * head
Definition: aiops.cc:126
#define assert(EX)
Definition: assert.h:19
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:318
bool canceled()
Definition: AsyncCall.h:52
void dequeue(AsyncCall::Pointer &head, AsyncCall::Pointer &prev)
remove us from the queue; we are head unless we are queued after prev
Definition: AsyncCall.cc:84
bool cancel(const char *reason)
Definition: AsyncCall.cc:56
AsyncCall::Pointer & Next()
Definition: AsyncCall.h:65
void setNext(AsyncCall::Pointer aNext)
Definition: AsyncCall.h:61
Base class for Squid-to-client bandwidth limiting.
double bucketLevel
how much can be written now
void refillBucket()
Increases the bucket level with the writeSpeedLimit speed.
double bucketSizeLimit
maximum bucket size
virtual void onFdClosed()
Performs cleanup when the related file descriptor becomes closed.
double prevTime
previous time when we checked
virtual bool applyQuota(int &nleft, Comm::IoCallback *state)
double writeSpeedLimit
Write speed limit in bytes per second.
static BandwidthBucket * SelectBucket(fde *f)
virtual void reduceBucket(const int len)
Decreases the bucket level.
bool selectWaiting
is between commSetSelect and commHandleWrite
virtual void reduceBucket(int len) override
Decreases the bucket level.
Definition: comm.cc:1339
void quotaDequeue()
pops queue head from queue
Definition: comm.cc:1257
bool hasQueue() const
whether any clients are waiting for write quota
Definition: comm.cc:1218
void writeOrDequeue()
either selects the head descriptor for writing or calls quotaDequeue()
Definition: comm.cc:1196
void setWriteLimiter(const int aWriteSpeedLimit, const double anInitialBurst, const double aHighWatermark)
Definition: comm.cc:1348
CommQuotaQueue * quotaQueue
clients waiting for more write quota
Definition: ClientInfo.h:68
virtual void onFdClosed() override
Performs cleanup when the related file descriptor becomes closed.
Definition: comm.cc:1331
bool eventWaiting
waiting for commHandleWriteHelper event to fire
Definition: ClientInfo.h:71
virtual void scheduleWrite(Comm::IoCallback *state) override
Will plan another write call.
Definition: comm.cc:1322
time_t last_seen
Definition: ClientInfo.h:63
unsigned int quotaEnqueue(int fd)
client starts waiting in queue; create the queue if necessary
Definition: comm.cc:1249
virtual int quota() override
allocate quota for a just dequeued client
Definition: comm.cc:1277
int quotaPeekFd() const
returns the next fd reservation
Definition: comm.cc:1233
int rationedQuota
precomputed quota preserving fairness among clients
Definition: ClientInfo.h:69
unsigned int quotaPeekReserv() const
returns the next reserv. to pop
Definition: comm.cc:1241
int rationedCount
number of clients that will receive rationedQuota
Definition: ClientInfo.h:70
bool firstTimeConnection
is this first time connection for this client
Definition: ClientInfo.h:66
void kickQuotaQueue()
Definition: comm.cc:1264
virtual bool applyQuota(int &nleft, Comm::IoCallback *state) override
Definition: comm.cc:1308
bool writeLimitingActive
Is write limiter active.
Definition: ClientInfo.h:65
static const Pointer & Current()
Definition: CodeContext.cc:33
static void Reset()
forgets the current context, setting it to nil/unknown
Definition: CodeContext.cc:75
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:90
~CommQuotaQueue()
Definition: comm.cc:1378
int ins
number of enqueue calls, used to generate a "reservation" ID
Definition: ClientInfo.h:126
ClientInfo * clientInfo
bucket responsible for quota maintenance
Definition: ClientInfo.h:123
void dequeue()
removes queue head
Definition: comm.cc:1396
unsigned int enqueue(int fd)
places the given fd at the end of the queue; returns reservation ID
Definition: comm.cc:1385
int outs
number of dequeue calls, used to check the "reservation" ID
Definition: ClientInfo.h:127
size_t size() const
Definition: ClientInfo.h:118
Store fds
descriptor queue
Definition: ClientInfo.h:132
bool empty() const
Definition: ClientInfo.h:117
CommQuotaQueue(ClientInfo *info)
Definition: comm.cc:1372
int front() const
Definition: ClientInfo.h:119
virtual int checkEvents(int timeout)
Definition: comm.cc:1649
static AcceptLimiter & Instance()
Details about a particular Comm IO callback event.
Definition: IoCallback.h:30
Comm::ConnectionPointer conn
Definition: IoCallback.h:33
unsigned int quotaQueueReserv
reservation ID from CommQuotaQueue
Definition: IoCallback.h:42
An unordered collection of unique descriptors with O(1) add/del/has ops.
Definition: DescriptorSet.h:19
const_iterator begin() const
begin iterator a la STL; may become invalid if the object is modified
Definition: DescriptorSet.h:40
const_iterator end() const
end iterator a la STL; may become invalid if the object is modified
Definition: DescriptorSet.h:42
bool del(int fd)
deletes if there; returns true if deleted
bool empty() const
number of descriptors in the set
Definition: DescriptorSet.h:37
const int * const_iterator
Definition: DescriptorSet.h:22
bool has(const int fd) const
checks whether fd is in the set
Definition: DescriptorSet.h:28
bool add(int fd)
adds if unique; returns true if added
FD event (FDECB) dialer.
Definition: CommCalls.h:293
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:792
static void InitAddr(struct addrinfo *&ai)
Definition: Address.cc:668
bool setIPv4()
Definition: Address.cc:224
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:686
void getAddrInfo(struct addrinfo *&ai, int force=AF_UNSPEC) const
Definition: Address.cc:599
bool isIPv4() const
Definition: Address.cc:158
bool isNoAddr() const
Definition: Address.cc:284
bool isAnyAddr() const
Definition: Address.cc:170
bool isIPv6() const
Definition: Address.cc:164
unsigned short port() const
Definition: Address.cc:778
C * getRaw() const
Definition: RefCount.h:80
size_t tcpRcvBufsz
Definition: SquidConfig.h:242
time_t write
Definition: SquidConfig.h:111
struct SquidConfig::@98 Timeout
struct StatCounters::@136::@141 sock
struct StatCounters::@136 syscalls
Definition: fde.h:52
#define SUN_LEN(ptr)
Definition: cmsg.h:113
void fd_open(const int fd, unsigned int, const char *description)
Definition: minimal.cc:14
void fd_close(const int fd)
Definition: minimal.cc:20
void commCallCloseHandlers(int fd)
Definition: comm.cc:727
int commSetNonBlocking(int fd)
Definition: comm.cc:1038
void _comm_close(int fd, char const *file, int line)
Definition: comm.cc:814
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:921
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:950
int commSetConnTimeout(const Comm::ConnectionPointer &conn, int timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:563
unsigned short comm_local_port(int fd)
Definition: comm.cc:161
void commStopHalfClosedMonitor(int const fd)
stop waiting for possibly half-closed connection to close
Definition: comm.cc:1609
void old_comm_reset_close(int fd)
Definition: comm.cc:768
static bool WillCheckHalfClosed
the set of half-closed FDs
Definition: comm.cc:72
bool comm_has_incomplete_write(int fd)
Definition: comm.cc:148
static void comm_init_opened(const Comm::ConnectionPointer &conn, const char *note, struct addrinfo *AI)
update FD tables after a local or remote (IPC) comm_openex();
Definition: comm.cc:413
int comm_openex(int sock_type, int proto, Ip::Address &addr, int flags, const char *note)
Definition: comm.cc:331
static bool AlreadyTimedOut(fde *F)
Definition: comm.cc:1472
static EVH commHalfClosedCheck
true if check is scheduled
Definition: comm.cc:73
void commSetCloseOnExec(int fd)
Definition: comm.cc:1099
static bool limitError(int const anErrno)
Definition: comm.cc:264
void commUnsetFdTimeout(int fd)
clear a timeout handler by FD number
Definition: comm.cc:550
static bool writeTimedOut(int fd)
Definition: comm.cc:1487
static IOCB commHalfClosedReader
Definition: comm.cc:59
int ignoreErrno(int ierrno)
Definition: comm.cc:1412
static void commSetReuseAddr(int)
Definition: comm.cc:1009
static void comm_set_v6only(int fd, int tos)
Definition: comm.cc:270
void comm_init(void)
Definition: comm.cc:1138
int commUnsetNonBlocking(int fd)
Definition: comm.cc:1071
int comm_open_uds(int sock_type, int proto, struct sockaddr_un *addr, int flags)
Create a unix-domain socket (UDS) that only supports FD_MSGHDR I/O.
Definition: comm.cc:1682
void checkTimeouts(void)
Definition: comm.cc:1499
void comm_exit(void)
Definition: comm.cc:1161
int comm_udp_sendto(int fd, const Ip::Address &to_addr, const void *buf, int len)
Definition: comm.cc:890
void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note)
Definition: comm.cc:233
static void commHandleWriteHelper(void *data)
Definition: comm.cc:1172
bool isOpen(const int fd)
Definition: comm.cc:85
bool commHasHalfClosedMonitor(int fd)
checks whether we are waiting for possibly half-closed connection to close
Definition: comm.cc:1602
static void commSetTcpRcvbuf(int, int)
Definition: comm.cc:1019
void commCloseAllSockets(void)
Definition: comm.cc:1442
void comm_import_opened(const Comm::ConnectionPointer &conn, const char *note, struct addrinfo *AI)
update Comm state after getting a comm_open() FD from another process
Definition: comm.cc:510
int comm_udp_recv(int fd, void *buf, size_t len, int flags)
Definition: comm.cc:135
static void commPlanHalfClosedCheck()
Definition: comm.cc:1563
static DescriptorSet * TheHalfClosed
Definition: comm.cc:71
static Comm::Flag commBind(int s, struct addrinfo &)
Definition: comm.cc:204
int comm_open(int sock_type, int proto, Ip::Address &addr, int flags, const char *note)
Definition: comm.cc:223
int comm_connect_addr(int sock, const Ip::Address &address)
Definition: comm.cc:602
static int comm_apply_flags(int new_socket, Ip::Address &addr, int flags, struct addrinfo *AI)
Definition: comm.cc:435
ssize_t comm_udp_send(int s, const void *buf, size_t len, int flags)
Definition: comm.cc:142
static void comm_empty_os_read_buffers(int fd)
Definition: comm.cc:99
static void commStartTlsClose(const FdeCbParams &params)
Definition: comm.cc:782
void commStartHalfClosedMonitor(int fd)
Start waiting for a possibly half-closed connection to close.
Definition: comm.cc:1552
static void commSetNoLinger(int)
Definition: comm.cc:994
int commUnsetConnTimeout(const Comm::ConnectionPointer &conn)
Definition: comm.cc:589
int comm_udp_recvfrom(int fd, void *buf, size_t len, int flags, Ip::Address &from)
Definition: comm.cc:122
static void comm_set_transparent(int fd)
Definition: comm.cc:289
void comm_reset_close(const Comm::ConnectionPointer &conn)
Definition: comm.cc:753
static void comm_close_complete(const FdeCbParams &params)
Definition: comm.cc:788
#define comm_close(x)
Definition: comm.h:27
#define SQUID_NONBLOCK
A const & min(A const &lhs, A const &rhs)
#define DBG_DATA
Definition: Stream.h:43
#define MYNAME
Definition: Stream.h:238
#define DBG_IMPORTANT
Definition: Stream.h:41
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
#define DBG_CRITICAL
Definition: Stream.h:40
#define COMM_SELECT_READ
Definition: defines.h:24
#define COMM_SELECT_WRITE
Definition: defines.h:25
@ FD_SOCKET
Definition: enums.h:16
@ FD_FILE
Definition: enums.h:15
@ FD_MSGHDR
Definition: enums.h:18
int type
Definition: errorpage.cc:152
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:107
void EVH(void *)
Definition: event.h:18
void fatal_dump(const char *message)
Definition: fatal.cc:78
void fdAdjustReserved(void)
Definition: fd.cc:287
#define fd_table
Definition: fde.h:189
int FD_READ_METHOD(int fd, char *buf, int len)
Definition: fde.h:194
int opt_reuseaddr
int shutting_down
int Squid_MaxFD
int RESERVED_FD
int Biggest_FD
int reconfiguring
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
#define IPV6_SPECIAL_SPLITSTACK
Definition: tools.h:22
#define IPV6_SPECIAL_V4MAPPING
Definition: tools.h:21
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
PF HandleWrite
Definition: forward.h:33
void ReadCancel(int fd, AsyncCall::Pointer &callback)
Cancel the read pending on FD. No action if none pending.
Definition: Read.cc:219
void CallbackTableDestruct()
Definition: IoCallback.cc:34
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
Flag
Definition: Flag.h:15
@ SHUTDOWN
Definition: Flag.h:20
@ OK
Definition: Flag.h:16
@ ERR_CLOSING
Definition: Flag.h:25
@ IDLE
Definition: Flag.h:21
@ TIMEOUT
Definition: Flag.h:19
@ COMM_ERROR
Definition: Flag.h:17
@ ERR_PROTOCOL
Definition: Flag.h:26
@ INPROGRESS
Definition: Flag.h:22
Comm::Flag DoSelect(int)
Do poll and trigger callback functions as appropriate.
Definition: ModDevPoll.cc:311
void CallbackTableInit()
Definition: IoCallback.cc:22
void SelectLoopInit(void)
Initialize the module on Squid startup.
Definition: ModDevPoll.cc:176
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:223
SSL Connection
Definition: Session.h:45
void SessionSendGoodbye(const Security::SessionPointer &)
send the shutdown/bye notice for an active TLS session.
Definition: Session.cc:199
void Controller::create() STUB void Controller Controller nil
static void handler(int signo)
Definition: purge.cc:854
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
#define TRUE
Definition: std-includes.h:55
#define FALSE
Definition: std-includes.h:56
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
struct sockaddr * ai_addr
socklen_t ai_addrlen
struct addrinfo * ai_next
char sun_path[256]
Definition: cmsg.h:108
Comm::AcceptLimiter dummy
Definition: stub_libcomm.cc:16
double current_dtime
the current UNIX time in seconds (with microsecond precision)
Definition: stub_libtime.cc:19
void leave_suid(void)
Definition: tools.cc:556
void enter_suid(void)
Definition: tools.cc:620
int socklen_t
Definition: types.h:158
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors