52#ifdef HAVE_NETINET_TCP_H
53#include <netinet/tcp.h>
86static void commSetTcpNoDelay(
int);
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);
150 return xsend(s, buf, len, flags);
170 struct addrinfo *addr =
nullptr;
176 debugs(5, 0,
"comm_local_port: FD " << fd <<
" has been closed.");
188 if (
xgetsockname(fd, addr->ai_addr, &(addr->ai_addrlen)) ) {
214#if defined(IP_BIND_ADDRESS_NO_PORT)
216 if (
xsetsockopt(fd, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, &flag,
sizeof(flag)) < 0) {
217 const auto savedErrno = errno;
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);
255 return comm_openex(sock_type, proto, addr, flags, note);
284 sock =
comm_openex(sock_type, proto, addr, flags, note);
292 return anErrno == ENFILE || anErrno == EMFILE;
299 if (
xsetsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &tos,
sizeof(
int)) < 0) {
317#if _SQUID_LINUX_ && defined(IP_TRANSPARENT)
318# define soLevel SOL_IP
319# define soFlag IP_TRANSPARENT
320 bool doneSuid =
false;
322#elif defined(SO_BINDANY)
323# define soLevel SOL_SOCKET
324# define soFlag SO_BINDANY
326 bool doneSuid =
true;
328#elif defined(IP_BINDANY)
329# define soLevel IPPROTO_IP
330# define soFlag IP_BINDANY
332 bool doneSuid =
true;
335 debugs(50,
DBG_CRITICAL,
"WARNING: comm_open: setsockopt(TPROXY) not supported on this platform");
339#if defined(soLevel) && defined(soFlag)
341 if (
xsetsockopt(fd, soLevel, soFlag, &tos,
sizeof(
int)) < 0) {
346 fd_table[fd].flags.transparent =
true;
365 struct addrinfo *AI =
nullptr;
370 if (!Ip::EnableIpv6 && addr.
isIPv6()) {
371 debugs(50, 2,
"refusing to open an IPv6 socket when IPv6 support is disabled: " << addr);
378 AI->ai_socktype = sock_type;
379 AI->ai_protocol = proto;
381 debugs(50, 3,
"comm_openex: Attempt open socket for: " << addr );
383 new_socket =
xsocket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);
384 const auto firstErrNo = errno;
388 if ( new_socket < 0 && Ip::EnableIpv6 && addr.
isIPv6() && addr.
setIPv4() ) {
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);
399 debugs(50, 2,
"attempt open " << note <<
" socket on: " << addr);
402 if (new_socket < 0) {
423 conn->
fd = new_socket;
425 debugs(50, 3,
"comm_openex: Opened socket " << conn <<
" : family=" << AI->ai_family <<
", type=" << AI->ai_socktype <<
", protocol=" << AI->ai_protocol );
458 debugs(5, 5, conn <<
" is a new socket");
479 const int sock_type = AI->ai_socktype;
487 if (addr.
port() > (
unsigned short) 0) {
489 if (sock_type != SOCK_DGRAM)
504 debugs(5,
DBG_IMPORTANT,
"WARNING: Squid is attempting to bind() port " << addr <<
" without being a listener.");
506 debugs(5,
DBG_CRITICAL,
"ERROR: Squid is attempting to bind() port " << addr <<
"!!");
508#if defined(SO_REUSEPORT)
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));
539 if (sock_type == SOCK_STREAM)
540 commSetTcpNoDelay(new_socket);
568 if (AI->ai_socktype == SOCK_STREAM)
583 debugs(5, 3,
"Remove timeout for FD " << fd);
596 debugs(5, 3, conn <<
" timeout " << timeout);
606 if (callback !=
nullptr) {
608 Params ¶ms = GetCommParams<Params>(callback);
620 debugs(5, 3,
"Remove timeout for " << conn);
638 struct addrinfo *AI =
nullptr;
642 debugs(5, 9,
"connecting socket FD " << sock <<
" to " << address <<
" (want family: " << F->
sock_family <<
")");
676 x =
xconnect(sock, AI->ai_addr, AI->ai_addrlen);
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);
693 xerrno = EINPROGRESS;
698 errlen =
sizeof(err);
699 x =
xgetsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);
710 if (x < 0 && xerrno == EPIPE)
720 if (xerrno == 0 || xerrno == EISCONN)
724 else if (xerrno == EAFNOSUPPORT || xerrno == EINVAL)
734 debugs(5,
DBG_DATA,
"comm_connect_addr: FD " << sock <<
" connected to " << address);
736 debugs(5,
DBG_DATA,
"comm_connect_addr: FD " << sock <<
" connection pending");
747 debugs(5, 5,
"commCallCloseHandlers: FD " << fd);
755 debugs(5, 5,
"commCallCloseHandlers: ch->handler=" << call);
770 struct linger l = {};
771 l.l_onoff = (enabled ==
OnOff::on ? 1 : 0);
774 fd_table[fd].flags.harshClosureRequested = (l.l_onoff && !l.l_linger);
776 if (
xsetsockopt(fd, SOL_SOCKET, SO_LINGER, &l,
sizeof(l)) < 0) {
777 const auto xerrno = errno;
817 F->dynamicTlsContext.reset();
841 debugs(5, 3,
"start closing FD " << fd <<
" by " << file <<
":" << line);
856 debugs(50,
DBG_IMPORTANT,
"ERROR: Squid BUG #3556: FD " << fd <<
" is not an open socket.");
871 const auto startCall =
asyncCall(5, 4,
"commStartTlsClose",
893 if (bucket->selectWaiting)
894 bucket->onFdClosed();
904 const auto completeCall =
asyncCall(5, 4,
"comm_close_complete",
918 debugs(50, 3,
"comm_udp_sendto: Attempt to send UDP packet to " << to_addr <<
921 struct addrinfo *AI =
nullptr;
923 int x =
xsendto(fd, buf, len, 0, AI->ai_addr, AI->ai_addrlen);
933 if (ECONNREFUSED != xerrno)
944 debugs(5, 5,
"comm_add_close_handler: FD " << fd <<
", handler=" <<
945 handler <<
", data=" << data);
956 debugs(5, 5,
"comm_add_close_handler: FD " << fd <<
", AsyncCall=" << call);
975 debugs(5, 5,
"comm_remove_close_handler: FD " << fd <<
", handler=" <<
976 handler <<
", data=" << data);
979 for (p =
fd_table[fd].closeHandler; p !=
nullptr; prev = p, p = p->
Next()) {
981 const Call *call =
dynamic_cast<const Call*
>(p.
getRaw());
986 const Params ¶ms = GetCommParams<Params>(p);
987 if (call->dialer.handler == handler && params.data == data)
994 p->
cancel(
"comm_remove_close_handler");
1003 debugs(5, 5,
"comm_remove_close_handler: FD " << fd <<
", AsyncCall=" << call);
1007 for (p =
fd_table[fd].closeHandler; p !=
nullptr && p != call; prev = p, p = p->
Next());
1011 call->
cancel(
"comm_remove_close_handler");
1035#ifdef TCP_WINDOW_CLAMP
1047 int nonblocking =
TRUE;
1049 if (ioctl(fd, FIONBIO, &nonblocking) < 0) {
1059 if ((flags = fcntl(fd, F_GETFL,
dummy)) < 0) {
1072 fd_table[fd].flags.nonblocking =
true;
1080 int nonblocking =
FALSE;
1082 if (ioctlsocket(fd, FIONBIO, (
unsigned long *) &nonblocking) < 0) {
1087 if ((flags = fcntl(fd, F_GETFL,
dummy)) < 0) {
1100 fd_table[fd].flags.nonblocking =
false;
1111 if ((flags = fcntl(fd, F_GETFD,
dummy)) < 0) {
1117 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
1126commSetTcpNoDelay(
int fd)
1187 debugs(77, 3,
"emptied queue");
1196 CallBack(headFde.codeContext, [&] {
1197 const auto ccb = COMMIO_FD_WRITECB(head);
1199 if (headFde.clientInfo == this &&
1200 quotaPeekReserv() == ccb->quotaQueueReserv &&
1201 !headFde.closing()) {
1204 Comm::SetSelect(head, COMM_SELECT_WRITE, Comm::HandleWrite, ccb, 0);
1205 selectWaiting = true;
1263 const double delay = (
bucketLevel < 1.0) ? 1.0 : 0.0;
1345 debugs(77,5,
"Write limits for " << (
const char*)
key <<
1346 " speed=" << aWriteSpeedLimit <<
" burst=" << anInitialBurst <<
1347 " highwatermark=" << aHighWatermark);
1383 ": FD " << fd <<
" with qqid" << (
ins+1) <<
' ' <<
fds.size());
1395 ": FD " <<
fds.front() <<
" with qqid" << (
outs+1) <<
' ' <<
1414#if EAGAIN != EWOULDBLOCK
1457 debugs(5, 5,
"commCloseAllSockets: FD " << fd <<
": Calling timeout handler");
1460 debugs(5, 5,
"commCloseAllSockets: FD " << fd <<
": calling comm_reset_close()");
1506 debugs(5, 5,
"checkTimeouts: FD " << fd <<
" auto write timeout");
1527 debugs(5, 5,
"checkTimeouts: FD " << fd <<
" Expired");
1530 debugs(5, 5,
"checkTimeouts: FD " << fd <<
": Call timeout handler");
1535 debugs(5, 5,
"checkTimeouts: FD " << fd <<
": Forcing comm_close()");
1581 AsyncCall::Pointer call = commCbCall(5,4,
"commHalfClosedReader",
1582 CommIoCbPtrFun(&commHalfClosedReader, nullptr));
1583 Comm::Read(c, call);
1584 fd_table[c->fd].halfClosedReader = call;
1610 if (reader !=
nullptr)
1612 fd_table[fd].halfClosedReader =
nullptr;
1626 fd_table[conn->
fd].halfClosedReader =
nullptr;
1634 debugs(5, 3,
"closing " << conn);
1646 static time_t last_timeout = 0;
1670 fatal_dump(
"comm.cc: Internal error -- this should never happen.");
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;
1702 if ((new_socket =
xsocket(AI.ai_family, AI.ai_socktype, AI.ai_protocol)) < 0) {
1717 debugs(50, 3,
"Opened UDS FD " << new_socket <<
" : family=" << AI.ai_family <<
", type=" << AI.ai_socktype <<
", protocol=" << AI.ai_protocol);
1720 debugs(50, 5,
"FD " << new_socket <<
" is a new socket");
1725 fd_table[new_socket].sock_family = AI.ai_family;
1748 if (sock_type == SOCK_STREAM)
1749 commSetTcpNoDelay(new_socket);
#define ScheduleCallHere(call)
RefCount< AsyncCallT< Dialer > > asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
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)
void IOCB(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag flag, int xerrno, void *data)
void CLCB(const CommCloseCbParams ¶ms)
#define COMM_DOBIND_PORT_LATER
Internal Comm optimization: Keep the source port unassigned until connect(2)
#define COMMIO_FD_WRITECB(fd)
#define COMMIO_FD_READCB(fd)
OnOff
safer than bool in a list of integer-like function parameters
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
squidaio_request_t * head
#define SQUID_TCP_SO_RCVBUF
#define CBDATA_CLASS_INIT(type)
void dequeue(AsyncCall::Pointer &head, AsyncCall::Pointer &prev)
remove us from the queue; we are head unless we are queued after prev
bool cancel(const char *reason)
AsyncCall::Pointer & Next()
void setNext(AsyncCall::Pointer aNext)
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.
void quotaDequeue()
pops queue head from queue
bool hasQueue() const
whether any clients are waiting for write quota
void writeOrDequeue()
either selects the head descriptor for writing or calls quotaDequeue()
void setWriteLimiter(const int aWriteSpeedLimit, const double anInitialBurst, const double aHighWatermark)
CommQuotaQueue * quotaQueue
clients waiting for more write quota
void onFdClosed() override
Performs cleanup when the related file descriptor becomes closed.
bool eventWaiting
waiting for commHandleWriteHelper event to fire
void scheduleWrite(Comm::IoCallback *state) override
Will plan another write call.
unsigned int quotaEnqueue(int fd)
client starts waiting in queue; create the queue if necessary
int quota() override
allocate quota for a just dequeued client
int quotaPeekFd() const
returns the next fd reservation
int rationedQuota
precomputed quota preserving fairness among clients
unsigned int quotaPeekReserv() const
returns the next reservation to pop
int rationedCount
number of clients that will receive rationedQuota
bool firstTimeConnection
is this first time connection for this client
bool applyQuota(int &nleft, Comm::IoCallback *state) override
bool writeLimitingActive
Is write limiter active.
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
ClientInfo * clientInfo
bucket responsible for quota maintenance
void dequeue()
removes queue head
unsigned int enqueue(int fd)
places the given fd at the end of the queue; returns reservation ID
int outs
number of dequeue calls, used to check the "reservation" ID
Store fds
descriptor queue
CommQuotaQueue(ClientInfo *info)
int checkEvents(int timeout) override
static AcceptLimiter & Instance()
InstanceId< Connection, uint64_t > id
Details about a particular Comm IO callback event.
Comm::ConnectionPointer conn
unsigned int quotaQueueReserv
reservation ID from CommQuotaQueue
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
static void InitAddr(struct addrinfo *&ai)
static void FreeAddr(struct addrinfo *&ai)
void getAddrInfo(struct addrinfo *&ai, int force=AF_UNSPEC) const
unsigned short port() const
struct SquidConfig::@77 Timeout
struct StatCounters::@112 syscalls
struct StatCounters::@112::@117 sock
bool closing() const
True if comm_close for this fd has been called.
AsyncCall::Pointer timeoutHandler
CodeContextPointer codeContext
What the I/O handlers are supposed to work on.
struct fde::_fde_flags flags
MessageBucket::Pointer writeQuotaHandler
response write limiter, if configured
Security::SessionPointer ssl
read handler for half-closed fds
AsyncCall::Pointer closeHandler
unsigned short remote_port
char ipaddr[MAX_IPSTRLEN]
void fd_open(const int fd, unsigned int, const char *description)
void fd_close(const int fd)
void commCallCloseHandlers(int fd)
void commUnsetConnTimeout(const Comm::ConnectionPointer &conn)
int commSetNonBlocking(int fd)
void _comm_close(int fd, char const *file, int line)
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
static void comm_close_complete(const int fd)
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
unsigned short comm_local_port(int fd)
void commSetConnTimeout(const Comm::ConnectionPointer &conn, time_t timeout, AsyncCall::Pointer &callback)
void commStopHalfClosedMonitor(int const fd)
stop waiting for possibly half-closed connection to close
void old_comm_reset_close(int fd)
static bool WillCheckHalfClosed
the set of half-closed FDs
bool comm_has_incomplete_write(int fd)
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();
static bool AlreadyTimedOut(fde *F)
static EVH commHalfClosedCheck
true if check is scheduled
void commSetCloseOnExec(int fd)
static bool limitError(int const anErrno)
void commUnsetFdTimeout(int fd)
clear a timeout handler by FD number
static bool writeTimedOut(int fd)
static IOCB commHalfClosedReader
int ignoreErrno(int ierrno)
static void commSetReuseAddr(int)
static void comm_set_v6only(int fd, int tos)
int commUnsetNonBlocking(int fd)
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.
static void commConfigureLinger(int fd, OnOff)
int comm_udp_sendto(int fd, const Ip::Address &to_addr, const void *buf, int len)
static int comm_openex(int sock_type, int proto, Ip::Address &, int flags, const char *note)
void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note)
static void commHandleWriteHelper(void *data)
static void commSetBindAddressNoPort(int)
bool isOpen(const int fd)
bool commHasHalfClosedMonitor(int fd)
checks whether we are waiting for possibly half-closed connection to close
static void commSetTcpRcvbuf(int, int)
void commCloseAllSockets(void)
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
int comm_udp_recv(int fd, void *buf, size_t len, int flags)
static void commPlanHalfClosedCheck()
static DescriptorSet * TheHalfClosed
static Comm::Flag commBind(int s, struct addrinfo &)
int comm_open(int sock_type, int proto, Ip::Address &addr, int flags, const char *note)
int comm_connect_addr(int sock, const Ip::Address &address)
static int comm_apply_flags(int new_socket, Ip::Address &addr, int flags, struct addrinfo *AI)
ssize_t comm_udp_send(int s, const void *buf, size_t len, int flags)
static void comm_empty_os_read_buffers(int fd)
static void commStartTlsClose(const int fd)
void commStartHalfClosedMonitor(int fd)
Start waiting for a possibly half-closed connection to close.
int comm_udp_recvfrom(int fd, void *buf, size_t len, int flags, Ip::Address &from)
static void comm_set_transparent(int fd)
void comm_reset_close(const Comm::ConnectionPointer &conn)
A const & min(A const &lhs, A const &rhs)
#define debugs(SECTION, LEVEL, CONTENT)
#define COMM_SELECT_WRITE
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
void fatal_dump(const char *message)
void fdAdjustReserved(void)
int FD_READ_METHOD(int fd, char *buf, int len)
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
void ReadCancel(int fd, AsyncCall::Pointer &callback)
Cancel the read pending on FD. No action if none pending.
bool IsConnOpen(const Comm::ConnectionPointer &conn)
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.
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
int xsocket(int domain, int type, int protocol)
POSIX socket(2) equivalent.
ssize_t xsendto(int socketFd, const void *buf, size_t bufLength, int flags, const struct sockaddr *to, socklen_t toLength)
POSIX sendto(2) equivalent.
int xbind(int socketFd, const struct sockaddr *sa, socklen_t saLength)
POSIX bind(2) equivalent.
ssize_t xsend(int socketFd, const void *buf, size_t bufLength, int flags)
POSIX send(2) equivalent.
ssize_t xrecvfrom(int socketFd, void *buf, size_t bufLength, int flags, struct sockaddr *from, socklen_t *fromLength)
POSIX recvfrom(2) equivalent.
int xgetsockopt(int socketFd, int level, int optionName, void *optionValue, socklen_t *optionLength)
POSIX getsockopt(2) equivalent.
int xgetsockname(int socketFd, struct sockaddr *sa, socklen_t *saLength)
POSIX getsockname(2) equivalent.
int xsetsockopt(int socketFd, int level, int option, const void *value, socklen_t valueLength)
POSIX setsockopt(2) equivalent.
int xconnect(int socketFd, const struct sockaddr *sa, socklen_t saLength)
POSIX connect(2) equivalent.
bool harshClosureRequested
whether comm_reset_close() (or old_comm_reset_close()) has been called
bool close_request
true if file_ or comm_close has been called
Comm::AcceptLimiter dummy
double current_dtime
the current UNIX time in seconds (with microsecond precision)
int xclose(int fd)
POSIX close(2) equivalent.
const char * xstrerr(int error)