You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/rust/src/websocket/parser.rs

98 lines
2.8 KiB
Rust

/* Copyright (C) 2023 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use nom7::bytes::streaming::take;
use nom7::combinator::cond;
use nom7::number::streaming::{be_u16, be_u32, be_u64, be_u8};
use nom7::IResult;
use suricata_derive::EnumStringU8;
#[derive(Clone, Debug, Default, EnumStringU8)]
#[repr(u8)]
pub enum WebSocketOpcode {
#[default]
Continuation = 0,
Text = 1,
Binary = 2,
Close = 8,
Ping = 9,
Pong = 10,
}
#[derive(Clone, Debug, Default)]
pub struct WebSocketPdu {
pub flags: u8,
pub fin: bool,
pub compress: bool,
pub opcode: u8,
pub mask: Option<u32>,
pub payload: Vec<u8>,
pub to_skip: u64,
}
// cf rfc6455#section-5.2
pub fn parse_message(i: &[u8], max_pl_size: u32) -> IResult<&[u8], WebSocketPdu> {
let (i, flags_op) = be_u8(i)?;
let fin = (flags_op & 0x80) != 0;
let compress = (flags_op & 0x40) != 0;
let flags = flags_op & 0xF0;
let opcode = flags_op & 0xF;
let (i, mask_plen) = be_u8(i)?;
let mask_flag = (mask_plen & 0x80) != 0;
let (i, payload_len) = match mask_plen & 0x7F {
126 => {
let (i, val) = be_u16(i)?;
Ok((i, val.into()))
}
127 => be_u64(i),
_ => Ok((i, (mask_plen & 0x7F).into())),
}?;
let (i, xormask) = cond(mask_flag, take(4usize))(i)?;
let mask = if mask_flag {
let (_, m) = be_u32(xormask.unwrap())?;
Some(m)
} else {
None
};
// we limit payload_len to u32, so as to build on 32-bit system
// where we cannot take(usize) with a u64
let (to_skip, payload_len) = if payload_len < max_pl_size.into() {
(0, payload_len as u32)
} else {
(payload_len - (max_pl_size as u64), max_pl_size)
};
let (i, payload_raw) = take(payload_len)(i)?;
let mut payload = payload_raw.to_vec();
if let Some(xorkey) = xormask {
for i in 0..payload.len() {
payload[i] ^= xorkey[i % 4];
}
}
Ok((
i,
WebSocketPdu {
flags,
fin,
compress,
opcode,
mask,
payload,
to_skip,
},
))
}