LCOV - code coverage report
Current view: top level - inet - TCPEndPoint.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 110 196 56.1 %
Date: 2024-02-15 08:20:41 Functions: 17 30 56.7 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020-2021 Project CHIP Authors
       4             :  *    Copyright (c) 2013-2018 Nest Labs, Inc.
       5             :  *
       6             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7             :  *    you may not use this file except in compliance with the License.
       8             :  *    You may obtain a copy of the License at
       9             :  *
      10             :  *        http://www.apache.org/licenses/LICENSE-2.0
      11             :  *
      12             :  *    Unless required by applicable law or agreed to in writing, software
      13             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             :  *    See the License for the specific language governing permissions and
      16             :  *    limitations under the License.
      17             :  */
      18             : 
      19             : /**
      20             :  *    @file
      21             :  *      This file implements the <tt>Inet::TCPEndPoint</tt> class,
      22             :  *      where the CHIP Inet Layer encapsulates methods for interacting
      23             :  *      with TCP transport endpoints (SOCK_DGRAM sockets on Linux and
      24             :  *      BSD-derived systems) or LwIP TCP protocol control blocks, as
      25             :  *      the system is configured accordingly.
      26             :  *
      27             :  */
      28             : 
      29             : #include <inet/TCPEndPoint.h>
      30             : 
      31             : #include <inet/InetFaultInjection.h>
      32             : #include <inet/arpa-inet-compatibility.h>
      33             : 
      34             : #include <lib/support/CodeUtils.h>
      35             : #include <lib/support/SafeInt.h>
      36             : #include <lib/support/logging/CHIPLogging.h>
      37             : #include <system/SystemFaultInjection.h>
      38             : 
      39             : #include <stdio.h>
      40             : #include <string.h>
      41             : #include <utility>
      42             : 
      43             : namespace chip {
      44             : namespace Inet {
      45             : 
      46          10 : CHIP_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr)
      47             : {
      48          10 :     VerifyOrReturnError(mState == State::kReady, CHIP_ERROR_INCORRECT_STATE);
      49           9 :     CHIP_ERROR res = CHIP_NO_ERROR;
      50             : 
      51           9 :     if (addr != IPAddress::Any && addr.Type() != IPAddressType::kAny && addr.Type() != addrType)
      52             :     {
      53           2 :         return INET_ERROR_WRONG_ADDRESS_TYPE;
      54             :     }
      55             : 
      56           7 :     res = BindImpl(addrType, addr, port, reuseAddr);
      57             : 
      58           7 :     if (res == CHIP_NO_ERROR)
      59             :     {
      60           6 :         mState = State::kBound;
      61             :     }
      62             : 
      63           7 :     return res;
      64             : }
      65             : 
      66           7 : CHIP_ERROR TCPEndPoint::Listen(uint16_t backlog)
      67             : {
      68           7 :     VerifyOrReturnError(mState == State::kBound, CHIP_ERROR_INCORRECT_STATE);
      69           6 :     CHIP_ERROR res = CHIP_NO_ERROR;
      70             : 
      71           6 :     res = ListenImpl(backlog);
      72             : 
      73           6 :     if (res == CHIP_NO_ERROR)
      74             :     {
      75             :         // Once Listening, bump the reference count.  The corresponding call to Release() will happen in DoClose().
      76           6 :         Retain();
      77           6 :         mState = State::kListening;
      78             :     }
      79             : 
      80           6 :     return res;
      81             : }
      82             : 
      83           4 : CHIP_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, InterfaceId intfId)
      84             : {
      85           4 :     VerifyOrReturnError(mState == State::kReady || mState == State::kBound, CHIP_ERROR_INCORRECT_STATE);
      86           3 :     CHIP_ERROR res = CHIP_NO_ERROR;
      87             : 
      88           3 :     ReturnErrorOnFailure(ConnectImpl(addr, port, intfId));
      89             : 
      90           3 :     StartConnectTimerIfSet();
      91             : 
      92           3 :     return res;
      93             : }
      94             : 
      95           4 : CHIP_ERROR TCPEndPoint::Send(System::PacketBufferHandle && data, bool push)
      96             : {
      97           4 :     VerifyOrReturnError(mState == State::kConnected || mState == State::kReceiveShutdown, CHIP_ERROR_INCORRECT_STATE);
      98           3 :     CHIP_ERROR res = CHIP_NO_ERROR;
      99             : 
     100           3 :     bool queueWasEmpty = mSendQueue.IsNull();
     101           3 :     if (queueWasEmpty)
     102             :     {
     103           3 :         mSendQueue = std::move(data);
     104             :     }
     105             :     else
     106             :     {
     107           0 :         mSendQueue->AddToEnd(std::move(data));
     108             :     }
     109             : 
     110           3 :     ReturnErrorOnFailure(SendQueuedImpl(queueWasEmpty));
     111             : 
     112           3 :     if (push)
     113             :     {
     114           3 :         res = DriveSending();
     115             :     }
     116             : 
     117           3 :     return res;
     118             : }
     119             : 
     120           0 : CHIP_ERROR TCPEndPoint::SetReceivedDataForTesting(System::PacketBufferHandle && data)
     121             : {
     122           0 :     VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE);
     123             : 
     124           0 :     mRcvQueue = std::move(data);
     125             : 
     126           0 :     return CHIP_NO_ERROR;
     127             : }
     128             : 
     129           0 : uint32_t TCPEndPoint::PendingSendLength()
     130             : {
     131           0 :     if (!mSendQueue.IsNull())
     132             :     {
     133           0 :         return mSendQueue->TotalLength();
     134             :     }
     135           0 :     return 0;
     136             : }
     137             : 
     138           1 : uint32_t TCPEndPoint::PendingReceiveLength()
     139             : {
     140           1 :     if (!mRcvQueue.IsNull())
     141             :     {
     142           0 :         return mRcvQueue->TotalLength();
     143             :     }
     144           1 :     return 0;
     145             : }
     146             : 
     147           0 : void TCPEndPoint::Shutdown()
     148             : {
     149           0 :     VerifyOrReturn(IsConnected());
     150             : 
     151             :     // If fully connected, enter the SendShutdown state.
     152           0 :     if (mState == State::kConnected)
     153             :     {
     154           0 :         mState = State::kSendShutdown;
     155           0 :         DriveSending();
     156             :     }
     157             : 
     158             :     // Otherwise, if the peer has already closed their end of the connection,
     159           0 :     else if (mState == State::kReceiveShutdown)
     160             :     {
     161           0 :         DoClose(CHIP_NO_ERROR, false);
     162             :     }
     163             : }
     164             : 
     165          12 : void TCPEndPoint::Close()
     166             : {
     167             :     // Clear the receive queue.
     168          12 :     mRcvQueue = nullptr;
     169             : 
     170             :     // Suppress closing callbacks, since the application explicitly called Close().
     171          12 :     OnConnectionClosed = nullptr;
     172          12 :     OnPeerClose        = nullptr;
     173          12 :     OnConnectComplete  = nullptr;
     174             : 
     175             :     // Perform a graceful close.
     176          12 :     DoClose(CHIP_NO_ERROR, true);
     177          12 : }
     178             : 
     179           0 : void TCPEndPoint::Abort()
     180             : {
     181             :     // Suppress closing callbacks, since the application explicitly called Abort().
     182           0 :     OnConnectionClosed = nullptr;
     183           0 :     OnPeerClose        = nullptr;
     184           0 :     OnConnectComplete  = nullptr;
     185             : 
     186           0 :     DoClose(CHIP_ERROR_CONNECTION_ABORTED, true);
     187           0 : }
     188             : 
     189          12 : void TCPEndPoint::Free()
     190             : {
     191             :     // Ensure no callbacks to the app after this point.
     192          12 :     OnAcceptError        = nullptr;
     193          12 :     OnConnectComplete    = nullptr;
     194          12 :     OnConnectionReceived = nullptr;
     195          12 :     OnConnectionClosed   = nullptr;
     196          12 :     OnPeerClose          = nullptr;
     197          12 :     OnDataReceived       = nullptr;
     198          12 :     OnDataSent           = nullptr;
     199             : 
     200             :     // Ensure the end point is Closed or Closing.
     201          12 :     Close();
     202             : 
     203             :     // Release the Retain() that happened when the end point was allocated.
     204          12 :     Release();
     205          12 : }
     206             : 
     207             : #if INET_TCP_IDLE_CHECK_INTERVAL > 0
     208           0 : void TCPEndPoint::SetIdleTimeout(uint32_t timeoutMS)
     209             : {
     210           0 :     uint32_t newIdleTimeout = (timeoutMS + (INET_TCP_IDLE_CHECK_INTERVAL - 1)) / INET_TCP_IDLE_CHECK_INTERVAL;
     211           0 :     bool isIdleTimerRunning = IsIdleTimerRunning(GetEndPointManager());
     212             : 
     213           0 :     if (newIdleTimeout > UINT16_MAX)
     214             :     {
     215           0 :         newIdleTimeout = UINT16_MAX;
     216             :     }
     217           0 :     mIdleTimeout = mRemainingIdleTime = static_cast<uint16_t>(newIdleTimeout);
     218             : 
     219           0 :     if (!isIdleTimerRunning && mIdleTimeout)
     220             :     {
     221           0 :         GetSystemLayer().StartTimer(System::Clock::Milliseconds32(INET_TCP_IDLE_CHECK_INTERVAL), HandleIdleTimer,
     222           0 :                                     &GetEndPointManager());
     223             :     }
     224           0 : }
     225             : 
     226             : // static
     227           0 : void TCPEndPoint::HandleIdleTimer(chip::System::Layer * aSystemLayer, void * aAppState)
     228             : {
     229           0 :     auto & endPointManager = *reinterpret_cast<EndPointManager<TCPEndPoint> *>(aAppState);
     230           0 :     bool lTimerRequired    = IsIdleTimerRunning(endPointManager);
     231             : 
     232           0 :     endPointManager.ForEachEndPoint([](TCPEndPoint * lEndPoint) -> Loop {
     233           0 :         if (!lEndPoint->IsConnected())
     234           0 :             return Loop::Continue;
     235           0 :         if (lEndPoint->mIdleTimeout == 0)
     236           0 :             return Loop::Continue;
     237             : 
     238           0 :         if (lEndPoint->mRemainingIdleTime == 0)
     239             :         {
     240           0 :             lEndPoint->DoClose(INET_ERROR_IDLE_TIMEOUT, false);
     241             :         }
     242             :         else
     243             :         {
     244           0 :             --lEndPoint->mRemainingIdleTime;
     245             :         }
     246             : 
     247           0 :         return Loop::Continue;
     248             :     });
     249             : 
     250           0 :     if (lTimerRequired)
     251             :     {
     252           0 :         aSystemLayer->StartTimer(System::Clock::Milliseconds32(INET_TCP_IDLE_CHECK_INTERVAL), HandleIdleTimer, &endPointManager);
     253             :     }
     254           0 : }
     255             : 
     256             : // static
     257           0 : bool TCPEndPoint::IsIdleTimerRunning(EndPointManager<TCPEndPoint> & endPointManager)
     258             : {
     259             :     // See if there are any TCP connections with the idle timer check in use.
     260           0 :     return Loop::Break == endPointManager.ForEachEndPoint([](TCPEndPoint * lEndPoint) {
     261           0 :         return (lEndPoint->mIdleTimeout == 0) ? Loop::Continue : Loop::Break;
     262           0 :     });
     263             : }
     264             : 
     265             : #endif // INET_TCP_IDLE_CHECK_INTERVAL > 0
     266             : 
     267           0 : CHIP_ERROR TCPEndPoint::SetUserTimeout(uint32_t userTimeoutMillis)
     268             : {
     269           0 :     VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE);
     270           0 :     CHIP_ERROR res = CHIP_NO_ERROR;
     271             : 
     272             : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     273             : 
     274             :     // Store the User timeout configuration if it is being overridden.
     275           0 :     mUserTimeoutMillis = userTimeoutMillis;
     276             : 
     277             : #else // !INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     278             : 
     279             :     res = SetUserTimeoutImpl(userTimeoutMillis);
     280             : 
     281             : #endif // !INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     282             : 
     283           0 :     return res;
     284             : }
     285             : 
     286           3 : void TCPEndPoint::StartConnectTimerIfSet()
     287             : {
     288           3 :     if (mConnectTimeoutMsecs > 0)
     289             :     {
     290           0 :         GetSystemLayer().StartTimer(System::Clock::Milliseconds32(mConnectTimeoutMsecs), TCPConnectTimeoutHandler, this);
     291             :     }
     292           3 : }
     293             : 
     294          15 : void TCPEndPoint::StopConnectTimer()
     295             : {
     296          15 :     GetSystemLayer().CancelTimer(TCPConnectTimeoutHandler, this);
     297          15 : }
     298             : 
     299           0 : void TCPEndPoint::TCPConnectTimeoutHandler(chip::System::Layer * aSystemLayer, void * aAppState)
     300             : {
     301           0 :     TCPEndPoint * tcpEndPoint = reinterpret_cast<TCPEndPoint *>(aAppState);
     302           0 :     VerifyOrDie((aSystemLayer != nullptr) && (tcpEndPoint != nullptr));
     303             : 
     304             :     // Close Connection as we have timed out and Connect has not returned to stop this timer.
     305           0 :     tcpEndPoint->DoClose(INET_ERROR_TCP_CONNECT_TIMEOUT, false);
     306           0 : }
     307             : 
     308          64 : bool TCPEndPoint::IsConnected(State state)
     309             : {
     310          64 :     return state == State::kConnected || state == State::kSendShutdown || state == State::kReceiveShutdown ||
     311          64 :         state == State::kClosing;
     312             : }
     313             : 
     314           3 : CHIP_ERROR TCPEndPoint::DriveSending()
     315             : {
     316           3 :     CHIP_ERROR err = DriveSendingImpl();
     317             : 
     318           3 :     if (err != CHIP_NO_ERROR)
     319             :     {
     320           0 :         DoClose(err, false);
     321             :     }
     322             : 
     323           3 :     CHIP_SYSTEM_FAULT_INJECT_ASYNC_EVENT();
     324             : 
     325           3 :     return err;
     326             : }
     327             : 
     328           6 : void TCPEndPoint::DriveReceiving()
     329             : {
     330             :     // If there's data in the receive queue and the app is ready to receive it then call the app's callback
     331             :     // with the entire receive queue.
     332           6 :     if (!mRcvQueue.IsNull() && mReceiveEnabled && OnDataReceived != nullptr)
     333             :     {
     334             :         // Acknowledgement is done after handling the buffers to allow the
     335             :         // application processing to throttle flow.
     336           3 :         uint16_t ackLength = mRcvQueue->TotalLength();
     337           3 :         CHIP_ERROR err     = OnDataReceived(this, std::move(mRcvQueue));
     338           3 :         if (err != CHIP_NO_ERROR)
     339             :         {
     340           0 :             DoClose(err, false);
     341           0 :             return;
     342             :         }
     343           3 :         AckReceive(ackLength);
     344             :     }
     345             : 
     346             :     // If the connection is closing, and the receive queue is now empty, call DoClose() to complete
     347             :     // the process of closing the connection.
     348           6 :     if (mState == State::kClosing && mRcvQueue.IsNull())
     349             :     {
     350           0 :         DoClose(CHIP_NO_ERROR, false);
     351             :     }
     352             : }
     353             : 
     354           3 : void TCPEndPoint::HandleConnectComplete(CHIP_ERROR err)
     355             : {
     356             :     // If the connect succeeded enter the Connected state and call the app's callback.
     357           3 :     if (err == CHIP_NO_ERROR)
     358             :     {
     359             :         // Stop the TCP Connect timer in case it is still running.
     360           3 :         StopConnectTimer();
     361             : 
     362             :         // Mark the connection as being active.
     363           3 :         MarkActive();
     364             : 
     365           3 :         mState = State::kConnected;
     366             : 
     367           3 :         HandleConnectCompleteImpl();
     368             : 
     369           3 :         if (OnConnectComplete != nullptr)
     370             :         {
     371           3 :             OnConnectComplete(this, CHIP_NO_ERROR);
     372             :         }
     373             :     }
     374             : 
     375             :     // Otherwise, close the connection with an error.
     376             :     else
     377             :     {
     378           0 :         DoClose(err, false);
     379             :     }
     380           3 : }
     381             : 
     382          12 : void TCPEndPoint::DoClose(CHIP_ERROR err, bool suppressCallback)
     383             : {
     384          12 :     State oldState = mState;
     385             : 
     386             :     // If in one of the connected states (Connected, LocalShutdown, PeerShutdown or Closing)
     387             :     // AND this is a graceful close (i.e. not prompted by an error)
     388             :     // AND there is data waiting to be processed on either the send or receive queues
     389             :     // ... THEN enter the Closing state, allowing the queued data to drain,
     390             :     // ... OTHERWISE go straight to the Closed state.
     391          12 :     if (IsConnected() && err == CHIP_NO_ERROR && (!mSendQueue.IsNull() || !mRcvQueue.IsNull()))
     392             :     {
     393           0 :         mState = State::kClosing;
     394             :     }
     395             :     else
     396             :     {
     397          12 :         mState = State::kClosed;
     398             :     }
     399             : 
     400          12 :     if (oldState != State::kClosed)
     401             :     {
     402             :         // Stop the Connect timer in case it is still running.
     403          12 :         StopConnectTimer();
     404             :     }
     405             : 
     406             :     // If not making a state transition, return immediately.
     407          12 :     if (mState == oldState)
     408             :     {
     409           0 :         return;
     410             :     }
     411             : 
     412          12 :     DoCloseImpl(err, oldState);
     413             : 
     414             : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     415             :     // Stop the TCP UserTimeout timer if it is running.
     416          12 :     StopTCPUserTimeoutTimer();
     417             : #endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     418             : 
     419             :     // If entering the Closed state...
     420          12 :     if (mState == State::kClosed)
     421             :     {
     422             :         // Clear clear the send and receive queues.
     423          12 :         mSendQueue = nullptr;
     424          12 :         mRcvQueue  = nullptr;
     425             : 
     426             :         // Call the appropriate app callback if allowed.
     427          12 :         if (!suppressCallback)
     428             :         {
     429           0 :             if (oldState == State::kConnecting)
     430             :             {
     431           0 :                 if (OnConnectComplete != nullptr)
     432             :                 {
     433           0 :                     OnConnectComplete(this, err);
     434             :                 }
     435             :             }
     436           0 :             else if ((oldState == State::kConnected || oldState == State::kSendShutdown || oldState == State::kReceiveShutdown ||
     437           0 :                       oldState == State::kClosing) &&
     438           0 :                      OnConnectionClosed != nullptr)
     439             :             {
     440           0 :                 OnConnectionClosed(this, err);
     441             :             }
     442             :         }
     443             : 
     444             :         // Decrement the ref count that was added when the connection started (in Connect()) or listening started (in Listen()).
     445          12 :         if (oldState != State::kReady && oldState != State::kBound)
     446             :         {
     447          12 :             Release();
     448             :         }
     449             :     }
     450             : }
     451             : 
     452             : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     453             : 
     454           3 : void TCPEndPoint::ScheduleNextTCPUserTimeoutPoll(uint32_t aTimeOut)
     455             : {
     456           3 :     GetSystemLayer().StartTimer(System::Clock::Milliseconds32(aTimeOut), TCPUserTimeoutHandler, this);
     457           3 : }
     458             : 
     459           3 : void TCPEndPoint::StartTCPUserTimeoutTimer()
     460             : {
     461           3 :     ScheduleNextTCPUserTimeoutPoll(mUserTimeoutMillis);
     462           3 :     mUserTimeoutTimerRunning = true;
     463           3 : }
     464             : 
     465          18 : void TCPEndPoint::StopTCPUserTimeoutTimer()
     466             : {
     467          18 :     GetSystemLayer().CancelTimer(TCPUserTimeoutHandler, this);
     468          18 :     mUserTimeoutTimerRunning = false;
     469          18 : }
     470             : 
     471           0 : void TCPEndPoint::RestartTCPUserTimeoutTimer()
     472             : {
     473           0 :     StopTCPUserTimeoutTimer();
     474           0 :     StartTCPUserTimeoutTimer();
     475           0 : }
     476             : 
     477             : // static
     478           0 : void TCPEndPoint::TCPUserTimeoutHandler(chip::System::Layer * aSystemLayer, void * aAppState)
     479             : {
     480           0 :     TCPEndPoint * tcpEndPoint = reinterpret_cast<TCPEndPoint *>(aAppState);
     481           0 :     VerifyOrDie((aSystemLayer != nullptr) && (tcpEndPoint != nullptr));
     482           0 :     tcpEndPoint->TCPUserTimeoutHandler();
     483           0 : }
     484             : 
     485             : #endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     486             : 
     487             : } // namespace Inet
     488             : } // namespace chip

Generated by: LCOV version 1.14