blob: 72be1786b27d7e6010cd55baa62175fb1dfe7a84 [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 contains definitions for the Weave Bulk Data Transfer
* Server. It manages BDXTransfer objects and interfaces with the
* BdxProtocol class to facilitate transfers.
*/
#include <Weave/Support/CodeUtils.h>
#include <Weave/Support/WeaveFaultInjection.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Profiles/common/CommonProfile.h>
#include <Weave/Profiles/bulk-data-transfer/Development/BDXNode.h>
namespace nl {
namespace Weave {
namespace Profiles {
namespace WeaveMakeManagedNamespaceIdentifier(BDX, kWeaveManagedNamespaceDesignation_Development) {
using namespace ::nl::Weave::Profiles::Common;
using namespace ::nl::Weave::Logging;
#define BDX_RESPONSE_TIMEOUT_MS WEAVE_CONFIG_BDX_RESPONSE_TIMEOUT_SEC * 1000
/*
* Here are the method definitions for the BdxNode class
*
* the no-arg constructor with defaulting for BDX clients
*/
BdxNode::BdxNode(void)
{
mExchangeMgr = NULL;
mIsBdxTransferAllowed = false;
mInitialized = false;
mSendInitHandler = NULL;
mReceiveInitHandler = NULL;
}
/**
* @brief
* Put all transfers in a default state ready for use, store the
* WeaveExchangeManager and any other necessary Weave resources,
* and sets allowBdxTransferToRun(true).
*
* @param[in] anExchangeMgr An exchange manager to use for this bulk transfer operation.
*
* @retval #WEAVE_NO_ERROR if successful
* @retval #WEAVE_ERROR_INCORRECT_STATE if mExchangeMgr isn't null, already initialized
*/
WEAVE_ERROR BdxNode::Init(WeaveExchangeManager *anExchangeMgr)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Error if already initialized.
VerifyOrExit(mExchangeMgr == NULL, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(anExchangeMgr != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
mExchangeMgr = anExchangeMgr;
// Initialize all the BDXTransfers
for (int i = 0; i < WEAVE_CONFIG_BDX_MAX_NUM_TRANSFERS; i++)
{
// Error if already initialized.
mTransferPool[i].Reset();
}
mIsBdxTransferAllowed = true;
mInitialized = true;
exit:
return err;
}
/**
* @brief
* Shuts down all transfers and releases any Weave resources
* (currently sets mExchangeMgr to NULL). Sets AllowBdxTransferToRun(false) and
* disconnects any current callbacks (for example, SendInitHandler).
*
* @note
* The BDX Protocol never calls this function as it is up to the platform
* to create and manage the BdxNode.
*
* @return #WEAVE_NO_ERROR if successfully shut down, other error if AwaitBdxSend/ReceiveInit returns an error
*/
WEAVE_ERROR BdxNode::Shutdown(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
for (int i = 0; i < WEAVE_CONFIG_BDX_MAX_NUM_TRANSFERS; i++)
{
ShutdownTransfer(&mTransferPool[i]);
}
AllowBdxTransferToRun(false);
#if WEAVE_CONFIG_BDX_SERVER_SUPPORT
// Unregister any callbacks BEFORE releasing mExchangeMgr
err = AwaitBdxSendInit(NULL);
VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_ERROR_INCORRECT_STATE ||
err == WEAVE_ERROR_NO_UNSOLICITED_MESSAGE_HANDLER,
WeaveLogDetail(BDX,
"Error removing existing sendinit callback in ShutdownServer: %d", err);
);
err = AwaitBdxReceiveInit(NULL);
VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_ERROR_INCORRECT_STATE ||
err == WEAVE_ERROR_NO_UNSOLICITED_MESSAGE_HANDLER,
WeaveLogDetail(BDX,
"Error removing existing receiveinit callback in ShutdownServer: %d", err);
);
exit:
#endif // WEAVE_CONFIG_BDX_SERVER_SUPPORT
mExchangeMgr = NULL;
return err;
}
/**
* @brief
* Get and set up a new BDXTransfer from transfer pool if available,
* or set to NULL otherwise and return an error.
*
* A BDXTransfer object obtained through this function is considered allocated
* by its mIsInitiated member being true. All other values will be the expected
* default values (see BDXTransfer::setDefaults()).
*
* @note
* You MUST shutdown this transfer in order to release it to the free pool!
* @note
* You will not typically use this function and instead should use one of the
* other versions, which in turn defer to this function. Only manually set up
* a BDXTransfer if you really know what you're doing and are prepared to do
* maintenance in the event of future changes to its structure!
*
* @param[in] aXfer A passed-by-reference pointer that will point to the
* new BDXTransfer object if one is available, else NULL.
*
* @retval #WEAVE_NO_ERROR If we successfully found a new BDXTransfer.
* @retval #WEAVE_ERROR_TOO_MANY_CONNECTIONS If too many transfers are currently active and aXfer is NULL
*/
WEAVE_ERROR BdxNode::AllocTransfer(BDXTransfer * &aXfer)
{
WEAVE_ERROR err = WEAVE_ERROR_TOO_MANY_CONNECTIONS;
WEAVE_FAULT_INJECT(FaultInjection::kFault_BDXAllocTransfer, ExitNow());
for (int i = 0; i < WEAVE_CONFIG_BDX_MAX_NUM_TRANSFERS; i++)
{
aXfer = &mTransferPool[i];
if (aXfer->mIsInitiated)
{
continue;
}
if (aXfer->mExchangeContext != NULL)
{
WeaveLogDetail(BDX, "Error! Xfer is not initiated but has mExchangeContext.");
continue;
}
aXfer->mIsInitiated = true;
err = WEAVE_NO_ERROR;
ExitNow();
}
// All of the connections are in use
aXfer = NULL;
exit:
return err;
}
/**
* @brief
* Get and set up a new BDXTransfer from transfer pool if available,
* or set to NULL otherwise and return an error.
*
* @note
* You MUST shutdown this transfer in order to release it to the free pool!
* @note
* If a particular handler should not be used in the transfer you are setting up
* (e.g., a PutBlockHandler when Sending), set it to NULL.
* @note
* See BDXTransferState.h for details about the handlers.
*
* @param[in] aBinding The Binding to the node we will initiate the transfer with.
* It's used to create an associated ExchangeContext for this transfer.
* @param[in] aBDXHandlers A structure of BDX callback handlers to be called during the transfer
* @param[in] aFileDesignator The file designator for the file that will be transferred.
* @param[in] anAppState An application-specific state object to be attached to the
* BDXTransfer for use by the user application and associated callbacks.
* @param[in] aXfer A passed-by-reference pointer that will point to the
* new BDXTransfer object if one is available, else NULL.
*
*
* @retval #WEAVE_NO_ERROR If we successfully found a new BDXTransfer.
* @retval #WEAVE_ERROR_NO_MEMORY If unable to create ExchangeContext
* @retval #WEAVE_ERROR_TOO_MANY_CONNECTIONS If too many transfers are currently active and aXfer is NULL
* @retval #WEAVE_ERROR_INCORRECT_STATE If aBinding is not prepared
*/
WEAVE_ERROR BdxNode::NewTransfer(Binding * aBinding, BDXHandlers aBDXHandlers,
ReferencedString &aFileDesignator, void * anAppState,
BDXTransfer * &aXfer)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext * ec = NULL;
VerifyOrExit(aBinding != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = aBinding->NewExchangeContext(ec);
SuccessOrExit(err);
// Note: ExchangeContext cleanup is handled within the call below
err = NewTransfer(ec, aBDXHandlers, aFileDesignator, anAppState, aXfer);
exit:
return err;
}
/**
* @brief
* Get and set up a new BDXTransfer from transfer pool if available,
* or set to NULL otherwise and return an error.
*
* @note
* You MUST shutdown this transfer in order to release it to the free pool!
* @note
* If a particular handler should not be used in the transfer you are setting up
* (e.g., a PutBlockHandler when Sending), set it to NULL.
* @note
* See BDXTransferState.h for details about the handlers.
*
* @param[in] aCon The WeaveConnection to the node we will initiate the transfer with.
* It's used to create an associated ExchangeContext for this transfer.
* @param[in] aBDXHandlers A structure of BDX callback handlers to be called during the transfer
* @param[in] aFileDesignator The file designator for the file that will be transferred.
* @param[in] anAppState An application-specific state object to be attached to the
* BDXTransfer for use by the user application and associated callbacks.
* @param[in] aXfer A passed-by-reference pointer that will point to the
* new BDXTransfer object if one is available, else NULL.
*
*
* @retval #WEAVE_NO_ERROR If we successfully found a new BDXTransfer.
* @retval #WEAVE_ERROR_NO_MEMORY If unable to create ExchangeContext
* @retval #WEAVE_ERROR_TOO_MANY_CONNECTIONS If too many transfers are currently active and aXfer is NULL
* @retval #WEAVE_ERROR_INCORRECT_STATE If mExchangeMgr has not been set yet or if aCon has not successfully connected
*/
WEAVE_ERROR BdxNode::NewTransfer(WeaveConnection * aCon, BDXHandlers aBDXHandlers,
ReferencedString &aFileDesignator, void * anAppState,
BDXTransfer * &aXfer)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *ec = NULL;
VerifyOrExit(aCon != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(aCon->State == WeaveConnection::kState_Connected, err = WEAVE_ERROR_INCORRECT_STATE);
ec = mExchangeMgr->NewContext(aCon);
VerifyOrExit(ec != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Note: ExchangeContext cleanup is handled within the call below
err = NewTransfer(ec, aBDXHandlers, aFileDesignator, anAppState, aXfer);
exit:
return err;
}
/**
* @brief
* Get and set up a new BDXTransfer from transfer pool if available,
* or set to NULL otherwise and return an error. The ExchangeContext (and possible
* underlying WeaveConnection) will have their AppState pointer set to
* the BDXTransfer.
*
* @note
* This is the version of NewTransfer that you should use if you want an encrypted
* exchange, which requires setting up the ExchangeContext manually.
* @note
* You MUST shutdown this transfer in order to release it to the free pool!
* @note
* If a particular handler should not be used in the transfer you are setting up
* (e.g., a PutBlockHandler when Sending), set it to NULL.
* @note
* See BDXTransferState.h for details about the handlers.
*
* @param[in] anEc The ExchangeContext with the node we will initiate the transfer with.
* The server will handle closing it when the transfer is shutdown.
* @param[in] aBDXHandlers A structure of BDX callback handlers to be called during the transfer
* @param[in] aFileDesignator The file designator for the file that will be transferred.
* @param[in] anAppState An application-specific state object to be attached to the
* BDXTransfer for use by the user application and associated callbacks.
* @param[in] aXfer A passed-by-reference pointer that will point to the
* new BDXTransfer object if one is available, else NULL.
*
*
* @retval #WEAVE_NO_ERROR If we successfully found a new BDXTransfer.
* @retval #WEAVE_ERROR_TOO_MANY_CONNECTIONS If too many transfers are currently active (aXfer is NULL)
* @retval #WEAVE_ERROR_INCORRECT_STATE If ec is NULL, closed, or otherwise invalid for a transfer
*/
WEAVE_ERROR BdxNode::NewTransfer(ExchangeContext *anEc, BDXHandlers aBDXHandlers,
ReferencedString &aFileDesignator, void * anAppState,
BDXTransfer * &aXfer)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
aXfer = NULL;
err = InitTransfer(anEc, aXfer);
SuccessOrExit(err);
// set up the application-specific state
aXfer->mFileDesignator = aFileDesignator;
aXfer->mAppState = anAppState;
// install application-specifc handlers
aXfer->SetHandlers(aBDXHandlers);
exit:
if (err != WEAVE_NO_ERROR)
{
if (aXfer != NULL)
{
aXfer->Shutdown();
}
else
{
// Transfer object uninitialized, so we do this manually
if (anEc != NULL)
{
anEc->Close();
anEc = NULL;
}
}
}
return err;
}
/**
* @brief
* Get and set up a new BDXTransfer from transfer pool if available,
* or set to NULL otherwise and return an error.
*
* @note
* You likely will not be using this method directly as you'll want to
* configure the BDXTransfer's parameters at the same time.
* @note
* You MUST shutdown this transfer in order to release it to the free pool!
*
* @param[in] anEc The ExchangeContext with the node we will initiate the transfer with.
* The server will handle closing it when the transfer is shutdown.
* @param[in] aXfer A passed-by-reference pointer that will point to the
* new BDXTransfer object if one is available, else NULL.
*
* @retval #WEAVE_NO_ERROR If we successfully found a new BDXTransfer.
* @retval #WEAVE_ERROR_TOO_MANY_CONNECTIONS If too many transfers are currently active (aXfer is NULL)
* @retval #WEAVE_ERROR_INCORRECT_STATE If ec is NULL, closed, or otherwise invalid for a transfer
*/
WEAVE_ERROR BdxNode::InitTransfer(ExchangeContext *anEc,
BDXTransfer * &aXfer)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
//TODO: check if there is already an ongoing BDX Transfer on this ExchangeContext:
//only one is allowed at a time according to the BDX specification
VerifyOrExit(anEc != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(!anEc->IsConnectionClosed(), err = WEAVE_ERROR_INCORRECT_STATE);
err = AllocTransfer(aXfer);
SuccessOrExit(err);
// Hang new BDXTransfer on exchange context so we can access the xfer object
// in the static handlers fired in response to incoming messages on the exchange
anEc->AppState = aXfer;
// Handle timeouts and other errors on this exchange
if (anEc->ResponseTimeout == 0)
{
anEc->ResponseTimeout = BDX_RESPONSE_TIMEOUT_MS;
}
anEc->OnResponseTimeout = BdxProtocol::HandleResponseTimeout;
anEc->OnConnectionClosed = BdxProtocol::HandleConnectionClosed;
anEc->OnKeyError = BdxProtocol::HandleKeyError;
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
anEc->OnSendError = BdxProtocol::HandleSendError;
#endif // #if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
// Initialize xfer struct
aXfer->mExchangeContext = anEc;
exit:
return err;
}
/**
* @brief
* Shutdown the given transfer object and return it to pool.
* This simply defers to BDXTransfer::Shutdown()
*
* @param[in] aXfer The BDXTransfer to shut down
*/
void BdxNode::ShutdownTransfer(BDXTransfer *aXfer)
{
aXfer->Shutdown();
}
/**
* @brief
* Use to enable/disable the BDX server without fully shutting it down and restarting.
*
* @param[in] aEnable Enable (true) or disable (false)
*/
void BdxNode::AllowBdxTransferToRun(bool aEnable)
{
mIsBdxTransferAllowed = aEnable;
}
/**
* @brief
* Returns true if the BDX server is allowed to start a transfer at this time, false otherwise.
*
* @return true if BDX transfer is allowed, false if not
*/
bool BdxNode::CanBdxTransferRun(void)
{
return mIsBdxTransferAllowed;
}
/**
* @brief
* Returns true if this BdxNode has already been initialized.
*
* @return true if this object has been initalized
*/
bool BdxNode::IsInitialized(void)
{
return mInitialized;
}
#if WEAVE_CONFIG_BDX_CLIENT_RECEIVE_SUPPORT
/**
* @brief
* Initializes a BdxReceive transfer with the specified parameters and passes it
* to BdxProtocol::InitBdxReceive to send actual ReceiveInit message.
* You must first establish a BDXTransfer object via NewTransfer() and configure
* it appropriately.
*
* @param[in] aXfer The initiated and configured transfer state object to
* use for this transfer
* @param[in] aICanDrive True if the initiator should propose that it can drive,
* false otherwise
* @param[in] aUCanDrive True if the initiator should propose that the sender can drive,
* false otherwise
* @param[in] aAsyncOk True if the initiator should propose using async transfer
* @param[in] aMetaData (optional) TLV metadata
*
* @retval #WEAVE_NO_ERROR If successful
* @retval #WEAVE_ERROR_INCORRECT_STATE If BDXTransfer can't run right now
* @retval #WEAVE_ERROR_NO_MEMORY BdxProtocol::InitBdxReceive failed to init
*/
WEAVE_ERROR BdxNode::InitBdxReceive(BDXTransfer &aXfer, bool aICanDrive, bool aUCanDrive,
bool aAsyncOk, ReferencedTLVData *aMetaData)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(CanBdxTransferRun(),
err = WEAVE_ERROR_INCORRECT_STATE; WeaveLogDetail(BDX, "InitBdxReceive called but server cannot run currently"));
aXfer.mAmInitiator = true;
aXfer.mAmSender = false;
// Arrange for messages in this exchange to go to our response handler.
// NOTE: we may want to set different handlers for e.g., Receive, Send in the future
aXfer.mExchangeContext->OnMessageReceived = BdxProtocol::HandleResponse;
err = BdxProtocol::InitBdxReceive(aXfer, aICanDrive, aUCanDrive, aAsyncOk, aMetaData);
exit:
return err;
}
#endif // WEAVE_CONFIG_BDX_CLIENT_RECEIVE_SUPPORT
#if WEAVE_CONFIG_BDX_CLIENT_SEND_SUPPORT
/**
* @brief
* Initializes a BdxSend transfer with the specified parameters and passes it
* to BdxProtocol::InitBdxSend to send actual SendInit message.
* You must first establish a BDXTransfer object via NewTransfer() and configure
* it appropriately.
*
* @param[in] aXfer The initiated and configured transfer state object to
* use for this transfer
* @param[in] aICanDrive True if the initiator should propose that it can drive,
* false otherwise
* @param[in] aUCanDrive True if the initiator should propose that the sender can drive,
* false otherwise
* @param[in] aAsyncOk True if the initiator should propose using async transfer
* @param[in] aMetaData (optional) TLV metadata
*
* @retval #WEAVE_NO_ERROR If successful
* @retval #WEAVE_ERROR_INCORRECT_STATE If BDXTransfer can't run right now
* @retval #WEAVE_ERROR_NO_MEMORY BdxProtocol::InitBdxSend failed to init
*/
WEAVE_ERROR BdxNode::InitBdxSend(BDXTransfer &aXfer, bool aICanDrive, bool aUCanDrive,
bool aAsyncOk, ReferencedTLVData *aMetaData)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(CanBdxTransferRun(),
err = WEAVE_ERROR_INCORRECT_STATE; WeaveLogDetail(BDX, "InitBdxSend called but server cannot run currently"));
aXfer.mAmInitiator = true;
aXfer.mAmSender = true;
// Arrange for messages in this exchange to go to our response handler.
aXfer.mExchangeContext->OnMessageReceived = BdxProtocol::HandleResponse;
err = BdxProtocol::InitBdxSend(aXfer, aICanDrive, aUCanDrive, aAsyncOk, aMetaData);
exit:
return err;
}
/**
* @brief
* Initializes a BdxSend transfer with the specified parameters and passes it
* to BdxProtocol::InitBdxSend to send actual SendInit message.
* You must first establish a BDXTransfer object via NewTransfer() and configure
* it appropriately.
*
* @param[in] aXfer The initiated and configured transfer state object to
* use for this transfer
* @param[in] aICanDrive True if the initiator should propose that it can drive,
* false otherwise
* @param[in] aUCanDrive True if the initiator should propose that the sender can drive,
* false otherwise
* @param[in] aAsyncOk True if the initiator should propose using async transfer
* @param[in] aMetaDataWriteCallback (optional) Function to write TLV metadata
* @param[in] aMetaDataAppState (optional) AppState for aMetaDataWriteCallback
*
* @retval #WEAVE_NO_ERROR If successful
* @retval #WEAVE_ERROR_INCORRECT_STATE If BDXTransfer can't run right now
* @retval #WEAVE_ERROR_NO_MEMORY BdxProtocol::InitBdxSend failed to init
*/
WEAVE_ERROR BdxNode::InitBdxSend(BDXTransfer &aXfer, bool aICanDrive, bool aUCanDrive,
bool aAsyncOk, SendInit::MetaDataTLVWriteCallback aMetaDataWriteCallback,
void *aMetaDataAppState)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(CanBdxTransferRun(),
err = WEAVE_ERROR_INCORRECT_STATE; WeaveLogDetail(BDX, "InitBdxSend called but server cannot run currently"));
aXfer.mAmInitiator = true;
aXfer.mAmSender = true;
// Arrange for messages in this exchange to go to our response handler.
aXfer.mExchangeContext->OnMessageReceived = BdxProtocol::HandleResponse;
err = BdxProtocol::InitBdxSend(aXfer, aICanDrive, aUCanDrive, aAsyncOk, aMetaDataWriteCallback, aMetaDataAppState);
exit:
return err;
}
#endif // WEAVE_CONFIG_BDX_CLIENT_SEND_SUPPORT
#if WEAVE_CONFIG_BDX_SERVER_SUPPORT
/**
* @brief
* Allow unsolicited ReceiveInit messages to be handled by the specified handlers on this server.
*
* @param[in] aReceiveInitHandler A handler for ReceiveInit messages that configures a
* BDXTransfer object and determines whether to accept the
* transfer based on the parameters contained within the ReceiveInit message.
*
* @retval #WEAVE_NO_ERROR If successful
* @retval #WEAVE_ERROR_INCORRECT_STATE If mExchangeMgr hasn't been initialized
* @retval other A call to register/unregister a message handler failed
*/
WEAVE_ERROR BdxNode::AwaitBdxReceiveInit(ReceiveInitHandler aReceiveInitHandler)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(mExchangeMgr != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
// Register callback if specified
if (aReceiveInitHandler != NULL)
{
err = mExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_BDX, kMsgType_ReceiveInit, HandleReceiveInit, this);
SuccessOrExit(err);
mReceiveInitHandler = aReceiveInitHandler;
}
// Otherwise, unregister any currently set one
else
{
err = mExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_BDX, kMsgType_ReceiveInit);
}
exit:
return err;
}
/**
* @brief
* Allow unsolicited SendInit messages to be handled by the specified handlers on this server.
*
* @param[in] aSendInitHandler A handler for SendInit messages that configures a
* BDXTransfer object and determines whether to accept the
* transfer based on the parameters contained within the SendInit message.
*
* @retval #WEAVE_NO_ERROR If successful
* @retval #WEAVE_ERROR_INCORRECT_STATE If mExchangeMgr hasn't been initialized
* @retval other A call to register/unregister a message handler failed
*/
WEAVE_ERROR BdxNode::AwaitBdxSendInit(SendInitHandler aSendInitHandler)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(mExchangeMgr != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
if (aSendInitHandler != NULL)
{
err = mExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_BDX, kMsgType_SendInit, HandleSendInit, this);
SuccessOrExit(err);
mSendInitHandler = aSendInitHandler;
}
else
{
err = mExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_BDX, kMsgType_SendInit);
}
exit:
return err;
}
/**
* @brief
* Handler for ReceiveInit messages that parses the incoming message, grabs a
* BDXTransfer object (if one is available), passes the ReceiveInit object
* to the previously specified ReceiveInitHandler, which will set up
* the BDXTransfer object and determine if the ReceiveInit should be accepted,
* in which case the handler sends the appropriate response. If anything fails
* in this function, we send a reject to tell the initiator that we can't accept.
*
* @note
* statusCode tracks the BDXProfile Status Error code (to be transferred in case of failure),
* while err tracks our internal WEAVE_ERROR, which should never be transmitted.
*
* @param[in] anEc ExchangeContext to be associated with this receive init
* @param[in] aPktInfo Unused here, but needs to match function prototype
* @param[in] aWeaveMsgInfo Weave Message Information for the message
* @param[in] aProfileId ID of the profile under which the message is defined
* @param[in] aMessageType The message type of that profile
* @param[in] aPayloadReceiveInit The packed message itself
*/
void BdxNode::HandleReceiveInit(ExchangeContext *anEc, const IPPacketInfo *aPktInfo,
const WeaveMessageInfo *aWeaveMsgInfo,
uint32_t aProfileId, uint8_t aMessageType,
PacketBuffer *aPayloadReceiveInit)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint16_t statusCode = kStatus_Success;
BdxNode *bdxApp = NULL;
BDXTransfer *xfer = NULL;
bool success = false;
ReceiveInit receiveInit;
// Set version early in case we exit before parsing. This allows us to send
// the reject corresponding to the BDX version we're using.
receiveInit.mVersion = WEAVE_CONFIG_BDX_VERSION;
WeaveLogDetail(BDX, "HandleReceiveInit entering");
VerifyOrExit(anEc != NULL, err = WEAVE_ERROR_INCORRECT_STATE; statusCode = kStatus_ServerBadState);
VerifyOrExit(aProfileId == kWeaveProfile_BDX, err = WEAVE_ERROR_INVALID_PROFILE_ID; statusCode = kStatus_BadMessageContents);
VerifyOrExit(aMessageType == kMsgType_ReceiveInit, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE; statusCode = kStatus_BadMessageContents);
// Parse init request and discard payload buffer
err = ReceiveInit::parse(aPayloadReceiveInit, receiveInit);
VerifyOrExit(err == WEAVE_NO_ERROR,
statusCode = kStatus_BadMessageContents;
WeaveLogDetail(BDX, "Error: HandleReceiveInit: Unable to parse Receive Init request: %d", err));
PacketBuffer::Free(aPayloadReceiveInit);
aPayloadReceiveInit = NULL;
bdxApp = static_cast<BdxNode *>(anEc->AppState);
VerifyOrExit(bdxApp->CanBdxTransferRun(),
err = WEAVE_ERROR_INCORRECT_STATE; statusCode = kStatus_ServerBadState);
// Grab BDXTransfer object for this transfer
err = bdxApp->InitTransfer(anEc, xfer);
VerifyOrExit(err == WEAVE_NO_ERROR && xfer != NULL,
err = WEAVE_ERROR_NO_MEMORY; statusCode = kStatus_ServerBadState);
// Configure xfer object
xfer->mAmInitiator = false;
xfer->mIsAccepted = false;
xfer->mAmSender = true;
xfer->mMaxBlockSize = receiveInit.mMaxBlockSize;
xfer->mVersion = (receiveInit.mVersion > WEAVE_CONFIG_BDX_VERSION) ? WEAVE_CONFIG_BDX_VERSION : receiveInit.mVersion;
// Verify we have a legitimate block size or reject
VerifyOrExit(receiveInit.mMaxBlockSize > 0,
err = WEAVE_ERROR_INVALID_ARGUMENT; statusCode = kStatus_BadRequest);
VerifyOrExit(receiveInit.mFileDesignator.theLength > 0,
err = WEAVE_ERROR_INVALID_ARGUMENT; statusCode = kStatus_BadRequest);
// Fire application callback to validate request and setup transfer
// Application should set the transfer mode and accept the transfer.
VerifyOrExit(bdxApp->mReceiveInitHandler,
err = WEAVE_ERROR_NO_MESSAGE_HANDLER; statusCode = kStatus_ServerBadState);
statusCode = bdxApp->mReceiveInitHandler(xfer, &receiveInit);
VerifyOrExit(statusCode == kStatus_Success, err = WEAVE_ERROR_INCORRECT_STATE);
// Validate the requested transfer mode
VerifyOrExit(!(((xfer->mTransferMode == kMode_ReceiverDrive) && !receiveInit.mReceiverDriveSupported) ||
((xfer->mTransferMode == kMode_SenderDrive) && !receiveInit.mSenderDriveSupported) ||
((xfer->mTransferMode == kMode_Asynchronous))),
//TODO: merge this up one line when async supported: && !receiveInit.mAsynchronousModeSupported)
err = WEAVE_ERROR_INVALID_TRANSFER_MODE; statusCode = kStatus_ServerBadState);
// TODO: validate max block size? anything else?
WeaveLogDetail(BDX, "HandleReceiveInit validated request\n");
err = SendReceiveAccept(anEc, xfer);
VerifyOrExit(err == WEAVE_NO_ERROR, statusCode = kStatus_FailureToSend);
WeaveLogDetail(BDX, "HandleReceiveInit exiting (success)\n");
success = true;
exit:
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(BDX, "HandleReceiveInit exiting (failure = %d)", err);
}
if (aPayloadReceiveInit)
{
PacketBuffer::Free(aPayloadReceiveInit);
}
if (!success)
{
if (statusCode != kStatus_BadMessageContents)
{
// Send a reject if we didn't succeed at handling the receive init and
// it was not due to bad message contents.
err = SendReject(anEc, receiveInit.mVersion, statusCode, kMsgType_ReceiveReject);
if (err != WEAVE_NO_ERROR)
{
WeaveLogDetail(BDX, "Sending reject message failed : %d", err);
}
}
if (xfer != NULL)
{
xfer->Shutdown();
}
else
{
// Transfer object uninitialized, so we do this manually
if (anEc != NULL)
{
if (anEc->Con != NULL)
{
anEc->Con->Close();
anEc->Con = NULL;
}
anEc->Close();
anEc = NULL;
}
}
}
}
/**
* @brief
* Handler for SendInit messages that parses the incoming message, grabs a
* BDXTransfer object (if one is available), passes the SendInit object
* to the previously specified SendInitHandler, which will set up
* the BDXTransfer object and determine if the SendInit should be accepted,
* in which case the handler sends the appropriate response. If anything fails
* in this function, we send a reject to tell the initiator that we can't accept.
*
* @note
* statusCode tracks the BDXProfile Status Error code (to be transferred in case of failure),
* while err tracks our internal WEAVE_ERROR, which should never be transmitted.
*
* @param[in] anEc ExchangeContext to be associated with this send init
* @param[in] aPktInfo Unused here, but needs to match function prototype
* @param[in] aWeaveMsgInfo Weave Message Information for the message
* @param[in] aProfileId ID of the profile under which the message is defined
* @param[in] aMessageType The message type of that profile
* @param[in] aPayload The packed message itself
*/
//TODO: pull in documentation from WeaveExchangeMgr.h::MessageReceiveFunct
void BdxNode::HandleSendInit(ExchangeContext *anEc, const IPPacketInfo *aPktInfo,
const WeaveMessageInfo *aWeaveMsgInfo,
uint32_t aProfileId, uint8_t aMessageType,
PacketBuffer *aPayload)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint16_t statusCode = kStatus_Success;
BdxNode * bdxApp;
BDXTransfer *xfer = NULL;
bool success = false;
SendInit sendInit;
// Set version early in case we exit before parsing. This allows us to send
// the reject corresponding to the BDX version we're using.
sendInit.mVersion = WEAVE_CONFIG_BDX_VERSION;
WeaveLogDetail(BDX, "HandleSendInit entering");
VerifyOrExit(anEc != NULL, err = WEAVE_ERROR_INCORRECT_STATE; statusCode = kStatus_ServerBadState);
VerifyOrExit(aProfileId == kWeaveProfile_BDX, err = WEAVE_ERROR_INVALID_PROFILE_ID; statusCode = kStatus_BadMessageContents);
VerifyOrExit(aMessageType == kMsgType_SendInit, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE; statusCode = kStatus_BadMessageContents);
// Parse init request and discard payload buffer
err = SendInit::parse(aPayload, sendInit);
VerifyOrExit(err == WEAVE_NO_ERROR,
statusCode = kStatus_BadMessageContents;
WeaveLogDetail(BDX, "Error: HandleSendInit: Unable to parse Send Init request: %d", err));
PacketBuffer::Free(aPayload);
aPayload = NULL;
bdxApp = static_cast<BdxNode *>(anEc->AppState);
VerifyOrExit(bdxApp->CanBdxTransferRun(),
err = WEAVE_ERROR_INCORRECT_STATE; statusCode = kStatus_ServerBadState);
// Grab BDXTransfer object for this transfer
err = bdxApp->InitTransfer(anEc, xfer);
VerifyOrExit(err == WEAVE_NO_ERROR && xfer != NULL,
err = WEAVE_ERROR_NO_MEMORY; statusCode = kStatus_ServerBadState);
// Configure xfer object
xfer->mIsAccepted = false;
xfer->mMaxBlockSize = sendInit.mMaxBlockSize;
xfer->mAmInitiator = false;
xfer->mAmSender = false;
xfer->mVersion = (sendInit.mVersion > WEAVE_CONFIG_BDX_VERSION) ? WEAVE_CONFIG_BDX_VERSION : sendInit.mVersion;
// Fire application callback to validate request and setup transfer
// Application should set the transfer mode and accept the transfer.
VerifyOrExit(bdxApp->mSendInitHandler,
err = WEAVE_ERROR_NO_MESSAGE_HANDLER; statusCode = kStatus_ServerBadState);
statusCode = bdxApp->mSendInitHandler(xfer, &sendInit);
VerifyOrExit(statusCode == kStatus_Success, err = WEAVE_ERROR_INCORRECT_STATE);
// Validate the requested transfer mode
VerifyOrExit(!(((xfer->mTransferMode == kMode_ReceiverDrive) && !sendInit.mReceiverDriveSupported) ||
((xfer->mTransferMode == kMode_SenderDrive) && !sendInit.mSenderDriveSupported) ||
((xfer->mTransferMode == kMode_Asynchronous))),
//TODO: merge this up one line when async supported: && !sendInit.mAsynchronousModeSupported)
err = WEAVE_ERROR_INVALID_TRANSFER_MODE; statusCode = kStatus_ServerBadState);
WeaveLogDetail(BDX, "HandleSendInit validated request\n");
err = SendSendAccept(anEc, xfer);
VerifyOrExit(err == WEAVE_NO_ERROR, statusCode = kStatus_FailureToSend);
WeaveLogDetail(BDX, "HandleSendInit exiting (success)\n");
success = true;
exit:
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(BDX, "HandleSendInit exiting on failure: %s\n", ErrorStr(err));
}
if (aPayload)
{
PacketBuffer::Free(aPayload);
}
if (!success)
{
if (statusCode != kStatus_BadMessageContents)
{
// Send a reject if we didn't succeed at handling the receive init
err = SendReject(anEc, sendInit.mVersion, statusCode, kMsgType_SendReject);
if (err != WEAVE_NO_ERROR)
{
WeaveLogDetail(BDX, "Sending reject message failed : %d", err);
}
}
if (xfer != NULL)
{
xfer->Shutdown();
}
else
{
// Transfer object uninitialized, so we do this manually
if (anEc != NULL)
{
if (anEc->Con != NULL)
{
anEc->Con->Close();
anEc->Con = NULL;
}
anEc->Close();
anEc = NULL;
}
}
}
}
/**
* Function to send a reject message with the specified error.
*/
WEAVE_ERROR BdxNode::SendReject(ExchangeContext *anEc, uint8_t aVersion, uint16_t anErr, uint8_t aMsgType)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
StatusReport rejectStatus;
PacketBuffer* responsePayload = NULL;
uint16_t flags = 0;
VerifyOrExit(aMsgType == kMsgType_SendReject || aMsgType == kMsgType_ReceiveReject,
err = WEAVE_ERROR_INVALID_ARGUMENT);
WeaveLogDetail(BDX, "%s sending Reject due to error: %d", __FUNCTION__, anErr);
err = rejectStatus.init(kWeaveProfile_BDX, anErr);
VerifyOrExit(err == WEAVE_NO_ERROR,
WeaveLogDetail(BDX, "SendReject error calling Init on rejectStatus: %d", err));
responsePayload = PacketBuffer::New();
VerifyOrExit(responsePayload != NULL,
err = WEAVE_ERROR_NO_MEMORY; WeaveLogDetail(BDX, "SendReject couldn't grab PacketBuffer"));
err = rejectStatus.pack(responsePayload);
VerifyOrExit(err == WEAVE_NO_ERROR,
WeaveLogDetail(BDX, "SendReject couldn't pack rejectStatus: %d", err));
flags = GetBDXAckFlag(anEc);
if (aVersion == 0)
{
err = anEc->SendMessage(kWeaveProfile_BDX, aMsgType, responsePayload, flags);
}
else if (aVersion == 1)
{
err = anEc->SendMessage(kWeaveProfile_Common, Common::kMsgType_StatusReport, responsePayload, flags);
}
else
{
err = WEAVE_ERROR_UNSUPPORTED_MESSAGE_VERSION;
}
VerifyOrExit(err == WEAVE_NO_ERROR,
WeaveLogDetail(BDX, "SendReject error sending reject message: %d", err));
responsePayload = NULL;
exit:
if (responsePayload)
{
PacketBuffer::Free(responsePayload);
}
return err;
}
/**
* Function to send a receive accept for the given transfer
*/
WEAVE_ERROR BdxNode::SendReceiveAccept(ExchangeContext *anEc, BDXTransfer *aXfer)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ReceiveAccept receiveAccept;
PacketBuffer* payload = NULL;
uint16_t flags;
// Send a ReceiveAccept response back to the receiver.
err = receiveAccept.init(aXfer->mVersion, aXfer->mTransferMode, aXfer->mMaxBlockSize, aXfer->mLength, NULL);
VerifyOrExit(err == WEAVE_NO_ERROR,
WeaveLogDetail(BDX, "SendReceiveAccept error calling Init on receiveAccept: %d", err));
payload = PacketBuffer::New();
VerifyOrExit(payload != NULL,
err = WEAVE_ERROR_NO_MEMORY;
WeaveLogDetail(BDX, "SendReceiveAccept error grabbing PacketBuffer"));
err = receiveAccept.pack(payload);
VerifyOrExit(err == WEAVE_NO_ERROR,
WeaveLogDetail(BDX, "SendReceiveAccept error packing receiveAccept : %d\n", err));
// Set ourselves up to handle first BlockQueryRequest.
anEc->OnMessageReceived = BdxProtocol::HandleResponse;
// Expect a response if we are not the driver
flags = aXfer->GetDefaultFlags(!aXfer->IsDriver());
err = anEc->SendMessage(kWeaveProfile_BDX, kMsgType_ReceiveAccept, payload, flags);
payload = NULL;
VerifyOrExit(err == WEAVE_NO_ERROR, WeaveLogDetail(BDX, "SendReceiveAccept error sending accept message: %d", err));
// Send first block if we're driving
if (aXfer->IsDriver())
{
WeaveLogDetail(BDX, "ReceiveAccept sent: Am driving so sending first block");
if (aXfer->mVersion == 1)
{
err = BdxProtocol::SendNextBlockV1(*aXfer);
}
#if WEAVE_CONFIG_BDX_V0_SUPPORT
else if (aXfer->mVersion == 0)
{
err = BdxProtocol::SendNextBlock(*aXfer);
}
#endif // WEAVE_CONFIG_BDX_V0_SUPPORT
else
{
err = WEAVE_ERROR_UNSUPPORTED_MESSAGE_VERSION;
}
VerifyOrExit(err == WEAVE_NO_ERROR,
WeaveLogDetail(BDX, "Error sending first block in SendReceiveAccept: %d", err));
}
exit:
if (payload)
{
PacketBuffer::Free(payload);
}
return err;
}
/**
* Function to send a send accept for the given transfer
*/
WEAVE_ERROR BdxNode::SendSendAccept(ExchangeContext *anEc, BDXTransfer *aXfer)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
SendAccept sendAccept;
PacketBuffer* payload = NULL;
uint16_t flags;
// Send a ReceiveAccept response back to the receiver.
err = sendAccept.init(aXfer->mVersion, aXfer->mTransferMode, aXfer->mMaxBlockSize, NULL);
VerifyOrExit(err == WEAVE_NO_ERROR,
WeaveLogDetail(BDX, "SendSendAccept error calling Init on sendAccept: %d", err));
payload = PacketBuffer::New();
VerifyOrExit(payload != NULL,
err = WEAVE_ERROR_NO_MEMORY;
WeaveLogDetail(BDX, "SendSendAccept error grabbing PacketBuffer"));
err = sendAccept.pack(payload);
VerifyOrExit(err == WEAVE_NO_ERROR,
WeaveLogDetail(BDX, "SendSendAccept error packing sendAccept : %d\n", err));
// Set ourselves up to handle first BlockQueryRequest.
anEc->OnMessageReceived = BdxProtocol::HandleResponse;
// Expect a response if we are not the driver
flags = aXfer->GetDefaultFlags(!aXfer->IsDriver());
err = anEc->SendMessage(kWeaveProfile_BDX, kMsgType_SendAccept, payload, flags);
payload = NULL;
VerifyOrExit(err == WEAVE_NO_ERROR,
WeaveLogDetail(BDX, "SendSendAccept error sending accept message: %d", err));
// Send a block query if we're driving
if (aXfer->IsDriver())
{
WeaveLogDetail(BDX, "SendAccept sent: Am driving so sending first block query");
if (aXfer->mVersion == 1)
{
err = BdxProtocol::SendBlockQueryV1(*aXfer);
}
#if WEAVE_CONFIG_BDX_V0_SUPPORT
else if (aXfer->mVersion == 0)
{
err = BdxProtocol::SendBlockQuery(*aXfer);
}
#endif // WEAVE_CONFIG_BDX_V0_SUPPORT
else
{
err = WEAVE_ERROR_UNSUPPORTED_MESSAGE_VERSION;
}
VerifyOrExit(err == WEAVE_NO_ERROR,
WeaveLogDetail(BDX, "Error sending first block query in SendSendAccept: %d", err));
}
exit:
if (payload)
{
PacketBuffer::Free(payload);
}
return err;
}
#endif // WEAVE_CONFIG_BDX_SERVER_SUPPORT
} // namespace BulkDataTransfer
} // namespace Profiles
} // namespace Weave
} // namespace nl