Line data Source code
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 22 : CHIP_ERROR GetPeerAddress(Inet::TCPEndPoint & endPoint, PeerAddress & outAddr)
55 : {
56 : Inet::IPAddress ipAddress;
57 : uint16_t port;
58 22 : Inet::InterfaceId interfaceId;
59 22 : ReturnErrorOnFailure(endPoint.GetPeerInfo(&ipAddress, &port));
60 22 : ReturnErrorOnFailure(endPoint.GetInterfaceId(&interfaceId));
61 22 : outAddr = PeerAddress::TCP(ipAddress, port, interfaceId);
62 :
63 22 : return CHIP_NO_ERROR;
64 : }
65 :
66 : } // namespace
67 :
68 53 : TCPBase::~TCPBase()
69 : {
70 : // Call Close to free the listening socket and close all active connections.
71 53 : Close();
72 53 : }
73 :
74 56 : void TCPBase::CloseActiveConnections()
75 : {
76 : // Nothing to do; we can't release as long as references are being held
77 280 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
78 : {
79 224 : if (mActiveConnections[i].InUse())
80 : {
81 2 : CloseConnectionInternal(mActiveConnections[i], CHIP_NO_ERROR, SuppressCallback::Yes);
82 : }
83 : }
84 56 : }
85 :
86 23 : CHIP_ERROR TCPBase::Init(TcpListenParameters & params)
87 : {
88 23 : CHIP_ERROR err = CHIP_NO_ERROR;
89 :
90 23 : VerifyOrExit(mState == TCPState::kNotReady, err = CHIP_ERROR_INCORRECT_STATE);
91 :
92 23 : mEndpointType = params.GetAddressType();
93 :
94 : // Primary socket endpoint created to help get EndPointManager handle for creating multiple
95 : // connection endpoints at runtime.
96 23 : err = params.GetEndPointManager()->NewEndPoint(mListenSocket);
97 23 : SuccessOrExit(err);
98 :
99 23 : if (params.IsServerListenEnabled())
100 : {
101 44 : err = mListenSocket->Bind(params.GetAddressType(), Inet::IPAddress::Any, params.GetListenPort(),
102 22 : params.GetInterfaceId().IsPresent());
103 22 : SuccessOrExit(err);
104 :
105 22 : mListenSocket->mAppState = reinterpret_cast<void *>(this);
106 22 : mListenSocket->OnConnectionReceived = HandleIncomingConnection;
107 22 : mListenSocket->OnAcceptError = HandleAcceptError;
108 :
109 22 : err = mListenSocket->Listen(kListenBacklogSize);
110 22 : SuccessOrExit(err);
111 22 : ChipLogProgress(Inet, "TCP server listening on port %d for incoming connections", params.GetListenPort());
112 : }
113 :
114 23 : mState = TCPState::kInitialized;
115 :
116 23 : exit:
117 46 : if (err != CHIP_NO_ERROR)
118 : {
119 0 : ChipLogError(Inet, "Failed to initialize TCP transport: %" CHIP_ERROR_FORMAT, err.Format());
120 0 : mListenSocket.Release();
121 : }
122 :
123 23 : return err;
124 : }
125 :
126 56 : void TCPBase::Close()
127 : {
128 56 : mListenSocket.Release();
129 :
130 56 : CloseActiveConnections();
131 :
132 56 : mState = TCPState::kNotReady;
133 56 : }
134 :
135 30 : ActiveTCPConnectionState * TCPBase::AllocateConnection(const Inet::TCPEndPointHandle & endpoint, const PeerAddress & address)
136 : {
137 : // If a peer initiates a connection through HandleIncomingConnection but the connection is never claimed
138 : // in ProcessSingleMessage, we'll be left with a dangling ActiveTCPConnectionState which can be
139 : // reclaimed. Don't try to reclaim these connections unless we're out of space
140 30 : for (int reclaim = 0; reclaim < 2; reclaim++)
141 : {
142 43 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
143 : {
144 43 : ActiveTCPConnectionState * activeConnection = &mActiveConnections[i];
145 43 : if (!activeConnection->InUse() && (activeConnection->GetReferenceCount() == 0))
146 : {
147 : // Update state for the active connection
148 50 : activeConnection->Init(endpoint, address, [this](auto & conn) { TCPDisconnect(conn, true); });
149 30 : return activeConnection;
150 : }
151 : }
152 :
153 : // Out of space; reclaim connections that were never claimed by ProcessSingleMessage
154 : // (i.e. that have a ref count of 0)
155 0 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
156 : {
157 0 : ActiveTCPConnectionState * activeConnection = &mActiveConnections[i];
158 0 : if (!activeConnection->InUse() && (activeConnection->GetReferenceCount() != 0))
159 : {
160 : char addrStr[Transport::PeerAddress::kMaxToStringSize];
161 0 : activeConnection->mPeerAddr.ToString(addrStr);
162 0 : ChipLogError(Inet, "Leaked TCP connection %p to %s.", activeConnection, addrStr);
163 : }
164 0 : ActiveTCPConnectionHandle releaseUnclaimed(activeConnection);
165 0 : }
166 : }
167 0 : return nullptr;
168 : }
169 :
170 : // Find an ActiveTCPConnectionState corresponding to a peer address
171 32 : ActiveTCPConnectionHandle TCPBase::FindInUseConnection(const PeerAddress & address)
172 : {
173 32 : if (address.GetTransportType() != Type::kTcp)
174 : {
175 0 : return nullptr;
176 : }
177 :
178 100 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
179 : {
180 83 : auto & conn = mActiveConnections[i];
181 83 : if (!conn.InUse())
182 : {
183 68 : continue;
184 : }
185 :
186 15 : if (conn.mPeerAddr == address)
187 : {
188 : Inet::IPAddress addr;
189 : uint16_t port;
190 15 : if (conn.IsConnected())
191 : {
192 : // Failure to get peer information means the connection is bad; close it
193 6 : CHIP_ERROR err = conn.mEndPoint->GetPeerInfo(&addr, &port);
194 12 : if (err != CHIP_NO_ERROR)
195 : {
196 0 : CloseConnectionInternal(conn, err, SuppressCallback::No);
197 0 : return nullptr;
198 : }
199 : }
200 :
201 15 : return ActiveTCPConnectionHandle(&conn);
202 : }
203 : }
204 :
205 17 : return nullptr;
206 : }
207 :
208 : // Find the ActiveTCPConnectionState for a given TCPEndPoint
209 16 : ActiveTCPConnectionState * TCPBase::FindActiveConnection(const Inet::TCPEndPointHandle & endPoint)
210 : {
211 23 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
212 : {
213 23 : if (mActiveConnections[i].mEndPoint == endPoint && mActiveConnections[i].IsConnected())
214 : {
215 16 : return &mActiveConnections[i];
216 : }
217 : }
218 0 : return nullptr;
219 : }
220 :
221 24 : ActiveTCPConnectionHandle TCPBase::FindInUseConnection(const Inet::TCPEndPoint & endPoint)
222 : {
223 29 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
224 : {
225 29 : if (mActiveConnections[i].mEndPoint == endPoint)
226 : {
227 24 : return ActiveTCPConnectionHandle(&mActiveConnections[i]);
228 : }
229 : }
230 0 : return nullptr;
231 : }
232 :
233 13 : CHIP_ERROR TCPBase::PrepareBuffer(System::PacketBufferHandle & msgBuf)
234 : {
235 : // Sent buffer data format is:
236 : // - packet size as a uint32_t
237 : // - actual data
238 :
239 13 : VerifyOrReturnError(mState == TCPState::kInitialized, CHIP_ERROR_INCORRECT_STATE);
240 13 : VerifyOrReturnError(kPacketSizeBytes + msgBuf->DataLength() <= System::PacketBuffer::kLargeBufMaxSizeWithoutReserve,
241 : CHIP_ERROR_INVALID_ARGUMENT);
242 :
243 : static_assert(kPacketSizeBytes <= UINT16_MAX);
244 13 : VerifyOrReturnError(msgBuf->EnsureReservedSize(static_cast<uint16_t>(kPacketSizeBytes)), CHIP_ERROR_NO_MEMORY);
245 :
246 13 : msgBuf->SetStart(msgBuf->Start() - kPacketSizeBytes);
247 :
248 13 : uint8_t * output = msgBuf->Start();
249 13 : LittleEndian::Write32(output, static_cast<uint32_t>(msgBuf->DataLength() - kPacketSizeBytes));
250 :
251 13 : return CHIP_NO_ERROR;
252 : }
253 :
254 9 : CHIP_ERROR TCPBase::SendMessage(const Transport::PeerAddress & address, System::PacketBufferHandle && msgBuf)
255 : {
256 9 : VerifyOrReturnError(address.GetTransportType() == Type::kTcp, CHIP_ERROR_INVALID_ARGUMENT);
257 9 : ReturnErrorOnFailure(PrepareBuffer(msgBuf));
258 :
259 : // Must find a previously-established connection with an owning reference
260 9 : auto connection = FindInUseConnection(address);
261 9 : VerifyOrReturnError(!connection.IsNull(), CHIP_ERROR_INCORRECT_STATE);
262 9 : if (connection->IsConnected())
263 : {
264 2 : return connection->mEndPoint->Send(std::move(msgBuf));
265 : }
266 :
267 7 : return SendAfterConnect(connection, std::move(msgBuf));
268 9 : }
269 :
270 4 : CHIP_ERROR TCPBase::SendMessage(const ActiveTCPConnectionHandle & connection, System::PacketBufferHandle && msgBuf)
271 : {
272 4 : VerifyOrReturnError(!connection.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
273 4 : ReturnErrorOnFailure(PrepareBuffer(msgBuf));
274 :
275 4 : if (connection->IsConnected())
276 : {
277 4 : return connection->mEndPoint->Send(std::move(msgBuf));
278 : }
279 :
280 0 : return SendAfterConnect(connection, std::move(msgBuf));
281 : }
282 :
283 21 : CHIP_ERROR TCPBase::StartConnect(const PeerAddress & addr, Transport::AppTCPConnectionCallbackCtxt * appState,
284 : ActiveTCPConnectionHandle & outPeerConnState)
285 : {
286 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
287 21 : Inet::TCPEndPointHandle endPoint;
288 21 : outPeerConnState.Release();
289 21 : ReturnErrorOnFailure(mListenSocket->GetEndPointManager().NewEndPoint(endPoint));
290 :
291 21 : InitEndpoint(endPoint);
292 :
293 21 : ActiveTCPConnectionHandle activeConnection = FindInUseConnection(addr);
294 : // Re-use existing connection to peer if already connected
295 21 : if (!activeConnection.IsNull())
296 : {
297 4 : if (appState != nullptr)
298 : {
299 : // We do not support parallel attempts to connect to peer when setting appState
300 0 : VerifyOrReturnError(activeConnection->mConnectionState == TCPState::kConnected &&
301 : activeConnection->mAppState == nullptr,
302 : CHIP_ERROR_INCORRECT_STATE);
303 0 : activeConnection->mAppState = appState;
304 : }
305 4 : outPeerConnState = activeConnection;
306 :
307 4 : if (activeConnection->mConnectionState == TCPState::kConnected)
308 : {
309 2 : HandleConnectionAttemptComplete(activeConnection, CHIP_NO_ERROR);
310 : }
311 :
312 4 : return CHIP_NO_ERROR;
313 : }
314 :
315 17 : activeConnection = AllocateConnection(endPoint, addr);
316 17 : VerifyOrReturnError(!activeConnection.IsNull(), CHIP_ERROR_NO_MEMORY);
317 17 : activeConnection->mAppState = appState;
318 17 : activeConnection->mConnectionState = TCPState::kConnecting;
319 :
320 17 : ReturnErrorOnFailure(endPoint->Connect(addr.GetIPAddress(), addr.GetPort(), addr.GetInterface()));
321 :
322 17 : mUsedEndPointCount++;
323 :
324 : // Set the return value of the peer connection state to the allocated
325 : // connection.
326 17 : outPeerConnState = activeConnection;
327 :
328 17 : return CHIP_NO_ERROR;
329 : #else
330 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
331 : #endif
332 21 : }
333 :
334 7 : CHIP_ERROR TCPBase::SendAfterConnect(const ActiveTCPConnectionHandle & existing, System::PacketBufferHandle && msg)
335 : {
336 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
337 7 : VerifyOrReturnError(!existing.IsNull(), CHIP_ERROR_INCORRECT_STATE);
338 7 : const PeerAddress & addr = existing->mPeerAddr;
339 :
340 : // This will initiate a connection to the specified peer
341 7 : bool alreadyConnecting = false;
342 :
343 : // Iterate through the ENTIRE array. If a pending packet for
344 : // the address already exists, this means a connection is pending and
345 : // does NOT need to be re-established.
346 7 : mPendingPackets.ForEachActiveObject([&](PendingPacket * pending) {
347 2 : if (pending->mPeerAddress == addr)
348 : {
349 : // same destination exists.
350 2 : alreadyConnecting = true;
351 2 : pending->mPacketBuffer->AddToEnd(std::move(msg));
352 2 : return Loop::Break;
353 : }
354 0 : return Loop::Continue;
355 : });
356 :
357 : // If already connecting, buffer was just enqueued for more sending
358 7 : if (alreadyConnecting)
359 : {
360 2 : return CHIP_NO_ERROR;
361 : }
362 :
363 : // enqueue the packet once the connection succeeds
364 5 : VerifyOrReturnError(mPendingPackets.CreateObject(addr, std::move(msg)) != nullptr, CHIP_ERROR_NO_MEMORY);
365 :
366 5 : return CHIP_NO_ERROR;
367 : #else
368 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
369 : #endif
370 : }
371 :
372 16 : CHIP_ERROR TCPBase::ProcessReceivedBuffer(const Inet::TCPEndPointHandle & endPoint, const PeerAddress & peerAddress,
373 : System::PacketBufferHandle && buffer)
374 : {
375 16 : ActiveTCPConnectionState * state = FindActiveConnection(endPoint);
376 : // There must be a preceding TCPConnect to hold a reference to connection
377 16 : VerifyOrReturnError(state != nullptr, CHIP_ERROR_INTERNAL);
378 16 : state->mReceived.AddToEnd(std::move(buffer));
379 :
380 36 : while (!state->mReceived.IsNull())
381 : {
382 : uint8_t messageSizeBuf[kPacketSizeBytes];
383 22 : CHIP_ERROR err = state->mReceived->Read(messageSizeBuf);
384 44 : if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
385 : {
386 : // We don't have enough data to read the message size. Wait until there's more.
387 0 : return CHIP_NO_ERROR;
388 : }
389 44 : if (err != CHIP_NO_ERROR)
390 : {
391 0 : return err;
392 : }
393 22 : uint32_t messageSize = LittleEndian::Get32(messageSizeBuf);
394 22 : if (messageSize >= kMaxTCPMessageSize)
395 : {
396 : // Message is too big for this node to process. Disconnect from peer.
397 1 : ChipLogError(Inet, "Received TCP message of length %" PRIu32 " exceeds limit.", messageSize);
398 1 : CloseConnectionInternal(*state, CHIP_ERROR_MESSAGE_TOO_LONG, SuppressCallback::No);
399 :
400 1 : return CHIP_ERROR_MESSAGE_TOO_LONG;
401 : }
402 : // The subtraction will not underflow because we successfully read kPacketSizeBytes.
403 21 : if (messageSize > (state->mReceived->TotalLength() - kPacketSizeBytes))
404 : {
405 : // We have not yet received the complete message.
406 0 : return CHIP_NO_ERROR;
407 : }
408 :
409 21 : state->mReceived.Consume(kPacketSizeBytes);
410 :
411 21 : if (messageSize == 0)
412 : {
413 : // No payload but considered a valid message. Return success to keep the connection alive.
414 1 : return CHIP_NO_ERROR;
415 : }
416 :
417 20 : ReturnErrorOnFailure(ProcessSingleMessage(peerAddress, *state, messageSize));
418 : }
419 :
420 14 : return CHIP_NO_ERROR;
421 : }
422 :
423 20 : CHIP_ERROR TCPBase::ProcessSingleMessage(const PeerAddress & peerAddress, ActiveTCPConnectionState & state, size_t messageSize)
424 : {
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 20 : System::PacketBufferHandle message;
429 :
430 20 : if (state.mReceived->DataLength() == messageSize)
431 : {
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 13 : message = state.mReceived.PopHead();
436 : }
437 : else
438 : {
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 7 : message = System::PacketBufferHandle::New(messageSize, 0);
444 7 : if (message.IsNull())
445 : {
446 0 : return CHIP_ERROR_NO_MEMORY;
447 : }
448 7 : CHIP_ERROR err = state.mReceived->Read(message->Start(), messageSize);
449 7 : state.mReceived.Consume(messageSize);
450 7 : ReturnErrorOnFailure(err);
451 7 : message->SetDataLength(messageSize);
452 : }
453 :
454 20 : MessageTransportContext msgContext;
455 20 : msgContext.conn = &state; // Take ownership
456 20 : HandleMessageReceived(peerAddress, std::move(message), &msgContext);
457 20 : return CHIP_NO_ERROR;
458 20 : }
459 :
460 30 : void TCPBase::CloseConnectionInternal(ActiveTCPConnectionState & connection, CHIP_ERROR err, SuppressCallback suppressCallback)
461 : {
462 30 : if (connection.mConnectionState == TCPState::kClosed || !connection.mEndPoint)
463 : {
464 0 : return;
465 : }
466 : TCPState prevState;
467 : char addrStr[Transport::PeerAddress::kMaxToStringSize];
468 30 : connection.mPeerAddr.ToString(addrStr);
469 30 : ChipLogProgress(Inet, "Closing connection with peer %s.", addrStr);
470 :
471 30 : Inet::TCPEndPointHandle endpoint = connection.mEndPoint;
472 30 : connection.mEndPoint.Release();
473 60 : if (err == CHIP_NO_ERROR)
474 : {
475 2 : endpoint->Close();
476 : }
477 : else
478 : {
479 28 : endpoint->Abort();
480 : }
481 :
482 30 : prevState = connection.mConnectionState;
483 30 : connection.mConnectionState = TCPState::kClosed;
484 :
485 30 : if (suppressCallback == SuppressCallback::No)
486 : {
487 8 : if (prevState == TCPState::kConnecting)
488 : {
489 0 : ActiveTCPConnectionHandle holder(&connection);
490 : // Call upper layer connection attempt complete handler
491 0 : HandleConnectionAttemptComplete(holder, err);
492 0 : }
493 : else
494 : {
495 : // Call upper layer connection closed handler
496 8 : HandleConnectionClosed(connection, err);
497 : }
498 : }
499 :
500 30 : connection.Free();
501 30 : mUsedEndPointCount--;
502 30 : }
503 :
504 9 : CHIP_ERROR TCPBase::HandleTCPEndPointDataReceived(const Inet::TCPEndPointHandle & endPoint, System::PacketBufferHandle && buffer)
505 : {
506 9 : PeerAddress peerAddress;
507 9 : ReturnErrorOnFailure(GetPeerAddress(*endPoint, peerAddress));
508 :
509 9 : TCPBase * tcp = reinterpret_cast<TCPBase *>(endPoint->mAppState);
510 9 : CHIP_ERROR err = tcp->ProcessReceivedBuffer(endPoint, peerAddress, std::move(buffer));
511 :
512 18 : if (err != CHIP_NO_ERROR)
513 : {
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 : }
518 9 : return CHIP_NO_ERROR;
519 : }
520 :
521 17 : void TCPBase::HandleTCPEndPointConnectComplete(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR conErr)
522 : {
523 17 : CHIP_ERROR err = CHIP_NO_ERROR;
524 17 : bool foundPendingPacket = false;
525 17 : TCPBase * tcp = reinterpret_cast<TCPBase *>(endPoint->mAppState);
526 17 : ActiveTCPConnectionHandle activeConnection;
527 :
528 17 : PeerAddress addr;
529 : char addrStr[Transport::PeerAddress::kMaxToStringSize];
530 17 : activeConnection = tcp->FindInUseConnection(*endPoint);
531 17 : if (activeConnection.IsNull())
532 : {
533 0 : err = GetPeerAddress(*endPoint, addr);
534 : }
535 : else
536 : {
537 17 : addr = activeConnection->mPeerAddr;
538 : }
539 34 : if (err == CHIP_NO_ERROR)
540 : {
541 17 : addr.ToString(addrStr);
542 : }
543 :
544 51 : if (conErr != CHIP_NO_ERROR || err != CHIP_NO_ERROR)
545 : {
546 0 : auto failure = (conErr != CHIP_NO_ERROR) ? conErr : err;
547 0 : if (!activeConnection.IsNull())
548 : {
549 0 : tcp->CloseConnectionInternal(*activeConnection, failure, SuppressCallback::No);
550 : }
551 0 : ChipLogError(Inet, "Connection establishment with %s encountered an error: %" CHIP_ERROR_FORMAT, addrStr, failure.Format());
552 0 : return;
553 : }
554 : // Set the Data received handler when connection completes
555 17 : endPoint->OnDataReceived = HandleTCPEndPointDataReceived;
556 17 : endPoint->OnDataSent = nullptr;
557 17 : endPoint->OnConnectionClosed = HandleTCPEndPointConnectionClosed;
558 :
559 17 : VerifyOrDie(!activeConnection.IsNull());
560 :
561 : // Set to Connected state
562 17 : 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 17 : err = endPoint->EnableNoDelay();
569 34 : if (err != CHIP_NO_ERROR)
570 : {
571 0 : tcp->CloseConnectionInternal(*activeConnection, err, SuppressCallback::No);
572 0 : return;
573 : }
574 :
575 : // Send any pending packets that are queued for this connection
576 17 : tcp->mPendingPackets.ForEachActiveObject([&](PendingPacket * pending) {
577 5 : if (pending->mPeerAddress == addr)
578 : {
579 5 : foundPendingPacket = true;
580 5 : System::PacketBufferHandle buffer = std::move(pending->mPacketBuffer);
581 5 : tcp->mPendingPackets.ReleaseObject(pending);
582 :
583 15 : if ((conErr == CHIP_NO_ERROR) && (err == CHIP_NO_ERROR))
584 : {
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 5 : err = endPoint->Send(std::move(buffer));
588 : }
589 5 : }
590 5 : return Loop::Continue;
591 : });
592 :
593 : // Set the TCPKeepalive configurations on the established connection
594 34 : TEMPORARY_RETURN_IGNORED endPoint->EnableKeepAlive(activeConnection->mTCPKeepAliveIntervalSecs,
595 17 : activeConnection->mTCPMaxNumKeepAliveProbes);
596 :
597 17 : ChipLogProgress(Inet, "Connection established successfully with %s.", addrStr);
598 :
599 : // Let higher layer/delegate know that connection is successfully
600 : // established
601 17 : tcp->HandleConnectionAttemptComplete(activeConnection, CHIP_NO_ERROR);
602 17 : }
603 :
604 7 : void TCPBase::HandleTCPEndPointConnectionClosed(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR err)
605 : {
606 7 : TCPBase * tcp = reinterpret_cast<TCPBase *>(endPoint->mAppState);
607 7 : ActiveTCPConnectionHandle activeConnection = tcp->FindInUseConnection(*endPoint);
608 :
609 7 : if (activeConnection.IsNull())
610 : {
611 0 : return;
612 : }
613 :
614 14 : if (err == CHIP_NO_ERROR && activeConnection->IsConnected())
615 : {
616 2 : err = CHIP_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY;
617 : }
618 :
619 7 : tcp->CloseConnectionInternal(*activeConnection, err, SuppressCallback::No);
620 7 : }
621 :
622 : // Handler for incoming connection requests from peer nodes
623 15 : void TCPBase::HandleIncomingConnection(const Inet::TCPEndPointHandle & listenEndPoint, const Inet::TCPEndPointHandle & endPoint,
624 : const Inet::IPAddress & peerAddress, uint16_t peerPort)
625 : {
626 15 : TCPBase * tcp = reinterpret_cast<TCPBase *>(listenEndPoint->mAppState);
627 15 : ReturnAndLogOnFailure(tcp->DoHandleIncomingConnection(listenEndPoint, endPoint, peerAddress, peerPort), Inet,
628 : "Failure accepting incoming connection");
629 : }
630 :
631 15 : CHIP_ERROR TCPBase::DoHandleIncomingConnection(const Inet::TCPEndPointHandle & listenEndPoint,
632 : const Inet::TCPEndPointHandle & endPoint, const Inet::IPAddress & peerAddress,
633 : uint16_t peerPort)
634 : {
635 : #if INET_CONFIG_TEST
636 15 : if (sForceFailureInDoHandleIncomingConnection)
637 : {
638 2 : return CHIP_ERROR_INTERNAL;
639 : }
640 : #endif
641 :
642 : // GetPeerAddress may fail if the client has already closed the connection, just drop it.
643 13 : PeerAddress addr;
644 13 : ReturnErrorOnFailure(GetPeerAddress(*endPoint, addr));
645 :
646 13 : ActiveTCPConnectionState * activeConnection = AllocateConnection(endPoint, addr);
647 13 : VerifyOrReturnError(activeConnection != nullptr, CHIP_ERROR_TOO_MANY_CONNECTIONS);
648 :
649 13 : auto connectionCleanup = ScopeExit([&]() { activeConnection->Free(); });
650 :
651 13 : endPoint->mAppState = this;
652 13 : endPoint->OnDataReceived = HandleTCPEndPointDataReceived;
653 13 : endPoint->OnDataSent = nullptr;
654 13 : 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 13 : RETURN_SAFELY_IGNORED endPoint->EnableNoDelay();
659 :
660 13 : mUsedEndPointCount++;
661 13 : 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 13 : RETURN_SAFELY_IGNORED endPoint->EnableKeepAlive(activeConnection->mTCPKeepAliveIntervalSecs,
666 13 : activeConnection->mTCPMaxNumKeepAliveProbes);
667 :
668 : char addrStr[Transport::PeerAddress::kMaxToStringSize];
669 13 : peerAddress.ToString(addrStr);
670 13 : ChipLogProgress(Inet, "Incoming connection established with peer at %s.", addrStr);
671 :
672 : // Call the upper layer handler for incoming connection received.
673 13 : HandleConnectionReceived(*activeConnection);
674 :
675 13 : connectionCleanup.release();
676 :
677 13 : return CHIP_NO_ERROR;
678 13 : }
679 :
680 2 : void TCPBase::HandleAcceptError(const Inet::TCPEndPointHandle & listeningEndpoint, CHIP_ERROR err)
681 : {
682 2 : ChipLogError(Inet, "Accept error: %" CHIP_ERROR_FORMAT, err.Format());
683 2 : }
684 :
685 21 : CHIP_ERROR TCPBase::TCPConnect(const PeerAddress & address, Transport::AppTCPConnectionCallbackCtxt * appState,
686 : ActiveTCPConnectionHandle & outPeerConnState)
687 : {
688 21 : VerifyOrReturnError(mState == TCPState::kInitialized, CHIP_ERROR_INCORRECT_STATE);
689 :
690 : // Verify that PeerAddress AddressType is TCP
691 21 : VerifyOrReturnError(address.GetTransportType() == Transport::Type::kTcp, CHIP_ERROR_INVALID_ARGUMENT);
692 :
693 21 : VerifyOrReturnError(mUsedEndPointCount < mActiveConnectionsSize, CHIP_ERROR_NO_MEMORY);
694 :
695 : char addrStr[Transport::PeerAddress::kMaxToStringSize];
696 21 : address.ToString(addrStr);
697 21 : ChipLogProgress(Inet, "Connecting to peer %s.", addrStr);
698 :
699 21 : ReturnErrorOnFailure(StartConnect(address, appState, outPeerConnState));
700 :
701 21 : return CHIP_NO_ERROR;
702 : }
703 :
704 20 : void TCPBase::TCPDisconnect(ActiveTCPConnectionState & conn, bool shouldAbort)
705 : {
706 : // If there are still active references, we need to notify them of connection closure
707 20 : 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 20 : if ((conn.IsConnected() && shouldAbort) || conn.IsConnecting())
712 : {
713 20 : CloseConnectionInternal(conn, CHIP_ERROR_CONNECTION_ABORTED, suppressCallback);
714 : }
715 :
716 20 : if (conn.IsConnected() && !shouldAbort)
717 : {
718 0 : CloseConnectionInternal(conn, CHIP_NO_ERROR, suppressCallback);
719 : }
720 20 : }
721 :
722 42 : bool TCPBase::HasActiveConnections() const
723 : {
724 178 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
725 : {
726 144 : if (mActiveConnections[i].IsConnected())
727 : {
728 8 : return true;
729 : }
730 : }
731 :
732 34 : return false;
733 : }
734 :
735 21 : void TCPBase::InitEndpoint(const Inet::TCPEndPointHandle & endpoint)
736 : {
737 21 : endpoint->mAppState = reinterpret_cast<void *>(this);
738 21 : endpoint->OnConnectComplete = HandleTCPEndPointConnectComplete;
739 21 : endpoint->SetConnectTimeout(mConnectTimeout);
740 21 : }
741 :
742 : #if INET_CONFIG_TEST
743 : bool TCPBase::sForceFailureInDoHandleIncomingConnection = false;
744 : #endif
745 :
746 : } // namespace Transport
747 : } // namespace chip
|