blob: bc74155ce6fbc06060b83e97e947a3a21d21b721 [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 a process to effect a functional test for
* a client for the Weave Software Update (SWU) profile.
*
*/
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <inttypes.h>
#include "ToolCommon.h"
#include <Weave/Core/WeaveSecurityMgr.h>
#include <Weave/Profiles/security/WeaveSecurity.h>
#include "nlweaveswuclient.h"
#include "MockIAServer.h"
using nl::StatusReportStr;
using namespace nl::Weave::Profiles;
using namespace nl::Weave::Profiles::Security;
#define TOOL_NAME "weave-swu-client"
static bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg);
static bool HandleNonOptionArgs(const char *progName, int argc, char *argv[]);
static void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con);
static void StartClientConnection();
static void HandleConnectionComplete(WeaveConnection *con, WEAVE_ERROR conErr);
static void HandleConnectionClosed(WeaveConnection *con, WEAVE_ERROR conErr);
bool Listening = false;
bool UseTCP = true;
bool Debug = false;
const char *DestIPAddrStr = NULL;
uint16_t DestPort; // only used for UDP
SoftwareUpdateClient SWUClient;
MockImageAnnounceServer MIAServer;
//Globals used by SWU-client
bool WaitingForSWUResp = false;
static uint64_t DestNodeId = 1;
IPAddress DestIPAddr;
WeaveConnection *Con = NULL;
static OptionDef gToolOptionDefs[] =
{
{ "listen", kNoArgument, 'L' },
{ "dest-addr", kArgumentRequired, 'D' },
{ "debug", kArgumentRequired, 'd' },
{ "tcp", kNoArgument, 't' },
{ "udp", kNoArgument, 'u' },
{ }
};
static const char *gToolOptionHelp =
" -D, --dest-addr <host>[:<port>]\n"
" Send an ImageQuery request to a specific address rather than one\n"
" derived from the destination node id. <host> can be a hostname,\n"
" an IPv4 address or an IPv6 address. If <port> is specified, ImageQuery\n"
" requests will be sent to the specified port.\n"
"\n"
" NOTE: When specifying a port with an IPv6 address, the IPv6 address\n"
" must be enclosed in brackets, e.g. [fd00:0:1:1::1]:11095.\n"
"\n"
" -t, --tcp\n"
" Use TCP to send SWU Requests. This is the default.\n"
"\n"
" -u, --udp\n"
" Use UDP to send SWU Requests.\n"
"\n"
" -L, --listen\n"
" Listen and respond to ImageAnnounce notifications sent from another node.\n"
"\n"
" -d, --debug\n"
" Enable debug messages.\n"
"\n";
static OptionSet gToolOptions =
{
HandleOption,
gToolOptionDefs,
"GENERAL OPTIONS",
gToolOptionHelp
};
static HelpOptions gHelpOptions(
TOOL_NAME,
"Usage: " TOOL_NAME " [<options...>] <dest-node-id>[@<dest-host>[:<dest-port>][%%<interface>]]\n"
" " TOOL_NAME " [<options...>] --listen\n",
WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT
);
static OptionSet *gToolOptionSets[] =
{
&gToolOptions,
&gNetworkOptions,
&gWeaveNodeOptions,
&gFaultInjectionOptions,
&gHelpOptions,
NULL
};
static void HandleImageAnnounce(ExchangeContext *ec)
{
WEAVE_ERROR err;
printf("0 SWU HandleImageAnnounce entering\n");
if (Listening)
{
printf("1 SWU HandleImageAnnounce (while listening, Con: %p)\n", Con);
if (Con != NULL)
{
printf("2 SWU HandleImageAnnounce Sending TCP ImageQuery request\n");
err = SWUClient.SendImageQueryRequest(Con);
}
else
{
char buffer[64];
printf("3 SWU HandleImageAnnounce (destIPAddr: %s (printed as a string))\n", DestIPAddr.ToString(buffer, strlen(buffer)));
err = SWUClient.SendImageQueryRequest(DestNodeId, DestIPAddr);
}
if (err == WEAVE_NO_ERROR)
{
WaitingForSWUResp = true;
}
else
{
printf("4 SWUClient.SendRequest() failed: %X\n", err);
Con->Close();
Con = NULL;
}
}
else
{
printf("5 SWU HandleImageAnnounce (while not listening)\n");
}
printf("6 SWU HandleImageAnnounce exiting\n");
}
int main(int argc, char *argv[])
{
WEAVE_ERROR err;
InitToolCommon();
SetSIGUSR1Handler();
if (argc == 1)
{
gHelpOptions.PrintBriefUsage(stderr);
exit(EXIT_FAILURE);
}
if (!ParseArgsFromEnvVar(TOOL_NAME, TOOL_OPTIONS_ENV_VAR_NAME, gToolOptionSets, NULL, true) ||
!ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets, HandleNonOptionArgs) ||
!ResolveWeaveNetworkOptions(TOOL_NAME, gWeaveNodeOptions, gNetworkOptions))
{
exit(EXIT_FAILURE);
}
InitSystemLayer();
InitNetwork();
InitWeaveStack(Listening || !UseTCP, true);
// Arrange to get called for various activity in the message layer.
MessageLayer.OnConnectionReceived = HandleConnectionReceived;
MessageLayer.OnReceiveError = HandleMessageReceiveError;
MessageLayer.OnAcceptError = HandleAcceptConnectionError;
// Initialize the SWU-client application.
err = SWUClient.Init(&ExchangeMgr);
if (err != WEAVE_NO_ERROR)
{
printf("SoftwareUpdateClient::Init failed: %s\n", ErrorStr(err));
exit(-1);
}
err = MIAServer.Init(&ExchangeMgr);
if (err != WEAVE_NO_ERROR)
{
printf("MockImageAnnounceServer::Init failed: %s\n", ErrorStr(err));
exit(-1);
}
MIAServer.OnImageAnnounceReceived = HandleImageAnnounce;
PrintNodeConfig();
if (!Listening)
{
if (DestNodeId == 0)
printf("Sending SWU requests to node at %s\n", DestIPAddrStr);
else if (DestIPAddrStr == NULL)
printf("Sending SWU requests to node %" PRIX64 "\n", DestNodeId);
else
printf("Sending SWU requests to node %" PRIX64 " at %s\n", DestNodeId, DestIPAddrStr);
//Set up connection and connect callbacks to handle success/failure cases
StartClientConnection();
}
else
{
if (!UseTCP)
{
MIAServer.CreateExchangeCtx(DestNodeId, DestIPAddr);
}
printf("Listening for ImageAnnounce notifications...\n");
}
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 100000;
ServiceNetwork(sleepTime);
}
if (Con != NULL)
{
Con->Close();
Con = NULL;
}
MIAServer.Shutdown();
SWUClient.Shutdown();
printf("Completed the SWU interactive protocol test!\n");
ShutdownWeaveStack();
ShutdownNetwork();
ShutdownSystemLayer();
return 0;
}
void StartClientConnection()
{
printf("0 StartClientConnection entering (Con: %p)\n", Con);
if (Con != NULL && Con->State == WeaveConnection::kState_Closed)
{
printf(" 1 remove previous con (currently closed)\n");
Con->Close();
Con = NULL;
}
// Create a new connection unless there is already one in progress
// (probably started via an ImageAnnounce notification.)
if (Con == NULL)
{
printf(" 2 no existing connection (probably no ImageAnnounce received)\n");
Con = MessageLayer.NewConnection();
if (Con == NULL)
{
printf(" 3 WeaveConnection.Connect failed: no memory\n");
return;
}
Con->OnConnectionComplete = HandleConnectionComplete;
Con->OnConnectionClosed = HandleConnectionClosed;
printf(" 4 Con: %p\n", Con);
printf(" 5 (DestNodeId: %ld, DestIPAddrStr: %s)\n", (long)DestNodeId, DestIPAddrStr);
IPAddress::FromString(DestIPAddrStr, DestIPAddr);
WEAVE_ERROR err = Con->Connect(DestNodeId, kWeaveAuthMode_Unauthenticated, DestIPAddr);
if (err != WEAVE_NO_ERROR)
{
printf(" 6 WeaveConnection.Connect failed: %X (%s)\n", err, ErrorStr(err));
Con->Close();
Con = NULL;
return;
}
}
else
{
printf(" 7 existing connection (probably ImageAnnounce received)\n");
HandleConnectionComplete(Con, WEAVE_NO_ERROR);
}
printf("8 StartClientConnection exiting\n");
}
bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg)
{
switch (id)
{
case 't':
UseTCP = true;
break;
case 'u':
UseTCP = false;
break;
case 'L':
Listening = true;
break;
case 'D':
DestIPAddrStr = arg;
break;
default:
PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
return false;
}
return true;
}
bool HandleNonOptionArgs(const char *progName, int argc, char *argv[])
{
if (argc > 0)
{
if (argc > 1)
{
PrintArgError("%s: Unexpected argument: %s\n", progName, argv[1]);
return false;
}
if (Listening)
{
PrintArgError("%s: Please specify either a node id or --listen\n", progName);
return false;
}
const char *nodeId = argv[0];
char *p = (char *)strchr(nodeId, '@');
if (p != NULL)
{
*p = 0;
DestIPAddrStr = p+1;
}
if (!ParseNodeId(nodeId, DestNodeId))
{
PrintArgError("%s: Invalid value specified for destination node-id: %s\n", progName, nodeId);
return false;
}
}
else
{
if (!Listening)
{
PrintArgError("%s: Please specify either a node id or --listen\n", progName);
return false;
}
}
return true;
}
void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con)
{
char ipAddrStr[64];
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
printf("Connection received from node %" PRIX64 " (%s)\n", con->PeerNodeId, ipAddrStr);
con->OnConnectionClosed = HandleConnectionClosed;
Con = con;
MIAServer.CreateExchangeCtx(con);
}
void HandleConnectionComplete(WeaveConnection *con, WEAVE_ERROR conErr)
{
printf("0 HandleConnectionComplete entering\n");
WEAVE_ERROR err;
char ipAddrStr[64];
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
if (conErr != WEAVE_NO_ERROR)
{
printf(" 1 Connection FAILED to node %" PRIX64 " (%s): %s\n", con->PeerNodeId, ipAddrStr, ErrorStr(conErr));
con->Close();
Con = NULL;
return;
}
printf(" 2 Connection established to node %" PRIX64 " (%s)\n", con->PeerNodeId, ipAddrStr);
printf(" 4 PacketBuffer for ImageQuery request\n");
if (Con != NULL)
{
printf(" 5 Sending TCP ImageQuery request\n");
err = SWUClient.SendImageQueryRequest(Con);
}
else
{
char buffer[64];
printf(" 6 (destIPAddr: %s (printed into a string))\n", DestIPAddr.ToString(buffer, strlen(buffer)));
err = SWUClient.SendImageQueryRequest(DestNodeId, DestIPAddr);
}
if (err == WEAVE_NO_ERROR)
{
WaitingForSWUResp = true;
}
else
{
printf("7 SWUClient.SendRequest() failed: %X\n", err);
Con->Close();
Con = NULL;
}
printf("8 HandleConnectionComplete exiting\n");
}
void HandleConnectionClosed(WeaveConnection *con, WEAVE_ERROR conErr)
{
char ipAddrStr[64];
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
if (conErr == WEAVE_NO_ERROR)
printf("Connection closed to node %" PRIX64 " (%s)\n", con->PeerNodeId, ipAddrStr);
else
printf("Connection ABORTED to node %" PRIX64 " (%s): %s\n", con->PeerNodeId, ipAddrStr, ErrorStr(conErr));
WaitingForSWUResp = false;
if (Listening)
con->Close();
else if (con == Con)
{
con->Close();
Con = NULL;
}
}