Matter SDK Coverage Report
Current view: top level - inet - TCPEndPoint.cpp (source / functions) Coverage Total Hit
Test: SHA:2a48c1efeab1c0f76f3adb3a0940b0f7de706453 Lines: 64.6 % 198 128
Test Date: 2026-01-31 08:14:20 Functions: 63.3 % 30 19

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2020-2025 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           28 : CHIP_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr)
      47              : {
      48           28 :     VerifyOrReturnError(mState == State::kReady, CHIP_ERROR_INCORRECT_STATE);
      49           27 :     CHIP_ERROR res = CHIP_NO_ERROR;
      50              : 
      51           27 :     if (addr != IPAddress::Any && addr.Type() != IPAddressType::kAny && addr.Type() != addrType)
      52              :     {
      53            2 :         return INET_ERROR_WRONG_ADDRESS_TYPE;
      54              :     }
      55              : 
      56           25 :     res = BindImpl(addrType, addr, port, reuseAddr);
      57              : 
      58           50 :     if (res == CHIP_NO_ERROR)
      59              :     {
      60           24 :         mState = State::kBound;
      61              :     }
      62              : 
      63           25 :     return res;
      64              : }
      65              : 
      66           25 : CHIP_ERROR TCPEndPoint::Listen(uint16_t backlog)
      67              : {
      68           25 :     VerifyOrReturnError(mState == State::kBound, CHIP_ERROR_INCORRECT_STATE);
      69           24 :     CHIP_ERROR res = CHIP_NO_ERROR;
      70              : 
      71           24 :     res = ListenImpl(backlog);
      72              : 
      73           48 :     if (res == CHIP_NO_ERROR)
      74              :     {
      75           24 :         mState = State::kListening;
      76              :     }
      77              : 
      78           24 :     return res;
      79              : }
      80              : 
      81           20 : CHIP_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, InterfaceId intfId)
      82              : {
      83           20 :     VerifyOrReturnError(mState == State::kReady || mState == State::kBound, CHIP_ERROR_INCORRECT_STATE);
      84           19 :     CHIP_ERROR res = CHIP_NO_ERROR;
      85              : 
      86           19 :     ReturnErrorOnFailure(ConnectImpl(addr, port, intfId));
      87              : 
      88           19 :     StartConnectTimerIfSet();
      89              : 
      90           19 :     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 :         TEMPORARY_RETURN_IGNORED 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           66 : void TCPEndPoint::Close()
     164              : {
     165              :     // Clear the receive queue.
     166           66 :     mRcvQueue = nullptr;
     167              : 
     168              :     // Suppress closing callbacks, since the application explicitly called Close().
     169           66 :     OnConnectionClosed = nullptr;
     170           66 :     OnPeerClose        = nullptr;
     171           66 :     OnConnectComplete  = nullptr;
     172              : 
     173              :     // Perform a graceful close.
     174           66 :     DoClose(CHIP_NO_ERROR, true);
     175           66 : }
     176              : 
     177           31 : void TCPEndPoint::Abort()
     178              : {
     179              :     // Suppress closing callbacks, since the application explicitly called Abort().
     180           31 :     OnConnectionClosed = nullptr;
     181           31 :     OnPeerClose        = nullptr;
     182           31 :     OnConnectComplete  = nullptr;
     183              : 
     184           31 :     DoClose(CHIP_ERROR_CONNECTION_ABORTED, true);
     185           31 : }
     186              : 
     187           64 : void TCPEndPoint::Free()
     188              : {
     189              :     // Ensure no callbacks to the app after this point.
     190           64 :     OnAcceptError        = nullptr;
     191           64 :     OnConnectComplete    = nullptr;
     192           64 :     OnConnectionReceived = nullptr;
     193           64 :     OnConnectionClosed   = nullptr;
     194           64 :     OnPeerClose          = nullptr;
     195           64 :     OnDataReceived       = nullptr;
     196           64 :     OnDataSent           = nullptr;
     197              : 
     198              :     // Ensure the end point is Closed or Closing.
     199           64 :     Close();
     200              : 
     201           64 :     Delete();
     202           64 : }
     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 :         TEMPORARY_RETURN_IGNORED GetSystemLayer().StartTimer(System::Clock::Milliseconds32(INET_TCP_IDLE_CHECK_INTERVAL),
     219            0 :                                                              HandleIdleTimer, &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([](const TCPEndPointHandle & 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 :         TEMPORARY_RETURN_IGNORED aSystemLayer->StartTimer(System::Clock::Milliseconds32(INET_TCP_IDLE_CHECK_INTERVAL),
     250              :                                                           HandleIdleTimer, &endPointManager);
     251              :     }
     252            0 : }
     253              : 
     254              : // static
     255            0 : bool TCPEndPoint::IsIdleTimerRunning(EndPointManager<TCPEndPoint> & endPointManager)
     256              : {
     257              :     // See if there are any TCP connections with the idle timer check in use.
     258            0 :     return Loop::Break == endPointManager.ForEachEndPoint([](const TCPEndPointHandle & lEndPoint) {
     259            0 :         return (lEndPoint->mIdleTimeout == 0) ? Loop::Continue : Loop::Break;
     260            0 :     });
     261              : }
     262              : 
     263              : #endif // INET_TCP_IDLE_CHECK_INTERVAL > 0
     264              : 
     265            0 : CHIP_ERROR TCPEndPoint::SetUserTimeout(uint32_t userTimeoutMillis)
     266              : {
     267            0 :     VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE);
     268            0 :     CHIP_ERROR res = CHIP_NO_ERROR;
     269              : 
     270              : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     271              : 
     272              :     // Store the User timeout configuration if it is being overridden.
     273            0 :     mUserTimeoutMillis = userTimeoutMillis;
     274              : 
     275              : #else // !INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     276              : 
     277              :     res = SetUserTimeoutImpl(userTimeoutMillis);
     278              : 
     279              : #endif // !INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     280              : 
     281            0 :     return res;
     282              : }
     283              : 
     284           19 : void TCPEndPoint::StartConnectTimerIfSet()
     285              : {
     286           19 :     if (mConnectTimeoutMsecs > 0)
     287              :     {
     288           19 :         TEMPORARY_RETURN_IGNORED GetSystemLayer().StartTimer(System::Clock::Milliseconds32(mConnectTimeoutMsecs),
     289              :                                                              TCPConnectTimeoutHandler, this);
     290              :     }
     291           19 : }
     292              : 
     293           82 : void TCPEndPoint::StopConnectTimer()
     294              : {
     295           82 :     GetSystemLayer().CancelTimer(TCPConnectTimeoutHandler, this);
     296           82 : }
     297              : 
     298            0 : void TCPEndPoint::TCPConnectTimeoutHandler(chip::System::Layer * aSystemLayer, void * aAppState)
     299              : {
     300            0 :     TCPEndPoint * tcpEndPoint = reinterpret_cast<TCPEndPoint *>(aAppState);
     301            0 :     VerifyOrDie((aSystemLayer != nullptr) && (tcpEndPoint != nullptr));
     302              : 
     303              :     // Close Connection as we have timed out and Connect has not returned to stop this timer.
     304            0 :     tcpEndPoint->DoClose(INET_ERROR_TCP_CONNECT_TIMEOUT, false);
     305            0 : }
     306              : 
     307          313 : bool TCPEndPoint::IsConnected(State state)
     308              : {
     309          313 :     return state == State::kConnected || state == State::kSendShutdown || state == State::kReceiveShutdown ||
     310          313 :         state == State::kClosing;
     311              : }
     312              : 
     313           11 : CHIP_ERROR TCPEndPoint::DriveSending()
     314              : {
     315           11 :     CHIP_ERROR err = DriveSendingImpl();
     316              : 
     317           22 :     if (err != CHIP_NO_ERROR)
     318              :     {
     319            0 :         DoClose(err, false);
     320              :     }
     321              : 
     322           11 :     CHIP_SYSTEM_FAULT_INJECT_ASYNC_EVENT();
     323              : 
     324           11 :     return err;
     325              : }
     326              : 
     327           17 : void TCPEndPoint::DriveReceiving(const TCPEndPointHandle & handle)
     328              : {
     329              :     // If there's data in the receive queue and the app is ready to receive it then call the app's callback
     330              :     // with the entire receive queue.
     331           17 :     if (!mRcvQueue.IsNull() && mReceiveEnabled && OnDataReceived != nullptr)
     332              :     {
     333              :         // Acknowledgement is done after handling the buffers to allow the
     334              :         // application processing to throttle flow.
     335            9 :         size_t ackLength = mRcvQueue->TotalLength();
     336            9 :         CHIP_ERROR err   = OnDataReceived(handle, std::move(mRcvQueue));
     337           18 :         if (err != CHIP_NO_ERROR)
     338              :         {
     339            0 :             DoClose(err, false);
     340            0 :             return;
     341              :         }
     342            9 :         TEMPORARY_RETURN_IGNORED AckReceive(ackLength);
     343              :     }
     344              : 
     345              :     // If the connection is closing, and the receive queue is now empty, call DoClose() to complete
     346              :     // the process of closing the connection.
     347           17 :     if (mState == State::kClosing && mRcvQueue.IsNull())
     348              :     {
     349            2 :         DoClose(CHIP_NO_ERROR, false);
     350              :     }
     351              : }
     352              : 
     353           18 : void TCPEndPoint::HandleConnectComplete(CHIP_ERROR err)
     354              : {
     355           18 :     TCPEndPointHandle handle(this);
     356              :     // If the connect succeeded enter the Connected state and call the app's callback.
     357           36 :     if (err == CHIP_NO_ERROR)
     358              :     {
     359              :         // Stop the TCP Connect timer in case it is still running.
     360           18 :         StopConnectTimer();
     361              : 
     362              :         // Mark the connection as being active.
     363           18 :         MarkActive();
     364              : 
     365           18 :         mState = State::kConnected;
     366              : 
     367           18 :         HandleConnectCompleteImpl();
     368              : 
     369           18 :         if (OnConnectComplete != nullptr)
     370              :         {
     371           18 :             OnConnectComplete(handle, CHIP_NO_ERROR);
     372              :         }
     373              :     }
     374              : 
     375              :     // Otherwise, close the connection with an error.
     376              :     else
     377              :     {
     378            0 :         DoClose(err, false);
     379              :     }
     380           18 : }
     381              : 
     382          105 : void TCPEndPoint::DoClose(CHIP_ERROR err, bool suppressCallback)
     383              : {
     384          105 :     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          139 :     if (IsConnected() && err == CHIP_NO_ERROR && (!mSendQueue.IsNull() || !mRcvQueue.IsNull()))
     392              :     {
     393            0 :         mState = State::kClosing;
     394              :     }
     395              :     else
     396              :     {
     397          105 :         mState = State::kClosed;
     398              :     }
     399              : 
     400          105 :     if (oldState != State::kClosed)
     401              :     {
     402              :         // Stop the Connect timer in case it is still running.
     403           64 :         StopConnectTimer();
     404              :     }
     405              : 
     406              :     // If not making a state transition, return immediately.
     407          105 :     if (mState == oldState)
     408              :     {
     409           41 :         return;
     410              :     }
     411              : 
     412           64 :     DoCloseImpl(err, oldState);
     413              : 
     414              : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     415              :     // Stop the TCP UserTimeout timer if it is running.
     416           64 :     StopTCPUserTimeoutTimer();
     417              : #endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     418              : 
     419              :     // If entering the Closed state...
     420           64 :     if (mState == State::kClosed)
     421              :     {
     422              :         // Clear clear the send and receive queues.
     423           64 :         mSendQueue = nullptr;
     424           64 :         mRcvQueue  = nullptr;
     425              : 
     426              :         // Call the appropriate app callback if allowed.
     427           64 :         if (!suppressCallback)
     428              :         {
     429            8 :             if (oldState == State::kConnecting)
     430              :             {
     431            0 :                 if (OnConnectComplete != nullptr)
     432              :                 {
     433            0 :                     TCPEndPointHandle handle(this);
     434            0 :                     OnConnectComplete(handle, err);
     435            0 :                 }
     436              :             }
     437            8 :             else if ((oldState == State::kConnected || oldState == State::kSendShutdown || oldState == State::kReceiveShutdown ||
     438            8 :                       oldState == State::kClosing) &&
     439            8 :                      OnConnectionClosed != nullptr)
     440              :             {
     441            8 :                 TCPEndPointHandle handle(this);
     442            8 :                 OnConnectionClosed(handle, err);
     443            8 :             }
     444              :         }
     445              :     }
     446              : }
     447              : 
     448              : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     449              : 
     450           13 : void TCPEndPoint::ScheduleNextTCPUserTimeoutPoll(uint32_t aTimeOut)
     451              : {
     452           13 :     TEMPORARY_RETURN_IGNORED GetSystemLayer().StartTimer(System::Clock::Milliseconds32(aTimeOut), TCPUserTimeoutHandler, this);
     453           13 : }
     454              : 
     455           13 : void TCPEndPoint::StartTCPUserTimeoutTimer()
     456              : {
     457           13 :     ScheduleNextTCPUserTimeoutPoll(mUserTimeoutMillis);
     458           13 :     mUserTimeoutTimerRunning = true;
     459           13 : }
     460              : 
     461           85 : void TCPEndPoint::StopTCPUserTimeoutTimer()
     462              : {
     463           85 :     GetSystemLayer().CancelTimer(TCPUserTimeoutHandler, this);
     464           85 :     mUserTimeoutTimerRunning = false;
     465           85 : }
     466              : 
     467            4 : void TCPEndPoint::RestartTCPUserTimeoutTimer()
     468              : {
     469            4 :     StopTCPUserTimeoutTimer();
     470            4 :     StartTCPUserTimeoutTimer();
     471            4 : }
     472              : 
     473              : // static
     474            0 : void TCPEndPoint::TCPUserTimeoutHandler(chip::System::Layer * aSystemLayer, void * aAppState)
     475              : {
     476            0 :     TCPEndPoint * tcpEndPoint = reinterpret_cast<TCPEndPoint *>(aAppState);
     477            0 :     VerifyOrDie((aSystemLayer != nullptr) && (tcpEndPoint != nullptr));
     478            0 :     tcpEndPoint->TCPUserTimeoutHandler();
     479            0 : }
     480              : 
     481              : #endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     482              : 
     483              : #if INET_CONFIG_TEST
     484              : bool TCPEndPoint::sForceEarlyFailureIncomingConnection = false;
     485              : #endif
     486              : 
     487              : } // namespace Inet
     488              : } // namespace chip
        

Generated by: LCOV version 2.0-1