Coverage Report

Created: 2026-07-10 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/connectedhomeip/src/transport/raw/TCP.cpp
Line
Count
Source
1
/*
2
 *
3
 *    Copyright (c) 2020-2025 Project CHIP Authors
4
 *    Copyright (c) 2013-2017 Nest Labs, Inc.
5
 *    All rights reserved.
6
 *
7
 *    Licensed under the Apache License, Version 2.0 (the "License");
8
 *    you may not use this file except in compliance with the License.
9
 *    You may obtain a copy of the License at
10
 *
11
 *        http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 *    Unless required by applicable law or agreed to in writing, software
14
 *    distributed under the License is distributed on an "AS IS" BASIS,
15
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 *    See the License for the specific language governing permissions and
17
 *    limitations under the License.
18
 */
19
20
/**
21
 *    @file
22
 *      This file implements the CHIP Transport object that maintains TCP connections
23
 *      to peers. Handles both establishing new connections and accepting peer connection
24
 *      requests.
25
 */
26
#include <transport/raw/TCP.h>
27
28
#include <lib/core/CHIPEncoding.h>
29
#include <lib/support/CodeUtils.h>
30
#include <lib/support/logging/CHIPLogging.h>
31
#include <transport/raw/MessageHeader.h>
32
33
#include <inttypes.h>
34
#include <limits>
35
36
namespace chip {
37
namespace Transport {
38
namespace {
39
40
using namespace chip::Encoding;
41
42
// Packets start with a 32-bit size field.
43
constexpr size_t kPacketSizeBytes = 4;
44
45
static_assert(System::PacketBuffer::kLargeBufMaxSizeWithoutReserve <= UINT32_MAX, "Cast below could truncate the value");
46
static_assert(System::PacketBuffer::kLargeBufMaxSizeWithoutReserve >= kPacketSizeBytes,
47
              "Large buffer allocation should be large enough to hold the length field");
48
49
constexpr uint32_t kMaxTCPMessageSize =
50
    static_cast<uint32_t>(System::PacketBuffer::kLargeBufMaxSizeWithoutReserve - kPacketSizeBytes);
51
52
constexpr int kListenBacklogSize = 2;
53
54
CHIP_ERROR GetPeerAddress(Inet::TCPEndPoint & endPoint, PeerAddress & outAddr)
55
0
{
56
0
    Inet::IPAddress ipAddress;
57
0
    uint16_t port;
58
0
    Inet::InterfaceId interfaceId;
59
0
    ReturnErrorOnFailure(endPoint.GetPeerInfo(&ipAddress, &port));
60
0
    ReturnErrorOnFailure(endPoint.GetInterfaceId(&interfaceId));
61
0
    outAddr = PeerAddress::TCP(ipAddress, port, interfaceId);
62
63
0
    return CHIP_NO_ERROR;
64
0
}
65
66
} // namespace
67
68
0
TCPBase::~TCPBase() = default;
69
70
void TCPBase::CloseActiveConnections()
71
0
{
72
    // Nothing to do; we can't release as long as references are being held
73
0
    for (size_t i = 0; i < mActiveConnectionsSize; i++)
74
0
    {
75
0
        if (mActiveConnections[i].InUse())
76
0
        {
77
0
            CloseConnectionInternal(mActiveConnections[i], CHIP_NO_ERROR, SuppressCallback::Yes);
78
0
        }
79
0
    }
80
0
}
81
82
CHIP_ERROR TCPBase::Init(TcpListenParameters & params)
83
0
{
84
0
    CHIP_ERROR err = CHIP_NO_ERROR;
85
86
0
    VerifyOrExit(mState == TCPState::kNotReady, err = CHIP_ERROR_INCORRECT_STATE);
87
88
0
    mEndpointType = params.GetAddressType();
89
90
    // Primary socket endpoint created to help get EndPointManager handle for creating multiple
91
    // connection endpoints at runtime.
92
0
    err = params.GetEndPointManager()->NewEndPoint(mListenSocket);
93
0
    SuccessOrExit(err);
94
95
0
    if (params.IsServerListenEnabled())
96
0
    {
97
0
        err = mListenSocket->Bind(params.GetAddressType(), Inet::IPAddress::Any, params.GetListenPort(),
98
0
                                  params.GetInterfaceId().IsPresent());
99
0
        SuccessOrExit(err);
100
101
0
        mListenSocket->mAppState            = reinterpret_cast<void *>(this);
102
0
        mListenSocket->OnConnectionReceived = HandleIncomingConnection;
103
0
        mListenSocket->OnAcceptError        = HandleAcceptError;
104
105
0
        err = mListenSocket->Listen(kListenBacklogSize);
106
0
        SuccessOrExit(err);
107
0
        ChipLogProgress(Inet, "TCP server listening on port %d for incoming connections", params.GetListenPort());
108
0
    }
109
110
0
    mState = TCPState::kInitialized;
111
112
0
exit:
113
0
    if (err != CHIP_NO_ERROR)
114
0
    {
115
0
        ChipLogError(Inet, "Failed to initialize TCP transport: %" CHIP_ERROR_FORMAT, err.Format());
116
0
        mListenSocket.Release();
117
0
    }
118
119
0
    return err;
120
0
}
121
122
void TCPBase::Close()
123
0
{
124
0
    mListenSocket.Release();
125
126
0
    CloseActiveConnections();
127
128
0
    mState = TCPState::kNotReady;
129
0
}
130
131
ActiveTCPConnectionState * TCPBase::AllocateConnection(const Inet::TCPEndPointHandle & endpoint, const PeerAddress & address)
132
0
{
133
    // If a peer initiates a connection through HandleIncomingConnection but the connection is never claimed
134
    // in ProcessSingleMessage, we'll be left with a dangling ActiveTCPConnectionState which can be
135
    // reclaimed.  Don't try to reclaim these connections unless we're out of space
136
0
    for (int reclaim = 0; reclaim < 2; reclaim++)
137
0
    {
138
0
        for (size_t i = 0; i < mActiveConnectionsSize; i++)
139
0
        {
140
0
            ActiveTCPConnectionState * activeConnection = &mActiveConnections[i];
141
0
            if (!activeConnection->InUse() && (activeConnection->GetReferenceCount() == 0))
142
0
            {
143
                // Update state for the active connection
144
0
                activeConnection->Init(endpoint, address, [this](auto & conn) { TCPDisconnect(conn, true); });
145
0
                return activeConnection;
146
0
            }
147
0
        }
148
149
        // Out of space; reclaim connections that were never claimed by ProcessSingleMessage
150
        // (i.e. that have a ref count of 0)
151
0
        for (size_t i = 0; i < mActiveConnectionsSize; i++)
152
0
        {
153
0
            ActiveTCPConnectionState * activeConnection = &mActiveConnections[i];
154
0
            if (!activeConnection->InUse() && (activeConnection->GetReferenceCount() != 0))
155
0
            {
156
0
                char addrStr[Transport::PeerAddress::kMaxToStringSize];
157
0
                activeConnection->mPeerAddr.ToString(addrStr);
158
0
                ChipLogError(Inet, "Leaked TCP connection %p to %s.", activeConnection, addrStr);
159
                // Try to notify callbacks in the hope that they release; the connection is no good
160
0
                CloseConnectionInternal(*activeConnection, CHIP_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY, SuppressCallback::No);
161
0
            }
162
0
            ActiveTCPConnectionHandle releaseUnclaimed(activeConnection);
163
0
        }
164
0
    }
165
0
    return nullptr;
166
0
}
167
168
// Find an ActiveTCPConnectionState corresponding to a peer address
169
ActiveTCPConnectionHandle TCPBase::FindInUseConnection(const PeerAddress & address)
170
0
{
171
0
    if (address.GetTransportType() != Type::kTcp)
172
0
    {
173
0
        return nullptr;
174
0
    }
175
176
0
    for (size_t i = 0; i < mActiveConnectionsSize; i++)
177
0
    {
178
0
        auto & conn = mActiveConnections[i];
179
0
        if (!conn.InUse())
180
0
        {
181
0
            continue;
182
0
        }
183
184
0
        if (conn.mPeerAddr == address)
185
0
        {
186
0
            Inet::IPAddress addr;
187
0
            uint16_t port;
188
0
            if (conn.IsConnected())
189
0
            {
190
                // Failure to get peer information means the connection is bad; close it
191
0
                CHIP_ERROR err = conn.mEndPoint->GetPeerInfo(&addr, &port);
192
0
                if (err != CHIP_NO_ERROR)
193
0
                {
194
0
                    CloseConnectionInternal(conn, err, SuppressCallback::No);
195
0
                    return nullptr;
196
0
                }
197
0
            }
198
199
0
            return ActiveTCPConnectionHandle(&conn);
200
0
        }
201
0
    }
202
203
0
    return nullptr;
204
0
}
205
206
// Find the ActiveTCPConnectionState for a given TCPEndPoint
207
ActiveTCPConnectionState * TCPBase::FindActiveConnection(const Inet::TCPEndPointHandle & endPoint)
208
0
{
209
0
    for (size_t i = 0; i < mActiveConnectionsSize; i++)
210
0
    {
211
0
        if (mActiveConnections[i].mEndPoint == endPoint && mActiveConnections[i].IsConnected())
212
0
        {
213
0
            return &mActiveConnections[i];
214
0
        }
215
0
    }
216
0
    return nullptr;
217
0
}
218
219
ActiveTCPConnectionHandle TCPBase::FindInUseConnection(const Inet::TCPEndPoint & endPoint)
220
0
{
221
0
    for (size_t i = 0; i < mActiveConnectionsSize; i++)
222
0
    {
223
0
        if (mActiveConnections[i].mEndPoint == endPoint)
224
0
        {
225
0
            return ActiveTCPConnectionHandle(&mActiveConnections[i]);
226
0
        }
227
0
    }
228
0
    return nullptr;
229
0
}
230
231
CHIP_ERROR TCPBase::PrepareBuffer(System::PacketBufferHandle & msgBuf)
232
0
{
233
    // Sent buffer data format is:
234
    //    - packet size as a uint32_t
235
    //    - actual data
236
237
0
    VerifyOrReturnError(mState == TCPState::kInitialized, CHIP_ERROR_INCORRECT_STATE);
238
0
    VerifyOrReturnError(kPacketSizeBytes + msgBuf->DataLength() <= System::PacketBuffer::kLargeBufMaxSizeWithoutReserve,
239
0
                        CHIP_ERROR_INVALID_ARGUMENT);
240
241
0
    static_assert(kPacketSizeBytes <= UINT16_MAX);
242
0
    VerifyOrReturnError(msgBuf->EnsureReservedSize(static_cast<uint16_t>(kPacketSizeBytes)), CHIP_ERROR_NO_MEMORY);
243
244
0
    msgBuf->SetStart(msgBuf->Start() - kPacketSizeBytes);
245
246
0
    uint8_t * output = msgBuf->Start();
247
0
    LittleEndian::Write32(output, static_cast<uint32_t>(msgBuf->DataLength() - kPacketSizeBytes));
248
249
0
    return CHIP_NO_ERROR;
250
0
}
251
252
CHIP_ERROR TCPBase::SendMessage(const Transport::PeerAddress & address, System::PacketBufferHandle && msgBuf)
253
0
{
254
0
    VerifyOrReturnError(address.GetTransportType() == Type::kTcp, CHIP_ERROR_INVALID_ARGUMENT);
255
0
    ReturnErrorOnFailure(PrepareBuffer(msgBuf));
256
257
    // Must find a previously-established connection with an owning reference
258
0
    auto connection = FindInUseConnection(address);
259
0
    VerifyOrReturnError(!connection.IsNull(), CHIP_ERROR_INCORRECT_STATE);
260
0
    if (connection->IsConnected())
261
0
    {
262
0
        return connection->mEndPoint->Send(std::move(msgBuf));
263
0
    }
264
265
0
    return SendAfterConnect(connection, std::move(msgBuf));
266
0
}
267
268
CHIP_ERROR TCPBase::SendMessage(const ActiveTCPConnectionHandle & connection, System::PacketBufferHandle && msgBuf)
269
0
{
270
0
    VerifyOrReturnError(!connection.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
271
0
    ReturnErrorOnFailure(PrepareBuffer(msgBuf));
272
273
0
    if (connection->IsConnected())
274
0
    {
275
0
        return connection->mEndPoint->Send(std::move(msgBuf));
276
0
    }
277
278
0
    return SendAfterConnect(connection, std::move(msgBuf));
279
0
}
280
281
CHIP_ERROR TCPBase::StartConnect(const PeerAddress & addr, Transport::AppTCPConnectionCallbackCtxt * appState,
282
                                 ActiveTCPConnectionHandle & outPeerConnState)
283
0
{
284
0
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
285
0
    Inet::TCPEndPointHandle endPoint;
286
0
    outPeerConnState.Release();
287
0
    ReturnErrorOnFailure(mListenSocket->GetEndPointManager().NewEndPoint(endPoint));
288
289
0
    InitEndpoint(endPoint);
290
291
0
    ActiveTCPConnectionHandle activeConnection = FindInUseConnection(addr);
292
    // Re-use existing connection to peer if already connected
293
0
    if (!activeConnection.IsNull())
294
0
    {
295
0
        if (appState != nullptr)
296
0
        {
297
            // We do not support parallel attempts to connect to peer when setting appState
298
0
            VerifyOrReturnError(activeConnection->mConnectionState == TCPState::kConnected &&
299
0
                                    activeConnection->mAppState == nullptr,
300
0
                                CHIP_ERROR_INCORRECT_STATE);
301
0
            activeConnection->mAppState = appState;
302
0
        }
303
0
        outPeerConnState = activeConnection;
304
305
0
        if (activeConnection->mConnectionState == TCPState::kConnected)
306
0
        {
307
0
            HandleConnectionAttemptComplete(activeConnection, CHIP_NO_ERROR);
308
0
        }
309
310
0
        return CHIP_NO_ERROR;
311
0
    }
312
313
0
    activeConnection = AllocateConnection(endPoint, addr);
314
0
    VerifyOrReturnError(!activeConnection.IsNull(), CHIP_ERROR_NO_MEMORY);
315
0
    activeConnection->mAppState        = appState;
316
0
    activeConnection->mConnectionState = TCPState::kConnecting;
317
318
0
    mUsedEndPointCount++;
319
320
0
    ReturnErrorOnFailure(endPoint->Connect(addr.GetIPAddress(), addr.GetPort(), addr.GetInterface()));
321
322
    // Set the return value of the peer connection state to the allocated
323
    // connection.
324
0
    outPeerConnState = activeConnection;
325
326
0
    return CHIP_NO_ERROR;
327
#else
328
    return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
329
#endif
330
0
}
331
332
CHIP_ERROR TCPBase::SendAfterConnect(const ActiveTCPConnectionHandle & existing, System::PacketBufferHandle && msg)
333
0
{
334
0
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
335
0
    VerifyOrReturnError(!existing.IsNull(), CHIP_ERROR_INCORRECT_STATE);
336
0
    const PeerAddress & addr = existing->mPeerAddr;
337
338
    // This will initiate a connection to the specified peer
339
0
    bool alreadyConnecting = false;
340
341
    // Iterate through the ENTIRE array. If a pending packet for
342
    // the address already exists, this means a connection is pending and
343
    // does NOT need to be re-established.
344
0
    mPendingPackets.ForEachActiveObject([&](PendingPacket * pending) {
345
0
        if (pending->mPeerAddress == addr)
346
0
        {
347
            // same destination exists.
348
0
            alreadyConnecting = true;
349
0
            pending->mPacketBuffer->AddToEnd(std::move(msg));
350
0
            return Loop::Break;
351
0
        }
352
0
        return Loop::Continue;
353
0
    });
354
355
    // If already connecting, buffer was just enqueued for more sending
356
0
    if (alreadyConnecting)
357
0
    {
358
0
        return CHIP_NO_ERROR;
359
0
    }
360
361
    // enqueue the packet once the connection succeeds
362
0
    VerifyOrReturnError(mPendingPackets.CreateObject(addr, std::move(msg)) != nullptr, CHIP_ERROR_NO_MEMORY);
363
364
0
    return CHIP_NO_ERROR;
365
#else
366
    return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
367
#endif
368
0
}
369
370
CHIP_ERROR TCPBase::ProcessReceivedBuffer(const Inet::TCPEndPointHandle & endPoint, const PeerAddress & peerAddress,
371
                                          System::PacketBufferHandle && buffer)
372
0
{
373
0
    ActiveTCPConnectionState * state = FindActiveConnection(endPoint);
374
    // There must be a preceding TCPConnect to hold a reference to connection
375
0
    VerifyOrReturnError(state != nullptr, CHIP_ERROR_INTERNAL);
376
0
    state->mReceived.AddToEnd(std::move(buffer));
377
378
0
    while (!state->mReceived.IsNull())
379
0
    {
380
0
        uint8_t messageSizeBuf[kPacketSizeBytes];
381
0
        CHIP_ERROR err = state->mReceived->Read(messageSizeBuf);
382
0
        if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
383
0
        {
384
            // We don't have enough data to read the message size. Wait until there's more.
385
0
            return CHIP_NO_ERROR;
386
0
        }
387
0
        if (err != CHIP_NO_ERROR)
388
0
        {
389
0
            return err;
390
0
        }
391
0
        uint32_t messageSize = LittleEndian::Get32(messageSizeBuf);
392
0
        if (messageSize > kMaxTCPMessageSize)
393
0
        {
394
            // Message is too big for this node to process. Disconnect from peer.
395
0
            ChipLogError(Inet, "Received TCP message of length %" PRIu32 " exceeds limit.", messageSize);
396
0
            CloseConnectionInternal(*state, CHIP_ERROR_MESSAGE_TOO_LONG, SuppressCallback::No);
397
398
0
            return CHIP_ERROR_MESSAGE_TOO_LONG;
399
0
        }
400
        // The subtraction will not underflow because we successfully read kPacketSizeBytes.
401
0
        if (messageSize > (state->mReceived->TotalLength() - kPacketSizeBytes))
402
0
        {
403
            // We have not yet received the complete message.
404
0
            return CHIP_NO_ERROR;
405
0
        }
406
407
0
        state->mReceived.Consume(kPacketSizeBytes);
408
409
0
        if (messageSize == 0)
410
0
        {
411
            // Zero-length messages are not valid Matter messages. Reject to
412
            // prevent attackers from holding TCP connection slots indefinitely.
413
0
            ChipLogError(Inet, "Received zero-length TCP message, closing connection.");
414
0
            return CHIP_ERROR_INVALID_MESSAGE_LENGTH;
415
0
        }
416
417
0
        ReturnErrorOnFailure(ProcessSingleMessage(peerAddress, *state, messageSize));
418
0
    }
419
420
0
    return CHIP_NO_ERROR;
421
0
}
422
423
CHIP_ERROR TCPBase::ProcessSingleMessage(const PeerAddress & peerAddress, ActiveTCPConnectionState & state, size_t messageSize)
424
0
{
425
    // We enter with `state->mReceived` containing at least one full message, perhaps in a chain.
426
    // `state->mReceived->Start()` currently points to the message data.
427
    // On exit, `state->mReceived` will have had `messageSize` bytes consumed, no matter what.
428
0
    System::PacketBufferHandle message;
429
430
0
    if (state.mReceived->DataLength() == messageSize)
431
0
    {
432
        // In this case, the head packet buffer contains exactly the message.
433
        // This is common because typical messages fit in a network packet, and are delivered as such.
434
        // Peel off the head to pass upstream, which effectively consumes it from `state->mReceived`.
435
0
        message = state.mReceived.PopHead();
436
0
    }
437
0
    else
438
0
    {
439
        // The message is either longer or shorter than the head buffer.
440
        // In either case, copy the message to a fresh linear buffer to pass upstream. We always copy, rather than provide
441
        // a shared reference to the current buffer, in case upper layers manipulate the buffer in ways that would affect
442
        // our use, e.g. chaining it elsewhere or reusing space beyond the current message.
443
0
        message = System::PacketBufferHandle::New(messageSize, 0);
444
0
        if (message.IsNull())
445
0
        {
446
0
            return CHIP_ERROR_NO_MEMORY;
447
0
        }
448
0
        CHIP_ERROR err = state.mReceived->Read(message->Start(), messageSize);
449
0
        state.mReceived.Consume(messageSize);
450
0
        ReturnErrorOnFailure(err);
451
0
        message->SetDataLength(messageSize);
452
0
    }
453
454
0
    MessageTransportContext msgContext;
455
0
    msgContext.conn = &state; // Take ownership
456
0
    HandleMessageReceived(peerAddress, std::move(message), &msgContext);
457
0
    return CHIP_NO_ERROR;
458
0
}
459
460
void TCPBase::CloseConnectionInternal(ActiveTCPConnectionState & connection, CHIP_ERROR err, SuppressCallback suppressCallback)
461
0
{
462
0
    if (connection.mConnectionState == TCPState::kClosed || !connection.mEndPoint)
463
0
    {
464
0
        return;
465
0
    }
466
0
    TCPState prevState;
467
0
    char addrStr[Transport::PeerAddress::kMaxToStringSize];
468
0
    connection.mPeerAddr.ToString(addrStr);
469
0
    ChipLogProgress(Inet, "Closing connection with peer %s.", addrStr);
470
471
0
    Inet::TCPEndPointHandle endpoint = connection.mEndPoint;
472
0
    connection.mEndPoint.Release();
473
0
    if (err == CHIP_NO_ERROR)
474
0
    {
475
0
        endpoint->Close();
476
0
    }
477
0
    else
478
0
    {
479
0
        endpoint->Abort();
480
0
    }
481
482
0
    prevState                   = connection.mConnectionState;
483
0
    connection.mConnectionState = TCPState::kClosed;
484
485
0
    if (suppressCallback == SuppressCallback::No)
486
0
    {
487
0
        if (prevState == TCPState::kConnecting)
488
0
        {
489
0
            ActiveTCPConnectionHandle holder(&connection);
490
            // Call upper layer connection attempt complete handler
491
0
            HandleConnectionAttemptComplete(holder, err);
492
0
        }
493
0
        else
494
0
        {
495
            // Call upper layer connection closed handler
496
0
            HandleConnectionClosed(connection, err);
497
0
        }
498
0
    }
499
500
0
    connection.Free();
501
0
    mUsedEndPointCount--;
502
0
}
503
504
CHIP_ERROR TCPBase::HandleTCPEndPointDataReceived(const Inet::TCPEndPointHandle & endPoint, System::PacketBufferHandle && buffer)
505
0
{
506
0
    PeerAddress peerAddress;
507
0
    ReturnErrorOnFailure(GetPeerAddress(*endPoint, peerAddress));
508
509
0
    TCPBase * tcp  = reinterpret_cast<TCPBase *>(endPoint->mAppState);
510
0
    CHIP_ERROR err = tcp->ProcessReceivedBuffer(endPoint, peerAddress, std::move(buffer));
511
512
0
    if (err != CHIP_NO_ERROR)
513
0
    {
514
        // Connection could need to be closed at this point
515
0
        ChipLogError(Inet, "Failed to accept received TCP message: %" CHIP_ERROR_FORMAT, err.Format());
516
0
        return CHIP_ERROR_UNEXPECTED_EVENT;
517
0
    }
518
0
    return CHIP_NO_ERROR;
519
0
}
520
521
void TCPBase::HandleTCPEndPointConnectComplete(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR conErr)
522
0
{
523
0
    CHIP_ERROR err          = CHIP_NO_ERROR;
524
0
    bool foundPendingPacket = false;
525
0
    TCPBase * tcp           = reinterpret_cast<TCPBase *>(endPoint->mAppState);
526
0
    ActiveTCPConnectionHandle activeConnection;
527
528
0
    PeerAddress addr;
529
0
    char addrStr[Transport::PeerAddress::kMaxToStringSize];
530
0
    activeConnection = tcp->FindInUseConnection(*endPoint);
531
0
    if (activeConnection.IsNull())
532
0
    {
533
0
        err = GetPeerAddress(*endPoint, addr);
534
0
    }
535
0
    else
536
0
    {
537
0
        addr = activeConnection->mPeerAddr;
538
0
    }
539
0
    if (err == CHIP_NO_ERROR)
540
0
    {
541
0
        addr.ToString(addrStr);
542
0
    }
543
544
0
    if (conErr != CHIP_NO_ERROR || err != CHIP_NO_ERROR)
545
0
    {
546
0
        auto failure = (conErr != CHIP_NO_ERROR) ? conErr : err;
547
0
        if (!activeConnection.IsNull())
548
0
        {
549
0
            tcp->CloseConnectionInternal(*activeConnection, failure, SuppressCallback::No);
550
0
        }
551
0
        ChipLogError(Inet, "Connection establishment with %s encountered an error: %" CHIP_ERROR_FORMAT, addrStr, failure.Format());
552
0
        return;
553
0
    }
554
    // Set the Data received handler when connection completes
555
0
    endPoint->OnDataReceived     = HandleTCPEndPointDataReceived;
556
0
    endPoint->OnDataSent         = nullptr;
557
0
    endPoint->OnConnectionClosed = HandleTCPEndPointConnectionClosed;
558
559
0
    VerifyOrDie(!activeConnection.IsNull());
560
561
    // Set to Connected state
562
0
    activeConnection->mConnectionState = TCPState::kConnected;
563
564
    // Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true.
565
    // This is to expedite transmission of payload data and not rely on the
566
    // network stack's configuration of collating enough data in the TCP
567
    // window to begin transmission.
568
0
    err = endPoint->EnableNoDelay();
569
0
    if (err != CHIP_NO_ERROR)
570
0
    {
571
0
        tcp->CloseConnectionInternal(*activeConnection, err, SuppressCallback::No);
572
0
        return;
573
0
    }
574
575
    // Send any pending packets that are queued for this connection
576
0
    tcp->mPendingPackets.ForEachActiveObject([&](PendingPacket * pending) {
577
0
        if (pending->mPeerAddress == addr)
578
0
        {
579
0
            foundPendingPacket                = true;
580
0
            System::PacketBufferHandle buffer = std::move(pending->mPacketBuffer);
581
0
            tcp->mPendingPackets.ReleaseObject(pending);
582
583
0
            if ((conErr == CHIP_NO_ERROR) && (err == CHIP_NO_ERROR))
584
0
            {
585
                // TODO(gmarcosb): These errors are just swallowed; caller unaware their message is just dropped?
586
                // Likely just falls through to a timeout instead of fail-fast
587
0
                err = endPoint->Send(std::move(buffer));
588
0
            }
589
0
        }
590
0
        return Loop::Continue;
591
0
    });
592
593
    // Set the TCPKeepalive configurations on the established connection
594
0
    TEMPORARY_RETURN_IGNORED endPoint->EnableKeepAlive(activeConnection->mTCPKeepAliveIntervalSecs,
595
0
                                                       activeConnection->mTCPMaxNumKeepAliveProbes);
596
597
0
    ChipLogProgress(Inet, "Connection established successfully with %s.", addrStr);
598
599
    // Let higher layer/delegate know that connection is successfully
600
    // established
601
0
    tcp->HandleConnectionAttemptComplete(activeConnection, CHIP_NO_ERROR);
602
0
}
603
604
void TCPBase::HandleTCPEndPointConnectionClosed(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR err)
605
0
{
606
0
    TCPBase * tcp                              = reinterpret_cast<TCPBase *>(endPoint->mAppState);
607
0
    ActiveTCPConnectionHandle activeConnection = tcp->FindInUseConnection(*endPoint);
608
609
0
    if (activeConnection.IsNull())
610
0
    {
611
0
        return;
612
0
    }
613
614
0
    if (err == CHIP_NO_ERROR && activeConnection->IsConnected())
615
0
    {
616
0
        err = CHIP_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY;
617
0
    }
618
619
0
    tcp->CloseConnectionInternal(*activeConnection, err, SuppressCallback::No);
620
0
}
621
622
// Handler for incoming connection requests from peer nodes
623
void TCPBase::HandleIncomingConnection(const Inet::TCPEndPointHandle & listenEndPoint, const Inet::TCPEndPointHandle & endPoint,
624
                                       const Inet::IPAddress & peerAddress, uint16_t peerPort)
625
0
{
626
0
    TCPBase * tcp = reinterpret_cast<TCPBase *>(listenEndPoint->mAppState);
627
0
    ReturnAndLogOnFailure(tcp->DoHandleIncomingConnection(listenEndPoint, endPoint, peerAddress, peerPort), Inet,
628
0
                          "Failure accepting incoming connection");
629
0
}
630
631
CHIP_ERROR TCPBase::DoHandleIncomingConnection(const Inet::TCPEndPointHandle & listenEndPoint,
632
                                               const Inet::TCPEndPointHandle & endPoint, const Inet::IPAddress & peerAddress,
633
                                               uint16_t peerPort)
634
0
{
635
0
#if INET_CONFIG_TEST
636
0
    if (sForceFailureInDoHandleIncomingConnection)
637
0
    {
638
0
        return CHIP_ERROR_INTERNAL;
639
0
    }
640
0
#endif
641
642
    // GetPeerAddress may fail if the client has already closed the connection, just drop it.
643
0
    PeerAddress addr;
644
0
    ReturnErrorOnFailure(GetPeerAddress(*endPoint, addr));
645
646
0
    ActiveTCPConnectionState * activeConnection = AllocateConnection(endPoint, addr);
647
0
    VerifyOrReturnError(activeConnection != nullptr, CHIP_ERROR_TOO_MANY_CONNECTIONS);
648
649
0
    auto connectionCleanup = ScopeExit([&]() { activeConnection->Free(); });
650
651
0
    endPoint->mAppState          = this;
652
0
    endPoint->OnDataReceived     = HandleTCPEndPointDataReceived;
653
0
    endPoint->OnDataSent         = nullptr;
654
0
    endPoint->OnConnectionClosed = HandleTCPEndPointConnectionClosed;
655
656
    // By default, disable TCP Nagle buffering by setting TCP_NODELAY socket option to true
657
    // If it fails, we can still use the connection
658
0
    RETURN_SAFELY_IGNORED endPoint->EnableNoDelay();
659
660
0
    mUsedEndPointCount++;
661
0
    activeConnection->mConnectionState = TCPState::kConnected;
662
663
    // Set the TCPKeepalive configurations on the received connection
664
    // If it fails, we can still use the connection until it dies
665
0
    RETURN_SAFELY_IGNORED endPoint->EnableKeepAlive(activeConnection->mTCPKeepAliveIntervalSecs,
666
0
                                                    activeConnection->mTCPMaxNumKeepAliveProbes);
667
668
0
    char addrStr[Transport::PeerAddress::kMaxToStringSize];
669
0
    peerAddress.ToString(addrStr);
670
0
    ChipLogProgress(Inet, "Incoming connection established with peer at %s.", addrStr);
671
672
    // Call the upper layer handler for incoming connection received.
673
0
    HandleConnectionReceived(*activeConnection);
674
675
0
    connectionCleanup.release();
676
677
0
    return CHIP_NO_ERROR;
678
0
}
679
680
void TCPBase::HandleAcceptError(const Inet::TCPEndPointHandle & listeningEndpoint, CHIP_ERROR err)
681
0
{
682
0
    ChipLogError(Inet, "Accept error: %" CHIP_ERROR_FORMAT, err.Format());
683
0
}
684
685
CHIP_ERROR TCPBase::TCPConnect(const PeerAddress & address, Transport::AppTCPConnectionCallbackCtxt * appState,
686
                               ActiveTCPConnectionHandle & outPeerConnState)
687
0
{
688
0
    VerifyOrReturnError(mState == TCPState::kInitialized, CHIP_ERROR_INCORRECT_STATE);
689
690
    // Verify that PeerAddress AddressType is TCP
691
0
    VerifyOrReturnError(address.GetTransportType() == Transport::Type::kTcp, CHIP_ERROR_INVALID_ARGUMENT);
692
693
0
    VerifyOrReturnError(mUsedEndPointCount < mActiveConnectionsSize, CHIP_ERROR_NO_MEMORY);
694
695
0
    char addrStr[Transport::PeerAddress::kMaxToStringSize];
696
0
    address.ToString(addrStr);
697
0
    ChipLogProgress(Inet, "Connecting to peer %s.", addrStr);
698
699
0
    ReturnErrorOnFailure(StartConnect(address, appState, outPeerConnState));
700
701
0
    return CHIP_NO_ERROR;
702
0
}
703
704
void TCPBase::TCPDisconnect(ActiveTCPConnectionState & conn, bool shouldAbort)
705
0
{
706
    // If there are still active references, we need to notify them of connection closure
707
0
    SuppressCallback suppressCallback = (conn.GetReferenceCount() > 0) ? SuppressCallback::No : SuppressCallback::Yes;
708
709
    // This call should be able to disconnect the connection either when it is
710
    // already established, or when it is being set up.
711
0
    if ((conn.IsConnected() && shouldAbort) || conn.IsConnecting())
712
0
    {
713
0
        CloseConnectionInternal(conn, CHIP_ERROR_CONNECTION_ABORTED, suppressCallback);
714
0
    }
715
716
0
    if (conn.IsConnected() && !shouldAbort)
717
0
    {
718
0
        CloseConnectionInternal(conn, CHIP_NO_ERROR, suppressCallback);
719
0
    }
720
0
}
721
722
bool TCPBase::HasActiveConnections() const
723
0
{
724
0
    for (size_t i = 0; i < mActiveConnectionsSize; i++)
725
0
    {
726
0
        if (mActiveConnections[i].IsConnected())
727
0
        {
728
0
            return true;
729
0
        }
730
0
    }
731
732
0
    return false;
733
0
}
734
735
void TCPBase::InitEndpoint(const Inet::TCPEndPointHandle & endpoint)
736
0
{
737
0
    endpoint->mAppState         = reinterpret_cast<void *>(this);
738
0
    endpoint->OnConnectComplete = HandleTCPEndPointConnectComplete;
739
0
    endpoint->SetConnectTimeout(mConnectTimeout);
740
0
}
741
742
#if INET_CONFIG_TEST
743
bool TCPBase::sForceFailureInDoHandleIncomingConnection = false;
744
#endif
745
746
} // namespace Transport
747
} // namespace chip