summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/nsSecurityHeaderParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/nsSecurityHeaderParser.cpp')
-rw-r--r--security/manager/ssl/nsSecurityHeaderParser.cpp244
1 files changed, 244 insertions, 0 deletions
diff --git a/security/manager/ssl/nsSecurityHeaderParser.cpp b/security/manager/ssl/nsSecurityHeaderParser.cpp
new file mode 100644
index 000000000..9fb9f0d6d
--- /dev/null
+++ b/security/manager/ssl/nsSecurityHeaderParser.cpp
@@ -0,0 +1,244 @@
+/* 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'));
+}