-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
567 additions
and
12 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod sniffer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,20 @@ | ||
#[tokio::main] | ||
async fn main() { | ||
warp::serve(warp::fs::dir("client/dist")) | ||
.run(([127, 0, 0, 1], 3550)) | ||
.await; | ||
// #[tokio::main] | ||
// async fn main() { | ||
// warp::serve(warp::fs::dir("client/dist")) | ||
// .run(([127, 0, 0, 1], 3550)) | ||
// .await; | ||
// } | ||
|
||
use rtpeeker::sniffer; | ||
|
||
fn main() { | ||
let Ok(mut sniffer) = sniffer::Sniffer::from_file("./pcap_examples/rtp.pcap") else { | ||
println!("Cannot open file"); | ||
return; | ||
}; | ||
|
||
while let Ok(mut packet) = sniffer.next_packet() { | ||
packet.parse_as(sniffer::packet::PacketType::RtpOverUdp); | ||
println!("{:?}", packet); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
use packet::Packet; | ||
use std::result; | ||
|
||
pub mod packet; | ||
pub mod rtcp; | ||
pub mod rtp; | ||
|
||
pub enum Error { | ||
FileNotFound, | ||
DeviceNotFound, | ||
DeviceUnavailable, | ||
CouldntReceivePacket, | ||
UnsupportedPacketType, | ||
} | ||
|
||
type Result<T> = result::Result<T, Error>; | ||
|
||
pub struct Sniffer<T: pcap::State> { | ||
capture: pcap::Capture<T>, | ||
} | ||
|
||
impl Sniffer<pcap::Offline> { | ||
pub fn from_file(file: &str) -> Result<Self> { | ||
match pcap::Capture::from_file(file) { | ||
Ok(capture) => Ok(Self { capture }), | ||
Err(_) => Err(Error::FileNotFound), | ||
} | ||
} | ||
} | ||
|
||
impl Sniffer<pcap::Active> { | ||
pub fn from_device(device: &str) -> Result<Self> { | ||
let Ok(capture) = pcap::Capture::from_device(device) else { | ||
return Err(Error::DeviceNotFound); | ||
}; | ||
|
||
match capture.open() { | ||
Ok(capture) => Ok(Self { capture }), | ||
Err(_) => Err(Error::DeviceUnavailable), | ||
} | ||
} | ||
} | ||
|
||
impl<T: pcap::Activated> Sniffer<T> { | ||
pub fn next_packet(&mut self) -> Result<Packet> { | ||
let Ok(packet) = self.capture.next_packet() else { | ||
return Err(Error::CouldntReceivePacket); | ||
}; | ||
|
||
match Packet::build(&packet) { | ||
Some(packet) => Ok(packet), | ||
None => Err(Error::UnsupportedPacketType), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
use super::{rtcp::RtcpPacket, rtp::RtpPacket}; | ||
use etherparse::{ | ||
IpHeader::{self, Version4, Version6}, | ||
Ipv4Header, Ipv6Header, PacketHeaders, TcpHeader, | ||
TransportHeader::{self, Tcp, Udp}, | ||
UdpHeader, | ||
}; | ||
use std::net::{ | ||
IpAddr::{V4, V6}, | ||
Ipv4Addr, Ipv6Addr, SocketAddr, | ||
}; | ||
use std::ops::Add; | ||
use std::time::Duration; | ||
|
||
#[derive(Debug)] | ||
pub enum PacketType { | ||
RtpOverUdp, | ||
RtcpOverUdp, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum TransportProtocol { | ||
Tcp, | ||
Udp, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum SessionPacket { | ||
Unknown, | ||
Rtp(RtpPacket), | ||
Rtcp(Vec<RtcpPacket>), | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Packet { | ||
pub payload: Vec<u8>, | ||
pub timestamp: Duration, | ||
pub length: u32, | ||
pub source_addr: SocketAddr, | ||
pub destination_addr: SocketAddr, | ||
pub transport_protocol: TransportProtocol, | ||
pub contents: SessionPacket, | ||
} | ||
|
||
impl Packet { | ||
pub fn build(raw_packet: &pcap::Packet) -> Option<Self> { | ||
let Ok(packet) = PacketHeaders::from_ethernet_slice(raw_packet) else { | ||
return None; | ||
}; | ||
let PacketHeaders { | ||
ip: Some(ip), | ||
transport: Some(transport), | ||
.. | ||
} = packet else { | ||
return None; | ||
}; | ||
|
||
let transport_protocol = get_transport_protocol(&transport)?; | ||
let (source_addr, destination_addr) = convert_addr(&ip, &transport)?; | ||
let duration = get_duration(raw_packet); | ||
|
||
Some(Self { | ||
payload: packet.payload.to_vec(), | ||
length: raw_packet.header.len, | ||
timestamp: duration, | ||
source_addr, | ||
destination_addr, | ||
transport_protocol, | ||
contents: SessionPacket::Unknown, | ||
}) | ||
} | ||
|
||
pub fn parse_as(&mut self, packet_type: PacketType) { | ||
if let PacketType::RtpOverUdp = packet_type { | ||
let Some(rtp) = RtpPacket::build(self) else { | ||
return; | ||
}; | ||
self.contents = SessionPacket::Rtp(rtp); | ||
} | ||
} | ||
} | ||
|
||
fn get_transport_protocol(transport: &TransportHeader) -> Option<TransportProtocol> { | ||
let protocol = match transport { | ||
Udp(_) => TransportProtocol::Udp, | ||
Tcp(_) => TransportProtocol::Tcp, | ||
_ => return None, | ||
}; | ||
|
||
Some(protocol) | ||
} | ||
|
||
fn get_duration(raw_packet: &pcap::Packet) -> Duration { | ||
// i64 -> u64, but seconds should never be negative | ||
let secs = raw_packet.header.ts.tv_sec.try_into().unwrap(); | ||
let micrs = raw_packet.header.ts.tv_usec.try_into().unwrap(); | ||
|
||
let sec_duration = Duration::from_secs(secs); | ||
let micros_duration = Duration::from_micros(micrs); | ||
|
||
sec_duration.add(micros_duration) | ||
} | ||
|
||
fn convert_addr( | ||
ip_header: &IpHeader, | ||
transport: &TransportHeader, | ||
) -> Option<(SocketAddr, SocketAddr)> { | ||
let (source_port, dest_port) = match *transport { | ||
Udp(UdpHeader { | ||
source_port, | ||
destination_port, | ||
.. | ||
}) | ||
| Tcp(TcpHeader { | ||
source_port, | ||
destination_port, | ||
.. | ||
}) => (source_port, destination_port), | ||
_ => return None, | ||
}; | ||
|
||
let (source_ip_addr, dest_ip_addr) = match *ip_header { | ||
Version4( | ||
Ipv4Header { | ||
source: [s0, s1, s2, s3], | ||
destination: [d0, d1, d2, d3], | ||
.. | ||
}, | ||
_, | ||
) => { | ||
let source = V4(Ipv4Addr::new(s0, s1, s2, s3)); | ||
let destination = V4(Ipv4Addr::new(d0, d1, d2, d3)); | ||
(source, destination) | ||
} | ||
Version6( | ||
Ipv6Header { | ||
source, | ||
destination, | ||
.. | ||
}, | ||
_, | ||
) => { | ||
let s = to_u16(&source); | ||
let d = to_u16(&destination); | ||
|
||
let source = V6(Ipv6Addr::new( | ||
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], | ||
)); | ||
let destination = V6(Ipv6Addr::new( | ||
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], | ||
)); | ||
(source, destination) | ||
} | ||
}; | ||
let source = SocketAddr::new(source_ip_addr, source_port); | ||
let destination = SocketAddr::new(dest_ip_addr, dest_port); | ||
Some((source, destination)) | ||
} | ||
|
||
pub fn to_u16(buf: &[u8; 16]) -> [u16; 8] { | ||
// TODO: tests | ||
buf.iter() | ||
.zip(buf.iter().skip(1)) | ||
.map(|(a, b)| ((*a as u16) << 8) | *b as u16) | ||
.collect::<Vec<_>>() | ||
.as_slice() | ||
.try_into() | ||
.unwrap() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
use rtcp::packet; | ||
|
||
#[derive(Debug)] | ||
pub struct RtcpPacket {} | ||
|
||
impl RtcpPacket { | ||
pub fn build(packet: &super::Packet) -> Option<Self> { | ||
let mut buffer: &[u8] = &packet.payload; | ||
let Ok(_rtcp_packets) = packet::unmarshal(&mut buffer) else { | ||
return None; | ||
}; | ||
|
||
// TODO proper mapping of different RTCP packet types | ||
Some(Self {}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
use rtp::packet::Packet; | ||
use webrtc_util::marshal::Unmarshal; | ||
|
||
#[derive(Debug)] | ||
pub struct RtpPacket { | ||
pub version: u8, | ||
pub padding: bool, | ||
pub extension: bool, | ||
pub marker: bool, | ||
pub payload_type: u8, | ||
pub sequence_number: u16, | ||
pub timestamp: u32, | ||
pub ssrc: u32, | ||
pub csrc: Vec<u32>, | ||
pub payload_length: usize, // extension information skipped | ||
} | ||
|
||
impl RtpPacket { | ||
pub fn build(packet: &super::Packet) -> Option<Self> { | ||
let mut buffer: &[u8] = &packet.payload; | ||
let Ok(Packet { header, payload }) = Packet::unmarshal(&mut buffer) else { | ||
return None; | ||
}; | ||
|
||
Some(Self { | ||
version: header.version, | ||
padding: header.padding, | ||
extension: header.extension, | ||
marker: header.marker, | ||
payload_type: header.payload_type, | ||
sequence_number: header.sequence_number, | ||
timestamp: header.timestamp, | ||
ssrc: header.ssrc, | ||
csrc: header.csrc, | ||
payload_length: payload.len(), | ||
}) | ||
} | ||
} |