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 1531 : void Clear() 48 : { 49 1531 : responder = nullptr; 50 1531 : reportService = false; 51 1531 : reportNowAsAdditional = false; 52 1531 : alsoReportAdditionalQName = false; 53 1531 : } 54 : }; 55 : 56 : } // namespace Internal 57 : 58 : /// Allows building query responder configuration 59 : class QueryResponderSettings 60 : { 61 : public: 62 100 : QueryResponderSettings() : mInfo(nullptr) {} 63 215 : 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 77 : QueryResponderSettings & SetReportInServiceListing(bool reportService) 68 : { 69 77 : if (IsValid()) 70 : { 71 77 : mInfo->reportService = reportService; 72 : } 73 77 : 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 117 : QueryResponderSettings & SetReportAdditional(const FullQName & qname) 82 : { 83 117 : if (IsValid()) 84 : { 85 117 : mInfo->alsoReportAdditionalQName = true; 86 117 : mInfo->additionalQName = qname; 87 : } 88 117 : return *this; 89 : } 90 : 91 351 : 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 560 : 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 411 : QueryResponderRecordFilter & SetIncludeAdditionalRepliesOnly(bool includeAdditionalRepliesOnly) 110 : { 111 411 : mIncludeAdditionalRepliesOnly = includeAdditionalRepliesOnly; 112 411 : 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 298 : QueryResponderRecordFilter & SetReplyFilter(ReplyFilter * replyFilter) 118 : { 119 298 : mReplyFilter = replyFilter; 120 298 : return *this; 121 : } 122 : 123 : /// Filter out anything that was multicast past ms. 124 : /// If ms is 0, no filtering is done 125 114 : QueryResponderRecordFilter & SetIncludeOnlyMulticastBeforeMS(chip::System::Clock::Timestamp time) 126 : { 127 114 : mIncludeOnlyMulticastBefore = time; 128 114 : return *this; 129 : } 130 : 131 10163 : bool Accept(Internal::QueryResponderInfo * record) const 132 : { 133 10163 : if (record->responder == nullptr) 134 : { 135 5031 : return false; 136 : } 137 : 138 5132 : if (mIncludeAdditionalRepliesOnly && !record->reportNowAsAdditional) 139 : { 140 2143 : return false; 141 : } 142 : 143 4155 : if ((mIncludeOnlyMulticastBefore > chip::System::Clock::kZero) && 144 1166 : (record->lastMulticastTime >= mIncludeOnlyMulticastBefore)) 145 : { 146 0 : return false; 147 : } 148 : 149 4950 : if ((mReplyFilter != nullptr) && 150 1961 : !mReplyFilter->Accept(record->responder->GetQType(), record->responder->GetQClass(), record->responder->GetQName())) 151 : { 152 805 : return false; 153 : } 154 2184 : 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 3138 : QueryResponderIterator() : mCurrent(nullptr), mRemaining(0) {} 173 964 : QueryResponderIterator(QueryResponderRecordFilter * recordFilter, Internal::QueryResponderInfo * pos, size_t size) : 174 964 : mFilter(recordFilter), mCurrent(pos), mRemaining(size) 175 : { 176 964 : SkipInvalid(); 177 964 : } 178 : QueryResponderIterator(const QueryResponderIterator & other) = default; 179 : QueryResponderIterator & operator=(const QueryResponderIterator & other) = default; 180 : 181 2174 : QueryResponderIterator & operator++() 182 : { 183 2174 : if (mRemaining != 0) 184 : { 185 2174 : mCurrent++; 186 2174 : mRemaining--; 187 : } 188 2174 : SkipInvalid(); 189 2174 : return *this; 190 : } 191 : 192 2174 : QueryResponderIterator operator++(int) 193 : { 194 2174 : QueryResponderIterator tmp(*this); 195 2174 : operator++(); 196 2174 : return tmp; 197 : } 198 : 199 : bool operator==(const QueryResponderIterator & rhs) const { return mCurrent == rhs.mCurrent; } 200 3138 : bool operator!=(const QueryResponderIterator & rhs) const { return mCurrent != rhs.mCurrent; } 201 : 202 : QueryResponderRecord & operator*() { return *mCurrent; } 203 1858 : QueryResponderRecord * operator->() { return mCurrent; } 204 : 205 2168 : 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 3138 : void SkipInvalid() 212 : { 213 11117 : while ((mRemaining > 0) && !mFilter->Accept(mCurrent)) 214 : { 215 7979 : mRemaining--; 216 7979 : mCurrent++; 217 : } 218 3138 : if (mRemaining == 0) 219 : { 220 954 : mCurrent = nullptr; 221 : } 222 3138 : } 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 11 : ~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 964 : QueryResponderIterator begin(QueryResponderRecordFilter * filter) 259 : { 260 964 : return QueryResponderIterator(filter, mResponderInfos, mResponderInfoSize); 261 : } 262 3138 : 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 108 : QueryResponder() : QueryResponderBase(mData, kSize) { Init(); } 288 : 289 : private: 290 : Internal::QueryResponderInfo mData[kSize]; 291 : }; 292 : 293 : } // namespace Minimal 294 : } // namespace mdns