/** * Validates the extension pack name. * * @returns true if valid, false if not. * @param pszName The name to validate. * @sa VBoxExtPackExtractNameFromTarballPath */ bool VBoxExtPackIsValidName(const char *pszName) { if (!pszName) return false; /* * Check the characters making up the name, only english alphabet * characters, decimal digits and spaces are allowed. */ size_t off = 0; while (pszName[off]) { if (!RT_C_IS_ALNUM(pszName[off]) && pszName[off] != ' ') return false; off++; } /* * Check min and max name limits. */ if ( off > VBOX_EXTPACK_NAME_MAX_LEN || off < VBOX_EXTPACK_NAME_MIN_LEN) return false; return true; }
/** * Checks if an alledged manged extension pack name. * * @returns true if valid, false if not. * @param pszMangledName The mangled name to validate. * @param cchMax The max number of chars to test. * @sa VBoxExtPackMangleName */ bool VBoxExtPackIsValidMangledName(const char *pszMangledName, size_t cchMax /*= RTSTR_MAX*/) { if (!pszMangledName) return false; /* * Check the characters making up the name, only english alphabet * characters, decimal digits and underscores (=space) are allowed. */ size_t off = 0; while (off < cchMax && pszMangledName[off]) { if (!RT_C_IS_ALNUM(pszMangledName[off]) && pszMangledName[off] != '_') return false; off++; } /* * Check min and max name limits. */ if ( off > VBOX_EXTPACK_NAME_MAX_LEN || off < VBOX_EXTPACK_NAME_MIN_LEN) return false; return true; }
/** * Get's the C word starting at the current position minus one. * * @returns Pointer to the word on success and the stream position advanced to * the end of it. * NULL on failure, stream position normally unchanged. * @param pStream The stream to get the C word from. * @param pcchWord Where to return the word length. */ const char *ScmStreamCGetWordM1(PSCMSTREAM pStream, size_t *pcchWord) { /* Check stream state. */ AssertReturn(!pStream->fWriteOrRead, NULL); AssertReturn(RT_SUCCESS(pStream->rc), NULL); AssertReturn(pStream->fFullyLineated, NULL); /* Get the number of chars left on the line and locate the current char. */ size_t const iLine = pStream->iLine; size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1); const char *psz = &pStream->pch[pStream->off - 1]; /* Is it a leading C character. */ if (!RT_C_IS_ALPHA(*psz) && *psz != '_') return NULL; /* Find the end of the word. */ char ch; size_t off = 1; while ( off < cchLeft && ( (ch = psz[off]) == '_' || RT_C_IS_ALNUM(ch))) off++; pStream->off += off - 1; *pcchWord = off; return psz; }
/** * Extract the extension pack name from the tarball path. * * @returns String containing the name on success, the caller must delete it. * NULL if no valid name was found or if we ran out of memory. * @param pszTarball The path to the tarball. */ RTCString *VBoxExtPackExtractNameFromTarballPath(const char *pszTarball) { /* * Skip ahead to the filename part and count the number of characters * that matches the criteria for a mangled extension pack name. */ const char *pszSrc = RTPathFilename(pszTarball); if (!pszSrc) return NULL; size_t off = 0; while (RT_C_IS_ALNUM(pszSrc[off]) || pszSrc[off] == '_') off++; /* * Check min and max name limits. */ if ( off > VBOX_EXTPACK_NAME_MAX_LEN || off < VBOX_EXTPACK_NAME_MIN_LEN) return NULL; /* * Return the unmangled name. */ return VBoxExtPackUnmangleName(pszSrc, off); }
/** * If the given C word is at off - 1, return @c true and skip beyond it, * otherwise return @c false. * * @retval true if the given C-word is at the current position minus one char. * The stream position changes. * @retval false if not. The stream position is unchanged. * * @param pStream The stream. * @param cchWord The length of the word. * @param pszWord The word. */ bool ScmStreamCMatchingWordM1(PSCMSTREAM pStream, const char *pszWord, size_t cchWord) { /* Check stream state. */ AssertReturn(!pStream->fWriteOrRead, false); AssertReturn(RT_SUCCESS(pStream->rc), false); AssertReturn(pStream->fFullyLineated, false); /* Sufficient chars left on the line? */ size_t const iLine = pStream->iLine; AssertReturn(pStream->off > pStream->paLines[iLine].off, false); size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1); if (cchWord > cchLeft) return false; /* Do they match? */ const char *psz = &pStream->pch[pStream->off - 1]; if (memcmp(psz, pszWord, cchWord)) return false; /* Is it the end of a C word? */ if (cchWord < cchLeft) { psz += cchWord; if (RT_C_IS_ALNUM(*psz) || *psz == '_') return false; } /* Skip ahead. */ pStream->off += cchWord - 1; return true; }
void CollectorLinux::addRaidDisks(const char *pcszDevice, DiskList& listDisks) { FILE *f = fopen("/proc/mdstat", "r"); if (f) { char szBuf[128]; while (fgets(szBuf, sizeof(szBuf), f)) { char *pszBufName = szBuf; char *pszBufData = strchr(pszBufName, ' '); if (!pszBufData) { LogRel(("CollectorLinux::addRaidDisks() failed to parse disk stats: %s\n", szBuf)); continue; } *pszBufData++ = '\0'; if (!strcmp(pcszDevice, pszBufName)) { while (*pszBufData == ':') ++pszBufData; /* Skip delimiter */ while (*pszBufData == ' ') ++pszBufData; /* Skip spaces */ while (RT_C_IS_ALNUM(*pszBufData)) ++pszBufData; /* Skip status */ while (*pszBufData == ' ') ++pszBufData; /* Skip spaces */ while (RT_C_IS_ALNUM(*pszBufData)) ++pszBufData; /* Skip type */ while (*pszBufData != '\0') { while (*pszBufData == ' ') ++pszBufData; /* Skip spaces */ char *pszDisk = pszBufData; while (RT_C_IS_ALPHA(*pszBufData)) ++pszBufData; if (*pszBufData) { *pszBufData++ = '\0'; listDisks.push_back(RTCString(pszDisk)); while (*pszBufData != '\0' && *pszBufData != ' ') ++pszBufData; } else listDisks.push_back(RTCString(pszDisk)); } break; } } fclose(f); } }
/** * Validates an extension pack module string. * * @returns true if valid, false if not. * @param pszModule The module string to validate. */ bool VBoxExtPackIsValidModuleString(const char *pszModule) { if (!pszModule || *pszModule == '\0') return false; /* Restricted charset, no extensions (dots). */ while ( RT_C_IS_ALNUM(*pszModule) || *pszModule == '-' || *pszModule == '_') pszModule++; return *pszModule == '\0'; }
int GuestEnvironment::Set(const Utf8Str &strKey, const Utf8Str &strValue) { /** @todo Do some validation using regex. */ if (strKey.isEmpty()) return VERR_INVALID_PARAMETER; int rc = VINF_SUCCESS; const char *pszString = strKey.c_str(); while (*pszString != '\0' && RT_SUCCESS(rc)) { if ( !RT_C_IS_ALNUM(*pszString) && !RT_C_IS_GRAPH(*pszString)) rc = VERR_INVALID_PARAMETER; *pszString++; } if (RT_SUCCESS(rc)) mEnvironment[strKey] = strValue; return rc; }
/** * Unmangle an extension pack name (reverses VBoxExtPackMangleName). * * @returns String containing the mangled name on success, the caller must * delete it. NULL on failure. * @param pszMangledName The mangled name. * @param cchMax The max name length. RTSTR_MAX is fine. * @sa VBoxExtPackMangleName, VBoxExtPackIsValidMangledName */ RTCString *VBoxExtPackUnmangleName(const char *pszMangledName, size_t cchMax) { AssertReturn(VBoxExtPackIsValidMangledName(pszMangledName, cchMax), NULL); char szTmp[VBOX_EXTPACK_NAME_MAX_LEN + 1]; size_t off = 0; char ch; while ( off < cchMax && (ch = pszMangledName[off]) != '\0') { if (ch == '_') ch = ' '; else AssertReturn(RT_C_IS_ALNUM(ch) || ch == ' ', NULL); szTmp[off++] = ch; } szTmp[off] = '\0'; AssertReturn(VBoxExtPackIsValidName(szTmp), NULL); return new RTCString(szTmp, off); }
/** * @callback_method_impl{dtrace_pops_t,dtps_provide} */ static void vboxDtPOps_Provide(void *pvProv, const dtrace_probedesc_t *pDtProbeDesc) { PSUPDRVVDTPROVIDERCORE pProv = (PSUPDRVVDTPROVIDERCORE)pvProv; AssertPtrReturnVoid(pProv); LOG_DTRACE(("%s: %p / %p pDtProbeDesc=%p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, pDtProbeDesc)); if (pDtProbeDesc) return; /* We don't generate probes, so never mind these requests. */ if (pProv->TracerData.DTrace.fZombie) return; dtrace_provider_id_t const idProvider = pProv->TracerData.DTrace.idProvider; AssertPtrReturnVoid(idProvider); AssertPtrReturnVoid(pProv->pHdr); AssertReturnVoid(pProv->pHdr->offProbeLocs != 0); uint32_t const cProbeLocs = pProv->pHdr->cbProbeLocs / sizeof(VTGPROBELOC); /* Need a buffer for extracting the function names and mangling them in case of collision. */ size_t const cbFnNmBuf = _4K + _1K; char *pszFnNmBuf = (char *)RTMemAlloc(cbFnNmBuf); if (!pszFnNmBuf) return; /* * Itereate the probe location list and register all probes related to * this provider. */ uint16_t const idxProv = (uint16_t)((PVTGDESCPROVIDER)((uintptr_t)pProv->pHdr + pProv->pHdr->offProviders) - pProv->pDesc); uint32_t idxProbeLoc; for (idxProbeLoc = 0; idxProbeLoc < cProbeLocs; idxProbeLoc++) { /* Skip probe location belonging to other providers or once that we've already reported. */ PCVTGPROBELOC pProbeLocRO = &pProv->paProbeLocsRO[idxProbeLoc]; PVTGDESCPROBE pProbeDesc = pProbeLocRO->pProbe; if (pProbeDesc->idxProvider != idxProv) continue; uint32_t *pidProbe; if (!pProv->fUmod) pidProbe = (uint32_t *)&pProbeLocRO->idProbe; else pidProbe = &pProv->paR0ProbeLocs[idxProbeLoc].idProbe; if (*pidProbe != 0) continue; /* The function name may need to be stripped since we're using C++ compilers for most of the code. ASSUMES nobody are brave/stupid enough to use function pointer returns without typedef'ing properly them (e.g. signal). */ const char *pszPrbName = vboxDtVtgGetString(pProv->pHdr, pProbeDesc->offName); const char *pszFunc = pProbeLocRO->pszFunction; const char *psz = strchr(pProbeLocRO->pszFunction, '('); size_t cch; if (psz) { /* skip blanks preceeding the parameter parenthesis. */ while ( (uintptr_t)psz > (uintptr_t)pProbeLocRO->pszFunction && RT_C_IS_BLANK(psz[-1])) psz--; /* Find the start of the function name. */ pszFunc = psz - 1; while ((uintptr_t)pszFunc > (uintptr_t)pProbeLocRO->pszFunction) { char ch = pszFunc[-1]; if (!RT_C_IS_ALNUM(ch) && ch != '_' && ch != ':') break; pszFunc--; } cch = psz - pszFunc; } else cch = strlen(pszFunc); RTStrCopyEx(pszFnNmBuf, cbFnNmBuf, pszFunc, cch); /* Look up the probe, if we have one in the same function, mangle the function name a little to avoid having to deal with having multiple location entries with the same probe ID. (lazy bird) */ Assert(!*pidProbe); if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) != DTRACE_IDNONE) { RTStrPrintf(pszFnNmBuf+cch, cbFnNmBuf - cch, "-%u", pProbeLocRO->uLine); if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) != DTRACE_IDNONE) { unsigned iOrd = 2; while (iOrd < 128) { RTStrPrintf(pszFnNmBuf+cch, cbFnNmBuf - cch, "-%u-%u", pProbeLocRO->uLine, iOrd); if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) == DTRACE_IDNONE) break; iOrd++; } if (iOrd >= 128) { LogRel(("VBoxDrv: More than 128 duplicate probe location instances %s at line %u in function %s [%s], probe %s\n", pProbeLocRO->uLine, pProbeLocRO->pszFunction, pszFnNmBuf, pszPrbName)); continue; } } } /* Create the probe. */ AssertCompile(sizeof(*pidProbe) == sizeof(dtrace_id_t)); *pidProbe = dtrace_probe_create(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName, 1 /*aframes*/, (void *)(uintptr_t)idxProbeLoc); pProv->TracerData.DTrace.cProvidedProbes++; } RTMemFree(pszFnNmBuf); LOG_DTRACE(("%s: returns\n", __FUNCTION__)); }
/** * Validates the TAR header. * * @returns VINF_SUCCESS if valid, VERR_TAR_ZERO_HEADER if all zeros, and * the appropriate VERR_TAR_XXX otherwise. * @param pTar The TAR header. * @param penmType Where to return the type of header on success. */ static int rtZipTarHdrValidate(PCRTZIPTARHDR pTar, PRTZIPTARTYPE penmType) { /* * Calc the checksum first since this enables us to detect zero headers. */ int32_t i32ChkSum; int32_t i32ChkSumSignedAlt; if (rtZipTarCalcChkSum(pTar, &i32ChkSum, &i32ChkSumSignedAlt)) return VERR_TAR_ZERO_HEADER; /* * Read the checksum field and match the checksums. */ int64_t i64HdrChkSum; int rc = rtZipTarHdrFieldToNum(pTar->Common.chksum, sizeof(pTar->Common.chksum), true /*fOctalOnly*/, &i64HdrChkSum); if (RT_FAILURE(rc)) return VERR_TAR_BAD_CHKSUM_FIELD; if ( i32ChkSum != i64HdrChkSum && i32ChkSumSignedAlt != i64HdrChkSum) /** @todo test this */ return VERR_TAR_CHKSUM_MISMATCH; /* * Detect the TAR type. */ RTZIPTARTYPE enmType; if ( pTar->Common.magic[0] == 'u' && pTar->Common.magic[1] == 's' && pTar->Common.magic[2] == 't' && pTar->Common.magic[3] == 'a' && pTar->Common.magic[4] == 'r') { /** @todo detect star headers */ if ( pTar->Common.magic[5] == '\0' && pTar->Common.version[0] == '0' && pTar->Common.version[1] == '0') enmType = RTZIPTARTYPE_POSIX; else if ( pTar->Common.magic[5] == ' ' && pTar->Common.version[0] == ' ' && pTar->Common.version[1] == '\0') enmType = RTZIPTARTYPE_GNU; else return VERR_TAR_NOT_USTAR_V00; } else enmType = RTZIPTARTYPE_ANCIENT; *penmType = enmType; /* * Perform some basic checks. */ switch (enmType) { case RTZIPTARTYPE_POSIX: if ( !RT_C_IS_ALNUM(pTar->Common.typeflag) && !pTar->Common.typeflag == '\0') return VERR_TAR_UNKNOWN_TYPE_FLAG; break; case RTZIPTARTYPE_GNU: switch (pTar->Common.typeflag) { case RTZIPTAR_TF_OLDNORMAL: case RTZIPTAR_TF_NORMAL: case RTZIPTAR_TF_CONTIG: case RTZIPTAR_TF_DIR: case RTZIPTAR_TF_CHR: case RTZIPTAR_TF_BLK: case RTZIPTAR_TF_LINK: case RTZIPTAR_TF_SYMLINK: case RTZIPTAR_TF_FIFO: break; case RTZIPTAR_TF_GNU_LONGLINK: case RTZIPTAR_TF_GNU_LONGNAME: break; case RTZIPTAR_TF_GNU_DUMPDIR: case RTZIPTAR_TF_GNU_MULTIVOL: case RTZIPTAR_TF_GNU_SPARSE: case RTZIPTAR_TF_GNU_VOLDHR: /** @todo Implement full GNU TAR support. .*/ return VERR_TAR_UNSUPPORTED_GNU_HDR_TYPE; default: return VERR_TAR_UNKNOWN_TYPE_FLAG; } break; case RTZIPTARTYPE_ANCIENT: switch (pTar->Common.typeflag) { case RTZIPTAR_TF_OLDNORMAL: case RTZIPTAR_TF_NORMAL: case RTZIPTAR_TF_CONTIG: case RTZIPTAR_TF_DIR: case RTZIPTAR_TF_LINK: case RTZIPTAR_TF_SYMLINK: case RTZIPTAR_TF_FIFO: break; default: return VERR_TAR_UNKNOWN_TYPE_FLAG; } break; default: /* shut up gcc */ AssertFailedReturn(VERR_INTERNAL_ERROR_3); } return VINF_SUCCESS; }
/** * Get the next token from the config stream and create a token structure. * * @returns VBox status code. * @param pCfgTokenizer The config tokenizer data. * @param pCfgTokenUse Allocated token structure to use or NULL to allocate * a new one. It will bee freed if an error is encountered. * @param ppCfgToken Where to store the pointer to the next token on success. */ static int autostartConfigTokenizerCreateToken(PCFGTOKENIZER pCfgTokenizer, PCFGTOKEN pCfgTokenUse, PCFGTOKEN *ppCfgToken) { const char *pszToken = NULL; size_t cchToken = 1; size_t cchAdvance = 0; CFGTOKENTYPE enmType = CFGTOKENTYPE_INVALID; int rc = VINF_SUCCESS; for (;;) { pszToken = pCfgTokenizer->pszLineCurr; /* Skip all spaces. */ while (RT_C_IS_BLANK(*pszToken)) { pszToken++; cchAdvance++; } /* Check if we have to read a new line. */ if ( *pszToken == '\0' || *pszToken == '#') { rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer); if (rc == VERR_EOF) { enmType = CFGTOKENTYPE_EOF; rc = VINF_SUCCESS; break; } else if (RT_FAILURE(rc)) break; /* start from the beginning. */ cchAdvance = 0; } else if (*pszToken == '=') { enmType = CFGTOKENTYPE_EQUAL; break; } else if (*pszToken == ',') { enmType = CFGTOKENTYPE_COMMA; break; } else if (*pszToken == '{') { enmType = CFGTOKENTYPE_CURLY_OPEN; break; } else if (*pszToken == '}') { enmType = CFGTOKENTYPE_CURLY_CLOSING; break; } else { const char *pszTmp = pszToken; cchToken = 0; enmType = CFGTOKENTYPE_ID; /* Get the complete token. */ while ( RT_C_IS_ALNUM(*pszTmp) || *pszTmp == '_' || *pszTmp == '.') { pszTmp++; cchToken++; } break; } } Assert(RT_FAILURE(rc) || enmType != CFGTOKENTYPE_INVALID); if (RT_SUCCESS(rc)) { /* Free the given token if it is an ID or the current one is an ID token. */ if ( pCfgTokenUse && ( pCfgTokenUse->enmType == CFGTOKENTYPE_ID || enmType == CFGTOKENTYPE_ID)) { autostartConfigTokenFree(pCfgTokenizer, pCfgTokenUse); pCfgTokenUse = NULL; } if (!pCfgTokenUse) { size_t cbToken = sizeof(CFGTOKEN); if (enmType == CFGTOKENTYPE_ID) cbToken += (cchToken + 1) * sizeof(char); pCfgTokenUse = (PCFGTOKEN)RTMemAllocZ(cbToken); if (!pCfgTokenUse) rc = VERR_NO_MEMORY; } if (RT_SUCCESS(rc)) { /* Copy token data. */ pCfgTokenUse->enmType = enmType; pCfgTokenUse->cchStart = pCfgTokenizer->cchCurr; pCfgTokenUse->iLine = pCfgTokenizer->iLine; if (enmType == CFGTOKENTYPE_ID) { pCfgTokenUse->u.Id.cchToken = cchToken; memcpy(pCfgTokenUse->u.Id.achToken, pszToken, cchToken); } } else if (pCfgTokenUse) autostartConfigTokenFree(pCfgTokenizer, pCfgTokenUse); if (RT_SUCCESS(rc)) { /* Set new position in config stream. */ pCfgTokenizer->pszLineCurr += cchToken + cchAdvance; pCfgTokenizer->cchCurr += cchToken + cchAdvance; *ppCfgToken = pCfgTokenUse; } } return rc; }
static int rtUriParse(const char *pszUri, PRTURIPARSED pParsed) { /* * Validate the input and clear the output. */ AssertPtrReturn(pParsed, VERR_INVALID_POINTER); RT_ZERO(*pParsed); pParsed->uAuthorityPort = UINT32_MAX; AssertPtrReturn(pszUri, VERR_INVALID_POINTER); size_t const cchUri = strlen(pszUri); if (RT_LIKELY(cchUri >= 3)) { /* likely */ } else return cchUri ? VERR_URI_TOO_SHORT : VERR_URI_EMPTY; /* * Validating escaped text sequences is much simpler if we know that * that the base URI string is valid. Also, we don't necessarily trust * the developer calling us to remember to do this. */ int rc = RTStrValidateEncoding(pszUri); AssertRCReturn(rc, rc); /* * RFC-3986, section 3.1: * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) * * The scheme ends with a ':', which we also skip here. */ size_t off = 0; char ch = pszUri[off++]; if (RT_LIKELY(RT_C_IS_ALPHA(ch))) { /* likely */ } else return VERR_URI_INVALID_SCHEME; for (;;) { ch = pszUri[off]; if (ch == ':') break; if (RT_LIKELY(RT_C_IS_ALNUM(ch) || ch == '.' || ch == '-' || ch == '+')) { /* likely */ } else return VERR_URI_INVALID_SCHEME; off++; } pParsed->cchScheme = off; /* Require the scheme length to be at least two chars so we won't confuse it with a path starting with a DOS drive letter specification. */ if (RT_LIKELY(off >= 2)) { /* likely */ } else return VERR_URI_INVALID_SCHEME; off++; /* (skip colon) */ /* * Find the end of the path, we'll need this several times. * Also, while we're potentially scanning the whole thing, check for '%'. */ size_t const offHash = RTStrOffCharOrTerm(&pszUri[off], '#') + off; size_t const offQuestionMark = RTStrOffCharOrTerm(&pszUri[off], '?') + off; if (memchr(pszUri, '%', cchUri) != NULL) pParsed->fFlags |= RTURIPARSED_F_CONTAINS_ESCAPED_CHARS; /* * RFC-3986, section 3.2: * The authority component is preceeded by a double slash ("//")... */ if ( pszUri[off] == '/' && pszUri[off + 1] == '/') { off += 2; pParsed->offAuthority = pParsed->offAuthorityUsername = pParsed->offAuthorityPassword = pParsed->offAuthorityHost = off; pParsed->fFlags |= RTURIPARSED_F_HAVE_AUTHORITY; /* * RFC-3986, section 3.2: * ...and is terminated by the next slash ("/"), question mark ("?"), * or number sign ("#") character, or by the end of the URI. */ const char *pszAuthority = &pszUri[off]; size_t cchAuthority = RTStrOffCharOrTerm(pszAuthority, '/'); cchAuthority = RT_MIN(cchAuthority, offHash - off); cchAuthority = RT_MIN(cchAuthority, offQuestionMark - off); pParsed->cchAuthority = cchAuthority; /* The Authority can be empty, like for: file:///usr/bin/grep */ if (cchAuthority > 0) { pParsed->cchAuthorityHost = cchAuthority; /* * If there is a userinfo part, it is ended by a '@'. */ const char *pszAt = (const char *)memchr(pszAuthority, '@', cchAuthority); if (pszAt) { size_t cchTmp = pszAt - pszAuthority; pParsed->offAuthorityHost += cchTmp + 1; pParsed->cchAuthorityHost -= cchTmp + 1; /* If there is a password part, it's separated from the username with a colon. */ const char *pszColon = (const char *)memchr(pszAuthority, ':', cchTmp); if (pszColon) { pParsed->cchAuthorityUsername = pszColon - pszAuthority; pParsed->offAuthorityPassword = &pszColon[1] - pszUri; pParsed->cchAuthorityPassword = pszAt - &pszColon[1]; } else { pParsed->cchAuthorityUsername = cchTmp; pParsed->offAuthorityPassword = off + cchTmp; } } /* * If there is a port part, its after the last colon in the host part. */ const char *pszColon = (const char *)memrchr(&pszUri[pParsed->offAuthorityHost], ':', pParsed->cchAuthorityHost); if (pszColon) { size_t cchTmp = &pszUri[pParsed->offAuthorityHost + pParsed->cchAuthorityHost] - &pszColon[1]; pParsed->cchAuthorityHost -= cchTmp + 1; pParsed->uAuthorityPort = 0; while (cchTmp-- > 0) { ch = *++pszColon; if ( RT_C_IS_DIGIT(ch) && pParsed->uAuthorityPort < UINT32_MAX / UINT32_C(10)) { pParsed->uAuthorityPort *= 10; pParsed->uAuthorityPort += ch - '0'; } else return VERR_URI_INVALID_PORT_NUMBER; } } } /* Skip past the authority. */ off += cchAuthority; } else pParsed->offAuthority = pParsed->offAuthorityUsername = pParsed->offAuthorityPassword = pParsed->offAuthorityHost = off; /* * RFC-3986, section 3.3: Path * The path is terminated by the first question mark ("?") * or number sign ("#") character, or by the end of the URI. */ pParsed->offPath = off; pParsed->cchPath = RT_MIN(offHash, offQuestionMark) - off; off += pParsed->cchPath; /* * RFC-3986, section 3.4: Query * The query component is indicated by the first question mark ("?") * character and terminated by a number sign ("#") character or by the * end of the URI. */ if ( off == offQuestionMark && off < cchUri) { Assert(pszUri[offQuestionMark] == '?'); pParsed->offQuery = ++off; pParsed->cchQuery = offHash - off; off = offHash; } else { Assert(!pszUri[offQuestionMark]); pParsed->offQuery = off; } /* * RFC-3986, section 3.5: Fragment * A fragment identifier component is indicated by the presence of a * number sign ("#") character and terminated by the end of the URI. */ if ( off == offHash && off < cchUri) { pParsed->offFragment = ++off; pParsed->cchFragment = cchUri - off; } else { Assert(!pszUri[offHash]); pParsed->offFragment = off; } /* * If there are any escape sequences, validate them. * * This is reasonably simple as we already know that the string is valid UTF-8 * before they get decoded. Thus we only have to validate the escaped sequences. */ if (pParsed->fFlags & RTURIPARSED_F_CONTAINS_ESCAPED_CHARS) { const char *pchSrc = (const char *)memchr(pszUri, '%', cchUri); AssertReturn(pchSrc, VERR_INTERNAL_ERROR); do { char szUtf8Seq[8]; unsigned cchUtf8Seq = 0; unsigned cchNeeded = 0; size_t cchLeft = &pszUri[cchUri] - pchSrc; do { if (cchLeft >= 3) { char chHigh = pchSrc[1]; char chLow = pchSrc[2]; if ( RT_C_IS_XDIGIT(chHigh) && RT_C_IS_XDIGIT(chLow)) { uint8_t b = RT_C_IS_DIGIT(chHigh) ? chHigh - '0' : (chHigh & ~0x20) - 'A' + 10; b <<= 4; b |= RT_C_IS_DIGIT(chLow) ? chLow - '0' : (chLow & ~0x20) - 'A' + 10; if (!(b & 0x80)) { /* We don't want the string to be terminated prematurely. */ if (RT_LIKELY(b != 0)) { /* likely */ } else return VERR_URI_ESCAPED_ZERO; /* Check that we're not expecting more UTF-8 bytes. */ if (RT_LIKELY(cchNeeded == 0)) { /* likely */ } else return VERR_URI_MISSING_UTF8_CONTINUATION_BYTE; } /* Are we waiting UTF-8 bytes? */ else if (cchNeeded > 0) { if (RT_LIKELY(!(b & 0x40))) { /* likely */ } else return VERR_URI_INVALID_ESCAPED_UTF8_CONTINUATION_BYTE; szUtf8Seq[cchUtf8Seq++] = (char)b; if (--cchNeeded == 0) { szUtf8Seq[cchUtf8Seq] = '\0'; rc = RTStrValidateEncoding(szUtf8Seq); if (RT_FAILURE(rc)) return VERR_URI_ESCAPED_CHARS_NOT_VALID_UTF8; cchUtf8Seq = 0; } } /* Start a new UTF-8 sequence. */ else { if ((b & 0xf8) == 0xf0) cchNeeded = 3; else if ((b & 0xf0) == 0xe0) cchNeeded = 2; else if ((b & 0xe0) == 0xc0) cchNeeded = 1; else return VERR_URI_INVALID_ESCAPED_UTF8_LEAD_BYTE; szUtf8Seq[0] = (char)b; cchUtf8Seq = 1; } pchSrc += 3; cchLeft -= 3; } else return VERR_URI_INVALID_ESCAPE_SEQ; } else return VERR_URI_INVALID_ESCAPE_SEQ; } while (cchLeft > 0 && pchSrc[0] == '%'); /* Check that we're not expecting more UTF-8 bytes. */ if (RT_LIKELY(cchNeeded == 0)) { /* likely */ } else return VERR_URI_MISSING_UTF8_CONTINUATION_BYTE; /* next */ pchSrc = (const char *)memchr(pchSrc, '%', cchLeft); } while (pchSrc); } pParsed->u32Magic = RTURIPARSED_MAGIC; return VINF_SUCCESS; }