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