-
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.
Handle files and interfaces uniformly.
Add command-line parsing to allow the specification of an interface name or a filename. Once opened, treat these cap sources in the same way. Made possible by pcap improvements as discussed in rust-pcap/pcap#16.
- Loading branch information
1 parent
0ef9fc4
commit 9f11500
Showing
2 changed files
with
122 additions
and
22 deletions.
There are no files selected for viewing
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 |
---|---|---|
|
@@ -5,7 +5,9 @@ authors = ["Jonathan Anderson <[email protected]>"] | |
|
||
[dependencies] | ||
byteorder = "0.3.11" | ||
pcap = "0.3.2" | ||
docopt = "0.6.70" | ||
pcap = "0.4.2" | ||
rustc-serialize="*" | ||
|
||
[[bin]] | ||
doc = false | ||
|
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,33 +1,131 @@ | ||
extern crate docopt; | ||
extern crate rshark; | ||
extern crate rustc_serialize; | ||
extern crate pcap; | ||
|
||
use pcap::Device; | ||
use docopt::Docopt; | ||
|
||
|
||
fn process(p: &pcap::Packet) { | ||
print!("received {}-B packet:", p.data.len()); | ||
// TODO: use docopt_macros once rust-lang/rust#28089 is resolved | ||
const USAGE: &'static str = " | ||
Usage: rshark [options] <source> | ||
rshark (--help | --version) | ||
match rshark::ethernet::dissect(p.data) { | ||
Err(e) => println!["Error: {}", e], | ||
Ok(p) => print!["{}", p.pretty_print(1)], | ||
} | ||
Options: | ||
-f, --filter BFP filter (see http://biot.com/capstats/bpf.html) | ||
-h, --help Show this message | ||
-p, --promiscuous Listen to all packets | ||
-s, --snaplen=<len> Bytes to capture from each packet [default: 5000] | ||
-t, --timeout=<ms> Packet read timeout, in ms [default: 10] | ||
-v, --version Show the version of rshark | ||
"; | ||
|
||
const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); | ||
|
||
|
||
#[derive(RustcDecodable)] | ||
struct Args { | ||
arg_source: String, | ||
flag_filter: String, | ||
flag_snaplen: i32, | ||
flag_timeout: i32, | ||
flag_promiscuous: bool, | ||
flag_version: bool, | ||
} | ||
|
||
type PcapResult = Result<pcap::Capture<pcap::Activated>, pcap::Error>; | ||
|
||
|
||
fn main() { | ||
let dev = Device::lookup().unwrap(); | ||
println!("Device name: {}", dev.name); | ||
|
||
let mut cap = pcap::Capture::from_device(dev).unwrap() | ||
.promisc(true) | ||
.snaplen(5000) | ||
.timeout(10) | ||
.open().unwrap(); | ||
|
||
loop { | ||
match cap.next() { | ||
Some(ref packet) => { process(packet); }, | ||
None => {}, | ||
} | ||
let args: Args = Docopt::new(USAGE) | ||
.and_then(|d| d.argv(std::env::args()).decode()) | ||
.unwrap_or_else(|e| e.exit()) | ||
; | ||
|
||
if args.flag_version { | ||
println!["rshark v{}", VERSION.unwrap_or("<unknown>")]; | ||
return; | ||
} | ||
|
||
let result = open_capture(&args) | ||
.map(|mut c| { | ||
let mut count = 0; | ||
|
||
while let Some(packet) = c.next() { | ||
println!("received {}-B packet:", packet.data.len()); | ||
|
||
match rshark::ethernet::dissect(packet.data) { | ||
Ok(dissected) => print!["{}", dissected.pretty_print(1)], | ||
Err(e) => println!["Error: {}", e], | ||
} | ||
|
||
count += 1; | ||
} | ||
|
||
count | ||
}) | ||
; | ||
|
||
|
||
match result { | ||
Ok(packet_count) => println!["Processed {} packets", packet_count], | ||
Err(e) => { | ||
println!["{}", e]; | ||
std::process::exit(1); | ||
}, | ||
} | ||
} | ||
|
||
|
||
fn open_capture(args: &Args) -> PcapResult { | ||
let device = try![open_device(args)]; | ||
|
||
let capture = match device { | ||
Some(d) => Ok(d), | ||
None => open_file(&args.arg_source), | ||
}; | ||
|
||
capture.and_then(|mut c| { | ||
try![c.filter(&args.flag_filter)]; | ||
Ok(c) | ||
}) | ||
} | ||
|
||
fn open_device(args: &Args) | ||
-> Result<Option<pcap::Capture<pcap::Activated>>, pcap::Error> { | ||
|
||
match pcap::Device::list() { | ||
Ok(devices) => { | ||
for d in devices { | ||
if d.name == args.arg_source { | ||
return pcap::Capture::from_device(d) | ||
.map(|d| d.promisc(args.flag_promiscuous) | ||
.rfmon(args.flag_promiscuous) | ||
.snaplen(args.flag_snaplen) | ||
.timeout(args.flag_timeout)) | ||
.and_then(|d| d.open()) | ||
.map(|c| c.into()) | ||
.map(Some) | ||
; | ||
} | ||
}; | ||
|
||
Ok(None) | ||
}, | ||
Err(e) => Err(e), | ||
} | ||
} | ||
|
||
fn open_file(filename: &str) -> PcapResult { | ||
std::fs::metadata(filename) | ||
.map_err(|e| pcap::Error::PcapError(format!["{}", e])) | ||
.and_then(|f| | ||
if f.is_file() { | ||
pcap::Capture::from_file(filename) | ||
.map(|c| c.into()) | ||
} else { | ||
Err(pcap::Error::PcapError( | ||
format!["{} is not a file or interface", filename])) | ||
} | ||
) | ||
} |