| /* |
| * Copyright (c) 2020 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #include "Command.h" |
| #include "platform/PlatformManager.h" |
| |
| #include <netdb.h> |
| #include <sstream> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| |
| #include <lib/core/CHIPSafeCasts.h> |
| #include <lib/support/BytesToHex.h> |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/SafeInt.h> |
| #include <lib/support/ScopedBuffer.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| |
| bool Command::InitArguments(int argc, char ** argv) |
| { |
| bool isValidCommand = false; |
| size_t argsCount = mArgs.size(); |
| |
| size_t argsOptionalCount = 0; |
| for (size_t i = 0; i < argsCount; i++) |
| { |
| if (mArgs[i].optional) |
| { |
| argsOptionalCount++; |
| } |
| } |
| |
| VerifyOrExit((size_t)(argc) >= (argsCount - argsOptionalCount) && (size_t)(argc) <= argsCount, |
| ChipLogError(chipTool, "InitArgs: Wrong arguments number: %d instead of %zu", argc, argsCount)); |
| |
| for (size_t i = 0; i < (size_t) argc; i++) |
| { |
| if (!InitArgument(i, argv[i])) |
| { |
| ExitNow(); |
| } |
| } |
| |
| for (size_t i = (size_t) argc; i < argsCount; i++) |
| { |
| } |
| |
| isValidCommand = true; |
| |
| exit: |
| return isValidCommand; |
| } |
| |
| static bool ParseAddressWithInterface(const char * addressString, Command::AddressWithInterface * address) |
| { |
| struct addrinfo hints; |
| struct addrinfo * result; |
| int ret; |
| |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = AF_UNSPEC; |
| hints.ai_socktype = SOCK_DGRAM; |
| ret = getaddrinfo(addressString, nullptr, &hints, &result); |
| if (ret < 0) |
| { |
| ChipLogError(chipTool, "Invalid address: %s", addressString); |
| return false; |
| } |
| |
| address->address = ::chip::Inet::IPAddress::FromSockAddr(*result->ai_addr); |
| if (result->ai_family == AF_INET6) |
| { |
| struct sockaddr_in6 * addr = reinterpret_cast<struct sockaddr_in6 *>(result->ai_addr); |
| address->interfaceId = ::chip::Inet::InterfaceId(addr->sin6_scope_id); |
| } |
| else |
| { |
| address->interfaceId = chip::Inet::InterfaceId::Null(); |
| } |
| |
| return true; |
| } |
| |
| bool Command::InitArgument(size_t argIndex, char * argValue) |
| { |
| bool isValidArgument = false; |
| bool isHexNotation = strncmp(argValue, "0x", 2) == 0 || strncmp(argValue, "0X", 2) == 0; |
| |
| Argument arg = mArgs.at(argIndex); |
| switch (arg.type) |
| { |
| case ArgumentType::Attribute: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<char *> *>(arg.value))->Emplace(); |
| char * value = reinterpret_cast<char *>(arg.value); |
| isValidArgument = (strcmp(argValue, value) == 0); |
| break; |
| } |
| |
| case ArgumentType::String: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<const char **> *>(arg.value))->Emplace(); |
| const char ** value = reinterpret_cast<const char **>(arg.value); |
| *value = argValue; |
| isValidArgument = true; |
| break; |
| } |
| |
| case ArgumentType::CharString: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<chip::Span<const char>> *>(arg.value))->Emplace(); |
| auto * value = static_cast<chip::Span<const char> *>(arg.value); |
| *value = chip::Span<const char>(argValue, strlen(argValue)); |
| isValidArgument = true; |
| break; |
| } |
| |
| case ArgumentType::OctetString: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<chip::ByteSpan *> *>(arg.value))->Emplace(); |
| auto * value = static_cast<chip::ByteSpan *>(arg.value); |
| // We support two ways to pass an octet string argument. If it happens |
| // to be all-ASCII, you can just pass it in. Otherwise you can pass in |
| // 0x followed by the hex-encoded bytes. |
| size_t argLen = strlen(argValue); |
| static constexpr char hexPrefix[] = "hex:"; |
| constexpr size_t prefixLen = ArraySize(hexPrefix) - 1; // Don't count the null |
| if (strncmp(argValue, hexPrefix, prefixLen) == 0) |
| { |
| // Hex-encoded. Decode it into a temporary buffer first, so if we |
| // run into errors we can do correct "argument is not valid" logging |
| // that actually shows the value that was passed in. After we |
| // determine it's valid, modify the passed-in value to hold the |
| // right bytes, so we don't need to worry about allocating storage |
| // for this somewhere else. This works because the hex |
| // representation is always longer than the octet string it encodes, |
| // so we have enough space in argValue for the decoded version. |
| chip::Platform::ScopedMemoryBuffer<uint8_t> buffer; |
| if (!buffer.Calloc(argLen)) // Bigger than needed, but it's fine. |
| { |
| isValidArgument = false; |
| break; |
| } |
| |
| size_t octetCount = chip::Encoding::HexToBytes(argValue + prefixLen, argLen - prefixLen, buffer.Get(), argLen); |
| if (octetCount == 0) |
| { |
| isValidArgument = false; |
| break; |
| } |
| |
| memcpy(argValue, buffer.Get(), octetCount); |
| *value = chip::ByteSpan(chip::Uint8::from_char(argValue), octetCount); |
| isValidArgument = true; |
| } |
| else |
| { |
| // Just ASCII. Check for the "str:" prefix. |
| static constexpr char strPrefix[] = "str:"; |
| constexpr size_t strPrefixLen = ArraySize(strPrefix) - 1; // Don't count the null |
| if (strncmp(argValue, strPrefix, strPrefixLen) == 0) |
| { |
| // Skip the prefix |
| argValue += strPrefixLen; |
| argLen -= strPrefixLen; |
| } |
| *value = chip::ByteSpan(chip::Uint8::from_char(argValue), argLen); |
| isValidArgument = true; |
| } |
| break; |
| } |
| |
| case ArgumentType::Boolean: |
| case ArgumentType::Number_uint8: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<uint8_t *> *>(arg.value))->Emplace(); |
| uint8_t * value = reinterpret_cast<uint8_t *>(arg.value); |
| |
| // stringstream treats uint8_t as char, which is not what we want here. |
| uint16_t tmpValue; |
| std::stringstream ss; |
| isHexNotation ? ss << std::hex << argValue : ss << argValue; |
| ss >> tmpValue; |
| if (chip::CanCastTo<uint8_t>(tmpValue)) |
| { |
| *value = static_cast<uint8_t>(tmpValue); |
| |
| uint64_t min = chip::CanCastTo<uint64_t>(arg.min) ? static_cast<uint64_t>(arg.min) : 0; |
| uint64_t max = arg.max; |
| isValidArgument = (!ss.fail() && ss.eof() && *value >= min && *value <= max); |
| } |
| else |
| { |
| isValidArgument = false; |
| } |
| break; |
| } |
| |
| case ArgumentType::Number_uint16: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<uint16_t *> *>(arg.value))->Emplace(); |
| uint16_t * value = reinterpret_cast<uint16_t *>(arg.value); |
| std::stringstream ss; |
| isHexNotation ? ss << std::hex << argValue : ss << argValue; |
| ss >> *value; |
| |
| uint64_t min = chip::CanCastTo<uint64_t>(arg.min) ? static_cast<uint64_t>(arg.min) : 0; |
| uint64_t max = arg.max; |
| isValidArgument = (!ss.fail() && ss.eof() && *value >= min && *value <= max); |
| break; |
| } |
| |
| case ArgumentType::Number_uint32: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<uint32_t *> *>(arg.value))->Emplace(); |
| uint32_t * value = reinterpret_cast<uint32_t *>(arg.value); |
| std::stringstream ss; |
| isHexNotation ? ss << std::hex << argValue : ss << argValue; |
| ss >> *value; |
| |
| uint64_t min = chip::CanCastTo<uint64_t>(arg.min) ? static_cast<uint64_t>(arg.min) : 0; |
| uint64_t max = arg.max; |
| isValidArgument = (!ss.fail() && ss.eof() && *value >= min && *value <= max); |
| break; |
| } |
| |
| case ArgumentType::Number_uint64: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<uint64_t *> *>(arg.value))->Emplace(); |
| uint64_t * value = reinterpret_cast<uint64_t *>(arg.value); |
| std::stringstream ss; |
| isHexNotation ? ss << std::hex << argValue : ss << argValue; |
| ss >> *value; |
| |
| uint64_t min = chip::CanCastTo<uint64_t>(arg.min) ? static_cast<uint64_t>(arg.min) : 0; |
| uint64_t max = arg.max; |
| isValidArgument = (!ss.fail() && ss.eof() && *value >= min && *value <= max); |
| break; |
| } |
| |
| case ArgumentType::Number_int8: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<int8_t *> *>(arg.value))->Emplace(); |
| int8_t * value = reinterpret_cast<int8_t *>(arg.value); |
| |
| // stringstream treats int8_t as char, which is not what we want here. |
| int16_t tmpValue; |
| std::stringstream ss; |
| isHexNotation ? ss << std::hex << argValue : ss << argValue; |
| ss >> tmpValue; |
| if (chip::CanCastTo<int8_t>(tmpValue)) |
| { |
| *value = static_cast<int8_t>(tmpValue); |
| |
| int64_t min = arg.min; |
| int64_t max = chip::CanCastTo<int64_t>(arg.max) ? static_cast<int64_t>(arg.max) : INT64_MAX; |
| isValidArgument = (!ss.fail() && ss.eof() && *value >= min && *value <= max); |
| } |
| else |
| { |
| isValidArgument = false; |
| } |
| break; |
| } |
| |
| case ArgumentType::Number_int16: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<int16_t *> *>(arg.value))->Emplace(); |
| int16_t * value = reinterpret_cast<int16_t *>(arg.value); |
| std::stringstream ss; |
| isHexNotation ? ss << std::hex << argValue : ss << argValue; |
| ss >> *value; |
| |
| int64_t min = arg.min; |
| int64_t max = chip::CanCastTo<int64_t>(arg.max) ? static_cast<int64_t>(arg.max) : INT64_MAX; |
| isValidArgument = (!ss.fail() && ss.eof() && *value >= min && *value <= max); |
| break; |
| } |
| |
| case ArgumentType::Number_int32: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<int32_t *> *>(arg.value))->Emplace(); |
| int32_t * value = reinterpret_cast<int32_t *>(arg.value); |
| std::stringstream ss; |
| isHexNotation ? ss << std::hex << argValue : ss << argValue; |
| ss >> *value; |
| |
| int64_t min = arg.min; |
| int64_t max = chip::CanCastTo<int64_t>(arg.max) ? static_cast<int64_t>(arg.max) : INT64_MAX; |
| isValidArgument = (!ss.fail() && ss.eof() && *value >= min && *value <= max); |
| break; |
| } |
| |
| case ArgumentType::Number_int64: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<int64_t *> *>(arg.value))->Emplace(); |
| int64_t * value = reinterpret_cast<int64_t *>(arg.value); |
| std::stringstream ss; |
| isHexNotation ? ss << std::hex << argValue : ss << argValue; |
| ss >> *value; |
| |
| int64_t min = arg.min; |
| int64_t max = chip::CanCastTo<int64_t>(arg.max) ? static_cast<int64_t>(arg.max) : INT64_MAX; |
| isValidArgument = (!ss.fail() && ss.eof() && *value >= min && *value <= max); |
| break; |
| } |
| |
| case ArgumentType::Address: { |
| if (arg.optional) |
| arg.value = &(reinterpret_cast<chip::Optional<AddressWithInterface *> *>(arg.value))->Emplace(); |
| AddressWithInterface * value = reinterpret_cast<AddressWithInterface *>(arg.value); |
| isValidArgument = ParseAddressWithInterface(argValue, value); |
| break; |
| } |
| } |
| |
| if (!isValidArgument) |
| { |
| ChipLogError(chipTool, "InitArgs: Invalid argument %s: %s", arg.name, argValue); |
| } |
| |
| return isValidArgument; |
| } |
| |
| size_t Command::AddArgument(const char * name, const char * value, bool optional) |
| { |
| Argument arg; |
| arg.type = ArgumentType::Attribute; |
| arg.name = name; |
| arg.value = const_cast<void *>(reinterpret_cast<const void *>(value)); |
| arg.optional = optional; |
| |
| mArgs.emplace_back(arg); |
| return mArgs.size(); |
| } |
| |
| size_t Command::AddArgument(const char * name, char ** value, bool optional) |
| { |
| Argument arg; |
| arg.type = ArgumentType::CharString; |
| arg.name = name; |
| arg.value = reinterpret_cast<void *>(value); |
| arg.optional = optional; |
| |
| mArgs.emplace_back(arg); |
| return mArgs.size(); |
| } |
| |
| size_t Command::AddArgument(const char * name, chip::CharSpan * value, bool optional) |
| { |
| Argument arg; |
| arg.type = ArgumentType::CharString; |
| arg.name = name; |
| arg.value = reinterpret_cast<void *>(value); |
| arg.optional = optional; |
| |
| mArgs.emplace_back(arg); |
| return mArgs.size(); |
| } |
| |
| size_t Command::AddArgument(const char * name, chip::ByteSpan * value, bool optional) |
| { |
| Argument arg; |
| arg.type = ArgumentType::OctetString; |
| arg.name = name; |
| arg.value = reinterpret_cast<void *>(value); |
| arg.optional = optional; |
| |
| mArgs.emplace_back(arg); |
| return mArgs.size(); |
| } |
| |
| size_t Command::AddArgument(const char * name, AddressWithInterface * out, bool optional) |
| { |
| Argument arg; |
| arg.type = ArgumentType::Address; |
| arg.name = name; |
| arg.value = reinterpret_cast<void *>(out); |
| arg.optional = optional; |
| |
| mArgs.emplace_back(arg); |
| return mArgs.size(); |
| } |
| |
| size_t Command::AddArgument(const char * name, int64_t min, uint64_t max, void * out, ArgumentType type, bool optional) |
| { |
| Argument arg; |
| arg.type = type; |
| arg.name = name; |
| arg.value = out; |
| arg.min = min; |
| arg.max = max; |
| arg.optional = optional; |
| |
| mArgs.emplace_back(arg); |
| return mArgs.size(); |
| } |
| |
| size_t Command::AddArgument(const char * name, int64_t min, uint64_t max, void * out, bool optional) |
| { |
| Argument arg; |
| arg.type = ArgumentType::Number_uint8; |
| arg.name = name; |
| arg.value = out; |
| arg.min = min; |
| arg.max = max; |
| arg.optional = optional; |
| |
| mArgs.emplace_back(arg); |
| return mArgs.size(); |
| } |
| |
| const char * Command::GetArgumentName(size_t index) const |
| { |
| if (index < mArgs.size()) |
| { |
| return mArgs.at(index).name; |
| } |
| |
| return nullptr; |
| } |
| |
| const char * Command::GetAttribute(void) const |
| { |
| size_t argsCount = mArgs.size(); |
| for (size_t i = 0; i < argsCount; i++) |
| { |
| Argument arg = mArgs.at(i); |
| if (arg.type == ArgumentType::Attribute) |
| { |
| return reinterpret_cast<const char *>(arg.value); |
| } |
| } |
| |
| return nullptr; |
| } |