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 : namespace { 42 : 43 3860 : char SafeToLower(uint8_t ch) 44 : { 45 3860 : return static_cast<char>(std::tolower(static_cast<unsigned char>(ch))); 46 : } 47 2822 : bool IsKey(const ByteSpan & key, const char * desired) 48 : { 49 2822 : if (key.size() != strlen(desired)) 50 : { 51 1455 : return false; 52 : } 53 : 54 1367 : auto desired_bytes = Uint8::from_const_char(desired); 55 2354 : for (size_t i = 0; i < key.size(); ++i) 56 : { 57 1930 : if (SafeToLower(key.data()[i]) != SafeToLower(desired_bytes[i])) 58 : { 59 943 : return false; 60 : } 61 : } 62 424 : return true; 63 : } 64 : 65 250 : uint32_t MakeU32FromAsciiDecimal(const ByteSpan & val, uint32_t defaultValue = 0) 66 : { 67 : // +1 because `digits10` means the number of decimal digits that fit in `uint32_t`, 68 : // not how many digits are enough to represent any `uint32_t` 69 : // +1 for null-terminator 70 : char nullTerminatedValue[std::numeric_limits<uint32_t>::digits10 + 2]; 71 : 72 : // value is too long to store `uint32_t` 73 250 : if (val.size() >= sizeof(nullTerminatedValue)) 74 6 : return defaultValue; 75 : 76 : // value contains leading zeros 77 244 : if (val.size() > 1 && *val.data() == static_cast<uint8_t>('0')) 78 12 : return defaultValue; 79 : 80 232 : Platform::CopyString(nullTerminatedValue, sizeof(nullTerminatedValue), val); 81 : 82 : char * endPtr; 83 232 : unsigned long num = strtoul(nullTerminatedValue, &endPtr, 10); 84 : 85 232 : if (endPtr > nullTerminatedValue && *endPtr == '\0' && num != ULONG_MAX && CanCastTo<uint32_t>(num)) 86 218 : return static_cast<uint32_t>(num); 87 : 88 14 : return defaultValue; 89 : } 90 : 91 164 : uint16_t MakeU16FromAsciiDecimal(const ByteSpan & val) 92 : { 93 164 : const uint32_t num = MakeU32FromAsciiDecimal(val); 94 164 : return CanCastTo<uint16_t>(num) ? static_cast<uint16_t>(num) : 0; 95 : } 96 : 97 41 : uint8_t MakeU8FromAsciiDecimal(const ByteSpan & val) 98 : { 99 41 : const uint32_t num = MakeU32FromAsciiDecimal(val); 100 41 : return CanCastTo<uint8_t>(num) ? static_cast<uint8_t>(num) : 0; 101 : } 102 : 103 6 : bool MakeBoolFromAsciiDecimal(const ByteSpan & val) 104 : { 105 6 : return val.size() == 1 && static_cast<char>(*val.data()) == '1'; 106 : } 107 : 108 6 : Optional<bool> MakeOptionalBoolFromAsciiDecimal(const ByteSpan & val) 109 : { 110 6 : char character = static_cast<char>(*val.data()); 111 6 : if (val.size() == 1 && ((character == '1') || (character == '0'))) 112 : { 113 4 : return MakeOptional(character == '1'); 114 : } 115 2 : return NullOptional; 116 : } 117 : 118 78 : size_t GetPlusSignIdx(const ByteSpan & value) 119 : { 120 : // First value is the vendor id, second (after the +) is the product. 121 436 : for (size_t i = 0; i < value.size(); ++i) 122 : { 123 434 : if (static_cast<char>(value.data()[i]) == '+') 124 : { 125 76 : return i; 126 : } 127 : } 128 2 : return value.size(); 129 : } 130 : 131 : } // namespace 132 : 133 39 : uint16_t GetProduct(const ByteSpan & value) 134 : { 135 39 : size_t plussign = GetPlusSignIdx(value); 136 39 : if (plussign < value.size() - 1) 137 : { 138 37 : const uint8_t * productStrStart = value.data() + plussign + 1; 139 37 : size_t productStrLen = value.size() - plussign - 1; 140 37 : return MakeU16FromAsciiDecimal(ByteSpan(productStrStart, productStrLen)); 141 : } 142 2 : return 0; 143 : } 144 : 145 39 : uint16_t GetVendor(const ByteSpan & value) 146 : { 147 39 : size_t plussign = GetPlusSignIdx(value); 148 39 : return MakeU16FromAsciiDecimal(ByteSpan(value.data(), plussign)); 149 : } 150 : 151 36 : uint16_t GetLongDiscriminator(const ByteSpan & value) 152 : { 153 36 : return MakeU16FromAsciiDecimal(value); 154 : } 155 : 156 37 : uint8_t GetCommissioningMode(const ByteSpan & value) 157 : { 158 37 : return MakeU8FromAsciiDecimal(value); 159 : } 160 : 161 3 : uint32_t GetDeviceType(const ByteSpan & value) 162 : { 163 3 : return MakeU32FromAsciiDecimal(value); 164 : } 165 : 166 4 : void GetDeviceName(const ByteSpan & value, char * name) 167 : { 168 4 : Platform::CopyString(name, kMaxDeviceNameLen + 1, value); 169 4 : } 170 : 171 8 : void GetRotatingDeviceId(const ByteSpan & value, uint8_t * rotatingId, size_t * len) 172 : { 173 8 : *len = Encoding::HexToBytes(reinterpret_cast<const char *>(value.data()), value.size(), rotatingId, kMaxRotatingIdLen); 174 8 : } 175 : 176 36 : uint16_t GetPairingHint(const ByteSpan & value) 177 : { 178 36 : return MakeU16FromAsciiDecimal(value); 179 : } 180 : 181 36 : void GetPairingInstruction(const ByteSpan & value, char * pairingInstruction) 182 : { 183 36 : Platform::CopyString(pairingInstruction, kMaxPairingInstructionLen + 1, value); 184 36 : } 185 : 186 4 : uint8_t GetCommissionerPasscode(const ByteSpan & value) 187 : { 188 4 : return MakeU8FromAsciiDecimal(value); 189 : } 190 : 191 42 : Optional<System::Clock::Milliseconds32> GetRetryInterval(const ByteSpan & value) 192 : { 193 42 : const auto undefined = std::numeric_limits<uint32_t>::max(); 194 42 : const auto retryInterval = MakeU32FromAsciiDecimal(value, undefined); 195 : 196 42 : if (retryInterval != undefined && retryInterval <= kMaxRetryInterval.count()) 197 18 : return MakeOptional(System::Clock::Milliseconds32(retryInterval)); 198 : 199 24 : return NullOptional; 200 : } 201 : 202 16 : Optional<System::Clock::Milliseconds16> GetRetryActiveThreshold(const ByteSpan & value) 203 : { 204 16 : const auto retryInterval = MakeU16FromAsciiDecimal(value); 205 : 206 16 : if (retryInterval == 0) 207 : { 208 12 : return NullOptional; 209 : } 210 : 211 4 : return MakeOptional(System::Clock::Milliseconds16(retryInterval)); 212 : } 213 : 214 429 : TxtFieldKey GetTxtFieldKey(const ByteSpan & key) 215 : { 216 2827 : for (auto & info : txtFieldInfo) 217 : { 218 2822 : if (IsKey(key, info.keyStr)) 219 : { 220 424 : return info.key; 221 : } 222 : } 223 5 : return TxtFieldKey::kUnknown; 224 : } 225 : 226 : } // namespace Internal 227 : 228 174 : void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & val, CommissionNodeData & nodeData) 229 : { 230 174 : TxtFieldKey keyType = Internal::GetTxtFieldKey(key); 231 174 : switch (keyType) 232 : { 233 34 : case TxtFieldKey::kLongDiscriminator: 234 34 : nodeData.longDiscriminator = Internal::GetLongDiscriminator(val); 235 34 : break; 236 34 : case TxtFieldKey::kVendorProduct: 237 34 : nodeData.vendorId = Internal::GetVendor(val); 238 34 : nodeData.productId = Internal::GetProduct(val); 239 34 : break; 240 33 : case TxtFieldKey::kCommissioningMode: 241 33 : nodeData.commissioningMode = Internal::GetCommissioningMode(val); 242 33 : break; 243 1 : case TxtFieldKey::kDeviceType: 244 1 : nodeData.deviceType = Internal::GetDeviceType(val); 245 1 : break; 246 2 : case TxtFieldKey::kDeviceName: 247 2 : Internal::GetDeviceName(val, nodeData.deviceName); 248 2 : break; 249 1 : case TxtFieldKey::kRotatingDeviceId: 250 1 : Internal::GetRotatingDeviceId(val, nodeData.rotatingId, &nodeData.rotatingIdLen); 251 1 : break; 252 33 : case TxtFieldKey::kPairingInstruction: 253 33 : Internal::GetPairingInstruction(val, nodeData.pairingInstruction); 254 33 : break; 255 33 : case TxtFieldKey::kPairingHint: 256 33 : nodeData.pairingHint = Internal::GetPairingHint(val); 257 33 : break; 258 1 : case TxtFieldKey::kCommissionerPasscode: 259 1 : nodeData.commissionerPasscode = Internal::GetCommissionerPasscode(val); 260 1 : break; 261 2 : default: 262 2 : break; 263 : } 264 174 : } 265 : 266 235 : void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & value, CommonResolutionData & nodeData) 267 : { 268 235 : switch (Internal::GetTxtFieldKey(key)) 269 : { 270 21 : case TxtFieldKey::kSessionIdleInterval: 271 21 : nodeData.mrpRetryIntervalIdle = Internal::GetRetryInterval(value); 272 21 : break; 273 21 : case TxtFieldKey::kSessionActiveInterval: 274 21 : nodeData.mrpRetryIntervalActive = Internal::GetRetryInterval(value); 275 21 : break; 276 16 : case TxtFieldKey::kSessionActiveThreshold: 277 16 : nodeData.mrpRetryActiveThreshold = Internal::GetRetryActiveThreshold(value); 278 16 : break; 279 6 : case TxtFieldKey::kTcpSupported: 280 6 : nodeData.supportsTcp = Internal::MakeBoolFromAsciiDecimal(value); 281 6 : break; 282 6 : case TxtFieldKey::kLongIdleTimeICD: 283 6 : nodeData.isICDOperatingAsLIT = Internal::MakeOptionalBoolFromAsciiDecimal(value); 284 6 : break; 285 165 : default: 286 165 : break; 287 : } 288 235 : } 289 : 290 : } // namespace Dnssd 291 : } // namespace chip