1extern crate libc;
50
51use unique::Unique;
52use std::marker::PhantomData;
53use std::ptr;
54use std::ffi::{CStr,CString};
55use std::path::Path;
56use std::slice;
57use std::ops::Deref;
58use std::mem::transmute;
59use std::str;
60use std::fmt;
61use self::Error::*;
62#[cfg(not(windows))]
63use std::os::unix::io::{RawFd, AsRawFd};
64
65pub use raw::PacketHeader;
66
67mod raw;
68mod unique;
69
70const PCAP_ERROR_NOT_ACTIVATED: i32 = -3;
71const PCAP_ERRBUF_SIZE: usize = 256;
72
73#[derive(Debug)]
75pub enum Error {
76 MalformedError(str::Utf8Error),
77 InvalidString,
78 PcapError(String),
79 InvalidLinktype,
80 TimeoutExpired,
81 NoMorePackets,
82 InsufficientMemory,
83}
84
85impl Error {
86 fn new<T>(ptr: *const libc::c_char) -> Result<T, Error> {
87 Err(PcapError(try!(cstr_to_string(ptr))))
88 }
89}
90
91impl fmt::Display for Error {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 match *self {
94 MalformedError(e) => {
95 write!(f, "pcap returned a string that was not encoded properly: {}", e)
96 },
97 InvalidString => {
98 write!(f, "pcap returned an invalid (null) string")
99 },
100 PcapError(ref e) => {
101 write!(f, "pcap error: {}", e)
102 },
103 InvalidLinktype => {
104 write!(f, "invalid or unknown linktype")
105 },
106 TimeoutExpired => {
107 write!(f, "timeout expired")
108 },
109 NoMorePackets => {
110 write!(f, "no more packets to read from the file")
111 },
112 InsufficientMemory => {
113 write!(f, "insufficient memory")
114 },
115 }
116 }
117}
118
119impl std::error::Error for Error {
120 fn description(&self) -> &str {
121 match *self {
122 MalformedError(..) => "message from pcap is not encoded properly",
123 PcapError(..) => "pcap FFI error",
124 InvalidString => "pcap returned an invalid (null) string",
125 InvalidLinktype => "invalid or unknown linktype",
126 TimeoutExpired => "pcap was reading from a live capture and the timeout expired",
127 NoMorePackets => "pcap was reading from a file and there were no more packets to read",
128 InsufficientMemory => "insufficient memory",
129 }
130 }
131
132 fn cause(&self) -> Option<&std::error::Error> {
133 match *self {
134 MalformedError(ref e) => Some(e),
135 _ => None
136 }
137 }
138}
139
140impl From<str::Utf8Error> for Error {
141 fn from(obj: str::Utf8Error) -> Error {
142 MalformedError(obj)
143 }
144}
145
146#[derive(Debug)]
147pub struct Device {
149 pub name: String,
150 pub desc: Option<String>
151}
152
153impl Device {
154 pub fn open(self) -> Result<Capture<Active>, Error> {
156 Ok(try!(try!(Capture::from_device(self)).open()))
157 }
158
159 pub fn lookup() -> Result<Device, Error> {
162 let mut errbuf = [0i8; PCAP_ERRBUF_SIZE];
163
164 unsafe {
165 let default_name = raw::pcap_lookupdev(errbuf.as_mut_ptr() as *mut _);
166 println!("{:?}",cstr_to_string(default_name));
167 if default_name.is_null() {
168 return Error::new(errbuf.as_ptr() as *const _);
169 }
170
171 Ok(Device {
172 name: try!(cstr_to_string(default_name)),
173 desc: None
174 })
175 }
176 }
177
178 pub fn list() -> Result<Vec<Device>, Error> {
180 unsafe {
181 let mut errbuf = [0i8; PCAP_ERRBUF_SIZE];
182 let mut dev_buf: *mut raw::Struct_pcap_if = ptr::null_mut();
183 let mut ret = vec![];
184
185 match raw::pcap_findalldevs(&mut dev_buf, errbuf.as_mut_ptr() as *mut _) {
186 0 => {
187 let mut cur = dev_buf;
188
189 while !cur.is_null() {
190 ret.push(Device {
191 name: cstr_to_string((&*cur).name).unwrap(),
192 desc: {
193 if !(&*cur).description.is_null() {
194 Some(cstr_to_string((&*cur).description).unwrap())
195 } else {
196 None
197 }
198 }
199 });
200
201 cur = (&*cur).next;
202 }
203
204 raw::pcap_freealldevs(dev_buf);
205
206 Ok(ret)
207 },
208 _ => {
209 Error::new(errbuf.as_ptr() as *mut _)
210 }
211 }
212 }
213 }
214}
215
216impl<'a> Into<Device> for &'a str {
217 fn into(self) -> Device {
218 Device {
219 name: self.into(),
220 desc: None
221 }
222 }
223}
224
225#[derive(Debug, PartialEq, Eq)]
230pub struct Linktype(pub i32);
231
232impl Linktype {
233 pub fn get_name(&self) -> Result<String, Error> {
235 unsafe {
236 let name = raw::pcap_datalink_val_to_name(self.0);
237
238 if name.is_null() {
239 return Err(InvalidLinktype)
240 } else {
241 Ok(try!(cstr_to_string(name)))
242 }
243 }
244 }
245
246 pub fn get_description(&self) -> Result<String, Error> {
248 unsafe {
249 let description = raw::pcap_datalink_val_to_description(self.0);
250
251 if description.is_null() {
252 return Err(InvalidLinktype)
253 } else {
254 Ok(try!(cstr_to_string(description)))
255 }
256 }
257 }
258}
259
260#[derive(Debug)]
262pub struct Packet<'a> {
263 pub header: &'a PacketHeader,
264 pub data: &'a [u8]
265}
266
267impl<'b> Deref for Packet<'b> {
268 type Target = [u8];
269
270 fn deref(&self) -> &[u8] {
271 self.data
272 }
273}
274
275#[derive(Debug, Clone, Copy)]
276pub struct Stat {
277 pub received: u32,
278 pub dropped: u32,
279 pub if_dropped: u32
280}
281
282pub enum Precision {
283 Micro,
284 Nano,
285}
286
287pub enum Inactive {}
289pub enum Active {}
291pub enum Offline {}
294pub enum Dead {}
298
299pub unsafe trait Activated: State {}
300
301unsafe impl Activated for Active {}
302unsafe impl Activated for Offline {}
303unsafe impl Activated for Dead {}
304
305pub unsafe trait State {}
310
311unsafe impl State for Inactive {}
312unsafe impl State for Active {}
313unsafe impl State for Offline {}
314unsafe impl State for Dead {}
315
316pub struct Capture<T: State + ?Sized> {
348 handle: Unique<raw::pcap_t>,
349 _marker: PhantomData<T>
350}
351
352impl Capture<Offline> {
353 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Capture<Offline>, Error> {
355 let name = CString::new(path.as_ref().to_str().unwrap()).unwrap();
356 let mut errbuf = [0i8; PCAP_ERRBUF_SIZE];
357
358 unsafe {
359 let handle = raw::pcap_open_offline(name.as_ptr(), errbuf.as_mut_ptr() as *mut _);
360 if handle.is_null() {
361 return Error::new(errbuf.as_ptr() as *mut _);
362 }
363
364 Ok(Capture {
365 handle: Unique::new(handle),
366 _marker: PhantomData
367 })
368 }
369 }
370
371 pub fn from_file_with_precision<P: AsRef<Path>>(path: P, precision: Precision) -> Result<Capture<Offline>, Error> {
374 let name = CString::new(path.as_ref().to_str().unwrap()).unwrap();
375 let mut errbuf = [0i8; PCAP_ERRBUF_SIZE];
376
377 unsafe {
378 let handle = raw::pcap_open_offline_with_tstamp_precision(name.as_ptr(), match precision {
379 Precision::Micro => 0,
380 Precision::Nano => 1,
381 }, errbuf.as_mut_ptr() as *mut _);
382 if handle.is_null() {
383 return Error::new(errbuf.as_ptr() as *const _);
384 }
385
386 Ok(Capture {
387 handle: Unique::new(handle),
388 _marker: PhantomData
389 })
390 }
391 }
392}
393
394pub enum TstampType {
395 Host,
396 HostLowPrec,
397 HostHighPrec,
398 Adapter,
399 AdapterUnsynced,
400}
401
402pub enum Direction {
403 InOut,
404 In,
405 Out,
406}
407
408impl Capture<Inactive> {
409 pub fn from_device<D: Into<Device>>(device: D) -> Result<Capture<Inactive>, Error> {
412 let device: Device = device.into();
413 let name = CString::new(device.name).unwrap();
414 let mut errbuf = [0i8; PCAP_ERRBUF_SIZE];
415
416 unsafe {
417 let handle = raw::pcap_create(name.as_ptr(), errbuf.as_mut_ptr() as *mut _);
418 if handle.is_null() {
419 return Error::new(errbuf.as_ptr() as *const _);
420 }
421
422 Ok(Capture {
423 handle: Unique::new(handle),
424 _marker: PhantomData
425 })
426 }
427 }
428
429 pub fn open(self) -> Result<Capture<Active>, Error> {
432 unsafe {
433 let cap = transmute::<Capture<Inactive>, Capture<Active>>(self);
434
435 if 0 != raw::pcap_activate(*cap.handle) {
436 return Error::new(raw::pcap_geterr(*cap.handle));
437 }
438
439 Ok(cap)
440 }
441 }
442
443 pub fn timeout(self, ms: i32) -> Capture<Inactive> {
446 unsafe {
447 raw::pcap_set_timeout(*self.handle, ms);
448 self
449 }
450 }
451
452 #[cfg(not(windows))]
454 pub fn tstamp_type(self, t: TstampType) -> Capture<Inactive> {
455 unsafe {
456 raw::pcap_set_tstamp_type(*self.handle, match t {
457 TstampType::Host => 0,
458 TstampType::HostLowPrec => 1,
459 TstampType::HostHighPrec => 2,
460 TstampType::Adapter => 3,
461 TstampType::AdapterUnsynced => 4,
462 });
463 self
464 }
465 }
466
467 pub fn promisc(self, to: bool) -> Capture<Inactive> {
469 unsafe {
470 raw::pcap_set_promisc(*self.handle, if to {1} else {0});
471 self
472 }
473 }
474
475 #[cfg(not(target_os = "windows"))]
479 pub fn rfmon(self, to: bool) -> Capture<Inactive> {
480 unsafe {
481 raw::pcap_set_rfmon(*self.handle, if to {1} else {0});
482 self
483 }
484 }
485
486 pub fn buffer_size(self, to: i32) -> Capture<Inactive> {
490 unsafe {
491 raw::pcap_set_buffer_size(*self.handle, to);
492 self
493 }
494 }
495
496 #[cfg(not(windows))]
498 pub fn precision(self, precision: Precision) -> Capture<Inactive> {
499 unsafe {
500 raw::pcap_set_tstamp_precision(*self.handle, match precision {
501 Precision::Micro => 0,
502 Precision::Nano => 1,
503 });
504 self
505 }
506 }
507
508 pub fn snaplen(self, to: i32) -> Capture<Inactive> {
513 unsafe {
514 raw::pcap_set_snaplen(*self.handle, to);
515 self
516 }
517 }
518}
519
520impl<T: Activated + ?Sized> Capture<T> {
522 pub fn list_datalinks(&self) -> Result<Vec<Linktype>, Error> {
524 unsafe {
525 let mut links: *mut i32 = ptr::null_mut();
526
527 let num = raw::pcap_list_datalinks(*self.handle, &mut links);
528
529 if num == PCAP_ERROR_NOT_ACTIVATED {
530 raw::pcap_free_datalinks(links);
531 panic!("It should not be possible to run list_datalinks on a Capture that is not activated, please report this bug!")
532 } else if num < 0 {
533 raw::pcap_free_datalinks(links);
534 Error::new(raw::pcap_geterr(*self.handle))
535 } else {
536 let slice = slice::from_raw_parts(links, num as usize).iter().map(|&a| Linktype(a)).collect();
537 raw::pcap_free_datalinks(links);
538
539 Ok(slice)
540 }
541 }
542 }
543
544 pub fn set_datalink(&mut self, linktype: Linktype) -> Result<(), Error> {
546 unsafe {
547 match raw::pcap_set_datalink(*self.handle, linktype.0) {
548 0 => {
549 Ok(())
550 },
551 _ => {
552 Error::new(raw::pcap_geterr(*self.handle))
553 }
554 }
555 }
556 }
557
558 pub fn get_datalink(&self) -> Linktype {
560 unsafe {
561 match raw::pcap_datalink(*self.handle) {
562 PCAP_ERROR_NOT_ACTIVATED => {
563 panic!("It should not be possible to run get_datalink on a Capture that is not activated, please report this bug!");
564 },
565 lt => {
566 Linktype(lt)
567 }
568 }
569 }
570 }
571
572 pub fn savefile<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
575 let name = CString::new(path.as_ref().to_str().unwrap()).unwrap();
576 unsafe {
577 let handle = raw::pcap_dump_open(*self.handle, name.as_ptr());
578
579 if handle.is_null() {
580 Error::new(raw::pcap_geterr(*self.handle))
581 } else {
582 Ok(Savefile {
583 handle: Unique::new(handle)
584 })
585 }
586 }
587 }
588
589 pub fn direction(&self, direction: Direction) -> Result<(), Error> {
591 let result = unsafe {
592 raw::pcap_setdirection(*self.handle, match direction {
593 Direction::InOut => raw::PCAP_D_INOUT,
594 Direction::In => raw::PCAP_D_IN,
595 Direction::Out => raw::PCAP_D_OUT,
596 })
597 };
598 if result == 0 {
599 Ok(())
600 } else {
601 Error::new( unsafe { raw::pcap_geterr(*self.handle) })
602 }
603 }
604
605 pub fn next<'a>(&'a mut self) -> Result<Packet<'a>, Error> {
612 unsafe {
613 let mut header: *mut raw::Struct_pcap_pkthdr = ptr::null_mut();
614 let mut packet: *const libc::c_uchar = ptr::null();
615 match raw::pcap_next_ex(*self.handle, &mut header, &mut packet) {
616 i if i >= 1 => {
617 Ok(Packet {
619 header: transmute(&*header),
620 data: slice::from_raw_parts(packet, (&*header).caplen as usize)
621 })
622 },
623 0 => {
624 Err(TimeoutExpired)
627 },
628 -1 => {
629 Error::new(raw::pcap_geterr(*self.handle))
631 },
632 -2 => {
633 Err(NoMorePackets)
636 },
637 _ => {
638 unreachable!()
640 }
641 }
642 }
643 }
644
645 pub fn filter(&mut self, program: &str) -> Result<(), Error> {
650 let program = CString::new(program).unwrap();
651
652 unsafe {
653 let mut bpf_program: raw::Struct_bpf_program = Default::default();
654
655 if -1 == raw::pcap_compile(*self.handle, &mut bpf_program, program.as_ptr(), 0, 0) {
656 return Error::new(raw::pcap_geterr(*self.handle));
657 }
658
659 if -1 == raw::pcap_setfilter(*self.handle, &mut bpf_program) {
660 raw::pcap_freecode(&mut bpf_program);
661 return Error::new(raw::pcap_geterr(*self.handle));
662 }
663
664 raw::pcap_freecode(&mut bpf_program);
665 Ok(())
666 }
667 }
668
669 pub fn stats(&mut self) -> Result<Stat, Error> {
670 unsafe {
671 let mut stats: raw::Struct_pcap_stat =
672 raw::Struct_pcap_stat {ps_recv: 0, ps_drop: 0, ps_ifdrop: 0};
673
674 if -1 == raw::pcap_stats(*self.handle, &mut stats) {
675 return Error::new(raw::pcap_geterr(*self.handle));
676 }
677
678 Ok(Stat {
679 received: stats.ps_recv,
680 dropped: stats.ps_drop,
681 if_dropped: stats.ps_ifdrop
682 })
683 }
684 }
685}
686
687impl Capture<Active> {
688 pub fn sendpacket<'a>(&mut self, buf: &'a [u8]) -> Result<(), Error> {
690 unsafe {
691 let result = raw::pcap_sendpacket(*self.handle, buf.as_ptr() as *const _, buf.len() as i32);
692
693 match result {
694 -1 => {
695 return Error::new(raw::pcap_geterr(*self.handle));
696 },
697 _ => {
698 Ok(())
699 }
700 }
701 }
702 }
703}
704
705impl Capture<Dead> {
706 pub fn dead(linktype: Linktype) -> Result<Capture<Dead>, Error> {
708 unsafe {
709 let handle = raw::pcap_open_dead(linktype.0, 65535);
710 if handle.is_null() {
711 return Err(Error::InsufficientMemory);
712 }
713
714 Ok(Capture {
715 handle: Unique::new(handle),
716 _marker: PhantomData
717 })
718 }
719 }
720}
721
722#[cfg(not(windows))]
723impl AsRawFd for Capture<Active> {
724 fn as_raw_fd(&self) -> RawFd {
725 unsafe {
726 let fd = raw::pcap_fileno(*self.handle);
727
728 match fd {
729 -1 => {
730 panic!("Unable to get file descriptor for live capture");
731 },
732 fd => {
733 fd
734 }
735 }
736 }
737 }
738}
739
740impl<T: State + ?Sized> Drop for Capture<T> {
741 fn drop(&mut self) {
742 unsafe {
743 raw::pcap_close(*self.handle)
744 }
745 }
746}
747
748impl<T: Activated> From<Capture<T>> for Capture<Activated> {
749 fn from(cap: Capture<T>) -> Capture<Activated> {
750 unsafe { transmute(cap) }
751 }
752}
753
754pub struct Savefile {
756 handle: Unique<raw::pcap_dumper_t>
757}
758
759impl Savefile {
760 pub fn write<'a>(&mut self, packet: &'a Packet<'a>) {
761 unsafe {
762 raw::pcap_dump(*self.handle as *mut u8, transmute::<_, &raw::Struct_pcap_pkthdr>(packet.header), packet.data.as_ptr());
763 }
764 }
765}
766
767impl Drop for Savefile {
768 fn drop(&mut self) {
769 unsafe {
770 raw::pcap_dump_close(*self.handle);
771 }
772 }
773}
774
775#[inline]
776fn cstr_to_string(ptr: *const libc::c_char) -> Result<String, Error> {
777 if ptr.is_null() {
778 Err(InvalidString)
779 } else {
780 Ok(try!(str::from_utf8(unsafe{CStr::from_ptr(ptr)}.to_bytes())).into())
781 }
782}