Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
4 : *
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
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
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.
16 : */
17 :
18 : #pragma once
19 :
20 : #include "RecordResponder.h"
21 : #include "ReplyFilter.h"
22 : #include "Responder.h"
23 :
24 : #include <system/SystemClock.h>
25 :
26 : namespace mdns {
27 : namespace Minimal {
28 :
29 : /// Represents available data (replies) for mDNS queries.
30 : struct QueryResponderRecord
31 : {
32 : Responder * responder = nullptr; // what response/data is available
33 : bool reportService = false; // report as a service when listing dnssd services
34 : chip::System::Clock::Timestamp lastMulticastTime = chip::System::Clock::kZero; // last time this record was multicast
35 : };
36 :
37 : namespace Internal {
38 :
39 : /// Internal information for query responder records.
40 : struct QueryResponderInfo : public QueryResponderRecord
41 : {
42 : bool reportNowAsAdditional; // report as additional data required
43 :
44 : bool alsoReportAdditionalQName = false; // report more data when this record is listed
45 : FullQName additionalQName; // if alsoReportAdditionalQName is set, send this extra data
46 :
47 1510 : void Clear()
48 : {
49 1510 : responder = nullptr;
50 1510 : reportService = false;
51 1510 : reportNowAsAdditional = false;
52 1510 : alsoReportAdditionalQName = false;
53 1510 : }
54 : };
55 :
56 : } // namespace Internal
57 :
58 : /// Allows building query responder configuration
59 : class QueryResponderSettings
60 : {
61 : public:
62 100 : QueryResponderSettings() : mInfo(nullptr) {}
63 206 : QueryResponderSettings(Internal::QueryResponderInfo * info) : mInfo(info) {}
64 : QueryResponderSettings(const QueryResponderSettings & other) = default;
65 :
66 : /// This record should be part of dns-sd service listing requests
67 72 : QueryResponderSettings & SetReportInServiceListing(bool reportService)
68 : {
69 72 : if (IsValid())
70 : {
71 72 : mInfo->reportService = reportService;
72 : }
73 72 : return *this;
74 : }
75 :
76 : /// When this record is send back, additional records should also be provided.
77 : ///
78 : /// This is useful to avoid chattyness by sending back referenced records
79 : /// (e.g. when sending a PTR record, send the corresponding SRV and when sending
80 : /// SRV, send back the corresponding A/AAAA records).
81 110 : QueryResponderSettings & SetReportAdditional(const FullQName & qname)
82 : {
83 110 : if (IsValid())
84 : {
85 110 : mInfo->alsoReportAdditionalQName = true;
86 110 : mInfo->additionalQName = qname;
87 : }
88 110 : return *this;
89 : }
90 :
91 330 : bool IsValid() const { return mInfo != nullptr; }
92 :
93 : private:
94 : Internal::QueryResponderInfo * mInfo;
95 : };
96 :
97 : /// Determines what query records should be included in a response.
98 : ///
99 : /// Provides an 'Accept' method to determine if a reply is to be sent or not.
100 : class QueryResponderRecordFilter
101 : {
102 : public:
103 : /// Default contstructor accepts everything that is not null
104 520 : QueryResponderRecordFilter() {}
105 : QueryResponderRecordFilter(const QueryResponderRecordFilter & other) = default;
106 : QueryResponderRecordFilter & operator=(const QueryResponderRecordFilter & other) = default;
107 :
108 : /// Set if to include only items marked as 'additional reply' or everything.
109 385 : QueryResponderRecordFilter & SetIncludeAdditionalRepliesOnly(bool includeAdditionalRepliesOnly)
110 : {
111 385 : mIncludeAdditionalRepliesOnly = includeAdditionalRepliesOnly;
112 385 : return *this;
113 : }
114 :
115 : /// Filter out anything rejected by the given reply filter.
116 : /// If replyFilter is nullptr, no such filtering is applied.
117 270 : QueryResponderRecordFilter & SetReplyFilter(ReplyFilter * replyFilter)
118 : {
119 270 : mReplyFilter = replyFilter;
120 270 : return *this;
121 : }
122 :
123 : /// Filter out anything that was multicast past ms.
124 : /// If ms is 0, no filtering is done
125 100 : QueryResponderRecordFilter & SetIncludeOnlyMulticastBeforeMS(chip::System::Clock::Timestamp time)
126 : {
127 100 : mIncludeOnlyMulticastBefore = time;
128 100 : return *this;
129 : }
130 :
131 9339 : bool Accept(Internal::QueryResponderInfo * record) const
132 : {
133 9339 : if (record->responder == nullptr)
134 : {
135 4499 : return false;
136 : }
137 :
138 4840 : if (mIncludeAdditionalRepliesOnly && !record->reportNowAsAdditional)
139 : {
140 2007 : return false;
141 : }
142 :
143 3917 : if ((mIncludeOnlyMulticastBefore > chip::System::Clock::kZero) &&
144 1084 : (record->lastMulticastTime >= mIncludeOnlyMulticastBefore))
145 : {
146 0 : return false;
147 : }
148 :
149 4686 : if ((mReplyFilter != nullptr) &&
150 1853 : !mReplyFilter->Accept(record->responder->GetQType(), record->responder->GetQClass(), record->responder->GetQName()))
151 : {
152 781 : return false;
153 : }
154 2052 : return true;
155 : }
156 :
157 : private:
158 : bool mIncludeAdditionalRepliesOnly = false;
159 : ReplyFilter * mReplyFilter = nullptr;
160 : chip::System::Clock::Timestamp mIncludeOnlyMulticastBefore = chip::System::Clock::kZero;
161 : };
162 :
163 : /// Iterates over an array of QueryResponderRecord items, providing only 'valid' ones, where
164 : /// valid is based on the provided filter.
165 : class QueryResponderIterator
166 : {
167 : public:
168 : using value_type = QueryResponderRecord;
169 : using pointer = QueryResponderRecord *;
170 : using reference = QueryResponderRecord &;
171 :
172 2936 : QueryResponderIterator() : mCurrent(nullptr), mRemaining(0) {}
173 894 : QueryResponderIterator(QueryResponderRecordFilter * recordFilter, Internal::QueryResponderInfo * pos, size_t size) :
174 894 : mFilter(recordFilter), mCurrent(pos), mRemaining(size)
175 : {
176 894 : SkipInvalid();
177 894 : }
178 : QueryResponderIterator(const QueryResponderIterator & other) = default;
179 : QueryResponderIterator & operator=(const QueryResponderIterator & other) = default;
180 :
181 2042 : QueryResponderIterator & operator++()
182 : {
183 2042 : if (mRemaining != 0)
184 : {
185 2042 : mCurrent++;
186 2042 : mRemaining--;
187 : }
188 2042 : SkipInvalid();
189 2042 : return *this;
190 : }
191 :
192 2042 : QueryResponderIterator operator++(int)
193 : {
194 2042 : QueryResponderIterator tmp(*this);
195 2042 : operator++();
196 2042 : return tmp;
197 : }
198 :
199 : bool operator==(const QueryResponderIterator & rhs) const { return mCurrent == rhs.mCurrent; }
200 2936 : bool operator!=(const QueryResponderIterator & rhs) const { return mCurrent != rhs.mCurrent; }
201 :
202 : QueryResponderRecord & operator*() { return *mCurrent; }
203 1716 : QueryResponderRecord * operator->() { return mCurrent; }
204 :
205 2044 : Internal::QueryResponderInfo * GetInternal() { return mCurrent; }
206 : const Internal::QueryResponderInfo * GetInternal() const { return mCurrent; }
207 :
208 : private:
209 : /// Skips invalid/not useful values.
210 : /// ensures that if mRemaining is 0, mCurrent is nullptr;
211 2936 : void SkipInvalid()
212 : {
213 10223 : while ((mRemaining > 0) && !mFilter->Accept(mCurrent))
214 : {
215 7287 : mRemaining--;
216 7287 : mCurrent++;
217 : }
218 2936 : if (mRemaining == 0)
219 : {
220 884 : mCurrent = nullptr;
221 : }
222 2936 : }
223 :
224 : QueryResponderRecordFilter * mFilter;
225 : Internal::QueryResponderInfo * mCurrent;
226 : size_t mRemaining;
227 : };
228 :
229 : /// Responds to mDNS queries.
230 : ///
231 : /// In particular:
232 : /// - replies data as provided by the underlying responders
233 : /// - replies to "_services._dns-sd._udp.local."
234 : ///
235 : /// Maintains a stateful list of 'additional replies' that can be marked/unmarked
236 : /// for query processing
237 : class QueryResponderBase : public Responder // "_services._dns-sd._udp.local"
238 : {
239 : public:
240 : /// Builds a new responder with the given storage for the response infos
241 : QueryResponderBase(Internal::QueryResponderInfo * infos, size_t infoSizes);
242 13 : ~QueryResponderBase() override {}
243 :
244 : /// Setup initial settings (clears all infos and sets up dns-sd query replies)
245 : void Init();
246 :
247 : /// Add a new responder to be processed
248 : ///
249 : /// Return valid QueryResponderSettings on add success.
250 : QueryResponderSettings AddResponder(RecordResponder * responder);
251 :
252 : /// Implementation of the responder delegate.
253 : ///
254 : /// Adds responses for all known _dns-sd services.
255 : void AddAllResponses(const chip::Inet::IPPacketInfo * source, ResponderDelegate * delegate,
256 : const ResponseConfiguration & configuration) override;
257 :
258 894 : QueryResponderIterator begin(QueryResponderRecordFilter * filter)
259 : {
260 894 : return QueryResponderIterator(filter, mResponderInfos, mResponderInfoSize);
261 : }
262 2936 : QueryResponderIterator end() { return QueryResponderIterator(); }
263 :
264 : /// Clear any items marked as 'additional'.
265 : void ResetAdditionals();
266 :
267 : /// Marks queries matching this qname as 'to be additionally reported'
268 : /// @return the number of items marked new as 'additional data'.
269 : size_t MarkAdditional(const FullQName & qname);
270 :
271 : /// Flag any additional responses required for the given iterator
272 : void MarkAdditionalRepliesFor(QueryResponderIterator it);
273 :
274 : /// Resets the internal broadcast throttle setting to allow re-broadcasting
275 : /// of all packets without a timedelay.
276 : void ClearBroadcastThrottle();
277 :
278 : private:
279 : Internal::QueryResponderInfo * mResponderInfos;
280 : size_t mResponderInfoSize;
281 : };
282 :
283 : template <size_t kSize>
284 : class QueryResponder : public QueryResponderBase
285 : {
286 : public:
287 134 : QueryResponder() : QueryResponderBase(mData, kSize) { Init(); }
288 :
289 : private:
290 : Internal::QueryResponderInfo mData[kSize];
291 : };
292 :
293 : } // namespace Minimal
294 : } // namespace mdns
|