|
|
|
|
@ -24,6 +24,7 @@ use super::range::{SCHTPFileCloseHandleRange, SCHttpRangeFreeBlock};
|
|
|
|
|
use crate::applayer::{self, *};
|
|
|
|
|
use crate::conf::{conf_get, get_memval};
|
|
|
|
|
use crate::core::*;
|
|
|
|
|
use crate::detect::transforms::urldecode::hex_value;
|
|
|
|
|
use crate::direction::Direction;
|
|
|
|
|
use crate::dns::dns::DnsVariant;
|
|
|
|
|
use crate::filecontainer::FileContainerWrapper;
|
|
|
|
|
@ -289,6 +290,7 @@ pub struct HTTP2Transaction {
|
|
|
|
|
pub progress: HTTP2Progress,
|
|
|
|
|
to_drop: bool,
|
|
|
|
|
child_stream_id: u32,
|
|
|
|
|
pub normalized_uri: Vec<u8>,
|
|
|
|
|
|
|
|
|
|
pub frames_tc: Vec<HTTP2Frame>,
|
|
|
|
|
pub frames_ts: Vec<HTTP2Frame>,
|
|
|
|
|
@ -306,6 +308,70 @@ pub struct HTTP2Transaction {
|
|
|
|
|
pub doh: Option<DohHttp2Tx>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum NormalizeUriState {
|
|
|
|
|
Path,
|
|
|
|
|
Percent,
|
|
|
|
|
Percent2,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn http2_normalize_uri(uri: &[u8]) -> Vec<u8> {
|
|
|
|
|
let mut state = NormalizeUriState::Path;
|
|
|
|
|
let mut query = false;
|
|
|
|
|
let mut percent = 0u8;
|
|
|
|
|
let mut prevc = 0u8;
|
|
|
|
|
let mut r = Vec::with_capacity(uri.len());
|
|
|
|
|
for c in uri {
|
|
|
|
|
match state {
|
|
|
|
|
NormalizeUriState::Path => match c {
|
|
|
|
|
b'%' => state = NormalizeUriState::Percent,
|
|
|
|
|
b'+' if query => {
|
|
|
|
|
r.push(b' ');
|
|
|
|
|
}
|
|
|
|
|
b'?' => {
|
|
|
|
|
query = true;
|
|
|
|
|
r.push(*c);
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
r.push(*c);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
NormalizeUriState::Percent => {
|
|
|
|
|
if let Some(v) = hex_value(*c) {
|
|
|
|
|
percent = v << 4;
|
|
|
|
|
prevc = *c;
|
|
|
|
|
state = NormalizeUriState::Percent2;
|
|
|
|
|
} else {
|
|
|
|
|
r.push(b'%');
|
|
|
|
|
r.push(*c);
|
|
|
|
|
state = NormalizeUriState::Path;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NormalizeUriState::Percent2 => {
|
|
|
|
|
if let Some(v) = hex_value(*c) {
|
|
|
|
|
percent |= v;
|
|
|
|
|
r.push(percent);
|
|
|
|
|
} else {
|
|
|
|
|
r.push(b'%');
|
|
|
|
|
r.push(prevc);
|
|
|
|
|
r.push(*c);
|
|
|
|
|
}
|
|
|
|
|
state = NormalizeUriState::Path;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
match state {
|
|
|
|
|
NormalizeUriState::Percent => {
|
|
|
|
|
r.push(b'%');
|
|
|
|
|
}
|
|
|
|
|
NormalizeUriState::Percent2 => {
|
|
|
|
|
r.push(b'%');
|
|
|
|
|
r.push(prevc);
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Transaction for HTTP2Transaction {
|
|
|
|
|
fn id(&self) -> u64 {
|
|
|
|
|
self.tx_id
|
|
|
|
|
@ -325,6 +391,7 @@ impl HTTP2Transaction {
|
|
|
|
|
stream_id: 0,
|
|
|
|
|
child_stream_id: 0,
|
|
|
|
|
progress: HTTP2Progress::STREAM(HTTP2StreamProgress::init()),
|
|
|
|
|
normalized_uri: Vec::new(),
|
|
|
|
|
to_drop: false,
|
|
|
|
|
frames_tc: Vec::new(),
|
|
|
|
|
frames_ts: Vec::new(),
|
|
|
|
|
@ -389,7 +456,10 @@ impl HTTP2Transaction {
|
|
|
|
|
self.doh = Some(doh);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if block.name.as_ref() == b":path" {
|
|
|
|
|
} else if block.name.as_ref() == b":path" && dir == Direction::ToServer {
|
|
|
|
|
if self.normalized_uri.is_empty() {
|
|
|
|
|
self.normalized_uri = http2_normalize_uri(&block.value);
|
|
|
|
|
}
|
|
|
|
|
path = Some(&block.value);
|
|
|
|
|
} else if block.name.eq_ignore_ascii_case(b":authority") {
|
|
|
|
|
authority = Some(&block.value);
|
|
|
|
|
@ -752,7 +822,7 @@ pub struct HTTP2State {
|
|
|
|
|
response_frame_size: u32,
|
|
|
|
|
dynamic_headers_ts: HTTP2DynTable,
|
|
|
|
|
dynamic_headers_tc: HTTP2DynTable,
|
|
|
|
|
transactions: VecDeque<HTTP2Transaction>,
|
|
|
|
|
pub transactions: VecDeque<HTTP2Transaction>,
|
|
|
|
|
progress: HTTP2ConnectionState,
|
|
|
|
|
|
|
|
|
|
comp_len: u64,
|
|
|
|
|
|