Matter SDK Coverage Report
Current view: top level - inet - TCPEndPoint.cpp (source / functions) Coverage Total Hit
Test: SHA:3108862db59e5fa02f4a254cea1d5089c60155eb Lines: 64.2 % 193 124
Test Date: 2025-10-12 07:08:15 Functions: 63.3 % 30 19

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

Generated by: LCOV version 2.0-1