blob: 4793d622135d481af0836f8531887fd41845ed87 [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>
namespace nl {
namespace Weave {
WEAVE_ERROR WeaveConnection::StartConnectToAddressLiteral(const char aAddressLiteral[])
{
WEAVE_ERROR lReturn = 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(aAddressLiteral, PeerAddr))
lReturn = StartConnect();
#endif // WEAVE_CONFIG_RESOLVE_IPADDR_LITERAL
return lReturn;
}
/**
* 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:
*
* <host-name>
* <host-name>:<port>
* <ip-4-addr>
* <ip-4-addr>:<port>
* <ip-6-addr>
* [<ip-6-addr>]:<port>
*
* @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:
*
* <host-name>
* <host-name>:<port>
* <ip-4-addr>
* <ip-4-addr>:<port>
* <ip-6-addr>
* [<ip-6-addr>]:<port>
*
* @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)
{
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, WEAVE_CONFIG_CONNECT_IP_ADDRS, mPeerAddrs, HandleResolveComplete, this);
#else // !WEAVE_CONFIG_ENABLE_DNS_RESOLVER
err = StartConnectToAddressLiteral(hostName);
#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)
{
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;
// 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;
}
#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() frees 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.
*
* A call to Close() terminates the WeaveConnection. Any further use of a WeaveConnection needs to be
* initiated by a call to WeaveMessageLayer::NewConnection();
*
* @sa Shutdown() and Abort().
*
* @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() frees 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.
*
* A call to Close() terminates the WeaveConnection. Any further use of a WeaveConnection needs to be
* initiated by a call to WeaveMessageLayer::NewConnection();
*
* @param[in] suppressCloseLog true if logs need to be suppressed, false otherwise.
*
* @sa Shutdown() and Abort().
*
* @return #WEAVE_NO_ERROR unconditionally.
*
*/
WEAVE_ERROR WeaveConnection::Close(bool suppressCloseLog)
{
VerifyOrDie(mRefCount != 0);
// 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()).
mRefCount--;
return WEAVE_NO_ERROR;
}
/**
* Performs an un-graceful close of the TCP- or BLE-based WeaveConnection, freeing all local state
* without informing the remote host.
*
* A call to Abort() terminates the WeaveConnection. Any further use of a WeaveConnection needs to be
* initiated by a call to WeaveMessageLayer::NewConnection();
*
* @sa Shutdown() and Close().
*
*/
void WeaveConnection::Abort()
{
VerifyOrDie(mRefCount != 0);
// 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()).
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)
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", LogId(), hostName);
State = kState_Resolving;
err = MessageLayer->Inet->ResolveHostAddress(hostName, strlen(hostName), WEAVE_CONFIG_CONNECT_IP_ADDRS,
mPeerAddrs, HandleResolveComplete, this);
#else // !WEAVE_CONFIG_ENABLE_DNS_RESOLVER
err = StartConnectToAddressLiteral(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);
}
}
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;
uint16_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 data.
err = msgLayer->DecodeMessageWithLength(data, con->PeerNodeId, con, &msgInfo, &payload, &payloadLen, &frameLen);
// If the data buffer contains only part of a 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;
}
// 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;
uint16_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