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