3 * Copyright (c) 2020 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 #include "ReplyFilter.h"
21 #include "Responder.h"
23 #include <inet/InetLayer.h>
28 /// Represents available data (replies) for mDNS queries.
29 struct QueryResponderRecord
31 Responder * responder = nullptr; // what response/data is available
32 bool reportService = false; // report as a service when listing dnssd services
33 uint64_t lastMulticastTime = 0; // last time this record was multicast
38 /// Internal information for query responder records.
39 struct QueryResponderInfo : public QueryResponderRecord
41 bool reportNowAsAdditional; // report as additional data required
43 bool alsoReportAdditionalQName = false; // report more data when this record is listed
44 FullQName additionalQName; // if alsoReportAdditionalQName is set, send this extra data
49 reportService = false;
50 reportNowAsAdditional = false;
51 alsoReportAdditionalQName = false;
55 } // namespace Internal
57 /// Allows building query responder configuration
58 class QueryResponderSettings
61 QueryResponderSettings() : mInfo(nullptr) {}
62 QueryResponderSettings(Internal::QueryResponderInfo * info) : mInfo(info) {}
63 QueryResponderSettings(const QueryResponderSettings & other) = default;
65 /// This record should be part of dns-sd service listing requests
66 QueryResponderSettings & SetReportInServiceListing(bool reportService)
70 mInfo->reportService = reportService;
75 /// When this record is send back, additional records should also be provided.
77 /// This is useful to avoid chattyness by sending back referenced records
78 /// (e.g. when sending a PTR record, send the corresponding SRV and when sending
79 /// SRV, send back the corresponding A/AAAA records).
80 QueryResponderSettings & SetReportAdditional(const FullQName & qname)
84 mInfo->alsoReportAdditionalQName = true;
85 mInfo->additionalQName = qname;
90 bool IsValid() const { return mInfo != nullptr; }
93 Internal::QueryResponderInfo * mInfo;
96 /// Determines what query records should be included in a response.
98 /// Provides an 'Accept' method to determine if a reply is to be sent or not.
99 class QueryResponderRecordFilter
102 /// Default contstructor accepts everything that is not null
103 QueryResponderRecordFilter() {}
104 QueryResponderRecordFilter(const QueryResponderRecordFilter & other) = default;
105 QueryResponderRecordFilter & operator=(const QueryResponderRecordFilter & other) = default;
107 /// Set if to include only items marked as 'additional reply' or everything.
108 QueryResponderRecordFilter & SetIncludeAdditionalRepliesOnly(bool includeAdditionalRepliesOnly)
110 mIncludeAdditionalRepliesOnly = includeAdditionalRepliesOnly;
114 /// Filter out anything rejected by the given reply filter.
115 /// If replyFilter is nullptr, no such filtering is applied.
116 QueryResponderRecordFilter & SetReplyFilter(ReplyFilter * replyFilter)
118 mReplyFilter = replyFilter;
122 /// Filter out anything that was multicast past ms.
123 /// If ms is 0, no filtering is done
124 QueryResponderRecordFilter & SetIncludeOnlyMulticastBeforeMS(uint64_t ms)
126 mIncludeOnlyMulticastBeforeMS = ms;
130 bool Accept(Internal::QueryResponderInfo * record) const
132 if (record->responder == nullptr)
137 if (mIncludeAdditionalRepliesOnly && !record->reportNowAsAdditional)
142 if ((mIncludeOnlyMulticastBeforeMS > 0) && (record->lastMulticastTime >= mIncludeOnlyMulticastBeforeMS))
147 if ((mReplyFilter != nullptr) &&
148 !mReplyFilter->Accept(record->responder->GetQType(), record->responder->GetQClass(), record->responder->GetQName()))
156 bool mIncludeAdditionalRepliesOnly = false;
157 ReplyFilter * mReplyFilter = nullptr;
158 uint64_t mIncludeOnlyMulticastBeforeMS = 0;
161 /// Iterates over an array of QueryResponderRecord items, providing only 'valid' ones, where
162 /// valid is based on the provided filter.
163 class QueryResponderIterator
166 using value_type = QueryResponderRecord;
167 using pointer = QueryResponderRecord *;
168 using reference = QueryResponderRecord &;
170 QueryResponderIterator() : mCurrent(nullptr), mRemaining(0) {}
171 QueryResponderIterator(QueryResponderRecordFilter * recordFilter, Internal::QueryResponderInfo * pos, size_t size) :
172 mFilter(recordFilter), mCurrent(pos), mRemaining(size)
176 QueryResponderIterator(const QueryResponderIterator & other) = default;
177 QueryResponderIterator & operator=(const QueryResponderIterator & other) = default;
179 QueryResponderIterator & operator++()
190 QueryResponderIterator operator++(int)
192 QueryResponderIterator tmp(*this);
197 bool operator==(const QueryResponderIterator & rhs) const { return mCurrent == rhs.mCurrent; }
198 bool operator!=(const QueryResponderIterator & rhs) const { return mCurrent != rhs.mCurrent; }
200 QueryResponderRecord & operator*() { return *mCurrent; }
201 QueryResponderRecord * operator->() { return mCurrent; }
203 Internal::QueryResponderInfo * GetInternal() { return mCurrent; }
204 const Internal::QueryResponderInfo * GetInternal() const { return mCurrent; }
207 /// Skips invalid/not useful values.
208 /// ensures that if mRemaining is 0, mCurrent is nullptr;
211 while ((mRemaining > 0) && !mFilter->Accept(mCurrent))
222 QueryResponderRecordFilter * mFilter;
223 Internal::QueryResponderInfo * mCurrent;
227 /// Responds to mDNS queries.
230 /// - replies data as provided by the underlying responders
231 /// - replies to "_services._dns-sd._udp.local."
233 /// Maintains a stateful list of 'additional replies' that can be marked/unmarked
234 /// for query processing
235 class QueryResponderBase : public Responder // "_services._dns-sd._udp.local"
238 /// Builds a new responder with the given storage for the response infos
239 QueryResponderBase(Internal::QueryResponderInfo * infos, size_t infoSizes);
240 virtual ~QueryResponderBase() {}
242 /// Setup initial settings (clears all infos and sets up dns-sd query replies)
245 /// Add a new responder to be processed
247 /// Return valid QueryResponderSettings on add success.
248 QueryResponderSettings AddResponder(Responder * responder);
250 /// Implementation of the responder delegate.
252 /// Adds responses for all known _dns-sd services.
253 void AddAllResponses(const chip::Inet::IPPacketInfo * source, ResponderDelegate * delegate) override;
255 QueryResponderIterator begin(QueryResponderRecordFilter * filter)
257 return QueryResponderIterator(filter, mResponderInfos, mResponderInfoSize);
259 QueryResponderIterator end() { return QueryResponderIterator(); }
261 /// Clear any items marked as 'additional'.
262 void ResetAdditionals();
264 /// Marks queries matching this qname as 'to be additionally reported'
265 /// @return the number of items marked new as 'additional data'.
266 size_t MarkAdditional(const FullQName & qname);
268 /// Flag any additional responses required for the given iterator
269 void MarkAdditionalRepliesFor(QueryResponderIterator it);
271 /// Resets the internal broadcast throttle setting to allow re-broadcasting
272 /// of all packets without a timedelay.
273 void ClearBroadcastThrottle();
276 Internal::QueryResponderInfo * mResponderInfos;
277 size_t mResponderInfoSize;
280 template <size_t kSize>
281 class QueryResponder : public QueryResponderBase
284 QueryResponder() : QueryResponderBase(mData, kSize) { Init(); }
287 Internal::QueryResponderInfo mData[kSize];
290 } // namespace Minimal