blob: 07b0da7bba125124f654d6e5d5a5f2c5a9344daf [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 command line tool, weave-key-export, for the
* Weave Key Export Protocol (Security Profile).
*
* The weave-kye-export tool implements a facility for acting as either
* the originator or responder for the key export request, with a
* variety of options.
*
*/
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <inttypes.h>
#include <limits.h>
#include <signal.h>
#include "ToolCommon.h"
#include <Weave/WeaveVersion.h>
#include <Weave/Core/WeaveSecurityMgr.h>
#include <Weave/Profiles/security/WeaveSecurity.h>
#include <Weave/Profiles/service-directory/ServiceDirectory.h>
#include <Weave/Support/crypto/WeaveCrypto.h>
#include <Weave/Support/TimeUtils.h>
#include <Weave/Support/WeaveFaultInjection.h>
using nl::StatusReportStr;
using namespace nl::Weave::Profiles::Security;
#define TOOL_NAME "weave-key-export"
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 DriveSending();
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);
static void HandleKeyExportComplete(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState, uint32_t exportedKeyId, const uint8_t *exportedKey, uint16_t exportedKeyLen);
static void HandleKeyExportError(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState, WEAVE_ERROR localErr, StatusReport *statusReport);
static void ParseDestAddress();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
static WEAVE_ERROR GetRootDirectoryEntry(uint8_t *, uint16_t);
static void HandleServiceMgrStatus(void *appState, WEAVE_ERROR anError, StatusReport *aReport);
#endif
int32_t MaxKeyExportCount = -1;
int32_t KeyExportInterval = 1000000;
bool UseTCP = true;
bool Debug = false;
bool SignKeyExportMsgs = true;
uint32_t ExportKeyId = WeaveKeyId::kClientRootKey;
uint64_t DestNodeId;
const char *DestAddr = NULL;
IPAddress DestIPAddr; // only used for UDP
uint16_t DestPort = WEAVE_PORT; // only used for UDP
InterfaceId DestIntf = INET_NULL_INTERFACEID; // only used for UDP
uint64_t LastKeyExportTime = 0;
bool WaitingForKeyExportResponse = false;
uint64_t KeyExportRequestCount = 0;
uint64_t KeyExportResponseCount = 0;
WeaveConnection *Con = NULL;
bool ClientConInProgress = false;
bool ClientConEstablished = false;
WeaveAuthMode AuthMode = kWeaveAuthMode_Unauthenticated;
uint8_t InitiatorKeyExportConfig = kKeyExportConfig_Unspecified;
uint32_t SenderBusyRespCount = 0;
const uint32_t MaxSenderBusyRespCount = 10;
// In case of SenderBusy, wait 10 seconds before trying again to establish a secure session
const uint64_t SenderBusyRespDelay = 10 * nl::kMicrosecondsPerSecond;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
bool UseServiceDir = false;
ServiceDirectory::WeaveServiceManager ServiceMgr;
uint8_t ServiceDirCache[300];
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
bool UseWRMP = false;
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
enum NameResolutionStateEnum
{
kNameResolutionState_NotStarted,
kNameResolutionState_InProgress,
kNameResolutionState_Complete
} NameResolutionState = kNameResolutionState_NotStarted;
enum
{
kToolOpt_UseServiceDir = 1000,
};
bool ParseKeyExportConfig(const char *str, uint8_t& output)
{
uint32_t configNum;
if (!ParseInt(str, configNum))
return false;
switch (configNum)
{
case 1:
output = kKeyExportConfig_Config1;
return true;
case 2:
output = kKeyExportConfig_Config2;
return true;
default:
return false;
}
}
static OptionDef gToolOptionDefs[] =
{
{ "dest-addr", kArgumentRequired, 'D' },
{ "key-id", kArgumentRequired, 'K' },
{ "key-export-config", kArgumentRequired, 'k' },
{ "dont-sign-msgs", kNoArgument, 'd' },
{ "count", kArgumentRequired, 'c' },
{ "interval", kArgumentRequired, 'i' },
{ "tcp", kNoArgument, 't' },
{ "udp", kNoArgument, 'u' },
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
{ "wrmp", kNoArgument, 'w' },
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
{ "service-dir", kNoArgument, kToolOpt_UseServiceDir },
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
{ NULL }
};
static const char *const gToolOptionHelp =
" -D, --dest-addr <host>[:<port>][%<interface>]\n"
" Send Key Export Requests 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, Key Export\n"
" Requests will be sent to the specified port. If <interface> is\n"
" specified, Key Export Requests will be sent over the specified local\n"
" interface.\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"
" -K, --key-id <num>\n"
" Identifier of the key to be exported. If not specified the client\n"
" root key is exported by default.\n"
"\n"
" -k, --key-export-config <num>\n"
" Proposed the specified key export configuration when initiating a key\n"
" export request. If not specified the dafult value provided by\n"
" WeaveSecurityManager is used.\n"
"\n"
" -d, --dont-sign-msgs\n"
" Don't sign Key Export Request/Response messages. If not specified,\n"
" by default the messages are signed with ECDSA signature using device\n"
" private key.\n"
"\n"
" -c, --count <num>\n"
" Send the specified number of Key Export Requests and exit.\n"
"\n"
" -i, --interval <ms>\n"
" Send Key Export Requests at the specified interval in milliseconds.\n"
"\n"
" -t, --tcp\n"
" Use TCP to send Key Export Requests. This is the default.\n"
"\n"
" -u, --udp\n"
" Use UDP to send Key Export Requests.\n"
"\n"
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
" -w, --wrmp\n"
" Use UDP with Weave reliable messaging to send Key Export Requests.\n"
"\n"
#endif
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
" --service-dir\n"
" Use service directory to lookup the destination node address.\n"
"\n"
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
;
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"
WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT,
"Send key export request and receive key export response messages.\n"
);
static OptionSet *gToolOptionSets[] =
{
&gToolOptions,
&gNetworkOptions,
&gWeaveNodeOptions,
&gWRMPOptions,
&gCASEOptions,
&gKeyExportOptions,
&gDeviceDescOptions,
&gServiceDirClientOptions,
&gFaultInjectionOptions,
&gHelpOptions,
NULL
};
static void ResetTestContext(void)
{
Done = false;
WaitingForKeyExportResponse = false;
KeyExportRequestCount = 0;
KeyExportResponseCount = 0;
SenderBusyRespCount = 0;
}
int main(int argc, char *argv[])
{
WEAVE_ERROR err;
nl::Weave::System::Stats::Snapshot before;
nl::Weave::System::Stats::Snapshot after;
const bool printStats = true;
uint32_t iter;
#if WEAVE_CONFIG_TEST
SetupFaultInjectionContext(argc, argv);
SetSignalHandler(DoneOnHandleSIGUSR1);
#endif
{
unsigned int seed;
err = nl::Weave::Platform::Security::GetSecureRandomData((uint8_t *)&seed, sizeof(seed));
FAIL_ERROR(err, "Random number generator seeding failed");
srand(seed);
}
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))
{
exit(EXIT_FAILURE);
}
// TODO (arg clean up): generalize code that infers node ids from local address
if (gNetworkOptions.LocalIPv6Addr != IPAddress::Any)
{
if (!gNetworkOptions.LocalIPv6Addr.IsIPv6ULA())
{
printf("ERROR: Local address must be an IPv6 ULA\n");
exit(EXIT_FAILURE);
}
gWeaveNodeOptions.FabricId = gNetworkOptions.LocalIPv6Addr.GlobalId();
gWeaveNodeOptions.LocalNodeId = IPv6InterfaceIdToWeaveNodeId(gNetworkOptions.LocalIPv6Addr.InterfaceId());
gWeaveNodeOptions.SubnetId = gNetworkOptions.LocalIPv6Addr.Subnet();
}
InitSystemLayer();
InitNetwork();
InitWeaveStack(!UseTCP, true);
if (InitiatorKeyExportConfig != kKeyExportConfig_Unspecified)
SecurityMgr.InitiatorKeyExportConfig = InitiatorKeyExportConfig;
if (gKeyExportOptions.AllowedKeyExportConfigs != 0)
SecurityMgr.InitiatorAllowedKeyExportConfigs = gKeyExportOptions.AllowedKeyExportConfigs;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
err = ServiceMgr.init(&ExchangeMgr, ServiceDirCache, sizeof(ServiceDirCache),
GetRootDirectoryEntry, kWeaveAuthMode_CASE_ServiceEndPoint);
if (err != WEAVE_NO_ERROR)
{
printf("ServiceMgr.init() failed with error: %s\n", ErrorStr(err));
exit(EXIT_FAILURE);
}
#endif
#if WEAVE_CONFIG_TEST
nl::Weave::Stats::UpdateSnapshot(before);
#endif
// Arrange to get called for various activities in the message layer.
MessageLayer.OnConnectionReceived = HandleConnectionReceived;
MessageLayer.OnReceiveError = HandleMessageReceiveError;
MessageLayer.OnAcceptError = HandleAcceptConnectionError;
PrintNodeConfig();
if (!UseTCP && DestAddr != NULL)
ParseDestAddress();
if (DestNodeId == 0)
printf("Sending key export request to node at %s\n", DestAddr);
else if (DestAddr == NULL)
printf("Sending key export request to node %" PRIX64 "\n", DestNodeId);
else
printf("Sending key export request to node %" PRIX64 " at %s\n", DestNodeId, DestAddr);
#if WEAVE_CONFIG_TEST
for (iter = 0; iter < gFaultInjectionOptions.TestIterations; iter++)
{
printf("Iteration %u\n", iter);
#endif // WEAVE_CONFIG_TEST
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 100000;
ServiceNetwork(sleepTime);
if (!Done)
DriveSending();
fflush(stdout);
}
ResetTestContext();
#if WEAVE_CONFIG_TEST
if (gSigusr1Received)
{
printf("Sigusr1Received\n");
break;
}
}
#endif // WEAVE_CONFIG_TEST
#if WEAVE_CONFIG_TEST
ProcessStats(before, after, printStats, NULL);
PrintFaultInjectionCounters();
#endif // WEAVE_CONFIG_TEST
ShutdownWeaveStack();
ShutdownNetwork();
ShutdownSystemLayer();
return EXIT_SUCCESS;
}
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;
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
case 'w':
UseTCP = false;
UseWRMP = true;
break;
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
case kToolOpt_UseServiceDir:
UseServiceDir = true;
break;
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
case 'K':
if (!ParseInt(arg, ExportKeyId, 0) || !WeaveKeyId::IsValidKeyId(ExportKeyId))
{
PrintArgError("%s: Invalid value specified for key identifier: %s\n", progName, arg);
return false;
}
break;
case 'k':
if (!ParseKeyExportConfig(arg, InitiatorKeyExportConfig))
{
PrintArgError("%s: Invalid value specified for KeyExport config: %s\n", progName, arg);
return false;
}
break;
case 'd':
SignKeyExportMsgs = false;
break;
case 'c':
if (!ParseInt(arg, MaxKeyExportCount) || MaxKeyExportCount < 0)
{
PrintArgError("%s: Invalid value specified for send count: %s\n", progName, arg);
return false;
}
break;
case 'i':
if (!ParseInt(arg, KeyExportInterval) || KeyExportInterval < 0 || KeyExportInterval > (INT32_MAX / 1000))
{
PrintArgError("%s: Invalid value specified for send interval: %s\n", progName, arg);
return false;
}
KeyExportInterval = KeyExportInterval * 1000;
break;
case 'D':
DestAddr = 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;
}
// TODO (arg clean up): generalize parsing of destination node ids and addresses.
const char *nodeId = argv[0];
char *p = (char *)strchr(nodeId, '@');
if (p != NULL)
{
*p = 0;
DestAddr = p+1;
}
if (!ParseNodeId(nodeId, DestNodeId))
{
PrintArgError("%s: Invalid value specified for destination node Id: %s\n", progName, nodeId);
return false;
}
}
else
{
PrintArgError("%s: Please specify destination node Id\n", progName);
return false;
}
return true;
}
void DriveSending()
{
WEAVE_ERROR err;
if (Now() < LastKeyExportTime + KeyExportInterval)
return;
if (WaitingForKeyExportResponse)
{
printf("No key export response received\n");
WaitingForKeyExportResponse = false;
// Rescan interfaces to see if we got any new IP addresses
if (!UseTCP)
{
printf("Refreshing endpoints\n");
err = MessageLayer.RefreshEndpoints();
if (err != WEAVE_NO_ERROR)
printf("WeaveMessageLayer.RefreshEndpoints() failed: %s\n", ErrorStr(err));
}
}
if (MaxKeyExportCount != -1 && KeyExportRequestCount >= (uint64_t) MaxKeyExportCount)
{
if (Con != NULL && Con->State != WeaveConnection::kState_Closed)
{
printf("Connection closed\n");
Con->Close();
Con = NULL;
ClientConEstablished = false;
ClientConInProgress = false;
}
Done = true;
return;
}
if (UseTCP)
{
if (!ClientConEstablished)
{
StartClientConnection();
return;
}
else
{
VerifyOrDie(Con != NULL && ClientConEstablished);
}
}
err = SecurityMgr.StartKeyExport(Con, DestNodeId, DestIPAddr, DestPort, ExportKeyId, SignKeyExportMsgs,
NULL, HandleKeyExportComplete, HandleKeyExportError);
LastKeyExportTime = Now();
if (err == WEAVE_NO_ERROR)
{
KeyExportRequestCount++;
WaitingForKeyExportResponse = true;
}
else
{
printf("SecurityMgr.StartKeyExport() failed: %s\n", ErrorStr(err));
}
}
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;
}
void StartClientConnection()
{
WEAVE_ERROR err;
if (Con != NULL && Con->State == WeaveConnection::kState_Closed)
{
Con->Close();
Con = NULL;
}
// Do nothing if a connect attempt is already in progress.
if (ClientConInProgress)
return;
ClientConEstablished = false;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (UseServiceDir)
{
err = ServiceMgr.connect(DestNodeId,
AuthMode,
NULL,
HandleServiceMgrStatus,
HandleConnectionComplete);
if (err != WEAVE_NO_ERROR)
{
printf("WeaveServiceManager.Connect(): failed: %s\n", ErrorStr(err));
LastKeyExportTime = Now();
return;
}
}
else
#endif
{
Con = MessageLayer.NewConnection();
if (Con == NULL)
{
printf("WeaveConnection.Connect failed: %s\n", ErrorStr(WEAVE_ERROR_NO_MEMORY));
LastKeyExportTime = Now();
Done = true;
return;
}
Con->OnConnectionComplete = HandleConnectionComplete;
Con->OnConnectionClosed = HandleConnectionClosed;
err = Con->Connect(DestNodeId, AuthMode, DestAddr);
if (err != WEAVE_NO_ERROR)
{
printf("WeaveConnection.Connect failed: %s\n", ErrorStr(err));
Con->Close();
Con = NULL;
LastKeyExportTime = Now();
Done = true;
return;
}
}
ClientConInProgress = true;
}
void HandleConnectionComplete(WeaveConnection *con, WEAVE_ERROR conErr)
{
char ipAddrStr[64];
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
if (conErr != WEAVE_NO_ERROR)
{
printf("Connection FAILED to node %" PRIX64 " (%s): %s\n", con->PeerNodeId, ipAddrStr, ErrorStr(conErr));
con->Close();
Con = NULL;
LastKeyExportTime = Now();
ClientConEstablished = false;
ClientConInProgress = false;
Done = true;
return;
}
printf("Connection established to node %" PRIX64 " (%s)\n", con->PeerNodeId, ipAddrStr);
Con = con;
con->OnConnectionClosed = HandleConnectionClosed;
ClientConEstablished = true;
ClientConInProgress = false;
}
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));
if (con == Con)
{
con->Close();
Con = NULL;
}
WaitingForKeyExportResponse = false;
ClientConEstablished = false;
ClientConInProgress = false;
}
void HandleKeyExportComplete(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState, uint32_t exportedKeyId, const uint8_t *exportedKey, uint16_t exportedKeyLen)
{
uint64_t peerNodeId;
char ipAddrStr[64];
WaitingForKeyExportResponse = false;
KeyExportResponseCount++;
LastKeyExportTime = Now();
SenderBusyRespCount = 0;
if (con != NULL)
{
peerNodeId = con->PeerNodeId;
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
}
else
{
peerNodeId = DestNodeId;
DestIPAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
}
printf("Received Key Export Response from node %" PRIX64 " (%s) for requested keyId = 0x%08X.\n", peerNodeId, ipAddrStr, ExportKeyId);
printf("Exported Key 0x%08X (%d bytes):\n", exportedKeyId, exportedKeyLen);
DumpMemoryCStyle(exportedKey, exportedKeyLen, " ", 16);
}
void HandleKeyExportError(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState, WEAVE_ERROR localErr, StatusReport *statusReport)
{
uint64_t peerNodeId;
char ipAddrStr[64];
bool isSenderBusy = false;
WaitingForKeyExportResponse = false;
if (con != NULL)
{
peerNodeId = con->PeerNodeId;
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
}
else
{
peerNodeId = DestNodeId;
DestIPAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
}
if (localErr == WEAVE_ERROR_STATUS_REPORT_RECEIVED && statusReport != NULL)
printf("FAILED to export key (keyId = 0x%08X) from node %" PRIX64 " (%s): %s\n", ExportKeyId, peerNodeId, ipAddrStr, StatusReportStr(statusReport->mProfileId, statusReport->mStatusCode));
else
printf("FAILED to export key (keyId = 0x%08X) from node %" PRIX64 " (%s): %s\n", ExportKeyId, peerNodeId, ipAddrStr, ErrorStr(localErr));
isSenderBusy = (localErr == WEAVE_ERROR_STATUS_REPORT_RECEIVED &&
statusReport != NULL &&
statusReport->mProfileId == nl::Weave::Profiles::kWeaveProfile_Common &&
statusReport->mStatusCode == nl::Weave::Profiles::Common::kStatus_Busy);
if (isSenderBusy)
{
// Force the main loop not to retry too soon.
LastKeyExportTime = Now() + SenderBusyRespDelay;
SenderBusyRespCount++;
}
if (!isSenderBusy || SenderBusyRespCount > MaxSenderBusyRespCount)
{
if (Con != NULL && Con->State != WeaveConnection::kState_Closed)
{
printf("Connection closed\n");
Con->Close();
Con = NULL;
ClientConEstablished = false;
ClientConInProgress = false;
}
Done = true;
}
}
void ParseDestAddress()
{
// NOTE: This function is only used when communicating over UDP. Code in the WeaveConnection object handles
// parsing the destination node address for TCP connections.
WEAVE_ERROR err;
const char *addr;
uint16_t addrLen;
const char *intfName;
uint16_t intfNameLen;
err = ParseHostPortAndInterface(DestAddr, strlen(DestAddr), addr, addrLen, DestPort, intfName, intfNameLen);
if (err != INET_NO_ERROR)
{
printf("Invalid destination address: %s\n", DestAddr);
exit(EXIT_FAILURE);
}
if (!IPAddress::FromString(addr, DestIPAddr))
{
printf("Invalid destination address: %s\n", DestAddr);
exit(EXIT_FAILURE);
}
if (intfName != NULL)
{
err = InterfaceNameToId(intfName, DestIntf);
if (err != INET_NO_ERROR)
{
printf("Invalid interface name: %s\n", intfName);
exit(EXIT_FAILURE);
}
}
}
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
WEAVE_ERROR GetRootDirectoryEntry(uint8_t *buf, uint16_t bufSize)
{
return gServiceDirClientOptions.GetRootDirectoryEntry(buf, bufSize);
}
void HandleServiceMgrStatus(void* anAppState, WEAVE_ERROR anError, StatusReport *aReport)
{
if (aReport)
printf("service directory status report [%" PRIx32 ", %" PRIx32 "]", aReport->mProfileId, aReport->mStatusCode);
else
printf("service directory error %" PRIx32 "", static_cast<uint32_t>(anError));
LastKeyExportTime = Now();
ClientConEstablished = false;
ClientConInProgress = false;
}
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY