blob: be05b1c78514189f32a21116aa87b77a2525550b [file] [log] [blame]
/*
*
* 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 WeaveConnection class. It manages
* communication over a TCP connection between Weave nodes.
*
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <inttypes.h>
#include <Weave/Core/WeaveCore.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Support/CodeUtils.h>
#include <SystemLayer/SystemStats.h>
#include <SystemLayer/SystemTimer.h>
#include <InetLayer/InetLayer.h>
#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
#define CONN_REPAIR_APP_CALLBACK_DELAY_MSECS (1)
#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
namespace nl {
namespace Weave {
/**
* Reserve a reference to the WeaveConnection object.
*
* The AddRef() method increments the reference count associated with the WeaveConnection object. For every
* call to AddRef(), the application is responsible for making a corresponding call to either Release(), Close()
* or Abort().
*/
void WeaveConnection::AddRef()
{
VerifyOrDie(mRefCount < UINT8_MAX);
++mRefCount;
}
/**
* Decrement the reference count on the WeaveConnection object.
*
* The Release() method decrements the reference count associated with the WeaveConnection object. If
* this results in the reference count reaching zero, the connection is closed and the connection object
* is freed. When this happens, the application must have no further interactions with the object.
*/
void WeaveConnection::Release()
{
// If the only reference that will remain after this call is the one that was automatically added
// when the connection started, close the connection.
if (mRefCount == 2 && State != kState_ReadyToConnect && State != kState_Closed)
{
// Suppress callbacks.
OnConnectionComplete = NULL;
OnConnectionClosed = NULL;
// Perform a graceful close.
DoClose(WEAVE_NO_ERROR, kDoCloseFlag_SuppressCallback);
}
VerifyOrDie(mRefCount != 0);
mRefCount--;
}
WEAVE_ERROR WeaveConnection::StartConnectToAddressLiteral(const char *peerAddr, size_t peerAddrLen)
{
WEAVE_ERROR err = WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE;
#if WEAVE_CONFIG_RESOLVE_IPADDR_LITERAL
// Literal address conversion is only supported on BSD sockets network targets
if (IPAddress::FromString(peerAddr, peerAddrLen, PeerAddr))
err = StartConnect();
#endif // WEAVE_CONFIG_RESOLVE_IPADDR_LITERAL
return err;
}
/**
* Connect to a Weave node using a fabric IP address derived from the specified node identifier.
*
* @param[in] peerNodeId The node identifier of the peer.
*
* @retval #WEAVE_NO_ERROR on successful initiation of the connection to the peer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection state is incorrect.
*
* @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE if the requested authentication mode is not supported.
*
* @retval #WEAVE_ERROR_INVALID_ADDRESS if the destination address cannot be deduced from the
* node id.
*
* @retval other Inet layer errors generated by the TCPEndPoint connect operations.
*
*/
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId)
{
return Connect(peerNodeId, IPAddress::Any);
}
/**
* Connect to a Weave node using a node identifier and/or an IP address.
*
* @note
* If peerAddr is IPAddress::Any, the node's fabric IP address will be derived from the peerNodeId field.
* If peerNodeId is kNodeIdNotSpecified (0), the node's identifier will be derived (if possible)
* from the peerAddr. The connection will be made to the specified port, or the default
* #WEAVE_PORT if peerPort is 0.
*
* @param[in] peerNodeId The node identifier of the peer, kNodeIdNotSpecified or 0 if
* not known.
*
* @param[in] peerAddr The IP address of the peer, IPAddress::Any if not known.
*
* @param[in] peerPort The optional port of the peer, default to #WEAVE_PORT.
*
* @retval #WEAVE_NO_ERROR on successful initiation of the connection to the peer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection state is incorrect.
*
* @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE if the requested authentication mode is not supported.
*
* @retval #WEAVE_ERROR_INVALID_ADDRESS if the destination address cannot be deduced from
* the node id.
*
* @retval other Inet layer errors generated by the TCPEndPoint connect operations.
*
*/
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, const IPAddress &peerAddr, uint16_t peerPort)
{
return Connect(peerNodeId, kWeaveAuthMode_Unauthenticated, peerAddr, peerPort, INET_NULL_INTERFACEID);
}
/**
* Connect to a Weave node using a node identifier and/or an IP address on a specific interface.
*
* @note
* If peerAddr is IPAddress::Any, the node's fabric IP address will be derived from the peerNodeId field.
* If peerNodeId is kNodeIdNotSpecified (0), the node's identifier will be derived (if possible)
* from the peerAddr. The connection will be made to the specified port, or the default
* #WEAVE_PORT if peerPort is 0.
*
* @param[in] peerNodeId The node identifier of the peer, kNodeIdNotSpecified or 0 if
* not known.
*
* @param[in] authMode The desired authenticate mode for the peer. Only CASE, PASE and Unauthenticated
* modes are supported.
*
* @param[in] peerAddr The IP address of the peer, IPAddress::Any if not known.
*
* @param[in] peerPort The optional port of the peer, default to #WEAVE_PORT.
*
* @param[in] intf The optional interface to use to connect to the peer node,
* default to #INET_NULL_INTERFACEID.
*
* @retval #WEAVE_NO_ERROR on successful initiation of the connection to the peer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection state is incorrect.
*
* @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE if the requested authentication mode is not supported.
*
* @retval #WEAVE_ERROR_INVALID_ADDRESS if the destination address cannot be deduced from the
* node identifier.
*
* @retval other Inet layer errors generated by the TCPEndPoint connect operations.
*
*/
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, const IPAddress &peerAddr, uint16_t peerPort, InterfaceId intf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(State == kState_ReadyToConnect, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || IsCASEAuthMode(authMode) || IsPASEAuthMode(authMode), err = WEAVE_ERROR_INVALID_ARGUMENT);
// Can't request authentication if the security manager is not initialized.
VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || MessageLayer->SecurityMgr != NULL, err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE);
// Application has made this an IP-based WeaveConnection.
NetworkType = kNetworkType_IP;
PeerNodeId = peerNodeId;
PeerAddr = peerAddr;
PeerPort = (peerPort != 0) ? peerPort : WEAVE_PORT;
mTargetInterface = intf;
AuthMode = authMode;
// Bump the reference count when we start the connection process. The corresponding decrement happens when the
// DoClose() method is called. This ensures the object stays live while there's the possibility of a callback
// happening from an underlying layer (e.g. TCPEndPoint or DNS resolution).
mRefCount++;
WeaveLogProgress(MessageLayer, "Con start %04X %016llX %04X", LogId(), peerNodeId, authMode);
err = StartConnect();
SuccessOrExit(err);
exit:
return err;
}
/**
* Connect to a Weave node using a node identifier and/or a string host name. If supplied, peerAddr can
* be any of:
* @code
* <host-name>
* <host-name>:<port>
* <ip-4-addr>
* <ip-4-addr>:<port>
* <ip-6-addr>
* [<ip-6-addr>]:<port>
* @endcode
* @note
* If `<port>` is not supplied, defaultPort is used. If defaultPort is 0, the default #WEAVE_PORT is used.
* If peerAddr is null, the node's fabric IP address will be derived from the peerNodeId field. If peerNodeId
* is kNodeIdNotSpecified (0), the node's identifier will be derived (if possible) from the peer's IP address.
*
* @param[in] peerNodeId The node identifier of the peer, kNodeIdNotSpecified or 0 if
* not known.
*
* @param[in] authMode The desired authenticate mode for the peer. Only CASE, PASE and Unauthenticated
* modes are supported.
*
* @param[in] peerAddr The address or hostname of the peer as a NULL-terminated C string.
*
* @param[in] defaultPort The optional default port to use for the connection if not supplied in the peerAddr
* string.
*
* @retval #WEAVE_NO_ERROR on successful initiation of the connection to the peer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection state is incorrect.
*
* @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE if the requested authentication mode is not supported.
*
* @retval #WEAVE_ERROR_INVALID_ADDRESS if the destination address cannot be deduced from the node id.
*
* @retval other Inet layer errors generated by the TCPEndPoint connect operations.
*
*/
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, const char *peerAddr, uint16_t defaultPort)
{
return Connect(peerNodeId, authMode, peerAddr, (peerAddr != NULL) ? strlen(peerAddr) : 0, defaultPort);
}
/**
* Connect to a Weave node using a node identifier and/or a string peer address.
*
* If supplied, peerAddr can be any of:
* @code
* <host-name>
* <host-name>:<port>
* <ip-4-addr>
* <ip-4-addr>:<port>
* <ip-6-addr>
* [<ip-6-addr>]:<port>
* @endcode
* @note
* If `<port>` is not supplied, defaultPort is used. If defaultPort is 0, the default Weave port is used.
* If peerAddr is null, the node's fabric IP address will be derived from the peerNodeId field. If peerNodeId
* is kNodeIdNotSpecified (0), the node's identifier will be derived (if possible) from the peer's IP address.
*
* @param[in] peerNodeId The node identifier of the peer, kNodeIdNotSpecified or 0 if
* not known.
*
* @param[in] authMode The desired authenticate mode for the peer. Only CASE, PASE and Unauthenticated
* modes are supported.
*
* @param[in] peerAddr The address or hostname of the peer as a non-NULL-terminated C string.
*
* @param[in] peerAddrLen The length of the peerAddr string.
*
* @param[in] defaultPort The optional default port to use for the connection if not supplied in the peerAddr
* string.
*
* @retval #WEAVE_NO_ERROR on successful initiation of the connection to the peer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection state is incorrect.
*
* @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE if the requested authentication mode is not supported.
*
* @retval #WEAVE_ERROR_INVALID_ADDRESS if the destination address cannot be deduced from the node id.
*
* @retval other Inet layer errors generated by the TCPEndPoint connect operations.
*
*/
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, const char *peerAddr,
uint16_t peerAddrLen, uint16_t defaultPort)
{
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
const uint8_t dnsOptions = ::nl::Inet::kDNSOption_Default;
#else
const uint8_t dnsOptions = 0;
#endif
return Connect(peerNodeId, authMode, peerAddr, peerAddrLen, dnsOptions, defaultPort);
}
/**
* Connect to a Weave node using a node identifier and/or a string peer address.
*
* If supplied, peerAddr can be any of:
* @code
* <host-name>
* <host-name>:<port>
* <ip-4-addr>
* <ip-4-addr>:<port>
* <ip-6-addr>
* [<ip-6-addr>]:<port>
* @endcode
* @note
* If `<port>` is not supplied, defaultPort is used. If defaultPort is 0, the default Weave port is used.
* If peerAddr is null, the node's fabric IP address will be derived from the peerNodeId field. If peerNodeId
* is kNodeIdNotSpecified (0), the node's identifier will be derived (if possible) from the peer's IP address.
*
* @param[in] peerNodeId The node identifier of the peer, kNodeIdNotSpecified or 0 if
* not known.
*
* @param[in] authMode The desired authenticate mode for the peer. Only CASE, PASE and Unauthenticated
* modes are supported.
*
* @param[in] peerAddr The address or hostname of the peer as a non-NULL-terminated C string.
*
* @param[in] peerAddrLen The length of the peerAddr string.
*
* @param[in] dnsOptions An integer value controlling how host name resolution is performed.
* Value should be the OR of one or more values from from the
* #::nl::Inet::DNSOptions enumeration.
*
* @param[in] defaultPort The optional default port to use for the connection if not supplied in the peerAddr
* string.
*
* @retval #WEAVE_NO_ERROR on successful initiation of the connection to the peer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection state is incorrect.
*
* @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE if the requested authentication mode is not supported.
*
* @retval #WEAVE_ERROR_INVALID_ADDRESS if the destination address cannot be deduced from the node id.
*
* @retval other Inet layer errors generated by the TCPEndPoint connect operations.
*
*/
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, const char *peerAddr,
uint16_t peerAddrLen, uint8_t dnsOptions, uint16_t defaultPort)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
const char *hostName;
uint16_t hostNameLen;
const char *intfName;
uint16_t intfNameLen;
VerifyOrExit(State == kState_ReadyToConnect, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || IsCASEAuthMode(authMode) || IsPASEAuthMode(authMode), err = WEAVE_ERROR_INVALID_ARGUMENT);
// Can't request authentication if the security manager is not initialized.
VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || MessageLayer->SecurityMgr != NULL, err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE);
// If no peer address given, connect using the node id.
if (peerAddr == NULL || peerAddrLen == 0)
{
err = Connect(peerNodeId, authMode, IPAddress::Any, defaultPort);
ExitNow();
}
// Application has made this an IP-based WeaveConnection.
NetworkType = kNetworkType_IP;
// Parse the address into a host, port and interface name.
err = nl::Inet::ParseHostPortAndInterface(peerAddr, peerAddrLen, hostName, hostNameLen, PeerPort, intfName, intfNameLen);
SuccessOrExit(err);
if (PeerPort == 0)
PeerPort = (defaultPort != 0) ? defaultPort : WEAVE_PORT;
// If an interface name has been specified, attempt to convert it to a network interface id.
if (intfName != NULL)
{
err = InterfaceNameToId(intfName, mTargetInterface);
SuccessOrExit(err);
}
// Clear the list of resolved peer addresses in preparation for resolving the host name.
memset(mPeerAddrs, 0, sizeof(mPeerAddrs));
PeerNodeId = peerNodeId;
AuthMode = authMode;
// Bump the reference count when we start the connection process. The corresponding decrement happens when the
// DoClose() method is called. This ensures the object stays live while there's the possibility of a callback
// happening from an underlying layer (e.g. TCPEndPoint or DNS resolution).
mRefCount++;
WeaveLogProgress(MessageLayer, "Con start %04X %016llX %04X", LogId(), peerNodeId, authMode);
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
// Initiate the host name resolution.
State = kState_Resolving;
err = MessageLayer->Inet->ResolveHostAddress(hostName, hostNameLen, dnsOptions, WEAVE_CONFIG_CONNECT_IP_ADDRS, mPeerAddrs, HandleResolveComplete, this);
#else // !WEAVE_CONFIG_ENABLE_DNS_RESOLVER
err = StartConnectToAddressLiteral(hostName, hostNameLen);
#endif // !WEAVE_CONFIG_ENABLE_DNS_RESOLVER
exit:
return err;
}
/**
* Connect to a Weave node using a node identifier and/or a list of hostname and ports.
*
* @param[in] peerNodeId The node identifier of the peer.
*
* @param[in] authMode The authentication mode used for the connection.
*
* @param[in] hostPortList The list of hostnames and ports.
*
* @param[in] intf The optional interface to use to connect to the peer node,
* default to #INET_NULL_INTERFACEID.
*
* @retval #WEAVE_NO_ERROR on successful initiation of the connection to the peer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection state is incorrect.
*
* @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE if the requested authentication mode is not supported.
*
* @retval #WEAVE_ERROR_INVALID_ADDRESS if the destination address cannot be deduced from the node id.
*
* @retval other Inet layer errors generated by the TCPEndPoint connect operations.
*
*/
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, HostPortList hostPortList,
InterfaceId intf)
{
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
const uint8_t dnsOptions = ::nl::Inet::kDNSOption_Default;
#else
const uint8_t dnsOptions = 0;
#endif
return Connect(peerNodeId, authMode, hostPortList, dnsOptions, intf);
}
/**
* Connect to a Weave node using a node identifier and/or a list of hostname and ports.
*
* @param[in] peerNodeId The node identifier of the peer.
*
* @param[in] authMode The authentication mode used for the connection.
*
* @param[in] hostPortList The list of hostnames and ports.
*
* @param[in] dnsOptions An integer value controlling how host name resolution is performed.
* Value should be the OR of one or more values from from the
* #::nl::Inet::DNSOptions enumeration.
*
* @param[in] intf The optional interface to use to connect to the peer node,
* default to #INET_NULL_INTERFACEID.
*
* @retval #WEAVE_NO_ERROR on successful initiation of the connection to the peer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection state is incorrect.
*
* @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE if the requested authentication mode is not supported.
*
* @retval #WEAVE_ERROR_INVALID_ADDRESS if the destination address cannot be deduced from the node id.
*
* @retval other Inet layer errors generated by the TCPEndPoint connect operations.
*
*/
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, HostPortList hostPortList,
uint8_t dnsOptions, InterfaceId intf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(State == kState_ReadyToConnect, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || IsCASEAuthMode(authMode) || IsPASEAuthMode(authMode), err = WEAVE_ERROR_INVALID_ARGUMENT);
// Can't request authentication if the security manager is not initialized.
VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || MessageLayer->SecurityMgr != NULL, err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE);
// Application has made this an IP-based WeaveConnection.
NetworkType = kNetworkType_IP;
// Clear the list of resolved peer addresses in preparation for resolving the first host name.
memset(mPeerAddrs, 0, sizeof(mPeerAddrs));
PeerNodeId = peerNodeId;
AuthMode = authMode;
mPeerHostPortList = hostPortList;
mTargetInterface = intf;
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
mDNSOptions = dnsOptions;
#endif
// Bump the reference count when we start the connection process. The corresponding decrement happens when the
// DoClose() method is called. This ensures the object stays live while there's the possibility of a callback
// happening from an underlying layer (e.g. TCPEndPoint or DNS resolution).
mRefCount++;
WeaveLogProgress(MessageLayer, "Con start %04X %016llX %04X", LogId(), peerNodeId, authMode);
err = TryNextPeerAddress(WEAVE_ERROR_HOST_PORT_LIST_EMPTY);
SuccessOrExit(err);
exit:
return err;
}
/**
* @brief Set timeout for Connect to succeed or return an error.
*
* @param[in] connTimeoutMsecs
*
* @note
* Setting a value of zero means use system defaults.
*/
void WeaveConnection::SetConnectTimeout(const uint32_t connTimeoutMsecs)
{
mConnectTimeout = connTimeoutMsecs;
}
/**
* Get the IP address information of the peer.
*
* @param[out] addrInfo A reference to the IPPacketInfo object.
*
* @retval #WEAVE_NO_ERROR On success.
* @retval #WEAVE_ERROR_NOT_IMPLEMENTED If this function is invoked for an incompatible endpoint
* (e.g., BLE) in the network layer.
*
*/
WEAVE_ERROR WeaveConnection::GetPeerAddressInfo(IPPacketInfo& addrInfo)
{
#if CONFIG_NETWORK_LAYER_BLE
if (mBleEndPoint != NULL)
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif
addrInfo.Clear();
addrInfo.SrcAddress = PeerAddr;
addrInfo.SrcPort = PeerPort;
return WEAVE_NO_ERROR;
}
/**
* Constructs a string describing the peer node associated with the connection.
*
* @param[in] buf A pointer to a buffer into which the string should be written. The supplied
* buffer should be at least as big as kGetPeerDescription_MaxLength. If a
* smaller buffer is given the string will be truncated to fit. The output
* will include a NUL termination character in all cases.
* @param[in] bufSize The size of the buffer pointed at by buf.
*/
void WeaveConnection::GetPeerDescription(char * buf, size_t bufSize) const
{
WeaveMessageLayer::GetPeerDescription(buf, bufSize, PeerNodeId,
(NetworkType == kNetworkType_IP) ? &PeerAddr : NULL,
(NetworkType == kNetworkType_IP) ? PeerPort : 0,
INET_NULL_INTERFACEID,
this);
}
#if WEAVE_CONFIG_ENABLE_TUNNELING
/**
* Send a tunneled Weave message over an established connection.
*
* @param[in] msgInfo A pointer to a WeaveMessageInfo object.
*
* @param[in] msgBuf A pointer to the PacketBuffer object holding the packet to send.
*
* @retval #WEAVE_NO_ERROR on successfully sending the message down to
* the network layer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection object is not
* in the correct state for sending messages.
* @retval #WEAVE_ERROR_INVALID_DESTINATION_NODE_ID if the destination node identifier is unspecified.
* @retval #WEAVE_ERROR_SENDING_BLOCKED if the message is too long to be sent.
* @retval other Inet layer errors related to the specific endpoint send operations.
*
*/
WEAVE_ERROR WeaveConnection::SendTunneledMessage (WeaveMessageInfo *msgInfo, PacketBuffer *msgBuf)
{
//Set message version to V2
msgInfo->MessageVersion = kWeaveMessageVersion_V2;
//Set the tunneling flag
msgInfo->Flags |= kWeaveMessageFlag_TunneledData;
return SendMessage(msgInfo, msgBuf);
}
#endif // WEAVE_CONFIG_ENABLE_TUNNELING
/**
* Send a Weave message over an established connection.
*
* @param[in] msgInfo A pointer to a WeaveMessageInfo object.
*
* @param[in] msgBuf A pointer to the PacketBuffer object holding the packet to send.
*
* @retval #WEAVE_NO_ERROR on successfully sending the message down to
* the network layer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection object is not
* in the correct state for sending messages.
* @retval #WEAVE_ERROR_INVALID_DESTINATION_NODE_ID if the destination node identifier is unspecified.
* @retval #WEAVE_ERROR_SENDING_BLOCKED if the message is too long to be sent.
* @retval other Inet layer errors related to the specific endpoint send operations.
*
*/
WEAVE_ERROR WeaveConnection::SendMessage (WeaveMessageInfo *msgInfo, PacketBuffer *msgBuf)
{
WEAVE_ERROR res = WEAVE_NO_ERROR;
VerifyOrDie(mRefCount != 0);
if (!StateAllowsSend())
{
ExitNow(res = WEAVE_ERROR_INCORRECT_STATE);
}
// TODO: implement back pressure
// Set the source node identifier in the message header.
msgInfo->SourceNodeId = MessageLayer->FabricState->LocalNodeId;
// If necessary, arrange for the source node identifier field to be encoded in the message.
if (SendSourceNodeId)
{
msgInfo->Flags |= kWeaveMessageFlag_SourceNodeId;
}
// If the caller didn't supply a destination node id, use the peer's node id.
if ((msgInfo->Flags & kWeaveMessageFlag_DestNodeId) == 0 && msgInfo->DestNodeId == kNodeIdNotSpecified)
{
msgInfo->DestNodeId = PeerNodeId;
}
// Fail immediately if the caller didn't provide a valid destination node id.
if (msgInfo->DestNodeId == kNodeIdNotSpecified)
{
ExitNow(res = WEAVE_ERROR_INVALID_DESTINATION_NODE_ID);
}
// If we determined when the connection was established that the destination node identifier should always be sent,
// OR if the destination node identifier for the current message does not match the peer node id, then force the
// destination node identifier field to be encoded in the message.
if (SendDestNodeId || msgInfo->DestNodeId != PeerNodeId)
{
msgInfo->Flags |= kWeaveMessageFlag_DestNodeId;
}
// Encode the Weave message. NOTE that this results in the payload buffer containing the entire encoded message.
// If the encoded message would have exceeded the sent limit, return WEAVE_ERROR_SENDING_BLOCKED to the caller.
res = MessageLayer->EncodeMessageWithLength(msgInfo, msgBuf, this, UINT16_MAX);
if (res != WEAVE_NO_ERROR)
{
ExitNow(res = (res == WEAVE_ERROR_MESSAGE_TOO_LONG) ? WEAVE_ERROR_SENDING_BLOCKED : res);
}
// Copy msg to a right-sized buffer if applicable
msgBuf = PacketBuffer::RightSize(msgBuf);
#if CONFIG_NETWORK_LAYER_BLE
if (mBleEndPoint != NULL)
{
res = mBleEndPoint->Send(msgBuf);
}
else
#endif
{
res = mTcpEndPoint->Send(msgBuf, true);
}
msgBuf = NULL;
exit:
if (msgBuf != NULL)
{
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
}
return res;
}
/**
* Performs a graceful TCP send-shutdown, ensuring all outgoing data has been sent and received
* by the peer's TCP stack. With most (but not all) TCP implementations, receipt of a send-shutdown
* will cause the remote host to shutdown their side of the connection as well, resulting in a
* connection close. A subsequent call to Close() would terminate the WeaveConnection.
*
* @note
* This method is not available for BLE WeaveConnections. There is no Weave over BLE protocol
* mechanism to perform a send-shutdown. Application protocols/profiles must perform their own
* acknowledgements of message receipt e.g. via Status Report messages before they close a
* WeaveConnection. This is good practice with TCP-based WeaveConnections as well.
*
* @sa Close() and Abort().
*
* @retval #WEAVE_NO_ERROR on successful shutdown of the tcp connection.
* @retval #WEAVE_ERROR_NOT_IMPLEMENTED if this function is invoked for an incompatible
* endpoint (e.g., BLE) in the network layer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection object is not
* in the correct state before initiating
* a shutdown.
* @retval other Inet layer errors related to the specific endpoint shutdown operations.
*
*/
WEAVE_ERROR WeaveConnection::Shutdown()
{
#if CONFIG_NETWORK_LAYER_BLE
if (mBleEndPoint != NULL)
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif
VerifyOrDie(mRefCount != 0);
if (State != kState_Connected && State != kState_SendShutdown && State != kState_Closed)
return WEAVE_ERROR_INCORRECT_STATE;
if (State == kState_Connected)
{
State = kState_SendShutdown;
mTcpEndPoint->Shutdown();
}
return WEAVE_NO_ERROR;
}
/**
* Performs a non-blocking graceful close of the TCP- or BLE-based WeaveConnection, delivering any
* remaining outgoing data before politely informing the remote host that we have reset the connection.
*
* This method provides no strong guarantee that any outgoing message not acknowledged at the application
* protocol level has been received by the remote peer. For both TCP and BLE, the underlying protocol stack
* will make a best-effort to deliver any pending outgoing data before resetting the connection. For TCP,
* Shutdown() should be used before Close() if a transport-layer message receipt confirmation is required
* before closing the connection. BLE connections provide no Shutdown() equivalent.
*
* For BLE-based connections, Close() closes the WeaveConnection and returns immediately, but may cause the
* underlying BLEEndPoint object to linger until all outgoing data has been sent. This is a side effect of
* the Weave over BLE transport protocol implementation existing within the Weave BleLayer.
*
* Once Close() has been called, the WeaveConnection object can no longer be used for further communication.
*
* Calling Close() decrements the reference count associated with the WeaveConnection object, whether or not
* the connection is open/active at the time the method is called. If this results in the reference count
* reaching zero, the resources associated with the connection object are freed. When this happens, the
* application must have no further interactions with the object.
*
* @sa Shutdown(), Abort(), AddRef() and Release().
*
* @return #WEAVE_NO_ERROR unconditionally.
*
*/
WEAVE_ERROR WeaveConnection::Close()
{
return Close(false);
}
/**
* Performs a non-blocking graceful close of the TCP- or BLE-based WeaveConnection, delivering any
* remaining outgoing data before politely informing the remote host that we have reset the connection.
*
* This method provides no strong guarantee that any outgoing message not acknowledged at the application
* protocol level has been received by the remote peer. For both TCP and BLE, the underlying protocol stack
* will make a best-effort to deliver any pending outgoing data before resetting the connection. For TCP,
* Shutdown() should be used before Close() if a transport-layer message receipt confirmation is required
* before closing the connection. BLE connections provide no Shutdown() equivalent.
*
* For BLE-based connections, Close() closes the WeaveConnection and returns immediately, but may cause the
* underlying BLEEndPoint object to linger until all outgoing data has been sent. This is a side effect of
* the Weave over BLE transport protocol implementation existing within the Weave BleLayer.
*
* Once Close() has been called, the WeaveConnection object can no longer be used for further communication.
*
* Calling Close() decrements the reference count associated with the WeaveConnection object, whether or not
* the connection is open/active at the time the method is called. If this results in the reference count
* reaching zero, the resources associated with the connection object are freed. When this happens, the
* application must have no further interactions with the object.
*
* @param[in] suppressCloseLog true if logs need to be suppressed, false otherwise.
*
* @sa Shutdown(), Abort(), AddRef() and Release().
*
* @return #WEAVE_NO_ERROR unconditionally.
*
*/
WEAVE_ERROR WeaveConnection::Close(bool suppressCloseLog)
{
// Suppress callbacks.
OnConnectionComplete = NULL;
OnConnectionClosed = NULL;
// Perform a graceful close.
DoClose(WEAVE_NO_ERROR, kDoCloseFlag_SuppressCallback | (suppressCloseLog ? kDoCloseFlag_SuppressLogging : 0));
// Decrement the ref count that was added when the WeaveConnection object
// was allocated (in WeaveMessageLayer::NewConnection()).
VerifyOrDie(mRefCount != 0);
mRefCount--;
return WEAVE_NO_ERROR;
}
/**
* Performs an un-graceful close of the TCP- or BLE-based WeaveConnection, discarding any data that might be
* in flight to or from the peer.
*
* A call to Abort() immediately terminates the underlying connection. After this point, the WeaveConnection
* object can no longer be used for further communication.
*
* Calling Abort() decrements the reference count associated with the WeaveConnection object, whether or not
* the connection is open/active at the time the method is called. If this results in the reference count
* reaching zero, the resources associated with the connection object are freed. When this happens, the
* application must have no further interactions with the object.
*
* @sa Shutdown(), Abort(), AddRef() and Release().
*
*/
void WeaveConnection::Abort()
{
// Suppress callbacks.
OnConnectionComplete = NULL;
OnConnectionClosed = NULL;
// Perform an abortive close of the connection.
DoClose(WEAVE_ERROR_CONNECTION_ABORTED, kDoCloseFlag_SuppressCallback);
// Decrement the ref count that was added when the WeaveConnection object
// was allocated (in WeaveMessageLayer::NewConnection()).
VerifyOrDie(mRefCount != 0);
mRefCount--;
}
/**
* Enable receiving over this WeaveConnection. This method is used by the
* application to indicate to the WeaveConnection object that it is ready
* to receive any data that arrives over the TCP connection.
*
* @sa DisableReceive()
*
*/
void WeaveConnection::EnableReceive()
{
// If receiving was disabled, enable it and process any pending received data.
if (!ReceiveEnabled)
{
ReceiveEnabled = true;
//ProcessReceivedData();
// TODO: fix this
}
}
/**
* Disable receiving over this WeaveConnection. This method is used by the application
* to indicate that it is not ready to receive any arrived data over the TCP connection.
* In order to re-enable receiving, the application needs to call EnableReceive() to
* allow WeaveConnection to hand over any received data by invoking the approrpiate
* callbacks.
*
* @sa EnableReceive()
*
*/
void WeaveConnection::DisableReceive()
{
ReceiveEnabled = false;
// TODO: make this disable received in the TcpEndPoint.
}
/**
* WeaveConnection::EnableKeepAlive
*
* @brief
* Enable TCP keepalive probes on the underlying TCP connection.
*
* @param[in] interval
* The interval (in seconds) between keepalive probes. This value also controls
* the time between last data packet sent and the transmission of the first keepalive
* probe.
*
* @param[in] timeoutCount
* The maximum number of unacknowledged probes before the connection will be deemed
* to have failed.
*
* @note
* -This method can only be called on a Weave connection backed by a TCP connection.
*
* -This method can only be called when the connection is in a state that allows sending.
*
* -This method can be called multiple times to adjust the keepalive interval or timeout
* count.
*
* @retval #WEAVE_NO_ERROR on successful enabling of TCP keepalive probes
* on the connection.
* @retval #WEAVE_ERROR_NOT_IMPLEMENTED if this function is invoked for an incompatible
* endpoint (e.g., BLE) in the network layer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection object is not
* in the correct state for sending messages.
* @retval other Inet layer errors related to the TCP endpoint enable keepalive operation.
*
*/
WEAVE_ERROR WeaveConnection::EnableKeepAlive(uint16_t interval, uint16_t timeoutCount)
{
#if CONFIG_NETWORK_LAYER_BLE
if (mBleEndPoint != NULL)
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif
if (!StateAllowsSend())
return WEAVE_ERROR_INCORRECT_STATE;
return mTcpEndPoint->EnableKeepAlive(interval, timeoutCount);
}
/**
* WeaveConnection::DisableKeepAlive
*
* @brief
* Disable TCP keepalive probes on the underlying TCP connection.
*
* @note
* This method can only be called on a Weave connection backed by a TCP connection.
*
* This method can only be called when the connection is in a state that allows sending.
*
* This method does nothing if keepalives have not been enabled on the connection.
*
* @retval #WEAVE_NO_ERROR on successful disabling of TCP keepalive probes
* on the connection.
* @retval #WEAVE_ERROR_NOT_IMPLEMENTED if this function is invoked for an incompatible
* endpoint (e.g., BLE) in the network layer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection object is not
* in the correct state for sending messages.
* @retval other Inet layer errors related to the TCP endpoint enable keepalive operation.
*
*/
WEAVE_ERROR WeaveConnection::DisableKeepAlive()
{
#if CONFIG_NETWORK_LAYER_BLE
if (mBleEndPoint != NULL)
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif
if (!StateAllowsSend())
return WEAVE_ERROR_INCORRECT_STATE;
return mTcpEndPoint->DisableKeepAlive();
}
/**
* WeaveConnection::SetUserTimeout
*
* @brief
* Set the TCP user timeout socket option.
*
* @param[in] userTimeoutMillis
* Tcp user timeout value in milliseconds.
*
* @details
* When the value is greater than 0, it specifies the maximum amount of
* time in milliseconds that transmitted data may remain
* unacknowledged before TCP will forcibly close the
* corresponding connection. If the option value is specified as 0,
* TCP will use the system default.
* See RFC 5482, for further details.
*
* @note
* -This method can only be called on a Weave connection backed by a TCP connection.
*
* -This method can only be called when the connection is in a state that allows sending.
*
* -This method can be called multiple times to adjust the TCP user timeout.
*
* @retval #WEAVE_NO_ERROR on successful setting of TCP user timeout
* on the connection.
* @retval #WEAVE_ERROR_NOT_IMPLEMENTED if this function is invoked for an incompatible
* endpoint (e.g., BLE) in the network layer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection object is not
* in the correct state for sending messages.
* @retval other Inet layer errors related to the TCP endpoint setting of the TCP user timeout.
*
*/
WEAVE_ERROR WeaveConnection::SetUserTimeout(uint32_t userTimeoutMillis)
{
#if CONFIG_NETWORK_LAYER_BLE
if (mBleEndPoint != NULL)
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif
if (!StateAllowsSend())
return WEAVE_ERROR_INCORRECT_STATE;
return mTcpEndPoint->SetUserTimeout(userTimeoutMillis);
}
/**
* WeaveConnection::ResetUserTimeout
*
* @brief
* Reset the TCP user timeout socket option to the system default.
*
* @note
* -This method can only be called on a Weave connection backed by a TCP connection.
*
* -This method can only be called when the connection is in a state that allows sending.
*
* -This method does nothing if user timeout has not been set on the connection.
*
* @retval #WEAVE_NO_ERROR on successful resetting of TCP user timeout
* on the connection.
* @retval #WEAVE_ERROR_NOT_IMPLEMENTED if this function is invoked for an incompatible
* endpoint (e.g., BLE) in the network layer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection object is not
* in the correct state for sending messages.
* @retval other Inet layer errors related to the TCP endpoint resetting of the TCP user timeout.
*
*/
WEAVE_ERROR WeaveConnection::ResetUserTimeout(void)
{
return SetUserTimeout(0);
}
/**
* Set the idle timeout on the underlying network layer connection.
*
* @param[in] timeoutMS the timeout in milliseconds.
*
* @retval #WEAVE_NO_ERROR on successful setting of the idle timeout
* for the connection.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection object is not
* in the correct state for receiving messages.
*
*/
WEAVE_ERROR WeaveConnection::SetIdleTimeout(uint32_t timeoutMS)
{
if (!StateAllowsReceive())
return WEAVE_ERROR_INCORRECT_STATE;
#if CONFIG_NETWORK_LAYER_BLE
if (mBleEndPoint)
{
//TODO COM-320: implement for BLEEndPoint. Use InetTimer until we have separate PlatformLayer.
}
else
#endif
{
mTcpEndPoint->SetIdleTimeout(timeoutMS);
}
return WEAVE_NO_ERROR;
}
void WeaveConnection::DoClose(WEAVE_ERROR err, uint8_t flags)
{
if (State != kState_Closed)
{
#if CONFIG_NETWORK_LAYER_BLE
if (mBleEndPoint != NULL)
{
if (err == WEAVE_NO_ERROR)
{
mBleEndPoint->Close();
}
else
{
// Cancel pending BLEEndPoint transmission(s) if WeaveConnection has been aborted.
mBleEndPoint->Abort();
}
// BleEndPoint frees itself once it finishes the close operation. It must stick around to negotiate the
// close, potentially after it's emptied the remainder of its WoBle transmit buffer.
mBleEndPoint = NULL;
}
else
#endif
{
if (mTcpEndPoint != NULL)
{
if (err == WEAVE_NO_ERROR)
err = mTcpEndPoint->Close();
if (err != WEAVE_NO_ERROR)
mTcpEndPoint->Abort();
mTcpEndPoint->Free();
mTcpEndPoint = NULL;
}
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
// Cancel any outstanding DNS query that may still be active. (This situation can
// arise if the application initiates a connection to a peer using a DNS name and
// then aborts/closes the connection before the DNS lookup completes).
MessageLayer->Inet->CancelResolveHostAddress(HandleResolveComplete, this);
#endif // WEAVE_CONFIG_ENABLE_DNS_RESOLVER
}
uint8_t oldState = State;
State = kState_Closed;
if ((flags & kDoCloseFlag_SuppressLogging) == 0)
WeaveLogProgress(MessageLayer, "Con closed %04X %ld", LogId(), (long)err);
// If the exchange manager has been initialized, call its callback.
if (MessageLayer->ExchangeMgr != NULL)
MessageLayer->ExchangeMgr->HandleConnectionClosed(this, err);
// Call the Fabric state object to alert it of the connection close.
MessageLayer->FabricState->HandleConnectionClosed(this);
// Call the appropriate app callback if allowed.
if ((flags & kDoCloseFlag_SuppressCallback) == 0)
{
if (oldState == kState_Resolving || oldState == kState_Connecting || oldState == kState_EstablishingSession)
{
if (OnConnectionComplete != NULL)
OnConnectionComplete(this, err);
}
else if (OnConnectionClosed != NULL)
OnConnectionClosed(this, err);
}
// Decrement the ref count that was added when the connection started.
if (oldState != kState_ReadyToConnect && oldState != kState_Closed)
{
VerifyOrDie(mRefCount != 0);
mRefCount--;
}
}
}
void WeaveConnection::HandleResolveComplete(void *appState, INET_ERROR dnsRes, uint8_t addrCount, IPAddress *addrArray)
{
WeaveConnection *con = (WeaveConnection *)appState;
// It is legal for a DNS entry to exist but contain no A/AAAA records. If this happens, return a reasonable error
// to the user.
if (dnsRes == INET_NO_ERROR && addrCount == 0)
dnsRes = INET_ERROR_HOST_NOT_FOUND;
WeaveLogProgress(MessageLayer, "Con DNS complete %04X %ld", con->LogId(), (long)dnsRes);
// Attempt to connect to the first resolved address (if any).
con->TryNextPeerAddress(dnsRes);
}
WEAVE_ERROR WeaveConnection::TryNextPeerAddress(WEAVE_ERROR lastErr)
{
WEAVE_ERROR err = lastErr; // If there are no more addresses to try, lastErr will become the error returned to the user.
// Search the list of peer addresses for one we haven't tried yet...
for (int i = 0; i < WEAVE_CONFIG_CONNECT_IP_ADDRS; i++)
if (mPeerAddrs[i] != IPAddress::Any)
{
// Select the next address, removing it from the list so it won't get tried again.
PeerAddr = mPeerAddrs[i];
mPeerAddrs[i] = IPAddress::Any;
// Initiate a connection to the new address.
err = StartConnect();
ExitNow();
}
// If Connect() was called with a host/port list and there are additional entries in the list, then...
if (!mPeerHostPortList.IsEmpty())
{
char hostName[256]; // Per spec, max DNS name length is 253.
// Pop the next host/port pair from the list.
err = mPeerHostPortList.Pop(hostName, sizeof(hostName), PeerPort);
SuccessOrExit(err);
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
// Initiate name resolution for the new host name.
//
// NOTE: there was a moment, maybe it's passed now, when the
// log statement below was very useful since in showed exactly
// which host we thought we were getting a particular service
// from. for now i'm leaving it in place, protected by an
// ifdef 0 at the top of the file.
//
WeaveLogProgress(MessageLayer, "Con DNS start %04" PRIX16 " %s %02" PRIX8, LogId(), hostName, mDNSOptions);
State = kState_Resolving;
err = MessageLayer->Inet->ResolveHostAddress(hostName, strlen(hostName), mDNSOptions,
WEAVE_CONFIG_CONNECT_IP_ADDRS,
mPeerAddrs, HandleResolveComplete, this);
#else // !WEAVE_CONFIG_ENABLE_DNS_RESOLVER
err = StartConnectToAddressLiteral(hostName, strlen(hostName));
#endif // !WEAVE_CONFIG_ENABLE_DNS_RESOLVER
}
exit:
// Enter the closed state if an error occurred.
if (err != WEAVE_NO_ERROR)
DoClose(err, 0);
return err;
}
void WeaveConnection::StartSession()
{
// If the application requested authentication
if (AuthMode != kWeaveAuthMode_Unauthenticated)
{
// Enter the establishing session state.
State = kState_EstablishingSession;
WEAVE_ERROR err;
// Call the security manager to initiate secure CASE session.
if (IsCASEAuthMode(AuthMode))
err = MessageLayer->SecurityMgr->StartCASESession(this, PeerNodeId, PeerAddr, PeerPort,
AuthMode, NULL, HandleSecureSessionEstablished, HandleSecureSessionError);
// Call the security manager to initiate secure PASE session.
else if (IsPASEAuthMode(AuthMode))
err = MessageLayer->SecurityMgr->StartPASESession(this, AuthMode, NULL,
HandleSecureSessionEstablished, HandleSecureSessionError);
else
err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE;
if (err != WEAVE_NO_ERROR)
DoClose(err, 0);
}
// Otherwise...
else
{
// Enter the connected state.
State = kState_Connected;
WeaveLogProgress(MessageLayer, "Con complete %04X", LogId());
// Call the app's completion function.
if (OnConnectionComplete != NULL)
OnConnectionComplete(this, WEAVE_NO_ERROR);
}
}
#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
void WeaveConnection::AppCallbackAfterConnectionRepair(System::Layer* aSystemLayer, void* aAppState, System::Error aError)
{
WeaveConnection* con = static_cast<WeaveConnection*>(aAppState);
con->mTcpEndPoint->OnConnectComplete(con->mTcpEndPoint, INET_NO_ERROR);
}
/**
* Attempt to repair the TCP connection using a given TCP RepairInfo.
*
* @param[in] peerNodeId NodeId of the peer with which to repair TCP
* connection.
*
* @param[in] repairInfo A reference to the TCP Connection repair info.
*
* @param[in] intf InterfaceId over which the connection is
* expected to be made.
*
* @note
* The TCP connection restoration can only be attempted from the client side.
* Upon failure to restore, the client would fall back to establishing a
* negotiated TCP connection.
*
*/
WEAVE_ERROR WeaveConnection::TryConnectionRepair(uint64_t peerNodeId, const TCPConnRepairInfo &repairInfo, InterfaceId intf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Allocate a new TCP end point.
err = MessageLayer->Inet->NewTCPEndPoint(&mTcpEndPoint);
SuccessOrExit(err);
mTcpEndPoint->AppState = this;
mTcpEndPoint->OnConnectComplete = HandleConnectComplete;
mRefCount++;
State = kState_Connecting;
WeaveLogProgress(MessageLayer, "Trying to repair connection to node %" PRIx64 "", peerNodeId);
// Call into TCPEndPoint to repair the TCP connection with the fetched
// repair info.
err = mTcpEndPoint->RepairConnection(repairInfo, intf);
SuccessOrExit(err);
WeaveLogProgress(MessageLayer, "Connection successfully repaired");
// Set the connection variables
PeerAddr = repairInfo.dstIP;
PeerNodeId = peerNodeId;
// If the peer address is not a ULA, or if the interface identifier portion of the peer address does not match
// the peer node id, then force the destination node identifier field to be encoded in all sent messages.
if (!PeerAddr.IsIPv6ULA() || IPv6InterfaceIdToWeaveNodeId(PeerAddr.InterfaceId()) != PeerNodeId)
{
SendDestNodeId = true;
}
PeerPort = repairInfo.dstPort;
IsRepaired = true;
// After repairing connection, set an immediate timer to call the OnConnectComplete callback.
// This is necessary to allow the call stack to unwind and also prevent the
// caller of this function to inadvertently change state variables after the
// application has been called back.
MessageLayer->SystemLayer->StartTimer(CONN_REPAIR_APP_CALLBACK_DELAY_MSECS, AppCallbackAfterConnectionRepair, this);
exit:
if (err != WEAVE_NO_ERROR)
{
WeaveLogProgress(MessageLayer, "Failed to repair connection to node %" PRIx64 ", err = %ld" , peerNodeId, (long)err);
if (mTcpEndPoint != NULL)
{
mTcpEndPoint->Abort();
mTcpEndPoint->Free();
mTcpEndPoint = NULL;
}
}
return err;
}
#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
WEAVE_ERROR WeaveConnection::StartConnect()
{
WEAVE_ERROR err;
// TODO: this is wrong. PeerNodeId should only be set once we have a successful connection (including security).
// Determine the peer address/node identifier based on the information given by the caller.
err = MessageLayer->SelectDestNodeIdAndAddress(PeerNodeId, PeerAddr);
if (err != WEAVE_NO_ERROR)
return err;
// Allocate a new TCP end point.
err = MessageLayer->Inet->NewTCPEndPoint(&mTcpEndPoint);
if (err != WEAVE_NO_ERROR)
return err;
// If the peer address is not a ULA, or if the interface identifier portion of the peer address does not match
// the peer node id, then force the destination node identifier field to be encoded in all sent messages.
if (!PeerAddr.IsIPv6ULA() || IPv6InterfaceIdToWeaveNodeId(PeerAddr.InterfaceId()) != PeerNodeId)
{
SendDestNodeId = true;
}
#if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
// TEMPORARY TESTING CODE: If the destination address is IPv6, and an IPv6 listening address has been specified,
// bind the end point to the listening address so that packets sent over the connection have the listening
// address as their source address. This makes it possible to assign multiple simulated fabric addresses to a
// single interface (e.g. the loopback interface) and ensure that packets sent from a particular node have the
// correct source address.
#if INET_CONFIG_ENABLE_IPV4
if (!PeerAddr.IsIPv4() && MessageLayer->FabricState->ListenIPv6Addr != IPAddress::Any)
#else // !INET_CONFIG_ENABLE_IPV4
if (MessageLayer->FabricState->ListenIPv6Addr != IPAddress::Any)
#endif // !INET_CONFIG_ENABLE_IPV4
{
err = mTcpEndPoint->Bind(kIPAddressType_IPv6, MessageLayer->FabricState->ListenIPv6Addr, 0, true);
if (err != WEAVE_NO_ERROR)
return err;
}
#endif
State = kState_Connecting;
mTcpEndPoint->AppState = this;
mTcpEndPoint->OnConnectComplete = HandleConnectComplete;
mTcpEndPoint->SetConnectTimeout(mConnectTimeout);
#if WEAVE_PROGRESS_LOGGING
{
char ipAddrStr[64];
PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
WeaveLogProgress(MessageLayer, "TCP con start %04" PRIX16 " %s %d", LogId(), ipAddrStr, (int)PeerPort);
}
#endif
// Initiate the TCP connection.
return mTcpEndPoint->Connect(PeerAddr, PeerPort, mTargetInterface);
}
void WeaveConnection::HandleConnectComplete(TCPEndPoint *endPoint, INET_ERROR conRes)
{
WeaveConnection *con = (WeaveConnection *) endPoint->AppState;
WeaveLogProgress(MessageLayer, "TCP con complete %04X %ld", con->LogId(), (long)conRes);
// If the connection was successful...
if (conRes == INET_NO_ERROR)
{
INET_ERROR err;
IPAddress localAddr;
uint16_t localPort;
// If the peer node identifier is unknown, attempt to infer it from the address of the peer.
if (con->PeerNodeId == kNodeIdNotSpecified && con->PeerAddr.IsIPv6ULA())
con->PeerNodeId = IPv6InterfaceIdToWeaveNodeId(con->PeerAddr.InterfaceId());
// Get the local address that was used for the connection.
err = endPoint->GetLocalInfo(&localAddr, &localPort);
if (err != INET_NO_ERROR)
{
con->DoClose(err, 0);
return;
}
// If the local address is not a ULA, or if the interface identifier portion of the local address does not match
// the local node id, then arrange to encode the source node identifier field in messages sent to the peer.
//
// We rely on this behavior in the case of Thread-assisted pairing. In this case, the (non-ULA) link-local
// address used by the new device while provisionally joined to the assisting device's PAN forces it to
// encode its source node identifier in all messages sent to the remote commissioning device. If the new device's
// local address were a ULA in this scenario, it would be unable to receive communications from the remote
// comissioner. The new device, having a ULA, would not encode its source node identifier in messages sent to the
// commissioner, and the comissioner, on a different network than the new device, would be unable to see
// the new device's ULA and use it to infer that device's node id.
if (!localAddr.IsIPv6ULA() || IPv6InterfaceIdToWeaveNodeId(localAddr.InterfaceId()) != con->MessageLayer->FabricState->LocalNodeId)
{
con->SendSourceNodeId = true;
}
// Setup various callbacks on the end point.
endPoint->OnDataReceived = HandleDataReceived;
endPoint->OnDataSent = NULL; // TODO: should handle flow control
endPoint->OnConnectionClosed = HandleTcpConnectionClosed;
// Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true
err = endPoint->EnableNoDelay();
if (err != INET_NO_ERROR)
{
con->DoClose(err, 0);
return;
}
// Negotiate secure session (or not) based on AuthMode.
con->StartSession();
}
// Otherwise the connection failed...
else
{
// Release the end point object.
endPoint->Free();
con->mTcpEndPoint = NULL;
// Attempt to connect to another address if available.
con->TryNextPeerAddress(conRes);
}
}
void WeaveConnection::HandleDataReceived(TCPEndPoint *endPoint, PacketBuffer *data)
{
WEAVE_ERROR err;
WeaveConnection *con = (WeaveConnection *) endPoint->AppState;
WeaveMessageLayer *msgLayer = con->MessageLayer;
// While in a state that allows receiving, process the received data...
while (data != NULL &&
con->StateAllowsReceive() &&
con->ReceiveEnabled &&
(con->OnMessageReceived != NULL
#if WEAVE_CONFIG_ENABLE_TUNNELING
|| con->OnTunneledMessageReceived != NULL
#endif
))
{
IPPacketInfo packetInfo;
WeaveMessageInfo msgInfo;
uint8_t* payload;
uint16_t payloadLen;
PacketBuffer* payloadBuf = NULL;
uint32_t frameLen;
packetInfo.Clear();
con->GetPeerAddressInfo(packetInfo);
msgInfo.Clear();
msgInfo.InPacketInfo = &packetInfo;
msgInfo.InCon = con;
// Attempt to parse an message from the head of the received queue.
err = msgLayer->DecodeMessageWithLength(data, con->PeerNodeId, con, &msgInfo, &payload, &payloadLen, &frameLen);
// If the initial buffer in the receive queue is not big enough to hold the entirety of
// the incoming message...
if (err == WEAVE_ERROR_MESSAGE_TOO_LONG)
{
// The Weave message decoding logic expects message data to be in contiguous memory.
// Therefore, if the packet buffer containing the initial portion of the message is
// not big enough to hold the entirety of the message, the data must be moved into
// a new buffer that is big enough.
//
// This situation can arise, for example, when when a TCP segment arrives containing
// part of a Weave message and the underlying network interface chooses to place the
// packet into a buffer that is smaller than the Weave message.
//
// Note that the logic here implies that when a system runs low on buffers, message
// reception can fail for lack of an appropriately sized buffer, resulting in the TCP
// connection being aborted. The only way to avoid this is for the underlying network
// interface to always place packets into buffers that are big enough to hold the
// maximum size Weave message. If such a buffer is not available when a packet comes
// in, the network interface can simply discard the packet, resulting in the peer
// retransmitting it and the system recovering gracefully once the buffer pressure
// subsides.
// Attempt to allocate a buffer big enough to hold the entire message. Fail with
// WEAVE_ERROR_MESSAGE_TOO_LONG if no such buffer is available.
PacketBuffer * newBuf = PacketBuffer::NewWithAvailableSize(0, frameLen);
if (newBuf == NULL)
{
break;
}
// Prepend the new buffer to the receive queue and copy the received message data into
// the new buffer, discarding the original buffer(s).
newBuf->AddToEnd(data);
data = newBuf;
data->CompactHead();
// Try again to decode the message.
continue;
}
// If the initial buffer in the receive queue contains only part of the next message...
if (err == WEAVE_ERROR_MESSAGE_INCOMPLETE)
{
// If there are more buffers in the queue, move as much data as possible into the head buffer
// and try again.
if (data->Next() != NULL)
{
data->CompactHead();
continue;
}
// Otherwise, we must wait for more data from the peer...
// Open the receive window just enough to allow the remainder of the message to be received.
// This is necessary in the case where the message size exceeds the TCP window size to ensure
// the peer has enough window to send us the entire message.
uint16_t neededLen = frameLen - data->DataLength();
err = endPoint->AckReceive(neededLen);
if (err == WEAVE_NO_ERROR)
break;
}
// If we successfully parsed a message, open the TCP receive window by the size of the message.
if (err == WEAVE_NO_ERROR)
err = endPoint->AckReceive(frameLen);
// Verify that destination node identifier refers to the local node.
if (err == WEAVE_NO_ERROR)
{
if (msgInfo.DestNodeId != msgLayer->FabricState->LocalNodeId && msgInfo.DestNodeId != kAnyNodeId)
err = WEAVE_ERROR_INVALID_DESTINATION_NODE_ID;
}
if (err == WEAVE_NO_ERROR)
{
// If there's no more data in the current buffer beyond the message that was just parsed,
// then avoid a copy by giving the buffer to the application layer.
if (data->DataLength() == 0)
{
// Detach the buffer from the data queue.
payloadBuf = data;
data = data->DetachTail();
// Adjust the buffer to point at the payload of the message.
payloadBuf->SetStart(payload);
payloadBuf->SetDataLength(payloadLen);
}
// Otherwise we need to keep the buffer so we can parse the remaining data, so copy the
// payload data into a new buffer and arrange to pass the new buffer to the application.
else
{
payloadBuf = PacketBuffer::New(0);
if (payloadBuf != NULL)
{
memcpy(payloadBuf->Start(), payload, payloadLen);
payloadBuf->SetDataLength(payloadLen);
}
else
err = WEAVE_ERROR_NO_MEMORY;
}
}
// Disconnect if an error occurred.
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(MessageLayer, "Con rcv data err %04X %ld", con->LogId(), err);
// Send key error response to the peer if required.
if (msgLayer->SecurityMgr->IsKeyError(err))
{
if (data != NULL)
{
PacketBuffer::Free(data);
data = NULL;
}
msgLayer->SecurityMgr->SendKeyErrorMsg(&msgInfo, NULL, con, err);
}
con->DisconnectOnError(err);
break;
}
//Check if message carries tunneled data and needs to be sent to Tunnel Agent
if (msgInfo.MessageVersion == kWeaveMessageVersion_V2)
{
if (msgInfo.Flags & kWeaveMessageFlag_TunneledData)
{
#if WEAVE_CONFIG_ENABLE_TUNNELING
// Dispatch the tunneled data message to the application if it is not a duplicate.
// Although TCP guarantees in-order, at-most-once delivery in normal conditions,
// checking for and eliminating duplicate tunneled messages here prevents replay
// of messages by a malicious man-in-the-middle.
if (!(msgInfo.Flags & kWeaveMessageFlag_DuplicateMessage))
{
if (con->OnTunneledMessageReceived)
{
con->OnTunneledMessageReceived(con, &msgInfo, payloadBuf);
}
else
{
con->DisconnectOnError(WEAVE_ERROR_NO_MESSAGE_HANDLER);
break;
}
}
#endif
}
else
{
if (con->OnMessageReceived)
{
con->OnMessageReceived(con, &msgInfo, payloadBuf);
}
else
{
con->DisconnectOnError(WEAVE_ERROR_NO_MESSAGE_HANDLER);
break;
}
}
}
else if (msgInfo.MessageVersion == kWeaveMessageVersion_V1)
{
// Pass the message header and payload to the application.
// NOTE that when this function returns, the state of the connection may have changed.
if (con->OnMessageReceived)
{
con->OnMessageReceived(con, &msgInfo, payloadBuf);
}
else
{
con->DisconnectOnError(WEAVE_ERROR_NO_MESSAGE_HANDLER);
break;
}
}
}
// If we couldn't process all the received data push it back into the end point. When
// more data arrives, it will call us back with this data plus the new data. If the underlying
// TCP connection is closed (which means we're never going to receive any more data), fail with
// a MESSAGE_INCOMPLETE error. If the WeaveConnection is closed (e.g. the app called close)
// then simply discard the received data.
if (data != NULL)
{
if (con->StateAllowsReceive())
{
if (endPoint->State == TCPEndPoint::kState_Connected ||
endPoint->State == TCPEndPoint::kState_SendShutdown)
{
endPoint->PutBackReceivedData(data);
data = NULL;
}
else
con->DoClose(WEAVE_ERROR_MESSAGE_INCOMPLETE, 0);
}
if (data != NULL)
PacketBuffer::Free(data);
}
}
void WeaveConnection::HandleTcpConnectionClosed(TCPEndPoint *endPoint, INET_ERROR err)
{
WeaveConnection *con = (WeaveConnection *) endPoint->AppState;
if (err == INET_NO_ERROR && con->State == kState_EstablishingSession)
err = WEAVE_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY;
con->DoClose(err, 0);
}
void WeaveConnection::HandleSecureSessionEstablished(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState,
uint16_t sessionKeyId, uint64_t peerNodeId, uint8_t encType)
{
// Establish the peer node identifier and the default key and encryption type to be used to send messages.
con->PeerNodeId = peerNodeId;
con->DefaultKeyId = sessionKeyId;
con->DefaultEncryptionType = encType;
// Enter the connected state.
con->State = kState_Connected;
WeaveLogProgress(MessageLayer, "Con complete %04X", con->LogId());
// Invoke the app's completion function.
if (con->OnConnectionComplete != NULL)
con->OnConnectionComplete(con, WEAVE_NO_ERROR);
}
void WeaveConnection::HandleSecureSessionError(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState,
WEAVE_ERROR localErr, uint64_t peerNodeId, Profiles::StatusReporting::StatusReport *statusReport)
{
// Couldn't get a secure session, so fail the connect attempt.
con->DoClose(localErr, 0);
}
void WeaveConnection::Init(WeaveMessageLayer *msgLayer)
{
// NOTE: Please keep these in declared order to make it easier keep in sync.
PeerNodeId = 0;
PeerAddr = IPAddress::Any;
MessageLayer = msgLayer;
AppState = NULL;
PeerPort = 0;
DefaultKeyId = WeaveKeyId::kNone;
AuthMode = kWeaveAuthMode_NotSpecified;
DefaultEncryptionType = kWeaveEncryptionType_None;
State = WeaveConnection::kState_ReadyToConnect;
NetworkType = kNetworkType_Unassigned;
ReceiveEnabled = true;
OnConnectionComplete = NULL;
OnMessageReceived = NULL;
#if WEAVE_CONFIG_ENABLE_TUNNELING
OnTunneledMessageReceived = NULL;
#endif
OnConnectionClosed = DefaultConnectionClosedHandler;
OnReceiveError = NULL;
memset(&mPeerAddrs, 0, sizeof(mPeerAddrs));
mTcpEndPoint = NULL;
#if CONFIG_NETWORK_LAYER_BLE
mBleEndPoint = NULL;
#endif
mPeerHostPortList.Clear();
mTargetInterface = INET_NULL_INTERFACEID;
mRefCount = 1;
SendSourceNodeId = false;
SendDestNodeId = false;
mConnectTimeout = 0;
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
mDNSOptions = 0;
#endif
#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
IsRepaired = false;
#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
mFlags = 0;
}
// Default OnConnectionClosed handler.
//
// This handler is installed in the OnConnectionClosed callback of all new WeaveConnection objects.
// The function automatically releases the application's reference to the object whenever the connection
// closes spontaneously from the network side. This eliminates the need for boiler-plate code to release
// the object in every Weave application that uses connections, and helps to avoid bugs in applications
// that fail to properly take care of this themselves.
//
// Note that this behavior only applies to closes initiated from the network. If the application itself
// calls Close() or Abort() on the connection, this function is never called.
//
void WeaveConnection::DefaultConnectionClosedHandler(WeaveConnection *con, WEAVE_ERROR conErr)
{
// Call Close() on the connection object to release the application's reference to the connection.
con->Close();
}
void WeaveConnection::MakeConnectedTcp(TCPEndPoint *endPoint, const IPAddress &localAddr, const IPAddress &peerAddr)
{
mTcpEndPoint = endPoint;
NetworkType = kNetworkType_IP;
endPoint->AppState = this;
endPoint->OnDataReceived = HandleDataReceived;
endPoint->OnDataSent = NULL; // TODO: should handle flow control
endPoint->OnConnectionClosed = HandleTcpConnectionClosed;
PeerNodeId = (peerAddr.IsIPv6ULA()) ? IPv6InterfaceIdToWeaveNodeId(peerAddr.InterfaceId()) : kNodeIdNotSpecified;
PeerAddr = peerAddr;
// If the local address is not a ULA, or if the interface identifier portion of the local address does not match
// the local node id, then arrange to encode the source node identifier field in all messages sent to the peer.
if (!localAddr.IsIPv6ULA() || IPv6InterfaceIdToWeaveNodeId(localAddr.InterfaceId()) != MessageLayer->FabricState->LocalNodeId)
{
SendSourceNodeId = true;
}
// Similarly, if we were unable do derive the node identifier of the peer from its address, then arrange for the
// destination node identifier field to be encoded in all sent messages.
if (PeerNodeId == kNodeIdNotSpecified)
{
SendDestNodeId = true;
}
AppState = NULL;
mRefCount++;
ReceiveEnabled = true;
// Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true
endPoint->EnableNoDelay();
State = kState_Connected;
}
#if CONFIG_NETWORK_LAYER_BLE
/**
* Bind WeaveConnection to BLE connection handle from application.
* The application must call this function when and only when it, acting in the role
* of a BLE Central, has actively connected to a BLE Peripheral which supports the
* Weave BLE service.
*
* If this function and the WeaveConnection's OnConnectionComplete callback both
* return without error, Weave has accepted the BLE connection.
* However, if Weave accepts a BLE connection, the platform MUST notify Weave via
* the appropriate BleLayer callback if the central's subscription is canceled or the
* underlying BLE connection is closed, otherwise the associated WeaveConnection
* will never be closed or freed.
*
* @note
* A downcall to this method may call OnConnectionComplete before it returns.
*
* @param[in] connObj The BLE connection object.
*
* @param[in] authMode The desired authenticate mode for the peer. Only CASE, PASE and Unauthenticated
* modes are supported.
*
* @param[in] autoClose true if automatic closing is enabled upon a long period of
* inactivity, otherwise false.
*
* @retval #WEAVE_NO_ERROR on successful initiation of the BLE connection to
* the peer.
* @retval #WEAVE_ERROR_INCORRECT_STATE if the WeaveConnection state is not ready to connect.
*
* @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE if the requested authentication mode is not supported or
* the security manager is not initialized.
*
* @retval other Inet layer errors generated by the BleEndPoint create and connect operations.
*
*
*/
WEAVE_ERROR WeaveConnection::ConnectBle(BLE_CONNECTION_OBJECT connObj, WeaveAuthMode authMode, bool autoClose)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(State == kState_ReadyToConnect && MessageLayer->mBle != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || IsCASEAuthMode(authMode) || IsPASEAuthMode(authMode), err = WEAVE_ERROR_INVALID_ARGUMENT);
// Can't request authentication if the security manager is not initialized.
VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || MessageLayer->SecurityMgr != NULL, err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE);
// Application has made us a BLE-based WeaveConnection.
NetworkType = kNetworkType_BLE;
AuthMode = authMode;
// Only BLE centrals can form new GATT connections, so specify this role in our creation of the BLEEndPoint.
err = MessageLayer->mBle->NewBleEndPoint(&mBleEndPoint, connObj, kBleRole_Central, autoClose);
SuccessOrExit(err);
// Enter the connecting state.
State = kState_Connecting;
// Set up callbacks we need to negotiate WoBle connection.
mBleEndPoint->mAppState = this;
mBleEndPoint->OnConnectComplete = HandleBleConnectComplete;
mBleEndPoint->OnConnectionClosed = HandleBleConnectionClosed;
// Must always send SourceNodeId over BLE-based WeaveConnection, as peer cannot infer it from source address.
SendSourceNodeId = true;
// Bump the reference count when we start the connection process. The corresponding decrement happens when the
// DoClose() method is called. This ensures the object stays live while there's the possibility of a callback
// happening from an underlying layer (e.g. a BLE GATT subscribe request).
mRefCount++;
// Initiate Weave over BLE protocol connection.
err = mBleEndPoint->StartConnect();
SuccessOrExit(err);
exit:
return err;
}
void WeaveConnection::HandleBleConnectComplete(BLEEndPoint *endPoint, BLE_ERROR err)
{
WeaveConnection *con = static_cast<WeaveConnection *>(endPoint->mAppState);
if (err != BLE_NO_ERROR)
ExitNow();
WeaveLogProgress(MessageLayer, "WoBle con complete %04X\n", con->LogId());
// Set message received callback.
con->mBleEndPoint->OnMessageReceived = HandleBleMessageReceived;
// Negotiate secure session (or not) based on AuthMode.
con->StartSession();
exit:
if (err != BLE_NO_ERROR)
{
WeaveLogError(MessageLayer, "WoBle con failed %04X %d", con->LogId(), err);
con->DoClose(err, 0);
}
}
void WeaveConnection::HandleBleMessageReceived(BLEEndPoint *endPoint, PacketBuffer *data)
{
WeaveConnection *con = (WeaveConnection *) endPoint->mAppState;
WeaveMessageLayer *msgLayer = con->MessageLayer;
// Weave's BLE layer reassembles received messages in their entirety before it passes them up the stack,
// so there's no need for received buffer compaction or reassembly at this layer.
WeaveMessageInfo msgInfo;
uint8_t *payload;
uint16_t payloadLen;
PacketBuffer *payloadBuf;
uint32_t frameLen;
WEAVE_ERROR err;
// Initialize the message info structure.
msgInfo.Clear();
msgInfo.InCon = con;
// Verify received buffer is not part of a multi-buffer chain.
VerifyOrExit(data->Next() == NULL, err = BLE_ERROR_BAD_ARGS);
// Attempt to parse the message.
err = msgLayer->DecodeMessageWithLength(data, con->PeerNodeId, con, &msgInfo, &payload,
&payloadLen, &frameLen);
SuccessOrExit(err);
// Verify that destination node id refers to the local node.
VerifyOrExit(((msgInfo.DestNodeId == msgLayer->FabricState->LocalNodeId) ||
(msgInfo.DestNodeId == kAnyNodeId)),
err = WEAVE_ERROR_INVALID_DESTINATION_NODE_ID);
// Verify that the received buffer contained exactly one Weave message.
VerifyOrExit(data->DataLength() == 0, err = BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG);
// Assign received data to payload buffer.
payloadBuf = data;
data = NULL;
// Adjust the buffer to point at the payload of the message.
payloadBuf->SetStart(payload);
payloadBuf->SetDataLength(payloadLen);
// Pass the message header and payload to the application.
// NOTE that when this function returns, the state of the connection may have changed.
con->OnMessageReceived(con, &msgInfo, payloadBuf);
exit:
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(MessageLayer, "HandleBleMessageReceived failed, err = %d", err);
if (data != NULL)
PacketBuffer::Free(data);
// Send key error response to the peer if required.
if (msgLayer->SecurityMgr->IsKeyError(err))
msgLayer->SecurityMgr->SendKeyErrorMsg(&msgInfo, NULL, con, err);
}
}
void WeaveConnection::HandleBleConnectionClosed(BLEEndPoint *endPoint, BLE_ERROR err)
{
WeaveConnection *con = (WeaveConnection *) endPoint->mAppState;
con->DoClose(err, 0);
}
void WeaveConnection::MakeConnectedBle(BLEEndPoint *endPoint)
{
mBleEndPoint = endPoint;
NetworkType = kNetworkType_BLE;
endPoint->mAppState = this;
endPoint->OnMessageReceived = HandleBleMessageReceived;
endPoint->OnConnectionClosed = HandleBleConnectionClosed;
endPoint->mState = BLEEndPoint::kState_Connected;
// Must always send SourceNodeId over BLE connection since peer cannot infer it from source address.
SendSourceNodeId = true;
State = kState_Connected;
mRefCount++;
}
#endif // CONFIG_NETWORK_LAYER_BLE
void WeaveConnection::DisconnectOnError (WEAVE_ERROR err)
{
if (OnReceiveError != NULL)
{
OnReceiveError(this, err);
}
else if (MessageLayer->OnReceiveError != NULL)
{
IPPacketInfo addrInfo;
GetPeerAddressInfo(addrInfo);
MessageLayer->OnReceiveError(MessageLayer, err, &addrInfo);
}
DoClose(err, 0);
}
} // namespace nl
} // namespace Weave