blob: 1283a58a6207023212071bb9aca9c32ae017af80 [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 unsolicited initiator (i.e., client)
* for the "legacy" (see [Managed Namespaces](@ref mns)) Weave
* Bulk Data Transfer (BDX) profile used for functional testing
* of the implementation of core message handlers and protocol
* engine for that profile.
*
*/
#include "ToolCommon.h"
#include "nlweavebdxclient.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
namespace nl {
namespace Weave {
namespace Profiles {
using namespace nl::Weave::Profiles::BulkDataTransfer;
BulkDataTransferClient::BulkDataTransferClient()
{
FabricState = NULL;
ExchangeMgr = NULL;
mBlockCounter = 0;
}
BulkDataTransferClient::~BulkDataTransferClient()
{
FabricState = NULL;
ExchangeMgr = NULL;
mBlockCounter = 0;
}
WEAVE_ERROR BulkDataTransferClient::Init(WeaveExchangeManager *exchangeMgr, const char* DestFileName)
{
// Error if already initialized.
if (ExchangeMgr != NULL)
return WEAVE_ERROR_INCORRECT_STATE;
ExchangeMgr = exchangeMgr;
FabricState = exchangeMgr->FabricState;
mBlockCounter = 0;
if (DestFileName)
{
DestFile = open(DestFileName, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (DestFile < 0)
return INET_MapOSError(errno);
}
return WEAVE_NO_ERROR;
}
void BulkDataTransferClient::setCon(WeaveConnection *con)
{
mCon = con;
}
WEAVE_ERROR BulkDataTransferClient::Shutdown()
{
ExchangeMgr = NULL;
FabricState = NULL;
mBlockCounter = 0;
return WEAVE_NO_ERROR;
}
/*
* ReceiveInit request
*/
WEAVE_ERROR BulkDataTransferClient::SendReceiveInitRequest(WeaveConnection *con)
{
// Discard any existing exchange context.
// Effectively we can only have one BDX exchange with a single node at any one time.
if (ExchangeCtx != NULL)
{
ExchangeCtx->Close();
ExchangeCtx = NULL;
}
// Create a new exchange context.
ExchangeCtx = ExchangeMgr->NewContext(con, this);
if (ExchangeCtx == NULL)
return WEAVE_ERROR_NO_MEMORY;
return SendReceiveInitRequest();
}
WEAVE_ERROR BulkDataTransferClient::SendReceiveInitRequest(uint64_t nodeId, IPAddress nodeAddr)
{
return SendReceiveInitRequest(nodeId, nodeAddr, WEAVE_PORT);
}
WEAVE_ERROR BulkDataTransferClient::SendReceiveInitRequest(uint64_t nodeId, IPAddress nodeAddr, uint16_t port)
{
// Discard any existing exchange context.
// Effectively we can only have one BDX exchange with a single node at any one time.
if (ExchangeCtx != NULL)
{
ExchangeCtx->Close();
ExchangeCtx = NULL;
}
if (nodeAddr == IPAddress::Any)
nodeAddr = FabricState->SelectNodeAddress(nodeId);
// Create a new exchange context.
ExchangeCtx = ExchangeMgr->NewContext(nodeId, nodeAddr, this);
if (ExchangeCtx == NULL)
return WEAVE_ERROR_NO_MEMORY;
return SendReceiveInitRequest();
}
#define BDX_MAX_BLOCK_SIZE 256
#define BDX_START_OFFSET 0
WEAVE_ERROR BulkDataTransferClient::SendReceiveInitRequest()
{
if (!ExchangeCtx)
return WEAVE_ERROR_INCORRECT_STATE;
printf("0 SendReceiveInitRequest entering\n");
// 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 = HandleReceiveInitResponse;
/*
* Build the ReceiveInit request
*/
ReferencedString aFileDesignator;
//NOTE: normally this URI would have been agreed upon with the SWU protocol
// Ex.: the SWU server returned "bdx://nestlabs/share/heatlink/data/firmware/production/heatlink.elf",
// so the file name is extracted and is sent to the BDX server.
char aFileDesignatorStr[] = "/var/log/heatlink.elf"; //FIXME: temporary hack for the purpose of testing
aFileDesignator.init((uint16_t)(sizeof(aFileDesignatorStr)-1), aFileDesignatorStr);
ReceiveInit receiveInit;
receiveInit.init(false /*SenderDrive*/, true /*ReceiverDrive*/, false /*AsynchMode*/, BDX_MAX_BLOCK_SIZE,
(uint32_t)BDX_START_OFFSET, (uint32_t)0 /*Length (zero means undefined length)*/, aFileDesignator, NULL /*MetaData*/);
PacketBuffer* receiveInitPayload = PacketBuffer::New();
receiveInit.pack(receiveInitPayload);
// Send a ReceiveInit request message. Discard the exchange context if the send fails.
WEAVE_ERROR err = ExchangeCtx->SendMessage(kWeaveProfile_BDX, kMsgType_ReceiveInit, receiveInitPayload);
receiveInitPayload = NULL;
if (err != WEAVE_NO_ERROR)
{
printf("1 SendReceiveInitRequest\n");
ExchangeCtx->Close();
ExchangeCtx = NULL;
mCon->Close();
mCon = NULL;
}
printf("2 SendReceiveInitRequest exiting\n");
return err;
}
void BulkDataTransferClient::HandleReceiveInitResponse(ExchangeContext *ec, const IPPacketInfo *packetInfo, const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payload)
{
printf("0 HandleReceiveInitResponse entering\n");
BulkDataTransferClient *bdxApp = (BulkDataTransferClient *)ec->AppState;
// Verify that the message is a ReceiveInit Response.
if (profileId != kWeaveProfile_BDX || (msgType != kMsgType_ReceiveAccept && msgType != kMsgType_ReceiveReject))
{
printf("1 HandleReceiveInitResponse\n");
// TODO: handle unexpected response
return;
}
// Verify that the exchange context matches our current context. Bail if not.
if (ec != bdxApp->ExchangeCtx)
{
printf("2 HandleReceiveInitResponse\n");
return;
}
if (msgType == kMsgType_ReceiveAccept)
{
printf("3 HandleReceiveInitResponse\n");
//Send BlockQuery request
bdxApp->SendBlockQueryRequest();
}
else if (msgType == kMsgType_ReceiveReject)
{
printf("4 HandleReceiveInitResponse\n");
bdxApp->mCon->Close();
bdxApp->mCon = NULL;
}
// Free the payload buffer.
PacketBuffer::Free(payload);
printf("5 HandleReceiveInitResponse exiting\n");
}
/*
* BlockQuery request
*/
WEAVE_ERROR BulkDataTransferClient::SendBlockQueryRequest()
{
printf("0 SendBlockQueryRequest entering\n");
if (!ExchangeCtx)
return WEAVE_ERROR_INCORRECT_STATE;
// 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 = HandleBlockQueryResponse;
/*
* Build the BlockQuery request
*/
BlockQuery blockQuery;
blockQuery.init(mBlockCounter++); //first block requested is zero (next will be one, next two, etc)
PacketBuffer* blockQueryPayload = PacketBuffer::New();
blockQuery.pack(blockQueryPayload);
// Send a BlockQuery request message. Discard the exchange context if the send fails.
WEAVE_ERROR err = ExchangeCtx->SendMessage(kWeaveProfile_BDX, kMsgType_BlockQuery, blockQueryPayload);
blockQueryPayload = NULL;
if (err != WEAVE_NO_ERROR)
{
printf("1 SendBlockQueryRequest\n");
ExchangeCtx->Close();
ExchangeCtx = NULL;
mCon->Close();
mCon = NULL;
}
printf("2 SendBlockQueryRequest exiting\n");
return err;
}
void BulkDataTransferClient::HandleBlockQueryResponse(ExchangeContext *ec, const IPPacketInfo *packetInfo, const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payload)
{
printf("0 HandleBlockQueryResponse entering\n");
BulkDataTransferClient *bdxApp = (BulkDataTransferClient *)ec->AppState;
// Verify that the message is a ReceiveInit Response.
if (profileId != kWeaveProfile_BDX || (msgType != kMsgType_BlockSend && msgType != kMsgType_BlockEOF))
{
printf("1 HandleBlockQueryResponse\n");
// TODO: handle unexpected response
return;
}
// Verify that the exchange context matches our current context. Bail if not.
if (ec != bdxApp->ExchangeCtx)
{
printf("2 HandleBlockQueryResponse\n");
return;
}
//dump received block/payload
// DumpMemory(payload->Start(), payload->DataLength(), "==> ", 16);
if (msgType == kMsgType_BlockSend)
{
printf("3 HandleBlockQueryResponse (BlockSend)\n");
DumpMemory(payload->Start(), payload->DataLength(), "--> ", 16);
if (bdxApp->DestFile > 0) {
// Write bulk data to disk.
int wtd = write(bdxApp->DestFile, payload->Start(), payload->DataLength());
printf("Wrote %d bytes to disk.\n", wtd);
}
//Send another BlockQuery
bdxApp->SendBlockQueryRequest();
}
else if (msgType == kMsgType_BlockEOF)
{
printf("4 HandleBlockQueryResponse (BlockEOF)\n");
DumpMemory(payload->Start(), payload->DataLength(), "==> ", 16);
if (bdxApp->DestFile > 0) {
// Close destination file.
close(bdxApp->DestFile);
}
//Send final BlockEOFAck
bdxApp->SendBlockEOFAck();
}
// Free the payload buffer.
PacketBuffer::Free(payload);
printf("5 HandleBlockQueryResponse exiting\n");
}
/*
* BlockEOFAck
*/
WEAVE_ERROR BulkDataTransferClient::SendBlockEOFAck()
{
printf("0 SendBlockEOFAck entering\n");
if (!ExchangeCtx)
return WEAVE_ERROR_INCORRECT_STATE;
// 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 = HandleBlockEOFAckResponse;
/*
* Build the BlockEOFAck
*/
BlockEOFAck blockEOFAck;
blockEOFAck.init(mBlockCounter-1); //final ack uses same block-counter of last block-query request
PacketBuffer* blockEOFAckPayload = PacketBuffer::New();
blockEOFAck.pack(blockEOFAckPayload);
// Send an BlockEOFAck request message. Discard the exchange context if the send fails.
WEAVE_ERROR err = ExchangeCtx->SendMessage(kWeaveProfile_BDX, kMsgType_BlockEOFAck, blockEOFAckPayload);
blockEOFAckPayload = NULL;
// if (err != WEAVE_NO_ERROR)
// {
ExchangeCtx->Close();
ExchangeCtx = NULL;
mCon->Close();
mCon = NULL;
// }
printf("1 SendBlockEOFAck exiting\n");
Done = true;
return err;
}
void BulkDataTransferClient::HandleBlockEOFAckResponse(ExchangeContext *ec, const IPPacketInfo *packetInfo, const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payload)
{
printf("A response to BlockEOFAck is NOT expected!\n");
}
} // namespace Profiles
} // namespace Weave
} // namespace nl