Matter SDK Coverage Report
Current view: top level - inet - TCPEndPoint.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 63.3 % 196 124
Test Date: 2025-01-17 19:00:11 Functions: 60.0 % 30 18

            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           18 : CHIP_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr)
      47              : {
      48           18 :     VerifyOrReturnError(mState == State::kReady, CHIP_ERROR_INCORRECT_STATE);
      49           17 :     CHIP_ERROR res = CHIP_NO_ERROR;
      50              : 
      51           17 :     if (addr != IPAddress::Any && addr.Type() != IPAddressType::kAny && addr.Type() != addrType)
      52              :     {
      53            2 :         return INET_ERROR_WRONG_ADDRESS_TYPE;
      54              :     }
      55              : 
      56           15 :     res = BindImpl(addrType, addr, port, reuseAddr);
      57              : 
      58           15 :     if (res == CHIP_NO_ERROR)
      59              :     {
      60           14 :         mState = State::kBound;
      61              :     }
      62              : 
      63           15 :     return res;
      64              : }
      65              : 
      66           15 : CHIP_ERROR TCPEndPoint::Listen(uint16_t backlog)
      67              : {
      68           15 :     VerifyOrReturnError(mState == State::kBound, CHIP_ERROR_INCORRECT_STATE);
      69           14 :     CHIP_ERROR res = CHIP_NO_ERROR;
      70              : 
      71           14 :     res = ListenImpl(backlog);
      72              : 
      73           14 :     if (res == CHIP_NO_ERROR)
      74              :     {
      75              :         // Once Listening, bump the reference count.  The corresponding call to Release() will happen in DoClose().
      76           14 :         Retain();
      77           14 :         mState = State::kListening;
      78              :     }
      79              : 
      80           14 :     return res;
      81              : }
      82              : 
      83           11 : CHIP_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, InterfaceId intfId)
      84              : {
      85           11 :     VerifyOrReturnError(mState == State::kReady || mState == State::kBound, CHIP_ERROR_INCORRECT_STATE);
      86           10 :     CHIP_ERROR res = CHIP_NO_ERROR;
      87              : 
      88           10 :     ReturnErrorOnFailure(ConnectImpl(addr, port, intfId));
      89              : 
      90           10 :     StartConnectTimerIfSet();
      91              : 
      92           10 :     return res;
      93              : }
      94              : 
      95            6 : CHIP_ERROR TCPEndPoint::Send(System::PacketBufferHandle && data, bool push)
      96              : {
      97            6 :     VerifyOrReturnError(mState == State::kConnected || mState == State::kReceiveShutdown, CHIP_ERROR_INCORRECT_STATE);
      98            5 :     CHIP_ERROR res = CHIP_NO_ERROR;
      99              : 
     100            5 :     bool queueWasEmpty = mSendQueue.IsNull();
     101            5 :     if (queueWasEmpty)
     102              :     {
     103            5 :         mSendQueue = std::move(data);
     104              :     }
     105              :     else
     106              :     {
     107            0 :         mSendQueue->AddToEnd(std::move(data));
     108              :     }
     109              : 
     110            5 :     ReturnErrorOnFailure(SendQueuedImpl(queueWasEmpty));
     111              : 
     112            5 :     if (push)
     113              :     {
     114            5 :         res = DriveSending();
     115              :     }
     116              : 
     117            5 :     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 : size_t TCPEndPoint::PendingSendLength()
     130              : {
     131            0 :     if (!mSendQueue.IsNull())
     132              :     {
     133            0 :         return mSendQueue->TotalLength();
     134              :     }
     135            0 :     return 0;
     136              : }
     137              : 
     138            1 : size_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           44 : void TCPEndPoint::Close()
     166              : {
     167              :     // Clear the receive queue.
     168           44 :     mRcvQueue = nullptr;
     169              : 
     170              :     // Suppress closing callbacks, since the application explicitly called Close().
     171           44 :     OnConnectionClosed = nullptr;
     172           44 :     OnPeerClose        = nullptr;
     173           44 :     OnConnectComplete  = nullptr;
     174              : 
     175              :     // Perform a graceful close.
     176           44 :     DoClose(CHIP_NO_ERROR, true);
     177           44 : }
     178              : 
     179           10 : void TCPEndPoint::Abort()
     180              : {
     181              :     // Suppress closing callbacks, since the application explicitly called Abort().
     182           10 :     OnConnectionClosed = nullptr;
     183           10 :     OnPeerClose        = nullptr;
     184           10 :     OnConnectComplete  = nullptr;
     185              : 
     186           10 :     DoClose(CHIP_ERROR_CONNECTION_ABORTED, true);
     187           10 : }
     188              : 
     189           34 : void TCPEndPoint::Free()
     190              : {
     191              :     // Ensure no callbacks to the app after this point.
     192           34 :     OnAcceptError        = nullptr;
     193           34 :     OnConnectComplete    = nullptr;
     194           34 :     OnConnectionReceived = nullptr;
     195           34 :     OnConnectionClosed   = nullptr;
     196           34 :     OnPeerClose          = nullptr;
     197           34 :     OnDataReceived       = nullptr;
     198           34 :     OnDataSent           = nullptr;
     199              : 
     200              :     // Ensure the end point is Closed or Closing.
     201           34 :     Close();
     202              : 
     203              :     // Release the Retain() that happened when the end point was allocated.
     204           34 :     Release();
     205           34 : }
     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           10 : void TCPEndPoint::StartConnectTimerIfSet()
     287              : {
     288           10 :     if (mConnectTimeoutMsecs > 0)
     289              :     {
     290           10 :         GetSystemLayer().StartTimer(System::Clock::Milliseconds32(mConnectTimeoutMsecs), TCPConnectTimeoutHandler, this);
     291              :     }
     292           10 : }
     293              : 
     294           44 : void TCPEndPoint::StopConnectTimer()
     295              : {
     296           44 :     GetSystemLayer().CancelTimer(TCPConnectTimeoutHandler, this);
     297           44 : }
     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          214 : bool TCPEndPoint::IsConnected(State state)
     309              : {
     310          214 :     return state == State::kConnected || state == State::kSendShutdown || state == State::kReceiveShutdown ||
     311          214 :         state == State::kClosing;
     312              : }
     313              : 
     314            5 : CHIP_ERROR TCPEndPoint::DriveSending()
     315              : {
     316            5 :     CHIP_ERROR err = DriveSendingImpl();
     317              : 
     318            5 :     if (err != CHIP_NO_ERROR)
     319              :     {
     320            0 :         DoClose(err, false);
     321              :     }
     322              : 
     323            5 :     CHIP_SYSTEM_FAULT_INJECT_ASYNC_EVENT();
     324              : 
     325            5 :     return err;
     326              : }
     327              : 
     328           14 : 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           14 :     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            5 :         size_t ackLength = mRcvQueue->TotalLength();
     337            5 :         CHIP_ERROR err   = OnDataReceived(this, std::move(mRcvQueue));
     338            5 :         if (err != CHIP_NO_ERROR)
     339              :         {
     340            0 :             DoClose(err, false);
     341            0 :             return;
     342              :         }
     343            5 :         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           14 :     if (mState == State::kClosing && mRcvQueue.IsNull())
     349              :     {
     350            9 :         DoClose(CHIP_NO_ERROR, false);
     351              :     }
     352              : }
     353              : 
     354           10 : void TCPEndPoint::HandleConnectComplete(CHIP_ERROR err)
     355              : {
     356              :     // If the connect succeeded enter the Connected state and call the app's callback.
     357           10 :     if (err == CHIP_NO_ERROR)
     358              :     {
     359              :         // Stop the TCP Connect timer in case it is still running.
     360           10 :         StopConnectTimer();
     361              : 
     362              :         // Mark the connection as being active.
     363           10 :         MarkActive();
     364              : 
     365           10 :         mState = State::kConnected;
     366              : 
     367           10 :         HandleConnectCompleteImpl();
     368              : 
     369           10 :         if (OnConnectComplete != nullptr)
     370              :         {
     371           10 :             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           10 : }
     381              : 
     382           63 : void TCPEndPoint::DoClose(CHIP_ERROR err, bool suppressCallback)
     383              : {
     384           63 :     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           63 :     if (IsConnected() && err == CHIP_NO_ERROR && (!mSendQueue.IsNull() || !mRcvQueue.IsNull()))
     392              :     {
     393            0 :         mState = State::kClosing;
     394              :     }
     395              :     else
     396              :     {
     397           63 :         mState = State::kClosed;
     398              :     }
     399              : 
     400           63 :     if (oldState != State::kClosed)
     401              :     {
     402              :         // Stop the Connect timer in case it is still running.
     403           34 :         StopConnectTimer();
     404              :     }
     405              : 
     406              :     // If not making a state transition, return immediately.
     407           63 :     if (mState == oldState)
     408              :     {
     409           29 :         return;
     410              :     }
     411              : 
     412           34 :     DoCloseImpl(err, oldState);
     413              : 
     414              : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     415              :     // Stop the TCP UserTimeout timer if it is running.
     416           34 :     StopTCPUserTimeoutTimer();
     417              : #endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     418              : 
     419              :     // If entering the Closed state...
     420           34 :     if (mState == State::kClosed)
     421              :     {
     422              :         // Clear clear the send and receive queues.
     423           34 :         mSendQueue = nullptr;
     424           34 :         mRcvQueue  = nullptr;
     425              : 
     426              :         // Call the appropriate app callback if allowed.
     427           34 :         if (!suppressCallback)
     428              :         {
     429            9 :             if (oldState == State::kConnecting)
     430              :             {
     431            0 :                 if (OnConnectComplete != nullptr)
     432              :                 {
     433            0 :                     OnConnectComplete(this, err);
     434              :                 }
     435              :             }
     436            9 :             else if ((oldState == State::kConnected || oldState == State::kSendShutdown || oldState == State::kReceiveShutdown ||
     437            9 :                       oldState == State::kClosing) &&
     438            9 :                      OnConnectionClosed != nullptr)
     439              :             {
     440            9 :                 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           34 :         if (oldState != State::kReady && oldState != State::kBound)
     446              :         {
     447           34 :             Release();
     448              :         }
     449              :     }
     450              : }
     451              : 
     452              : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
     453              : 
     454            5 : void TCPEndPoint::ScheduleNextTCPUserTimeoutPoll(uint32_t aTimeOut)
     455              : {
     456            5 :     GetSystemLayer().StartTimer(System::Clock::Milliseconds32(aTimeOut), TCPUserTimeoutHandler, this);
     457            5 : }
     458              : 
     459            5 : void TCPEndPoint::StartTCPUserTimeoutTimer()
     460              : {
     461            5 :     ScheduleNextTCPUserTimeoutPoll(mUserTimeoutMillis);
     462            5 :     mUserTimeoutTimerRunning = true;
     463            5 : }
     464              : 
     465           48 : void TCPEndPoint::StopTCPUserTimeoutTimer()
     466              : {
     467           48 :     GetSystemLayer().CancelTimer(TCPUserTimeoutHandler, this);
     468           48 :     mUserTimeoutTimerRunning = false;
     469           48 : }
     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 2.0-1