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