blob: 8c7f7b83910330073e45b78b14f60e37455e817a [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 an object for a Weave Echo unsolicitied
* initiator (client).
*
*/
#include <Weave/Core/WeaveCore.h>
#include "WeaveEcho.h"
namespace nl {
namespace Weave {
namespace Profiles {
WeaveEchoClient::WeaveEchoClient()
{
FabricState = NULL;
ExchangeMgr = NULL;
EncryptionType = kWeaveEncryptionType_None;
KeyId = WeaveKeyId::kNone;
OnEchoResponseReceived = NULL;
ExchangeCtx = NULL;
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
OnAckRcvdReceived = NULL;
RequestAck = false;
AckReceived = false;
ResponseReceived = false;
WRMPACKDelay = WEAVE_CONFIG_WRMP_DEFAULT_ACK_TIMEOUT;
WRMPRetransInterval = WEAVE_CONFIG_WRMP_DEFAULT_ACTIVE_RETRANS_TIMEOUT;
WRMPRetransCount = WEAVE_CONFIG_WRMP_DEFAULT_MAX_RETRANS;
appContext = 0xcafebabe;
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
}
WEAVE_ERROR WeaveEchoClient::Init(WeaveExchangeManager *exchangeMgr)
{
// Error if already initialized.
if (ExchangeMgr != NULL)
return WEAVE_ERROR_INCORRECT_STATE;
ExchangeMgr = exchangeMgr;
FabricState = exchangeMgr->FabricState;
EncryptionType = kWeaveEncryptionType_None;
KeyId = WeaveKeyId::kNone;
OnEchoResponseReceived = NULL;
ExchangeCtx = NULL;
return WEAVE_NO_ERROR;
}
WEAVE_ERROR WeaveEchoClient::Shutdown()
{
if (ExchangeCtx != NULL)
{
ExchangeCtx->Abort();
ExchangeCtx = NULL;
}
ExchangeMgr = NULL;
FabricState = NULL;
return WEAVE_NO_ERROR;
}
/**
* Send an echo request over a WeaveConnection
*
* @param con The connection
* @param payload A PacketBuffer with the payload. This function takes ownership of the PacketBuffer
*
* @return WEAVE_ERROR_NO_MEMORY if no ExchangeContext is available.
* Other WEAVE_ERROR codes as returned by the lower layers.
*/
WEAVE_ERROR WeaveEchoClient::SendEchoRequest(WeaveConnection *con, PacketBuffer *payload)
{
// Discard any existing exchange context. Effectively we can only have one Echo exchange with
// a single node at any one time.
if (ExchangeCtx != NULL)
{
ExchangeCtx->Abort();
ExchangeCtx = NULL;
}
// Create a new exchange context.
ExchangeCtx = ExchangeMgr->NewContext(con, this);
if (ExchangeCtx == NULL)
{
PacketBuffer::Free(payload);
return WEAVE_ERROR_NO_MEMORY;
}
ExchangeCtx->OnConnectionClosed = HandleConnectionClosed;
return SendEchoRequest(payload);
}
/**
* Send an echo request to a Weave node using the default Weave port and the
* letting the system's routing table choose the output interface.
*
* @param nodeId The destination's nodeId
* @param nodeAddr The destination's ip address
* @param payload A PacketBuffer with the payload. This function takes ownership of the PacketBuffer
*
* @return WEAVE_ERROR_NO_MEMORY if no ExchangeContext is available.
* Other WEAVE_ERROR codes as returned by the lower layers.
*/
WEAVE_ERROR WeaveEchoClient::SendEchoRequest(uint64_t nodeId, IPAddress nodeAddr, PacketBuffer *payload)
{
return SendEchoRequest(nodeId, nodeAddr, WEAVE_PORT, INET_NULL_INTERFACEID, payload);
}
/**
* Send an echo request to a Weave node.
*
* @param nodeId The destination's nodeId
* @param nodeAddr The destination's ip address
* @param port The destination's UDP port (WEAVE_PORT by default)
* @param sendIntfId A specific interface to use
* @param payload A PacketBuffer with the payload. This function takes ownership of the PacketBuffer
*
* @return WEAVE_ERROR_NO_MEMORY if no ExchangeContext is available.
* Other WEAVE_ERROR codes as returned by the lower layers.
*/
WEAVE_ERROR WeaveEchoClient::SendEchoRequest(uint64_t nodeId, IPAddress nodeAddr, uint16_t port, InterfaceId sendIntfId, PacketBuffer *payload)
{
// Discard any existing exchange context. Effectively we can only have one Echo exchange with
// a single node at any one time.
if (ExchangeCtx != NULL)
{
ExchangeCtx->Abort();
ExchangeCtx = NULL;
}
if (nodeAddr == IPAddress::Any)
nodeAddr = FabricState->SelectNodeAddress(nodeId);
// Create a new exchange context.
ExchangeCtx = ExchangeMgr->NewContext(nodeId, nodeAddr, WEAVE_PORT, sendIntfId, this);
if (ExchangeCtx == NULL)
{
PacketBuffer::Free(payload);
return WEAVE_ERROR_NO_MEMORY;
}
return SendEchoRequest(payload);
}
WEAVE_ERROR WeaveEchoClient::SendEchoRequest(PacketBuffer *payload)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Configure the encryption and signature types to be used to send the request.
ExchangeCtx->EncryptionType = EncryptionType;
ExchangeCtx->KeyId = KeyId;
// Arrange for messages in this exchange to go to our response handler.
ExchangeCtx->OnMessageReceived = HandleResponse;
ExchangeCtx->OnKeyError = HandleKeyError;
// Send an Echo Request message. Discard the exchange context if the send fails.
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
AckReceived = false;
ResponseReceived = false;
if (RequestAck)
{
ExchangeCtx->OnAckRcvd = HandleAckRcvd;
ExchangeCtx->OnSendError = HandleSendError;
ExchangeCtx->mWRMPConfig.mAckPiggybackTimeout = WRMPACKDelay;
ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = WRMPRetransInterval;
ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = WRMPRetransInterval;
ExchangeCtx->mWRMPConfig.mMaxRetrans = WRMPRetransCount;
err = ExchangeCtx->SendMessage(kWeaveProfile_Echo, kEchoMessageType_EchoRequest, payload, ExchangeContext::kSendFlag_RequestAck, &appContext);
}
else
err = ExchangeCtx->SendMessage(kWeaveProfile_Echo, kEchoMessageType_EchoRequest, payload);
#else // !WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
err = ExchangeCtx->SendMessage(kWeaveProfile_Echo, kEchoMessageType_EchoRequest, payload);
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
payload = NULL;
if (err != WEAVE_NO_ERROR && ExchangeCtx)
{
ExchangeCtx->Abort();
ExchangeCtx = NULL;
}
return err;
}
void WeaveEchoClient::HandleResponse(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payload)
{
WeaveEchoClient *echoApp = (WeaveEchoClient *)ec->AppState;
// Assert that the exchange context matches the client's current context.
// This should never fail because even if SendEchoRequest is called
// back-to-back, the second call will call Close() on the first exchange,
// which clears the OnMessageReceived callback.
VerifyOrDie(echoApp && ec == echoApp->ExchangeCtx);
// Verify that the message is an Echo Response.
// If not, close the exchange and free the payload.
if (profileId != kWeaveProfile_Echo || msgType != kEchoMessageType_EchoResponse)
{
ec->Close();
echoApp->ExchangeCtx = NULL;
ExitNow();
}
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
echoApp->ResponseReceived = true;
if (!echoApp->RequestAck || echoApp->AckReceived || (!echoApp->OnAckRcvdReceived))
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
{
// Remove the EC from the app state now. OnEchoResponseReceived can call
// SendEchoRequest and install a new one.
echoApp->ExchangeCtx->Close();
echoApp->ExchangeCtx = NULL;
}
// Call the registered OnEchoResponseReceived handler, if any.
if (echoApp->OnEchoResponseReceived != NULL)
echoApp->OnEchoResponseReceived(msgInfo->SourceNodeId, (pktInfo != NULL) ? pktInfo->SrcAddress : IPAddress::Any, payload);
exit:
// Free the payload buffer.
PacketBuffer::Free(payload);
}
void WeaveEchoClient::HandleConnectionClosed(ExchangeContext *ec, WeaveConnection *con, WEAVE_ERROR conErr)
{
HandleError(ec, conErr);
}
void WeaveEchoClient::HandleSendError(ExchangeContext *ec, WEAVE_ERROR sendErr, void *msgCtxt)
{
HandleError(ec, sendErr);
}
void WeaveEchoClient::HandleKeyError(ExchangeContext *ec, WEAVE_ERROR keyErr)
{
HandleError(ec, keyErr);
}
void WeaveEchoClient::HandleError(ExchangeContext *ec, WEAVE_ERROR err)
{
WeaveEchoClient *echoApp = (WeaveEchoClient *)ec->AppState;
VerifyOrExit(echoApp && echoApp->ExchangeCtx, /* no action */);
VerifyOrDie(ec == echoApp->ExchangeCtx);
if (err != WEAVE_NO_ERROR)
{
echoApp->ExchangeCtx->Abort();
}
else
{
echoApp->ExchangeCtx->Close();
}
exit:
echoApp->ExchangeCtx = NULL;
}
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
void WeaveEchoClient::HandleAckRcvd(ExchangeContext *ec, void *msgCtxt)
{
WeaveEchoClient *echoApp = (WeaveEchoClient *)ec->AppState;
// Assert that the exchange context matches the client's current context.
// This should never fail because even if SendEchoRequest is called
// back-to-back, the second call will call Close() on the first exchange,
// which clears the OnAckReceived callback.
VerifyOrDie(echoApp && ec == echoApp->ExchangeCtx);
echoApp->AckReceived = true;
if (echoApp->ResponseReceived)
{
// Remove the EC from the app state now. OnAckRcvdReceived can call
// SendEchoRequest and install a new one.
echoApp->ExchangeCtx->Close();
echoApp->ExchangeCtx = NULL;
}
// Call the registered OnEchoResponseReceived handler, if any.
if (echoApp->OnAckRcvdReceived != NULL)
echoApp->OnAckRcvdReceived(msgCtxt);
}
void WeaveEchoClient::SetRequestAck(bool requestAck)
{
RequestAck = requestAck;
}
void WeaveEchoClient::SetWRMPACKDelay(uint16_t aWRMPACKDelay)
{
WRMPACKDelay = aWRMPACKDelay;
}
void WeaveEchoClient::SetWRMPRetransInterval(uint32_t aWRMPRetransInterval)
{
WRMPRetransInterval = aWRMPRetransInterval;
}
void WeaveEchoClient::SetWRMPRetransCount(uint8_t aWRMPRetransCount)
{
WRMPRetransCount = aWRMPRetransCount;
}
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
} // namespace Profiles
} // namespace Weave
} // namespace nl