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 <stdlib.h>
27 : #include <string.h>
28 :
29 : #include <lib/core/CHIPSafeCasts.h>
30 : #include <lib/dnssd/Advertiser.h>
31 : #include <lib/dnssd/Resolver.h>
32 : #include <lib/support/BytesToHex.h>
33 : #include <lib/support/CHIPMemString.h>
34 : #include <lib/support/SafeInt.h>
35 :
36 : namespace chip {
37 : namespace Dnssd {
38 :
39 : namespace Internal {
40 :
41 : constexpr uint8_t kTCPClient = 1;
42 : constexpr uint8_t kTCPServer = 2;
43 :
44 : namespace {
45 :
46 2174 : char SafeToLower(uint8_t ch)
47 : {
48 2174 : return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
49 : }
50 2005 : bool IsKey(const ByteSpan & key, const char * desired)
51 : {
52 2005 : if (key.size() != strlen(desired))
53 : {
54 1284 : return false;
55 : }
56 :
57 721 : auto desired_bytes = Uint8::from_const_char(desired);
58 1326 : for (size_t i = 0; i < key.size(); ++i)
59 : {
60 1087 : if (SafeToLower(key.data()[i]) != SafeToLower(desired_bytes[i]))
61 : {
62 482 : return false;
63 : }
64 : }
65 239 : return true;
66 : }
67 :
68 167 : uint32_t MakeU32FromAsciiDecimal(const ByteSpan & val, uint32_t defaultValue = 0)
69 : {
70 : // +1 because `digits10` means the number of decimal digits that fit in `uint32_t`,
71 : // not how many digits are enough to represent any `uint32_t`
72 : // +1 for null-terminator
73 : char nullTerminatedValue[std::numeric_limits<uint32_t>::digits10 + 2];
74 :
75 : // value is too long to store `uint32_t`
76 167 : if (val.size() >= sizeof(nullTerminatedValue))
77 6 : return defaultValue;
78 :
79 : // value contains leading zeros
80 161 : if (val.size() > 1 && *val.data() == static_cast<uint8_t>('0'))
81 12 : return defaultValue;
82 :
83 149 : Platform::CopyString(nullTerminatedValue, sizeof(nullTerminatedValue), val);
84 :
85 : char * endPtr;
86 149 : unsigned long num = strtoul(nullTerminatedValue, &endPtr, 10);
87 :
88 149 : if (endPtr > nullTerminatedValue && *endPtr == '\0' && num != ULONG_MAX && CanCastTo<uint32_t>(num))
89 133 : return static_cast<uint32_t>(num);
90 :
91 16 : return defaultValue;
92 : }
93 :
94 84 : uint16_t MakeU16FromAsciiDecimal(const ByteSpan & val)
95 : {
96 84 : const uint32_t num = MakeU32FromAsciiDecimal(val);
97 84 : return CanCastTo<uint16_t>(num) ? static_cast<uint16_t>(num) : 0;
98 : }
99 :
100 37 : uint8_t MakeU8FromAsciiDecimal(const ByteSpan & val)
101 : {
102 37 : const uint32_t num = MakeU32FromAsciiDecimal(val);
103 37 : return CanCastTo<uint8_t>(num) ? static_cast<uint8_t>(num) : 0;
104 : }
105 :
106 4 : bool MakeBoolFromAsciiDecimal(const ByteSpan & val)
107 : {
108 4 : return val.size() == 1 && static_cast<char>(*val.data()) == '1';
109 : }
110 :
111 6 : std::optional<bool> MakeOptionalBoolFromAsciiDecimal(const ByteSpan & val)
112 : {
113 6 : char character = static_cast<char>(*val.data());
114 6 : if (val.size() == 1 && ((character == '1') || (character == '0')))
115 : {
116 4 : return std::make_optional(character == '1');
117 : }
118 2 : return std::nullopt;
119 : }
120 :
121 38 : size_t GetPlusSignIdx(const ByteSpan & value)
122 : {
123 : // First value is the vendor id, second (after the +) is the product.
124 196 : for (size_t i = 0; i < value.size(); ++i)
125 : {
126 194 : if (static_cast<char>(value.data()[i]) == '+')
127 : {
128 36 : return i;
129 : }
130 : }
131 2 : return value.size();
132 : }
133 :
134 : } // namespace
135 :
136 19 : uint16_t GetProduct(const ByteSpan & value)
137 : {
138 19 : size_t plussign = GetPlusSignIdx(value);
139 19 : if (plussign < value.size() - 1)
140 : {
141 17 : const uint8_t * productStrStart = value.data() + plussign + 1;
142 17 : size_t productStrLen = value.size() - plussign - 1;
143 17 : return MakeU16FromAsciiDecimal(ByteSpan(productStrStart, productStrLen));
144 : }
145 2 : return 0;
146 : }
147 :
148 19 : uint16_t GetVendor(const ByteSpan & value)
149 : {
150 19 : size_t plussign = GetPlusSignIdx(value);
151 19 : return MakeU16FromAsciiDecimal(ByteSpan(value.data(), plussign));
152 : }
153 :
154 16 : uint16_t GetLongDiscriminator(const ByteSpan & value)
155 : {
156 16 : return MakeU16FromAsciiDecimal(value);
157 : }
158 :
159 17 : uint8_t GetCommissioningMode(const ByteSpan & value)
160 : {
161 17 : return MakeU8FromAsciiDecimal(value);
162 : }
163 :
164 : #if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
165 : BitFlags<JointFabricMode> GetJointFabricMode(const ByteSpan & value)
166 : {
167 : 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 16 : uint16_t GetPairingHint(const ByteSpan & value)
187 : {
188 16 : return MakeU16FromAsciiDecimal(value);
189 : }
190 :
191 16 : void GetPairingInstruction(const ByteSpan & value, char * pairingInstruction)
192 : {
193 16 : Platform::CopyString(pairingInstruction, kMaxPairingInstructionLen + 1, value);
194 16 : }
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 245 : TxtFieldKey GetTxtFieldKey(const ByteSpan & key)
225 : {
226 2011 : for (auto & info : txtFieldInfo)
227 : {
228 2005 : if (IsKey(key, info.keyStr))
229 : {
230 239 : return info.key;
231 : }
232 : }
233 6 : return TxtFieldKey::kUnknown;
234 : }
235 :
236 : } // namespace Internal
237 :
238 74 : void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & val, CommissionNodeData & nodeData)
239 : {
240 74 : TxtFieldKey keyType = Internal::GetTxtFieldKey(key);
241 74 : switch (keyType)
242 : {
243 14 : case TxtFieldKey::kLongDiscriminator:
244 14 : nodeData.longDiscriminator = Internal::GetLongDiscriminator(val);
245 14 : break;
246 14 : case TxtFieldKey::kVendorProduct:
247 14 : nodeData.vendorId = Internal::GetVendor(val);
248 14 : nodeData.productId = Internal::GetProduct(val);
249 14 : break;
250 13 : case TxtFieldKey::kCommissioningMode:
251 13 : nodeData.commissioningMode = Internal::GetCommissioningMode(val);
252 13 : 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 13 : case TxtFieldKey::kPairingInstruction:
263 13 : Internal::GetPairingInstruction(val, nodeData.pairingInstruction);
264 13 : break;
265 13 : case TxtFieldKey::kPairingHint:
266 13 : nodeData.pairingHint = Internal::GetPairingHint(val);
267 13 : 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 : case TxtFieldKey::kJointFabricMode:
273 : nodeData.jointFabricMode = Internal::GetJointFabricMode(val);
274 : 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 74 : }
281 :
282 151 : void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & value, CommonResolutionData & nodeData)
283 : {
284 151 : 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 6 : case TxtFieldKey::kLongIdleTimeICD:
303 6 : nodeData.isICDOperatingAsLIT = Internal::MakeOptionalBoolFromAsciiDecimal(value);
304 6 : break;
305 66 : default:
306 66 : break;
307 : }
308 151 : }
309 :
310 : } // namespace Dnssd
311 : } // namespace chip
|