blob: 02d7468d3148f2b2d59e4985a4d5b04363835271 [file] [log] [blame]
/*
*
* 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