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