Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2022 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 : #pragma once
18 :
19 : #include <lib/dnssd/Resolver.h>
20 : #include <lib/dnssd/Types.h>
21 : #include <lib/dnssd/minimal_mdns/Parser.h>
22 : #include <lib/dnssd/minimal_mdns/RecordData.h>
23 : #include <lib/dnssd/minimal_mdns/core/QName.h>
24 : #include <lib/support/BitFlags.h>
25 : #include <lib/support/Variant.h>
26 :
27 : namespace chip {
28 : namespace Dnssd {
29 :
30 : /// Allows storing and retrieving of a SerializedQName for later recall.
31 : ///
32 : /// This is a convenience storage as MDNS generally provides data as QNames
33 : /// and comparisons between QNames is more convenient than replacing them with
34 : /// null-terminated character strings.
35 : class StoredServerName
36 : {
37 : public:
38 252 : StoredServerName() {}
39 :
40 25 : void Clear() { memset(mNameBuffer, 0, sizeof(mNameBuffer)); }
41 :
42 : /// Set the underlying value. Will return CHIP_ERROR_NO_MEMORY
43 : /// on insufficient storage space.
44 : ///
45 : /// If insufficient space, buffer will be cleared.
46 : CHIP_ERROR Set(mdns::Minimal::SerializedQNameIterator value);
47 :
48 : /// Return the underlying value in this object.
49 : ///
50 : /// Value valid as long as this object is valid and underlying Set() is
51 : /// not called.
52 : mdns::Minimal::SerializedQNameIterator Get() const;
53 :
54 : private:
55 : // Try to have space for at least:
56 : // L1234._sub._matterc._udp.local => 30 chars
57 : // <fabric>-<node>._matter._tcp.local => 52 chars
58 : // <hostname>.local (where hostname is kHostNameMaxLength == 16)
59 : //
60 : // This does not try to optimize out ".local" suffix which is always expected
61 : // since comparisons are easier when suffix is still present.
62 : static constexpr size_t kMaxStoredNameLength = 64;
63 :
64 : uint8_t mNameBuffer[kMaxStoredNameLength] = {};
65 : };
66 :
67 : /// Incrementally accumulates data from DNSSD packets. It is geared twoards
68 : /// resource-constrained dns-sd querier implementations.
69 : ///
70 : /// It all starts with processing SRV records which define the type of record
71 : /// (could be operational, commissionable or commissioner), after which the
72 : /// additional data is accumulated, specifically TXT information and A/AAAA
73 : ///
74 : /// Class can also be used to determine what additional data is missing from a
75 : /// record so that additional DNSSD queries can be made recursively (e.g. if
76 : /// only a SRV/TXT records are available, ask for AAAA records).
77 : class IncrementalResolver
78 : {
79 : public:
80 : // Elements that the incremental resolve still needs
81 : enum class RequiredInformationBitFlags : uint8_t
82 : {
83 : kSrvInitialization = (1 << 0), // server being initialized
84 : kIpAddress = (1 << 1), // IP address missing
85 : };
86 : using RequiredInformationFlags = BitFlags<RequiredInformationBitFlags>;
87 :
88 : // Type of service name that is contained in this resolver
89 : enum class ServiceNameType
90 : {
91 : kInvalid, // not a matter service name
92 : kOperational,
93 : kCommissioner,
94 : kCommissionable,
95 : };
96 :
97 126 : IncrementalResolver() = default;
98 :
99 : /// Checks if object has been initialized using the `InitializeParsing`
100 : /// method.
101 384 : bool IsActive() const { return mSpecificResolutionData.Valid(); }
102 :
103 36 : bool IsActiveCommissionParse() const { return mSpecificResolutionData.Is<CommissionNodeData>(); }
104 0 : bool IsActiveOperationalParse() const { return mSpecificResolutionData.Is<OperationalNodeData>(); }
105 :
106 12 : ServiceNameType GetCurrentType() const { return mServiceNameType; }
107 :
108 0 : PeerId OperationalParsePeerId() const
109 : {
110 0 : VerifyOrReturnValue(IsActiveOperationalParse(), PeerId());
111 0 : return mSpecificResolutionData.Get<OperationalNodeData>().peerId;
112 : }
113 :
114 : /// Start parsing a new record. SRV records are the records we are mainly
115 : /// interested on, after which TXT and A/AAAA are looked for.
116 : ///
117 : /// If this function returns with error, the object will be in an inactive state.
118 : CHIP_ERROR InitializeParsing(mdns::Minimal::SerializedQNameIterator name, const uint64_t ttl,
119 : const mdns::Minimal::SrvRecord & srv);
120 :
121 : /// Notify that a new record is being processed.
122 : /// Will handle filtering and processing of data to determine if the entry is relevant for
123 : /// the current resolver.
124 : ///
125 : /// Providing a data that is not relevant to the current parser is not considered and error,
126 : /// however if the resource fails parsing completely an error will be returned.
127 : ///
128 : ///
129 : /// [data] represents the record received via [interface] and [packetRange] represents the range
130 : /// of valid bytes within the packet for the purpose of QName parsing
131 : CHIP_ERROR OnRecord(Inet::InterfaceId interface, const mdns::Minimal::ResourceData & data,
132 : mdns::Minimal::BytesRange packetRange);
133 :
134 : /// Return what additional data is required until the object can be extracted
135 : ///
136 : /// If `!GetREquiredInformation().HasAny()` the parsed information is ready
137 : /// to be processed.
138 : RequiredInformationFlags GetMissingRequiredInformation() const;
139 :
140 : /// Fetch the target host name set by `InitializeParsing`
141 : ///
142 : /// VALIDITY: Data references internal storage of this object and is valid as long
143 : /// as this object is valid and InitializeParsing is not called again.
144 0 : mdns::Minimal::SerializedQNameIterator GetTargetHostName() const { return mTargetHostName.Get(); }
145 :
146 : /// Fetch the record name set by `InitializeParsing`.
147 : ///
148 : /// VALIDITY: Data references internal storage of this object and is valid as long
149 : /// as this object is valid and InitializeParsing is not called again.
150 0 : mdns::Minimal::SerializedQNameIterator GetRecordName() const { return mRecordName.Get(); }
151 :
152 : /// Take the current value of the object and clear it once returned.
153 : ///
154 : /// Object must be in `IsActive()` for this to succeed.
155 : /// Data will be returned (and cleared) even if not yet complete based
156 : /// on `GetMissingRequiredInformation()`. This method takes as much data as
157 : /// it was parsed so far.
158 : CHIP_ERROR Take(DiscoveredNodeData & outputData);
159 :
160 : /// Take the current value of the object and clear it once returned.
161 : ///
162 : /// Object must be in `IsActiveOperationalParse()` for this to succeed.
163 : /// Data will be returned (and cleared) even if not yet complete based
164 : /// on `GetMissingRequiredInformation()`. This method takes as much data as
165 : /// it was parsed so far.
166 : CHIP_ERROR Take(ResolvedNodeData & outputData);
167 :
168 : /// Clears current state, setting as inactive
169 15 : void ResetToInactive()
170 : {
171 15 : mCommonResolutionData.Reset();
172 15 : mSpecificResolutionData = ParsedRecordSpecificData();
173 15 : }
174 :
175 : private:
176 : /// Notify that a PTR record can be parsed.
177 : ///
178 : /// Input data MUST have GetType() == QType::TXT
179 : CHIP_ERROR OnTxtRecord(const mdns::Minimal::ResourceData & data, mdns::Minimal::BytesRange packetRange);
180 :
181 : /// Notify that a new IP address has been found.
182 : ///
183 : /// This is to be called on both A (if IPv4 support is enabled) and AAAA
184 : /// addresses.
185 : ///
186 : /// Prerequisite: IP address belongs to the right nost name
187 : CHIP_ERROR OnIpAddress(Inet::InterfaceId interface, const Inet::IPAddress & addr);
188 :
189 : using ParsedRecordSpecificData = Variant<OperationalNodeData, CommissionNodeData>;
190 :
191 : StoredServerName mRecordName; // Record name for what is parsed (SRV/PTR/TXT)
192 : StoredServerName mTargetHostName; // `Target` for the SRV record
193 : ServiceNameType mServiceNameType = ServiceNameType::kInvalid;
194 : CommonResolutionData mCommonResolutionData;
195 : ParsedRecordSpecificData mSpecificResolutionData;
196 : };
197 :
198 : } // namespace Dnssd
199 : } // namespace chip
|