| /* |
| * |
| * 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. |
| */ |
| |
| #pragma once |
| |
| #include "RecordResponder.h" |
| #include "ReplyFilter.h" |
| #include "Responder.h" |
| |
| #include <inet/InetLayer.h> |
| |
| namespace mdns { |
| namespace Minimal { |
| |
| /// Represents available data (replies) for mDNS queries. |
| struct QueryResponderRecord |
| { |
| Responder * responder = nullptr; // what response/data is available |
| bool reportService = false; // report as a service when listing dnssd services |
| uint64_t lastMulticastTime = 0; // last time this record was multicast |
| }; |
| |
| namespace Internal { |
| |
| /// Internal information for query responder records. |
| struct QueryResponderInfo : public QueryResponderRecord |
| { |
| bool reportNowAsAdditional; // report as additional data required |
| |
| bool alsoReportAdditionalQName = false; // report more data when this record is listed |
| FullQName additionalQName; // if alsoReportAdditionalQName is set, send this extra data |
| |
| void Clear() |
| { |
| responder = nullptr; |
| reportService = false; |
| reportNowAsAdditional = false; |
| alsoReportAdditionalQName = false; |
| } |
| }; |
| |
| } // namespace Internal |
| |
| /// Allows building query responder configuration |
| class QueryResponderSettings |
| { |
| public: |
| QueryResponderSettings() : mInfo(nullptr) {} |
| QueryResponderSettings(Internal::QueryResponderInfo * info) : mInfo(info) {} |
| QueryResponderSettings(const QueryResponderSettings & other) = default; |
| |
| /// This record should be part of dns-sd service listing requests |
| QueryResponderSettings & SetReportInServiceListing(bool reportService) |
| { |
| if (IsValid()) |
| { |
| mInfo->reportService = reportService; |
| } |
| return *this; |
| } |
| |
| /// When this record is send back, additional records should also be provided. |
| /// |
| /// This is useful to avoid chattyness by sending back referenced records |
| /// (e.g. when sending a PTR record, send the corresponding SRV and when sending |
| /// SRV, send back the corresponding A/AAAA records). |
| QueryResponderSettings & SetReportAdditional(const FullQName & qname) |
| { |
| if (IsValid()) |
| { |
| mInfo->alsoReportAdditionalQName = true; |
| mInfo->additionalQName = qname; |
| } |
| return *this; |
| } |
| |
| bool IsValid() const { return mInfo != nullptr; } |
| |
| private: |
| Internal::QueryResponderInfo * mInfo; |
| }; |
| |
| /// Determines what query records should be included in a response. |
| /// |
| /// Provides an 'Accept' method to determine if a reply is to be sent or not. |
| class QueryResponderRecordFilter |
| { |
| public: |
| /// Default contstructor accepts everything that is not null |
| QueryResponderRecordFilter() {} |
| QueryResponderRecordFilter(const QueryResponderRecordFilter & other) = default; |
| QueryResponderRecordFilter & operator=(const QueryResponderRecordFilter & other) = default; |
| |
| /// Set if to include only items marked as 'additional reply' or everything. |
| QueryResponderRecordFilter & SetIncludeAdditionalRepliesOnly(bool includeAdditionalRepliesOnly) |
| { |
| mIncludeAdditionalRepliesOnly = includeAdditionalRepliesOnly; |
| return *this; |
| } |
| |
| /// Filter out anything rejected by the given reply filter. |
| /// If replyFilter is nullptr, no such filtering is applied. |
| QueryResponderRecordFilter & SetReplyFilter(ReplyFilter * replyFilter) |
| { |
| mReplyFilter = replyFilter; |
| return *this; |
| } |
| |
| /// Filter out anything that was multicast past ms. |
| /// If ms is 0, no filtering is done |
| QueryResponderRecordFilter & SetIncludeOnlyMulticastBeforeMS(uint64_t ms) |
| { |
| mIncludeOnlyMulticastBeforeMS = ms; |
| return *this; |
| } |
| |
| bool Accept(Internal::QueryResponderInfo * record) const |
| { |
| if (record->responder == nullptr) |
| { |
| return false; |
| } |
| |
| if (mIncludeAdditionalRepliesOnly && !record->reportNowAsAdditional) |
| { |
| return false; |
| } |
| |
| if ((mIncludeOnlyMulticastBeforeMS > 0) && (record->lastMulticastTime >= mIncludeOnlyMulticastBeforeMS)) |
| { |
| return false; |
| } |
| |
| if ((mReplyFilter != nullptr) && |
| !mReplyFilter->Accept(record->responder->GetQType(), record->responder->GetQClass(), record->responder->GetQName())) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| private: |
| bool mIncludeAdditionalRepliesOnly = false; |
| ReplyFilter * mReplyFilter = nullptr; |
| uint64_t mIncludeOnlyMulticastBeforeMS = 0; |
| }; |
| |
| /// Iterates over an array of QueryResponderRecord items, providing only 'valid' ones, where |
| /// valid is based on the provided filter. |
| class QueryResponderIterator |
| { |
| public: |
| using value_type = QueryResponderRecord; |
| using pointer = QueryResponderRecord *; |
| using reference = QueryResponderRecord &; |
| |
| QueryResponderIterator() : mCurrent(nullptr), mRemaining(0) {} |
| QueryResponderIterator(QueryResponderRecordFilter * recordFilter, Internal::QueryResponderInfo * pos, size_t size) : |
| mFilter(recordFilter), mCurrent(pos), mRemaining(size) |
| { |
| SkipInvalid(); |
| } |
| QueryResponderIterator(const QueryResponderIterator & other) = default; |
| QueryResponderIterator & operator=(const QueryResponderIterator & other) = default; |
| |
| QueryResponderIterator & operator++() |
| { |
| if (mRemaining != 0) |
| { |
| mCurrent++; |
| mRemaining--; |
| } |
| SkipInvalid(); |
| return *this; |
| } |
| |
| QueryResponderIterator operator++(int) |
| { |
| QueryResponderIterator tmp(*this); |
| operator++(); |
| return tmp; |
| } |
| |
| bool operator==(const QueryResponderIterator & rhs) const { return mCurrent == rhs.mCurrent; } |
| bool operator!=(const QueryResponderIterator & rhs) const { return mCurrent != rhs.mCurrent; } |
| |
| QueryResponderRecord & operator*() { return *mCurrent; } |
| QueryResponderRecord * operator->() { return mCurrent; } |
| |
| Internal::QueryResponderInfo * GetInternal() { return mCurrent; } |
| const Internal::QueryResponderInfo * GetInternal() const { return mCurrent; } |
| |
| private: |
| /// Skips invalid/not useful values. |
| /// ensures that if mRemaining is 0, mCurrent is nullptr; |
| void SkipInvalid() |
| { |
| while ((mRemaining > 0) && !mFilter->Accept(mCurrent)) |
| { |
| mRemaining--; |
| mCurrent++; |
| } |
| if (mRemaining == 0) |
| { |
| mCurrent = nullptr; |
| } |
| } |
| |
| QueryResponderRecordFilter * mFilter; |
| Internal::QueryResponderInfo * mCurrent; |
| size_t mRemaining; |
| }; |
| |
| /// Responds to mDNS queries. |
| /// |
| /// In particular: |
| /// - replies data as provided by the underlying responders |
| /// - replies to "_services._dns-sd._udp.local." |
| /// |
| /// Maintains a stateful list of 'additional replies' that can be marked/unmarked |
| /// for query processing |
| class QueryResponderBase : public Responder // "_services._dns-sd._udp.local" |
| { |
| public: |
| /// Builds a new responder with the given storage for the response infos |
| QueryResponderBase(Internal::QueryResponderInfo * infos, size_t infoSizes); |
| virtual ~QueryResponderBase() {} |
| |
| /// Setup initial settings (clears all infos and sets up dns-sd query replies) |
| void Init(); |
| |
| /// Add a new responder to be processed |
| /// |
| /// Return valid QueryResponderSettings on add success. |
| QueryResponderSettings AddResponder(RecordResponder * responder); |
| |
| /// Implementation of the responder delegate. |
| /// |
| /// Adds responses for all known _dns-sd services. |
| void AddAllResponses(const chip::Inet::IPPacketInfo * source, ResponderDelegate * delegate) override; |
| |
| QueryResponderIterator begin(QueryResponderRecordFilter * filter) |
| { |
| return QueryResponderIterator(filter, mResponderInfos, mResponderInfoSize); |
| } |
| QueryResponderIterator end() { return QueryResponderIterator(); } |
| |
| /// Clear any items marked as 'additional'. |
| void ResetAdditionals(); |
| |
| /// Marks queries matching this qname as 'to be additionally reported' |
| /// @return the number of items marked new as 'additional data'. |
| size_t MarkAdditional(const FullQName & qname); |
| |
| /// Flag any additional responses required for the given iterator |
| void MarkAdditionalRepliesFor(QueryResponderIterator it); |
| |
| /// Resets the internal broadcast throttle setting to allow re-broadcasting |
| /// of all packets without a timedelay. |
| void ClearBroadcastThrottle(); |
| |
| private: |
| Internal::QueryResponderInfo * mResponderInfos; |
| size_t mResponderInfoSize; |
| }; |
| |
| template <size_t kSize> |
| class QueryResponder : public QueryResponderBase |
| { |
| public: |
| QueryResponder() : QueryResponderBase(mData, kSize) { Init(); } |
| |
| private: |
| Internal::QueryResponderInfo mData[kSize]; |
| }; |
| |
| } // namespace Minimal |
| } // namespace mdns |