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