VirtualBox

Ignore:
Timestamp:
Feb 14, 2014 2:10:23 AM (11 years ago)
Author:
vboxsync
Message:

RTNetStrToIPv6Addr: first cut at IPv6 address parsing. I'm still not
sure how to deal with zone ids properly given that RFC 4007 leaves
their syntax as "implementation dependent". Suggestions are welcome.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/net/netaddrstr2.cpp

    r50418 r50456  
    3131#include <iprt/net.h>
    3232
     33#include <iprt/asm.h>
    3334#include <iprt/mem.h>
    3435#include <iprt/string.h>
     
    123124}
    124125RT_EXPORT_SYMBOL(RTNetIsIPv4AddrStr);
     126
     127
     128static int rtNetStrToHexGroup(const char *pcszValue, char **ppszNext,
     129                              uint16_t *pu16)
     130{
     131    char *pszNext;
     132    int rc;
     133
     134    rc = RTStrToUInt16Ex(pcszValue, &pszNext, 16, pu16);
     135    if (RT_FAILURE(rc))
     136        return rc;
     137
     138    if (   rc != VINF_SUCCESS
     139        && rc != VWRN_TRAILING_CHARS
     140        && rc != VWRN_TRAILING_SPACES)
     141    {
     142        return -rc;             /* convert warning to error */
     143    }
     144
     145    /* parser always accepts 0x prefix */
     146    if (pcszValue[0] == '0' && (pcszValue[1] == 'x' || pcszValue[1] == 'X'))
     147    {
     148        if (pu16)
     149            *pu16 = 0;
     150        if (ppszNext)
     151            *ppszNext = (/* UNCONST */ char *)pcszValue + 1; /* to 'x' */
     152        return VWRN_TRAILING_CHARS;
     153    }
     154
     155    /* parser accepts leading zeroes "000000f" */
     156    if (pszNext - pcszValue > 4)
     157        return VERR_PARSE_ERROR;
     158
     159    if (ppszNext)
     160        *ppszNext = pszNext;
     161    return rc;
     162}
     163
     164
     165/*
     166 * This function deals only with the hex-group IPv6 address syntax
     167 * proper (with possible embedded IPv4).
     168 */
     169DECLHIDDEN(int) rtNetStrToIPv6AddrBase(const char *pcszAddr, PRTNETADDRIPV6 pAddrResult,
     170                                       char **ppszNext)
     171{
     172    RTNETADDRIPV6 ipv6;
     173    RTNETADDRIPV4 ipv4;
     174    const char *pcszPos;
     175    char *pszNext;
     176    int iGroup;
     177    uint16_t u16;
     178    int rc;
     179
     180    memset(&ipv6, 0, sizeof(ipv6));
     181
     182    pcszPos = pcszAddr;
     183
     184    if (pcszPos[0] == ':') /* compressed zero run at the beginning? */
     185    {
     186        if (pcszPos[1] != ':')
     187            return VERR_PARSE_ERROR;
     188
     189        pcszPos += 2;           /* skip over "::" */
     190        pszNext = (/* UNCONST */ char *)pcszPos;
     191        iGroup = 1;
     192    }
     193    else
     194    {
     195        /*
     196         * Scan forward until we either get complete address or find
     197         * "::" compressed zero run.
     198         */
     199        for (iGroup = 0; iGroup < 8; ++iGroup)
     200        {
     201            /* check for embedded IPv4 at the end */
     202            if (iGroup == 6)
     203            {
     204                rc = rtNetStrToIPv4AddrEx(pcszPos, &ipv4, &pszNext);
     205                if (rc == VINF_SUCCESS)
     206                {
     207                    ipv6.au32[3] = ipv4.au32[0];
     208                    iGroup = 8; /* filled 6 and 7 */
     209                    break;      /* we are done */
     210                }
     211            }
     212
     213            rc = rtNetStrToHexGroup(pcszPos, &pszNext, &u16);
     214            if (RT_FAILURE(rc))
     215                return VERR_PARSE_ERROR;
     216
     217            ipv6.au16[iGroup] = RT_H2N_U16(u16);
     218
     219            if (iGroup == 7)
     220                pcszPos = pszNext;
     221            else
     222            {
     223                /* skip the colon that delimits this group */
     224                if (*pszNext != ':')
     225                    return VERR_PARSE_ERROR;
     226                pcszPos = pszNext + 1;
     227
     228                /* compressed zero run? */
     229                if (*pcszPos == ':')
     230                {
     231                    ++pcszPos;    /* skip over :: */
     232                    pszNext += 2; /* skip over :: (in case we are done) */
     233                    iGroup += 2;  /* current field and the zero in the next */
     234                    break;
     235                }
     236            }
     237        }
     238    }
     239
     240    if (iGroup != 8)
     241    {
     242        /*
     243         * iGroup is the first group that can be filled by the part of
     244         * the address after "::".
     245         */
     246        RTNETADDRIPV6 ipv6Tail;
     247        const int iMaybeStart = iGroup;
     248        int j;
     249
     250        memset(&ipv6Tail, 0, sizeof(ipv6Tail));
     251
     252        /*
     253         * We try to accept longest match; we'll shift if necessary.
     254         * Unlike the first loop, a failure to parse a group doesn't
     255         * mean invalid address.
     256         */
     257        for (; iGroup < 8; ++iGroup)
     258        {
     259            /* check for embedded IPv4 at the end */
     260            if (iGroup <= 6)
     261            {
     262                rc = rtNetStrToIPv4AddrEx(pcszPos, &ipv4, &pszNext);
     263                if (rc == VINF_SUCCESS)
     264                {
     265                    ipv6Tail.au16[iGroup]     = ipv4.au16[0];
     266                    ipv6Tail.au16[iGroup + 1] = ipv4.au16[1];
     267                    iGroup = iGroup + 2; /* these two are done */
     268                    break;               /* the rest is trailer */
     269                }
     270            }
     271
     272            rc = rtNetStrToHexGroup(pcszPos, &pszNext, &u16);
     273            if (RT_FAILURE(rc))
     274                break;
     275
     276            ipv6Tail.au16[iGroup] = RT_H2N_U16(u16);
     277
     278            if (iGroup == 7)
     279                pcszPos = pszNext;
     280            else
     281            {
     282                if (*pszNext != ':')
     283                {
     284                    ++iGroup;   /* this one is done */
     285                    break;      /* the rest is trailer */
     286                }
     287
     288                pcszPos = pszNext + 1;
     289            }
     290        }
     291
     292        for (j = 7, --iGroup; iGroup >= iMaybeStart; --j, --iGroup)
     293            ipv6.au16[j] = ipv6Tail.au16[iGroup];
     294    }
     295
     296    if (pAddrResult != NULL)
     297        memcpy(pAddrResult, &ipv6, sizeof(ipv6));
     298    if (ppszNext != NULL)
     299        *ppszNext = pszNext;
     300    return VINF_SUCCESS;
     301}
     302
     303
     304DECLHIDDEN(int) rtNetStrToIPv6AddrEx(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
     305                                     char **ppszZone, char **ppszNext)
     306{
     307    char *pszNext, *pszZone;
     308    int rc;
     309
     310    rc = rtNetStrToIPv6AddrBase(pcszAddr, pAddr, &pszNext);
     311    if (RT_FAILURE(rc))
     312        return rc;
     313
     314    if (*pszNext != '%')      /* is there a zone id? */
     315    {
     316        pszZone = NULL;
     317    }
     318    else
     319    {
     320        pszZone = pszNext + 1; /* skip '%' zone id delimiter */
     321        if (*pszZone == '\0')
     322            return VERR_PARSE_ERROR; /* empty zone id */
     323
     324        /*
     325         * XXX: this is speculative as zone id syntax is
     326         * implementation dependent, so we kinda guess here (accepting
     327         * unreserved characters from URI syntax).
     328         */
     329        for (pszNext = pszZone; *pszNext != '\0'; ++pszNext)
     330        {
     331            const char c = *pszNext;
     332            if (   !('0' <= c && c <= '9')
     333                   && !('a' <= c && c <= 'z')
     334                   && !('A' <= c && c <= 'Z')
     335                   && c != '_'
     336                   && c != '.'
     337                   && c != '-'
     338                   && c != '~')
     339            {
     340                break;
     341            }
     342        }
     343    }
     344
     345    if (ppszZone != NULL)
     346        *ppszZone = pszZone;
     347    if (ppszNext != NULL)
     348        *ppszNext = pszNext;
     349
     350    if (*pszNext == '\0')       /* all input string consumed */
     351        return VINF_SUCCESS;
     352    else
     353    {
     354        while (*pszNext == ' ' || *pszNext == '\t')
     355            ++pszNext;
     356        if (*pszNext == '\0')
     357            return VWRN_TRAILING_SPACES;
     358        else
     359            return VWRN_TRAILING_CHARS;
     360    }
     361}
     362
     363
     364RTDECL(int) RTNetStrToIPv6AddrEx(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
     365                                 char **ppszNext)
     366{
     367    AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
     368    AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
     369
     370    return rtNetStrToIPv6AddrBase(pcszAddr, pAddr, ppszNext);
     371}
     372RT_EXPORT_SYMBOL(RTNetStrToIPv6AddrEx);
     373
     374
     375RTDECL(int) RTNetStrToIPv6Addr(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
     376                               char **ppszZone)
     377{
     378    int rc;
     379
     380    AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
     381    AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
     382    AssertPtrReturn(ppszZone, VERR_INVALID_PARAMETER);
     383
     384    pcszAddr = RTStrStripL(pcszAddr);
     385    rc = rtNetStrToIPv6AddrEx(pcszAddr, pAddr, ppszZone, NULL);
     386    if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
     387        return VERR_INVALID_PARAMETER;
     388
     389    return VINF_SUCCESS;   
     390}
     391RT_EXPORT_SYMBOL(RTNetStrToIPv6Addr);
     392
     393
     394RTDECL(bool) RTNetIsIPv6AddrStr(const char *pcszAddr)
     395{
     396    RTNETADDRIPV6 addrIPv6;
     397    int rc;
     398
     399    if (pcszAddr == NULL)
     400        return false;
     401
     402    rc = rtNetStrToIPv6AddrEx(pcszAddr, &addrIPv6, NULL, NULL);
     403    if (rc != VINF_SUCCESS)
     404        return false;
     405
     406    return true;
     407}
     408RT_EXPORT_SYMBOL(RTNetIsIPv6AddrStr);
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette