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