Line data Source code
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
19 0 : IsTokenSymbol(signed char chr) {
20 0 : if (chr < 33 || chr == 127 ||
21 0 : chr == '(' || chr == ')' || chr == '<' || chr == '>' ||
22 0 : chr == '@' || chr == ',' || chr == ';' || chr == ':' ||
23 0 : chr == '"' || chr == '/' || chr == '[' || chr == ']' ||
24 0 : chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') {
25 0 : return false;
26 : }
27 0 : return true;
28 : }
29 :
30 : // A quoted-string consists of a quote (") followed by any amount of
31 : // qdtext or quoted-pair, followed by a quote.
32 : // qdtext is any TEXT except a quote.
33 : // TEXT is any 8-bit octet except CTLs, but including LWS.
34 : // quoted-pair is a backslash (\) followed by a CHAR.
35 : // So, it turns out, \ can't really be a qdtext symbol for our purposes.
36 : // This returns true if chr is any octet 9,10,13,32-126 except <"> or "\"
37 : bool
38 0 : IsQuotedTextSymbol(signed char chr) {
39 0 : return ((chr >= 32 && chr != '"' && chr != '\\' && chr != 127) ||
40 0 : chr == 0x9 || chr == 0xa || chr == 0xd);
41 : }
42 :
43 : // The octet following the "\" in a quoted pair can be anything 0-127.
44 : bool
45 0 : IsQuotedPairSymbol(signed char chr) {
46 0 : return (chr >= 0);
47 : }
48 :
49 : static mozilla::LazyLogModule sSHParserLog("nsSecurityHeaderParser");
50 :
51 : #define SHPARSERLOG(args) MOZ_LOG(sSHParserLog, mozilla::LogLevel::Debug, args)
52 :
53 0 : nsSecurityHeaderParser::nsSecurityHeaderParser(const nsCString& aHeader)
54 0 : : mCursor(aHeader.get())
55 0 : , mError(false)
56 : {
57 0 : }
58 :
59 0 : nsSecurityHeaderParser::~nsSecurityHeaderParser() {
60 : nsSecurityHeaderDirective *directive;
61 0 : while ((directive = mDirectives.popFirst())) {
62 0 : delete directive;
63 : }
64 0 : }
65 :
66 : mozilla::LinkedList<nsSecurityHeaderDirective> *
67 0 : nsSecurityHeaderParser::GetDirectives() {
68 0 : return &mDirectives;
69 : }
70 :
71 : nsresult
72 0 : nsSecurityHeaderParser::Parse() {
73 0 : MOZ_ASSERT(mDirectives.isEmpty());
74 0 : SHPARSERLOG(("trying to parse '%s'", mCursor));
75 :
76 0 : Header();
77 :
78 : // if we didn't consume the entire input, we were unable to parse it => error
79 0 : if (mError || *mCursor) {
80 0 : return NS_ERROR_FAILURE;
81 : } else {
82 0 : return NS_OK;
83 : }
84 : }
85 :
86 : bool
87 0 : nsSecurityHeaderParser::Accept(char aChr)
88 : {
89 0 : if (*mCursor == aChr) {
90 0 : Advance();
91 0 : return true;
92 : }
93 :
94 0 : return false;
95 : }
96 :
97 : bool
98 0 : nsSecurityHeaderParser::Accept(bool (*aClassifier) (signed char))
99 : {
100 0 : if (aClassifier(*mCursor)) {
101 0 : Advance();
102 0 : return true;
103 : }
104 :
105 0 : return false;
106 : }
107 :
108 : void
109 0 : nsSecurityHeaderParser::Expect(char aChr)
110 : {
111 0 : if (*mCursor != aChr) {
112 0 : mError = true;
113 : } else {
114 0 : Advance();
115 : }
116 0 : }
117 :
118 : void
119 0 : nsSecurityHeaderParser::Advance()
120 : {
121 : // Technically, 0 is valid in quoted-pair, but we were handed a
122 : // null-terminated const char *, so this doesn't handle that.
123 0 : if (*mCursor) {
124 0 : mOutput.Append(*mCursor);
125 0 : mCursor++;
126 : } else {
127 0 : mError = true;
128 : }
129 0 : }
130 :
131 : void
132 0 : nsSecurityHeaderParser::Header()
133 : {
134 0 : Directive();
135 0 : while (Accept(';')) {
136 0 : Directive();
137 : }
138 0 : }
139 :
140 : void
141 0 : nsSecurityHeaderParser::Directive()
142 : {
143 0 : mDirective = new nsSecurityHeaderDirective();
144 0 : LWSMultiple();
145 0 : DirectiveName();
146 0 : LWSMultiple();
147 0 : if (Accept('=')) {
148 0 : LWSMultiple();
149 0 : DirectiveValue();
150 0 : LWSMultiple();
151 : }
152 0 : mDirectives.insertBack(mDirective);
153 0 : SHPARSERLOG(("read directive name '%s', value '%s'",
154 : mDirective->mName.Data(), mDirective->mValue.Data()));
155 0 : }
156 :
157 : void
158 0 : nsSecurityHeaderParser::DirectiveName()
159 : {
160 0 : mOutput.Truncate(0);
161 0 : Token();
162 0 : mDirective->mName.Assign(mOutput);
163 0 : }
164 :
165 : void
166 0 : nsSecurityHeaderParser::DirectiveValue()
167 : {
168 0 : mOutput.Truncate(0);
169 0 : if (Accept(IsTokenSymbol)) {
170 0 : Token();
171 0 : mDirective->mValue.Assign(mOutput);
172 0 : } else if (Accept('"')) {
173 : // Accept advances the cursor if successful, which appends a character to
174 : // mOutput. The " is not part of what we want to capture, so truncate
175 : // mOutput again.
176 0 : mOutput.Truncate(0);
177 0 : QuotedString();
178 0 : mDirective->mValue.Assign(mOutput);
179 0 : Expect('"');
180 : }
181 0 : }
182 :
183 : void
184 0 : nsSecurityHeaderParser::Token()
185 : {
186 0 : while (Accept(IsTokenSymbol));
187 0 : }
188 :
189 : void
190 0 : nsSecurityHeaderParser::QuotedString()
191 : {
192 : while (true) {
193 0 : if (Accept(IsQuotedTextSymbol)) {
194 0 : QuotedText();
195 0 : } else if (Accept('\\')) {
196 0 : QuotedPair();
197 : } else {
198 0 : break;
199 : }
200 : }
201 0 : }
202 :
203 : void
204 0 : nsSecurityHeaderParser::QuotedText()
205 : {
206 0 : while (Accept(IsQuotedTextSymbol));
207 0 : }
208 :
209 : void
210 0 : nsSecurityHeaderParser::QuotedPair()
211 : {
212 0 : Accept(IsQuotedPairSymbol);
213 0 : }
214 :
215 : void
216 0 : nsSecurityHeaderParser::LWSMultiple()
217 : {
218 : while (true) {
219 0 : if (Accept('\r')) {
220 0 : LWSCRLF();
221 0 : } else if (Accept(' ') || Accept('\t')) {
222 0 : LWS();
223 : } else {
224 0 : break;
225 : }
226 : }
227 0 : }
228 :
229 : void
230 0 : nsSecurityHeaderParser::LWSCRLF() {
231 0 : Expect('\n');
232 0 : if (!(Accept(' ') || Accept('\t'))) {
233 0 : mError = true;
234 : }
235 0 : LWS();
236 0 : }
237 :
238 : void
239 0 : nsSecurityHeaderParser::LWS()
240 : {
241 : // Note that becaue of how we're called, we don't have to check for
242 : // the mandatory presense of at least one of SP or HT.
243 0 : while (Accept(' ') || Accept('\t'));
244 0 : }
|