mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 17:16:12 +00:00
245 lines
5.5 KiB
C++
245 lines
5.5 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsSecurityHeaderParser.h"
|
|
#include "mozilla/Logging.h"
|
|
|
|
// The character classes in this file are informed by [RFC2616], Section 2.2.
|
|
// signed char is a signed data type one byte (8 bits) wide, so its value can
|
|
// never be greater than 127. The following implicitly makes use of this.
|
|
|
|
// A token is one or more CHAR except CTLs or separators.
|
|
// A CHAR is any US-ASCII character (octets 0 - 127).
|
|
// A CTL is any US-ASCII control character (octets 0 - 31) and DEL (127).
|
|
// A separator is one of ()<>@,;:\"/[]?={} as well as space and
|
|
// horizontal-tab (32 and 9, respectively).
|
|
// So, this returns true if chr is any octet 33-126 except ()<>@,;:\"/[]?={}
|
|
bool
|
|
IsTokenSymbol(signed char chr) {
|
|
if (chr < 33 || chr == 127 ||
|
|
chr == '(' || chr == ')' || chr == '<' || chr == '>' ||
|
|
chr == '@' || chr == ',' || chr == ';' || chr == ':' ||
|
|
chr == '"' || chr == '/' || chr == '[' || chr == ']' ||
|
|
chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// A quoted-string consists of a quote (") followed by any amount of
|
|
// qdtext or quoted-pair, followed by a quote.
|
|
// qdtext is any TEXT except a quote.
|
|
// TEXT is any 8-bit octet except CTLs, but including LWS.
|
|
// quoted-pair is a backslash (\) followed by a CHAR.
|
|
// So, it turns out, \ can't really be a qdtext symbol for our purposes.
|
|
// This returns true if chr is any octet 9,10,13,32-126 except <"> or "\"
|
|
bool
|
|
IsQuotedTextSymbol(signed char chr) {
|
|
return ((chr >= 32 && chr != '"' && chr != '\\' && chr != 127) ||
|
|
chr == 0x9 || chr == 0xa || chr == 0xd);
|
|
}
|
|
|
|
// The octet following the "\" in a quoted pair can be anything 0-127.
|
|
bool
|
|
IsQuotedPairSymbol(signed char chr) {
|
|
return (chr >= 0);
|
|
}
|
|
|
|
static mozilla::LazyLogModule sSHParserLog("nsSecurityHeaderParser");
|
|
|
|
#define SHPARSERLOG(args) MOZ_LOG(sSHParserLog, mozilla::LogLevel::Debug, args)
|
|
|
|
nsSecurityHeaderParser::nsSecurityHeaderParser(const char *aHeader)
|
|
: mCursor(aHeader)
|
|
, mError(false)
|
|
{
|
|
}
|
|
|
|
nsSecurityHeaderParser::~nsSecurityHeaderParser() {
|
|
nsSecurityHeaderDirective *directive;
|
|
while ((directive = mDirectives.popFirst())) {
|
|
delete directive;
|
|
}
|
|
}
|
|
|
|
mozilla::LinkedList<nsSecurityHeaderDirective> *
|
|
nsSecurityHeaderParser::GetDirectives() {
|
|
return &mDirectives;
|
|
}
|
|
|
|
nsresult
|
|
nsSecurityHeaderParser::Parse() {
|
|
MOZ_ASSERT(mDirectives.isEmpty());
|
|
SHPARSERLOG(("trying to parse '%s'", mCursor));
|
|
|
|
Header();
|
|
|
|
// if we didn't consume the entire input, we were unable to parse it => error
|
|
if (mError || *mCursor) {
|
|
return NS_ERROR_FAILURE;
|
|
} else {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsSecurityHeaderParser::Accept(char aChr)
|
|
{
|
|
if (*mCursor == aChr) {
|
|
Advance();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
nsSecurityHeaderParser::Accept(bool (*aClassifier) (signed char))
|
|
{
|
|
if (aClassifier(*mCursor)) {
|
|
Advance();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::Expect(char aChr)
|
|
{
|
|
if (*mCursor != aChr) {
|
|
mError = true;
|
|
} else {
|
|
Advance();
|
|
}
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::Advance()
|
|
{
|
|
// Technically, 0 is valid in quoted-pair, but we were handed a
|
|
// null-terminated const char *, so this doesn't handle that.
|
|
if (*mCursor) {
|
|
mOutput.Append(*mCursor);
|
|
mCursor++;
|
|
} else {
|
|
mError = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::Header()
|
|
{
|
|
Directive();
|
|
while (Accept(';')) {
|
|
Directive();
|
|
}
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::Directive()
|
|
{
|
|
mDirective = new nsSecurityHeaderDirective();
|
|
LWSMultiple();
|
|
DirectiveName();
|
|
LWSMultiple();
|
|
if (Accept('=')) {
|
|
LWSMultiple();
|
|
DirectiveValue();
|
|
LWSMultiple();
|
|
}
|
|
mDirectives.insertBack(mDirective);
|
|
SHPARSERLOG(("read directive name '%s', value '%s'",
|
|
mDirective->mName.Data(), mDirective->mValue.Data()));
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::DirectiveName()
|
|
{
|
|
mOutput.Truncate(0);
|
|
Token();
|
|
mDirective->mName.Assign(mOutput);
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::DirectiveValue()
|
|
{
|
|
mOutput.Truncate(0);
|
|
if (Accept(IsTokenSymbol)) {
|
|
Token();
|
|
mDirective->mValue.Assign(mOutput);
|
|
} else if (Accept('"')) {
|
|
// Accept advances the cursor if successful, which appends a character to
|
|
// mOutput. The " is not part of what we want to capture, so truncate
|
|
// mOutput again.
|
|
mOutput.Truncate(0);
|
|
QuotedString();
|
|
mDirective->mValue.Assign(mOutput);
|
|
Expect('"');
|
|
}
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::Token()
|
|
{
|
|
while (Accept(IsTokenSymbol));
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::QuotedString()
|
|
{
|
|
while (true) {
|
|
if (Accept(IsQuotedTextSymbol)) {
|
|
QuotedText();
|
|
} else if (Accept('\\')) {
|
|
QuotedPair();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::QuotedText()
|
|
{
|
|
while (Accept(IsQuotedTextSymbol));
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::QuotedPair()
|
|
{
|
|
Accept(IsQuotedPairSymbol);
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::LWSMultiple()
|
|
{
|
|
while (true) {
|
|
if (Accept('\r')) {
|
|
LWSCRLF();
|
|
} else if (Accept(' ') || Accept('\t')) {
|
|
LWS();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::LWSCRLF() {
|
|
Expect('\n');
|
|
if (!(Accept(' ') || Accept('\t'))) {
|
|
mError = true;
|
|
}
|
|
LWS();
|
|
}
|
|
|
|
void
|
|
nsSecurityHeaderParser::LWS()
|
|
{
|
|
// Note that becaue of how we're called, we don't have to check for
|
|
// the mandatory presense of at least one of SP or HT.
|
|
while (Accept(' ') || Accept('\t'));
|
|
}
|