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