summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/net')
-rw-r--r--src/VBox/Runtime/common/net/Makefile.kup0
-rw-r--r--src/VBox/Runtime/common/net/macstr.cpp123
-rw-r--r--src/VBox/Runtime/common/net/netaddrstr.cpp1233
-rw-r--r--src/VBox/Runtime/common/net/netaddrstr2.cpp737
4 files changed, 2093 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/net/Makefile.kup b/src/VBox/Runtime/common/net/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/common/net/Makefile.kup
diff --git a/src/VBox/Runtime/common/net/macstr.cpp b/src/VBox/Runtime/common/net/macstr.cpp
new file mode 100644
index 00000000..a5eab282
--- /dev/null
+++ b/src/VBox/Runtime/common/net/macstr.cpp
@@ -0,0 +1,123 @@
+/* $Id: macstr.cpp $ */
+/** @file
+ * IPRT - Command Line Parsing
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cidr.h>
+#include <iprt/net.h> /* must come before getopt.h */
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/message.h>
+#include <iprt/string.h>
+
+
+/**
+ * Converts an stringified Ethernet MAC address into the RTMAC representation.
+ *
+ * @returns VINF_SUCCESS on success.
+ *
+ * @param pszValue The value to convert.
+ * @param pAddr Where to store the result.
+ */
+RTDECL(int) RTNetStrToMacAddr(const char *pszValue, PRTMAC pAddr)
+{
+ /*
+ * First check if it might be a 12 xdigit string without any separators.
+ */
+ size_t cchValue = strlen(pszValue);
+ if (cchValue >= 12 && memchr(pszValue, ':', 12) == NULL)
+ {
+ bool fOkay = true;
+ for (size_t off = 0; off < 12 && fOkay; off++)
+ fOkay = RT_C_IS_XDIGIT(pszValue[off]);
+ if (fOkay && cchValue > 12)
+ for (size_t off = 12; off < cchValue && fOkay; off++)
+ fOkay = RT_C_IS_SPACE(pszValue[off]);
+ if (fOkay)
+ {
+ int rc = RTStrConvertHexBytes(pszValue, pAddr, sizeof(*pAddr), 0);
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ return rc;
+ }
+ }
+
+ /*
+ * Not quite sure if I should accept stuff like "08::27:::1" here...
+ * The code is accepting "::" patterns now, except for for the first
+ * and last parts.
+ */
+
+ /* first */
+ char *pszNext;
+ int rc = RTStrToUInt8Ex(RTStrStripL(pszValue), &pszNext, 16, &pAddr->au8[0]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+ if (*pszNext++ != ':')
+ return VERR_INVALID_PARAMETER;
+
+ /* middle */
+ for (unsigned i = 1; i < 5; i++)
+ {
+ if (*pszNext == ':')
+ pAddr->au8[i] = 0;
+ else
+ {
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &pAddr->au8[i]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
+ return rc;
+ if (*pszNext != ':')
+ return VERR_INVALID_PARAMETER;
+ }
+ pszNext++;
+ }
+
+ /* last */
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &pAddr->au8[5]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
+ return rc;
+ pszNext = RTStrStripL(pszNext);
+ if (*pszNext)
+ return VERR_INVALID_PARAMETER;
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTNetStrToMacAddr);
diff --git a/src/VBox/Runtime/common/net/netaddrstr.cpp b/src/VBox/Runtime/common/net/netaddrstr.cpp
new file mode 100644
index 00000000..160c8aaf
--- /dev/null
+++ b/src/VBox/Runtime/common/net/netaddrstr.cpp
@@ -0,0 +1,1233 @@
+/* $Id: netaddrstr.cpp $ */
+/** @file
+ * IPRT - Network Address String Handling.
+ *
+ * @remarks Don't add new functionality to this file, it goes into netaddrstr2.cpp
+ * or some other suitable file (legal reasons + code not up to oracle
+ * quality standards and requires rewrite from scratch).
+ */
+
+/*
+ * Contributed by Oliver Loch.
+ *
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/net.h>
+
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include "internal/string.h"
+
+
+/** @page pg_rtnetipv6_addr IPv6 Address Format
+ *
+ * IPv6 Addresses, their representation in text and other problems.
+ *
+ * The following is based on:
+ *
+ * - http://tools.ietf.org/html/rfc4291
+ * - http://tools.ietf.org/html/rfc5952
+ * - http://tools.ietf.org/html/rfc6052
+ *
+ *
+ * Before you start using those functions, you should have an idea of
+ * what you're dealing with, before you come and blame the functions...
+ *
+ * First of all, the address itself:
+ *
+ * An address is written like this: (READ THIS FINE MANUAL!)
+ *
+ * - 2001:db8:abc:def::1
+ *
+ * The characters between two colons are called a "hextet".
+ * Each hextet consists of four characters and each IPv6 address
+ * consists of a maximum of eight hextets. So a full blown address
+ * would look like this:
+ *
+ * - 1111:2222:3333:4444:5555:6666:7777:8888
+ *
+ * The allowed characters are "0123456789abcdef". They have to be
+ * lower case. Upper case is not allowed.
+ *
+ * *** Gaps and adress shortening
+ *
+ * If an address contains hextets that contain only "0"s, they
+ * can be shortened, like this:
+ *
+ * - 1111:2222:0000:0000:0000:0000:7777:8888 -> 1111:2222::7777:8888
+ *
+ * The double colon represents the hextets that have been shortened "::".
+ * The "::" will be called "gap" from now on.
+ *
+ * When shortening an address, there are some special rules that need to be applied:
+ *
+ * - Shorten always the longest group of hextets.
+ *
+ * Let's say, you have this address: 2001:db8:0:0:0:1:0:0 then it has to be
+ * shortened to "2001:db8::1:0:0". Shortening to "2001:db8:0:0:0:1::" would
+ * return an error.
+ *
+ * - Two or more gaps the same size.
+ *
+ * Let's say you have this address: 2001:db8:0:0:1:0:0:1. As you can see, there
+ * are two gaps, both the size of two hextets. If you shorten the last two hextets,
+ * you end up in pain, as the RFC forbids this, so the correct address is:
+ * "2001:db8::1:0:0:1"
+ *
+ * It's important to note that an address can only be shortened ONE TIME!
+ * This is invalid: "2001:db8::1::1"
+ *
+ * *** The scope.
+ *
+ * Each address has a so called "scope" it is added to the end of the address,
+ * separated by a percent sign "%". If there is no scope at the end, it defaults
+ * to "0".
+ *
+ * So "2001:db8::1" is the same as "2001:db8::1%0".
+ *
+ * As in IPv6 all network interfaces can/should have the same address, the scope
+ * gives you the ability to choose on which interface the system should listen.
+ *
+ * AFAIK, the scope can be used with unicast as well as link local addresses, but
+ * it is mandatory with link local addresses (starting with fe80::).
+ *
+ * On Linux the default scope is the interface's name. On Windows it's just the index
+ * of the interface. Run "route print -6" in the shell, to see the interface's index
+ * on Winodows.
+ *
+ * All functions can deal with the scope, and DO NOT warn if you put garbage there.
+ *
+ * *** Port added to the IPv6 address
+ *
+ * There is only one way to add a port to an IPv6 address is to embed it in brackets:
+ *
+ * [2001:db8::1]:12345
+ *
+ * This gives you the address "2001:db8::1" and the port "12345".
+ *
+ * What also works, but is not recommended by rfc is to separate the port
+ * by a dot:
+ *
+ * 2001:db8::1.12345
+ *
+ * It even works with embedded IPv4 addresses.
+ *
+ * *** Special addresses and how they are written
+ *
+ * The following are notations to represent "special addresses".
+ *
+ * "::" IN6ADDR_ANY
+ * ":::123" IN6ADDR_ANY with port "123"
+ * "[::]:123" IN6ADDR_ANY with port "123"
+ * "[:::123]" -> NO. Not allowed and makes no sense
+ * "::1" -> address of the loopback device (127.0.0.1 in v4)
+ *
+ * On systems with dual sockets, one can use so called embedded IPv4 addresses:
+ *
+ * "::ffff:192.168.1.1" results in the IPv6 address "::ffff:c0a8:0101" as two octets
+ * of the IPv4 address will be converted to one hextet in the IPv6 address.
+ *
+ * The prefix of such addresses MUST BE "::ffff:", 10 bytes as zero and two bytes as 255.
+ *
+ * The so called IPv4-compatible IPv6 addresses are deprecated and no longer in use.
+ *
+ * *** Valid addresses and string
+ *
+ * If you use any of the IPv6 address functions, keep in mind, that those addresses
+ * are all returning "valid" even if the underlying system (e.g. VNC) doesn't like
+ * such strings.
+ *
+ * [2001:db8::1]
+ * [2001:db8::1]:12345
+ *
+ * and so on. So to make sure you only pass the underlying software a pure IPv6 address
+ * without any garbage, you should use the "outAddress" parameters to get a RFC compliant
+ * address returned.
+ *
+ * So after reading the above, you'll start using the functions and see a bool called
+ * "followRfc" which is true by default. This is what this bool does:
+ *
+ * The following addresses all represent the exact same address:
+ *
+ * 1 - 2001:db8::1
+ * 2 - 2001:db8:0::1
+ * 3 - 2001:0db8:0000:0000:0000:0000:0000:0001
+ * 4 - 2001:DB8::1
+ * 5 - [2001:db8::1]
+ * 6 - [2001:db8:0::1]
+ *
+ * According to RFC 5952, number two, three, four and six are invalid.
+ *
+ * #2 - because there is a single hextet that hasn't been shortened
+ *
+ * #3 - because there has nothing been shortened (hextets 3 to 7) and
+ * there are leading zeros in at least one hextet ("0db8")
+ *
+ * #4 - all characters in an IPv6 address have to be lower case
+ *
+ * #6 - same as two but included in brackets
+ *
+ * If you follow RFC, the above addresses are not converted and an
+ * error is returned. If you turn RFC off, you will get the expected
+ * representation of the address.
+ *
+ * It's a nice way to convert "weird" addresses to rfc compliant addresses
+ *
+ */
+
+
+/**
+ * Parses any string and tests if it is an IPv6 Address
+ *
+ * This function should NOT be used directly. If you do, note
+ * that no security checks are done at the moment. This can change.
+ *
+ * @returns iprt sstatus code, yeah, right... This function most certainly DOES
+ * NOT RETURN ANY IPRT STATUS CODES. It's also a unreadable mess.
+ * @param pszAddress The strin that holds the IPv6 address
+ * @param addressLength The length of pszAddress
+ * @param pszAddressOut Returns a plain, full blown IPv6 address
+ * as a char array
+ * @param addressOutSize The size of pszAddressOut (length)
+ * @param pPortOut 32 bit unsigned integer, holding the port
+ * If pszAddress doesn't contain a port, it's 0
+ * @param pszScopeOut Returns the scope of the address, if none it's 0
+ * @param scopeOutSize sizeof(pszScopeOut)
+ * @param pBrackets returns true if the address was enclosed in brackets
+ * @param pEmbeddedV4 returns true if the address is an embedded IPv4 address
+ * @param followRfc if set to true, the function follows RFC (default)
+ */
+static int rtStrParseAddrStr6(const char *pszAddress, size_t addressLength, char *pszAddressOut, size_t addressOutSize, uint32_t *pPortOut, char *pszIfIdOut, size_t ifIdOutSize, bool *pBrackets, bool *pEmbeddedV4, bool followRfc)
+{
+ /************************\
+ * Pointer Hell Ahead *
+ \************************/
+
+ const char szIpV6AddressChars[] = "ABCDEF01234567890abcdef.:[]%"; // order IMPORTANT
+ const char szIpV4AddressChars[] = "01234567890.:[]"; // order IMPORTANT
+ const char szLinkLocalPrefix[] = "FfEe8800"; //
+ const char *pszIpV6AddressChars = NULL, *pszIpV4AddressChars = NULL, *pszLinkLocalPrefix = NULL;
+
+ char *pszSourceAddress = NULL, *pszSourceAddressStart = NULL;
+ char *pszResultAddress = NULL, *pszResultAddressStart = NULL;
+ char *pszResultAddress4 = NULL, *pszResultAddress4Start = NULL;
+ char *pszResultPort = NULL, *pszResultPortStart = NULL;
+ char *pszInternalAddress = NULL, *pszInternalAddressStart = NULL;
+ char *pszInternalPort = NULL, *pszInternalPortStart = NULL;
+
+ char *pStart = NULL, *pNow = NULL, *pNext = NULL, *pNowChar = NULL, *pIfId = NULL, *pIfIdEnd = NULL;
+ char *pNowDigit = NULL, *pFrom = NULL, *pTo = NULL, *pLast = NULL;
+ char *pGap = NULL, *pMisc = NULL, *pDotStart = NULL, *pFieldStart = NULL, *pFieldEnd = NULL;
+ char *pFieldStartLongest = NULL, *pBracketOpen = NULL, *pBracketClose = NULL;
+ char *pszRc = NULL;
+
+ bool isLinkLocal = false;
+ char szDummy[4];
+
+ uint8_t *pByte = NULL;
+ uint32_t byteOut = 0;
+ uint16_t returnValue = 0;
+ uint32_t colons = 0;
+ uint32_t colonsOverAll = 0;
+ uint32_t fieldLength = 0;
+ uint32_t dots = 0;
+ size_t gapSize = 0;
+ uint32_t intPortOut = 0;
+
+ pszIpV4AddressChars = &szIpV4AddressChars[0];
+ pszIpV6AddressChars = &szIpV6AddressChars[6];
+ pszLinkLocalPrefix = &szLinkLocalPrefix[6];
+
+ if (!followRfc)
+ pszIpV6AddressChars = &szIpV6AddressChars[0];
+
+ if (addressLength<2)
+ returnValue = 711;
+
+ pszResultAddressStart = (char *)RTMemTmpAlloc(34);
+ pszInternalAddressStart = (char *)RTMemTmpAlloc(34);
+ pszInternalPortStart = (char * )RTMemTmpAlloc(10);
+
+ if (! (pszResultAddressStart && pszInternalAddressStart && pszInternalPortStart))
+ {
+ if (pszResultAddressStart)
+ RTMemTmpFree(pszResultAddressStart);
+
+ if (pszInternalAddressStart)
+ RTMemTmpFree(pszInternalAddressStart);
+
+ if (pszInternalPortStart)
+ RTMemTmpFree(pszInternalPortStart);
+
+ return -701;
+ }
+
+ memset(szDummy, '\0', 4);
+
+ pszResultAddress = pszResultAddressStart;
+ memset(pszResultAddressStart, '\0', 34);
+
+ pszInternalAddress = pszInternalAddressStart;
+ memset(pszInternalAddressStart, '\0' , 34);
+
+ pszInternalPort = pszInternalPortStart;
+ memset(pszInternalPortStart, '\0', 10);
+
+ pszSourceAddress = pszSourceAddressStart = (char *)pszAddress;
+
+ pFrom = pTo = pStart = pLast = pszSourceAddressStart;
+
+ while (*pszSourceAddress != '\0' && !returnValue)
+ {
+ pNow = NULL;
+ pNext = NULL;
+ pNowChar = NULL;
+ pNowDigit = NULL;
+
+ pNow = pszSourceAddress;
+ pNext = pszSourceAddress + 1;
+
+ if (!pFrom)
+ pFrom = pTo = pNow;
+
+ pNowChar = (char *)memchr(pszIpV6AddressChars, *pNow, strlen(pszIpV6AddressChars));
+ pNowDigit = (char *)memchr(pszIpV6AddressChars, *pNow, strlen(pszIpV6AddressChars) - 5);
+
+ if (pszResultPort)
+ {
+ if (pLast && (pszResultPort == pszSourceAddressStart))
+ {
+ if (*pLast == '\0')
+ returnValue = 721;
+
+ pszResultPortStart = (char *)RTMemTmpAlloc(10);
+
+ if (!pszResultPortStart)
+ returnValue = 702;
+
+ memset(pszResultPortStart, '\0', 10);
+ pszResultPort = pszResultPortStart;
+ pszSourceAddress = pLast;
+ pMisc = pLast;
+ pLast = NULL;
+ continue;
+ }
+
+ pNowDigit = NULL;
+ pNowDigit = (char *)memchr(pszIpV4AddressChars, *pNow, strlen(pszIpV4AddressChars) - 4);
+
+ if (strlen(pszResultPortStart) == 5)
+ returnValue = 11;
+
+ if (*pNow == '0' && pszResultPort == pszResultPortStart && *pNext != '\0' && (pNow - pMisc) < 5 )
+ {
+ pszSourceAddress++;
+ continue;
+ }
+
+ if (pNowDigit)
+ {
+ *pszResultPort = *pNowDigit;
+ pszResultPort++;
+ pszSourceAddress++;
+ continue;
+ }
+ else
+ returnValue = 12;
+ }
+
+ if (pszResultAddress4)
+ {
+ if (pszResultAddress4 == pszSourceAddressStart && pLast)
+ {
+ dots = 0;
+ pszResultAddress4 = NULL;
+ pszResultAddress4Start = NULL;
+ pszResultAddress4Start = (char *)RTMemTmpAlloc(20);
+
+ if (!pszResultAddress4Start)
+ {
+ returnValue = 401;
+ break;
+ }
+
+ memset(pszResultAddress4Start, '\0', 20);
+ pszResultAddress4 = pszResultAddress4Start;
+ pszSourceAddress = pLast;
+ pFrom = pLast;
+ pTo = pLast;
+ pLast = NULL;
+ continue;
+ }
+
+ pTo = pNow;
+ pNowDigit = NULL;
+ pNowDigit = (char *)memchr(pszIpV4AddressChars, *pNow, strlen(pszIpV4AddressChars) - 4);
+
+ if (!pNowDigit && *pNow != '.' && *pNow != ']' && *pNow != ':' && *pNow != '%')
+ returnValue = 412;
+
+ if ((pNow - pFrom) > 3)
+ {
+ returnValue = 402;
+ break;
+ }
+
+ if (pNowDigit && *pNext != '\0')
+ {
+ pszSourceAddress++;
+ continue;
+ }
+
+ if (!pNowDigit && !pBracketOpen && (*pNext == '.' || *pNext == ']' || *pNext == ':'))
+ returnValue = 411;
+
+ memset(pszResultAddress4, '0', 3);
+ pMisc = pszResultAddress4 + 2;
+ pszResultAddress4 = pszResultAddress4 + 3;
+
+ if (*pNow != '.' && !pNowDigit && strlen(pszResultAddress4Start) < 9)
+ returnValue = 403;
+
+ if ((pTo - pFrom) > 0)
+ pTo--;
+
+ dots++;
+
+ while (pTo >= pFrom)
+ {
+ *pMisc = *pTo;
+ pMisc--;
+ pTo--;
+ }
+
+ if (dots == 4 && *pNow == '.')
+ {
+ if (!pBracketOpen)
+ {
+ pszResultPort = pszSourceAddressStart;
+ pLast = pNext;
+ }
+ else
+ {
+ returnValue = 409;
+ }
+ }
+
+ dots = 0;
+
+ pFrom = pNext;
+ pTo = pNext;
+
+ if (strlen(pszResultAddress4Start) > 11)
+ pszResultAddress4 = NULL;
+
+ if ((*pNow == ':' || *pNow == '.') && strlen(pszResultAddress4Start) == 12)
+ {
+ pLast = pNext;
+ pszResultPort = pszSourceAddressStart;
+ }
+
+ if (*pNow == '%')
+ {
+ pIfId = pNow;
+ pLast = pNow;
+ continue;
+ }
+ pszSourceAddress = pNext;
+
+ if (*pNow != ']')
+ continue;
+
+ pFrom = pNow;
+ pTo = pNow;
+ }
+
+ if (pIfId && (!pIfIdEnd))
+ {
+ if (*pIfId == '%' && pIfId == pLast && *pNext != '\0')
+ {
+ pFrom = pNext;
+ pIfId = pNext;
+ pLast = NULL;
+
+ pszSourceAddress++;
+ continue;
+ }
+
+ if (*pNow == '%' && pIfId <= pNow)
+ {
+ returnValue = 442;
+ break;
+ }
+
+ if (*pNow != ']' && *pNext != '\0')
+ {
+ pTo = pNow;
+ pszSourceAddress++;
+ continue;
+ }
+
+ if (*pNow == ']')
+ {
+ pIfIdEnd = pNow - 1;
+ pFrom = pNow;
+ pTo = pNow;
+ continue;
+ }
+ else
+ {
+ pIfIdEnd = pNow;
+ pFrom = NULL;
+ pTo = NULL;
+ pszSourceAddress++;
+ continue;
+ }
+ }
+
+ if (!pNowChar)
+ {
+ returnValue = 254;
+
+ if (followRfc)
+ {
+ pMisc = (char *)memchr(&szIpV6AddressChars[0], *pNow, strlen(&szIpV6AddressChars[0]));
+
+ if (pMisc)
+ returnValue = 253;
+ }
+ }
+
+ if (strlen(pszResultAddressStart) > 32 && !pszResultAddress4Start)
+ returnValue = 255;
+
+ if (pNowDigit && *pNext != '\0' && colons == 0)
+ {
+ pTo = pNow;
+ pszSourceAddress++;
+ continue;
+ }
+
+ if (*pNow == ':' && *pNext != '\0')
+ {
+ colonsOverAll++;
+ colons++;
+ pszSourceAddress++;
+ continue;
+ }
+
+ if (*pNow == ':' )
+ {
+ colons++;
+ colonsOverAll++;
+ }
+
+ if (*pNow == '.')
+ {
+ pMisc = pNow;
+
+ while (*pMisc != '\0' && *pMisc != ']')
+ {
+ if (*pMisc == '.')
+ dots++;
+
+ pMisc++;
+ }
+ }
+
+ if (*pNow == ']')
+ {
+ if (pBracketClose)
+ returnValue = 77;
+
+ if (!pBracketOpen)
+ returnValue = 22;
+
+ if (*pNext == ':' || *pNext == '.')
+ {
+ pszResultPort = pszSourceAddressStart;
+ pLast = pNext + 1;
+ }
+
+ if (pFrom == pNow)
+ pFrom = NULL;
+
+ pBracketClose = pNow;
+ }
+
+ if (*pNow == '[')
+ {
+ if (pBracketOpen)
+ returnValue = 23;
+
+ if (pStart != pNow)
+ returnValue = 24;
+
+ pBracketOpen = pNow;
+ pStart++;
+ pFrom++;
+ pszSourceAddress++;
+ continue;
+ }
+
+ if (*pNow == '%')
+ {
+ if (pIfId)
+ returnValue = 441;
+
+ pLast = pNext;
+ pIfId = pNext;
+ }
+
+ if (colons > 0)
+ {
+ if (colons == 1)
+ {
+ if (pStart + 1 == pNow )
+ returnValue = 31;
+
+ if (*pNext == '\0' && !pNowDigit)
+ returnValue = 32;
+
+ pLast = pNow;
+ }
+
+ if (colons == 2)
+ {
+ if (pGap)
+ returnValue = 33;
+
+ pGap = pszResultAddress + 4;
+
+ if (pStart + 1 == pNow || pStart + 2 == pNow)
+ {
+ pGap = pszResultAddressStart;
+ pFrom = pNow;
+ }
+
+ if (*pNext == '\0' && !pNowDigit)
+ pszSourceAddress++;
+
+ if (*pNext != ':' && *pNext != '.')
+ pLast = pNow;
+ }
+
+ if (colons == 3)
+ {
+ pFrom = pLast;
+ pLast = pNow;
+
+ if (*pNext == '\0' && !pNowDigit)
+ returnValue = 34;
+
+ if (pBracketOpen)
+ returnValue = 35;
+
+ if (pGap && followRfc)
+ returnValue = 36;
+
+ if (!pGap)
+ pGap = pszResultAddress + 4;
+
+ if (pStart + 3 == pNow)
+ {
+ pszResultPort = pszSourceAddressStart;
+ pGap = pszResultAddress;
+ pFrom = NULL;
+ }
+
+ if (pNowDigit)
+ {
+ pszResultPort = pszSourceAddressStart;
+ }
+ }
+ }
+ if (*pNext == '\0' && colons == 0 && !pIfIdEnd)
+ {
+ pFrom = pLast;
+
+ if (pNowDigit)
+ pTo = pNow;
+
+ pLast = NULL;
+ }
+
+ if (dots > 0)
+ {
+ if (dots == 1)
+ {
+ pszResultPort = pszSourceAddressStart;
+ pLast = pNext;
+ }
+
+ if (dots == 4 && pBracketOpen)
+ returnValue = 601;
+
+ if (dots == 3 || dots == 4)
+ {
+ pszResultAddress4 = pszSourceAddressStart;
+ pLast = pFrom;
+ pFrom = NULL;
+ }
+
+ if (dots > 4)
+ returnValue = 603;
+
+ dots = 0;
+ }
+
+ if (pFrom && pTo)
+ {
+ if (pTo - pFrom > 3)
+ {
+ returnValue = 51;
+ break;
+ }
+
+ if (followRfc)
+ {
+ if ((pTo - pFrom > 0) && *pFrom == '0')
+ returnValue = 101;
+
+ if ((pTo - pFrom) == 0 && *pFrom == '0' && colons == 2)
+ returnValue = 102;
+
+ if ((pTo - pFrom) == 0 && *pFrom == '0' && pszResultAddress == pGap)
+ returnValue = 103;
+
+ if ((pTo - pFrom) == 0 && *pFrom == '0')
+ {
+ if (!pFieldStart)
+ {
+ pFieldStart = pszResultAddress;
+ pFieldEnd = pszResultAddress + 4;
+ }
+ else
+ {
+ pFieldEnd = pFieldEnd + 4;
+ }
+ }
+ else
+ {
+ if ((size_t)(pFieldEnd - pFieldStart) > fieldLength)
+ {
+ fieldLength = pFieldEnd - pFieldStart;
+ pFieldStartLongest = pFieldStart;
+ }
+
+ pFieldStart = NULL;
+ pFieldEnd = NULL;
+ }
+ }
+ if (!(pGap == pszResultAddressStart && (size_t)(pNow - pStart) == colons))
+ {
+ memset(pszResultAddress, '0', 4);
+ pMisc = pszResultAddress + 3;
+ pszResultAddress = pszResultAddress + 4;
+
+ if (pFrom == pStart && (pTo - pFrom) == 3)
+ {
+ isLinkLocal = true;
+
+ while (pTo >= pFrom)
+ {
+ *pMisc = *pTo;
+
+ if (*pTo != *pszLinkLocalPrefix && *pTo != *(pszLinkLocalPrefix + 1))
+ isLinkLocal = false;
+
+ pTo--;
+ pMisc--;
+ pszLinkLocalPrefix = pszLinkLocalPrefix - 2;
+ }
+ }
+ else
+ {
+ while (pTo >= pFrom)
+ {
+ *pMisc = *pTo;
+ pMisc--;
+ pTo--;
+ }
+ }
+ }
+
+ pFrom = pNow;
+ pTo = pNow;
+
+ }
+ if (*pNext == '\0' && colons == 0)
+ pszSourceAddress++;
+
+ if (*pNext == '\0' && !pBracketClose && !pszResultPort)
+ pTo = pNext;
+
+ colons = 0;
+ } // end of loop
+
+ if (!returnValue && colonsOverAll < 2)
+ returnValue = 252;
+
+ if (!returnValue && (pBracketOpen && !pBracketClose))
+ returnValue = 25;
+
+ if (!returnValue && pGap)
+ {
+ gapSize = 32 - strlen(pszResultAddressStart);
+
+ if (followRfc)
+ {
+ if (gapSize < 5)
+ returnValue = 104;
+
+ if (fieldLength > gapSize)
+ returnValue = 105;
+
+ if (fieldLength == gapSize && pFieldStartLongest < pGap)
+ returnValue = 106;
+ }
+
+ pszResultAddress = pszResultAddressStart;
+ pszInternalAddress = pszInternalAddressStart;
+
+ if (!returnValue && pszResultAddress4Start)
+ {
+ if (strlen(pszResultAddressStart) > 4)
+ returnValue = 405;
+
+ pszResultAddress = pszResultAddressStart;
+
+ if (pGap != pszResultAddressStart)
+ returnValue = 407;
+
+ memset(pszInternalAddressStart, '0', 20);
+ pszInternalAddress = pszInternalAddressStart + 20;
+
+ for (int i = 0; i < 4; i++)
+ {
+ if (*pszResultAddress != 'f' && *pszResultAddress != 'F')
+ {
+ returnValue = 406;
+ break;
+ }
+
+ *pszInternalAddress = *pszResultAddress;
+ pszResultAddress++;
+ pszInternalAddress++;
+ }
+ pszResultAddress4 = pszResultAddress4Start;
+
+ for (int i = 0; i<4; i++)
+ {
+ memcpy(szDummy, pszResultAddress4, 3);
+
+ int rc = RTStrToUInt32Ex((const char *)&szDummy[0], NULL, 16, &byteOut);
+
+ if (rc == 0 && byteOut < 256)
+ {
+ RTStrPrintf(szDummy, 3, "%02x", byteOut);
+ memcpy(pszInternalAddress, szDummy, 2);
+ pszInternalAddress = pszInternalAddress + 2;
+ pszResultAddress4 = pszResultAddress4 + 3;
+ memset(szDummy, '\0', 4);
+ }
+ else
+ {
+ returnValue = 499;
+ }
+ }
+ }
+ else
+ {
+ while (!returnValue && pszResultAddress != pGap)
+ {
+ *pszInternalAddress = *pszResultAddress;
+ pszResultAddress++;
+ pszInternalAddress++;
+ }
+
+ memset(pszInternalAddress, '0', gapSize);
+ pszInternalAddress = pszInternalAddress + gapSize;
+
+ while (!returnValue && *pszResultAddress != '\0')
+ {
+ *pszInternalAddress = *pszResultAddress;
+ pszResultAddress++;
+ pszInternalAddress++;
+ }
+ }
+ }
+ else
+ {
+ if (!returnValue)
+ {
+ if (strlen(pszResultAddressStart) != 32)
+ returnValue = 111;
+
+ if (followRfc)
+ {
+ if (fieldLength > 4)
+ returnValue = 112;
+ }
+
+ memcpy(pszInternalAddressStart, pszResultAddressStart, strlen(pszResultAddressStart));
+ }
+ }
+
+ if (pszResultPortStart)
+ {
+ if (strlen(pszResultPortStart) > 0 && strlen(pszResultPortStart) < 6)
+ {
+ memcpy(pszInternalPortStart, pszResultPortStart, strlen(pszResultPortStart));
+
+ intPortOut = 0;
+ int rc = RTStrToUInt32Ex(pszInternalPortStart, NULL, 10, &intPortOut);
+
+ if (rc == 0)
+ {
+ if (!(intPortOut > 0 && intPortOut < 65536))
+ intPortOut = 0;
+ }
+ else
+ {
+ returnValue = 888;
+ }
+ }
+ else
+ {
+ returnValue = 889;
+ }
+ }
+
+ /*
+ full blown address 32 bytes, no colons -> pszInternalAddressStart
+ port as string -> pszResultPortStart
+ port as binary integer -> intPortOut
+ interface id in pIfId and pIfIdEnd
+
+ Now fill the out parameters.
+
+ */
+
+ if (!returnValue && pszAddressOut)
+ {
+ if (strlen(pszInternalAddressStart) < addressOutSize)
+ {
+ pszRc = NULL;
+ pszRc = (char *)memset(pszAddressOut, '\0', addressOutSize);
+
+ if (!pszRc)
+ returnValue = 910;
+
+ pszRc = NULL;
+
+ pszRc = (char *)memcpy(pszAddressOut, pszInternalAddressStart, strlen(pszInternalAddressStart));
+
+ if (!pszRc)
+ returnValue = 911;
+ }
+ else
+ {
+ returnValue = 912;
+ }
+ }
+
+ if (!returnValue && pPortOut)
+ {
+ *pPortOut = intPortOut;
+ }
+
+ if (!returnValue && pszIfIdOut)
+ {
+ if (pIfIdEnd && pIfId)
+ {
+ if ((size_t)(pIfIdEnd - pIfId) + 1 < ifIdOutSize)
+ {
+ pszRc = NULL;
+ pszRc = (char *)memset(pszIfIdOut, '\0', ifIdOutSize);
+
+ if (!pszRc)
+ returnValue = 913;
+
+ pszRc = NULL;
+ pszRc = (char *)memcpy(pszIfIdOut, pIfId, (pIfIdEnd - pIfId) + 1);
+
+ if (!pszRc)
+ returnValue = 914;
+ }
+ else
+ {
+ returnValue = 915;
+ }
+ }
+ else
+ {
+ pszRc = NULL;
+ pszRc = (char *)memset(pszIfIdOut, '\0', ifIdOutSize);
+
+ if (!pszRc)
+ returnValue = 916;
+ }
+ // temporary hack
+ if (isLinkLocal && (strlen(pszIfIdOut) < 1))
+ {
+ memset(pszIfIdOut, '\0', ifIdOutSize);
+ *pszIfIdOut = '%';
+ pszIfIdOut++;
+ *pszIfIdOut = '0';
+ pszIfIdOut++;
+ }
+ }
+
+ if (pBracketOpen && pBracketClose && pBrackets)
+ *pBrackets = true;
+
+ if (pEmbeddedV4 && pszResultAddress4Start)
+ *pEmbeddedV4 = true;
+
+ if (pszResultAddressStart)
+ RTMemTmpFree(pszResultAddressStart);
+
+ if (pszResultPortStart)
+ RTMemTmpFree(pszResultPortStart);
+
+ if (pszResultAddress4Start)
+ RTMemTmpFree(pszResultAddress4Start);
+
+ if (pszInternalAddressStart)
+ RTMemTmpFree(pszInternalAddressStart);
+
+ if (pszInternalPortStart)
+ RTMemTmpFree(pszInternalPortStart);
+
+ return (uint32_t)(returnValue - (returnValue * 2)); // make it negative...
+}
+
+/**
+ * Takes a string and returns a RFC compliant string of the address
+ * This function SHOULD NOT be used directly. It expects a 33 byte
+ * char array with a full blown IPv6 address without separators.
+ *
+ * @returns iprt status code.
+ * @param psz The string to convert
+ * @param pszAddrOut The char[] that will hold the result
+ * @param addOutSize The size of the char[] from above.
+ * @param pszPortOut char[] for the text representation of the port
+ * @param portOutSize sizeof(pszPortOut);
+ */
+DECLHIDDEN(int) rtStrToIpAddr6Str(const char *psz, char *pszAddrOut, size_t addrOutSize, char *pszPortOut, size_t portOutSize, bool followRfc)
+{
+ char *pStart = NULL;
+ char *pGapStart = NULL;
+ char *pGapEnd = NULL;
+ char *pGapTStart = NULL;
+ char *pGapTEnd = NULL;
+ char *pCurrent = NULL;
+ char *pOut = NULL;
+
+ if (!psz || !pszAddrOut)
+ return VERR_NOT_SUPPORTED;
+
+ if (addrOutSize < 40)
+ return VERR_NOT_SUPPORTED;
+
+ pStart = (char *)psz;
+ pCurrent = (char *)psz;
+ pGapStart = (char *)psz;
+ pGapEnd = (char *)psz;
+
+ while (*pCurrent != '\0')
+ {
+ if (*pCurrent != '0')
+ pGapTStart = NULL;
+
+ if ((pCurrent - pStart) % 4 == 0) // ok, start of a hextet
+ {
+ if (*pCurrent == '0' && !pGapTStart)
+ pGapTStart = pCurrent;
+ }
+
+ if ((pCurrent - pStart) % 4 == 3)
+ {
+ if (*pCurrent == '0' && pGapTStart)
+ pGapTEnd = pCurrent;
+
+ if (pGapTStart && pGapTEnd)
+ {
+ pGapTEnd = pCurrent;
+
+ if ((pGapTEnd - pGapTStart) > (pGapEnd - pGapStart))
+ {
+ pGapEnd = pGapTEnd;
+ pGapStart = pGapTStart;
+ }
+ }
+ }
+
+ pCurrent++;
+ }
+
+ pCurrent = (char *)psz;
+ pStart = (char *)psz;
+ pOut = (char *)pszAddrOut;
+
+ while (*pCurrent != '\0')
+ {
+ if (*pCurrent != '0')
+ pGapTStart = NULL;
+
+ if (!pGapTStart)
+ {
+ *pOut = *pCurrent;
+ pOut++;
+ }
+
+ if ((pCurrent - pStart) % 4 == 3)
+ {
+ if (pGapTStart && *pCurrent == '0')
+ {
+ *pOut = *pCurrent;
+ pOut++;
+ }
+
+ if (*(pCurrent + 1) != '\0')
+ {
+ *pOut = ':';
+ pOut++;
+ }
+
+ pGapTStart = pCurrent + 1;
+ }
+
+ if ((pCurrent + 1) == pGapStart && (pGapEnd - pGapStart) > 3)
+ {
+ *pOut = ':';
+ pOut++;
+ pCurrent = pGapEnd;
+ }
+
+ pCurrent++;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Tests if the given string is a valid IPv6 address.
+ *
+ * @returns 0 if valid, some random number if not. THIS IS NOT AN IPRT STATUS!
+ * @param psz The string to test
+ * @param pszResultAddress plain address, optional read "valid addresses
+ * and strings" above.
+ * @param resultAddressSize size of pszResultAddress
+ * @param addressOnly return only the plain address (no scope)
+ * Ignored, and will always return the if id
+ */
+static int rtNetIpv6CheckAddrStr(const char *psz, char *pszResultAddress, size_t resultAddressSize, bool addressOnly, bool followRfc)
+{
+ int rc;
+ int rc2;
+ int returnValue = VERR_NOT_SUPPORTED; /* gcc want's this initialized, I understand its confusion. */
+
+ char *p = NULL, *pl = NULL;
+
+ size_t memAllocMaxSize = RT_MAX(strlen(psz), resultAddressSize) + 40;
+
+ char *pszAddressOutLocal = (char *)RTMemTmpAlloc(memAllocMaxSize);
+ char *pszIfIdOutLocal = (char *)RTMemTmpAlloc(memAllocMaxSize);
+ char *pszAddressRfcOutLocal = (char *)RTMemTmpAlloc(memAllocMaxSize);
+
+ if (!pszAddressOutLocal || !pszIfIdOutLocal || !pszAddressRfcOutLocal)
+ return VERR_NO_TMP_MEMORY;
+
+ memset(pszAddressOutLocal, '\0', memAllocMaxSize);
+ memset(pszIfIdOutLocal, '\0', memAllocMaxSize);
+ memset(pszAddressRfcOutLocal, '\0', memAllocMaxSize);
+
+ rc = rtStrParseAddrStr6(psz, strlen(psz), pszAddressOutLocal, memAllocMaxSize, NULL, pszIfIdOutLocal, memAllocMaxSize, NULL, NULL, followRfc);
+
+ if (rc == 0)
+ returnValue = VINF_SUCCESS;
+
+ if (rc == 0 && pszResultAddress)
+ {
+ // convert the 32 characters to a valid, shortened ipv6 address
+
+ rc2 = rtStrToIpAddr6Str((const char *)pszAddressOutLocal, pszAddressRfcOutLocal, memAllocMaxSize, NULL, 0, followRfc);
+
+ if (rc2 != 0)
+ returnValue = 951;
+
+ // this is a temporary solution
+ if (!returnValue && strlen(pszIfIdOutLocal) > 0) // the if identifier is copied over _ALWAYS_ && !addressOnly)
+ {
+ p = pszAddressRfcOutLocal + strlen(pszAddressRfcOutLocal);
+
+ *p = '%';
+
+ p++;
+
+ pl = (char *)memcpy(p, pszIfIdOutLocal, strlen(pszIfIdOutLocal));
+
+ if (!pl)
+ returnValue = VERR_NOT_SUPPORTED;
+ }
+
+ pl = NULL;
+
+ pl = (char *)memcpy(pszResultAddress, pszAddressRfcOutLocal, strlen(pszAddressRfcOutLocal));
+
+ if (!pl)
+ returnValue = VERR_NOT_SUPPORTED;
+ }
+
+ if (rc != 0)
+ returnValue = VERR_NOT_SUPPORTED;
+
+ if (pszAddressOutLocal)
+ RTMemTmpFree(pszAddressOutLocal);
+
+ if (pszAddressRfcOutLocal)
+ RTMemTmpFree(pszAddressRfcOutLocal);
+
+ if (pszIfIdOutLocal)
+ RTMemTmpFree(pszIfIdOutLocal);
+
+ return returnValue;
+
+}
diff --git a/src/VBox/Runtime/common/net/netaddrstr2.cpp b/src/VBox/Runtime/common/net/netaddrstr2.cpp
new file mode 100644
index 00000000..5c848a63
--- /dev/null
+++ b/src/VBox/Runtime/common/net/netaddrstr2.cpp
@@ -0,0 +1,737 @@
+/* $Id: netaddrstr2.cpp $ */
+/** @file
+ * IPRT - Network Address String Handling.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/net.h>
+
+#include <iprt/asm.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include "internal/string.h"
+
+
+DECLHIDDEN(int) rtNetStrToIPv4AddrEx(const char *pcszAddr, PRTNETADDRIPV4 pAddr,
+ char **ppszNext)
+{
+ char *pszNext;
+ int rc;
+
+ AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
+
+ rc = RTStrToUInt8Ex(pcszAddr, &pszNext, 10, &pAddr->au8[0]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+ if (*pszNext++ != '.')
+ return VERR_INVALID_PARAMETER;
+
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[1]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+ if (*pszNext++ != '.')
+ return VERR_INVALID_PARAMETER;
+
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[2]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+ if (*pszNext++ != '.')
+ return VERR_INVALID_PARAMETER;
+
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[3]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+
+ if (ppszNext != NULL)
+ *ppszNext = pszNext;
+ return rc;
+}
+
+
+RTDECL(int) RTNetStrToIPv4AddrEx(const char *pcszAddr, PRTNETADDRIPV4 pAddr,
+ char **ppszNext)
+{
+ return rtNetStrToIPv4AddrEx(pcszAddr, pAddr, ppszNext);
+}
+RT_EXPORT_SYMBOL(RTNetStrToIPv4AddrEx);
+
+
+RTDECL(int) RTNetStrToIPv4Addr(const char *pcszAddr, PRTNETADDRIPV4 pAddr)
+{
+ char *pszNext;
+ int rc;
+
+ AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
+
+ pcszAddr = RTStrStripL(pcszAddr);
+ rc = rtNetStrToIPv4AddrEx(pcszAddr, pAddr, &pszNext);
+ if (RT_FAILURE(rc) || rc == VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTNetStrToIPv4Addr);
+
+
+RTDECL(bool) RTNetIsIPv4AddrStr(const char *pcszAddr)
+{
+ RTNETADDRIPV4 addrIPv4;
+ char *pszNext;
+ int rc;
+
+ if (pcszAddr == NULL)
+ return false;
+
+ rc = rtNetStrToIPv4AddrEx(pcszAddr, &addrIPv4, &pszNext);
+ if (rc != VINF_SUCCESS)
+ return false;
+
+ if (*pszNext != '\0')
+ return false;
+
+ return true;
+}
+RT_EXPORT_SYMBOL(RTNetIsIPv4AddrStr);
+
+
+RTDECL(bool) RTNetStrIsIPv4AddrAny(const char *pcszAddr)
+{
+ RTNETADDRIPV4 addrIPv4;
+ char *pszNext;
+ int rc;
+
+ if (pcszAddr == NULL)
+ return false;
+
+ pcszAddr = RTStrStripL(pcszAddr);
+ rc = rtNetStrToIPv4AddrEx(pcszAddr, &addrIPv4, &pszNext);
+ if (RT_FAILURE(rc) || rc == VWRN_TRAILING_CHARS)
+ return false;
+
+ if (addrIPv4.u != 0u) /* INADDR_ANY? */
+ return false;
+
+ return true;
+}
+RT_EXPORT_SYMBOL(RTNetStrIsIPv4AddrAny);
+
+
+RTDECL(int) RTNetMaskToPrefixIPv4(PCRTNETADDRIPV4 pMask, int *piPrefix)
+{
+ AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
+
+ if (pMask->u == 0)
+ {
+ if (piPrefix != NULL)
+ *piPrefix = 0;
+ return VINF_SUCCESS;
+ }
+
+ const uint32_t uMask = RT_N2H_U32(pMask->u);
+
+ uint32_t uPrefixMask = UINT32_C(0xffffffff);
+ int iPrefixLen = 32;
+
+ while (iPrefixLen > 0)
+ {
+ if (uMask == uPrefixMask)
+ {
+ if (piPrefix != NULL)
+ *piPrefix = iPrefixLen;
+ return VINF_SUCCESS;
+ }
+
+ --iPrefixLen;
+ uPrefixMask <<= 1;
+ }
+
+ return VERR_INVALID_PARAMETER;
+}
+RT_EXPORT_SYMBOL(RTNetMaskToPrefixIPv4);
+
+
+RTDECL(int) RTNetPrefixToMaskIPv4(int iPrefix, PRTNETADDRIPV4 pMask)
+{
+ AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
+
+ if (RT_UNLIKELY(iPrefix < 0 || 32 < iPrefix))
+ return VERR_INVALID_PARAMETER;
+
+ if (RT_LIKELY(iPrefix != 0))
+ pMask->u = RT_H2N_U32(UINT32_C(0xffffffff) << (32 - iPrefix));
+ else /* avoid UB in the shift */
+ pMask->u = 0;
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTNetPrefixToMaskIPv4);
+
+
+RTDECL(int) RTNetStrToIPv4Cidr(const char *pcszAddr, PRTNETADDRIPV4 pAddr, int *piPrefix)
+{
+ RTNETADDRIPV4 Addr, Mask;
+ uint8_t u8Prefix;
+ char *pszNext;
+ int rc;
+
+ AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(piPrefix, VERR_INVALID_PARAMETER);
+
+ pcszAddr = RTStrStripL(pcszAddr);
+ rc = rtNetStrToIPv4AddrEx(pcszAddr, &Addr, &pszNext);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * If the prefix is missing, treat is as exact (/32) address
+ * specification.
+ */
+ if (*pszNext == '\0' || rc == VWRN_TRAILING_SPACES)
+ {
+ *pAddr = Addr;
+ *piPrefix = 32;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Be flexible about the way the prefix is specified after the
+ * slash: accept both the prefix length and the netmask, and for
+ * the latter accept both dotted-decimal and hex. The inputs we
+ * convert here are likely coming from a user and people have
+ * different preferences. Sometimes they just remember specific
+ * different networks in specific formats!
+ */
+ if (*pszNext == '/')
+ ++pszNext;
+ else
+ return VERR_INVALID_PARAMETER;
+
+ /* .../0x... is a hex mask */
+ if (pszNext[0] == '0' && (pszNext[1] == 'x' || pszNext[1] == 'X'))
+ {
+ rc = RTStrToUInt32Ex(pszNext, &pszNext, 16, &Mask.u);
+ if (rc == VINF_SUCCESS || rc == VWRN_TRAILING_SPACES)
+ Mask.u = RT_H2N_U32(Mask.u);
+ else
+ return VERR_INVALID_PARAMETER;
+
+ int iPrefix;
+ rc = RTNetMaskToPrefixIPv4(&Mask, &iPrefix);
+ if (RT_SUCCESS(rc))
+ u8Prefix = (uint8_t)iPrefix;
+ else
+ return VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ char *pszLookAhead;
+ uint32_t u32;
+ rc = RTStrToUInt32Ex(pszNext, &pszLookAhead, 10, &u32);
+
+ /* single number after the slash is prefix length */
+ if (rc == VINF_SUCCESS || rc == VWRN_TRAILING_SPACES)
+ {
+ if (u32 <= 32)
+ u8Prefix = (uint8_t)u32;
+ else
+ return VERR_INVALID_PARAMETER;
+ }
+ /* a number followed by more stuff, may be a dotted-decimal */
+ else if (rc == VWRN_TRAILING_CHARS)
+ {
+ if (*pszLookAhead != '.') /* don't even bother checking */
+ return VERR_INVALID_PARAMETER;
+
+ rc = rtNetStrToIPv4AddrEx(pszNext, &Mask, &pszNext);
+ if (rc == VINF_SUCCESS || rc == VWRN_TRAILING_SPACES)
+ {
+ int iPrefix;
+ rc = RTNetMaskToPrefixIPv4(&Mask, &iPrefix);
+ if (RT_SUCCESS(rc))
+ u8Prefix = (uint8_t)iPrefix;
+ else
+ return VERR_INVALID_PARAMETER;
+ }
+ else
+ return VERR_INVALID_PARAMETER;
+ }
+ /* failed to convert to number */
+ else
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (u8Prefix > 32)
+ return VERR_INVALID_PARAMETER;
+
+ *pAddr = Addr;
+ *piPrefix = u8Prefix;
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTNetStrToIPv4Cidr);
+
+
+static int rtNetStrToHexGroup(const char *pcszValue, char **ppszNext,
+ uint16_t *pu16)
+{
+ char *pszNext;
+ int rc;
+
+ rc = RTStrToUInt16Ex(pcszValue, &pszNext, 16, pu16);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if ( rc != VINF_SUCCESS
+ && rc != VWRN_TRAILING_CHARS
+ && rc != VWRN_TRAILING_SPACES)
+ {
+ return -rc; /* convert warning to error */
+ }
+
+ /* parser always accepts 0x prefix */
+ if (pcszValue[0] == '0' && (pcszValue[1] == 'x' || pcszValue[1] == 'X'))
+ {
+ if (pu16)
+ *pu16 = 0;
+ if (ppszNext)
+ *ppszNext = (/* UNCONST */ char *)pcszValue + 1; /* to 'x' */
+ return VWRN_TRAILING_CHARS;
+ }
+
+ /* parser accepts leading zeroes "000000f" */
+ if (pszNext - pcszValue > 4)
+ return VERR_PARSE_ERROR;
+
+ if (ppszNext)
+ *ppszNext = pszNext;
+ return rc;
+}
+
+
+/*
+ * This function deals only with the hex-group IPv6 address syntax
+ * proper (with possible embedded IPv4).
+ */
+DECLHIDDEN(int) rtNetStrToIPv6AddrBase(const char *pcszAddr, PRTNETADDRIPV6 pAddrResult,
+ char **ppszNext)
+{
+ RTNETADDRIPV6 ipv6;
+ RTNETADDRIPV4 ipv4;
+ const char *pcszPos;
+ char *pszNext;
+ int iGroup;
+ uint16_t u16;
+ int rc;
+
+ RT_ZERO(ipv6);
+
+ pcszPos = pcszAddr;
+
+ if (pcszPos[0] == ':') /* compressed zero run at the beginning? */
+ {
+ if (pcszPos[1] != ':')
+ return VERR_PARSE_ERROR;
+
+ pcszPos += 2; /* skip over "::" */
+ pszNext = (/* UNCONST */ char *)pcszPos;
+ iGroup = 1;
+ }
+ else
+ {
+ /*
+ * Scan forward until we either get complete address or find
+ * "::" compressed zero run.
+ */
+ pszNext = NULL; /* (MSC incorrectly thinks it may be used unitialized) */
+ for (iGroup = 0; iGroup < 8; ++iGroup)
+ {
+ /* check for embedded IPv4 at the end */
+ if (iGroup == 6)
+ {
+ rc = rtNetStrToIPv4AddrEx(pcszPos, &ipv4, &pszNext);
+ if (rc == VINF_SUCCESS)
+ {
+ ipv6.au32[3] = ipv4.au32[0];
+ iGroup = 8; /* filled 6 and 7 */
+ break; /* we are done */
+ }
+ }
+
+ rc = rtNetStrToHexGroup(pcszPos, &pszNext, &u16);
+ if (RT_FAILURE(rc))
+ return VERR_PARSE_ERROR;
+
+ ipv6.au16[iGroup] = RT_H2N_U16(u16);
+
+ if (iGroup == 7)
+ pcszPos = pszNext;
+ else
+ {
+ /* skip the colon that delimits this group */
+ if (*pszNext != ':')
+ return VERR_PARSE_ERROR;
+ pcszPos = pszNext + 1;
+
+ /* compressed zero run? */
+ if (*pcszPos == ':')
+ {
+ ++pcszPos; /* skip over :: */
+ pszNext += 2; /* skip over :: (in case we are done) */
+ iGroup += 2; /* current field and the zero in the next */
+ break;
+ }
+ }
+ }
+ }
+
+ if (iGroup != 8)
+ {
+ /*
+ * iGroup is the first group that can be filled by the part of
+ * the address after "::".
+ */
+ RTNETADDRIPV6 ipv6Tail;
+ const int iMaybeStart = iGroup;
+ int j;
+
+ RT_ZERO(ipv6Tail);
+
+ /*
+ * We try to accept longest match; we'll shift if necessary.
+ * Unlike the first loop, a failure to parse a group doesn't
+ * mean invalid address.
+ */
+ for (; iGroup < 8; ++iGroup)
+ {
+ /* check for embedded IPv4 at the end */
+ if (iGroup <= 6)
+ {
+ rc = rtNetStrToIPv4AddrEx(pcszPos, &ipv4, &pszNext);
+ if (rc == VINF_SUCCESS)
+ {
+ ipv6Tail.au16[iGroup] = ipv4.au16[0];
+ ipv6Tail.au16[iGroup + 1] = ipv4.au16[1];
+ iGroup = iGroup + 2; /* these two are done */
+ break; /* the rest is trailer */
+ }
+ }
+
+ rc = rtNetStrToHexGroup(pcszPos, &pszNext, &u16);
+ if (RT_FAILURE(rc))
+ break;
+
+ ipv6Tail.au16[iGroup] = RT_H2N_U16(u16);
+
+ if (iGroup == 7)
+ pcszPos = pszNext;
+ else
+ {
+ if (*pszNext != ':')
+ {
+ ++iGroup; /* this one is done */
+ break; /* the rest is trailer */
+ }
+
+ pcszPos = pszNext + 1;
+ }
+ }
+
+ for (j = 7, --iGroup; iGroup >= iMaybeStart; --j, --iGroup)
+ ipv6.au16[j] = ipv6Tail.au16[iGroup];
+ }
+
+ if (pAddrResult != NULL)
+ memcpy(pAddrResult, &ipv6, sizeof(ipv6));
+ if (ppszNext != NULL)
+ *ppszNext = pszNext;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtNetStrToIPv6AddrEx(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
+ char **ppszZone, char **ppszNext)
+{
+ char *pszNext, *pszZone;
+ int rc;
+
+ rc = rtNetStrToIPv6AddrBase(pcszAddr, pAddr, &pszNext);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (*pszNext != '%') /* is there a zone id? */
+ {
+ pszZone = NULL;
+ }
+ else
+ {
+ pszZone = pszNext + 1; /* skip '%' zone id delimiter */
+ if (*pszZone == '\0')
+ return VERR_PARSE_ERROR; /* empty zone id */
+
+ /*
+ * XXX: this is speculative as zone id syntax is
+ * implementation dependent, so we kinda guess here (accepting
+ * unreserved characters from URI syntax).
+ */
+ for (pszNext = pszZone; *pszNext != '\0'; ++pszNext)
+ {
+ const char c = *pszNext;
+ if ( !('0' <= c && c <= '9')
+ && !('a' <= c && c <= 'z')
+ && !('A' <= c && c <= 'Z')
+ && c != '_'
+ && c != '.'
+ && c != '-'
+ && c != '~')
+ {
+ break;
+ }
+ }
+ }
+
+ if (ppszZone != NULL)
+ *ppszZone = pszZone;
+ if (ppszNext != NULL)
+ *ppszNext = pszNext;
+
+ if (*pszNext == '\0') /* all input string consumed */
+ return VINF_SUCCESS;
+ else
+ {
+ while (*pszNext == ' ' || *pszNext == '\t')
+ ++pszNext;
+ if (*pszNext == '\0')
+ return VWRN_TRAILING_SPACES;
+ else
+ return VWRN_TRAILING_CHARS;
+ }
+}
+
+
+RTDECL(int) RTNetStrToIPv6AddrEx(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
+ char **ppszNext)
+{
+ AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
+
+ return rtNetStrToIPv6AddrBase(pcszAddr, pAddr, ppszNext);
+}
+RT_EXPORT_SYMBOL(RTNetStrToIPv6AddrEx);
+
+
+RTDECL(int) RTNetStrToIPv6Addr(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
+ char **ppszZone)
+{
+ int rc;
+
+ AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppszZone, VERR_INVALID_PARAMETER);
+
+ pcszAddr = RTStrStripL(pcszAddr);
+ rc = rtNetStrToIPv6AddrEx(pcszAddr, pAddr, ppszZone, NULL);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
+ return VERR_INVALID_PARAMETER;
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTNetStrToIPv6Addr);
+
+
+RTDECL(bool) RTNetIsIPv6AddrStr(const char *pcszAddr)
+{
+ RTNETADDRIPV6 addrIPv6;
+ int rc;
+
+ if (pcszAddr == NULL)
+ return false;
+
+ rc = rtNetStrToIPv6AddrEx(pcszAddr, &addrIPv6, NULL, NULL);
+ if (rc != VINF_SUCCESS)
+ return false;
+
+ return true;
+}
+RT_EXPORT_SYMBOL(RTNetIsIPv6AddrStr);
+
+
+RTDECL(bool) RTNetStrIsIPv6AddrAny(const char *pcszAddr)
+{
+ RTNETADDRIPV6 addrIPv6;
+ char *pszZone, *pszNext;
+ int rc;
+
+ if (pcszAddr == NULL)
+ return false;
+
+ pcszAddr = RTStrStripL(pcszAddr);
+ rc = rtNetStrToIPv6AddrEx(pcszAddr, &addrIPv6, &pszZone, &pszNext);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
+ return false;
+
+ if (pszZone != NULL)
+ return false;
+
+ if (addrIPv6.s.Lo != 0 || addrIPv6.s.Hi != 0) /* in6addr_any? */
+ return false;
+
+ return true;
+}
+RT_EXPORT_SYMBOL(RTNetStrIsIPv6AddrAny);
+
+
+RTDECL(int) RTNetMaskToPrefixIPv6(PCRTNETADDRIPV6 pMask, int *piPrefix)
+{
+ AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
+
+ int iPrefix = 0;
+ unsigned int i;
+
+ for (i = 0; i < RT_ELEMENTS(pMask->au8); ++i)
+ {
+ int iBits;
+ switch (pMask->au8[i])
+ {
+ case 0x00: iBits = 0; break;
+ case 0x80: iBits = 1; break;
+ case 0xc0: iBits = 2; break;
+ case 0xe0: iBits = 3; break;
+ case 0xf0: iBits = 4; break;
+ case 0xf8: iBits = 5; break;
+ case 0xfc: iBits = 6; break;
+ case 0xfe: iBits = 7; break;
+ case 0xff: iBits = 8; break;
+ default: /* non-contiguous mask */
+ return VERR_INVALID_PARAMETER;
+ }
+
+ iPrefix += iBits;
+ if (iBits != 8)
+ break;
+ }
+
+ for (++i; i < RT_ELEMENTS(pMask->au8); ++i)
+ if (pMask->au8[i] != 0)
+ return VERR_INVALID_PARAMETER;
+
+ if (piPrefix != NULL)
+ *piPrefix = iPrefix;
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTNetMaskToPrefixIPv6);
+
+
+RTDECL(int) RTNetPrefixToMaskIPv6(int iPrefix, PRTNETADDRIPV6 pMask)
+{
+ AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
+
+ if (RT_UNLIKELY(iPrefix < 0 || 128 < iPrefix))
+ return VERR_INVALID_PARAMETER;
+
+ for (unsigned int i = 0; i < RT_ELEMENTS(pMask->au32); ++i)
+ {
+ if (iPrefix == 0)
+ {
+ pMask->au32[i] = 0;
+ }
+ else if (iPrefix >= 32)
+ {
+ pMask->au32[i] = UINT32_C(0xffffffff);
+ iPrefix -= 32;
+ }
+ else
+ {
+ pMask->au32[i] = RT_H2N_U32(UINT32_C(0xffffffff) << (32 - iPrefix));
+ iPrefix = 0;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTNetPrefixToMaskIPv6);
+
+
+RTDECL(int) RTNetStrToIPv6Cidr(const char *pcszAddr, PRTNETADDRIPV6 pAddr, int *piPrefix)
+{
+ RTNETADDRIPV6 Addr;
+ uint8_t u8Prefix;
+ char *pszZone, *pszNext;
+ int rc;
+
+ AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(piPrefix, VERR_INVALID_PARAMETER);
+
+ pcszAddr = RTStrStripL(pcszAddr);
+ rc = rtNetStrToIPv6AddrEx(pcszAddr, &Addr, &pszZone, &pszNext);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ RT_NOREF(pszZone);
+
+ /*
+ * If the prefix is missing, treat is as exact (/128) address
+ * specification.
+ */
+ if (*pszNext == '\0' || rc == VWRN_TRAILING_SPACES)
+ {
+ *pAddr = Addr;
+ *piPrefix = 128;
+ return VINF_SUCCESS;
+ }
+
+ if (*pszNext != '/')
+ return VERR_INVALID_PARAMETER;
+
+ ++pszNext;
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &u8Prefix);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
+ return VERR_INVALID_PARAMETER;
+
+ if (u8Prefix > 128)
+ return VERR_INVALID_PARAMETER;
+
+ *pAddr = Addr;
+ *piPrefix = u8Prefix;
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTNetStrToIPv6Cidr);