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 3 : uint32_t GetDeviceType(const ByteSpan & value)
165 : {
166 3 : return MakeU32FromAsciiDecimal(value);
167 : }
168 :
169 4 : void GetDeviceName(const ByteSpan & value, char * name)
170 : {
171 4 : Platform::CopyString(name, kMaxDeviceNameLen + 1, value);
172 4 : }
173 :
174 8 : void GetRotatingDeviceId(const ByteSpan & value, uint8_t * rotatingId, size_t * len)
175 : {
176 8 : *len = Encoding::HexToBytes(reinterpret_cast<const char *>(value.data()), value.size(), rotatingId, kMaxRotatingIdLen);
177 8 : }
178 :
179 16 : uint16_t GetPairingHint(const ByteSpan & value)
180 : {
181 16 : return MakeU16FromAsciiDecimal(value);
182 : }
183 :
184 16 : void GetPairingInstruction(const ByteSpan & value, char * pairingInstruction)
185 : {
186 16 : Platform::CopyString(pairingInstruction, kMaxPairingInstructionLen + 1, value);
187 16 : }
188 :
189 4 : uint8_t GetCommissionerPasscode(const ByteSpan & value)
190 : {
191 4 : return MakeBoolFromAsciiDecimal(value);
192 : }
193 :
194 43 : std::optional<System::Clock::Milliseconds32> GetRetryInterval(const ByteSpan & value)
195 : {
196 43 : const auto undefined = std::numeric_limits<uint32_t>::max();
197 43 : const auto retryInterval = MakeU32FromAsciiDecimal(value, undefined);
198 :
199 43 : if (retryInterval != undefined && retryInterval <= kMaxRetryInterval.count())
200 19 : return std::make_optional(System::Clock::Milliseconds32(retryInterval));
201 :
202 24 : return std::nullopt;
203 : }
204 :
205 16 : std::optional<System::Clock::Milliseconds16> GetRetryActiveThreshold(const ByteSpan & value)
206 : {
207 16 : const auto retryInterval = MakeU16FromAsciiDecimal(value);
208 :
209 16 : if (retryInterval == 0)
210 : {
211 12 : return std::nullopt;
212 : }
213 :
214 4 : return std::make_optional(System::Clock::Milliseconds16(retryInterval));
215 : }
216 :
217 245 : TxtFieldKey GetTxtFieldKey(const ByteSpan & key)
218 : {
219 2011 : for (auto & info : txtFieldInfo)
220 : {
221 2005 : if (IsKey(key, info.keyStr))
222 : {
223 239 : return info.key;
224 : }
225 : }
226 6 : return TxtFieldKey::kUnknown;
227 : }
228 :
229 : } // namespace Internal
230 :
231 74 : void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & val, CommissionNodeData & nodeData)
232 : {
233 74 : TxtFieldKey keyType = Internal::GetTxtFieldKey(key);
234 74 : switch (keyType)
235 : {
236 14 : case TxtFieldKey::kLongDiscriminator:
237 14 : nodeData.longDiscriminator = Internal::GetLongDiscriminator(val);
238 14 : break;
239 14 : case TxtFieldKey::kVendorProduct:
240 14 : nodeData.vendorId = Internal::GetVendor(val);
241 14 : nodeData.productId = Internal::GetProduct(val);
242 14 : break;
243 13 : case TxtFieldKey::kCommissioningMode:
244 13 : nodeData.commissioningMode = Internal::GetCommissioningMode(val);
245 13 : break;
246 1 : case TxtFieldKey::kDeviceType:
247 1 : nodeData.deviceType = Internal::GetDeviceType(val);
248 1 : break;
249 2 : case TxtFieldKey::kDeviceName:
250 2 : Internal::GetDeviceName(val, nodeData.deviceName);
251 2 : break;
252 1 : case TxtFieldKey::kRotatingDeviceId:
253 1 : Internal::GetRotatingDeviceId(val, nodeData.rotatingId, &nodeData.rotatingIdLen);
254 1 : break;
255 13 : case TxtFieldKey::kPairingInstruction:
256 13 : Internal::GetPairingInstruction(val, nodeData.pairingInstruction);
257 13 : break;
258 13 : case TxtFieldKey::kPairingHint:
259 13 : nodeData.pairingHint = Internal::GetPairingHint(val);
260 13 : break;
261 1 : case TxtFieldKey::kCommissionerPasscode:
262 1 : nodeData.supportsCommissionerGeneratedPasscode = Internal::GetCommissionerPasscode(val);
263 1 : break;
264 2 : default:
265 2 : FillNodeDataFromTxt(key, val, static_cast<CommonResolutionData &>(nodeData));
266 2 : break;
267 : }
268 74 : }
269 :
270 151 : void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & value, CommonResolutionData & nodeData)
271 : {
272 151 : switch (Internal::GetTxtFieldKey(key))
273 : {
274 21 : case TxtFieldKey::kSessionIdleInterval:
275 21 : nodeData.mrpRetryIntervalIdle = Internal::GetRetryInterval(value);
276 21 : break;
277 22 : case TxtFieldKey::kSessionActiveInterval:
278 22 : nodeData.mrpRetryIntervalActive = Internal::GetRetryInterval(value);
279 22 : break;
280 16 : case TxtFieldKey::kSessionActiveThreshold:
281 16 : nodeData.mrpRetryActiveThreshold = Internal::GetRetryActiveThreshold(value);
282 16 : break;
283 20 : case TxtFieldKey::kTcpSupported: {
284 : // bit 0 is reserved and deprecated
285 20 : uint8_t support = Internal::MakeU8FromAsciiDecimal(value);
286 20 : nodeData.supportsTcpClient = (support & (1 << Internal::kTCPClient)) != 0;
287 20 : nodeData.supportsTcpServer = (support & (1 << Internal::kTCPServer)) != 0;
288 20 : break;
289 : }
290 6 : case TxtFieldKey::kLongIdleTimeICD:
291 6 : nodeData.isICDOperatingAsLIT = Internal::MakeOptionalBoolFromAsciiDecimal(value);
292 6 : break;
293 66 : default:
294 66 : break;
295 : }
296 151 : }
297 :
298 : } // namespace Dnssd
299 : } // namespace chip
|