LCOV - code coverage report
Current view: top level - netwerk/base - nsURLHelper.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 301 500 60.2 %
Date: 2017-07-14 16:53:18 Functions: 25 33 75.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim:set ts=4 sw=4 sts=4 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/RangedPtr.h"
       8             : 
       9             : #include <algorithm>
      10             : #include <iterator>
      11             : 
      12             : #include "nsASCIIMask.h"
      13             : #include "nsURLHelper.h"
      14             : #include "nsIFile.h"
      15             : #include "nsIURLParser.h"
      16             : #include "nsCOMPtr.h"
      17             : #include "nsCRT.h"
      18             : #include "nsNetCID.h"
      19             : #include "mozilla/Preferences.h"
      20             : #include "prnetdb.h"
      21             : #include "mozilla/Tokenizer.h"
      22             : 
      23             : using namespace mozilla;
      24             : 
      25             : //----------------------------------------------------------------------------
      26             : // Init/Shutdown
      27             : //----------------------------------------------------------------------------
      28             : 
      29             : static bool gInitialized = false;
      30             : static nsIURLParser *gNoAuthURLParser = nullptr;
      31             : static nsIURLParser *gAuthURLParser = nullptr;
      32             : static nsIURLParser *gStdURLParser = nullptr;
      33             : static int32_t gMaxLength = 1048576; // Default: 1MB
      34             : 
      35             : static void
      36           3 : InitGlobals()
      37             : {
      38           6 :     nsCOMPtr<nsIURLParser> parser;
      39             : 
      40           3 :     parser = do_GetService(NS_NOAUTHURLPARSER_CONTRACTID);
      41           3 :     NS_ASSERTION(parser, "failed getting 'noauth' url parser");
      42           3 :     if (parser) {
      43           3 :         gNoAuthURLParser = parser.get();
      44           3 :         NS_ADDREF(gNoAuthURLParser);
      45             :     }
      46             : 
      47           3 :     parser = do_GetService(NS_AUTHURLPARSER_CONTRACTID);
      48           3 :     NS_ASSERTION(parser, "failed getting 'auth' url parser");
      49           3 :     if (parser) {
      50           3 :         gAuthURLParser = parser.get();
      51           3 :         NS_ADDREF(gAuthURLParser);
      52             :     }
      53             : 
      54           3 :     parser = do_GetService(NS_STDURLPARSER_CONTRACTID);
      55           3 :     NS_ASSERTION(parser, "failed getting 'std' url parser");
      56           3 :     if (parser) {
      57           3 :         gStdURLParser = parser.get();
      58           3 :         NS_ADDREF(gStdURLParser);
      59             :     }
      60             : 
      61           3 :     gInitialized = true;
      62             :     Preferences::AddIntVarCache(&gMaxLength,
      63           3 :                                 "network.standard-url.max-length", 1048576);
      64           3 : }
      65             : 
      66             : void
      67           0 : net_ShutdownURLHelper()
      68             : {
      69           0 :     if (gInitialized) {
      70           0 :         NS_IF_RELEASE(gNoAuthURLParser);
      71           0 :         NS_IF_RELEASE(gAuthURLParser);
      72           0 :         NS_IF_RELEASE(gStdURLParser);
      73           0 :         gInitialized = false;
      74             :     }
      75           0 : }
      76             : 
      77       27006 : int32_t net_GetURLMaxLength()
      78             : {
      79       27006 :     return gMaxLength;
      80             : }
      81             : 
      82             : //----------------------------------------------------------------------------
      83             : // nsIURLParser getters
      84             : //----------------------------------------------------------------------------
      85             : 
      86             : nsIURLParser *
      87         151 : net_GetAuthURLParser()
      88             : {
      89         151 :     if (!gInitialized)
      90           0 :         InitGlobals();
      91         151 :     return gAuthURLParser;
      92             : }
      93             : 
      94             : nsIURLParser *
      95        5715 : net_GetNoAuthURLParser()
      96             : {
      97        5715 :     if (!gInitialized)
      98           0 :         InitGlobals();
      99        5715 :     return gNoAuthURLParser;
     100             : }
     101             : 
     102             : nsIURLParser *
     103       12233 : net_GetStdURLParser()
     104             : {
     105       12233 :     if (!gInitialized)
     106           3 :         InitGlobals();
     107       12233 :     return gStdURLParser;
     108             : }
     109             : 
     110             : //---------------------------------------------------------------------------
     111             : // GetFileFromURLSpec implementations
     112             : //---------------------------------------------------------------------------
     113             : nsresult
     114           0 : net_GetURLSpecFromDir(nsIFile *aFile, nsACString &result)
     115             : {
     116           0 :     nsAutoCString escPath;
     117           0 :     nsresult rv = net_GetURLSpecFromActualFile(aFile, escPath);
     118           0 :     if (NS_FAILED(rv))
     119           0 :         return rv;
     120             : 
     121           0 :     if (escPath.Last() != '/') {
     122           0 :         escPath += '/';
     123             :     }
     124             : 
     125           0 :     result = escPath;
     126           0 :     return NS_OK;
     127             : }
     128             : 
     129             : nsresult
     130        1121 : net_GetURLSpecFromFile(nsIFile *aFile, nsACString &result)
     131             : {
     132        2242 :     nsAutoCString escPath;
     133        1121 :     nsresult rv = net_GetURLSpecFromActualFile(aFile, escPath);
     134        1121 :     if (NS_FAILED(rv))
     135           0 :         return rv;
     136             : 
     137             :     // if this file references a directory, then we need to ensure that the
     138             :     // URL ends with a slash.  this is important since it affects the rules
     139             :     // for relative URL resolution when this URL is used as a base URL.
     140             :     // if the file does not exist, then we make no assumption about its type,
     141             :     // and simply leave the URL unmodified.
     142        1121 :     if (escPath.Last() != '/') {
     143             :         bool dir;
     144        1121 :         rv = aFile->IsDirectory(&dir);
     145        1121 :         if (NS_SUCCEEDED(rv) && dir)
     146          15 :             escPath += '/';
     147             :     }
     148             : 
     149        1121 :     result = escPath;
     150        1121 :     return NS_OK;
     151             : }
     152             : 
     153             : //----------------------------------------------------------------------------
     154             : // file:// URL parsing
     155             : //----------------------------------------------------------------------------
     156             : 
     157             : nsresult
     158        2655 : net_ParseFileURL(const nsACString &inURL,
     159             :                  nsACString &outDirectory,
     160             :                  nsACString &outFileBaseName,
     161             :                  nsACString &outFileExtension)
     162             : {
     163             :     nsresult rv;
     164             : 
     165        2655 :     if (inURL.Length() > (uint32_t) gMaxLength) {
     166           0 :         return NS_ERROR_MALFORMED_URI;
     167             :     }
     168             : 
     169        2655 :     outDirectory.Truncate();
     170        2655 :     outFileBaseName.Truncate();
     171        2655 :     outFileExtension.Truncate();
     172             : 
     173        5310 :     const nsPromiseFlatCString &flatURL = PromiseFlatCString(inURL);
     174        2655 :     const char *url = flatURL.get();
     175             : 
     176        5310 :     nsAutoCString scheme;
     177        2655 :     rv = net_ExtractURLScheme(flatURL, scheme);
     178        2655 :     if (NS_FAILED(rv)) return rv;
     179             : 
     180        2655 :     if (!scheme.EqualsLiteral("file")) {
     181           0 :         NS_ERROR("must be a file:// url");
     182           0 :         return NS_ERROR_UNEXPECTED;
     183             :     }
     184             : 
     185        2655 :     nsIURLParser *parser = net_GetNoAuthURLParser();
     186        2655 :     NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
     187             : 
     188             :     uint32_t pathPos, filepathPos, directoryPos, basenamePos, extensionPos;
     189             :     int32_t pathLen, filepathLen, directoryLen, basenameLen, extensionLen;
     190             : 
     191             :     // invoke the parser to extract the URL path
     192        2655 :     rv = parser->ParseURL(url, flatURL.Length(),
     193             :                           nullptr, nullptr, // don't care about scheme
     194             :                           nullptr, nullptr, // don't care about authority
     195        5310 :                           &pathPos, &pathLen);
     196        2655 :     if (NS_FAILED(rv)) return rv;
     197             : 
     198             :     // invoke the parser to extract filepath from the path
     199        2655 :     rv = parser->ParsePath(url + pathPos, pathLen,
     200             :                            &filepathPos, &filepathLen,
     201             :                            nullptr, nullptr,  // don't care about query
     202        5310 :                            nullptr, nullptr); // don't care about ref
     203        2655 :     if (NS_FAILED(rv)) return rv;
     204             : 
     205        2655 :     filepathPos += pathPos;
     206             : 
     207             :     // invoke the parser to extract the directory and filename from filepath
     208        2655 :     rv = parser->ParseFilePath(url + filepathPos, filepathLen,
     209             :                                &directoryPos, &directoryLen,
     210             :                                &basenamePos, &basenameLen,
     211        5310 :                                &extensionPos, &extensionLen);
     212        2655 :     if (NS_FAILED(rv)) return rv;
     213             : 
     214        2655 :     if (directoryLen > 0)
     215        2655 :         outDirectory = Substring(inURL, filepathPos + directoryPos, directoryLen);
     216        2655 :     if (basenameLen > 0)
     217        2655 :         outFileBaseName = Substring(inURL, filepathPos + basenamePos, basenameLen);
     218        2655 :     if (extensionLen > 0)
     219        2653 :         outFileExtension = Substring(inURL, filepathPos + extensionPos, extensionLen);
     220             :     // since we are using a no-auth url parser, there will never be a host
     221             :     // XXX not strictly true... file://localhost/foo/bar.html is a valid URL
     222             : 
     223        2655 :     return NS_OK;
     224             : }
     225             : 
     226             : //----------------------------------------------------------------------------
     227             : // path manipulation functions
     228             : //----------------------------------------------------------------------------
     229             : 
     230             : // Replace all /./ with a / while resolving URLs
     231             : // But only till #?
     232             : void
     233        5030 : net_CoalesceDirs(netCoalesceFlags flags, char* path)
     234             : {
     235             :     /* Stolen from the old netlib's mkparse.c.
     236             :      *
     237             :      * modifies a url of the form   /foo/../foo1  ->  /foo1
     238             :      *                       and    /foo/./foo1   ->  /foo/foo1
     239             :      *                       and    /foo/foo1/..  ->  /foo/
     240             :      */
     241        5030 :     char *fwdPtr = path;
     242        5030 :     char *urlPtr = path;
     243        5030 :     char *lastslash = path;
     244        5030 :     uint32_t traversal = 0;
     245        5030 :     uint32_t special_ftp_len = 0;
     246             : 
     247             :     /* Remember if this url is a special ftp one: */
     248        5030 :     if (flags & NET_COALESCE_DOUBLE_SLASH_IS_ROOT)
     249             :     {
     250             :        /* some schemes (for example ftp) have the speciality that
     251             :           the path can begin // or /%2F to mark the root of the
     252             :           servers filesystem, a simple / only marks the root relative
     253             :           to the user loging in. We remember the length of the marker */
     254           0 :         if (nsCRT::strncasecmp(path,"/%2F",4) == 0)
     255           0 :             special_ftp_len = 4;
     256           0 :         else if (nsCRT::strncmp(path,"//",2) == 0 )
     257           0 :             special_ftp_len = 2;
     258             :     }
     259             : 
     260             :     /* find the last slash before # or ? */
     261     1191390 :     for(; (*fwdPtr != '\0') &&
     262      795984 :             (*fwdPtr != '?') &&
     263      395563 :             (*fwdPtr != '#'); ++fwdPtr)
     264             :     {
     265             :     }
     266             : 
     267             :     /* found nothing, but go back one only */
     268             :     /* if there is something to go back to */
     269        5030 :     if (fwdPtr != path && *fwdPtr == '\0')
     270             :     {
     271        4843 :         --fwdPtr;
     272             :     }
     273             : 
     274             :     /* search the slash */
     275      154352 :     for(; (fwdPtr != path) &&
     276       77176 :             (*fwdPtr != '/'); --fwdPtr)
     277             :     {
     278             :     }
     279        5030 :     lastslash = fwdPtr;
     280        5030 :     fwdPtr = path;
     281             : 
     282             :     /* replace all %2E or %2e with . in the path */
     283             :     /* but stop at lastchar if non null */
     284      965266 :     for(; (*fwdPtr != '\0') &&
     285      646864 :             (*fwdPtr != '?') &&
     286      970296 :             (*fwdPtr != '#') &&
     287      646864 :             (*lastslash == '\0' || fwdPtr != lastslash); ++fwdPtr)
     288             :     {
     289      318402 :         if (*fwdPtr == '%' && *(fwdPtr+1) == '2' &&
     290           0 :             (*(fwdPtr+2) == 'E' || *(fwdPtr+2) == 'e'))
     291             :         {
     292           0 :             *urlPtr++ = '.';
     293           0 :             ++fwdPtr;
     294           0 :             ++fwdPtr;
     295             :         }
     296             :         else
     297             :         {
     298      318402 :             *urlPtr++ = *fwdPtr;
     299             :         }
     300             :     }
     301             :     // Copy remaining stuff past the #?;
     302      164232 :     for (; *fwdPtr != '\0'; ++fwdPtr)
     303             :     {
     304       79601 :         *urlPtr++ = *fwdPtr;
     305             :     }
     306        5030 :     *urlPtr = '\0';  // terminate the url
     307             : 
     308             :     // start again, this time for real
     309        5030 :     fwdPtr = path;
     310        5030 :     urlPtr = path;
     311             : 
     312     1190664 :     for(; (*fwdPtr != '\0') &&
     313      795500 :             (*fwdPtr != '?') &&
     314      395321 :             (*fwdPtr != '#'); ++fwdPtr)
     315             :     {
     316      395149 :         if (*fwdPtr == '/' && *(fwdPtr+1) == '.' && *(fwdPtr+2) == '/' )
     317             :         {
     318             :             // remove . followed by slash
     319         180 :             ++fwdPtr;
     320             :         }
     321      395000 :         else if(*fwdPtr == '/' && *(fwdPtr+1) == '.' && *(fwdPtr+2) == '.' &&
     322          31 :                 (*(fwdPtr+3) == '/' ||
     323           0 :                     *(fwdPtr+3) == '\0' || // This will take care of
     324           0 :                     *(fwdPtr+3) == '?' ||  // something like foo/bar/..#sometag
     325           0 :                     *(fwdPtr+3) == '#'))
     326             :         {
     327             :             // remove foo/..
     328             :             // reverse the urlPtr to the previous slash if possible
     329             :             // if url does not allow relative root then drop .. above root
     330             :             // otherwise retain them in the path
     331          62 :             if(traversal > 0 || !(flags &
     332             :                                   NET_COALESCE_ALLOW_RELATIVE_ROOT))
     333             :             {
     334          31 :                 if (urlPtr != path)
     335          31 :                     urlPtr--; // we must be going back at least by one
     336         219 :                 for(;*urlPtr != '/' && urlPtr != path; urlPtr--)
     337             :                     ;  // null body
     338          31 :                 --traversal; // count back
     339             :                 // forward the fwdPtr past the ../
     340          31 :                 fwdPtr += 2;
     341             :                 // if we have reached the beginning of the path
     342             :                 // while searching for the previous / and we remember
     343             :                 // that it is an url that begins with /%2F then
     344             :                 // advance urlPtr again by 3 chars because /%2F already
     345             :                 // marks the root of the path
     346          31 :                 if (urlPtr == path && special_ftp_len > 3)
     347             :                 {
     348           0 :                     ++urlPtr;
     349           0 :                     ++urlPtr;
     350           0 :                     ++urlPtr;
     351             :                 }
     352             :                 // special case if we have reached the end
     353             :                 // to preserve the last /
     354          62 :                 if (*fwdPtr == '.' && *(fwdPtr+1) == '\0')
     355           0 :                     ++urlPtr;
     356             :             }
     357             :             else
     358             :             {
     359             :                 // there are to much /.. in this path, just copy them instead.
     360             :                 // forward the urlPtr past the /.. and copying it
     361             : 
     362             :                 // However if we remember it is an url that starts with
     363             :                 // /%2F and urlPtr just points at the "F" of "/%2F" then do
     364             :                 // not overwrite it with the /, just copy .. and move forward
     365             :                 // urlPtr.
     366           0 :                 if (special_ftp_len > 3 && urlPtr == path+special_ftp_len-1)
     367           0 :                     ++urlPtr;
     368             :                 else
     369           0 :                     *urlPtr++ = *fwdPtr;
     370           0 :                 ++fwdPtr;
     371           0 :                 *urlPtr++ = *fwdPtr;
     372           0 :                 ++fwdPtr;
     373           0 :                 *urlPtr++ = *fwdPtr;
     374             :             }
     375             :         }
     376             :         else
     377             :         {
     378             :             // count the hierachie, but only if we do not have reached
     379             :             // the root of some special urls with a special root marker
     380      394938 :             if (*fwdPtr == '/' &&  *(fwdPtr+1) != '.' &&
     381           0 :                (special_ftp_len != 2 || *(fwdPtr+1) != '/'))
     382       41528 :                 traversal++;
     383             :             // copy the url incrementaly
     384      394938 :             *urlPtr++ = *fwdPtr;
     385             :         }
     386             :     }
     387             : 
     388             :     /*
     389             :      *  Now lets remove trailing . case
     390             :      *     /foo/foo1/.   ->  /foo/foo1/
     391             :      */
     392             : 
     393        5030 :     if ((urlPtr > (path+1)) && (*(urlPtr-1) == '.') && (*(urlPtr-2) == '/'))
     394           0 :         urlPtr--;
     395             : 
     396             :     // Copy remaining stuff past the #?;
     397       10254 :     for (; *fwdPtr != '\0'; ++fwdPtr)
     398             :     {
     399        2612 :         *urlPtr++ = *fwdPtr;
     400             :     }
     401        5030 :     *urlPtr = '\0';  // terminate the url
     402        5030 : }
     403             : 
     404             : nsresult
     405           0 : net_ResolveRelativePath(const nsACString &relativePath,
     406             :                         const nsACString &basePath,
     407             :                         nsACString &result)
     408             : {
     409           0 :     nsAutoCString name;
     410           0 :     nsAutoCString path(basePath);
     411           0 :     bool needsDelim = false;
     412             : 
     413           0 :     if ( !path.IsEmpty() ) {
     414           0 :         char16_t last = path.Last();
     415           0 :         needsDelim = !(last == '/');
     416             :     }
     417             : 
     418           0 :     nsACString::const_iterator beg, end;
     419           0 :     relativePath.BeginReading(beg);
     420           0 :     relativePath.EndReading(end);
     421             : 
     422           0 :     bool stop = false;
     423             :     char c;
     424           0 :     for (; !stop; ++beg) {
     425           0 :         c = (beg == end) ? '\0' : *beg;
     426             :         //printf("%c [name=%s] [path=%s]\n", c, name.get(), path.get());
     427           0 :         switch (c) {
     428             :           case '\0':
     429             :           case '#':
     430             :           case '?':
     431           0 :             stop = true;
     432             :             MOZ_FALLTHROUGH;
     433             :           case '/':
     434             :             // delimiter found
     435           0 :             if (name.EqualsLiteral("..")) {
     436             :                 // pop path
     437             :                 // If we already have the delim at end, then
     438             :                 //  skip over that when searching for next one to the left
     439           0 :                 int32_t offset = path.Length() - (needsDelim ? 1 : 2);
     440             :                 // First check for errors
     441           0 :                 if (offset < 0 )
     442           0 :                     return NS_ERROR_MALFORMED_URI;
     443           0 :                 int32_t pos = path.RFind("/", false, offset);
     444           0 :                 if (pos >= 0)
     445           0 :                     path.Truncate(pos + 1);
     446             :                 else
     447           0 :                     path.Truncate();
     448             :             }
     449           0 :             else if (name.IsEmpty() || name.EqualsLiteral(".")) {
     450             :                 // do nothing
     451             :             }
     452             :             else {
     453             :                 // append name to path
     454           0 :                 if (needsDelim)
     455           0 :                     path += '/';
     456           0 :                 path += name;
     457           0 :                 needsDelim = true;
     458             :             }
     459           0 :             name.Truncate();
     460           0 :             break;
     461             : 
     462             :           default:
     463             :             // append char to name
     464           0 :             name += c;
     465             :         }
     466             :     }
     467             :     // append anything left on relativePath (e.g. #..., ;..., ?...)
     468           0 :     if (c != '\0')
     469           0 :         path += Substring(--beg, end);
     470             : 
     471           0 :     result = path;
     472           0 :     return NS_OK;
     473             : }
     474             : 
     475             : //----------------------------------------------------------------------------
     476             : // scheme fu
     477             : //----------------------------------------------------------------------------
     478             : 
     479        7064 : static bool isAsciiAlpha(char c) {
     480        7064 :     return nsCRT::IsAsciiAlpha(c);
     481             : }
     482             : 
     483             : static bool
     484       38419 : net_IsValidSchemeChar(const char aChar)
     485             : {
     486       84104 :     if (nsCRT::IsAsciiAlpha(aChar) || nsCRT::IsAsciiDigit(aChar) ||
     487       45681 :         aChar == '+' || aChar == '.' || aChar == '-') {
     488       31646 :         return true;
     489             :     }
     490        6773 :     return false;
     491             : }
     492             : 
     493             : /* Extract URI-Scheme if possible */
     494             : nsresult
     495        6551 : net_ExtractURLScheme(const nsACString &inURI,
     496             :                      nsACString& scheme)
     497             : {
     498        6551 :     nsACString::const_iterator start, end;
     499        6551 :     inURI.BeginReading(start);
     500        6551 :     inURI.EndReading(end);
     501             : 
     502             :     // Strip C0 and space from begining
     503        6551 :     while (start != end) {
     504        6551 :         if ((uint8_t) *start > 0x20) {
     505        6551 :             break;
     506             :         }
     507           0 :         start++;
     508             :     }
     509             : 
     510       13102 :     Tokenizer p(Substring(start, end), "\r\n\t");
     511        6551 :     p.Record();
     512        6551 :     if (!p.CheckChar(isAsciiAlpha)) {
     513             :         // First char must be alpha
     514          29 :         return NS_ERROR_MALFORMED_URI;
     515             :     }
     516             : 
     517       33913 :     while (p.CheckChar(net_IsValidSchemeChar) || p.CheckWhite()) {
     518             :         // Skip valid scheme characters or \r\n\t
     519             :     }
     520             : 
     521        6522 :     if (!p.CheckChar(':')) {
     522         279 :         return NS_ERROR_MALFORMED_URI;
     523             :     }
     524             : 
     525        6243 :     p.Claim(scheme);
     526        6243 :     scheme.StripTaggedASCII(ASCIIMask::MaskCRLFTab());
     527        6243 :     return NS_OK;
     528             : }
     529             : 
     530             : bool
     531        7459 : net_IsValidScheme(const char *scheme, uint32_t schemeLen)
     532             : {
     533             :     // first char must be alpha
     534        7459 :     if (!nsCRT::IsAsciiAlpha(*scheme))
     535           0 :         return false;
     536             : 
     537             :     // nsCStrings may have embedded nulls -- reject those too
     538       78283 :     for (; schemeLen; ++scheme, --schemeLen) {
     539       35520 :         if (!(nsCRT::IsAsciiAlpha(*scheme) ||
     540         108 :               nsCRT::IsAsciiDigit(*scheme) ||
     541         108 :               *scheme == '+' ||
     542          54 :               *scheme == '.' ||
     543          54 :               *scheme == '-'))
     544           0 :             return false;
     545             :     }
     546             : 
     547        7459 :     return true;
     548             : }
     549             : 
     550             : bool
     551         513 : net_IsAbsoluteURL(const nsACString& uri)
     552             : {
     553         513 :     nsACString::const_iterator start, end;
     554         513 :     uri.BeginReading(start);
     555         513 :     uri.EndReading(end);
     556             : 
     557             :     // Strip C0 and space from begining
     558         513 :     while (start != end) {
     559         513 :         if ((uint8_t) *start > 0x20) {
     560         513 :             break;
     561             :         }
     562           0 :         start++;
     563             :     }
     564             : 
     565        1026 :     Tokenizer p(Substring(start, end), "\r\n\t");
     566             : 
     567             :     // First char must be alpha
     568         513 :     if (!p.CheckChar(isAsciiAlpha)) {
     569           4 :         return false;
     570             :     }
     571             : 
     572        4764 :     while (p.CheckChar(net_IsValidSchemeChar) || p.CheckWhite()) {
     573             :         // Skip valid scheme characters or \r\n\t
     574             :     }
     575         509 :     if (!p.CheckChar(':')) {
     576         279 :         return false;
     577             :     }
     578         230 :     p.SkipWhites();
     579             : 
     580         230 :     if (!p.CheckChar('/')) {
     581           0 :         return false;
     582             :     }
     583         230 :     p.SkipWhites();
     584             : 
     585         230 :     if (p.CheckChar('/')) {
     586             :         // aSpec is really absolute. Ignore aBaseURI in this case
     587         230 :         return true;
     588             :     }
     589           0 :     return false;
     590             : }
     591             : 
     592             : void
     593        5256 : net_FilterURIString(const nsACString& input, nsACString& result)
     594             : {
     595        5256 :     result.Truncate();
     596             : 
     597        5256 :     auto start = input.BeginReading();
     598        5256 :     auto end = input.EndReading();
     599             : 
     600             :     // Trim off leading and trailing invalid chars.
     601       10512 :     auto charFilter = [](char c) { return static_cast<uint8_t>(c) > 0x20; };
     602        5256 :     auto newStart = std::find_if(start, end, charFilter);
     603       10512 :     auto newEnd = std::find_if(
     604       10512 :         std::reverse_iterator<decltype(end)>(end),
     605       10512 :         std::reverse_iterator<decltype(newStart)>(newStart),
     606        5256 :         charFilter).base();
     607             : 
     608             :     // Check if chars need to be stripped.
     609        5256 :     bool needsStrip = false;
     610        5256 :     const ASCIIMaskArray& mask = ASCIIMask::MaskCRLFTab();
     611      403581 :     for (auto itr = start; itr != end; ++itr) {
     612      398328 :         if (ASCIIMask::IsMasked(mask, *itr)) {
     613           3 :             needsStrip = true;
     614           3 :             break;
     615             :         }
     616             :     }
     617             : 
     618             :     // Just use the passed in string rather than creating new copies if no
     619             :     // changes are necessary.
     620        5256 :     if (newStart == start && newEnd == end && !needsStrip) {
     621        5253 :         result = input;
     622        5253 :         return;
     623             :     }
     624             : 
     625           3 :     result.Assign(Substring(newStart, newEnd));
     626           3 :     if (needsStrip) {
     627           3 :         result.StripTaggedASCII(mask);
     628             :     }
     629             : }
     630             : 
     631             : 
     632             : #if defined(XP_WIN)
     633             : bool
     634             : net_NormalizeFileURL(const nsACString &aURL, nsCString &aResultBuf)
     635             : {
     636             :     bool writing = false;
     637             : 
     638             :     nsACString::const_iterator beginIter, endIter;
     639             :     aURL.BeginReading(beginIter);
     640             :     aURL.EndReading(endIter);
     641             : 
     642             :     const char *s, *begin = beginIter.get();
     643             : 
     644             :     for (s = begin; s != endIter.get(); ++s)
     645             :     {
     646             :         if (*s == '\\')
     647             :         {
     648             :             writing = true;
     649             :             if (s > begin)
     650             :                 aResultBuf.Append(begin, s - begin);
     651             :             aResultBuf += '/';
     652             :             begin = s + 1;
     653             :         }
     654             :     }
     655             :     if (writing && s > begin)
     656             :         aResultBuf.Append(begin, s - begin);
     657             : 
     658             :     return writing;
     659             : }
     660             : #endif
     661             : 
     662             : //----------------------------------------------------------------------------
     663             : // miscellaneous (i.e., stuff that should really be elsewhere)
     664             : //----------------------------------------------------------------------------
     665             : 
     666             : static inline
     667       32819 : void ToLower(char &c)
     668             : {
     669       32819 :     if ((unsigned)(c - 'A') <= (unsigned)('Z' - 'A'))
     670           0 :         c += 'a' - 'A';
     671       32819 : }
     672             : 
     673             : void
     674        6312 : net_ToLowerCase(char *str, uint32_t length)
     675             : {
     676       39131 :     for (char *end = str + length; str < end; ++str)
     677       32819 :         ToLower(*str);
     678        6312 : }
     679             : 
     680             : void
     681           0 : net_ToLowerCase(char *str)
     682             : {
     683           0 :     for (; *str; ++str)
     684           0 :         ToLower(*str);
     685           0 : }
     686             : 
     687             : char *
     688        2733 : net_FindCharInSet(const char *iter, const char *stop, const char *set)
     689             : {
     690        5268 :     for (; iter != stop && *iter; ++iter) {
     691       12664 :         for (const char *s = set; *s; ++s) {
     692       10129 :             if (*iter == *s)
     693           1 :                 return (char *) iter;
     694             :         }
     695             :     }
     696         197 :     return (char *) iter;
     697             : }
     698             : 
     699             : char *
     700         389 : net_FindCharNotInSet(const char *iter, const char *stop, const char *set)
     701             : {
     702             : repeat:
     703        2079 :     for (const char *s = set; *s; ++s) {
     704        1827 :         if (*iter == *s) {
     705         137 :             if (++iter == stop)
     706           9 :                 break;
     707         128 :             goto repeat;
     708             :         }
     709             :     }
     710         261 :     return (char *) iter;
     711             : }
     712             : 
     713             : char *
     714          53 : net_RFindCharNotInSet(const char *stop, const char *iter, const char *set)
     715             : {
     716          53 :     --iter;
     717          53 :     --stop;
     718             : 
     719          53 :     if (iter == stop)
     720           0 :         return (char *) iter;
     721             : 
     722             : repeat:
     723         159 :     for (const char *s = set; *s; ++s) {
     724         106 :         if (*iter == *s) {
     725           0 :             if (--iter == stop)
     726           0 :                 break;
     727           0 :             goto repeat;
     728             :         }
     729             :     }
     730          53 :     return (char *) iter;
     731             : }
     732             : 
     733             : #define HTTP_LWS " \t"
     734             : 
     735             : // Return the index of the closing quote of the string, if any
     736             : static uint32_t
     737           0 : net_FindStringEnd(const nsCString& flatStr,
     738             :                   uint32_t stringStart,
     739             :                   char stringDelim)
     740             : {
     741           0 :     NS_ASSERTION(stringStart < flatStr.Length() &&
     742             :                  flatStr.CharAt(stringStart) == stringDelim &&
     743             :                  (stringDelim == '"' || stringDelim == '\''),
     744             :                  "Invalid stringStart");
     745             : 
     746           0 :     const char set[] = { stringDelim, '\\', '\0' };
     747             :     do {
     748             :         // stringStart points to either the start quote or the last
     749             :         // escaped char (the char following a '\\')
     750             : 
     751             :         // Write to searchStart here, so that when we get back to the
     752             :         // top of the loop right outside this one we search from the
     753             :         // right place.
     754           0 :         uint32_t stringEnd = flatStr.FindCharInSet(set, stringStart + 1);
     755           0 :         if (stringEnd == uint32_t(kNotFound))
     756           0 :             return flatStr.Length();
     757             : 
     758           0 :         if (flatStr.CharAt(stringEnd) == '\\') {
     759             :             // Hit a backslash-escaped char.  Need to skip over it.
     760           0 :             stringStart = stringEnd + 1;
     761           0 :             if (stringStart == flatStr.Length())
     762           0 :                 return stringStart;
     763             : 
     764             :             // Go back to looking for the next escape or the string end
     765           0 :             continue;
     766             :         }
     767             : 
     768           0 :         return stringEnd;
     769             : 
     770             :     } while (true);
     771             : 
     772             :     NS_NOTREACHED("How did we get here?");
     773             :     return flatStr.Length();
     774             : }
     775             : 
     776             : 
     777             : static uint32_t
     778         194 : net_FindMediaDelimiter(const nsCString& flatStr,
     779             :                        uint32_t searchStart,
     780             :                        char delimiter)
     781             : {
     782             :     do {
     783             :         // searchStart points to the spot from which we should start looking
     784             :         // for the delimiter.
     785         194 :         const char delimStr[] = { delimiter, '"', '\0' };
     786         194 :         uint32_t curDelimPos = flatStr.FindCharInSet(delimStr, searchStart);
     787         194 :         if (curDelimPos == uint32_t(kNotFound))
     788         388 :             return flatStr.Length();
     789             : 
     790           0 :         char ch = flatStr.CharAt(curDelimPos);
     791           0 :         if (ch == delimiter) {
     792             :             // Found delimiter
     793           0 :             return curDelimPos;
     794             :         }
     795             : 
     796             :         // We hit the start of a quoted string.  Look for its end.
     797           0 :         searchStart = net_FindStringEnd(flatStr, curDelimPos, ch);
     798           0 :         if (searchStart == flatStr.Length())
     799           0 :             return searchStart;
     800             : 
     801           0 :         ++searchStart;
     802             : 
     803             :         // searchStart now points to the first char after the end of the
     804             :         // string, so just go back to the top of the loop and look for
     805             :         // |delimiter| again.
     806             :     } while (true);
     807             : 
     808             :     NS_NOTREACHED("How did we get here?");
     809             :     return flatStr.Length();
     810             : }
     811             : 
     812             : // aOffset should be added to aCharsetStart and aCharsetEnd if this
     813             : // function sets them.
     814             : static void
     815         193 : net_ParseMediaType(const nsACString &aMediaTypeStr,
     816             :                    nsACString       &aContentType,
     817             :                    nsACString       &aContentCharset,
     818             :                    int32_t          aOffset,
     819             :                    bool             *aHadCharset,
     820             :                    int32_t          *aCharsetStart,
     821             :                    int32_t          *aCharsetEnd,
     822             :                    bool             aStrict)
     823             : {
     824         386 :     const nsCString& flatStr = PromiseFlatCString(aMediaTypeStr);
     825         193 :     const char* start = flatStr.get();
     826         193 :     const char* end = start + flatStr.Length();
     827             : 
     828             :     // Trim LWS leading and trailing whitespace from type.  We include '(' in
     829             :     // the trailing trim set to catch media-type comments, which are not at all
     830             :     // standard, but may occur in rare cases.
     831         193 :     const char* type = net_FindCharNotInSet(start, end, HTTP_LWS);
     832         193 :     const char* typeEnd = net_FindCharInSet(type, end, HTTP_LWS ";(");
     833             : 
     834         193 :     const char* charset = "";
     835         193 :     const char* charsetEnd = charset;
     836         193 :     int32_t charsetParamStart = 0;
     837         193 :     int32_t charsetParamEnd = 0;
     838             : 
     839         193 :     uint32_t consumed = typeEnd - type;
     840             : 
     841             :     // Iterate over parameters
     842         193 :     bool typeHasCharset = false;
     843         193 :     uint32_t paramStart = flatStr.FindChar(';', typeEnd - start);
     844         193 :     if (paramStart != uint32_t(kNotFound)) {
     845             :         // We have parameters.  Iterate over them.
     846           1 :         uint32_t curParamStart = paramStart + 1;
     847           1 :         do {
     848             :             uint32_t curParamEnd =
     849           1 :                 net_FindMediaDelimiter(flatStr, curParamStart, ';');
     850             : 
     851           1 :             const char* paramName = net_FindCharNotInSet(start + curParamStart,
     852             :                                                          start + curParamEnd,
     853           1 :                                                          HTTP_LWS);
     854             :             static const char charsetStr[] = "charset=";
     855           1 :             if (PL_strncasecmp(paramName, charsetStr,
     856             :                                sizeof(charsetStr) - 1) == 0) {
     857           1 :                 charset = paramName + sizeof(charsetStr) - 1;
     858           1 :                 charsetEnd = start + curParamEnd;
     859           1 :                 typeHasCharset = true;
     860           1 :                 charsetParamStart = curParamStart - 1;
     861           1 :                 charsetParamEnd = curParamEnd;
     862             :             }
     863             : 
     864           1 :             consumed = curParamEnd;
     865           1 :             curParamStart = curParamEnd + 1;
     866           1 :         } while (curParamStart < flatStr.Length());
     867             :     }
     868             : 
     869         193 :     bool charsetNeedsQuotedStringUnescaping = false;
     870         193 :     if (typeHasCharset) {
     871             :         // Trim LWS leading and trailing whitespace from charset.  We include
     872             :         // '(' in the trailing trim set to catch media-type comments, which are
     873             :         // not at all standard, but may occur in rare cases.
     874           1 :         charset = net_FindCharNotInSet(charset, charsetEnd, HTTP_LWS);
     875           1 :         if (*charset == '"') {
     876           0 :             charsetNeedsQuotedStringUnescaping = true;
     877           0 :             charsetEnd =
     878           0 :                 start + net_FindStringEnd(flatStr, charset - start, *charset);
     879           0 :             charset++;
     880           0 :             NS_ASSERTION(charsetEnd >= charset, "Bad charset parsing");
     881             :         } else {
     882           1 :             charsetEnd = net_FindCharInSet(charset, charsetEnd, HTTP_LWS ";(");
     883             :         }
     884             :     }
     885             : 
     886             :     // if the server sent "*/*", it is meaningless, so do not store it.
     887             :     // also, if type is the same as aContentType, then just update the
     888             :     // charset.  however, if charset is empty and aContentType hasn't
     889             :     // changed, then don't wipe-out an existing aContentCharset.  We
     890             :     // also want to reject a mime-type if it does not include a slash.
     891             :     // some servers give junk after the charset parameter, which may
     892             :     // include a comma, so this check makes us a bit more tolerant.
     893             : 
     894         383 :     if (type != typeEnd &&
     895         573 :         memchr(type, '/', typeEnd - type) != nullptr &&
     896         190 :         (aStrict ? (net_FindCharNotInSet(start + consumed, end, HTTP_LWS) == end) :
     897         190 :                    (strncmp(type, "*/*", typeEnd - type) != 0))) {
     898             :         // Common case here is that aContentType is empty
     899         739 :         bool eq = !aContentType.IsEmpty() &&
     900         556 :             aContentType.Equals(Substring(type, typeEnd),
     901         563 :                                 nsCaseInsensitiveCStringComparator());
     902         190 :         if (!eq) {
     903         190 :             aContentType.Assign(type, typeEnd - type);
     904         190 :             ToLowerCase(aContentType);
     905             :         }
     906             : 
     907         190 :         if ((!eq && *aHadCharset) || typeHasCharset) {
     908           1 :             *aHadCharset = true;
     909           1 :             if (charsetNeedsQuotedStringUnescaping) {
     910             :                 // parameters using the "quoted-string" syntax need
     911             :                 // backslash-escapes to be unescaped (see RFC 2616 Section 2.2)
     912           0 :                 aContentCharset.Truncate();
     913           0 :                 for (const char *c = charset; c != charsetEnd; c++) {
     914           0 :                     if (*c == '\\' && c + 1 != charsetEnd) {
     915             :                         // eat escape
     916           0 :                         c++;
     917             :                     }
     918           0 :                     aContentCharset.Append(*c);
     919             :                 }
     920             :             }
     921             :             else {
     922           1 :                 aContentCharset.Assign(charset, charsetEnd - charset);
     923             :             }
     924           1 :             if (typeHasCharset) {
     925           1 :                 *aCharsetStart = charsetParamStart + aOffset;
     926           1 :                 *aCharsetEnd = charsetParamEnd + aOffset;
     927             :             }
     928             :         }
     929             :         // Only set a new charset position if this is a different type
     930             :         // from the last one we had and it doesn't already have a
     931             :         // charset param.  If this is the same type, we probably want
     932             :         // to leave the charset position on its first occurrence.
     933         190 :         if (!eq && !typeHasCharset) {
     934         189 :             int32_t charsetStart = int32_t(paramStart);
     935         189 :             if (charsetStart == kNotFound)
     936         189 :                 charsetStart =  flatStr.Length();
     937             : 
     938         189 :             *aCharsetEnd = *aCharsetStart = charsetStart + aOffset;
     939             :         }
     940             :     }
     941         193 : }
     942             : 
     943             : #undef HTTP_LWS
     944             : 
     945             : void
     946         193 : net_ParseContentType(const nsACString &aHeaderStr,
     947             :                      nsACString       &aContentType,
     948             :                      nsACString       &aContentCharset,
     949             :                      bool             *aHadCharset)
     950             : {
     951             :     int32_t dummy1, dummy2;
     952             :     net_ParseContentType(aHeaderStr, aContentType, aContentCharset,
     953         193 :                          aHadCharset, &dummy1, &dummy2);
     954         193 : }
     955             : 
     956             : void
     957         193 : net_ParseContentType(const nsACString &aHeaderStr,
     958             :                      nsACString       &aContentType,
     959             :                      nsACString       &aContentCharset,
     960             :                      bool             *aHadCharset,
     961             :                      int32_t          *aCharsetStart,
     962             :                      int32_t          *aCharsetEnd)
     963             : {
     964             :     //
     965             :     // Augmented BNF (from RFC 2616 section 3.7):
     966             :     //
     967             :     //   header-value = media-type *( LWS "," LWS media-type )
     968             :     //   media-type   = type "/" subtype *( LWS ";" LWS parameter )
     969             :     //   type         = token
     970             :     //   subtype      = token
     971             :     //   parameter    = attribute "=" value
     972             :     //   attribute    = token
     973             :     //   value        = token | quoted-string
     974             :     //
     975             :     //
     976             :     // Examples:
     977             :     //
     978             :     //   text/html
     979             :     //   text/html, text/html
     980             :     //   text/html,text/html; charset=ISO-8859-1
     981             :     //   text/html,text/html; charset="ISO-8859-1"
     982             :     //   text/html;charset=ISO-8859-1, text/html
     983             :     //   text/html;charset='ISO-8859-1', text/html
     984             :     //   application/octet-stream
     985             :     //
     986             : 
     987         193 :     *aHadCharset = false;
     988         386 :     const nsCString& flatStr = PromiseFlatCString(aHeaderStr);
     989             : 
     990             :     // iterate over media-types.  Note that ',' characters can happen
     991             :     // inside quoted strings, so we need to watch out for that.
     992         193 :     uint32_t curTypeStart = 0;
     993         193 :     do {
     994             :         // curTypeStart points to the start of the current media-type.  We want
     995             :         // to look for its end.
     996             :         uint32_t curTypeEnd =
     997         193 :             net_FindMediaDelimiter(flatStr, curTypeStart, ',');
     998             : 
     999             :         // At this point curTypeEnd points to the spot where the media-type
    1000             :         // starting at curTypeEnd ends.  Time to parse that!
    1001         386 :         net_ParseMediaType(Substring(flatStr, curTypeStart,
    1002             :                                      curTypeEnd - curTypeStart),
    1003             :                            aContentType, aContentCharset, curTypeStart,
    1004         193 :                            aHadCharset, aCharsetStart, aCharsetEnd, false);
    1005             : 
    1006             :         // And let's move on to the next media-type
    1007         193 :         curTypeStart = curTypeEnd + 1;
    1008         193 :     } while (curTypeStart < flatStr.Length());
    1009         193 : }
    1010             : 
    1011             : void
    1012           0 : net_ParseRequestContentType(const nsACString &aHeaderStr,
    1013             :                             nsACString       &aContentType,
    1014             :                             nsACString       &aContentCharset,
    1015             :                             bool             *aHadCharset)
    1016             : {
    1017             :     //
    1018             :     // Augmented BNF (from RFC 7231 section 3.1.1.1):
    1019             :     //
    1020             :     //   media-type   = type "/" subtype *( OWS ";" OWS parameter )
    1021             :     //   type         = token
    1022             :     //   subtype      = token
    1023             :     //   parameter    = token "=" ( token / quoted-string )
    1024             :     //
    1025             :     // Examples:
    1026             :     //
    1027             :     //   text/html
    1028             :     //   text/html; charset=ISO-8859-1
    1029             :     //   text/html; charset="ISO-8859-1"
    1030             :     //   application/octet-stream
    1031             :     //
    1032             : 
    1033           0 :     aContentType.Truncate();
    1034           0 :     aContentCharset.Truncate();
    1035           0 :     *aHadCharset = false;
    1036           0 :     const nsCString& flatStr = PromiseFlatCString(aHeaderStr);
    1037             : 
    1038             :     // At this point curTypeEnd points to the spot where the media-type
    1039             :     // starting at curTypeEnd ends.  Time to parse that!
    1040           0 :     nsAutoCString contentType, contentCharset;
    1041           0 :     bool hadCharset = false;
    1042             :     int32_t dummy1, dummy2;
    1043           0 :     uint32_t typeEnd = net_FindMediaDelimiter(flatStr, 0, ',');
    1044           0 :     if (typeEnd != flatStr.Length()) {
    1045             :         // We have some stuff left at the end, so this is not a valid
    1046             :         // request Content-Type header.
    1047           0 :         return;
    1048             :     }
    1049           0 :     net_ParseMediaType(flatStr, contentType, contentCharset, 0,
    1050           0 :                        &hadCharset, &dummy1, &dummy2, true);
    1051             : 
    1052           0 :     aContentType = contentType;
    1053           0 :     aContentCharset = contentCharset;
    1054           0 :     *aHadCharset = hadCharset;
    1055             : }
    1056             : 
    1057             : bool
    1058           9 : net_IsValidHostName(const nsACString& host)
    1059             : {
    1060           9 :     const char *end = host.EndReading();
    1061             :     // Use explicit whitelists to select which characters we are
    1062             :     // willing to send to lower-level DNS logic. This is more
    1063             :     // self-documenting, and can also be slightly faster than the
    1064             :     // blacklist approach, since DNS names are the common case, and
    1065             :     // the commonest characters will tend to be near the start of
    1066             :     // the list.
    1067             : 
    1068             :     // Whitelist for DNS names (RFC 1035) with extra characters added
    1069             :     // for pragmatic reasons "$+_"
    1070             :     // see https://bugzilla.mozilla.org/show_bug.cgi?id=355181#c2
    1071           9 :     if (net_FindCharNotInSet(host.BeginReading(), end,
    1072             :                              "abcdefghijklmnopqrstuvwxyz"
    1073             :                              ".-0123456789"
    1074             :                              "ABCDEFGHIJKLMNOPQRSTUVWXYZ$+_") == end)
    1075           9 :         return true;
    1076             : 
    1077             :     // Might be a valid IPv6 link-local address containing a percent sign
    1078           0 :     nsAutoCString strhost(host);
    1079             :     PRNetAddr addr;
    1080           0 :     return PR_StringToNetAddr(strhost.get(), &addr) == PR_SUCCESS;
    1081             : }
    1082             : 
    1083             : bool
    1084           0 : net_IsValidIPv4Addr(const char *addr, int32_t addrLen)
    1085             : {
    1086           0 :     RangedPtr<const char> p(addr, addrLen);
    1087             : 
    1088           0 :     int32_t octet = -1;   // means no digit yet
    1089           0 :     int32_t dotCount = 0; // number of dots in the address
    1090             : 
    1091           0 :     for (; addrLen; ++p, --addrLen) {
    1092           0 :         if (*p == '.') {
    1093           0 :             dotCount++;
    1094           0 :             if (octet == -1) {
    1095             :                 // invalid octet
    1096           0 :                 return false;
    1097             :             }
    1098           0 :             octet = -1;
    1099           0 :         } else if (*p >= '0' && *p <='9') {
    1100           0 :             if (octet == 0) {
    1101             :                 // leading 0 is not allowed
    1102           0 :                 return false;
    1103             :             }
    1104           0 :             if (octet == -1) {
    1105           0 :                 octet = *p - '0';
    1106             :             } else {
    1107           0 :                 octet *= 10;
    1108           0 :                 octet += *p - '0';
    1109           0 :                 if (octet > 255)
    1110           0 :                     return false;
    1111             :             }
    1112             :         } else {
    1113             :             // invalid character
    1114           0 :             return false;
    1115             :         }
    1116             :     }
    1117             : 
    1118           0 :     return (dotCount == 3 && octet != -1);
    1119             : }
    1120             : 
    1121             : bool
    1122           0 : net_IsValidIPv6Addr(const char *addr, int32_t addrLen)
    1123             : {
    1124           0 :     RangedPtr<const char> p(addr, addrLen);
    1125             : 
    1126           0 :     int32_t digits = 0; // number of digits in current block
    1127           0 :     int32_t colons = 0; // number of colons in a row during parsing
    1128           0 :     int32_t blocks = 0; // number of hexadecimal blocks
    1129           0 :     bool haveZeros = false; // true if double colon is present in the address
    1130             : 
    1131           0 :     for (; addrLen; ++p, --addrLen) {
    1132           0 :         if (*p == ':') {
    1133           0 :             if (colons == 0) {
    1134           0 :                 if (digits != 0) {
    1135           0 :                     digits = 0;
    1136           0 :                     blocks++;
    1137             :                 }
    1138           0 :             } else if (colons == 1) {
    1139           0 :                 if (haveZeros)
    1140           0 :                     return false; // only one occurrence is allowed
    1141           0 :                 haveZeros = true;
    1142             :             } else {
    1143             :                 // too many colons in a row
    1144           0 :                 return false;
    1145             :             }
    1146           0 :             colons++;
    1147           0 :         } else if ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') ||
    1148           0 :                    (*p >= 'A' && *p <= 'F')) {
    1149           0 :             if (colons == 1 && blocks == 0) // starts with a single colon
    1150           0 :                 return false;
    1151           0 :             if (digits == 4) // too many digits
    1152           0 :                 return false;
    1153           0 :             colons = 0;
    1154           0 :             digits++;
    1155           0 :         } else if (*p == '.') {
    1156             :             // check valid IPv4 from the beginning of the last block
    1157           0 :             if (!net_IsValidIPv4Addr(p.get() - digits, addrLen + digits))
    1158           0 :                 return false;
    1159           0 :             return (haveZeros && blocks < 6) || (!haveZeros && blocks == 6);
    1160             :         } else {
    1161             :             // invalid character
    1162           0 :             return false;
    1163             :         }
    1164             :     }
    1165             : 
    1166           0 :     if (colons == 1) // ends with a single colon
    1167           0 :         return false;
    1168             : 
    1169           0 :     if (digits) // there is a block at the end
    1170           0 :         blocks++;
    1171             : 
    1172           0 :     return (haveZeros && blocks < 8) || (!haveZeros && blocks == 8);
    1173             : }

Generated by: LCOV version 1.13