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 : #include <lib/dnssd/IncrementalResolve.h>
18 :
19 : #include <lib/dnssd/IPAddressSorter.h>
20 : #include <lib/dnssd/ServiceNaming.h>
21 : #include <lib/dnssd/TxtFields.h>
22 : #include <lib/dnssd/minimal_mdns/Logging.h>
23 : #include <lib/dnssd/minimal_mdns/core/RecordWriter.h>
24 : #include <lib/support/CHIPMemString.h>
25 : #include <tracing/macros.h>
26 :
27 : namespace chip {
28 : namespace Dnssd {
29 :
30 : using namespace mdns::Minimal;
31 : using namespace mdns::Minimal::Logging;
32 :
33 : namespace {
34 :
35 264 : ByteSpan GetSpan(const mdns::Minimal::BytesRange & range)
36 : {
37 264 : return ByteSpan(range.Start(), range.Size());
38 : }
39 :
40 : /// Handles filling record data from TXT records.
41 : ///
42 : /// Supported records are whatever `FillNodeDataFromTxt` supports.
43 : template <class DataType>
44 : class TxtParser : public mdns::Minimal::TxtRecordDelegate
45 : {
46 : public:
47 27 : explicit TxtParser(DataType & data) : mData(data) {}
48 132 : void OnRecord(const mdns::Minimal::BytesRange & name, const mdns::Minimal::BytesRange & value) override
49 : {
50 132 : FillNodeDataFromTxt(GetSpan(name), GetSpan(value), mData);
51 132 : }
52 :
53 : private:
54 : DataType & mData;
55 : };
56 :
57 : // Common prefix to check for all operational/commissioner/commissionable name parts
58 : constexpr QNamePart kOperationalSuffix[] = { kOperationalServiceName, kOperationalProtocol, kLocalDomain };
59 : constexpr QNamePart kCommissionableSuffix[] = { kCommissionableServiceName, kCommissionProtocol, kLocalDomain };
60 : constexpr QNamePart kCommissionerSuffix[] = { kCommissionerServiceName, kCommissionProtocol, kLocalDomain };
61 :
62 18 : IncrementalResolver::ServiceNameType ComputeServiceNameType(SerializedQNameIterator name)
63 : {
64 : // SRV record names look like:
65 : // <compressed-fabric-id>-<node-id>._matter._tcp.local (operational)
66 : // <instance>._matterc._udp.local (commissionable)
67 : // <instance>._matterd._udp.local (commissioner)
68 :
69 : // Starts with compressed-fabric-node or instance, skip over it.
70 18 : if (!name.Next() || !name.IsValid())
71 : {
72 : // missing required components - empty service name
73 0 : return IncrementalResolver::ServiceNameType::kInvalid;
74 : }
75 :
76 18 : if (name == kOperationalSuffix)
77 : {
78 2 : return IncrementalResolver::ServiceNameType::kOperational;
79 : }
80 :
81 16 : if (name == kCommissionableSuffix)
82 : {
83 14 : return IncrementalResolver::ServiceNameType::kCommissionable;
84 : }
85 :
86 2 : if (name == kCommissionerSuffix)
87 : {
88 1 : return IncrementalResolver::ServiceNameType::kCommissioner;
89 : }
90 :
91 1 : return IncrementalResolver::ServiceNameType::kInvalid;
92 : }
93 :
94 : /// Automatically resets a IncrementalResolver to inactive in its destructor
95 : /// unless disarmed.
96 : ///
97 : /// Used for RAII for inactive reset
98 : class AutoInactiveResetter
99 : {
100 : public:
101 18 : explicit AutoInactiveResetter(IncrementalResolver & resolver) : mResolver(resolver) {}
102 18 : ~AutoInactiveResetter()
103 : {
104 18 : if (mArmed)
105 : {
106 1 : mResolver.ResetToInactive();
107 : }
108 18 : }
109 :
110 17 : void Disarm() { mArmed = false; }
111 :
112 : private:
113 : bool mArmed = true;
114 : IncrementalResolver & mResolver;
115 : };
116 :
117 : } // namespace
118 :
119 71 : CHIP_ERROR StoredServerName::Set(SerializedQNameIterator value)
120 : {
121 71 : chip::Encoding::BigEndian::BufferWriter output(mNameBuffer, sizeof(mNameBuffer));
122 71 : RecordWriter writer(&output);
123 :
124 71 : writer.WriteQName(value);
125 :
126 71 : if (!writer.Fit())
127 : {
128 25 : Clear();
129 25 : return CHIP_ERROR_NO_MEMORY;
130 : }
131 :
132 46 : return CHIP_NO_ERROR;
133 : }
134 :
135 143 : SerializedQNameIterator StoredServerName::Get() const
136 : {
137 143 : return SerializedQNameIterator(BytesRange(mNameBuffer, mNameBuffer + sizeof(mNameBuffer)), mNameBuffer);
138 : }
139 :
140 18 : CHIP_ERROR IncrementalResolver::InitializeParsing(mdns::Minimal::SerializedQNameIterator name, const uint64_t ttl,
141 : const mdns::Minimal::SrvRecord & srv)
142 : {
143 18 : AutoInactiveResetter inactiveReset(*this);
144 :
145 18 : ReturnErrorOnFailure(mRecordName.Set(name));
146 18 : ReturnErrorOnFailure(mTargetHostName.Set(srv.GetName()));
147 18 : mCommonResolutionData.port = srv.GetPort();
148 :
149 : {
150 : // TODO: Chip code historically seems to assume that the host name is of the
151 : // form "<MAC or 802.15.4 Extended Address in hex>.local" and only saves the first part.
152 : //
153 : // This should not be needed as server name should not be relevant once parsed.
154 : // The Resolver keeps track of this name for the purpose of address resolution
155 : // in mTargetHostName.
156 : //
157 : // Keeping track of this in resolution data does not seem useful and should be
158 : // removed.
159 18 : SerializedQNameIterator serverName = srv.GetName();
160 :
161 18 : VerifyOrReturnError(serverName.Next() && serverName.IsValid(), CHIP_ERROR_INVALID_ARGUMENT);
162 :
163 : // only save the first part: the MAC or 802.15.4 Extended Address in hex
164 18 : Platform::CopyString(mCommonResolutionData.hostName, serverName.Value());
165 : }
166 :
167 18 : mServiceNameType = ComputeServiceNameType(name);
168 :
169 18 : switch (mServiceNameType)
170 : {
171 2 : case ServiceNameType::kOperational:
172 2 : mSpecificResolutionData.Set<OperationalNodeData>();
173 : {
174 : // Operational addresses start with peer node information
175 2 : SerializedQNameIterator nameCopy = name;
176 2 : if (!nameCopy.Next() || !nameCopy.IsValid())
177 : {
178 0 : return CHIP_ERROR_INVALID_ARGUMENT;
179 : }
180 :
181 : CHIP_ERROR err =
182 2 : ExtractIdFromInstanceName(nameCopy.Value(), &mSpecificResolutionData.Get<OperationalNodeData>().peerId);
183 2 : if (err != CHIP_NO_ERROR)
184 : {
185 0 : return err;
186 : }
187 2 : mSpecificResolutionData.Get<OperationalNodeData>().hasZeroTTL = (ttl == 0);
188 : }
189 :
190 2 : LogFoundOperationalSrvRecord(mSpecificResolutionData.Get<OperationalNodeData>().peerId, mTargetHostName.Get());
191 2 : break;
192 15 : case ServiceNameType::kCommissioner:
193 : case ServiceNameType::kCommissionable:
194 15 : mSpecificResolutionData.Set<CommissionNodeData>();
195 :
196 : {
197 : // Commission addresses start with instance name
198 15 : SerializedQNameIterator nameCopy = name;
199 15 : if (!nameCopy.Next() || !nameCopy.IsValid())
200 : {
201 0 : return CHIP_ERROR_INVALID_ARGUMENT;
202 : }
203 :
204 15 : Platform::CopyString(mSpecificResolutionData.Get<CommissionNodeData>().instanceName, nameCopy.Value());
205 : }
206 :
207 15 : LogFoundCommissionSrvRecord(mSpecificResolutionData.Get<CommissionNodeData>().instanceName, mTargetHostName.Get());
208 15 : break;
209 1 : default:
210 1 : return CHIP_ERROR_INVALID_ARGUMENT;
211 : }
212 :
213 17 : inactiveReset.Disarm();
214 17 : return CHIP_NO_ERROR;
215 18 : }
216 :
217 20 : IncrementalResolver::RequiredInformationFlags IncrementalResolver::GetMissingRequiredInformation() const
218 : {
219 20 : RequiredInformationFlags flags;
220 :
221 20 : if (!mSpecificResolutionData.Valid())
222 : {
223 1 : flags.Set(RequiredInformationBitFlags::kSrvInitialization);
224 : }
225 : else
226 : {
227 19 : if (mCommonResolutionData.numIPs == 0)
228 : {
229 5 : flags.Set(RequiredInformationBitFlags::kIpAddress);
230 : }
231 : }
232 :
233 20 : return flags;
234 : }
235 :
236 117 : CHIP_ERROR IncrementalResolver::OnRecord(Inet::InterfaceId interface, const ResourceData & data, BytesRange packetRange)
237 : {
238 117 : if (!IsActive())
239 : {
240 0 : return CHIP_NO_ERROR; // nothing to parse
241 : }
242 :
243 117 : switch (data.GetType())
244 : {
245 16 : case QType::TXT:
246 16 : if (data.GetName() != mRecordName.Get())
247 : {
248 : MATTER_TRACE_INSTANT("TXT not applicable", "Resolver");
249 2 : return CHIP_NO_ERROR;
250 : }
251 14 : return OnTxtRecord(data, packetRange);
252 12 : case QType::A: {
253 12 : if (data.GetName() != mTargetHostName.Get())
254 : {
255 : MATTER_TRACE_INSTANT("IPv4 not applicable", "Resolver");
256 0 : return CHIP_NO_ERROR;
257 : }
258 :
259 : #if INET_CONFIG_ENABLE_IPV4
260 : Inet::IPAddress addr;
261 12 : if (!ParseARecord(data.GetData(), &addr))
262 : {
263 0 : return CHIP_ERROR_INVALID_ARGUMENT;
264 : }
265 :
266 12 : return OnIpAddress(interface, addr);
267 : #else
268 : #if CHIP_MINMDNS_HIGH_VERBOSITY
269 : ChipLogProgress(Discovery, "Ignoring A record: IPv4 not supported");
270 : #endif
271 : // skipping IPv4 addresses
272 : return CHIP_NO_ERROR;
273 : #endif
274 : }
275 17 : case QType::AAAA: {
276 17 : if (data.GetName() != mTargetHostName.Get())
277 : {
278 : MATTER_TRACE_INSTANT("IPv6 not applicable", "Resolver");
279 2 : return CHIP_NO_ERROR;
280 : }
281 :
282 : Inet::IPAddress addr;
283 15 : if (!ParseAAAARecord(data.GetData(), &addr))
284 : {
285 0 : return CHIP_ERROR_INVALID_ARGUMENT;
286 : }
287 :
288 15 : return OnIpAddress(interface, addr);
289 : }
290 72 : case QType::SRV: // SRV handled on creation, ignored for 'additional data'
291 : default:
292 : // Other types not interesting during parsing
293 72 : return CHIP_NO_ERROR;
294 : }
295 :
296 : return CHIP_NO_ERROR;
297 : }
298 :
299 14 : CHIP_ERROR IncrementalResolver::OnTxtRecord(const ResourceData & data, BytesRange packetRange)
300 : {
301 : {
302 14 : TxtParser<CommonResolutionData> delegate(mCommonResolutionData);
303 14 : if (!ParseTxtRecord(data.GetData(), &delegate))
304 : {
305 0 : return CHIP_ERROR_INVALID_ARGUMENT;
306 : }
307 14 : }
308 :
309 14 : if (IsActiveCommissionParse())
310 : {
311 13 : TxtParser<CommissionNodeData> delegate(mSpecificResolutionData.Get<CommissionNodeData>());
312 13 : if (!ParseTxtRecord(data.GetData(), &delegate))
313 : {
314 0 : return CHIP_ERROR_INVALID_ARGUMENT;
315 : }
316 13 : }
317 :
318 14 : return CHIP_NO_ERROR;
319 : }
320 :
321 27 : CHIP_ERROR IncrementalResolver::OnIpAddress(Inet::InterfaceId interface, const Inet::IPAddress & addr)
322 : {
323 27 : if (mCommonResolutionData.numIPs >= ArraySize(mCommonResolutionData.ipAddress))
324 : {
325 0 : return CHIP_ERROR_NO_MEMORY;
326 : }
327 :
328 27 : if (!mCommonResolutionData.interfaceId.IsPresent())
329 : {
330 15 : mCommonResolutionData.interfaceId = interface;
331 : }
332 12 : else if (mCommonResolutionData.interfaceId != interface)
333 : {
334 : // IP addresses received from multiple packets over different interfaces.
335 : // Processing is assumed per single interface.
336 0 : return CHIP_ERROR_INVALID_ARGUMENT;
337 : }
338 :
339 27 : mCommonResolutionData.ipAddress[mCommonResolutionData.numIPs++] = addr;
340 :
341 27 : LogFoundIPAddress(mTargetHostName.Get(), addr);
342 :
343 27 : return CHIP_NO_ERROR;
344 : }
345 :
346 13 : CHIP_ERROR IncrementalResolver::Take(DiscoveredNodeData & outputData)
347 : {
348 13 : VerifyOrReturnError(IsActiveCommissionParse(), CHIP_ERROR_INCORRECT_STATE);
349 :
350 13 : IPAddressSorter::Sort(mCommonResolutionData.ipAddress, mCommonResolutionData.numIPs, mCommonResolutionData.interfaceId);
351 :
352 : // Set the DiscoveredNodeData with CommissionNodeData info specific to commissionable/commisssioner
353 : // node available in mSpecificResolutionData.
354 13 : outputData.Set<CommissionNodeData>(mSpecificResolutionData.Get<CommissionNodeData>());
355 :
356 : // IncrementalResolver stored CommonResolutionData separately in mCommonResolutionData hence copy the
357 : // CommonResolutionData info from mCommonResolutionData, to CommissionNodeData within DiscoveredNodeData
358 13 : CommonResolutionData & resolutionData = outputData.Get<CommissionNodeData>();
359 13 : resolutionData = mCommonResolutionData;
360 :
361 13 : ResetToInactive();
362 :
363 13 : return CHIP_NO_ERROR;
364 : }
365 :
366 1 : CHIP_ERROR IncrementalResolver::Take(ResolvedNodeData & outputData)
367 : {
368 1 : VerifyOrReturnError(IsActiveOperationalParse(), CHIP_ERROR_INCORRECT_STATE);
369 :
370 1 : outputData.resolutionData = mCommonResolutionData;
371 1 : outputData.operationalData = mSpecificResolutionData.Get<OperationalNodeData>();
372 :
373 1 : ResetToInactive();
374 :
375 1 : return CHIP_NO_ERROR;
376 : }
377 :
378 : } // namespace Dnssd
379 : } // namespace chip
|