blob: 21aa920fbae64c2fc8e78a1b49bc377d13f251e2 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2025 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file implements the CHIP Transport object that maintains TCP connections
* to peers. Handles both establishing new connections and accepting peer connection
* requests.
*/
#include <transport/raw/TCP.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <transport/raw/MessageHeader.h>
#include <inttypes.h>
#include <limits>
namespace chip {
namespace Transport {
namespace {
using namespace chip::Encoding;
// Packets start with a 32-bit size field.
constexpr size_t kPacketSizeBytes = 4;
static_assert(System::PacketBuffer::kLargeBufMaxSizeWithoutReserve <= UINT32_MAX, "Cast below could truncate the value");
static_assert(System::PacketBuffer::kLargeBufMaxSizeWithoutReserve >= kPacketSizeBytes,
"Large buffer allocation should be large enough to hold the length field");
constexpr uint32_t kMaxTCPMessageSize =
static_cast<uint32_t>(System::PacketBuffer::kLargeBufMaxSizeWithoutReserve - kPacketSizeBytes);
constexpr int kListenBacklogSize = 2;
CHIP_ERROR GetPeerAddress(Inet::TCPEndPoint & endPoint, PeerAddress & outAddr)
{
Inet::IPAddress ipAddress;
uint16_t port;
Inet::InterfaceId interfaceId;
ReturnErrorOnFailure(endPoint.GetPeerInfo(&ipAddress, &port));
ReturnErrorOnFailure(endPoint.GetInterfaceId(&interfaceId));
outAddr = PeerAddress::TCP(ipAddress, port, interfaceId);
return CHIP_NO_ERROR;
}
} // namespace
TCPBase::~TCPBase()
{
// Call Close to free the listening socket and close all active connections.
Close();
}
void TCPBase::CloseActiveConnections()
{
// Nothing to do; we can't release as long as references are being held
for (size_t i = 0; i < mActiveConnectionsSize; i++)
{
if (mActiveConnections[i].InUse())
{
CloseConnectionInternal(mActiveConnections[i], CHIP_NO_ERROR, SuppressCallback::Yes);
}
}
}
CHIP_ERROR TCPBase::Init(TcpListenParameters & params)
{
CHIP_ERROR err = CHIP_NO_ERROR;
VerifyOrExit(mState == TCPState::kNotReady, err = CHIP_ERROR_INCORRECT_STATE);
mEndpointType = params.GetAddressType();
// Primary socket endpoint created to help get EndPointManager handle for creating multiple
// connection endpoints at runtime.
err = params.GetEndPointManager()->NewEndPoint(mListenSocket);
SuccessOrExit(err);
if (params.IsServerListenEnabled())
{
err = mListenSocket->Bind(params.GetAddressType(), Inet::IPAddress::Any, params.GetListenPort(),
params.GetInterfaceId().IsPresent());
SuccessOrExit(err);
mListenSocket->mAppState = reinterpret_cast<void *>(this);
mListenSocket->OnConnectionReceived = HandleIncomingConnection;
mListenSocket->OnAcceptError = HandleAcceptError;
err = mListenSocket->Listen(kListenBacklogSize);
SuccessOrExit(err);
ChipLogProgress(Inet, "TCP server listening on port %d for incoming connections", params.GetListenPort());
}
mState = TCPState::kInitialized;
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(Inet, "Failed to initialize TCP transport: %" CHIP_ERROR_FORMAT, err.Format());
mListenSocket.Release();
}
return err;
}
void TCPBase::Close()
{
mListenSocket.Release();
CloseActiveConnections();
mState = TCPState::kNotReady;
}
ActiveTCPConnectionState * TCPBase::AllocateConnection(const Inet::TCPEndPointHandle & endpoint, const PeerAddress & address)
{
// If a peer initiates a connection through HandleIncomingConnection but the connection is never claimed
// in ProcessSingleMessage, we'll be left with a dangling ActiveTCPConnectionState which can be
// reclaimed. Don't try to reclaim these connections unless we're out of space
for (int reclaim = 0; reclaim < 2; reclaim++)
{
for (size_t i = 0; i < mActiveConnectionsSize; i++)
{
if (!mActiveConnections[i].InUse())
{
ActiveTCPConnectionState * activeConnection = &mActiveConnections[i];
VerifyOrDie(activeConnection->GetReferenceCount() == 0);
// Update state for the active connection
activeConnection->Init(endpoint, address, [this](auto & conn) { TCPDisconnect(conn, true); });
return activeConnection;
}
}
// Out of space; reclaim connections that were never claimed by ProcessSingleMessage
// (i.e. that have a ref count of 0)
for (size_t i = 0; i < mActiveConnectionsSize; i++)
{
ActiveTCPConnectionHandle releaseUnclaimed(&mActiveConnections[i]);
}
}
return nullptr;
}
// Find an ActiveTCPConnectionState corresponding to a peer address
ActiveTCPConnectionHandle TCPBase::FindInUseConnection(const PeerAddress & address)
{
if (address.GetTransportType() != Type::kTcp)
{
return nullptr;
}
for (size_t i = 0; i < mActiveConnectionsSize; i++)
{
auto & conn = mActiveConnections[i];
if (!conn.InUse())
{
continue;
}
if (conn.mPeerAddr == address)
{
Inet::IPAddress addr;
uint16_t port;
if (conn.IsConnected() && conn.mEndPoint->GetPeerInfo(&addr, &port) != CHIP_NO_ERROR)
{
// Failure to get peer information means the connection is bad, re-establish connection
CHIP_ERROR err = TryResetConnection(conn);
if (err != CHIP_NO_ERROR)
{
CloseConnectionInternal(conn, err, SuppressCallback::No);
continue;
}
}
return ActiveTCPConnectionHandle(&conn);
}
}
return nullptr;
}
// Find the ActiveTCPConnectionState for a given TCPEndPoint
ActiveTCPConnectionState * TCPBase::FindActiveConnection(const Inet::TCPEndPointHandle & endPoint)
{
for (size_t i = 0; i < mActiveConnectionsSize; i++)
{
if (mActiveConnections[i].mEndPoint == endPoint && mActiveConnections[i].IsConnected())
{
return &mActiveConnections[i];
}
}
return nullptr;
}
ActiveTCPConnectionHandle TCPBase::FindInUseConnection(const Inet::TCPEndPoint & endPoint)
{
for (size_t i = 0; i < mActiveConnectionsSize; i++)
{
if (mActiveConnections[i].mEndPoint == endPoint)
{
return ActiveTCPConnectionHandle(&mActiveConnections[i]);
}
}
return nullptr;
}
CHIP_ERROR TCPBase::PrepareBuffer(System::PacketBufferHandle & msgBuf)
{
// Sent buffer data format is:
// - packet size as a uint32_t
// - actual data
VerifyOrReturnError(mState == TCPState::kInitialized, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(kPacketSizeBytes + msgBuf->DataLength() <= System::PacketBuffer::kLargeBufMaxSizeWithoutReserve,
CHIP_ERROR_INVALID_ARGUMENT);
static_assert(kPacketSizeBytes <= UINT16_MAX);
VerifyOrReturnError(msgBuf->EnsureReservedSize(static_cast<uint16_t>(kPacketSizeBytes)), CHIP_ERROR_NO_MEMORY);
msgBuf->SetStart(msgBuf->Start() - kPacketSizeBytes);
uint8_t * output = msgBuf->Start();
LittleEndian::Write32(output, static_cast<uint32_t>(msgBuf->DataLength() - kPacketSizeBytes));
return CHIP_NO_ERROR;
}
CHIP_ERROR TCPBase::SendMessage(const Transport::PeerAddress & address, System::PacketBufferHandle && msgBuf)
{
VerifyOrReturnError(address.GetTransportType() == Type::kTcp, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(PrepareBuffer(msgBuf));
// Must find a previously-established connection with an owning reference
auto connection = FindInUseConnection(address);
VerifyOrReturnError(!connection.IsNull(), CHIP_ERROR_INCORRECT_STATE);
if (connection->IsConnected())
{
return connection->mEndPoint->Send(std::move(msgBuf));
}
return SendAfterConnect(connection, std::move(msgBuf));
}
CHIP_ERROR TCPBase::SendMessage(const ActiveTCPConnectionHandle & connection, System::PacketBufferHandle && msgBuf)
{
VerifyOrReturnError(!connection.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(PrepareBuffer(msgBuf));
if (connection->IsConnected())
{
return connection->mEndPoint->Send(std::move(msgBuf));
}
return SendAfterConnect(connection, std::move(msgBuf));
}
CHIP_ERROR TCPBase::StartConnect(const PeerAddress & addr, Transport::AppTCPConnectionCallbackCtxt * appState,
ActiveTCPConnectionHandle & outPeerConnState)
{
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
Inet::TCPEndPointHandle endPoint;
outPeerConnState.Release();
ReturnErrorOnFailure(mListenSocket->GetEndPointManager().NewEndPoint(endPoint));
InitEndpoint(endPoint);
ActiveTCPConnectionHandle activeConnection = FindInUseConnection(addr);
// Re-use existing connection to peer if already connected
if (!activeConnection.IsNull())
{
if (appState != nullptr)
{
// We do not support parallel attempts to connect to peer when setting appState
VerifyOrReturnError(activeConnection->mConnectionState == TCPState::kConnected &&
activeConnection->mAppState == nullptr,
CHIP_ERROR_INCORRECT_STATE);
activeConnection->mAppState = appState;
}
outPeerConnState = activeConnection;
if (activeConnection->mConnectionState == TCPState::kConnected)
{
HandleConnectionAttemptComplete(activeConnection, CHIP_NO_ERROR);
}
return CHIP_NO_ERROR;
}
activeConnection = AllocateConnection(endPoint, addr);
VerifyOrReturnError(!activeConnection.IsNull(), CHIP_ERROR_NO_MEMORY);
activeConnection->mAppState = appState;
activeConnection->mConnectionState = TCPState::kConnecting;
ReturnErrorOnFailure(endPoint->Connect(addr.GetIPAddress(), addr.GetPort(), addr.GetInterface()));
mUsedEndPointCount++;
// Set the return value of the peer connection state to the allocated
// connection.
outPeerConnState = activeConnection;
return CHIP_NO_ERROR;
#else
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif
}
CHIP_ERROR TCPBase::SendAfterConnect(const ActiveTCPConnectionHandle & existing, System::PacketBufferHandle && msg)
{
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
VerifyOrReturnError(!existing.IsNull(), CHIP_ERROR_INCORRECT_STATE);
const PeerAddress & addr = existing->mPeerAddr;
// This will initiate a connection to the specified peer
bool alreadyConnecting = false;
// Iterate through the ENTIRE array. If a pending packet for
// the address already exists, this means a connection is pending and
// does NOT need to be re-established.
mPendingPackets.ForEachActiveObject([&](PendingPacket * pending) {
if (pending->mPeerAddress == addr)
{
// same destination exists.
alreadyConnecting = true;
pending->mPacketBuffer->AddToEnd(std::move(msg));
return Loop::Break;
}
return Loop::Continue;
});
// If already connecting, buffer was just enqueued for more sending
if (alreadyConnecting)
{
return CHIP_NO_ERROR;
}
// enqueue the packet once the connection succeeds
VerifyOrReturnError(mPendingPackets.CreateObject(addr, std::move(msg)) != nullptr, CHIP_ERROR_NO_MEMORY);
return CHIP_NO_ERROR;
#else
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif
}
CHIP_ERROR TCPBase::ProcessReceivedBuffer(const Inet::TCPEndPointHandle & endPoint, const PeerAddress & peerAddress,
System::PacketBufferHandle && buffer)
{
ActiveTCPConnectionState * state = FindActiveConnection(endPoint);
// There must be a preceding TCPConnect to hold a reference to connection
VerifyOrReturnError(state != nullptr, CHIP_ERROR_INTERNAL);
state->mReceived.AddToEnd(std::move(buffer));
while (!state->mReceived.IsNull())
{
uint8_t messageSizeBuf[kPacketSizeBytes];
CHIP_ERROR err = state->mReceived->Read(messageSizeBuf);
if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
{
// We don't have enough data to read the message size. Wait until there's more.
return CHIP_NO_ERROR;
}
if (err != CHIP_NO_ERROR)
{
return err;
}
uint32_t messageSize = LittleEndian::Get32(messageSizeBuf);
if (messageSize >= kMaxTCPMessageSize)
{
// Message is too big for this node to process. Disconnect from peer.
ChipLogError(Inet, "Received TCP message of length %" PRIu32 " exceeds limit.", messageSize);
CloseConnectionInternal(*state, CHIP_ERROR_MESSAGE_TOO_LONG, SuppressCallback::No);
return CHIP_ERROR_MESSAGE_TOO_LONG;
}
// The subtraction will not underflow because we successfully read kPacketSizeBytes.
if (messageSize > (state->mReceived->TotalLength() - kPacketSizeBytes))
{
// We have not yet received the complete message.
return CHIP_NO_ERROR;
}
state->mReceived.Consume(kPacketSizeBytes);
if (messageSize == 0)
{
// No payload but considered a valid message. Return success to keep the connection alive.
return CHIP_NO_ERROR;
}
ReturnErrorOnFailure(ProcessSingleMessage(peerAddress, *state, messageSize));
}
return CHIP_NO_ERROR;
}
CHIP_ERROR TCPBase::ProcessSingleMessage(const PeerAddress & peerAddress, ActiveTCPConnectionState & state, size_t messageSize)
{
// We enter with `state->mReceived` containing at least one full message, perhaps in a chain.
// `state->mReceived->Start()` currently points to the message data.
// On exit, `state->mReceived` will have had `messageSize` bytes consumed, no matter what.
System::PacketBufferHandle message;
if (state.mReceived->DataLength() == messageSize)
{
// In this case, the head packet buffer contains exactly the message.
// This is common because typical messages fit in a network packet, and are delivered as such.
// Peel off the head to pass upstream, which effectively consumes it from `state->mReceived`.
message = state.mReceived.PopHead();
}
else
{
// The message is either longer or shorter than the head buffer.
// In either case, copy the message to a fresh linear buffer to pass upstream. We always copy, rather than provide
// a shared reference to the current buffer, in case upper layers manipulate the buffer in ways that would affect
// our use, e.g. chaining it elsewhere or reusing space beyond the current message.
message = System::PacketBufferHandle::New(messageSize, 0);
if (message.IsNull())
{
return CHIP_ERROR_NO_MEMORY;
}
CHIP_ERROR err = state.mReceived->Read(message->Start(), messageSize);
state.mReceived.Consume(messageSize);
ReturnErrorOnFailure(err);
message->SetDataLength(messageSize);
}
MessageTransportContext msgContext;
msgContext.conn = &state; // Take ownership
HandleMessageReceived(peerAddress, std::move(message), &msgContext);
return CHIP_NO_ERROR;
}
void TCPBase::CloseConnectionInternal(ActiveTCPConnectionState & connection, CHIP_ERROR err, SuppressCallback suppressCallback)
{
if (connection.mConnectionState == TCPState::kClosed || !connection.mEndPoint)
{
return;
}
TCPState prevState;
char addrStr[Transport::PeerAddress::kMaxToStringSize];
connection.mPeerAddr.ToString(addrStr);
ChipLogProgress(Inet, "Closing connection with peer %s.", addrStr);
Inet::TCPEndPointHandle endpoint = connection.mEndPoint;
connection.mEndPoint.Release();
if (err == CHIP_NO_ERROR)
{
endpoint->Close();
}
else
{
endpoint->Abort();
}
prevState = connection.mConnectionState;
connection.mConnectionState = TCPState::kClosed;
if (suppressCallback == SuppressCallback::No)
{
if (prevState == TCPState::kConnecting)
{
ActiveTCPConnectionHandle holder(&connection);
// Call upper layer connection attempt complete handler
HandleConnectionAttemptComplete(holder, err);
}
else
{
// Call upper layer connection closed handler
HandleConnectionClosed(connection, err);
}
}
connection.Free();
mUsedEndPointCount--;
}
CHIP_ERROR TCPBase::HandleTCPEndPointDataReceived(const Inet::TCPEndPointHandle & endPoint, System::PacketBufferHandle && buffer)
{
PeerAddress peerAddress;
ReturnErrorOnFailure(GetPeerAddress(*endPoint, peerAddress));
TCPBase * tcp = reinterpret_cast<TCPBase *>(endPoint->mAppState);
CHIP_ERROR err = tcp->ProcessReceivedBuffer(endPoint, peerAddress, std::move(buffer));
if (err != CHIP_NO_ERROR)
{
// Connection could need to be closed at this point
ChipLogError(Inet, "Failed to accept received TCP message: %" CHIP_ERROR_FORMAT, err.Format());
return CHIP_ERROR_UNEXPECTED_EVENT;
}
return CHIP_NO_ERROR;
}
void TCPBase::HandleTCPEndPointConnectComplete(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR conErr)
{
CHIP_ERROR err = CHIP_NO_ERROR;
bool foundPendingPacket = false;
TCPBase * tcp = reinterpret_cast<TCPBase *>(endPoint->mAppState);
ActiveTCPConnectionHandle activeConnection;
PeerAddress addr;
char addrStr[Transport::PeerAddress::kMaxToStringSize];
activeConnection = tcp->FindInUseConnection(*endPoint);
if (activeConnection.IsNull())
{
err = GetPeerAddress(*endPoint, addr);
}
else
{
addr = activeConnection->mPeerAddr;
}
if (err == CHIP_NO_ERROR)
{
addr.ToString(addrStr);
}
if (conErr != CHIP_NO_ERROR || err != CHIP_NO_ERROR)
{
auto failure = (conErr != CHIP_NO_ERROR) ? conErr : err;
if (!activeConnection.IsNull())
{
tcp->CloseConnectionInternal(*activeConnection, failure, SuppressCallback::No);
}
ChipLogError(Inet, "Connection establishment with %s encountered an error: %" CHIP_ERROR_FORMAT, addrStr, failure.Format());
return;
}
// Set the Data received handler when connection completes
endPoint->OnDataReceived = HandleTCPEndPointDataReceived;
endPoint->OnDataSent = nullptr;
endPoint->OnConnectionClosed = HandleTCPEndPointConnectionClosed;
VerifyOrDie(!activeConnection.IsNull());
// Set to Connected state
activeConnection->mConnectionState = TCPState::kConnected;
// Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true.
// This is to expedite transmission of payload data and not rely on the
// network stack's configuration of collating enough data in the TCP
// window to begin transmission.
err = endPoint->EnableNoDelay();
if (err != CHIP_NO_ERROR)
{
tcp->CloseConnectionInternal(*activeConnection, err, SuppressCallback::No);
return;
}
// Send any pending packets that are queued for this connection
tcp->mPendingPackets.ForEachActiveObject([&](PendingPacket * pending) {
if (pending->mPeerAddress == addr)
{
foundPendingPacket = true;
System::PacketBufferHandle buffer = std::move(pending->mPacketBuffer);
tcp->mPendingPackets.ReleaseObject(pending);
if ((conErr == CHIP_NO_ERROR) && (err == CHIP_NO_ERROR))
{
// TODO(gmarcosb): These errors are just swallowed; caller unaware their message is just dropped?
// Likely just falls through to a timeout instead of fail-fast
err = endPoint->Send(std::move(buffer));
}
}
return Loop::Continue;
});
// Set the TCPKeepalive configurations on the established connection
endPoint->EnableKeepAlive(activeConnection->mTCPKeepAliveIntervalSecs, activeConnection->mTCPMaxNumKeepAliveProbes);
ChipLogProgress(Inet, "Connection established successfully with %s.", addrStr);
// Let higher layer/delegate know that connection is successfully
// established
tcp->HandleConnectionAttemptComplete(activeConnection, CHIP_NO_ERROR);
}
void TCPBase::HandleTCPEndPointConnectionClosed(Inet::TCPEndPoint & endPoint, CHIP_ERROR err)
{
TCPBase * tcp = reinterpret_cast<TCPBase *>(endPoint.mAppState);
ActiveTCPConnectionHandle activeConnection = tcp->FindInUseConnection(endPoint);
if (activeConnection.IsNull())
{
return;
}
if (err == CHIP_NO_ERROR && activeConnection->IsConnected())
{
err = CHIP_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY;
}
tcp->CloseConnectionInternal(*activeConnection, err, SuppressCallback::No);
}
// Handler for incoming connection requests from peer nodes
void TCPBase::HandleIncomingConnection(const Inet::TCPEndPointHandle & listenEndPoint, const Inet::TCPEndPointHandle & endPoint,
const Inet::IPAddress & peerAddress, uint16_t peerPort)
{
TCPBase * tcp = reinterpret_cast<TCPBase *>(listenEndPoint->mAppState);
// To handle errors around endpoint in the calls to HandleAcceptError, we must initialize mAppState
endPoint->mAppState = tcp;
PeerAddress addr;
CHIP_ERROR err = GetPeerAddress(*endPoint, addr);
VerifyOrReturn(err == CHIP_NO_ERROR, HandleAcceptError(endPoint, err));
ActiveTCPConnectionState * activeConnection = tcp->AllocateConnection(endPoint, addr);
if (activeConnection != nullptr)
{
auto connectionCleanup = ScopeExit([&]() { activeConnection->Free(); });
endPoint->OnDataReceived = HandleTCPEndPointDataReceived;
endPoint->OnDataSent = nullptr;
endPoint->OnConnectionClosed = HandleTCPEndPointConnectionClosed;
// By default, disable TCP Nagle buffering by setting TCP_NODELAY socket option to true
endPoint->EnableNoDelay();
tcp->mUsedEndPointCount++;
activeConnection->mConnectionState = TCPState::kConnected;
// Set the TCPKeepalive configurations on the received connection
endPoint->EnableKeepAlive(activeConnection->mTCPKeepAliveIntervalSecs, activeConnection->mTCPMaxNumKeepAliveProbes);
char addrStr[Transport::PeerAddress::kMaxToStringSize];
peerAddress.ToString(addrStr);
ChipLogProgress(Inet, "Incoming connection established with peer at %s.", addrStr);
// Call the upper layer handler for incoming connection received.
tcp->HandleConnectionReceived(*activeConnection);
connectionCleanup.release();
}
else
{
ChipLogError(Inet, "Insufficient connection space to accept new connections.");
HandleAcceptError(endPoint, CHIP_ERROR_TOO_MANY_CONNECTIONS);
}
}
void TCPBase::HandleAcceptError(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR err)
{
ChipLogError(Inet, "Accept error: %" CHIP_ERROR_FORMAT, err.Format());
TCPBase * tcp = reinterpret_cast<TCPBase *>(endPoint->mAppState);
ActiveTCPConnectionState * conn = tcp->FindActiveConnection(endPoint);
if (conn != nullptr)
{
tcp->CloseConnectionInternal(*conn, err, SuppressCallback::No);
}
}
CHIP_ERROR TCPBase::TCPConnect(const PeerAddress & address, Transport::AppTCPConnectionCallbackCtxt * appState,
ActiveTCPConnectionHandle & outPeerConnState)
{
VerifyOrReturnError(mState == TCPState::kInitialized, CHIP_ERROR_INCORRECT_STATE);
// Verify that PeerAddress AddressType is TCP
VerifyOrReturnError(address.GetTransportType() == Transport::Type::kTcp, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(mUsedEndPointCount < mActiveConnectionsSize, CHIP_ERROR_NO_MEMORY);
char addrStr[Transport::PeerAddress::kMaxToStringSize];
address.ToString(addrStr);
ChipLogProgress(Inet, "Connecting to peer %s.", addrStr);
ReturnErrorOnFailure(StartConnect(address, appState, outPeerConnState));
return CHIP_NO_ERROR;
}
void TCPBase::TCPDisconnect(ActiveTCPConnectionState & conn, bool shouldAbort)
{
// If there are still active references, we need to notify them of connection closure
SuppressCallback suppressCallback = (conn.GetReferenceCount() > 0) ? SuppressCallback::No : SuppressCallback::Yes;
// This call should be able to disconnect the connection either when it is
// already established, or when it is being set up.
if ((conn.IsConnected() && shouldAbort) || conn.IsConnecting())
{
CloseConnectionInternal(conn, CHIP_ERROR_CONNECTION_ABORTED, suppressCallback);
}
if (conn.IsConnected() && !shouldAbort)
{
CloseConnectionInternal(conn, CHIP_NO_ERROR, suppressCallback);
}
}
bool TCPBase::HasActiveConnections() const
{
for (size_t i = 0; i < mActiveConnectionsSize; i++)
{
if (mActiveConnections[i].IsConnected())
{
return true;
}
}
return false;
}
void TCPBase::InitEndpoint(const Inet::TCPEndPointHandle & endpoint)
{
endpoint->mAppState = reinterpret_cast<void *>(this);
endpoint->OnConnectComplete = HandleTCPEndPointConnectComplete;
endpoint->SetConnectTimeout(mConnectTimeout);
}
CHIP_ERROR TCPBase::TryResetConnection(ActiveTCPConnectionState & connection)
{
Inet::TCPEndPointHandle endpoint;
ReturnErrorOnFailure(mListenSocket->GetEndPointManager().NewEndPoint(endpoint));
InitEndpoint(endpoint);
PeerAddress & addr = connection.mPeerAddr;
ReturnErrorOnFailure(endpoint->Connect(addr.GetIPAddress(), addr.GetPort(), addr.GetInterface()));
connection.mConnectionState = TCPState::kConnecting;
connection.mEndPoint = endpoint;
return CHIP_NO_ERROR;
}
} // namespace Transport
} // namespace chip