use nom::bytes::streaming::take;
use nom::combinator::{cond, map, map_parser, rest};
use nom::error::{make_error, ErrorKind};
use nom::multi::count;
use nom::number::streaming::{be_u16, be_u32, be_u64, be_u8};
use nom::{Err, IResult};
use rusticata_macros::newtype_enum;
#[derive(Debug, PartialEq)]
pub struct OpenVPNPacket<'a> {
pub hdr: OpenVPNHdr,
pub msg: Payload<'a>,
}
#[derive(Debug, PartialEq)]
pub struct OpenVPNHdr {
pub plen: Option<u16>,
pub opcode: Opcode,
pub key: u8,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Opcode(pub u8);
newtype_enum! {
impl debug Opcode {
P_CONTROL_HARD_RESET_CLIENT_V1 = 0x1,
P_CONTROL_HARD_RESET_SERVER_V1 = 0x2,
P_CONTROL_SOFT_RESET_V1 = 0x3,
P_CONTROL_V1 = 0x4,
P_ACK_V1 = 0x5,
P_DATA_V1 = 0x6,
P_CONTROL_HARD_RESET_CLIENT_V2 = 0x7,
P_CONTROL_HARD_RESET_SERVER_V2 = 0x8,
P_DATA_V2 = 0x9,
}
}
#[derive(Debug, PartialEq)]
pub enum Payload<'a> {
Control(PControl<'a>),
Ack(PAck<'a>),
Data(PData<'a>),
}
#[derive(Debug, PartialEq)]
pub struct PControl<'a> {
pub session_id: u64,
pub hmac: &'a [u8],
pub packet_id: u32,
pub net_time: u32,
pub msg_ar_len: u8,
pub msg_ar: Option<Vec<u32>>,
pub msg_packet_id: u32,
pub remote_session_id: Option<u64>,
pub payload: &'a [u8],
}
#[derive(Debug, PartialEq)]
pub struct PAck<'a> {
pub session_id: u64,
pub hmac: &'a [u8],
pub packet_id: u32,
pub net_time: u32,
pub msg_ar_len: u8,
pub msg_ar: Option<Vec<u32>>,
pub remote_session_id: Option<u64>,
}
#[derive(Debug, PartialEq)]
pub struct PData<'a> {
pub contents: &'a [u8],
}
pub fn parse_openvpn_tcp(i: &[u8]) -> IResult<&[u8], OpenVPNPacket> {
let (i, hdr) = parse_openvpn_header_tcp(i)?;
let plen = match hdr.plen {
Some(plen) if plen >= 2 => plen,
_ => return Err(Err::Error(make_error(i, ErrorKind::LengthValue))),
};
let (i, msg) = map_parser(take(plen - 1), parse_openvpn_msg_payload(hdr.opcode))(i)?;
Ok((i, OpenVPNPacket { hdr, msg }))
}
pub fn parse_openvpn_udp(i: &[u8]) -> IResult<&[u8], OpenVPNPacket> {
let (i, hdr) = parse_openvpn_header_udp(i)?;
let (i, msg) = parse_openvpn_msg_payload(hdr.opcode)(i)?;
Ok((i, OpenVPNPacket { hdr, msg }))
}
pub fn parse_openvpn_header_tcp(i: &[u8]) -> IResult<&[u8], OpenVPNHdr> {
let (i, plen) = be_u16(i)?;
let (i, opcode_and_key) = be_u8(i)?;
let opcode = Opcode(opcode_and_key >> 3);
let key = opcode_and_key & 0b111;
let hdr = OpenVPNHdr {
plen: Some(plen),
opcode,
key,
};
Ok((i, hdr))
}
pub fn parse_openvpn_header_udp(i: &[u8]) -> IResult<&[u8], OpenVPNHdr> {
let (i, opcode_and_key) = be_u8(i)?;
let opcode = Opcode(opcode_and_key >> 3);
let key = opcode_and_key & 0b111;
let hdr = OpenVPNHdr {
plen: None,
opcode,
key,
};
Ok((i, hdr))
}
pub fn parse_openvpn_msg_payload(msg_type: Opcode) -> impl FnMut(&[u8]) -> IResult<&[u8], Payload> {
move |i| match msg_type {
Opcode::P_CONTROL_HARD_RESET_CLIENT_V1
| Opcode::P_CONTROL_HARD_RESET_SERVER_V1
| Opcode::P_CONTROL_SOFT_RESET_V1
| Opcode::P_CONTROL_V1
| Opcode::P_CONTROL_HARD_RESET_CLIENT_V2
| Opcode::P_CONTROL_HARD_RESET_SERVER_V2 => {
map(parse_openvpn_msg_pcontrol, Payload::Control)(i)
}
Opcode::P_ACK_V1 => map(parse_openvpn_msg_pack, Payload::Ack)(i),
Opcode::P_DATA_V1 | Opcode::P_DATA_V2 => {
map(rest, |x| Payload::Data(PData { contents: x }))(i)
}
_ => Err(::nom::Err::Error(make_error(i, ErrorKind::Tag))),
}
}
pub fn parse_openvpn_msg_pcontrol(i: &[u8]) -> IResult<&[u8], PControl> {
let (i, session_id) = be_u64(i)?;
let (i, hmac) = take(20usize)(i)?;
let (i, packet_id) = be_u32(i)?;
let (i, net_time) = be_u32(i)?;
let (i, msg_ar_len) = be_u8(i)?;
let (i, msg_ar) = cond(msg_ar_len > 0, count(be_u32, msg_ar_len as usize))(i)?;
let (i, remote_session_id) = cond(msg_ar_len > 0, be_u64)(i)?;
let (i, msg_packet_id) = be_u32(i)?;
let (i, payload) = rest(i)?;
let pcontrol = PControl {
session_id,
hmac,
packet_id,
net_time,
msg_ar_len,
msg_ar,
remote_session_id,
msg_packet_id,
payload,
};
Ok((i, pcontrol))
}
pub fn parse_openvpn_msg_pack(i: &[u8]) -> IResult<&[u8], PAck> {
let (i, session_id) = be_u64(i)?;
let (i, hmac) = take(20usize)(i)?;
let (i, packet_id) = be_u32(i)?;
let (i, net_time) = be_u32(i)?;
let (i, msg_ar_len) = be_u8(i)?;
let (i, msg_ar) = cond(msg_ar_len > 0, count(be_u32, msg_ar_len as usize))(i)?;
let (i, remote_session_id) = cond(msg_ar_len > 0, be_u64)(i)?;
let pack = PAck {
session_id,
hmac,
packet_id,
net_time,
msg_ar_len,
msg_ar,
remote_session_id,
};
Ok((i, pack))
}