nsSecurityHeaderParser.cpp (5791B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "nsSecurityHeaderParser.h" 6 #include "mozilla/Logging.h" 7 8 // The character classes in this file are informed by [RFC2616], Section 2.2. 9 // signed char is a signed data type one byte (8 bits) wide, so its value can 10 // never be greater than 127. The following implicitly makes use of this. 11 12 // A token is one or more CHAR except CTLs or separators. 13 // A CHAR is any US-ASCII character (octets 0 - 127). 14 // A CTL is any US-ASCII control character (octets 0 - 31) and DEL (127). 15 // A separator is one of ()<>@,;:\"/[]?={} as well as space and 16 // horizontal-tab (32 and 9, respectively). 17 // So, this returns true if chr is any octet 33-126 except ()<>@,;:\"/[]?={} 18 bool IsTokenSymbol(signed char chr) { 19 if (chr < 33 || chr == 127 || chr == '(' || chr == ')' || chr == '<' || 20 chr == '>' || chr == '@' || chr == ',' || chr == ';' || chr == ':' || 21 chr == '"' || chr == '/' || chr == '[' || chr == ']' || chr == '?' || 22 chr == '=' || chr == '{' || chr == '}' || chr == '\\') { 23 return false; 24 } 25 return true; 26 } 27 28 // A quoted-string consists of a quote (") followed by any amount of 29 // qdtext or quoted-pair, followed by a quote. 30 // qdtext is any TEXT except a quote. 31 // TEXT is any 8-bit octet except CTLs, but including LWS. 32 // quoted-pair is a backslash (\) followed by a CHAR. 33 // So, it turns out, \ can't really be a qdtext symbol for our purposes. 34 // This returns true if chr is any octet 9,10,13,32-126 except <"> or "\" 35 bool IsQuotedTextSymbol(signed char chr) { 36 return ((chr >= 32 && chr != '"' && chr != '\\' && chr != 127) || 37 chr == 0x9 || chr == 0xa || chr == 0xd); 38 } 39 40 // The octet following the "\" in a quoted pair can be anything 0-127. 41 bool IsQuotedPairSymbol(signed char chr) { return (chr >= 0); } 42 43 static mozilla::LazyLogModule sSHParserLog("nsSecurityHeaderParser"); 44 45 #define SHPARSERLOG(args) MOZ_LOG(sSHParserLog, mozilla::LogLevel::Debug, args) 46 47 nsSecurityHeaderParser::nsSecurityHeaderParser(const nsCString& aHeader) 48 : mCursor(aHeader.get()), mDirective(nullptr), mError(false) {} 49 50 nsSecurityHeaderParser::~nsSecurityHeaderParser() { 51 nsSecurityHeaderDirective* directive; 52 while ((directive = mDirectives.popFirst())) { 53 delete directive; 54 } 55 } 56 57 mozilla::LinkedList<nsSecurityHeaderDirective>* 58 nsSecurityHeaderParser::GetDirectives() { 59 return &mDirectives; 60 } 61 62 nsresult nsSecurityHeaderParser::Parse() { 63 MOZ_ASSERT(mDirectives.isEmpty()); 64 SHPARSERLOG(("trying to parse '%s'", mCursor)); 65 66 Header(); 67 68 // if we didn't consume the entire input, we were unable to parse it => error 69 if (mError || *mCursor) { 70 return NS_ERROR_FAILURE; 71 } else { 72 return NS_OK; 73 } 74 } 75 76 bool nsSecurityHeaderParser::Accept(char aChr) { 77 if (*mCursor == aChr) { 78 Advance(); 79 return true; 80 } 81 82 return false; 83 } 84 85 bool nsSecurityHeaderParser::Accept(bool (*aClassifier)(signed char)) { 86 if (aClassifier(*mCursor)) { 87 Advance(); 88 return true; 89 } 90 91 return false; 92 } 93 94 void nsSecurityHeaderParser::Expect(char aChr) { 95 if (*mCursor != aChr) { 96 mError = true; 97 } else { 98 Advance(); 99 } 100 } 101 102 void nsSecurityHeaderParser::Advance() { 103 // Technically, 0 is valid in quoted-pair, but we were handed a 104 // null-terminated const char *, so this doesn't handle that. 105 if (*mCursor) { 106 mOutput.Append(*mCursor); 107 mCursor++; 108 } else { 109 mError = true; 110 } 111 } 112 113 void nsSecurityHeaderParser::Header() { 114 Directive(); 115 while (Accept(';')) { 116 Directive(); 117 } 118 } 119 120 void nsSecurityHeaderParser::Directive() { 121 mDirective = new nsSecurityHeaderDirective(); 122 LWSMultiple(); 123 DirectiveName(); 124 LWSMultiple(); 125 if (Accept('=')) { 126 LWSMultiple(); 127 DirectiveValue(); 128 LWSMultiple(); 129 } 130 mDirectives.insertBack(mDirective); 131 if (mDirective->mValue.isSome()) { 132 SHPARSERLOG(("read directive name '%s', value '%s'", 133 mDirective->mName.Data(), mDirective->mValue->Data())); 134 } else { 135 SHPARSERLOG( 136 ("read valueless directive name '%s'", mDirective->mName.Data())); 137 } 138 } 139 140 void nsSecurityHeaderParser::DirectiveName() { 141 mOutput.Truncate(0); 142 Token(); 143 mDirective->mName.Assign(mOutput); 144 } 145 146 void nsSecurityHeaderParser::DirectiveValue() { 147 mOutput.Truncate(0); 148 mDirective->mValue.emplace(); 149 if (Accept(IsTokenSymbol)) { 150 Token(); 151 mDirective->mValue->Assign(mOutput); 152 } else if (Accept('"')) { 153 // Accept advances the cursor if successful, which appends a character to 154 // mOutput. The " is not part of what we want to capture, so truncate 155 // mOutput again. 156 mOutput.Truncate(0); 157 QuotedString(); 158 mDirective->mValue->Assign(mOutput); 159 Expect('"'); 160 } 161 } 162 163 void nsSecurityHeaderParser::Token() { while (Accept(IsTokenSymbol)); } 164 165 void nsSecurityHeaderParser::QuotedString() { 166 while (true) { 167 if (Accept(IsQuotedTextSymbol)) { 168 QuotedText(); 169 } else if (Accept('\\')) { 170 QuotedPair(); 171 } else { 172 break; 173 } 174 } 175 } 176 177 void nsSecurityHeaderParser::QuotedText() { 178 while (Accept(IsQuotedTextSymbol)); 179 } 180 181 void nsSecurityHeaderParser::QuotedPair() { Accept(IsQuotedPairSymbol); } 182 183 void nsSecurityHeaderParser::LWSMultiple() { 184 while (true) { 185 if (Accept('\r')) { 186 LWSCRLF(); 187 } else if (Accept(' ') || Accept('\t')) { 188 LWS(); 189 } else { 190 break; 191 } 192 } 193 } 194 195 void nsSecurityHeaderParser::LWSCRLF() { 196 Expect('\n'); 197 if (!(Accept(' ') || Accept('\t'))) { 198 mError = true; 199 } 200 LWS(); 201 } 202 203 void nsSecurityHeaderParser::LWS() { 204 // Note that becaue of how we're called, we don't have to check for 205 // the mandatory presense of at least one of SP or HT. 206 while (Accept(' ') || Accept('\t')); 207 }