Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 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 :
18 : #include "TxtFields.h"
19 :
20 : #include <algorithm>
21 : #include <cctype>
22 : #include <climits>
23 : #include <cstdio>
24 : #include <inttypes.h>
25 : #include <limits>
26 : #include <optional>
27 : #include <stdlib.h>
28 : #include <string.h>
29 :
30 : #include <lib/core/CHIPSafeCasts.h>
31 : #include <lib/dnssd/Advertiser.h>
32 : #include <lib/dnssd/Resolver.h>
33 : #include <lib/support/BytesToHex.h>
34 : #include <lib/support/CHIPMemString.h>
35 : #include <lib/support/SafeInt.h>
36 :
37 : namespace chip {
38 : namespace Dnssd {
39 :
40 : namespace Internal {
41 :
42 : constexpr uint8_t kTCPClient = 1;
43 : constexpr uint8_t kTCPServer = 2;
44 :
45 : namespace {
46 :
47 1726 : char SafeToLower(uint8_t ch)
48 : {
49 1726 : return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
50 : }
51 1785 : bool IsKey(const ByteSpan & key, const char * desired)
52 : {
53 1785 : if (key.size() != strlen(desired))
54 : {
55 1241 : return false;
56 : }
57 :
58 544 : auto desired_bytes = Uint8::from_const_char(desired);
59 1053 : for (size_t i = 0; i < key.size(); ++i)
60 : {
61 863 : if (SafeToLower(key.data()[i]) != SafeToLower(desired_bytes[i]))
62 : {
63 354 : return false;
64 : }
65 : }
66 190 : return true;
67 : }
68 :
69 153 : uint32_t MakeU32FromAsciiDecimal(const ByteSpan & val, uint32_t defaultValue = 0)
70 : {
71 : // +1 because `digits10` means the number of decimal digits that fit in `uint32_t`,
72 : // not how many digits are enough to represent any `uint32_t`
73 : // +1 for null-terminator
74 : char nullTerminatedValue[std::numeric_limits<uint32_t>::digits10 + 2];
75 :
76 : // value is too long to store `uint32_t`
77 153 : if (val.size() >= sizeof(nullTerminatedValue))
78 6 : return defaultValue;
79 :
80 : // value contains leading zeros
81 147 : if (val.size() > 1 && *val.data() == static_cast<uint8_t>('0'))
82 12 : return defaultValue;
83 :
84 135 : Platform::CopyString(nullTerminatedValue, sizeof(nullTerminatedValue), val);
85 :
86 : char * endPtr;
87 135 : unsigned long num = strtoul(nullTerminatedValue, &endPtr, 10);
88 :
89 135 : if (endPtr > nullTerminatedValue && *endPtr == '\0' && num != ULONG_MAX && CanCastTo<uint32_t>(num))
90 119 : return static_cast<uint32_t>(num);
91 :
92 16 : return defaultValue;
93 : }
94 :
95 68 : uint16_t MakeU16FromAsciiDecimal(const ByteSpan & val)
96 : {
97 68 : const uint32_t num = MakeU32FromAsciiDecimal(val);
98 68 : return CanCastTo<uint16_t>(num) ? static_cast<uint16_t>(num) : 0;
99 : }
100 :
101 39 : uint8_t MakeU8FromAsciiDecimal(const ByteSpan & val)
102 : {
103 39 : const uint32_t num = MakeU32FromAsciiDecimal(val);
104 39 : return CanCastTo<uint8_t>(num) ? static_cast<uint8_t>(num) : 0;
105 : }
106 :
107 4 : bool MakeBoolFromAsciiDecimal(const ByteSpan & val)
108 : {
109 4 : return val.size() == 1 && static_cast<char>(*val.data()) == '1';
110 : }
111 :
112 8 : std::optional<bool> MakeOptionalBoolFromAsciiDecimal(const ByteSpan & val)
113 : {
114 8 : VerifyOrReturnValue(val.size() == 1, std::nullopt); // need to be a valid boolean (single char)
115 :
116 4 : char character = static_cast<char>(*val.data());
117 4 : VerifyOrReturnValue((character == '1') || (character == '0'), std::nullopt); // need to be a valid boolean (single char)
118 4 : return std::make_optional(character == '1');
119 : }
120 :
121 30 : size_t GetPlusSignIdx(const ByteSpan & value)
122 : {
123 : // First value is the vendor id, second (after the +) is the product.
124 148 : for (size_t i = 0; i < value.size(); ++i)
125 : {
126 146 : if (static_cast<char>(value.data()[i]) == '+')
127 : {
128 28 : return i;
129 : }
130 : }
131 2 : return value.size();
132 : }
133 :
134 : } // namespace
135 :
136 15 : uint16_t GetProduct(const ByteSpan & value)
137 : {
138 15 : size_t plussign = GetPlusSignIdx(value);
139 15 : if (plussign < value.size() - 1)
140 : {
141 13 : const uint8_t * productStrStart = value.data() + plussign + 1;
142 13 : size_t productStrLen = value.size() - plussign - 1;
143 13 : return MakeU16FromAsciiDecimal(ByteSpan(productStrStart, productStrLen));
144 : }
145 2 : return 0;
146 : }
147 :
148 15 : uint16_t GetVendor(const ByteSpan & value)
149 : {
150 15 : size_t plussign = GetPlusSignIdx(value);
151 15 : return MakeU16FromAsciiDecimal(ByteSpan(value.data(), plussign));
152 : }
153 :
154 12 : uint16_t GetLongDiscriminator(const ByteSpan & value)
155 : {
156 12 : return MakeU16FromAsciiDecimal(value);
157 : }
158 :
159 13 : uint8_t GetCommissioningMode(const ByteSpan & value)
160 : {
161 13 : return MakeU8FromAsciiDecimal(value);
162 : }
163 :
164 : #if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
165 6 : BitFlags<JointFabricMode> GetJointFabricMode(const ByteSpan & value)
166 : {
167 6 : return BitFlags<JointFabricMode>(MakeU8FromAsciiDecimal(value));
168 : }
169 : #endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
170 :
171 3 : uint32_t GetDeviceType(const ByteSpan & value)
172 : {
173 3 : return MakeU32FromAsciiDecimal(value);
174 : }
175 :
176 4 : void GetDeviceName(const ByteSpan & value, char * name)
177 : {
178 4 : Platform::CopyString(name, kMaxDeviceNameLen + 1, value);
179 4 : }
180 :
181 8 : void GetRotatingDeviceId(const ByteSpan & value, uint8_t * rotatingId, size_t * len)
182 : {
183 8 : *len = Encoding::HexToBytes(reinterpret_cast<const char *>(value.data()), value.size(), rotatingId, kMaxRotatingIdLen);
184 8 : }
185 :
186 12 : uint16_t GetPairingHint(const ByteSpan & value)
187 : {
188 12 : return MakeU16FromAsciiDecimal(value);
189 : }
190 :
191 4 : void GetPairingInstruction(const ByteSpan & value, char * pairingInstruction)
192 : {
193 4 : Platform::CopyString(pairingInstruction, kMaxPairingInstructionLen + 1, value);
194 4 : }
195 :
196 4 : uint8_t GetCommissionerPasscode(const ByteSpan & value)
197 : {
198 4 : return MakeBoolFromAsciiDecimal(value);
199 : }
200 :
201 43 : std::optional<System::Clock::Milliseconds32> GetRetryInterval(const ByteSpan & value)
202 : {
203 43 : const auto undefined = std::numeric_limits<uint32_t>::max();
204 43 : const auto retryInterval = MakeU32FromAsciiDecimal(value, undefined);
205 :
206 43 : if (retryInterval != undefined && retryInterval <= kMaxRetryInterval.count())
207 19 : return std::make_optional(System::Clock::Milliseconds32(retryInterval));
208 :
209 24 : return std::nullopt;
210 : }
211 :
212 16 : std::optional<System::Clock::Milliseconds16> GetRetryActiveThreshold(const ByteSpan & value)
213 : {
214 16 : const auto retryInterval = MakeU16FromAsciiDecimal(value);
215 :
216 16 : if (retryInterval == 0)
217 : {
218 12 : return std::nullopt;
219 : }
220 :
221 4 : return std::make_optional(System::Clock::Milliseconds16(retryInterval));
222 : }
223 :
224 196 : TxtFieldKey GetTxtFieldKey(const ByteSpan & key)
225 : {
226 1791 : for (auto & info : txtFieldInfo)
227 : {
228 1785 : if (IsKey(key, info.keyStr))
229 : {
230 190 : return info.key;
231 : }
232 : }
233 6 : return TxtFieldKey::kUnknown;
234 : }
235 :
236 : } // namespace Internal
237 :
238 47 : void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & val, CommissionNodeData & nodeData)
239 : {
240 47 : TxtFieldKey keyType = Internal::GetTxtFieldKey(key);
241 47 : switch (keyType)
242 : {
243 10 : case TxtFieldKey::kLongDiscriminator:
244 10 : nodeData.longDiscriminator = Internal::GetLongDiscriminator(val);
245 10 : break;
246 10 : case TxtFieldKey::kVendorProduct:
247 10 : nodeData.vendorId = Internal::GetVendor(val);
248 10 : nodeData.productId = Internal::GetProduct(val);
249 10 : break;
250 9 : case TxtFieldKey::kCommissioningMode:
251 9 : nodeData.commissioningMode = Internal::GetCommissioningMode(val);
252 9 : break;
253 1 : case TxtFieldKey::kDeviceType:
254 1 : nodeData.deviceType = Internal::GetDeviceType(val);
255 1 : break;
256 2 : case TxtFieldKey::kDeviceName:
257 2 : Internal::GetDeviceName(val, nodeData.deviceName);
258 2 : break;
259 1 : case TxtFieldKey::kRotatingDeviceId:
260 1 : Internal::GetRotatingDeviceId(val, nodeData.rotatingId, &nodeData.rotatingIdLen);
261 1 : break;
262 1 : case TxtFieldKey::kPairingInstruction:
263 1 : Internal::GetPairingInstruction(val, nodeData.pairingInstruction);
264 1 : break;
265 9 : case TxtFieldKey::kPairingHint:
266 9 : nodeData.pairingHint = Internal::GetPairingHint(val);
267 9 : break;
268 1 : case TxtFieldKey::kCommissionerPasscode:
269 1 : nodeData.supportsCommissionerGeneratedPasscode = Internal::GetCommissionerPasscode(val);
270 1 : break;
271 : #if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
272 1 : case TxtFieldKey::kJointFabricMode:
273 1 : nodeData.jointFabricMode = Internal::GetJointFabricMode(val);
274 1 : break;
275 : #endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
276 2 : default:
277 2 : FillNodeDataFromTxt(key, val, static_cast<CommonResolutionData &>(nodeData));
278 2 : break;
279 : }
280 47 : }
281 :
282 125 : void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & value, CommonResolutionData & nodeData)
283 : {
284 125 : switch (Internal::GetTxtFieldKey(key))
285 : {
286 21 : case TxtFieldKey::kSessionIdleInterval:
287 21 : nodeData.mrpRetryIntervalIdle = Internal::GetRetryInterval(value);
288 21 : break;
289 22 : case TxtFieldKey::kSessionActiveInterval:
290 22 : nodeData.mrpRetryIntervalActive = Internal::GetRetryInterval(value);
291 22 : break;
292 16 : case TxtFieldKey::kSessionActiveThreshold:
293 16 : nodeData.mrpRetryActiveThreshold = Internal::GetRetryActiveThreshold(value);
294 16 : break;
295 20 : case TxtFieldKey::kTcpSupported: {
296 : // bit 0 is reserved and deprecated
297 20 : uint8_t support = Internal::MakeU8FromAsciiDecimal(value);
298 20 : nodeData.supportsTcpClient = (support & (1 << Internal::kTCPClient)) != 0;
299 20 : nodeData.supportsTcpServer = (support & (1 << Internal::kTCPServer)) != 0;
300 20 : break;
301 : }
302 8 : case TxtFieldKey::kLongIdleTimeICD:
303 8 : nodeData.isICDOperatingAsLIT = Internal::MakeOptionalBoolFromAsciiDecimal(value);
304 8 : break;
305 38 : default:
306 38 : break;
307 : }
308 125 : }
309 :
310 : } // namespace Dnssd
311 : } // namespace chip
|