rust/htp: convert to nom 8

Ticket: #8090
pull/14332/head
Jason Ish 8 months ago committed by Victor Julien
parent ef9cd7beff
commit a9eee5dfd9

@ -1608,7 +1608,7 @@ dependencies = [
"lazy_static",
"libc",
"lzma-rs",
"nom 7.1.3",
"nom 8.0.0",
"rstest",
"time",
]

@ -24,7 +24,7 @@ crate-type = ["staticlib", "rlib", "cdylib"]
base64 = "0.22.1"
bstr = "1.12.0"
libc = "0.2"
nom = "7.1.3"
nom = "8.0.0"
lzma-rs = { version = "0.2.0", features = ["stream"] }
flate2 = { version = "~1.0.35", features = ["zlib-default"], default-features = false }
brotli = "~8.0.1"

@ -1,4 +1,5 @@
use brotli;
use nom::Parser as _;
use std::{
io::{Cursor, Write},
time::Instant,
@ -455,10 +456,9 @@ impl GzipBufWriter {
fn parse_start(data: &[u8]) -> nom::IResult<&[u8], u8> {
use nom::bytes::streaming::tag;
use nom::number::streaming::{le_i32, le_u8};
use nom::sequence::tuple;
let (rest, (_, flags, _mtime, _xfl, _operating_system)) =
tuple((tag(b"\x1f\x8b\x08"), le_u8, le_i32, le_u8, le_u8))(data)?;
(tag(&b"\x1f\x8b\x08"[..]), le_u8, le_i32, le_u8, le_u8).parse(data)?;
Ok((rest, flags))
}
}
@ -467,7 +467,6 @@ impl Write for GzipBufWriter {
fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
use nom::bytes::streaming::{tag, take_until};
use nom::number::streaming::le_u16;
use nom::sequence::tuple;
const FHCRC: u8 = 1 << 1;
const FEXTRA: u8 = 1 << 2;
@ -534,10 +533,11 @@ impl Write for GzipBufWriter {
}
GzState::Filename => {
if self.flags & FNAME != 0 {
match tuple((
match (
take_until::<&[u8], &[u8], nom::error::Error<&[u8]>>(b"\0" as &[u8]),
tag(b"\0"),
))(parse)
tag(&b"\0"[..]),
)
.parse(parse)
{
Ok((rest, _)) => {
parse = rest;
@ -557,10 +557,11 @@ impl Write for GzipBufWriter {
}
GzState::Comment => {
if self.flags & FCOMMENT != 0 {
match tuple((
match (
take_until::<&[u8], &[u8], nom::error::Error<&[u8]>>(b"\0" as &[u8]),
tag(b"\0"),
))(parse)
tag(&b"\0"[..]),
)
.parse(parse)
{
Ok((rest, _)) => {
parse = rest;

@ -1,13 +1,13 @@
use crate::util::{is_token, trimmed, FlagOperations};
use nom::AsChar;
use nom::{
branch::alt,
bytes::complete::tag as complete_tag,
bytes::streaming::{tag, take_till, take_while, take_while1},
character::{is_space, streaming::space0},
character::streaming::space0,
combinator::{complete, map, not, opt, peek},
sequence::tuple,
Err::Incomplete,
IResult, Needed,
IResult, Needed, Parser as _,
};
/// Helper for Parsed bytes and corresponding HeaderFlags
@ -133,9 +133,10 @@ impl Parser {
complete_tag("\n\r"),
complete_tag("\n"),
complete_tag("\r"),
))(input)
))
.parse(input)
} else {
alt((complete_tag("\r\n"), complete_tag("\n")))(input)
alt((complete_tag("\r\n"), complete_tag("\n"))).parse(input)
}
}
}
@ -146,19 +147,19 @@ impl Parser {
if self.side == Side::Response {
alt((
map(
tuple((
(
complete_tag("\n\r\r\n"),
peek(alt((complete_tag("\n"), complete_tag("\r\n")))),
)),
),
|(eol, _)| (eol, HeaderFlags::DEFORMED_EOL),
),
map(
tuple((
(
complete_tag("\r\n\r"),
take_while1(|c| c == b'\r' || c == b' ' || c == b'\t'),
opt(complete_tag("\n")),
not(alt((complete_tag("\n"), complete_tag("\r\n")))),
)),
),
|(eol1, eol2, eol3, _): (&[u8], &[u8], Option<&[u8]>, _)| {
(
&input[..(eol1.len() + eol2.len() + eol3.unwrap_or(b"").len())],
@ -166,18 +167,20 @@ impl Parser {
)
},
),
))(input)
))
.parse(input)
} else {
map(
alt((
tuple((
(
complete_tag("\n\r\r\n"),
peek(alt((complete_tag("\n"), complete_tag("\r\n")))),
)),
tuple((complete_tag("\n\r"), peek(complete_tag("\r\n")))),
),
(complete_tag("\n\r"), peek(complete_tag("\r\n"))),
)),
|(eol, _)| (eol, HeaderFlags::DEFORMED_EOL),
)(input)
)
.parse(input)
}
}
}
@ -188,28 +191,24 @@ impl Parser {
alt((
self.complete_eol_deformed(),
map(self.complete_eol_regular(), |eol| (eol, 0)),
))(input)
))
.parse(input)
}
}
/// Parse one header end of line, and guarantee that it is not folding
fn eol(&self) -> impl Fn(&[u8]) -> IResult<&[u8], ParsedBytes> + '_ {
move |input| {
map(
tuple((self.complete_eol(), not(folding_lws))),
|(end, _)| end,
)(input)
}
move |input| map((self.complete_eol(), not(folding_lws)), |(end, _)| end).parse(input)
}
/// Parse one null byte or one end of line, and guarantee that it is not folding
fn null_or_eol(&self) -> impl Fn(&[u8]) -> IResult<&[u8], ParsedBytes> + '_ {
move |input| alt((null, self.eol()))(input)
move |input| alt((null, self.eol())).parse(input)
}
/// Parse one null byte or complete end of line
fn complete_null_or_eol(&self) -> impl Fn(&[u8]) -> IResult<&[u8], ParsedBytes> + '_ {
move |input| alt((null, self.complete_eol()))(input)
move |input| alt((null, self.complete_eol())).parse(input)
}
/// Parse header folding bytes (eol + whitespace or eol + special cases)
@ -217,17 +216,19 @@ impl Parser {
move |input| {
if self.side == Side::Response {
map(
tuple((
(
map(self.complete_eol_regular(), |eol| (eol, 0)),
folding_lws,
)),
),
|((eol, flags), (lws, other_flags))| (eol, lws, flags | other_flags),
)(input)
)
.parse(input)
} else {
map(
tuple((self.complete_eol(), folding_lws)),
(self.complete_eol(), folding_lws),
|((eol, flags), (lws, other_flags))| (eol, lws, flags | other_flags),
)(input)
)
.parse(input)
}
}
}
@ -242,7 +243,8 @@ impl Parser {
((end, flags), Some(fold))
})),
map(self.complete_null_or_eol(), |end| (end, None)),
))(input)
))
.parse(input)
}
}
@ -256,7 +258,8 @@ impl Parser {
((end, flags), Some(fold))
}),
map(self.null_or_eol(), |end| (end, None)),
))(input)
))
.parse(input)
}
}
@ -264,9 +267,9 @@ impl Parser {
fn folding_or_terminator(&self) -> impl Fn(&[u8]) -> IResult<&[u8], FoldingOrTerminator> + '_ {
move |input| {
if self.complete {
self.complete_folding_or_terminator()(input)
self.complete_folding_or_terminator().parse(input)
} else {
self.streaming_folding_or_terminator()(input)
self.streaming_folding_or_terminator().parse(input)
}
}
}
@ -276,12 +279,12 @@ impl Parser {
/// eg. (bytes, (eol_bytes, Option<fold_bytes>))
fn value_bytes(&self) -> impl Fn(&[u8]) -> IResult<&[u8], ValueBytes> + '_ {
move |input| {
let (mut remaining, mut value) = take_till(self.is_eol())(input)?;
let (mut remaining, mut value) = take_till(self.is_eol()).parse(input)?;
if value.last() == Some(&b'\r') {
value = &value[..value.len() - 1];
remaining = &input[value.len()..];
}
let (remaining, result) = self.folding_or_terminator()(remaining)?;
let (remaining, result) = self.folding_or_terminator().parse(remaining)?;
Ok((remaining, (value, result)))
}
}
@ -289,7 +292,8 @@ impl Parser {
/// Parse a complete header value, including any folded headers
fn value(&self) -> impl Fn(&[u8]) -> IResult<&[u8], Value> + '_ {
move |input| {
let (mut rest, (val_bytes, ((_eol, mut flags), fold))) = self.value_bytes()(input)?;
let (mut rest, (val_bytes, ((_eol, mut flags), fold))) =
self.value_bytes().parse(input)?;
let mut value = val_bytes.to_vec();
if let Some(fold) = fold {
let mut i = rest;
@ -297,7 +301,7 @@ impl Parser {
loop {
if self.side == Side::Response {
// Peek ahead for ambiguous name with lws vs. value with folding
match tuple((token_chars, separator_regular))(i) {
match (token_chars, separator_regular).parse(i) {
Ok((_, ((_, tokens, _), (_, _)))) if !tokens.is_empty() => {
flags.unset(HeaderFlags::FOLDING_SPECIAL_CASE);
if value.is_empty() {
@ -312,7 +316,8 @@ impl Parser {
_ => {}
}
}
let (rest2, (val_bytes, ((eol, other_flags), fold))) = self.value_bytes()(i)?;
let (rest2, (val_bytes, ((eol, other_flags), fold))) =
self.value_bytes().parse(i)?;
i = rest2;
flags.set(other_flags);
//If the value is empty, the value started with a fold and we don't want to push back a space
@ -375,11 +380,11 @@ impl Parser {
let (name, rem) = input.split_at(offset);
let mut flags = 0;
if !name.is_empty() {
if is_space(name[0]) {
if name[0].is_space() {
flags.set(HeaderFlags::NAME_LEADING_WHITESPACE)
}
if let Some(end) = name.last() {
if is_space(*end) {
if end.is_space() {
flags.set(HeaderFlags::NAME_TRAILING_WHITESPACE);
}
}
@ -397,13 +402,13 @@ impl Parser {
/// Parse a separator between header name and value
fn separator(&self) -> impl Fn(&[u8]) -> IResult<&[u8], u64> + '_ {
move |input| map(separator_regular, |_| 0)(input)
move |input| map(separator_regular, |_| 0).parse(input)
}
/// Parse data before an eol with no colon as an empty name with the data as the value
fn header_sans_colon(&self) -> impl Fn(&[u8]) -> IResult<&[u8], Header> + '_ {
move |input| {
let (remaining, (_, value)) = tuple((not(complete_tag("\r\n")), self.value()))(input)?;
let (remaining, (_, value)) = (not(complete_tag("\r\n")), self.value()).parse(input)?;
let flags = value.flags | HeaderFlags::MISSING_COLON;
Ok((
@ -417,19 +422,22 @@ impl Parser {
fn header_with_colon(&self) -> impl Fn(&[u8]) -> IResult<&[u8], Header> + '_ {
move |input| {
map(
tuple((self.name(), self.separator(), self.value())),
(self.name(), self.separator(), self.value()),
|(mut name, flag, mut value)| {
name.flags |= flag;
value.flags |= flag;
Header::new(name, value)
},
)(input)
)
.parse(input)
}
}
/// Parses a header name and value with, or without a colon separator
fn header(&self) -> impl Fn(&[u8]) -> IResult<&[u8], Header> + '_ {
move |input| alt((complete(self.header_with_colon()), self.header_sans_colon()))(input)
move |input| {
alt((complete(self.header_with_colon()), self.header_sans_colon())).parse(input)
}
}
/// Parse multiple headers and indicate if end of headers or null was found
@ -438,7 +446,7 @@ impl Parser {
let mut out = Vec::with_capacity(16);
let mut i = input;
loop {
match self.header()(i) {
match self.header().parse(i) {
Ok((rest, head)) => {
i = rest;
let is_null_terminated =
@ -447,7 +455,7 @@ impl Parser {
if is_null_terminated {
return Ok((rest, (out, true)));
}
if let Ok((rest2, _eoh)) = self.complete_eol_regular()(rest) {
if let Ok((rest2, _eoh)) = self.complete_eol_regular().parse(rest) {
return Ok((rest2, (out, true)));
}
}
@ -459,7 +467,7 @@ impl Parser {
}
Err(e) => {
if out.is_empty() {
if let Ok((rest2, _eoh)) = self.complete_eol()(i) {
if let Ok((rest2, _eoh)) = self.complete_eol().parse(i) {
return Ok((rest2, (out, true)));
}
}
@ -475,25 +483,27 @@ impl Parser {
fn null(input: &[u8]) -> IResult<&[u8], ParsedBytes<'_>> {
map(complete_tag("\0"), |null| {
(null, HeaderFlags::NULL_TERMINATED)
})(input)
})
.parse(input)
}
/// Extracts folding lws (whitespace only)
fn folding_lws(input: &[u8]) -> IResult<&[u8], ParsedBytes<'_>> {
map(alt((tag(" "), tag("\t"), tag("\0"))), |fold| {
(fold, HeaderFlags::FOLDING)
})(input)
})
.parse(input)
}
/// Parse a regular separator (colon followed by optional spaces) between header name and value
fn separator_regular(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
tuple((complete_tag(":"), space0))(input)
(complete_tag(":"), space0).parse(input)
}
type leading_token_trailing<'a> = (&'a [u8], &'a [u8], &'a [u8]);
/// Parse token characters with leading and trailing whitespace
fn token_chars(input: &[u8]) -> IResult<&[u8], leading_token_trailing<'_>> {
tuple((space0, take_while(is_token), space0))(input)
(space0, take_while(is_token), space0).parse(input)
}
#[cfg(test)]
@ -542,13 +552,13 @@ mod test {
#[case] diff_res_expected: Option<IResult<&[u8], ParsedHeaders>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.headers()(input), expected);
assert_eq!(req_parser.headers().parse(input), expected);
let res_parser = Parser::new(Side::Response);
if let Some(res_expected) = diff_res_expected {
assert_eq!(res_parser.headers()(input), res_expected);
assert_eq!(res_parser.headers().parse(input), res_expected);
} else {
assert_eq!(res_parser.headers()(input), expected);
assert_eq!(res_parser.headers().parse(input), expected);
}
}
@ -615,11 +625,11 @@ mod test {
let req_parser = Parser::new(Side::Request);
let res_parser = Parser::new(Side::Response);
if let Some(req_expected) = diff_req_expected {
assert_eq!(req_parser.headers()(input), req_expected);
assert_eq!(req_parser.headers().parse(input), req_expected);
} else {
assert_eq!(req_parser.headers()(input), expected);
assert_eq!(req_parser.headers().parse(input), expected);
}
assert_eq!(res_parser.headers()(input), expected);
assert_eq!(res_parser.headers().parse(input), expected);
}
#[rstest]
@ -636,7 +646,7 @@ mod test {
#[case] response_parser_expected: Option<IResult<&[u8], Header>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.header_sans_colon()(input), expected);
assert_eq!(req_parser.header_sans_colon().parse(input), expected);
let res_parser = Parser::new(Side::Response);
let res_expected = if let Some(response_expected) = response_parser_expected {
@ -644,7 +654,7 @@ mod test {
} else {
expected
};
assert_eq!(res_parser.header_sans_colon()(input), res_expected);
assert_eq!(res_parser.header_sans_colon().parse(input), res_expected);
}
#[rstest]
@ -662,10 +672,10 @@ mod test {
#[case::lf_6(b"K: V\r\n a\r\n l\r\n u\r\n\te\r\n\r\n", Ok((b!("\r\n"), Header::new_with_flags(b"K", 0, b"V a l u\te", HeaderFlags::FOLDING))))]
fn test_header_with_colon(#[case] input: &[u8], #[case] expected: IResult<&[u8], Header>) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.header_with_colon()(input), expected);
assert_eq!(req_parser.header_with_colon().parse(input), expected);
let res_parser = Parser::new(Side::Response);
assert_eq!(res_parser.header_with_colon()(input), expected);
assert_eq!(res_parser.header_with_colon().parse(input), expected);
}
#[rstest]
@ -694,13 +704,13 @@ mod test {
#[case] diff_res_expected: Option<IResult<&[u8], Header>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.header()(input), expected);
assert_eq!(req_parser.header().parse(input), expected);
let res_parser = Parser::new(Side::Response);
if let Some(res_expected) = diff_res_expected {
assert_eq!(res_parser.header()(input), res_expected);
assert_eq!(res_parser.header().parse(input), res_expected);
} else {
assert_eq!(res_parser.header()(input), expected);
assert_eq!(res_parser.header().parse(input), expected);
}
}
@ -714,13 +724,13 @@ mod test {
#[case] diff_res_expected: Option<IResult<&[u8], u64>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.separator()(input), expected);
assert_eq!(req_parser.separator().parse(input), expected);
let res_parser = Parser::new(Side::Response);
if let Some(res_expected) = diff_res_expected {
assert_eq!(res_parser.separator()(input), res_expected);
assert_eq!(res_parser.separator().parse(input), res_expected);
} else {
assert_eq!(res_parser.separator()(input), expected);
assert_eq!(res_parser.separator().parse(input), expected);
}
}
@ -747,13 +757,13 @@ mod test {
#[case] diff_res_expected: Option<IResult<&[u8], Name>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.name()(input), expected);
assert_eq!(req_parser.name().parse(input), expected);
let res_parser = Parser::new(Side::Response);
if let Some(res_expected) = diff_res_expected {
assert_eq!(res_parser.name()(input), res_expected);
assert_eq!(res_parser.name().parse(input), res_expected);
} else {
assert_eq!(res_parser.name()(input), expected);
assert_eq!(res_parser.name().parse(input), expected);
}
}
@ -790,13 +800,13 @@ mod test {
#[case] diff_res_expected: Option<IResult<&[u8], ParsedBytes>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.eol()(input), expected);
assert_eq!(req_parser.eol().parse(input), expected);
let res_parser = Parser::new(Side::Response);
if let Some(res_expected) = diff_res_expected {
assert_eq!(res_parser.eol()(input), res_expected);
assert_eq!(res_parser.eol().parse(input), res_expected);
} else {
assert_eq!(res_parser.eol()(input), expected);
assert_eq!(res_parser.eol().parse(input), expected);
}
}
@ -824,13 +834,13 @@ mod test {
#[case] diff_res_expected: Option<IResult<&[u8], ParsedBytes>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.null_or_eol()(input), expected);
assert_eq!(req_parser.null_or_eol().parse(input), expected);
let res_parser = Parser::new(Side::Response);
if let Some(res_expected) = diff_res_expected {
assert_eq!(res_parser.null_or_eol()(input), res_expected);
assert_eq!(res_parser.null_or_eol().parse(input), res_expected);
} else {
assert_eq!(res_parser.null_or_eol()(input), expected);
assert_eq!(res_parser.null_or_eol().parse(input), expected);
}
}
@ -859,13 +869,13 @@ mod test {
#[case] diff_res_expected: Option<IResult<&[u8], FoldingBytes>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.folding()(input), expected);
assert_eq!(req_parser.folding().parse(input), expected);
let res_parser = Parser::new(Side::Response);
if let Some(res_expected) = diff_res_expected {
assert_eq!(res_parser.folding()(input), res_expected);
assert_eq!(res_parser.folding().parse(input), res_expected);
} else {
assert_eq!(res_parser.folding()(input), expected);
assert_eq!(res_parser.folding().parse(input), expected);
}
}
@ -890,13 +900,16 @@ mod test {
#[case] diff_res_expected: Option<IResult<&[u8], FoldingOrTerminator>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.folding_or_terminator()(input), expected);
assert_eq!(req_parser.folding_or_terminator().parse(input), expected);
let res_parser = Parser::new(Side::Response);
if let Some(res_expected) = diff_res_expected {
assert_eq!(res_parser.folding_or_terminator()(input), res_expected);
assert_eq!(
res_parser.folding_or_terminator().parse(input),
res_expected
);
} else {
assert_eq!(res_parser.folding_or_terminator()(input), expected);
assert_eq!(res_parser.folding_or_terminator().parse(input), expected);
}
}
@ -922,13 +935,13 @@ mod test {
#[case] diff_res_expected: Option<IResult<&[u8], ValueBytes>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.value_bytes()(input), expected);
assert_eq!(req_parser.value_bytes().parse(input), expected);
let res_parser = Parser::new(Side::Response);
if let Some(res_expected) = diff_res_expected {
assert_eq!(res_parser.value_bytes()(input), res_expected);
assert_eq!(res_parser.value_bytes().parse(input), res_expected);
} else {
assert_eq!(res_parser.value_bytes()(input), expected);
assert_eq!(res_parser.value_bytes().parse(input), expected);
}
}
@ -956,13 +969,13 @@ mod test {
#[case] diff_res_expected: Option<IResult<&[u8], Value>>,
) {
let req_parser = Parser::new(Side::Request);
assert_eq!(req_parser.value()(input), expected);
assert_eq!(req_parser.value().parse(input), expected);
let res_parser = Parser::new(Side::Response);
if let Some(res_expected) = diff_res_expected {
assert_eq!(res_parser.value()(input), res_expected);
assert_eq!(res_parser.value().parse(input), res_expected);
} else {
assert_eq!(res_parser.value()(input), expected);
assert_eq!(res_parser.value().parse(input), expected);
}
}
}

@ -14,10 +14,8 @@ use nom::{
branch::alt,
bytes::complete::{is_not, tag, tag_no_case, take_till, take_until, take_while},
combinator::{map, not, opt, peek},
error::ErrorKind,
multi::many0,
sequence::tuple,
IResult,
IResult, Parser as _,
};
/// Parses the content type header, trimming any leading whitespace.
@ -27,12 +25,13 @@ use nom::{
fn content_type() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
move |input| {
map(
tuple((
(
take_ascii_whitespace(),
take_till(|c| c == b';' || c == b',' || c == b' '),
)),
),
|(_, content_type)| content_type,
)(input)
)
.parse(input)
}
}
@ -80,7 +79,7 @@ pub(crate) fn parse_content_length(input: &[u8], logger: Option<&mut Logger>) ->
/// and after the number.
pub(crate) fn parse_chunked_length(input: &[u8]) -> Result<(Option<u64>, bool)> {
let (rest, _) = take_chunked_ctl_chars(input)?;
let (trailing_data, chunked_length) = hex_digits()(rest)?;
let (trailing_data, chunked_length) = hex_digits().parse(rest)?;
if trailing_data.is_empty() && chunked_length.is_empty() {
return Ok((None, false));
}
@ -105,9 +104,10 @@ pub(crate) fn scheme() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
// Scheme test: if it doesn't start with a forward slash character (which it must
// for the contents to be a path or an authority), then it must be the scheme part
map(
tuple((peek(not(tag("/"))), take_until(":"), tag(":"))),
(peek(not(tag("/"))), take_until(":"), tag(":")),
|(_, scheme, _)| scheme,
)(input)
)
.parse(input)
}
}
@ -123,8 +123,8 @@ pub(crate) fn credentials() -> impl Fn(&[u8]) -> IResult<&[u8], ParsedCredential
// One, three or more slash characters, and it's a path.
// Note: we only attempt to parse authority if we've seen a scheme.
let (input, (_, _, credentials, _)) =
tuple((tag("//"), peek(not(tag("/"))), take_until("@"), tag("@")))(input)?;
let (password, username) = opt(tuple((take_until(":"), tag(":"))))(credentials)?;
(tag("//"), peek(not(tag("/"))), take_until("@"), tag("@")).parse(input)?;
let (password, username) = opt((take_until(":"), tag(":"))).parse(credentials)?;
if let Some((username, _)) = username {
Ok((input, (username, Some(password))))
} else {
@ -139,7 +139,7 @@ pub(crate) fn credentials() -> impl Fn(&[u8]) -> IResult<&[u8], ParsedCredential
/// Returns a tuple of the remaining unconsumed data and the matched ipv6 hostname.
pub(crate) fn ipv6() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
move |input| -> IResult<&[u8], &[u8]> {
let (rest, _) = tuple((tag("["), is_not("/?#]"), opt(tag("]"))))(input)?;
let (rest, _) = (tag("["), is_not("/?#]"), opt(tag("]"))).parse(input)?;
Ok((rest, &input[..input.len() - rest.len()]))
}
}
@ -150,14 +150,15 @@ pub(crate) fn ipv6() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
pub(crate) fn hostname() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
move |input| {
let (input, mut hostname) = map(
tuple((
(
opt(tag("//")), //If it starts with "//", skip (might have parsed a scheme and no creds)
peek(not(tag("/"))), //If it starts with '/', this is a path, not a hostname
many0(tag(" ")),
alt((ipv6(), is_not("/?#:"))),
)),
),
|(_, _, _, hostname)| hostname,
)(input)?;
)
.parse(input)?;
//There may be spaces in the middle of a hostname, so much trim only at the end
while hostname.ends_with(b" ") {
hostname = &hostname[..hostname.len() - 1];
@ -174,7 +175,7 @@ pub(crate) fn port() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
move |input| {
// Must start with ":" for there to be a port to parse
let (input, (_, _, port, _)) =
tuple((tag(":"), many0(tag(" ")), is_not("/?#"), many0(tag(" "))))(input)?;
(tag(":"), many0(tag(" ")), is_not("/?#"), many0(tag(" "))).parse(input)?;
let (_, port) = is_not(" ")(port)?; //we assume there never will be a space in the middle of a port
Ok((input, port))
}
@ -185,7 +186,7 @@ pub(crate) fn port() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
///
/// Returns a tuple of the remaining unconsumed data and the matched path.
pub(crate) fn path() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
move |input| is_not("#?")(input)
move |input| is_not("#?").parse(input)
}
/// Attempts to extract the query from a given input URI,
@ -195,9 +196,7 @@ pub(crate) fn path() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
pub(crate) fn query() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
move |input| {
// Skip the starting '?'
map(tuple((tag("?"), take_till(|c| c == b'#'))), |(_, query)| {
query
})(input)
map((tag("?"), take_till(|c| c == b'#')), |(_, query)| query).parse(input)
}
}
@ -208,7 +207,7 @@ pub(crate) fn query() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
pub(crate) fn fragment() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
move |input| {
// Skip the starting '#'
let (input, _) = tag("#")(input)?;
let (input, _) = tag("#").parse(input)?;
Ok((b"", input))
}
}
@ -221,9 +220,9 @@ type parsed_hostport<'a> = (&'a [u8], parsed_port<'a>, bool);
/// Returns a remaining unparsed data, parsed hostname, parsed port, converted port number,
/// and a flag indicating whether the parsed data is valid.
pub(crate) fn parse_hostport(input: &[u8]) -> IResult<&[u8], parsed_hostport<'_>> {
let (input, host) = hostname()(input)?;
let (input, host) = hostname().parse(input)?;
let mut valid = validate_hostname(host);
if let Ok((_, p)) = port()(input) {
if let Ok((_, p)) = port().parse(input) {
if let Some(port) = convert_port(p) {
return Ok((input, (host, Some((p, Some(port))), valid)));
} else {
@ -241,7 +240,7 @@ pub(crate) fn parse_hostport(input: &[u8]) -> IResult<&[u8], parsed_hostport<'_>
/// Returns (any unparsed trailing data, (version_number, flag indicating whether input contains trailing and/or leading whitespace and/or leading zeros))
fn protocol_version(input: &[u8]) -> IResult<&[u8], (&[u8], bool)> {
map(
tuple((
(
take_ascii_whitespace(),
tag_no_case("HTTP"),
take_ascii_whitespace(),
@ -249,11 +248,12 @@ fn protocol_version(input: &[u8]) -> IResult<&[u8], (&[u8], bool)> {
take_while(|c: u8| c.is_ascii_whitespace() || c == b'0'),
alt((tag(".9"), tag("1.0"), tag("1.1"))),
take_ascii_whitespace(),
)),
),
|(_, _, leading, _, trailing, version, _)| {
(version, !leading.is_empty() || !trailing.is_empty())
},
)(input)
)
.parse(input)
}
/// Determines protocol number from a textual representation (i.e., "HTTP/1.1"). This
@ -306,16 +306,17 @@ pub(crate) fn parse_status(status: &[u8]) -> HtpResponseNumber {
/// Parses Digest Authorization request header.
fn parse_authorization_digest(auth_header_value: &[u8]) -> IResult<&[u8], Vec<u8>> {
// Extract the username
let (mut remaining_input, _) = tuple((
let (mut remaining_input, _) = (
take_until("username="),
tag("username="),
take_ascii_whitespace(), // allow lws
tag("\""), // First character after LWS must be a double quote
))(auth_header_value)?;
)
.parse(auth_header_value)?;
let mut result = Vec::new();
// Unescape any escaped double quotes and find the closing quote
loop {
let (remaining, (auth_header, _)) = tuple((take_until("\""), tag("\"")))(remaining_input)?;
let (remaining, (auth_header, _)) = (take_until("\""), tag("\"")).parse(remaining_input)?;
remaining_input = remaining;
result.extend_from_slice(auth_header);
if result.last() == Some(&(b'\\')) {
@ -333,16 +334,18 @@ fn parse_authorization_digest(auth_header_value: &[u8]) -> IResult<&[u8], Vec<u8
/// Parses Basic Authorization request header.
fn parse_authorization_basic(request_tx: &mut Transaction, auth_header: &Header) -> Result<()> {
// Skip 'Basic<lws>'
let (remaining_input, _) =
tuple((tag_no_case("basic"), take_ascii_whitespace()))(auth_header.value.as_slice())
.map_err(|_| HtpStatus::DECLINED)?;
let (remaining_input, _) = (tag_no_case("basic"), take_ascii_whitespace())
.parse(auth_header.value.as_slice())
.map_err(|_| HtpStatus::DECLINED)?;
// Decode base64-encoded data
let decoded = STANDARD
.decode(remaining_input)
.map_err(|_| HtpStatus::DECLINED)?;
let (password, (username, _)) =
tuple::<_, _, (&[u8], ErrorKind), _>((take_until(":"), tag(":")))(decoded.as_slice())
.map_err(|_| HtpStatus::DECLINED)?;
#[allow(clippy::type_complexity)]
let result: nom::IResult<&[u8], (&[u8], &[u8]), nom::error::Error<&[u8]>> =
(take_until(":"), tag(":")).parse(decoded.as_slice());
let (remaining, (username, _)) = result.map_err(|_| HtpStatus::DECLINED)?;
let password = remaining;
request_tx.request_auth_username = Some(Bstr::from(username));
request_tx.request_auth_password = Some(Bstr::from(password));
Ok(())
@ -377,11 +380,12 @@ pub(crate) fn parse_authorization(request_tx: &mut Transaction) -> Result<()> {
}
} else if auth_header.value.starts_with_nocase("bearer") {
request_tx.request_auth_type = HtpAuthType::BEARER;
let (token, _) = tuple((
let (token, _) = (
tag_no_case("bearer"),
take_ascii_whitespace(), // allow lws
))(auth_header.value.as_slice())
.map_err(|_| HtpStatus::DECLINED)?;
)
.parse(auth_header.value.as_slice())
.map_err(|_| HtpStatus::DECLINED)?;
request_tx.request_auth_token = Some(Bstr::from(token));
} else {
// Unrecognized authentication method

@ -18,7 +18,7 @@ use crate::{
},
HtpStatus,
};
use nom::sequence::tuple;
use nom::Parser as _;
use std::{
cmp::{min, Ordering},
mem::take,
@ -272,7 +272,7 @@ impl ConnectionParser {
// The request method starts at the beginning of the
// line and ends with the first whitespace character.
// We skip leading whitespace as IIS allows this.
let res = tuple((take_is_space, take_not_is_space))(buffered.as_slice());
let res = (take_is_space, take_not_is_space).parse(buffered.as_slice());
if let Ok((_, (_, method))) = res {
if HtpMethod::new(method) == HtpMethod::Unknown {
self.request_status = HtpStreamState::TUNNEL;
@ -889,18 +889,18 @@ impl ConnectionParser {
}
// The request method starts at the beginning of the
// line and ends with the first whitespace character.
let mut method_parser = tuple
let mut method_parser =
// skip past leading whitespace. IIS allows this
((take_is_space,
(take_is_space,
take_not_is_space,
// Ignore whitespace after request method. The RFC allows
// for only one SP, but then suggests any number of SP and HT
// should be permitted. Apache uses isspace(), which is even
// more permitting, so that's what we use here.
take_is_space
));
);
if let Ok((remaining, (ls, method, ws))) = method_parser(data) {
if let Ok((remaining, (ls, method, ws))) = method_parser.parse(data) {
if !ls.is_empty() {
htp_warn!(
self.logger,
@ -1369,7 +1369,7 @@ impl ConnectionParser {
//closing
return self.state_request_complete(input);
}
let res = tuple((take_is_space, take_not_is_space))(&data);
let res = (take_is_space, take_not_is_space).parse(&data);
if let Ok((_, (_, method))) = res {
if method.is_empty() {

@ -12,7 +12,7 @@ use crate::{
FlagOperations, HtpFlags,
},
};
use nom::{bytes::complete::take_while, error::ErrorKind, sequence::tuple};
use nom::{bytes::complete::take_while, error::ErrorKind};
use std::cmp::Ordering;
impl ConnectionParser {

@ -19,7 +19,7 @@ use crate::{
},
HtpStatus,
};
use nom::{bytes::streaming::take_till as streaming_take_till, error::ErrorKind, sequence::tuple};
use nom::{bytes::streaming::take_till as streaming_take_till, error::ErrorKind, Parser as _};
use std::{
cmp::{min, Ordering},
mem::take,
@ -739,16 +739,16 @@ impl ConnectionParser {
response_tx.response_status_number = HtpResponseNumber::Invalid;
response_tx.response_message = None;
let mut response_line_parser = tuple((
let mut response_line_parser = (
take_is_space_or_null,
take_not_is_space,
take_is_space,
take_not_is_space,
take_ascii_whitespace(),
));
);
let (message, (_ls, response_protocol, ws1, status_code, ws2)) =
response_line_parser(response_line)?;
response_line_parser.parse(response_line)?;
if response_protocol.is_empty() {
return Ok(());
}

@ -11,7 +11,7 @@ use crate::{
},
HtpStatus,
};
use nom::{error::ErrorKind, sequence::tuple};
use nom::{error::ErrorKind};
use std::cmp::Ordering;
impl ConnectionParser {

@ -7,7 +7,7 @@ use crate::{
utf8_decoder::decode_and_validate_inplace,
util::{convert_port, FlagOperations, HtpFlags},
};
use nom::{combinator::opt, sequence::tuple};
use nom::{combinator::opt, Parser as _};
/// URI structure. Each of the fields provides access to a single
/// URI element. Where an element is not present in a URI, the
@ -182,16 +182,13 @@ impl Uri {
/// It attempts, but is not guaranteed to successfully parse out a scheme, username, password, hostname, port, query, and fragment.
/// Note: only attempts to extract a username, password, and hostname and subsequently port if it successfully parsed a scheme.
pub(crate) fn parse_uri(&mut self, input: &[u8]) {
let res = tuple((
opt(tuple((
scheme(),
opt(credentials()),
opt(tuple((hostname(), opt(port())))),
))),
let res = (
opt((scheme(), opt(credentials()), opt((hostname(), opt(port()))))),
opt(path()),
opt(query()),
opt(fragment()),
))(input);
)
.parse(input);
if let Ok((_, (scheme_authority, path, query, fragment))) = res {
if let Some(path) = path {
self.path = Some(Bstr::from(path));

@ -12,8 +12,7 @@ use nom::{
combinator::{map, not},
multi::fold_many0,
number::complete::be_u8,
sequence::tuple,
IResult,
IResult, Parser as _,
};
/// Convert two input bytes, pointed to by the pointer parameter,
@ -22,7 +21,7 @@ use nom::{
///
/// Returns hex-decoded byte
fn x2c(input: &[u8]) -> IResult<&[u8], u8> {
let (input, (c1, c2)) = tuple((be_u8, be_u8))(input)?;
let (input, (c1, c2)) = (be_u8, be_u8).parse(input)?;
let mut decoded_byte = if c1 >= b'A' {
((c1 & 0xdf) - b'A') + 10
} else {
@ -101,13 +100,13 @@ fn path_decode_valid_u_encoding(
cfg: &DecoderConfig,
) -> impl Fn(&[u8]) -> IResult<&[u8], UrlParseResult> + '_ {
move |remaining_input| {
let (left, _) = tag_no_case("u")(remaining_input)?;
let (left, _) = tag_no_case("u").parse(remaining_input)?;
let mut output = remaining_input;
let mut byte = b'%';
let mut flags = 0;
let mut expected_status_code = HtpUnwanted::Ignore;
if cfg.u_encoding_decode {
let (left, hex) = take_while_m_n(4, 4, |c: u8| c.is_ascii_hexdigit())(left)?;
let (left, hex) = take_while_m_n(4, 4, |c: u8| c.is_ascii_hexdigit()).parse(left)?;
output = left;
expected_status_code = cfg.u_encoding_unwanted;
// Decode a valid %u encoding.
@ -163,9 +162,9 @@ fn path_decode_invalid_u_encoding(
let mut byte = b'%';
let mut flags = 0;
let mut expected_status_code = HtpUnwanted::Ignore;
let (left, _) = tag_no_case("u")(remaining_input)?;
let (left, _) = tag_no_case("u").parse(remaining_input)?;
if cfg.u_encoding_decode {
let (left, hex) = take(4usize)(left)?;
let (left, hex) = take(4usize).parse(left)?;
// Invalid %u encoding
flags = HtpFlags::PATH_INVALID_ENCODING;
expected_status_code = cfg.url_encoding_invalid_unwanted;
@ -215,8 +214,9 @@ fn path_decode_valid_hex(
move |remaining_input| {
let original_remaining = remaining_input;
// Valid encoding (2 xbytes)
not(tag_no_case("u"))(remaining_input)?;
let (mut left, hex) = take_while_m_n(2, 2, |c: u8| c.is_ascii_hexdigit())(remaining_input)?;
not(tag_no_case("u")).parse(remaining_input)?;
let (mut left, hex) =
take_while_m_n(2, 2, |c: u8| c.is_ascii_hexdigit()).parse(remaining_input)?;
let mut flags = 0;
// Convert from hex.
let (_, mut byte) = x2c(hex)?;
@ -265,8 +265,8 @@ fn path_decode_invalid_hex(
move |remaining_input| {
let mut remaining = remaining_input;
// Valid encoding (2 xbytes)
not(tag_no_case("u"))(remaining_input)?;
let (left, hex) = take(2usize)(remaining_input)?;
not(tag_no_case("u")).parse(remaining_input)?;
let (left, hex) = take(2usize).parse(remaining_input)?;
let mut byte = b'%';
// Invalid encoding
let flags = HtpFlags::PATH_INVALID_ENCODING;
@ -312,13 +312,13 @@ fn path_decode_percent(
) -> impl Fn(&[u8]) -> IResult<&[u8], UrlParseResult> + '_ {
move |i| {
map(
tuple((
(
char('%'),
alt((
path_decode_valid_u_encoding(cfg),
path_decode_invalid_u_encoding(cfg),
move |remaining_input| {
let (_, _) = tag_no_case("u")(remaining_input)?;
let (_, _) = tag_no_case("u").parse(remaining_input)?;
// Incomplete invalid %u encoding
Ok((
remaining_input,
@ -347,9 +347,10 @@ fn path_decode_percent(
))
},
)),
)),
),
|(_, result)| result,
)(i)
)
.parse(i)
}
}
@ -446,7 +447,8 @@ fn path_decode_uri<'a>(
acc.2 = upr.expected_status_code;
acc
},
)(input)
)
.parse(input)
}
/// Decode the parsed uri path inplace according to the settings in the
@ -486,7 +488,8 @@ fn decode_uri<'a>(
}
acc
},
)(input)
)
.parse(input)
}
/// Performs decoding of the uri string, according to the configuration specified
@ -525,9 +528,9 @@ fn decode_valid_u_encoding(
cfg: &DecoderConfig,
) -> impl Fn(&[u8]) -> IResult<&[u8], UrlParseResult> + '_ {
move |input| {
let (left, _) = alt((char('u'), char('U')))(input)?;
let (left, _) = alt((char('u'), char('U'))).parse(input)?;
if cfg.u_encoding_decode {
let (input, hex) = take_while_m_n(4, 4, |c: u8| c.is_ascii_hexdigit())(left)?;
let (input, hex) = take_while_m_n(4, 4, |c: u8| c.is_ascii_hexdigit()).parse(left)?;
let (_, (byte, flags)) = decode_u_encoding_params(hex, cfg)?;
return Ok((
input,
@ -559,14 +562,14 @@ fn decode_invalid_u_encoding(
cfg: &DecoderConfig,
) -> impl Fn(&[u8]) -> IResult<&[u8], UrlParseResult> + '_ {
move |mut input| {
let (left, _) = alt((char('u'), char('U')))(input)?;
let (left, _) = alt((char('u'), char('U'))).parse(input)?;
let mut byte = b'%';
let mut code = HtpUnwanted::Ignore;
let mut flags = 0;
let mut insert = true;
if cfg.u_encoding_decode {
// Invalid %u encoding (could not find 4 xdigits).
let (left, invalid_hex) = take(4usize)(left)?;
let (left, invalid_hex) = take(4usize).parse(left)?;
flags.set(HtpFlags::URLEN_INVALID_ENCODING);
code = if cfg.url_encoding_invalid_unwanted != HtpUnwanted::Ignore {
cfg.url_encoding_invalid_unwanted
@ -602,8 +605,8 @@ fn decode_invalid_u_encoding(
fn decode_valid_hex() -> impl Fn(&[u8]) -> IResult<&[u8], UrlParseResult> {
move |input| {
// Valid encoding (2 xbytes)
not(alt((char('u'), char('U'))))(input)?;
let (input, hex) = take_while_m_n(2, 2, |c: u8| c.is_ascii_hexdigit())(input)?;
not(alt((char('u'), char('U')))).parse(input)?;
let (input, hex) = take_while_m_n(2, 2, |c: u8| c.is_ascii_hexdigit()).parse(input)?;
let (_, byte) = x2c(hex)?;
Ok((
input,
@ -625,7 +628,7 @@ fn decode_invalid_hex(
cfg: &DecoderConfig,
) -> impl Fn(&[u8]) -> IResult<&[u8], UrlParseResult> + '_ {
move |mut input| {
not(alt((char('u'), char('U'))))(input)?;
not(alt((char('u'), char('U')))).parse(input)?;
// Invalid encoding (2 bytes, but not hexadecimal digits).
let mut byte = b'%';
let mut insert = true;
@ -657,7 +660,7 @@ fn decode_invalid_hex(
/// Returns decoded byte, corresponding status code, appropriate flags and whether the byte should be output.
fn decode_percent(cfg: &DecoderConfig) -> impl Fn(&[u8]) -> IResult<&[u8], UrlParseResult> + '_ {
move |i| {
let (input, _) = char('%')(i)?;
let (input, _) = char('%').parse(i)?;
let (input, upr) = alt((
decode_valid_u_encoding(cfg),
decode_invalid_u_encoding(cfg),
@ -677,7 +680,8 @@ fn decode_percent(cfg: &DecoderConfig) -> impl Fn(&[u8]) -> IResult<&[u8], UrlPa
},
))
},
))(input)?;
))
.parse(input)?;
//Did we get an encoded NUL byte?
if upr.byte == 0 {
let flags = upr.flags | HtpFlags::URLEN_ENCODED_NUL;
@ -722,7 +726,8 @@ fn decode_plus(cfg: &DecoderConfig) -> impl Fn(&[u8]) -> IResult<&[u8], UrlParse
} else {
byte as u8
}
})(input)?;
})
.parse(input)?;
Ok((
input,
UrlParseResult {

@ -1,6 +1,7 @@
//! Utility functions for http parsing.
use crate::{config::HtpServerPersonality, error::NomError};
use nom::AsChar;
use nom::{
branch::alt,
bytes::complete::{
@ -8,13 +9,10 @@ use nom::{
},
bytes::streaming::{tag as streaming_tag, take_till as streaming_take_till},
character::complete::{char, digit1},
character::is_space as nom_is_space,
combinator::{map, opt},
sequence::tuple,
Err::Incomplete,
IResult, Needed,
IResult, Needed, Parser,
};
use std::str::FromStr;
/// String for the libhtp version.
@ -195,7 +193,7 @@ pub(crate) fn is_token(c: u8) -> bool {
/// This parser takes leading whitespace as defined by is_ascii_whitespace.
pub(crate) fn take_ascii_whitespace() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
move |input| take_while(|c: u8| c.is_ascii_whitespace())(input)
move |input| take_while(|c: u8| c.is_ascii_whitespace()).parse(input)
}
/// Remove all line terminators (LF, CR or CRLF) from
@ -306,14 +304,15 @@ fn is_line_whitespace(data: &[u8]) -> bool {
/// Returns (any trailing non-LWS characters, (non-LWS leading characters, ascii digits))
pub(crate) fn ascii_digits(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
map(
tuple((
(
nom_take_is_space,
take_till(|c: u8| c.is_ascii_digit()),
digit1,
nom_take_is_space,
)),
),
|(_, leading_data, digits, _)| (leading_data, digits),
)(input)
)
.parse(input)
}
/// Searches for and extracts the next set of hex digits from the input slice if present
@ -323,13 +322,14 @@ pub(crate) fn ascii_digits(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
pub(crate) fn hex_digits() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
move |input| {
map(
tuple((
(
nom_take_is_space,
take_while(|c: u8| c.is_ascii_hexdigit()),
nom_take_is_space,
)),
),
|(_, digits, _)| digits,
)(input)
)
.parse(input)
}
}
@ -349,7 +349,7 @@ fn is_line_terminator(
if is_line_empty(data) {
return true;
}
if data.len() == 2 && nom_is_space(data[0]) && data[1] == b'\n' {
if data.len() == 2 && data[0].is_space() && data[1] == b'\n' {
return next_no_lf;
}
false
@ -387,7 +387,9 @@ pub(crate) fn treat_response_line_as_body(data: &[u8]) -> bool {
// IE: (?i)^\s*http\s*/
// Safari: ^HTTP/\d+\.\d+\s+\d{3}
tuple((opt(take_is_space_or_null), tag_no_case("http")))(data).is_err()
(opt(take_is_space_or_null), tag_no_case("http"))
.parse(data)
.is_err()
}
/// Implements relaxed (not strictly RFC) hostname validation.
@ -399,19 +401,22 @@ pub(crate) fn validate_hostname(input: &[u8]) -> bool {
}
// Check IPv6
if let Ok((_rest, (_left_br, addr, _right_br))) = tuple((
if let Ok((_rest, (_left_br, addr, _right_br))) = (
char::<_, NomError<&[u8]>>('['),
is_not::<_, _, NomError<&[u8]>>("#?/]"),
char::<_, NomError<&[u8]>>(']'),
))(input)
)
.parse(input)
{
if let Ok(str) = std::str::from_utf8(addr) {
return std::net::Ipv6Addr::from_str(str).is_ok();
}
}
if tag::<_, _, NomError<&[u8]>>(".")(input).is_ok()
|| take_until::<_, _, NomError<&[u8]>>("..")(input).is_ok()
if tag::<_, _, NomError<&[u8]>>(".").parse(input).is_ok()
|| take_until::<_, _, NomError<&[u8]>>("..")
.parse(input)
.is_ok()
{
return false;
}
@ -437,42 +442,42 @@ pub(crate) fn get_version() -> &'static str {
HTP_VERSION_STRING_FULL
}
/// Take leading whitespace as defined by nom_is_space.
/// Take leading whitespace as defined by AsChar::is_space.
pub(crate) fn nom_take_is_space(data: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(nom_is_space)(data)
take_while(|c: u8| c.is_space()).parse(data)
}
/// Take data before the first null character if it exists.
pub(crate) fn take_until_null(data: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(|c| c != b'\0')(data)
take_while(|c| c != b'\0').parse(data)
}
/// Take leading space as defined by util::is_space.
pub(crate) fn take_is_space(data: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(is_space)(data)
take_while(is_space).parse(data)
}
/// Take leading null characters or spaces as defined by util::is_space
pub(crate) fn take_is_space_or_null(data: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(|c| is_space(c) || c == b'\0')(data)
take_while(|c| is_space(c) || c == b'\0').parse(data)
}
/// Take any non-space character as defined by is_space.
pub(crate) fn take_not_is_space(data: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(|c: u8| !is_space(c))(data)
take_while(|c: u8| !is_space(c)).parse(data)
}
/// Returns all data up to and including the first new line or null
/// Returns Err if not found
pub(crate) fn take_till_lf_null(data: &[u8]) -> IResult<&[u8], &[u8]> {
let (_, line) = streaming_take_till(|c| c == b'\n' || c == 0)(data)?;
let (_, line) = streaming_take_till(|c| c == b'\n' || c == 0).parse(data)?;
Ok((&data[line.len() + 1..], &data[0..line.len() + 1]))
}
/// Returns all data up to and including the first new line
/// Returns Err if not found
pub(crate) fn take_till_lf(data: &[u8]) -> IResult<&[u8], &[u8]> {
let (_, line) = streaming_take_till(|c| c == b'\n')(data)?;
let (_, line) = streaming_take_till(|c| c == b'\n').parse(data)?;
Ok((&data[line.len() + 1..], &data[0..line.len() + 1]))
}
@ -480,14 +485,15 @@ pub(crate) fn take_till_lf(data: &[u8]) -> IResult<&[u8], &[u8]> {
///
/// Returns Err if not found
pub(crate) fn take_till_eol(data: &[u8]) -> IResult<&[u8], (&[u8], Eol)> {
let (_, (line, eol)) = tuple((
let (_, (line, eol)) = (
streaming_take_till(|c| c == b'\n' || c == b'\r'),
alt((
streaming_tag("\r\n"),
streaming_tag("\r"),
streaming_tag("\n"),
)),
))(data)?;
)
.parse(data)?;
match eol {
b"\n" => Ok((&data[line.len() + 1..], (&data[0..line.len() + 1], Eol::LF))),
b"\r" => Ok((&data[line.len() + 1..], (&data[0..line.len() + 1], Eol::CR))),
@ -501,18 +507,19 @@ pub(crate) fn take_till_eol(data: &[u8]) -> IResult<&[u8], (&[u8], Eol)> {
/// Skip control characters
pub(crate) fn take_chunked_ctl_chars(data: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(is_chunked_ctl_char)(data)
take_while(is_chunked_ctl_char).parse(data)
}
/// Check if the data contains valid chunked length chars, i.e. leading chunked ctl chars and ascii hexdigits
///
/// Returns true if valid, false otherwise
pub(crate) fn is_valid_chunked_length_data(data: &[u8]) -> bool {
tuple((
(
take_chunked_ctl_chars,
take_while1(|c: u8| !c.is_ascii_hexdigit()),
))(data)
.is_err()
)
.parse(data)
.is_err()
}
fn is_chunked_ctl_char(c: u8) -> bool {

Loading…
Cancel
Save