blob: 40c6cf25169a78ed88760253e37527beb3ecccdb [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
*
* 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.
*/
#include "DiscoveryManager.h"
#include <inttypes.h>
#include "lib/mdns/platform/Mdns.h"
#include "lib/support/logging/CHIPLogging.h"
#include "platform/CHIPDeviceConfig.h"
#include "platform/CHIPDeviceLayer.h"
#include "support/CodeUtils.h"
#include "support/ErrorStr.h"
#include "support/RandUtils.h"
#if CHIP_ENABLE_MDNS
namespace {
uint8_t HexToInt(char c)
{
if ('0' <= c && c <= '9')
{
return static_cast<uint8_t>(c - '0');
}
else if ('a' <= c && c <= 'f')
{
return static_cast<uint8_t>(0x0a + c - 'a');
}
else if ('A' <= c && c <= 'F')
{
return static_cast<uint8_t>(0x0a + c - 'A');
}
return UINT8_MAX;
}
constexpr uint64_t kUndefinedNodeId = 0;
} // namespace
#endif
namespace chip {
namespace Mdns {
DiscoveryManager DiscoveryManager::sManager;
CHIP_ERROR DiscoveryManager::Init()
{
#if CHIP_ENABLE_MDNS
CHIP_ERROR error;
mUnprovisionedInstanceName = GetRandU64();
SuccessOrExit(error = ChipMdnsInit(HandleMdnsInit, HandleMdnsError, this));
exit:
return error;
#else
return CHIP_NO_ERROR;
#endif // CHIP_ENABLE_MDNS
}
void DiscoveryManager::HandleMdnsInit(void * context, CHIP_ERROR initError)
{
#if CHIP_ENABLE_MDNS
DiscoveryManager * publisher = static_cast<DiscoveryManager *>(context);
if (initError == CHIP_NO_ERROR)
{
publisher->mMdnsInitialized = true;
}
else
{
ChipLogError(Discovery, "mDNS initialization failed with %s", chip::ErrorStr(initError));
}
#endif // CHIP_ENABLE_MDNS
}
void DiscoveryManager::HandleMdnsError(void * context, CHIP_ERROR error)
{
#if CHIP_ENABLE_MDNS
DiscoveryManager * publisher = static_cast<DiscoveryManager *>(context);
if (error == CHIP_ERROR_FORCED_RESET && publisher->mIsPublishing)
{
publisher->StartPublishDevice();
}
else
{
ChipLogError(Discovery, "mDNS error: %s", chip::ErrorStr(error));
}
#endif // CHIP_ENABLE_MDNS
}
CHIP_ERROR DiscoveryManager::StartPublishDevice(chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface)
{
#if CHIP_ENABLE_MDNS
CHIP_ERROR error;
// TODO: after multi-admin is decided we may need to publish both _chipc._udp and _chip._tcp service
if (!mIsPublishing)
{
SuccessOrExit(error = SetupHostname());
}
else if (chip::DeviceLayer::ConfigurationMgr().IsFullyProvisioned() != mIsPublishingProvisionedDevice)
{
SuccessOrExit(error = StopPublishDevice());
// Set hostname again in case the mac address changes when shifting from soft-AP to station
SuccessOrExit(error = SetupHostname());
}
mIsPublishingProvisionedDevice = chip::DeviceLayer::ConfigurationMgr().IsFullyProvisioned();
if (mIsPublishingProvisionedDevice)
{
error = PublishProvisionedDevice(addressType, interface);
}
else
{
error = PublishUnprovisionedDevice(addressType, interface);
}
mIsPublishing = true;
exit:
return error;
#else
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif // CHIP_ENABLE_MDNS
}
CHIP_ERROR DiscoveryManager::SetupHostname()
{
#if CHIP_ENABLE_MDNS
uint8_t mac[6]; // 6 byte wifi mac
char hostname[13]; // Hostname will be the hex representation of mac.
CHIP_ERROR error;
SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetPrimaryWiFiMACAddress(mac));
for (size_t i = 0; i < sizeof(mac); i++)
{
snprintf(&hostname[i * 2], sizeof(hostname) - i * 2, "%02X", mac[i]);
}
SuccessOrExit(error = ChipMdnsSetHostname(hostname));
exit:
return error;
#else
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif // CHIP_ENABLE_MDNS
}
CHIP_ERROR DiscoveryManager::PublishUnprovisionedDevice(chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface)
{
#if CHIP_ENABLE_MDNS
CHIP_ERROR error = CHIP_NO_ERROR;
MdnsService service;
uint16_t discriminator;
uint16_t vendorID;
uint16_t productID;
char discriminatorBuf[5]; // hex representation of 16-bit discriminator
char vendorProductBuf[10]; // "FFFF+FFFF"
// TODO: The text entry will be updated in the spec, update accordingly.
TextEntry entries[2] = {
{ "D", nullptr, 0 },
{ "VP", nullptr, 0 },
};
VerifyOrExit(mMdnsInitialized, error = CHIP_ERROR_INCORRECT_STATE);
ChipLogProgress(Discovery, "setup mdns service");
SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetSetupDiscriminator(discriminator));
snprintf(service.mName, sizeof(service.mName), "%016" PRIX64, mUnprovisionedInstanceName);
strncpy(service.mType, "_chipc", sizeof(service.mType));
service.mProtocol = MdnsServiceProtocol::kMdnsProtocolUdp;
SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetVendorId(vendorID));
SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetProductId(productID));
snprintf(discriminatorBuf, sizeof(discriminatorBuf), "%04X", discriminator);
snprintf(vendorProductBuf, sizeof(vendorProductBuf), "%04X+%04X", vendorID, productID);
entries[0].mData = reinterpret_cast<const uint8_t *>(discriminatorBuf);
entries[0].mDataSize = strnlen(discriminatorBuf, sizeof(discriminatorBuf));
entries[1].mData = reinterpret_cast<const uint8_t *>(vendorProductBuf);
entries[1].mDataSize = strnlen(discriminatorBuf, sizeof(vendorProductBuf));
service.mTextEntryies = entries;
service.mTextEntrySize = sizeof(entries) / sizeof(TextEntry);
service.mPort = CHIP_PORT;
service.mInterface = interface;
service.mAddressType = addressType;
error = ChipMdnsPublishService(&service);
exit:
return error;
#else
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif // CHIP_ENABLE_MDNS
}
CHIP_ERROR DiscoveryManager::PublishProvisionedDevice(chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface)
{
#if CHIP_ENABLE_MDNS
uint64_t deviceId;
uint64_t fabricId;
MdnsService service;
CHIP_ERROR error = CHIP_NO_ERROR;
// TODO: There may be multilple device/fabrid ids after multi-admin.
SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetFabricId(fabricId));
SuccessOrExit(error = chip::DeviceLayer::ConfigurationMgr().GetDeviceId(deviceId));
snprintf(service.mName, sizeof(service.mName), "%" PRIX64 "-%" PRIX64, deviceId, fabricId);
strncpy(service.mType, "_chip", sizeof(service.mType));
service.mProtocol = MdnsServiceProtocol::kMdnsProtocolTcp;
service.mPort = CHIP_PORT;
service.mTextEntryies = nullptr;
service.mTextEntrySize = 0;
service.mInterface = interface;
service.mAddressType = addressType;
error = ChipMdnsPublishService(&service);
exit:
return error;
#else
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif // CHIP_ENABLE_MDNS
}
CHIP_ERROR DiscoveryManager::StopPublishDevice()
{
#if CHIP_ENABLE_MDNS
mIsPublishing = false;
return ChipMdnsStopPublish();
#else
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif // CHIP_ENABLE_MDNS
}
CHIP_ERROR DiscoveryManager::RegisterResolveDelegate(ResolveDelegate * delegate)
{
if (mResolveDelegate != nullptr)
{
return CHIP_ERROR_INCORRECT_STATE;
}
else
{
mResolveDelegate = delegate;
return CHIP_NO_ERROR;
}
}
CHIP_ERROR DiscoveryManager::ResolveNodeId(uint64_t nodeId, uint64_t fabricId, Inet::IPAddressType type)
{
#if CHIP_ENABLE_MDNS
MdnsService service;
snprintf(service.mName, sizeof(service.mName), "%" PRIX64 "-%" PRIX64, nodeId, fabricId);
strncpy(service.mType, "_chip", sizeof(service.mType));
service.mProtocol = MdnsServiceProtocol::kMdnsProtocolTcp;
service.mAddressType = type;
return ChipMdnsResolve(&service, INET_NULL_INTERFACEID, HandleNodeIdResolve, this);
#else
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif // CHIP_ENABLE_MDNS
}
void DiscoveryManager::HandleNodeIdResolve(void * context, MdnsService * result, CHIP_ERROR error)
{
#if CHIP_ENABLE_MDNS
DiscoveryManager * mgr = static_cast<DiscoveryManager *>(context);
if (mgr->mResolveDelegate == nullptr)
{
return;
}
if (error != CHIP_NO_ERROR)
{
ChipLogError(Discovery, "Node ID resolved failed with %s", chip::ErrorStr(error));
mgr->mResolveDelegate->HandleNodeIdResolve(error, kUndefinedNodeId, MdnsService{});
}
else if (result == nullptr)
{
ChipLogError(Discovery, "Node ID resolve not found");
mgr->mResolveDelegate->HandleNodeIdResolve(CHIP_ERROR_UNKNOWN_RESOURCE_ID, kUndefinedNodeId, MdnsService{});
}
else
{
// Parse '%x-%x' from the name
uint64_t nodeId = 0;
bool deliminatorFound = false;
for (size_t i = 0; i < sizeof(result->mName) && result->mName[i] != 0; i++)
{
if (result->mName[i] == '-')
{
deliminatorFound = true;
break;
}
else
{
uint8_t val = HexToInt(result->mName[i]);
if (val == UINT8_MAX)
{
break;
}
else
{
nodeId = nodeId * 16 + val;
}
}
}
if (deliminatorFound)
{
ChipLogProgress(Discovery, "Node ID resolved for %" PRIX64, nodeId);
mgr->mResolveDelegate->HandleNodeIdResolve(error, nodeId, *result);
}
else
{
ChipLogProgress(Discovery, "Invalid service entry from node %" PRIX64, nodeId);
mgr->mResolveDelegate->HandleNodeIdResolve(error, kUndefinedNodeId, *result);
}
}
#endif // CHIP_ENABLE_MDNS
}
} // namespace Mdns
} // namespace chip