| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * Copyright (c) 2013-2017 Nest Labs, Inc. |
| * |
| * 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. |
| */ |
| |
| /** |
| * @file |
| * This file implements methods for parsing host names or IP |
| * addresses and an optional port number and/or an optional |
| * interface name from a human-readable string. |
| * |
| */ |
| |
| #include <string.h> |
| #include <type_traits> |
| |
| #include <support/DLLUtil.h> |
| |
| #include <inet/InetLayer.h> |
| |
| namespace chip { |
| namespace Inet { |
| |
| /** |
| * Parse a human-readable string containing a host or IP address and |
| * an optional port number (separated by a ':'), supporting the |
| * following formats: |
| * |
| * * <host-name> |
| * * <host-name>:\<port\> |
| * * <ip-4-addr> |
| * * <ip-4-addr>:\<port\> |
| * * <ip-6-addr> |
| * * [<ip-6-addr>]:\<port\> |
| * |
| * @param[in] aString The human-reable string to parse. |
| * |
| * @param[in] aStringLen The length, in characters, of aString. |
| * |
| * @param[out] aHost A pointer to the host name portion of the parsed |
| * string. |
| * |
| * @param[out] aHostLen The length, in characters, of aHost. |
| * |
| * @param[out] aPort The port number, if present and successfully |
| * parsed; otherwise, 0. |
| * |
| * @return #INET_ERROR_INVALID_HOST_NAME If the input to be parsed is of |
| * zero-length or otherwise |
| * malformed. |
| * @return #INET_ERROR_HOST_NAME_TOO_LONG If the host name exceeds 253 |
| * characters. |
| * @return #INET_NO_ERROR On success. |
| * |
| */ |
| DLL_EXPORT INET_ERROR ParseHostAndPort(const char * aString, uint16_t aStringLen, const char *& aHost, uint16_t & aHostLen, |
| uint16_t & aPort) |
| { |
| const char * end = aString + aStringLen; |
| const char * p; |
| |
| if (aStringLen == 0) |
| return INET_ERROR_INVALID_HOST_NAME; |
| |
| // If the string starts with a [ then it is a backeted |
| // host/address, possibly with a port number following it. |
| |
| if (*aString == '[') |
| { |
| // Search for the end bracket. |
| p = static_cast<const char *>(memchr(aString, ']', aStringLen)); |
| if (p == nullptr) |
| return INET_ERROR_INVALID_HOST_NAME; |
| |
| // Return the IPv6 address. |
| aHost = aString + 1; |
| // Cast is safe because we know p != aString, so p >= aHost, and at the |
| // same time p - aString < aStringLen, which is uint16_t. |
| static_assert(std::is_same<decltype(aStringLen), uint16_t>::value, "String length might be too big"); |
| aHostLen = static_cast<uint16_t>(p - aHost); |
| |
| // Skip the end bracket. |
| p++; |
| } |
| |
| // Otherwise, not a bracketed IPv6 address... |
| else |
| { |
| // Search for a colon. |
| p = static_cast<const char *>(memchr(aString, ':', aStringLen)); |
| |
| // If the string contains no colons, then it is a host name or |
| // IPv4 address without a port. |
| // |
| // If the string contains MULTIPLE colons, then it is an IPv6 |
| // address without a port. |
| // |
| // Note: The cast is safe because p points into the string of p is not |
| // null, so end - p - 1 can't be negative. |
| if (p == nullptr || memchr(p + 1, ':', static_cast<size_t>(end - p - 1)) != nullptr) |
| p = end; |
| |
| // Return the host/address portion. |
| aHost = aString; |
| // Cast is safe because we know p - aString < aStringLen, which is |
| // uint16_t. |
| static_assert(std::is_same<decltype(aStringLen), uint16_t>::value, "String length might be too big"); |
| aHostLen = static_cast<uint16_t>(p - aString); |
| } |
| |
| // Enforce the DNS limit on the maximum length of a host name. |
| if (aHostLen > 253) |
| return INET_ERROR_HOST_NAME_TOO_LONG; |
| |
| // If there are more characters after the host name... |
| if (p < end) |
| { |
| // Verify the presence of a colon. |
| if (*p++ != ':') |
| return INET_ERROR_INVALID_HOST_NAME; |
| |
| // Verify that the port number portion is not too long. |
| if ((end - p) > 5) |
| return INET_ERROR_INVALID_HOST_NAME; |
| |
| // Parse the port number. |
| aPort = 0; |
| for (; p < end; p++) |
| if (*p >= '0' && *p <= '9') |
| aPort = static_cast<uint16_t>((aPort * 10) + (*p - '0')); |
| else |
| return INET_ERROR_INVALID_HOST_NAME; |
| } |
| |
| // Otherwise, tell the caller there was no port number. |
| else |
| aPort = 0; |
| |
| return INET_NO_ERROR; |
| } |
| |
| /** |
| * Parse a human-readable string containing a host or IP address, an |
| * optional port number (separated by a ':'), and an optional |
| * interface name (separated by a '%'), supporting the following |
| * formats: |
| * |
| * * <host-name> |
| * * <host-name>%\<interface\> |
| * * <host-name>:\<port\> |
| * * <host-name>:\<port\>%\<interface\> |
| * * <ip-4-addr> |
| * * <ip-4-addr>%\<interface\> |
| * * <ip-4-addr>:\<port\> |
| * * <ip-4-addr>:\<port\>%\<interface\> |
| * * <ip-6-addr> |
| * * <ip-6-addr>%\<interface\> |
| * * [<ip-6-addr>]:\<port\> |
| * * [<ip-6-addr>]:\<port\>%\<interface\> |
| * |
| * @param[in] aString The human-reable string to parse. |
| * |
| * @param[in] aStringLen The length, in characters, of aString. |
| * |
| * @param[out] aHost A pointer to the host name portion of the parsed |
| * string. |
| * |
| * @param[out] aHostLen The length, in characters, of aHost. |
| * |
| * @param[out] aPort The port number, if present and successfully |
| * parsed; otherwise, 0. |
| * |
| * @param[out] aInterface A pointer to the interface portion of the parsed |
| * string. |
| * |
| * @param[out] aInterfaceLen The length, in characters, of aInterface. |
| * |
| * @return #INET_ERROR_INVALID_HOST_NAME If the input to be parsed is of |
| * zero-length or otherwise |
| * malformed. |
| * @return #INET_ERROR_HOST_NAME_TOO_LONG If the host name exceeds 253 |
| * characters. |
| * @return #INET_NO_ERROR On success. |
| * |
| */ |
| DLL_EXPORT INET_ERROR ParseHostPortAndInterface(const char * aString, uint16_t aStringLen, const char *& aHost, uint16_t & aHostLen, |
| uint16_t & aPort, const char *& aInterface, uint16_t & aInterfaceLen) |
| { |
| const char * end = aString + aStringLen; |
| |
| aInterface = nullptr; |
| aInterfaceLen = 0; |
| |
| for (uint16_t i = 1; i < aStringLen; i++) |
| { |
| char ch = *(end - i); |
| if (ch == '%') |
| { |
| aInterface = end - i + 1; |
| aInterfaceLen = static_cast<uint16_t>(i - 1); |
| aStringLen = static_cast<uint16_t>(aStringLen - i); |
| break; |
| } |
| if (ch == ':' || ch == ']') |
| break; |
| } |
| |
| return ParseHostAndPort(aString, aStringLen, aHost, aHostLen, aPort); |
| } |
| |
| } // namespace Inet |
| } // namespace chip |