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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors