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