/src/suricata8/rust/src/http2/parser.rs
Line | Count | Source |
1 | | /* Copyright (C) 2020 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | use super::huffman; |
19 | | use crate::common::nom7::bits; |
20 | | use crate::detect::uint::{detect_parse_uint, DetectUintData}; |
21 | | use crate::http2::http2::{HTTP2DynTable, HTTP2_MAX_TABLESIZE}; |
22 | | use nom7::bits::streaming::take as take_bits; |
23 | | use nom7::branch::alt; |
24 | | use nom7::bytes::complete::tag; |
25 | | use nom7::bytes::streaming::{is_a, is_not, take, take_while}; |
26 | | use nom7::combinator::{complete, cond, map_opt, opt, rest, verify}; |
27 | | use nom7::error::{make_error, ErrorKind}; |
28 | | use nom7::multi::many0; |
29 | | use nom7::number::streaming::{be_u16, be_u24, be_u32, be_u8}; |
30 | | use nom7::sequence::tuple; |
31 | | use nom7::{Err, IResult}; |
32 | | use std::fmt; |
33 | | use std::str::FromStr; |
34 | | use std::rc::Rc; |
35 | | use base64::{Engine, engine::general_purpose::STANDARD_NO_PAD}; |
36 | | |
37 | | #[repr(u8)] |
38 | | #[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)] |
39 | | pub enum HTTP2FrameType { |
40 | | Data = 0, |
41 | | Headers = 1, |
42 | | Priority = 2, |
43 | | RstStream = 3, |
44 | | Settings = 4, |
45 | | PushPromise = 5, |
46 | | Ping = 6, |
47 | | GoAway = 7, |
48 | | WindowUpdate = 8, |
49 | | Continuation = 9, |
50 | | } |
51 | | |
52 | | impl fmt::Display for HTTP2FrameType { |
53 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
54 | 0 | write!(f, "{:?}", self) |
55 | 0 | } |
56 | | } |
57 | | |
58 | | impl std::str::FromStr for HTTP2FrameType { |
59 | | type Err = String; |
60 | | |
61 | 133 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
62 | 133 | let su = s.to_uppercase(); |
63 | 133 | let su_slice: &str = &su; |
64 | 133 | match su_slice { |
65 | 133 | "DATA" => Ok(HTTP2FrameType::Data), |
66 | 133 | "HEADERS" => Ok(HTTP2FrameType::Headers), |
67 | 133 | "PRIORITY" => Ok(HTTP2FrameType::Priority), |
68 | 133 | "RSTSTREAM" => Ok(HTTP2FrameType::RstStream), |
69 | 133 | "SETTINGS" => Ok(HTTP2FrameType::Settings), |
70 | 133 | "PUSHPROMISE" => Ok(HTTP2FrameType::PushPromise), |
71 | 133 | "PING" => Ok(HTTP2FrameType::Ping), |
72 | 133 | "GOAWAY" => Ok(HTTP2FrameType::GoAway), |
73 | 50 | "WINDOWUPDATE" => Ok(HTTP2FrameType::WindowUpdate), |
74 | 50 | "CONTINUATION" => Ok(HTTP2FrameType::Continuation), |
75 | 50 | _ => Err(format!("'{}' is not a valid value for HTTP2FrameType", s)), |
76 | | } |
77 | 133 | } |
78 | | } |
79 | | |
80 | | #[derive(PartialEq, Eq, Debug)] |
81 | | pub struct HTTP2FrameHeader { |
82 | | //we could add detection on (GOAWAY) additional data |
83 | | pub length: u32, |
84 | | pub ftype: u8, |
85 | | pub flags: u8, |
86 | | pub reserved: u8, |
87 | | pub stream_id: u32, |
88 | | } |
89 | | |
90 | 7.44M | pub fn http2_parse_frame_header(i: &[u8]) -> IResult<&[u8], HTTP2FrameHeader> { |
91 | 7.44M | let (i, length) = be_u24(i)?; |
92 | 6.88M | let (i, ftype) = be_u8(i)?; |
93 | 6.85M | let (i, flags) = be_u8(i)?; |
94 | 6.79M | let (i, b) = be_u32(i)?; |
95 | 6.46M | let (reserved, stream_id) = ((b >> 31) as u8, b & 0x7fff_ffff); |
96 | 6.46M | Ok(( |
97 | 6.46M | i, |
98 | 6.46M | HTTP2FrameHeader { |
99 | 6.46M | length, |
100 | 6.46M | ftype, |
101 | 6.46M | flags, |
102 | 6.46M | reserved, |
103 | 6.46M | stream_id, |
104 | 6.46M | }, |
105 | 6.46M | )) |
106 | 7.44M | } |
107 | | |
108 | | #[repr(u32)] |
109 | | #[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)] |
110 | | pub enum HTTP2ErrorCode { |
111 | | NoError = 0, |
112 | | ProtocolError = 1, |
113 | | InternalError = 2, |
114 | | FlowControlError = 3, |
115 | | SettingsTimeout = 4, |
116 | | StreamClosed = 5, |
117 | | FrameSizeError = 6, |
118 | | RefusedStream = 7, |
119 | | Cancel = 8, |
120 | | CompressionError = 9, |
121 | | ConnectError = 10, |
122 | | EnhanceYourCalm = 11, |
123 | | InadequateSecurity = 12, |
124 | | Http11Required = 13, |
125 | | } |
126 | | |
127 | | impl fmt::Display for HTTP2ErrorCode { |
128 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
129 | 0 | write!(f, "{:?}", self) |
130 | 0 | } |
131 | | } |
132 | | |
133 | | impl std::str::FromStr for HTTP2ErrorCode { |
134 | | type Err = String; |
135 | | |
136 | 10 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
137 | 10 | let su = s.to_uppercase(); |
138 | 10 | let su_slice: &str = &su; |
139 | 10 | match su_slice { |
140 | 10 | "NO_ERROR" => Ok(HTTP2ErrorCode::NoError), |
141 | 10 | "PROTOCOL_ERROR" => Ok(HTTP2ErrorCode::ProtocolError), |
142 | 10 | "INTERNAL_ERROR" => Ok(HTTP2ErrorCode::InternalError), |
143 | 10 | "FLOW_CONTROL_ERROR" => Ok(HTTP2ErrorCode::FlowControlError), |
144 | 10 | "SETTINGS_TIMEOUT" => Ok(HTTP2ErrorCode::SettingsTimeout), |
145 | 10 | "STREAM_CLOSED" => Ok(HTTP2ErrorCode::StreamClosed), |
146 | 10 | "FRAME_SIZE_ERROR" => Ok(HTTP2ErrorCode::FrameSizeError), |
147 | 10 | "REFUSED_STREAM" => Ok(HTTP2ErrorCode::RefusedStream), |
148 | 10 | "CANCEL" => Ok(HTTP2ErrorCode::Cancel), |
149 | 10 | "COMPRESSION_ERROR" => Ok(HTTP2ErrorCode::CompressionError), |
150 | 10 | "CONNECT_ERROR" => Ok(HTTP2ErrorCode::ConnectError), |
151 | 10 | "ENHANCE_YOUR_CALM" => Ok(HTTP2ErrorCode::EnhanceYourCalm), |
152 | 10 | "INADEQUATE_SECURITY" => Ok(HTTP2ErrorCode::InadequateSecurity), |
153 | 10 | "HTTP_1_1_REQUIRED" => Ok(HTTP2ErrorCode::Http11Required), |
154 | 10 | _ => Err(format!("'{}' is not a valid value for HTTP2ErrorCode", s)), |
155 | | } |
156 | 10 | } |
157 | | } |
158 | | |
159 | | #[derive(Clone, Copy, Debug)] |
160 | | pub struct HTTP2FrameGoAway { |
161 | | pub errorcode: u32, //HTTP2ErrorCode |
162 | | } |
163 | | |
164 | 2.06k | pub fn http2_parse_frame_goaway(i: &[u8]) -> IResult<&[u8], HTTP2FrameGoAway> { |
165 | 2.06k | let (i, _last_stream_id) = be_u32(i)?; |
166 | 2.06k | let (i, errorcode) = be_u32(i)?; |
167 | 2.06k | Ok((i, HTTP2FrameGoAway { errorcode })) |
168 | 2.06k | } |
169 | | |
170 | | #[derive(Clone, Copy, Debug)] |
171 | | pub struct HTTP2FrameRstStream { |
172 | | pub errorcode: u32, ////HTTP2ErrorCode |
173 | | } |
174 | | |
175 | 5.36k | pub fn http2_parse_frame_rststream(i: &[u8]) -> IResult<&[u8], HTTP2FrameRstStream> { |
176 | 5.36k | let (i, errorcode) = be_u32(i)?; |
177 | 5.36k | Ok((i, HTTP2FrameRstStream { errorcode })) |
178 | 5.36k | } |
179 | | |
180 | | #[derive(Clone, Copy, Debug)] |
181 | | pub struct HTTP2FramePriority { |
182 | | pub exclusive: u8, |
183 | | pub dependency: u32, |
184 | | pub weight: u8, |
185 | | } |
186 | | |
187 | 47.8k | pub fn http2_parse_frame_priority(i: &[u8]) -> IResult<&[u8], HTTP2FramePriority> { |
188 | 47.8k | let (i, b) = be_u32(i)?; |
189 | 47.8k | let (exclusive, dependency) = ((b >> 31) as u8, b & 0x7fff_ffff); |
190 | 47.8k | let (i, weight) = be_u8(i)?; |
191 | 47.8k | Ok(( |
192 | 47.8k | i, |
193 | 47.8k | HTTP2FramePriority { |
194 | 47.8k | exclusive, |
195 | 47.8k | dependency, |
196 | 47.8k | weight, |
197 | 47.8k | }, |
198 | 47.8k | )) |
199 | 47.8k | } |
200 | | |
201 | | #[derive(Clone, Copy, Debug)] |
202 | | pub struct HTTP2FrameWindowUpdate { |
203 | | pub reserved: u8, |
204 | | pub sizeinc: u32, |
205 | | } |
206 | | |
207 | 1.63k | pub fn http2_parse_frame_windowupdate(i: &[u8]) -> IResult<&[u8], HTTP2FrameWindowUpdate> { |
208 | 1.63k | let (i, b) = be_u32(i)?; |
209 | 1.63k | let (reserved, sizeinc) = ((b >> 31) as u8, b & 0x7fff_ffff); |
210 | 1.63k | Ok((i, HTTP2FrameWindowUpdate { reserved, sizeinc })) |
211 | 1.63k | } |
212 | | |
213 | | #[derive(Clone, Copy, Debug)] |
214 | | pub struct HTTP2FrameHeadersPriority { |
215 | | pub exclusive: u8, |
216 | | pub dependency: u32, |
217 | | pub weight: u8, |
218 | | } |
219 | | |
220 | 17.4k | pub fn http2_parse_headers_priority(i: &[u8]) -> IResult<&[u8], HTTP2FrameHeadersPriority> { |
221 | 17.4k | let (i, b) = be_u32(i)?; |
222 | 15.9k | let (exclusive, dependency) = ((b >> 31) as u8, b & 0x7fff_ffff); |
223 | 15.9k | let (i, weight) = be_u8(i)?; |
224 | 14.9k | Ok(( |
225 | 14.9k | i, |
226 | 14.9k | HTTP2FrameHeadersPriority { |
227 | 14.9k | exclusive, |
228 | 14.9k | dependency, |
229 | 14.9k | weight, |
230 | 14.9k | }, |
231 | 14.9k | )) |
232 | 17.4k | } |
233 | | |
234 | | pub const HTTP2_STATIC_HEADERS_NUMBER: usize = 61; |
235 | | |
236 | 15.1M | fn http2_frame_header_static(n: u64, dyn_headers: &HTTP2DynTable) -> Option<HTTP2FrameHeaderBlock> { |
237 | 15.1M | let (name, value) = match n { |
238 | 330k | 1 => (":authority", ""), |
239 | 13.1k | 2 => (":method", "GET"), |
240 | 21.0k | 3 => (":method", "POST"), |
241 | 217k | 4 => (":path", "/"), |
242 | 81.7k | 5 => (":path", "/index.html"), |
243 | 101k | 6 => (":scheme", "http"), |
244 | 14.0k | 7 => (":scheme", "https"), |
245 | 16.5k | 8 => (":status", "200"), |
246 | 436k | 9 => (":status", "204"), |
247 | 19.3k | 10 => (":status", "206"), |
248 | 26.9k | 11 => (":status", "304"), |
249 | 26.1k | 12 => (":status", "400"), |
250 | 28.1k | 13 => (":status", "404"), |
251 | 76.4k | 14 => (":status", "500"), |
252 | 16.1k | 15 => ("accept-charset", ""), |
253 | 15.1k | 16 => ("accept-encoding", "gzip, deflate"), |
254 | 12.6k | 17 => ("accept-language", ""), |
255 | 11.5k | 18 => ("accept-ranges", ""), |
256 | 16.7k | 19 => ("accept", ""), |
257 | 47.1k | 20 => ("access-control-allow-origin", ""), |
258 | 6.23k | 21 => ("age", ""), |
259 | 11.9k | 22 => ("allow", ""), |
260 | 103k | 23 => ("authorization", ""), |
261 | 19.8k | 24 => ("cache-control", ""), |
262 | 8.23k | 25 => ("content-disposition", ""), |
263 | 23.6k | 26 => ("content-encoding", ""), |
264 | 50.3k | 27 => ("content-language", ""), |
265 | 6.07k | 28 => ("content-length", ""), |
266 | 13.1k | 29 => ("content-location", ""), |
267 | 362k | 30 => ("content-range", ""), |
268 | 12.8k | 31 => ("content-type", ""), |
269 | 247k | 32 => ("cookie", ""), |
270 | 11.9k | 33 => ("date", ""), |
271 | 5.45k | 34 => ("etag", ""), |
272 | 9.18k | 35 => ("expect", ""), |
273 | 12.8k | 36 => ("expires", ""), |
274 | 255k | 37 => ("from", ""), |
275 | 40.0k | 38 => ("host", ""), |
276 | 17.5k | 39 => ("if-match", ""), |
277 | 42.9k | 40 => ("if-modified-since", ""), |
278 | 167k | 41 => ("if-none-match", ""), |
279 | 32.9k | 42 => ("if-range", ""), |
280 | 5.04k | 43 => ("if-unmodified-since", ""), |
281 | 105k | 44 => ("last-modified", ""), |
282 | 2.33k | 45 => ("link", ""), |
283 | 23.9k | 46 => ("location", ""), |
284 | 122k | 47 => ("max-forwards", ""), |
285 | 25.8k | 48 => ("proxy-authenticate", ""), |
286 | 86.4k | 49 => ("proxy-authorization", ""), |
287 | 6.39k | 50 => ("range", ""), |
288 | 25.7k | 51 => ("referer", ""), |
289 | 11.7k | 52 => ("refresh", ""), |
290 | 111k | 53 => ("retry-after", ""), |
291 | 245k | 54 => ("server", ""), |
292 | 9.10k | 55 => ("set-cookie", ""), |
293 | 52.2k | 56 => ("strict-transport-security", ""), |
294 | 4.56k | 57 => ("transfer-encoding", ""), |
295 | 91.5k | 58 => ("user-agent", ""), |
296 | 35.1k | 59 => ("vary", ""), |
297 | 2.30k | 60 => ("via", ""), |
298 | 19.6k | 61 => ("www-authenticate", ""), |
299 | 11.1M | _ => ("", ""), |
300 | | }; |
301 | 15.1M | if !name.is_empty() { |
302 | 3.97M | return Some(HTTP2FrameHeaderBlock { |
303 | 3.97M | name: Rc::new(name.as_bytes().to_vec()), |
304 | 3.97M | value: Rc::new(value.as_bytes().to_vec()), |
305 | 3.97M | error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, |
306 | 3.97M | sizeupdate: 0, |
307 | 3.97M | }); |
308 | | } else { |
309 | | //use dynamic table |
310 | 11.1M | if n == 0 { |
311 | 6.50M | return Some(HTTP2FrameHeaderBlock { |
312 | 6.50M | name: Rc::new(Vec::new()), |
313 | 6.50M | value: Rc::new(Vec::new()), |
314 | 6.50M | error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0, |
315 | 6.50M | sizeupdate: 0, |
316 | 6.50M | }); |
317 | 4.68M | } else if dyn_headers.table.len() + HTTP2_STATIC_HEADERS_NUMBER < n as usize { |
318 | 4.02M | return Some(HTTP2FrameHeaderBlock { |
319 | 4.02M | name: Rc::new(Vec::new()), |
320 | 4.02M | value: Rc::new(Vec::new()), |
321 | 4.02M | error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed, |
322 | 4.02M | sizeupdate: 0, |
323 | 4.02M | }); |
324 | | } else { |
325 | 657k | let indyn = dyn_headers.table.len() - (n as usize - HTTP2_STATIC_HEADERS_NUMBER); |
326 | 657k | let headcopy = HTTP2FrameHeaderBlock { |
327 | 657k | name: dyn_headers.table[indyn].name.clone(), |
328 | 657k | value: dyn_headers.table[indyn].value.clone(), |
329 | 657k | error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, |
330 | 657k | sizeupdate: 0, |
331 | 657k | }; |
332 | 657k | return Some(headcopy); |
333 | | } |
334 | | } |
335 | 15.1M | } |
336 | | |
337 | | #[repr(u8)] |
338 | | #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)] |
339 | | pub enum HTTP2HeaderDecodeStatus { |
340 | | HTTP2HeaderDecodeSuccess = 0, |
341 | | HTTP2HeaderDecodeSizeUpdate = 1, |
342 | | HTTP2HeaderDecodeError = 0x80, |
343 | | HTTP2HeaderDecodeNotIndexed = 0x81, |
344 | | HTTP2HeaderDecodeIntegerOverflow = 0x82, |
345 | | HTTP2HeaderDecodeIndex0 = 0x83, |
346 | | } |
347 | | |
348 | | impl fmt::Display for HTTP2HeaderDecodeStatus { |
349 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
350 | 0 | write!(f, "{:?}", self) |
351 | 0 | } |
352 | | } |
353 | | |
354 | | #[derive(Clone, Debug)] |
355 | | pub struct HTTP2FrameHeaderBlock { |
356 | | // Use Rc reference counted so that indexed headers do not get copied. |
357 | | // Otherwise, this leads to quadratic complexity in memory occupation. |
358 | | pub name: Rc<Vec<u8>>, |
359 | | pub value: Rc<Vec<u8>>, |
360 | | pub error: HTTP2HeaderDecodeStatus, |
361 | | pub sizeupdate: u64, |
362 | | } |
363 | | |
364 | 12.8M | fn http2_parse_headers_block_indexed<'a>( |
365 | 12.8M | input: &'a [u8], dyn_headers: &HTTP2DynTable, |
366 | 12.8M | ) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> { |
367 | 12.8M | fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> { |
368 | 12.8M | bits(complete(tuple(( |
369 | 12.8M | verify(take_bits(1u8), |&x| x == 1), |
370 | 12.8M | take_bits(7u8), |
371 | 12.8M | ))))(input) |
372 | 12.8M | } |
373 | 12.8M | let (i2, indexed) = parser(input)?; |
374 | 12.8M | let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0x7F)?; |
375 | 12.8M | match http2_frame_header_static(indexreal, dyn_headers) { |
376 | 12.8M | Some(h) => Ok((i3, h)), |
377 | 0 | _ => Err(Err::Error(make_error(i3, ErrorKind::MapOpt))), |
378 | | } |
379 | 12.8M | } |
380 | | |
381 | 18.7M | fn http2_parse_headers_block_string(input: &[u8]) -> IResult<&[u8], Vec<u8>> { |
382 | 18.7M | fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> { |
383 | 18.7M | bits(tuple((take_bits(1u8), take_bits(7u8))))(input) |
384 | 18.7M | } |
385 | 18.7M | let (i1, huffslen) = parser(input)?; |
386 | 18.5M | let (i2, stringlen) = http2_parse_var_uint(i1, huffslen.1 as u64, 0x7F)?; |
387 | 18.5M | let (i3, data) = take(stringlen as usize)(i2)?; |
388 | 18.2M | if huffslen.0 == 0 { |
389 | 17.6M | return Ok((i3, data.to_vec())); |
390 | | } else { |
391 | 573k | let (_, val) = bits(many0(huffman::http2_decode_huffman))(data)?; |
392 | 573k | return Ok((i3, val)); |
393 | | } |
394 | 18.7M | } |
395 | | |
396 | 10.5M | fn http2_parse_headers_block_literal_common<'a>( |
397 | 10.5M | input: &'a [u8], index: u64, dyn_headers: &HTTP2DynTable, |
398 | 10.5M | ) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> { |
399 | 10.5M | let (i3, name, error) = if index == 0 { |
400 | 8.29M | match http2_parse_headers_block_string(input) { |
401 | 8.10M | Ok((r, n)) => Ok((r, Rc::new(n), HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess)), |
402 | 190k | Err(e) => Err(e), |
403 | | } |
404 | | } else { |
405 | 2.30M | match http2_frame_header_static(index, dyn_headers) { |
406 | 2.30M | Some(x) => Ok(( |
407 | 2.30M | input, |
408 | 2.30M | x.name, |
409 | 2.30M | HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, |
410 | 2.30M | )), |
411 | 0 | None => Ok(( |
412 | 0 | input, |
413 | 0 | Rc::new(Vec::new()), |
414 | 0 | HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed, |
415 | 0 | )), |
416 | | } |
417 | 190k | }?; |
418 | 10.4M | let (i4, value) = http2_parse_headers_block_string(i3)?; |
419 | 10.1M | return Ok(( |
420 | 10.1M | i4, |
421 | 10.1M | HTTP2FrameHeaderBlock { |
422 | 10.1M | name, |
423 | 10.1M | value: Rc::new(value), |
424 | 10.1M | error, |
425 | 10.1M | sizeupdate: 0, |
426 | 10.1M | }, |
427 | 10.1M | )); |
428 | 10.5M | } |
429 | | |
430 | 1.23M | fn http2_parse_headers_block_literal_incindex<'a>( |
431 | 1.23M | input: &'a [u8], dyn_headers: &mut HTTP2DynTable, |
432 | 1.23M | ) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> { |
433 | 1.23M | fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> { |
434 | 1.23M | bits(complete(tuple(( |
435 | 1.23M | verify(take_bits(2u8), |&x| x == 1), |
436 | 1.23M | take_bits(6u8), |
437 | 1.23M | ))))(input) |
438 | 1.23M | } |
439 | 1.23M | let (i2, indexed) = parser(input)?; |
440 | 1.23M | let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0x3F)?; |
441 | 1.23M | let r = http2_parse_headers_block_literal_common(i3, indexreal, dyn_headers); |
442 | 1.23M | match r { |
443 | 1.05M | Ok((r, head)) => { |
444 | 1.05M | let headcopy = HTTP2FrameHeaderBlock { |
445 | 1.05M | name: head.name.clone(), |
446 | 1.05M | value: head.value.clone(), |
447 | 1.05M | error: head.error, |
448 | 1.05M | sizeupdate: 0, |
449 | 1.05M | }; |
450 | 1.05M | if head.error == HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess { |
451 | 1.05M | dyn_headers.current_size += 32 + headcopy.name.len() + headcopy.value.len(); |
452 | | //in case of overflow, best effort is to keep first headers |
453 | 1.05M | if dyn_headers.overflow > 0 { |
454 | 424k | if dyn_headers.overflow == 1 { |
455 | 383k | if dyn_headers.current_size <= (unsafe { HTTP2_MAX_TABLESIZE } as usize) { |
456 | 231k | //overflow had not yet happened |
457 | 231k | dyn_headers.table.push(headcopy); |
458 | 231k | } else if dyn_headers.current_size > dyn_headers.max_size { |
459 | 67 | //overflow happens, we cannot replace evicted headers |
460 | 67 | dyn_headers.overflow = 2; |
461 | 151k | } |
462 | 40.9k | } |
463 | 633k | } else { |
464 | 633k | dyn_headers.table.push(headcopy); |
465 | 633k | } |
466 | 1.05M | let mut toremove = 0; |
467 | 1.39M | while dyn_headers.current_size > dyn_headers.max_size |
468 | 361k | && toremove < dyn_headers.table.len() |
469 | 335k | { |
470 | 335k | dyn_headers.current_size -= 32 |
471 | 335k | + dyn_headers.table[toremove].name.len() |
472 | 335k | + dyn_headers.table[toremove].value.len(); |
473 | 335k | toremove += 1; |
474 | 335k | } |
475 | 1.05M | dyn_headers.table.drain(0..toremove); |
476 | 0 | } |
477 | 1.05M | return Ok((r, head)); |
478 | | } |
479 | 176k | Err(e) => { |
480 | 176k | return Err(e); |
481 | | } |
482 | | } |
483 | 1.23M | } |
484 | | |
485 | 9.11M | fn http2_parse_headers_block_literal_noindex<'a>( |
486 | 9.11M | input: &'a [u8], dyn_headers: &HTTP2DynTable, |
487 | 9.11M | ) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> { |
488 | 9.11M | fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> { |
489 | 9.11M | bits(complete(tuple(( |
490 | 9.11M | verify(take_bits(4u8), |&x| x == 0), |
491 | 9.11M | take_bits(4u8), |
492 | 9.11M | ))))(input) |
493 | 9.11M | } |
494 | 9.11M | let (i2, indexed) = parser(input)?; |
495 | 9.11M | let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0xF)?; |
496 | 9.11M | let r = http2_parse_headers_block_literal_common(i3, indexreal, dyn_headers); |
497 | 9.11M | return r; |
498 | 9.11M | } |
499 | | |
500 | 247k | fn http2_parse_headers_block_literal_neverindex<'a>( |
501 | 247k | input: &'a [u8], dyn_headers: &HTTP2DynTable, |
502 | 247k | ) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> { |
503 | 247k | fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> { |
504 | 247k | bits(complete(tuple(( |
505 | 247k | verify(take_bits(4u8), |&x| x == 1), |
506 | 247k | take_bits(4u8), |
507 | 247k | ))))(input) |
508 | 247k | } |
509 | 247k | let (i2, indexed) = parser(input)?; |
510 | 247k | let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0xF)?; |
511 | 246k | let r = http2_parse_headers_block_literal_common(i3, indexreal, dyn_headers); |
512 | 246k | return r; |
513 | 247k | } |
514 | | |
515 | 44.3M | fn http2_parse_var_uint(input: &[u8], value: u64, max: u64) -> IResult<&[u8], u64> { |
516 | 44.3M | if value < max { |
517 | 43.6M | return Ok((input, value)); |
518 | 748k | } |
519 | 6.52M | let (i2, varia) = take_while(|ch| (ch & 0x80) != 0)(input)?; |
520 | 709k | let (i3, finalv) = be_u8(i2)?; |
521 | 709k | if varia.len() > 9 || (varia.len() == 9 && finalv > 1) { |
522 | | // this will overflow u64 |
523 | 54.4k | return Ok((i3, 0)); |
524 | 655k | } |
525 | 655k | let mut varval = max; |
526 | 711k | for (i, e) in varia.iter().enumerate() { |
527 | 711k | varval += ((e & 0x7F) as u64) << (7 * i); |
528 | 711k | } |
529 | 655k | match varval.checked_add((finalv as u64) << (7 * varia.len())) { |
530 | | None => { |
531 | 4.01k | return Err(Err::Error(make_error(i3, ErrorKind::LengthValue))); |
532 | | } |
533 | 651k | Some(x) => { |
534 | 651k | return Ok((i3, x)); |
535 | | } |
536 | | } |
537 | 44.3M | } |
538 | | |
539 | 2.29M | fn http2_parse_headers_block_dynamic_size<'a>( |
540 | 2.29M | input: &'a [u8], dyn_headers: &mut HTTP2DynTable, |
541 | 2.29M | ) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> { |
542 | 2.29M | fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> { |
543 | 2.29M | bits(complete(tuple(( |
544 | 2.29M | verify(take_bits(3u8), |&x| x == 1), |
545 | 2.29M | take_bits(5u8), |
546 | 2.29M | ))))(input) |
547 | 2.29M | } |
548 | 2.29M | let (i2, maxsize) = parser(input)?; |
549 | 2.29M | let (i3, maxsize2) = http2_parse_var_uint(i2, maxsize.1 as u64, 0x1F)?; |
550 | 2.28M | if (maxsize2 as usize) < dyn_headers.max_size { |
551 | | //dyn_headers.max_size is updated later with all headers |
552 | | //may evict entries |
553 | 1.99M | let mut toremove = 0; |
554 | 2.38M | while dyn_headers.current_size > (maxsize2 as usize) && toremove < dyn_headers.table.len() { |
555 | 392k | // we check dyn_headers.table as we may be in best effort |
556 | 392k | // because the previous maxsize was too big for us to retain all the headers |
557 | 392k | dyn_headers.current_size -= 32 |
558 | 392k | + dyn_headers.table[toremove].name.len() |
559 | 392k | + dyn_headers.table[toremove].value.len(); |
560 | 392k | toremove += 1; |
561 | 392k | } |
562 | 1.99M | dyn_headers.table.drain(0..toremove); |
563 | 292k | } |
564 | 2.28M | return Ok(( |
565 | 2.28M | i3, |
566 | 2.28M | HTTP2FrameHeaderBlock { |
567 | 2.28M | name: Rc::new(Vec::new()), |
568 | 2.28M | value: Rc::new(Vec::new()), |
569 | 2.28M | error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate, |
570 | 2.28M | sizeupdate: maxsize2, |
571 | 2.28M | }, |
572 | 2.28M | )); |
573 | 2.29M | } |
574 | | |
575 | 25.7M | fn http2_parse_headers_block<'a>( |
576 | 25.7M | input: &'a [u8], dyn_headers: &mut HTTP2DynTable, |
577 | 25.7M | ) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> { |
578 | | //caller guarantees o have at least one byte |
579 | 25.7M | if input[0] & 0x80 != 0 { |
580 | 12.8M | return http2_parse_headers_block_indexed(input, dyn_headers); |
581 | 12.8M | } else if input[0] & 0x40 != 0 { |
582 | 1.23M | return http2_parse_headers_block_literal_incindex(input, dyn_headers); |
583 | 11.6M | } else if input[0] & 0x20 != 0 { |
584 | 2.29M | return http2_parse_headers_block_dynamic_size(input, dyn_headers); |
585 | 9.36M | } else if input[0] & 0x10 != 0 { |
586 | 247k | return http2_parse_headers_block_literal_neverindex(input, dyn_headers); |
587 | | } else { |
588 | 9.11M | return http2_parse_headers_block_literal_noindex(input, dyn_headers); |
589 | | } |
590 | 25.7M | } |
591 | | |
592 | | #[derive(Clone, Debug)] |
593 | | pub struct HTTP2FrameHeaders { |
594 | | pub padlength: Option<u8>, |
595 | | pub priority: Option<HTTP2FrameHeadersPriority>, |
596 | | pub blocks: Vec<HTTP2FrameHeaderBlock>, |
597 | | } |
598 | | |
599 | | //end stream |
600 | | pub const HTTP2_FLAG_HEADER_EOS: u8 = 0x1; |
601 | | pub const HTTP2_FLAG_HEADER_END_HEADERS: u8 = 0x4; |
602 | | pub const HTTP2_FLAG_HEADER_PADDED: u8 = 0x8; |
603 | | const HTTP2_FLAG_HEADER_PRIORITY: u8 = 0x20; |
604 | | |
605 | 1.07M | fn http2_parse_headers_blocks<'a>( |
606 | 1.07M | input: &'a [u8], dyn_headers: &mut HTTP2DynTable, |
607 | 1.07M | ) -> IResult<&'a [u8], Vec<HTTP2FrameHeaderBlock>> { |
608 | 1.07M | let mut blocks = Vec::new(); |
609 | 1.07M | let mut i3 = input; |
610 | 26.3M | while !i3.is_empty() { |
611 | 25.7M | match http2_parse_headers_block(i3, dyn_headers) { |
612 | 25.2M | Ok((rem, b)) => { |
613 | 25.2M | blocks.push(b); |
614 | 25.2M | debug_validate_bug_on!(i3.len() == rem.len()); |
615 | 25.2M | if i3.len() == rem.len() { |
616 | | //infinite loop |
617 | 0 | return Err(Err::Error(make_error(input, ErrorKind::Eof))); |
618 | 25.2M | } |
619 | 25.2M | i3 = rem; |
620 | | } |
621 | 4.01k | Err(Err::Error(ref err)) => { |
622 | | // if we error from http2_parse_var_uint, we keep the first parsed headers |
623 | 4.01k | if err.code == ErrorKind::LengthValue { |
624 | 4.01k | blocks.push(HTTP2FrameHeaderBlock { |
625 | 4.01k | name: Rc::new(Vec::new()), |
626 | 4.01k | value: Rc::new(Vec::new()), |
627 | 4.01k | error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIntegerOverflow, |
628 | 4.01k | sizeupdate: 0, |
629 | 4.01k | }); |
630 | 4.01k | break; |
631 | 0 | } |
632 | | } |
633 | 509k | Err(x) => { |
634 | 509k | return Err(x); |
635 | | } |
636 | | } |
637 | | } |
638 | 562k | return Ok((i3, blocks)); |
639 | 1.07M | } |
640 | | |
641 | 80.9k | pub fn http2_parse_frame_headers<'a>( |
642 | 80.9k | input: &'a [u8], flags: u8, dyn_headers: &mut HTTP2DynTable, |
643 | 80.9k | ) -> IResult<&'a [u8], HTTP2FrameHeaders> { |
644 | 80.9k | let (i2, padlength) = cond(flags & HTTP2_FLAG_HEADER_PADDED != 0, be_u8)(input)?; |
645 | 80.0k | let (i3, priority) = cond( |
646 | 80.0k | flags & HTTP2_FLAG_HEADER_PRIORITY != 0, |
647 | 80.0k | http2_parse_headers_priority, |
648 | 80.0k | )(i2)?; |
649 | 77.6k | let (i3, blocks) = http2_parse_headers_blocks(i3, dyn_headers)?; |
650 | 27.0k | return Ok(( |
651 | 27.0k | i3, |
652 | 27.0k | HTTP2FrameHeaders { |
653 | 27.0k | padlength, |
654 | 27.0k | priority, |
655 | 27.0k | blocks, |
656 | 27.0k | }, |
657 | 27.0k | )); |
658 | 80.9k | } |
659 | | |
660 | | #[derive(Clone, Debug)] |
661 | | pub struct HTTP2FramePushPromise { |
662 | | pub padlength: Option<u8>, |
663 | | pub reserved: u8, |
664 | | pub stream_id: u32, |
665 | | pub blocks: Vec<HTTP2FrameHeaderBlock>, |
666 | | } |
667 | | |
668 | 34.4k | pub fn http2_parse_frame_push_promise<'a>( |
669 | 34.4k | input: &'a [u8], flags: u8, dyn_headers: &mut HTTP2DynTable, |
670 | 34.4k | ) -> IResult<&'a [u8], HTTP2FramePushPromise> { |
671 | 34.4k | let (i2, padlength) = cond(flags & HTTP2_FLAG_HEADER_PADDED != 0, be_u8)(input)?; |
672 | 31.9k | let (i3, stream_id) = bits(tuple((take_bits(1u8), take_bits(31u32))))(i2)?; |
673 | 30.2k | let (i3, blocks) = http2_parse_headers_blocks(i3, dyn_headers)?; |
674 | 21.6k | return Ok(( |
675 | 21.6k | i3, |
676 | 21.6k | HTTP2FramePushPromise { |
677 | 21.6k | padlength, |
678 | 21.6k | reserved: stream_id.0, |
679 | 21.6k | stream_id: stream_id.1, |
680 | 21.6k | blocks, |
681 | 21.6k | }, |
682 | 21.6k | )); |
683 | 34.4k | } |
684 | | |
685 | | #[derive(Clone, Debug)] |
686 | | pub struct HTTP2FrameContinuation { |
687 | | pub blocks: Vec<HTTP2FrameHeaderBlock>, |
688 | | } |
689 | | |
690 | 964k | pub fn http2_parse_frame_continuation<'a>( |
691 | 964k | input: &'a [u8], dyn_headers: &mut HTTP2DynTable, |
692 | 964k | ) -> IResult<&'a [u8], HTTP2FrameContinuation> { |
693 | 964k | let (i3, blocks) = http2_parse_headers_blocks(input, dyn_headers)?; |
694 | 513k | return Ok((i3, HTTP2FrameContinuation { blocks })); |
695 | 964k | } |
696 | | |
697 | | #[repr(u16)] |
698 | | #[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)] |
699 | | pub enum HTTP2SettingsId { |
700 | | HeaderTableSize = 1, |
701 | | EnablePush = 2, |
702 | | MaxConcurrentStreams = 3, |
703 | | InitialWindowSize = 4, |
704 | | MaxFrameSize = 5, |
705 | | MaxHeaderListSize = 6, |
706 | | EnableConnectProtocol = 8, // rfc8441 |
707 | | NoRfc7540Priorities = 9, // rfc9218 |
708 | | } |
709 | | |
710 | | impl fmt::Display for HTTP2SettingsId { |
711 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
712 | 0 | write!(f, "{:?}", self) |
713 | 0 | } |
714 | | } |
715 | | |
716 | | impl std::str::FromStr for HTTP2SettingsId { |
717 | | type Err = String; |
718 | | |
719 | 1.03k | fn from_str(s: &str) -> Result<Self, Self::Err> { |
720 | 1.03k | let su = s.to_uppercase(); |
721 | 1.03k | let su_slice: &str = &su; |
722 | 1.03k | match su_slice { |
723 | 1.03k | "SETTINGS_HEADER_TABLE_SIZE" => Ok(HTTP2SettingsId::HeaderTableSize), |
724 | 217 | "SETTINGS_ENABLE_PUSH" => Ok(HTTP2SettingsId::EnablePush), |
725 | 217 | "SETTINGS_MAX_CONCURRENT_STREAMS" => Ok(HTTP2SettingsId::MaxConcurrentStreams), |
726 | 217 | "SETTINGS_INITIAL_WINDOW_SIZE" => Ok(HTTP2SettingsId::InitialWindowSize), |
727 | 217 | "SETTINGS_MAX_FRAME_SIZE" => Ok(HTTP2SettingsId::MaxFrameSize), |
728 | 215 | "SETTINGS_MAX_HEADER_LIST_SIZE" => Ok(HTTP2SettingsId::MaxHeaderListSize), |
729 | 215 | "SETTINGS_ENABLE_CONNECT_PROTOCOL" => Ok(HTTP2SettingsId::EnableConnectProtocol), |
730 | 215 | "SETTINGS_NO_RFC7540_PRIORITIES" => Ok(HTTP2SettingsId::NoRfc7540Priorities), |
731 | 215 | _ => Err(format!("'{}' is not a valid value for HTTP2SettingsId", s)), |
732 | | } |
733 | 1.03k | } |
734 | | } |
735 | | |
736 | | pub struct DetectHTTP2settingsSigCtx { |
737 | | pub id: HTTP2SettingsId, //identifier |
738 | | pub value: Option<DetectUintData<u32>>, //optional value |
739 | | } |
740 | | |
741 | 1.03k | pub fn http2_parse_settingsctx(i: &str) -> IResult<&str, DetectHTTP2settingsSigCtx> { |
742 | 1.03k | let (i, _) = opt(is_a(" "))(i)?; |
743 | 1.03k | let (i, id) = map_opt(alt((complete(is_not(" <>=")), rest)), |s: &str| { |
744 | 1.03k | HTTP2SettingsId::from_str(s).ok() |
745 | 1.03k | })(i)?; |
746 | 824 | let (i, value) = opt(complete(detect_parse_uint))(i)?; |
747 | 824 | Ok((i, DetectHTTP2settingsSigCtx { id, value })) |
748 | 1.03k | } |
749 | | |
750 | | #[derive(Clone, Copy, Debug)] |
751 | | pub struct HTTP2FrameSettings { |
752 | | pub id: HTTP2SettingsId, |
753 | | pub value: u32, |
754 | | } |
755 | | |
756 | 106k | fn http2_parse_frame_setting(i: &[u8]) -> IResult<&[u8], HTTP2FrameSettings> { |
757 | 106k | let (i, id) = map_opt(be_u16, num::FromPrimitive::from_u16)(i)?; |
758 | 69.2k | let (i, value) = be_u32(i)?; |
759 | 29.4k | Ok((i, HTTP2FrameSettings { id, value })) |
760 | 106k | } |
761 | | |
762 | 77.0k | pub fn http2_parse_frame_settings(i: &[u8]) -> IResult<&[u8], Vec<HTTP2FrameSettings>> { |
763 | 77.0k | many0(complete(http2_parse_frame_setting))(i) |
764 | 77.0k | } |
765 | | |
766 | 0 | pub fn doh_extract_request(i: &[u8]) -> IResult<&[u8], Vec<u8>> { |
767 | 0 | let (i, _) = tag("/dns-query?dns=")(i)?; |
768 | 0 | match STANDARD_NO_PAD.decode(i) { |
769 | 0 | Ok(dec) => { |
770 | | // i is unused |
771 | 0 | return Ok((i, dec)); |
772 | | } |
773 | | _ => { |
774 | 0 | return Err(Err::Error(make_error(i, ErrorKind::MapOpt))); |
775 | | } |
776 | | } |
777 | 0 | } |
778 | | |
779 | | #[cfg(test)] |
780 | | mod tests { |
781 | | |
782 | | use super::*; |
783 | | use crate::detect::uint::DetectUintMode; |
784 | | |
785 | | #[test] |
786 | | fn test_http2_parse_header() { |
787 | | let buf0: &[u8] = &[0x82]; |
788 | | let mut dynh = HTTP2DynTable::new(); |
789 | | let r0 = http2_parse_headers_block(buf0, &mut dynh); |
790 | | match r0 { |
791 | | Ok((remainder, hd)) => { |
792 | | // Check the first message. |
793 | | assert_eq!(hd.name, ":method".as_bytes().to_vec().into()); |
794 | | assert_eq!(hd.value, "GET".as_bytes().to_vec().into()); |
795 | | // And we should have no bytes left. |
796 | | assert_eq!(remainder.len(), 0); |
797 | | } |
798 | | Err(Err::Incomplete(_)) => { |
799 | | panic!("Result should not have been incomplete."); |
800 | | } |
801 | | Err(Err::Error(err)) | Err(Err::Failure(err)) => { |
802 | | panic!("Result should not be an error: {:?}.", err); |
803 | | } |
804 | | } |
805 | | let buf1: &[u8] = &[0x53, 0x03, 0x2A, 0x2F, 0x2A]; |
806 | | let r1 = http2_parse_headers_block(buf1, &mut dynh); |
807 | | match r1 { |
808 | | Ok((remainder, hd)) => { |
809 | | // Check the first message. |
810 | | assert_eq!(hd.name, "accept".as_bytes().to_vec().into()); |
811 | | assert_eq!(hd.value, "*/*".as_bytes().to_vec().into()); |
812 | | // And we should have no bytes left. |
813 | | assert_eq!(remainder.len(), 0); |
814 | | assert_eq!(dynh.table.len(), 1); |
815 | | } |
816 | | Err(Err::Incomplete(_)) => { |
817 | | panic!("Result should not have been incomplete."); |
818 | | } |
819 | | Err(Err::Error(err)) | Err(Err::Failure(err)) => { |
820 | | panic!("Result should not be an error: {:?}.", err); |
821 | | } |
822 | | } |
823 | | let buf: &[u8] = &[ |
824 | | 0x41, 0x8a, 0xa0, 0xe4, 0x1d, 0x13, 0x9d, 0x09, 0xb8, 0xc8, 0x00, 0x0f, |
825 | | ]; |
826 | | let result = http2_parse_headers_block(buf, &mut dynh); |
827 | | match result { |
828 | | Ok((remainder, hd)) => { |
829 | | // Check the first message. |
830 | | assert_eq!(hd.name, ":authority".as_bytes().to_vec().into()); |
831 | | assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into()); |
832 | | // And we should have no bytes left. |
833 | | assert_eq!(remainder.len(), 0); |
834 | | assert_eq!(dynh.table.len(), 2); |
835 | | } |
836 | | Err(Err::Incomplete(_)) => { |
837 | | panic!("Result should not have been incomplete."); |
838 | | } |
839 | | Err(Err::Error(err)) | Err(Err::Failure(err)) => { |
840 | | panic!("Result should not be an error: {:?}.", err); |
841 | | } |
842 | | } |
843 | | let buf3: &[u8] = &[0xbe]; |
844 | | let r3 = http2_parse_headers_block(buf3, &mut dynh); |
845 | | match r3 { |
846 | | Ok((remainder, hd)) => { |
847 | | // same as before |
848 | | assert_eq!(hd.name, ":authority".as_bytes().to_vec().into()); |
849 | | assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into()); |
850 | | // And we should have no bytes left. |
851 | | assert_eq!(remainder.len(), 0); |
852 | | assert_eq!(dynh.table.len(), 2); |
853 | | } |
854 | | Err(Err::Incomplete(_)) => { |
855 | | panic!("Result should not have been incomplete."); |
856 | | } |
857 | | Err(Err::Error(err)) | Err(Err::Failure(err)) => { |
858 | | panic!("Result should not be an error: {:?}.", err); |
859 | | } |
860 | | } |
861 | | let buf4: &[u8] = &[0x80]; |
862 | | let r4 = http2_parse_headers_block(buf4, &mut dynh); |
863 | | match r4 { |
864 | | Ok((remainder, hd)) => { |
865 | | assert_eq!(hd.error, HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0); |
866 | | assert_eq!(remainder.len(), 0); |
867 | | assert_eq!(dynh.table.len(), 2); |
868 | | } |
869 | | Err(Err::Incomplete(_)) => { |
870 | | panic!("Result should not have been incomplete."); |
871 | | } |
872 | | Err(Err::Error(err)) | Err(Err::Failure(err)) => { |
873 | | panic!("Result should not be an error: {:?}.", err); |
874 | | } |
875 | | } |
876 | | let buf2: &[u8] = &[ |
877 | | 0x04, 0x94, 0x62, 0x43, 0x91, 0x8a, 0x47, 0x55, 0xa3, 0xa1, 0x89, 0xd3, 0x4d, 0x0c, |
878 | | 0x1a, 0xa9, 0x0b, 0xe5, 0x79, 0xd3, 0x4d, 0x1f, |
879 | | ]; |
880 | | let r2 = http2_parse_headers_block(buf2, &mut dynh); |
881 | | match r2 { |
882 | | Ok((remainder, hd)) => { |
883 | | // Check the first message. |
884 | | assert_eq!(hd.name, ":path".as_bytes().to_vec().into()); |
885 | | assert_eq!(hd.value, "/doc/manual/html/index.html".as_bytes().to_vec().into()); |
886 | | // And we should have no bytes left. |
887 | | assert_eq!(remainder.len(), 0); |
888 | | assert_eq!(dynh.table.len(), 2); |
889 | | } |
890 | | Err(Err::Incomplete(_)) => { |
891 | | panic!("Result should not have been incomplete."); |
892 | | } |
893 | | Err(Err::Error(err)) | Err(Err::Failure(err)) => { |
894 | | panic!("Result should not be an error: {:?}.", err); |
895 | | } |
896 | | } |
897 | | } |
898 | | |
899 | | /// Simple test of some valid data. |
900 | | #[test] |
901 | | fn test_http2_parse_settingsctx() { |
902 | | let s = "SETTINGS_ENABLE_PUSH"; |
903 | | let r = http2_parse_settingsctx(s); |
904 | | match r { |
905 | | Ok((rem, ctx)) => { |
906 | | assert_eq!(ctx.id, HTTP2SettingsId::EnablePush); |
907 | | assert!(ctx.value.is_none()); |
908 | | assert_eq!(rem.len(), 0); |
909 | | } |
910 | | Err(e) => { |
911 | | panic!("Result should not be an error {:?}.", e); |
912 | | } |
913 | | } |
914 | | |
915 | | //spaces in the end |
916 | | let s1 = "SETTINGS_ENABLE_PUSH "; |
917 | | let r1 = http2_parse_settingsctx(s1); |
918 | | match r1 { |
919 | | Ok((rem, ctx)) => { |
920 | | assert_eq!(ctx.id, HTTP2SettingsId::EnablePush); |
921 | | if ctx.value.is_some() { |
922 | | panic!("Unexpected value"); |
923 | | } |
924 | | assert_eq!(rem.len(), 1); |
925 | | } |
926 | | Err(e) => { |
927 | | panic!("Result should not be an error {:?}.", e); |
928 | | } |
929 | | } |
930 | | |
931 | | let s2 = "SETTINGS_MAX_CONCURRENT_STREAMS 42"; |
932 | | let r2 = http2_parse_settingsctx(s2); |
933 | | match r2 { |
934 | | Ok((rem, ctx)) => { |
935 | | assert_eq!(ctx.id, HTTP2SettingsId::MaxConcurrentStreams); |
936 | | match ctx.value { |
937 | | Some(ctxval) => { |
938 | | assert_eq!(ctxval.arg1, 42); |
939 | | } |
940 | | None => { |
941 | | panic!("No value"); |
942 | | } |
943 | | } |
944 | | assert_eq!(rem.len(), 0); |
945 | | } |
946 | | Err(e) => { |
947 | | panic!("Result should not be an error {:?}.", e); |
948 | | } |
949 | | } |
950 | | |
951 | | let s3 = "SETTINGS_MAX_CONCURRENT_STREAMS 42-68"; |
952 | | let r3 = http2_parse_settingsctx(s3); |
953 | | match r3 { |
954 | | Ok((rem, ctx)) => { |
955 | | assert_eq!(ctx.id, HTTP2SettingsId::MaxConcurrentStreams); |
956 | | match ctx.value { |
957 | | Some(ctxval) => { |
958 | | assert_eq!(ctxval.arg1, 42); |
959 | | assert_eq!(ctxval.mode, DetectUintMode::DetectUintModeRange); |
960 | | assert_eq!(ctxval.arg2, 68); |
961 | | } |
962 | | None => { |
963 | | panic!("No value"); |
964 | | } |
965 | | } |
966 | | assert_eq!(rem.len(), 0); |
967 | | } |
968 | | Err(e) => { |
969 | | panic!("Result should not be an error {:?}.", e); |
970 | | } |
971 | | } |
972 | | |
973 | | let s4 = "SETTINGS_MAX_CONCURRENT_STREAMS<54"; |
974 | | let r4 = http2_parse_settingsctx(s4); |
975 | | match r4 { |
976 | | Ok((rem, ctx)) => { |
977 | | assert_eq!(ctx.id, HTTP2SettingsId::MaxConcurrentStreams); |
978 | | match ctx.value { |
979 | | Some(ctxval) => { |
980 | | assert_eq!(ctxval.arg1, 54); |
981 | | assert_eq!(ctxval.mode, DetectUintMode::DetectUintModeLt); |
982 | | } |
983 | | None => { |
984 | | panic!("No value"); |
985 | | } |
986 | | } |
987 | | assert_eq!(rem.len(), 0); |
988 | | } |
989 | | Err(e) => { |
990 | | panic!("Result should not be an error {:?}.", e); |
991 | | } |
992 | | } |
993 | | |
994 | | let s5 = "SETTINGS_MAX_CONCURRENT_STREAMS > 76"; |
995 | | let r5 = http2_parse_settingsctx(s5); |
996 | | match r5 { |
997 | | Ok((rem, ctx)) => { |
998 | | assert_eq!(ctx.id, HTTP2SettingsId::MaxConcurrentStreams); |
999 | | match ctx.value { |
1000 | | Some(ctxval) => { |
1001 | | assert_eq!(ctxval.arg1, 76); |
1002 | | assert_eq!(ctxval.mode, DetectUintMode::DetectUintModeGt); |
1003 | | } |
1004 | | None => { |
1005 | | panic!("No value"); |
1006 | | } |
1007 | | } |
1008 | | assert_eq!(rem.len(), 0); |
1009 | | } |
1010 | | Err(e) => { |
1011 | | panic!("Result should not be an error {:?}.", e); |
1012 | | } |
1013 | | } |
1014 | | } |
1015 | | |
1016 | | #[test] |
1017 | | fn test_http2_parse_headers_block_string() { |
1018 | | let buf: &[u8] = &[0x01, 0xFF]; |
1019 | | let r = http2_parse_headers_block_string(buf); |
1020 | | match r { |
1021 | | Ok((remainder, _)) => { |
1022 | | assert_eq!(remainder.len(), 0); |
1023 | | } |
1024 | | Err(Err::Error(err)) | Err(Err::Failure(err)) => { |
1025 | | panic!("Result should not be an error: {:?}.", err); |
1026 | | } |
1027 | | _ => { |
1028 | | panic!("Result should have been ok"); |
1029 | | } |
1030 | | } |
1031 | | let buf2: &[u8] = &[0x83, 0xFF, 0xFF, 0xEA]; |
1032 | | let r2 = http2_parse_headers_block_string(buf2); |
1033 | | match r2 { |
1034 | | Ok((remainder, _)) => { |
1035 | | assert_eq!(remainder.len(), 0); |
1036 | | } |
1037 | | _ => { |
1038 | | panic!("Result should have been ok"); |
1039 | | } |
1040 | | } |
1041 | | } |
1042 | | |
1043 | | #[test] |
1044 | | fn test_http2_parse_frame_header() { |
1045 | | let buf: &[u8] = &[ |
1046 | | 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, |
1047 | | 0x64, |
1048 | | ]; |
1049 | | let result = http2_parse_frame_header(buf); |
1050 | | match result { |
1051 | | Ok((remainder, frame)) => { |
1052 | | // Check the first message. |
1053 | | assert_eq!(frame.length, 6); |
1054 | | assert_eq!(frame.ftype, HTTP2FrameType::Settings as u8); |
1055 | | assert_eq!(frame.flags, 0); |
1056 | | assert_eq!(frame.reserved, 0); |
1057 | | assert_eq!(frame.stream_id, 0); |
1058 | | |
1059 | | // And we should have 6 bytes left. |
1060 | | assert_eq!(remainder.len(), 6); |
1061 | | } |
1062 | | Err(Err::Incomplete(_)) => { |
1063 | | panic!("Result should not have been incomplete."); |
1064 | | } |
1065 | | Err(Err::Error(err)) | Err(Err::Failure(err)) => { |
1066 | | panic!("Result should not be an error: {:?}.", err); |
1067 | | } |
1068 | | } |
1069 | | } |
1070 | | } |