Index: trunk/src/VBox/Runtime/common/net/netaddrstr2.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/net/netaddrstr2.cpp	(revision 50418)
+++ trunk/src/VBox/Runtime/common/net/netaddrstr2.cpp	(revision 50456)
@@ -31,4 +31,5 @@
 #include <iprt/net.h>
 
+#include <iprt/asm.h>
 #include <iprt/mem.h>
 #include <iprt/string.h>
@@ -123,2 +124,285 @@
 }
 RT_EXPORT_SYMBOL(RTNetIsIPv4AddrStr);
+
+
+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;
+
+    memset(&ipv6, 0, sizeof(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.
+         */
+        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;
+
+        memset(&ipv6Tail, 0, sizeof(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);
