I'm trying to parse a bunch of network packets captured in the network and perform some analysis. But I'm having a hard time dealing with procotols with dynamic port mechanism...
Some backgrounds on what "dynamic port" is: For some network protocols like FTP, 2 separate TCP channels are used for communication(command channel and data channel). Command channel uses a well-known port (like 21 for FTP), but the port that data channel uses is "dynamic": it's randomly selected by the server and wrapped in the server response for PASV command.
Typical communication procedure:
- client establishes command channel( connects to well-known server port X)
- client asks for the port of the data channel
- server sends back a port Y
- client establishes data channel (connects to server port Y)
- client sends command on command channel(port X), server responses data on data channel(port Y)
When I'm parsing TCP packets, I use src and dst port to decide which parser function to use. To handle this situation, I think I need to keep a HashMap<u16, Parser>
, mapping port to parser functions. But I just cannot make it work(mostly confusing lifetime issues). I think the main problem is: the parser functions will need to update the Hashmap that contains it. But I don't know how to solve this...
Here's what I came up with right now(still does not compile), could anyone offer some better ideas? Am I doing this the wrong way? Thanks!
use bytes::{BufMut, BytesMut};
use nom::number::complete::{be_u16, be_u32};
use nom::IResult;
use nom::bytes::complete::take;
use std::collections::HashMap;
#[derive(Debug)]
struct Command<'a> {
command: &'a [u8],
port: u16,
}
#[derive(Debug)]
struct Data<'a> {
path: &'a [u8],
}
fn parse_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], Command<'a>> {
let (input, command_len) = be_u32(input)?;
let (input, command) = take(command_len as usize)(input)?;
let (input, port) = be_u16(input)?;
Ok((input, Command { command, port }))
}
fn parse_data<'a>(input: &'a [u8]) -> IResult<&'a [u8], Data<'a>> {
let (input, len) = be_u32(input)?;
let (input, path) = take(len as usize)(input)?;
Ok((input, Data { path }))
}
fn make_command_bytes(command: &[u8], data_port: u16) -> BytesMut {
let mut buf = BytesMut::with_capacity(1024);
buf.put_u32(command.len() as u32);
buf.put(&command[..]);
buf.put_u16(data_port);
buf
}
fn make_data_bytes(path: &[u8]) -> BytesMut {
let mut buf = BytesMut::with_capacity(1024);
buf.put_u32(path.len() as u32);
buf.put(&path[..]);
buf
}
#[derive(Debug)]
enum Payload<'a> {
Command(Command<'a>),
Data(Data<'a>),
}
trait Parser<'a> {
type Output;
fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], Self::Output>;
}
struct CommandParser<'a, 'b> {
map: &'b mut HashMap<u16, Box<dyn Parser<'a, Output = Payload<'a>>>>,
}
struct DataParser<'a, 'b> {
map: &'b mut HashMap<u16, Box<dyn Parser<'a, Output = Payload<'a>>>>,
}
impl<'a, 'b> Parser<'a> for CommandParser<'a, 'b> {
type Output = Payload<'a>;
fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], Self::Output> {
let (input, command) = parse_command(input)?;
self.map
.insert(command.port, Box::new(DataParser { map: self.map }))
.unwrap();
Ok((input, Payload::Command(command)))
}
}
impl<'a, 'b> Parser<'a> for DataParser<'a, 'b> {
type Output = Payload<'a>;
fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], Self::Output> {
let (input, data) = parse_data(input)?;
Ok((input, Payload::Data(data)))
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let data_port = 1234;
let command = b"test";
let data = make_command_bytes(&command[..], data_port);
let mut parser_map = HashMap::<u16, Box<dyn Parser<Output = Payload>>>::new();
parser_map.insert(21, Box::new(CommandParser { map: &parser_map }));
let parser = parser_map.get(&21).unwrap();
match parser.parse(&data) {
Ok((input, result)) => {
println!("result: {:?}, remain: {:?}", result, input);
}
Err(e) => {
println!("{:?}", e);
}
}
Ok(())
}
the compiler says:
--> src/main.rs:91:57
|
91 | parser_map.insert(21, Box::new(CommandParser { map: &parser_map }));
| ^^^^^^^^^^^ types differ in mutability
|
= note: expected mutable reference `&mut HashMap<u16, Box<(dyn Parser<'_, Output = Payload<'_>> + 'static)>>`
found reference `&HashMap<u16, Box<dyn Parser<'_, Output = Payload<'_>>>>`
where does + 'static
come from?