From 111a3dde86176277c9fbc60a1b401d908b8b7b24 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 20 May 2019 19:35:03 -0700 Subject: [PATCH] RX timeout Enable idle timeout if both peers have version>=2. --- src/remote/codec.cpp | 75 ++++++++++++++++++++++++++++++++++--------- src/remote/pv/codec.h | 3 ++ 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/src/remote/codec.cpp b/src/remote/codec.cpp index 424b4db..bf72803 100644 --- a/src/remote/codec.cpp +++ b/src/remote/codec.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -147,7 +148,12 @@ void AbstractCodec::processHeader() { int8_t magicCode = _socketBuffer.getByte(); // version - _version = _socketBuffer.getByte(); + int8_t ver = _socketBuffer.getByte(); + if(_version!=ver) { + // enable timeout if both ends support + _version = ver; + setRxTimeout(getRevision()>1); + } // flags _flags = _socketBuffer.getByte(); @@ -1115,6 +1121,10 @@ void BlockingTCPTransportCodec::receiveThread() */ Transport::shared_pointer ptr(this->shared_from_this()); + // initially enable timeout for all clients to weed out + // impersonators (security scanners?) + setRxTimeout(true); + while (this->isOpen()) { try { @@ -1166,6 +1176,27 @@ void BlockingTCPTransportCodec::sendThread() _sendQueue.clear(); } +void BlockingTCPTransportCodec::setRxTimeout(bool ena) +{ + double timeout = !ena ? 0.0 : std::max(0.0, _context->getConfiguration()->getPropertyAsDouble("EPICS_PVA_CONN_TMO", 30.0)); +#ifdef _WIN32 + DWORD timo = DWORD(timeout*1000); // in milliseconds +#else + timeval timo; + timo.tv_sec = unsigned(timeout); + timo.tv_usec = (timeout-timo.tv_sec)*1e6; +#endif + + int ret = setsockopt(_channel, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timo, sizeof(timo)); + if(ret==-1) { + int err = SOCKERRNO; + static int lasterr; + if(err!=lasterr) { + errlogPrintf("%s: Unable to set RX timeout: %d\n", _socketName.c_str(), err); + lasterr = err; + } + } +} void BlockingTCPTransportCodec::sendBufferFull(int tries) { // TODO constants @@ -1285,21 +1316,35 @@ int BlockingTCPTransportCodec::read(epics::pvData::ByteBuffer* dst) { // NOTE: do not log here, you might override SOCKERRNO relevant to recv() operation above - if(unlikely(bytesRead<=0)) { - - if (bytesRead<0) - { - int socketError = SOCKERRNO; - - // TODO SOCK_ENOBUFS, for read? - // interrupted or timeout - if (socketError == SOCK_EINTR || - socketError == EAGAIN || - socketError == SOCK_EWOULDBLOCK) - continue; - } - + if(unlikely(bytesRead==0)) { return -1; // 0 means connection loss for blocking transport, notify codec by returning -1 + + } else if(unlikely(bytesRead<0)) { + int err = SOCKERRNO; + + if(err==SOCK_EINTR) { + // interrupted by signal. Retry + continue; + + } else if(err==SOCK_EWOULDBLOCK || err==EAGAIN || err==SOCK_EINPROGRESS + || err==SOCK_ETIMEDOUT + || err==SOCK_ECONNABORTED || err==SOCK_ECONNRESET + ) { + // different ways of saying timeout. + // Linux: EAGAIN or EWOULDBLOCK, or EINPROGRESS + // WIN32: WSAETIMEDOUT + // others that RSRV checks for, but may not need to, ECONNABORTED, ECONNRESET + + // Note: with windows, after ETIMEOUT leaves the socket in an undefined state. + // so it must be closed. (cf. SO_RCVTIMEO) + + return -1; + + } else { + // some other (fatal) error + errlogPrintf("%s : Connection closed with RX socket error %d\n", _socketName.c_str(), err); + return -1; + } } dst->setPosition(dst->getPosition() + bytesRead); diff --git a/src/remote/pv/codec.h b/src/remote/pv/codec.h index 3772e8f..4807337 100644 --- a/src/remote/pv/codec.h +++ b/src/remote/pv/codec.h @@ -252,6 +252,7 @@ protected: void send(epics::pvData::ByteBuffer *buffer); void flushSendBuffer(); + virtual void setRxTimeout(bool ena) {} ReadMode _readMode; int8_t _version; @@ -436,6 +437,8 @@ private: void sendThread(); protected: + virtual void setRxTimeout(bool ena) OVERRIDE FINAL; + virtual void sendBufferFull(int tries) OVERRIDE FINAL; /**