439 lines
20 KiB
C++
439 lines
20 KiB
C++
/*
|
|
* Copyright (c) 2021, The OpenThread Authors.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef DNS_SERVER_HPP_
|
|
#define DNS_SERVER_HPP_
|
|
|
|
#include "openthread-core-config.h"
|
|
|
|
#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
|
|
|
|
#include <openthread/dnssd_server.h>
|
|
|
|
#include "common/as_core_type.hpp"
|
|
#include "common/message.hpp"
|
|
#include "common/non_copyable.hpp"
|
|
#include "common/timer.hpp"
|
|
#include "net/dns_types.hpp"
|
|
#include "net/ip6.hpp"
|
|
#include "net/netif.hpp"
|
|
#include "net/srp_server.hpp"
|
|
|
|
/**
|
|
* @file
|
|
* This file includes definitions for the DNS-SD server.
|
|
*/
|
|
|
|
namespace ot {
|
|
|
|
namespace Srp {
|
|
class Server;
|
|
}
|
|
|
|
namespace Dns {
|
|
namespace ServiceDiscovery {
|
|
|
|
/**
|
|
* This class implements DNS-SD server.
|
|
*
|
|
*/
|
|
class Server : public InstanceLocator, private NonCopyable
|
|
{
|
|
friend class Srp::Server;
|
|
|
|
public:
|
|
/**
|
|
* This class contains the counters of the DNS-SD server.
|
|
*
|
|
*/
|
|
class Counters : public otDnssdCounters, public Clearable<Counters>
|
|
{
|
|
};
|
|
|
|
/**
|
|
* This enumeration specifies a DNS-SD query type.
|
|
*
|
|
*/
|
|
enum DnsQueryType : uint8_t
|
|
{
|
|
kDnsQueryNone = OT_DNSSD_QUERY_TYPE_NONE, ///< Service type unspecified.
|
|
kDnsQueryBrowse = OT_DNSSD_QUERY_TYPE_BROWSE, ///< Service type browse service.
|
|
kDnsQueryResolve = OT_DNSSD_QUERY_TYPE_RESOLVE, ///< Service type resolve service instance.
|
|
kDnsQueryResolveHost = OT_DNSSD_QUERY_TYPE_RESOLVE_HOST, ///< Service type resolve hostname.
|
|
};
|
|
|
|
static constexpr uint16_t kPort = OPENTHREAD_CONFIG_DNSSD_SERVER_PORT; ///< The DNS-SD server port.
|
|
|
|
/**
|
|
* This constructor initializes the object.
|
|
*
|
|
* @param[in] aInstance A reference to the OpenThread instance.
|
|
*
|
|
*/
|
|
explicit Server(Instance &aInstance);
|
|
|
|
/**
|
|
* This method starts the DNS-SD server.
|
|
*
|
|
* @retval kErrorNone Successfully started the DNS-SD server.
|
|
* @retval kErrorFailed If failed to open or bind the UDP socket.
|
|
*
|
|
*/
|
|
Error Start(void);
|
|
|
|
/**
|
|
* This method stops the DNS-SD server.
|
|
*
|
|
*/
|
|
void Stop(void);
|
|
|
|
/**
|
|
* This method sets DNS-SD query callbacks.
|
|
*
|
|
* @param[in] aSubscribe A pointer to the callback function to subscribe a service or service instance.
|
|
* @param[in] aUnsubscribe A pointer to the callback function to unsubscribe a service or service instance.
|
|
* @param[in] aContext A pointer to the application-specific context.
|
|
*
|
|
*/
|
|
void SetQueryCallbacks(otDnssdQuerySubscribeCallback aSubscribe,
|
|
otDnssdQueryUnsubscribeCallback aUnsubscribe,
|
|
void * aContext);
|
|
|
|
/**
|
|
* This method notifies a discovered service instance.
|
|
*
|
|
* @param[in] aServiceFullName The null-terminated full service name.
|
|
* @param[in] aInstanceInfo A reference to the discovered service instance information.
|
|
*
|
|
*/
|
|
void HandleDiscoveredServiceInstance(const char *aServiceFullName, const otDnssdServiceInstanceInfo &aInstanceInfo);
|
|
|
|
/**
|
|
* This method notifies a discovered host.
|
|
*
|
|
* @param[in] aHostFullName The null-terminated full host name.
|
|
* @param[in] aHostInfo A reference to the discovered host information.
|
|
*
|
|
*/
|
|
void HandleDiscoveredHost(const char *aHostFullName, const otDnssdHostInfo &aHostInfo);
|
|
|
|
/**
|
|
* This method acquires the next query in the server.
|
|
*
|
|
* @param[in] aQuery The query pointer. Pass `nullptr` to get the first query.
|
|
*
|
|
* @returns A pointer to the query or `nullptr` if no more queries.
|
|
*
|
|
*/
|
|
const otDnssdQuery *GetNextQuery(const otDnssdQuery *aQuery) const;
|
|
|
|
/**
|
|
* This method acquires the DNS-SD query type and name for a specific query.
|
|
*
|
|
* @param[in] aQuery The query pointer.
|
|
* @param[out] aName The name output buffer.
|
|
*
|
|
* @returns The DNS-SD query type.
|
|
*
|
|
*/
|
|
static DnsQueryType GetQueryTypeAndName(const otDnssdQuery *aQuery, char (&aName)[Name::kMaxNameSize]);
|
|
|
|
/**
|
|
* This method returns the counters of the DNS-SD server.
|
|
*
|
|
* @returns A reference to the `Counters` instance.
|
|
*
|
|
*/
|
|
const Counters &GetCounters(void) const { return mCounters; };
|
|
|
|
private:
|
|
class NameCompressInfo : public Clearable<NameCompressInfo>
|
|
{
|
|
public:
|
|
explicit NameCompressInfo(void) = default;
|
|
|
|
explicit NameCompressInfo(const char *aDomainName)
|
|
: mDomainName(aDomainName)
|
|
, mDomainNameOffset(kUnknownOffset)
|
|
, mServiceNameOffset(kUnknownOffset)
|
|
, mInstanceNameOffset(kUnknownOffset)
|
|
, mHostNameOffset(kUnknownOffset)
|
|
{
|
|
}
|
|
|
|
static constexpr uint16_t kUnknownOffset = 0; // Unknown offset value (used when offset is not yet set).
|
|
|
|
uint16_t GetDomainNameOffset(void) const { return mDomainNameOffset; }
|
|
|
|
void SetDomainNameOffset(uint16_t aOffset) { mDomainNameOffset = aOffset; }
|
|
|
|
const char *GetDomainName(void) const { return mDomainName; }
|
|
|
|
uint16_t GetServiceNameOffset(const Message &aMessage, const char *aServiceName) const
|
|
{
|
|
return MatchCompressedName(aMessage, mServiceNameOffset, aServiceName)
|
|
? mServiceNameOffset
|
|
: static_cast<uint16_t>(kUnknownOffset);
|
|
};
|
|
|
|
void SetServiceNameOffset(uint16_t aOffset)
|
|
{
|
|
if (mServiceNameOffset == kUnknownOffset)
|
|
{
|
|
mServiceNameOffset = aOffset;
|
|
}
|
|
}
|
|
|
|
uint16_t GetInstanceNameOffset(const Message &aMessage, const char *aName) const
|
|
{
|
|
return MatchCompressedName(aMessage, mInstanceNameOffset, aName) ? mInstanceNameOffset
|
|
: static_cast<uint16_t>(kUnknownOffset);
|
|
}
|
|
|
|
void SetInstanceNameOffset(uint16_t aOffset)
|
|
{
|
|
if (mInstanceNameOffset == kUnknownOffset)
|
|
{
|
|
mInstanceNameOffset = aOffset;
|
|
}
|
|
}
|
|
|
|
uint16_t GetHostNameOffset(const Message &aMessage, const char *aName) const
|
|
{
|
|
return MatchCompressedName(aMessage, mHostNameOffset, aName) ? mHostNameOffset
|
|
: static_cast<uint16_t>(kUnknownOffset);
|
|
}
|
|
|
|
void SetHostNameOffset(uint16_t aOffset)
|
|
{
|
|
if (mHostNameOffset == kUnknownOffset)
|
|
{
|
|
mHostNameOffset = aOffset;
|
|
}
|
|
}
|
|
|
|
private:
|
|
static bool MatchCompressedName(const Message &aMessage, uint16_t aOffset, const char *aName)
|
|
{
|
|
return aOffset != kUnknownOffset && Name::CompareName(aMessage, aOffset, aName) == kErrorNone;
|
|
}
|
|
|
|
const char *mDomainName; // The serialized domain name.
|
|
uint16_t mDomainNameOffset; // Offset of domain name serialization into the response message.
|
|
uint16_t mServiceNameOffset; // Offset of service name serialization into the response message.
|
|
uint16_t mInstanceNameOffset; // Offset of instance name serialization into the response message.
|
|
uint16_t mHostNameOffset; // Offset of host name serialization into the response message.
|
|
};
|
|
|
|
static constexpr bool kBindUnspecifiedNetif = OPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF;
|
|
static constexpr uint8_t kProtocolLabelLength = 4;
|
|
static constexpr uint8_t kSubTypeLabelLength = 4;
|
|
static constexpr uint16_t kMaxConcurrentQueries = 32;
|
|
|
|
// This structure represents the splitting information of a full name.
|
|
struct NameComponentsOffsetInfo
|
|
{
|
|
static constexpr uint8_t kNotPresent = 0xff; // Indicates the component is not present.
|
|
|
|
explicit NameComponentsOffsetInfo(void)
|
|
: mDomainOffset(kNotPresent)
|
|
, mProtocolOffset(kNotPresent)
|
|
, mServiceOffset(kNotPresent)
|
|
, mSubTypeOffset(kNotPresent)
|
|
, mInstanceOffset(kNotPresent)
|
|
{
|
|
}
|
|
|
|
bool IsServiceInstanceName(void) const { return mInstanceOffset != kNotPresent; }
|
|
|
|
bool IsServiceName(void) const { return mServiceOffset != kNotPresent && mInstanceOffset == kNotPresent; }
|
|
|
|
bool IsHostName(void) const { return mProtocolOffset == kNotPresent && mDomainOffset != 0; }
|
|
|
|
uint8_t mDomainOffset; // The offset to the beginning of <Domain>.
|
|
uint8_t mProtocolOffset; // The offset to the beginning of <Protocol> (i.e. _tcp or _udp) or `kNotPresent` if
|
|
// the name is not a service or instance.
|
|
uint8_t mServiceOffset; // The offset to the beginning of <Service> or `kNotPresent` if the name is not a
|
|
// service or instance.
|
|
uint8_t mSubTypeOffset; // The offset to the beginning of sub-type label or `kNotPresent` is not a sub-type.
|
|
uint8_t mInstanceOffset; // The offset to the beginning of <Instance> or `kNotPresent` if the name is not a
|
|
// instance.
|
|
};
|
|
|
|
/**
|
|
* This class contains the compress information for a dns packet.
|
|
*
|
|
*/
|
|
class QueryTransaction : public InstanceLocatorInit
|
|
{
|
|
public:
|
|
explicit QueryTransaction(void)
|
|
: mResponseMessage(nullptr)
|
|
{
|
|
}
|
|
|
|
void Init(const Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
const NameCompressInfo &aCompressInfo,
|
|
const Ip6::MessageInfo &aMessageInfo,
|
|
Instance & aInstance);
|
|
bool IsValid(void) const { return mResponseMessage != nullptr; }
|
|
const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; }
|
|
const Header & GetResponseHeader(void) const { return mResponseHeader; }
|
|
Header & GetResponseHeader(void) { return mResponseHeader; }
|
|
const Message & GetResponseMessage(void) const { return *mResponseMessage; }
|
|
Message & GetResponseMessage(void) { return *mResponseMessage; }
|
|
TimeMilli GetStartTime(void) const { return mStartTime; }
|
|
NameCompressInfo & GetNameCompressInfo(void) { return mCompressInfo; };
|
|
void Finalize(Header::Response aResponseMessage, Ip6::Udp::Socket &aSocket);
|
|
|
|
Header mResponseHeader;
|
|
Message * mResponseMessage;
|
|
NameCompressInfo mCompressInfo;
|
|
Ip6::MessageInfo mMessageInfo;
|
|
TimeMilli mStartTime;
|
|
};
|
|
|
|
static constexpr uint32_t kQueryTimeout = OPENTHREAD_CONFIG_DNSSD_QUERY_TIMEOUT;
|
|
|
|
bool IsRunning(void) const { return mSocket.IsBound(); }
|
|
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
|
|
void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
|
void ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage, const Ip6::MessageInfo &aMessageInfo);
|
|
static Header::Response AddQuestions(const Header & aRequestHeader,
|
|
const Message & aRequestMessage,
|
|
Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
NameCompressInfo &aCompressInfo);
|
|
static Error AppendQuestion(const char * aName,
|
|
const Question & aQuestion,
|
|
Message & aMessage,
|
|
NameCompressInfo &aCompressInfo);
|
|
static Error AppendPtrRecord(Message & aMessage,
|
|
const char * aServiceName,
|
|
const char * aInstanceName,
|
|
uint32_t aTtl,
|
|
NameCompressInfo &aCompressInfo);
|
|
static Error AppendSrvRecord(Message & aMessage,
|
|
const char * aInstanceName,
|
|
const char * aHostName,
|
|
uint32_t aTtl,
|
|
uint16_t aPriority,
|
|
uint16_t aWeight,
|
|
uint16_t aPort,
|
|
NameCompressInfo &aCompressInfo);
|
|
static Error AppendTxtRecord(Message & aMessage,
|
|
const char * aInstanceName,
|
|
const void * aTxtData,
|
|
uint16_t aTxtLength,
|
|
uint32_t aTtl,
|
|
NameCompressInfo &aCompressInfo);
|
|
static Error AppendAaaaRecord(Message & aMessage,
|
|
const char * aHostName,
|
|
const Ip6::Address &aAddress,
|
|
uint32_t aTtl,
|
|
NameCompressInfo & aCompressInfo);
|
|
static Error AppendServiceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo);
|
|
static Error AppendInstanceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo);
|
|
static Error AppendHostName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo);
|
|
static void IncResourceRecordCount(Header &aHeader, bool aAdditional);
|
|
static Error FindNameComponents(const char *aName, const char *aDomain, NameComponentsOffsetInfo &aInfo);
|
|
static Error FindPreviousLabel(const char *aName, uint8_t &aStart, uint8_t &aStop);
|
|
void SendResponse(Header aHeader,
|
|
Header::Response aResponseCode,
|
|
Message & aMessage,
|
|
const Ip6::MessageInfo &aMessageInfo,
|
|
Ip6::Udp::Socket & aSocket);
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
Header::Response ResolveBySrp(Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
Server::NameCompressInfo &aCompressInfo);
|
|
Header::Response ResolveQuestionBySrp(const char * aName,
|
|
const Question & aQuestion,
|
|
Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
NameCompressInfo &aCompressInfo,
|
|
bool aAdditional);
|
|
const Srp::Server::Host * GetNextSrpHost(const Srp::Server::Host *aHost);
|
|
static const Srp::Server::Service *GetNextSrpService(const Srp::Server::Host & aHost,
|
|
const Srp::Server::Service *aService);
|
|
#endif
|
|
|
|
Error ResolveByQueryCallbacks(Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
NameCompressInfo & aCompressInfo,
|
|
const Ip6::MessageInfo &aMessageInfo);
|
|
QueryTransaction *NewQuery(const Header & aResponseHeader,
|
|
Message & aResponseMessage,
|
|
const NameCompressInfo &aCompressInfo,
|
|
const Ip6::MessageInfo &aMessageInfo);
|
|
static bool CanAnswerQuery(const QueryTransaction & aQuery,
|
|
const char * aServiceFullName,
|
|
const otDnssdServiceInstanceInfo &aInstanceInfo);
|
|
void AnswerQuery(QueryTransaction & aQuery,
|
|
const char * aServiceFullName,
|
|
const otDnssdServiceInstanceInfo &aInstanceInfo);
|
|
static bool CanAnswerQuery(const Server::QueryTransaction &aQuery, const char *aHostFullName);
|
|
void AnswerQuery(QueryTransaction &aQuery, const char *aHostFullName, const otDnssdHostInfo &aHostInfo);
|
|
void FinalizeQuery(QueryTransaction &aQuery, Header::Response aResponseCode);
|
|
static DnsQueryType GetQueryTypeAndName(const Header & aHeader,
|
|
const Message &aMessage,
|
|
char (&aName)[Name::kMaxNameSize]);
|
|
static bool HasQuestion(const Header &aHeader, const Message &aMessage, const char *aName, uint16_t aQuestionType);
|
|
static void HandleTimer(Timer &aTimer);
|
|
void HandleTimer(void);
|
|
void ResetTimer(void);
|
|
|
|
void UpdateResponseCounters(Header::Response aResponseCode);
|
|
|
|
static const char kDnssdProtocolUdp[];
|
|
static const char kDnssdProtocolTcp[];
|
|
static const char kDnssdSubTypeLabel[];
|
|
static const char kDefaultDomainName[];
|
|
Ip6::Udp::Socket mSocket;
|
|
|
|
QueryTransaction mQueryTransactions[kMaxConcurrentQueries];
|
|
void * mQueryCallbackContext;
|
|
otDnssdQuerySubscribeCallback mQuerySubscribe;
|
|
otDnssdQueryUnsubscribeCallback mQueryUnsubscribe;
|
|
TimerMilli mTimer;
|
|
|
|
Counters mCounters;
|
|
};
|
|
|
|
} // namespace ServiceDiscovery
|
|
} // namespace Dns
|
|
|
|
DefineMapEnum(otDnssdQueryType, Dns::ServiceDiscovery::Server::DnsQueryType);
|
|
|
|
} // namespace ot
|
|
|
|
#endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
|
|
|
|
#endif // DNS_SERVER_HPP_
|