feat: add pcap

This commit is contained in:
2020-11-07 11:21:53 +08:00
parent 82bb330b3c
commit 73eddfbd5b
19 changed files with 2160 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
# Changelog
## [Unreleased]
### Added
- Add `Derive(Clone)` to `Device` struct (#100).
- Build-time `libpcap` version detection.
- Add support for immediate mode.
### Changed
- Opt into Rust 2018.
- Now minimum supported rustc version is 1.40.0.
- Updated dependency from deprecated `tokio-core` to `tokio` 0.2.
- Updated dependency `futures` from version 0.1 to 0.3.
- Feature `tokio` renamed to `capture-stream` because Cargo does not allow
features and dependencies to have the same name.
- `PCAP_LIBDIR` renamed to `LIBPCAP_LIBDIR` to distinguish the `pcap` crate
from the `libpcap` library.
### Removed
- Feature flags `pcap-savefile-append`, `pcap-fopen-offline-precision`
(replaced by build-time `libpcap` version detection)
## [0.7.0] - 2017-08-04
No Changelog entries for <= 0.7.0

65
__network/pcap/Cargo.toml Normal file
View File

@@ -0,0 +1,65 @@
[package]
name = "pcap"
version = "0.7.0"
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
edition = "2018"
description = "A packet capture API around pcap/wpcap"
keywords = ["pcap", "packet", "sniffing"]
readme = "README.md"
homepage = "https://github.com/ebfull/pcap"
repository = "https://github.com/ebfull/pcap"
documentation = "https://docs.rs/pcap"
license = "MIT OR Apache-2.0"
build = "build.rs"
[dependencies]
libc = "0.2"
clippy = { version = "0.0.*", optional = true }
mio = { version = "0.6", optional = true }
tokio = { version = "0.2", features = ["io-driver"], optional = true }
futures = { version = "0.3", optional = true }
[dev-dependencies]
tempdir = "0.3"
tokio = { version = "0.2", features = ["rt-core"] }
[build-dependencies]
libloading = "0.6"
regex = "1"
[features]
# This feature enables access to the function Capture::stream.
# This is disabled by default, because it depends on a tokio and mio
capture-stream = ["mio", "tokio", "futures"]
# A shortcut to enable all features.
full = ["capture-stream"]
[lib]
name = "pcap"
[[example]]
name = "listenlocalhost"
path = "examples/listenlocalhost.rs"
[[example]]
name = "getdevices"
path = "examples/getdevices.rs"
[[example]]
name = "easylisten"
path = "examples/easylisten.rs"
[[example]]
name = "savefile"
path = "examples/savefile.rs"
[[example]]
name = "getstatistics"
path = "examples/getstatistics.rs"
[[example]]
name = "streamlisten"
path = "examples/streamlisten.rs"
required-features = ["capture-stream"]

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,19 @@
Copyright (c) 2015 The pcap Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

76
__network/pcap/README.md Normal file
View File

@@ -0,0 +1,76 @@
# pcap [![Build status](https://api.travis-ci.org/ebfull/pcap.svg)](https://travis-ci.org/ebfull/pcap) [![Crates.io](https://img.shields.io/crates/v/pcap.svg)](https://crates.io/crates/pcap) [![Docs.rs](https://docs.rs/pcap/badge.svg)](https://docs.rs/pcap) #
### [Documentation](https://docs.rs/pcap)
This is a **Rust language** crate for accessing the packet sniffing capabilities of pcap (or wpcap on Windows).
If you need anything feel free to post an issue or submit a pull request!
## Features:
* List devices
* Open capture handle on a device or savefiles
* Get packets from the capture handle
* Filter packets using BPF programs
* List/set/get datalink link types
* Configure some parameters like promiscuity and buffer length
* Write packets to savefiles
* Inject packets into an interface
See examples for usage.
# Building
As of 0.8.0 This crate uses Rust 2018 and requires a compiler version >= 1.40.0.
## Windows
Install [WinPcap](http://www.winpcap.org/install/default.htm).
Download the WinPcap [Developer's Pack](https://www.winpcap.org/devel.htm).
Add the `/Lib` or `/Lib/x64` folder to your `LIB` environment variable.
## Linux
On Debian based Linux, install `libpcap-dev`. If not running as root, you need to set capabilities like so: ```sudo setcap cap_net_raw,cap_net_admin=eip path/to/bin```
## Mac OS X
libpcap should be installed on Mac OS X by default.
**Note:** A timeout of zero may cause ```pcap::Capture::next``` to hang and never return (because it waits for the timeout to expire before returning). This can be fixed by using a non-zero timeout (as the libpcap manual recommends) and calling ```pcap::Capture::next``` in a loop.
## Library Location
If `LIBPCAP_LIBDIR` environment variable is set when building the crate, it will be added to the linker search path - this allows linking against a specific `libpcap`.
## Library Version
The crate will automatically try to detect the installed `libpcap`/`wpcap` version by loading it during the build and calling `pcap_lib_version`. If for some reason this is not suitable, you can specify the desired library version by setting the environment variable `LIBPCAP_VER` to the desired version (e.g. `env LIBPCAP_VER=1.5.0`). The version number is used to determine which library calls to include in the compilation.
## Optional Features
#### `capture-stream`
Use the `capture-stream` feature to enable support for streamed packet captures.
This feature is supported only on ubuntu and macosx.
```toml
[dependencies]
pcap = { version = "0.7", features = ["capture-stream"] }
```
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

122
__network/pcap/build.rs Normal file
View File

@@ -0,0 +1,122 @@
use std::env;
use std::ffi::CStr;
use std::os::raw::c_char;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Version {
major: usize,
minor: usize,
micro: usize,
}
impl Version {
fn new(major: usize, minor: usize, micro: usize) -> Version {
Version {
major,
minor,
micro,
}
}
fn parse(s: &str) -> Result<Version, Box<dyn std::error::Error>> {
let err = format!("invalid pcap lib version: {}", s);
let re = regex::Regex::new(r"([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)")?;
let captures = re.captures(s).ok_or_else(|| err.clone())?;
let major_str = captures.get(1).ok_or_else(|| err.clone())?.as_str();
let minor_str = captures.get(2).ok_or_else(|| err.clone())?.as_str();
let micro_str = captures.get(3).ok_or_else(|| err.clone())?.as_str();
Ok(Version::new(
major_str.parse::<usize>()?,
minor_str.parse::<usize>()?,
micro_str.parse::<usize>()?,
))
}
}
fn get_pcap_lib_version() -> Result<Version, Box<dyn std::error::Error>> {
if let Ok(libver) = env::var("LIBPCAP_VER") {
return Version::parse(&libver);
}
#[cfg(all(unix, not(target_os = "macos")))]
let libfile = "libpcap.so";
#[cfg(target_os = "macos")]
let libfile = "libpcap.dylib";
#[cfg(windows)]
let libfile = "wpcap.dll";
let lib = libloading::Library::new(libfile)?;
type PcapLibVersion = unsafe extern "C" fn() -> *mut c_char;
let pcap_lib_version = unsafe { lib.get::<PcapLibVersion>(b"pcap_lib_version")? };
let c_buf: *const c_char = unsafe { pcap_lib_version() };
let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) };
let v_str: &str = c_str.to_str()?;
let err = format!("cannot infer pcap lib version from: {}", v_str);
#[cfg(not(windows))]
{
let re = regex::Regex::new(r"libpcap version ([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)")?;
let captures = re.captures(v_str).ok_or_else(|| err.clone())?;
let major_str = captures.get(1).ok_or_else(|| err.clone())?.as_str();
let minor_str = captures.get(2).ok_or_else(|| err.clone())?.as_str();
let micro_str = captures.get(3).ok_or_else(|| err.clone())?.as_str();
Ok(Version::new(
major_str.parse::<usize>()?,
minor_str.parse::<usize>()?,
micro_str.parse::<usize>()?,
))
}
#[cfg(windows)]
{
let re = regex::Regex::new(r"based on libpcap version ([[:digit:]]+)\.([[:digit:]]+)")?;
let captures = re.captures(v_str).ok_or(err.clone())?;
let major_str = captures.get(1).ok_or(err.clone())?.as_str();
let minor_str = captures.get(2).ok_or(err.clone())?.as_str();
Ok(Version::new(
major_str.parse::<usize>()?,
minor_str.parse::<usize>()?,
0,
))
}
}
fn emit_cfg_flags(version: Version) {
assert!(
version >= Version::new(1, 0, 0),
"required pcap lib version: >=1.0.0"
);
let api_vers: Vec<Version> = vec![
Version::new(1, 2, 1),
Version::new(1, 5, 0),
Version::new(1, 7, 2),
Version::new(1, 9, 0),
Version::new(1, 9, 1),
];
for v in api_vers.iter().filter(|&v| v <= &version) {
println!("cargo:rustc-cfg=libpcap_{}_{}_{}", v.major, v.minor, v.micro);
}
}
fn main() {
println!("cargo:rerun-if-env-changed=LIBPCAP_LIBDIR");
println!("cargo:rerun-if-env-changed=LIBPCAP_VER");
if let Ok(libdir) = env::var("LIBPCAP_LIBDIR") {
println!("cargo:rustc-link-search=native={}", libdir);
}
let version = get_pcap_lib_version().unwrap();
emit_cfg_flags(version);
}

View File

@@ -0,0 +1,15 @@
fn main() {
// get the default Device
let device = pcap::Device::lookup().unwrap();
println!("Using device {}", device.name);
// Setup Capture
let mut cap = pcap::Capture::from_device(device)
.unwrap()
.immediate_mode(true)
.open()
.unwrap();
// get a packet and print its bytes
println!("{:?}", cap.next());
}

View File

@@ -0,0 +1,18 @@
fn main() {
// list all of the devices pcap tells us are available
for device in pcap::Device::list().unwrap() {
println!("Found device! {:?}", device);
// now you can create a Capture with this Device if you want.
let mut cap = pcap::Capture::from_device(device)
.unwrap()
.immediate_mode(true)
.open()
.unwrap();
// get a packet from this capture
let packet = cap.next();
println!("got a packet! {:?}", packet);
}
}

View File

@@ -0,0 +1,22 @@
fn main() {
// get the default Device
let device = pcap::Device::lookup().unwrap();
println!("Using device {}", device.name);
// Setup Capture
let mut cap = pcap::Capture::from_device(device)
.unwrap()
.immediate_mode(true)
.open()
.unwrap();
// get 10 packets
for _ in 0..10 {
cap.next().ok();
}
let stats = cap.stats().unwrap();
println!(
"Received: {}, dropped: {}, if_dropped: {}",
stats.received, stats.dropped, stats.if_dropped
);
}

View File

@@ -0,0 +1,16 @@
fn main() {
// listen on the device named "any", which is only available on Linux. This is only for
// demonstration purposes.
let mut cap = pcap::Capture::from_device("any")
.unwrap()
.immediate_mode(true)
.open()
.unwrap();
// filter out all packets that don't have 127.0.0.1 as a source or destination.
cap.filter("host 127.0.0.1").unwrap();
while let Ok(packet) = cap.next() {
println!("got packet! {:?}", packet);
}
}

View File

@@ -0,0 +1,37 @@
use pcap::*;
fn main() {
{
// open capture from default device
let device = Device::lookup().unwrap();
println!("Using device {}", device.name);
// Setup Capture
let mut cap = Capture::from_device(device)
.unwrap()
.immediate_mode(true)
.open()
.unwrap();
// open savefile using the capture
let mut savefile = cap.savefile("test.pcap").unwrap();
// get a packet from the interface
let p = cap.next().unwrap();
// print the packet out
println!("packet received on network: {:?}", p);
// write the packet to the savefile
savefile.write(&p);
}
// open a new capture from the test.pcap file we wrote to above
let mut cap = Capture::from_file("test.pcap").unwrap();
// get a packet
let p = cap.next().unwrap();
// print that packet out -- it should be the same as the one we printed above
println!("packet obtained from file: {:?}", p);
}

View File

@@ -0,0 +1,47 @@
use futures::StreamExt;
use pcap::stream::{PacketCodec, PacketStream};
use pcap::{Active, Capture, Device, Error, Packet};
pub struct SimpleDumpCodec;
impl PacketCodec for SimpleDumpCodec {
type Type = String;
fn decode<'p>(&mut self, packet: Packet<'p>) -> Result<Self::Type, Error> {
Ok(format!("{:?}", packet))
}
}
fn new_stream() -> Result<PacketStream<Active, SimpleDumpCodec>, Error> {
// get the default Device
let device = Device::lookup()?;
println!("Using device {}", device.name);
let cap = Capture::from_device(device)?
.immediate_mode(true)
.open()?
.setnonblock()?;
cap.stream(SimpleDumpCodec {})
}
fn main() {
let mut rt = tokio::runtime::Builder::new()
.enable_io()
.basic_scheduler()
.build()
.unwrap();
let stream = rt.enter(|| match new_stream() {
Ok(stream) => stream,
Err(e) => {
println!("{:?}", e);
std::process::exit(1);
}
});
let fut = stream.for_each(move |s| {
println!("{:?}", s);
futures::future::ready(())
});
rt.block_on(fut);
}

870
__network/pcap/src/lib.rs Normal file
View File

@@ -0,0 +1,870 @@
//! pcap is a packet capture library available on Linux, Windows and Mac. This
//! crate supports creating and configuring capture contexts, sniffing packets,
//! sending packets to interfaces, listing devices, and recording packet captures
//! to pcap-format dump files.
//!
//! # Capturing packets
//! The easiest way to open an active capture handle and begin sniffing is to
//! use `.open()` on a `Device`. You can obtain the "default" device using
//! `Device::lookup()`, or you can obtain the device(s) you need via `Device::list()`.
//!
//! ```ignore
//! use pcap::Device;
//!
//! fn main() {
//! let mut cap = Device::lookup().unwrap().open().unwrap();
//!
//! while let Ok(packet) = cap.next() {
//! println!("received packet! {:?}", packet);
//! }
//! }
//! ```
//!
//! `Capture`'s `.next()` will produce a `Packet` which can be dereferenced to access the
//! `&[u8]` packet contents.
//!
//! # Custom configuration
//!
//! You may want to configure the `timeout`, `snaplen` or other parameters for the capture
//! handle. In this case, use `Capture::from_device()` to obtain a `Capture<Inactive>`, and
//! proceed to configure the capture handle. When you're finished, run `.open()` on it to
//! turn it into a `Capture<Active>`.
//!
//! ```ignore
//! use pcap::{Device,Capture};
//!
//! fn main() {
//! let main_device = Device::lookup().unwrap();
//! let mut cap = Capture::from_device(main_device).unwrap()
//! .promisc(true)
//! .snaplen(5000)
//! .open().unwrap();
//!
//! while let Ok(packet) = cap.next() {
//! println!("received packet! {:?}", packet);
//! }
//! }
//! ```
use unique::Unique;
use std::borrow::Borrow;
use std::marker::PhantomData;
use std::ptr;
use std::ffi::{self, CString, CStr};
use std::path::Path;
use std::slice;
use std::ops::Deref;
use std::mem;
use std::fmt;
#[cfg(feature = "capture-stream")]
use std::io;
#[cfg(not(windows))]
use std::os::unix::io::{RawFd, AsRawFd};
use self::Error::*;
mod raw;
mod unique;
#[cfg(feature = "capture-stream")]
pub mod stream;
/// An error received from pcap
#[derive(Debug, PartialEq)]
pub enum Error {
MalformedError(std::str::Utf8Error),
InvalidString,
PcapError(String),
InvalidLinktype,
TimeoutExpired,
NoMorePackets,
NonNonBlock,
InsufficientMemory,
InvalidInputString,
IoError(std::io::ErrorKind),
#[cfg(not(windows))]
InvalidRawFd,
}
impl Error {
fn new(ptr: *const libc::c_char) -> Error {
match cstr_to_string(ptr) {
Err(e) => e as Error,
Ok(string) => PcapError(string.unwrap_or_default()),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
MalformedError(ref e) => write!(f, "libpcap returned invalid UTF-8: {}", e),
InvalidString => write!(f, "libpcap returned a null string"),
PcapError(ref e) => write!(f, "libpcap error: {}", e),
InvalidLinktype => write!(f, "invalid or unknown linktype"),
TimeoutExpired => write!(f, "timeout expired while reading from a live capture"),
NonNonBlock => write!(f, "must be in non-blocking mode to function"),
NoMorePackets => write!(f, "no more packets to read from the file"),
InsufficientMemory => write!(f, "insufficient memory"),
InvalidInputString => write!(f, "invalid input string (internal null)"),
IoError(ref e) => write!(f, "io error occurred: {:?}", e),
#[cfg(not(windows))]
InvalidRawFd => write!(f, "invalid raw file descriptor provided"),
}
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
MalformedError(..) => "libpcap returned invalid UTF-8",
PcapError(..) => "libpcap FFI error",
InvalidString => "libpcap returned a null string",
InvalidLinktype => "invalid or unknown linktype",
TimeoutExpired => "timeout expired while reading from a live capture",
NonNonBlock => "must be in non-blocking mode to function",
NoMorePackets => "no more packets to read from the file",
InsufficientMemory => "insufficient memory",
InvalidInputString => "invalid input string (internal null)",
IoError(..) => "io error occurred",
#[cfg(not(windows))]
InvalidRawFd => "invalid raw file descriptor provided",
}
}
fn cause(&self) -> Option<&dyn std::error::Error> {
match *self {
MalformedError(ref e) => Some(e),
_ => None,
}
}
}
impl From<ffi::NulError> for Error {
fn from(_: ffi::NulError) -> Error {
InvalidInputString
}
}
impl From<std::str::Utf8Error> for Error {
fn from(obj: std::str::Utf8Error) -> Error {
MalformedError(obj)
}
}
impl From<std::io::Error> for Error {
fn from(obj: std::io::Error) -> Error {
IoError(obj.kind())
}
}
impl From<std::io::ErrorKind> for Error {
fn from(obj: std::io::ErrorKind) -> Error {
IoError(obj)
}
}
#[derive(Debug, Clone)]
/// A network device name and (potentially) pcap's description of it.
pub struct Device {
pub name: String,
pub desc: Option<String>,
}
impl Device {
fn new(name: String, desc: Option<String>) -> Device {
Device { name, desc }
}
/// Opens a `Capture<Active>` on this device.
pub fn open(self) -> Result<Capture<Active>, Error> {
Capture::from_device(self)?.open()
}
/// Returns the default Device suitable for captures according to pcap_lookupdev,
/// or an error from pcap.
pub fn lookup() -> Result<Device, Error> {
with_errbuf(|err| unsafe {
cstr_to_string(raw::pcap_lookupdev(err))
?
.map(|name| Device::new(name, None))
.ok_or_else(|| Error::new(err))
})
}
/// Returns a vector of `Device`s known by pcap via pcap_findalldevs.
pub fn list() -> Result<Vec<Device>, Error> {
with_errbuf(|err| unsafe {
let mut dev_buf: *mut raw::pcap_if_t = ptr::null_mut();
if raw::pcap_findalldevs(&mut dev_buf, err) != 0 {
return Err(Error::new(err));
}
let result = (|| {
let mut devices = vec![];
let mut cur = dev_buf;
while !cur.is_null() {
let dev = &*cur;
devices.push(Device::new(cstr_to_string(dev.name)?.ok_or(InvalidString)?,
cstr_to_string(dev.description)?));
cur = dev.next;
}
Ok(devices)
})();
raw::pcap_freealldevs(dev_buf);
result
})
}
}
impl<'a> Into<Device> for &'a str {
fn into(self) -> Device {
Device::new(self.into(), None)
}
}
/// This is a datalink link type.
///
/// As an example, `Linktype(1)` is ethernet. A full list of linktypes is available
/// [here](http://www.tcpdump.org/linktypes.html).
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Linktype(pub i32);
impl Linktype {
/// Gets the name of the link type, such as EN10MB
pub fn get_name(&self) -> Result<String, Error> {
cstr_to_string(unsafe { raw::pcap_datalink_val_to_name(self.0) })
?
.ok_or(InvalidLinktype)
}
/// Gets the description of a link type.
pub fn get_description(&self) -> Result<String, Error> {
cstr_to_string(unsafe { raw::pcap_datalink_val_to_description(self.0) })
?
.ok_or(InvalidLinktype)
}
}
/// Represents a packet returned from pcap.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Packet<'a> {
pub header: &'a PacketHeader,
pub data: &'a [u8],
}
impl<'a> Packet<'a> {
#[doc(hidden)]
pub fn new(header: &'a PacketHeader, data: &'a [u8]) -> Packet<'a> {
Packet { header, data }
}
}
impl<'b> Deref for Packet<'b> {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.data
}
}
#[repr(C)]
#[derive(Copy, Clone)]
/// Represents a packet header provided by pcap, including the timeval, caplen and len.
pub struct PacketHeader {
pub ts: libc::timeval,
pub caplen: u32,
pub len: u32,
}
impl fmt::Debug for PacketHeader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"PacketHeader {{ ts: {}.{:06}, caplen: {}, len: {} }}",
self.ts.tv_sec,
self.ts.tv_usec,
self.caplen,
self.len)
}
}
impl PartialEq for PacketHeader {
fn eq(&self, rhs: &PacketHeader) -> bool {
self.ts.tv_sec == rhs.ts.tv_sec && self.ts.tv_usec == rhs.ts.tv_usec &&
self.caplen == rhs.caplen && self.len == rhs.len
}
}
impl Eq for PacketHeader {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Stat {
pub received: u32,
pub dropped: u32,
pub if_dropped: u32,
}
impl Stat {
fn new(received: u32, dropped: u32, if_dropped: u32) -> Stat {
Stat { received, dropped, if_dropped }
}
}
#[repr(u32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Precision {
Micro = 0,
Nano = 1,
}
/// Phantom type representing an inactive capture handle.
pub enum Inactive {}
/// Phantom type representing an active capture handle.
pub enum Active {}
/// Phantom type representing an offline capture handle, from a pcap dump file.
/// Implements `Activated` because it behaves nearly the same as a live handle.
pub enum Offline {}
/// Phantom type representing a dead capture handle. This can be use to create
/// new save files that are not generated from an active capture.
/// Implements `Activated` because it behaves nearly the same as a live handle.
pub enum Dead {}
pub unsafe trait Activated: State {}
unsafe impl Activated for Active {}
unsafe impl Activated for Offline {}
unsafe impl Activated for Dead {}
/// `Capture`s can be in different states at different times, and in these states they
/// may or may not have particular capabilities. This trait is implemented by phantom
/// types which allows us to punt these invariants to the type system to avoid runtime
/// errors.
pub unsafe trait State {}
unsafe impl State for Inactive {}
unsafe impl State for Active {}
unsafe impl State for Offline {}
unsafe impl State for Dead {}
/// This is a pcap capture handle which is an abstraction over the `pcap_t` provided by pcap.
/// There are many ways to instantiate and interact with a pcap handle, so phantom types are
/// used to express these behaviors.
///
/// **`Capture<Inactive>`** is created via `Capture::from_device()`. This handle is inactive,
/// so you cannot (yet) obtain packets from it. However, you can configure things like the
/// buffer size, snaplen, timeout, and promiscuity before you activate it.
///
/// **`Capture<Active>`** is created by calling `.open()` on a `Capture<Inactive>`. This
/// activates the capture handle, allowing you to get packets with `.next()` or apply filters
/// with `.filter()`.
///
/// **`Capture<Offline>`** is created via `Capture::from_file()`. This allows you to read a
/// pcap format dump file as if you were opening an interface -- very useful for testing or
/// analysis.
///
/// **`Capture<Dead>`** is created via `Capture::dead()`. This allows you to create a pcap
/// format dump file without needing an active capture.
///
/// # Example:
///
/// ```ignore
/// let cap = Capture::from_device(Device::lookup().unwrap()) // open the "default" interface
/// .unwrap() // assume the device exists and we are authorized to open it
/// .open() // activate the handle
/// .unwrap(); // assume activation worked
///
/// while let Ok(packet) = cap.next() {
/// println!("received packet! {:?}", packet);
/// }
/// ```
pub struct Capture<T: State + ? Sized> {
nonblock: bool,
handle: Unique<raw::pcap_t>,
_marker: PhantomData<T>,
}
impl<T: State + ? Sized> Capture<T> {
fn new(handle: *mut raw::pcap_t) -> Capture<T> {
unsafe {
Capture {
nonblock: false,
handle: Unique::new(handle),
_marker: PhantomData,
}
}
}
fn new_raw<F>(path: Option<&str>, func: F) -> Result<Capture<T>, Error>
where F: FnOnce(*const libc::c_char, *mut libc::c_char) -> *mut raw::pcap_t
{
with_errbuf(|err| {
let handle = match path {
None => func(ptr::null(), err),
Some(path) => {
let path = CString::new(path)?;
func(path.as_ptr(), err)
}
};
unsafe { handle.as_mut() }.map(|h| Capture::new(h)).ok_or_else(|| Error::new(err))
})
}
/// Set the minumum amount of data received by the kernel in a single call.
///
/// Note that this value is set to 0 when the capture is set to immediate mode. You should not
/// call `min_to_copy` on captures in immediate mode if you want them to stay in immediate mode.
#[cfg(windows)]
pub fn min_to_copy(self, to: i32) -> Capture<T> {
unsafe { raw::pcap_setmintocopy(*self.handle, to as _); }
self
}
#[inline]
fn check_err(&self, success: bool) -> Result<(), Error> {
if success {
Ok(())
} else {
Err(Error::new(unsafe { raw::pcap_geterr(*self.handle) }))
}
}
}
impl Capture<Offline> {
/// Opens an offline capture handle from a pcap dump file, given a path.
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Capture<Offline>, Error> {
Capture::new_raw(path.as_ref().to_str(),
|path, err| unsafe { raw::pcap_open_offline(path, err) })
}
/// Opens an offline capture handle from a pcap dump file, given a path.
/// Takes an additional precision argument specifying the time stamp precision desired.
#[cfg(libpcap_1_5_0)]
pub fn from_file_with_precision<P: AsRef<Path>>(path: P, precision: Precision) -> Result<Capture<Offline>, Error> {
Capture::new_raw(path.as_ref().to_str(), |path, err| unsafe {
raw::pcap_open_offline_with_tstamp_precision(path, precision as _, err)
})
}
/// Opens an offline capture handle from a pcap dump file, given a file descriptor.
#[cfg(not(windows))]
pub fn from_raw_fd(fd: RawFd) -> Result<Capture<Offline>, Error> {
open_raw_fd(fd, b'r')
.and_then(|file| Capture::new_raw(None, |_, err| unsafe {
raw::pcap_fopen_offline(file, err)
}))
}
/// Opens an offline capture handle from a pcap dump file, given a file descriptor.
/// Takes an additional precision argument specifying the time stamp precision desired.
#[cfg(all(not(windows), libpcap_1_5_0))]
pub fn from_raw_fd_with_precision(fd: RawFd, precision: Precision) -> Result<Capture<Offline>, Error> {
open_raw_fd(fd, b'r')
.and_then(|file| Capture::new_raw(None, |_, err| unsafe {
raw::pcap_fopen_offline_with_tstamp_precision(file, precision as _, err)
}))
}
}
#[repr(i32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TimestampType {
Host = 0,
HostLowPrec = 1,
HostHighPrec = 2,
Adapter = 3,
AdapterUnsynced = 4,
}
#[deprecated(note = "Renamed to TimestampType")]
pub type TstampType = TimestampType;
#[repr(u32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Direction {
InOut = raw::PCAP_D_INOUT,
In = raw::PCAP_D_IN,
Out = raw::PCAP_D_OUT,
}
impl Capture<Inactive> {
/// Opens a capture handle for a device. You can pass a `Device` or an `&str` device
/// name here. The handle is inactive, but can be activated via `.open()`.
///
/// # Example
/// ```
/// use pcap::*;
///
/// // Usage 1: Capture from a single owned device
/// let dev: Device = pcap::Device::lookup().unwrap();
/// let cap1 = Capture::from_device(dev);
///
/// // Usage 2: Capture from an element of device list.
/// let list: Vec<Device> = pcap::Device::list().unwrap();
/// let cap2 = Capture::from_device(list[0].clone());
///
/// // Usage 3: Capture from `&str` device name
/// let cap3 = Capture::from_device("eth0");
/// ```
pub fn from_device<D: Into<Device>>(device: D) -> Result<Capture<Inactive>, Error> {
let device: Device = device.into();
Capture::new_raw(Some(&device.name),
|name, err| unsafe { raw::pcap_create(name, err) })
}
/// Activates an inactive capture created from `Capture::from_device()` or returns
/// an error.
pub fn open(self) -> Result<Capture<Active>, Error> {
unsafe {
self.check_err(raw::pcap_activate(*self.handle) == 0)?;
Ok(mem::transmute(self))
}
}
/// Set the read timeout for the Capture. By default, this is 0, so it will block
/// indefinitely.
pub fn timeout(self, ms: i32) -> Capture<Inactive> {
unsafe { raw::pcap_set_timeout(*self.handle, ms) };
self
}
/// Set the time stamp type to be used by a capture device.
#[cfg(libpcap_1_2_1)]
pub fn tstamp_type(self, tstamp_type: TimestampType) -> Capture<Inactive> {
unsafe { raw::pcap_set_tstamp_type(*self.handle, tstamp_type as _) };
self
}
/// Set promiscuous mode on or off. By default, this is off.
pub fn promisc(self, to: bool) -> Capture<Inactive> {
unsafe { raw::pcap_set_promisc(*self.handle, to as _) };
self
}
/// Set immediate mode on or off. By default, this is off.
///
/// Note that in WinPcap immediate mode is set by passing a 0 argument to `min_to_copy`.
/// Immediate mode will be unset if `min_to_copy` is later called with a non-zero argument.
/// Immediate mode is unset by resetting `min_to_copy` to the WinPcap default possibly changing
/// a previously set value. When using `min_to_copy`, it is best to avoid `immediate_mode`.
#[cfg(any(libpcap_1_5_0, windows))]
pub fn immediate_mode(self, to: bool) -> Capture<Inactive> {
// Prior to 1.5.0 when `pcap_set_immediate_mode` was introduced, the necessary steps to set
// immediate mode were more complicated, depended on the OS, and in some configurations had
// to be set on an active capture. See
// https://www.tcpdump.org/manpages/pcap_set_immediate_mode.3pcap.html. Since we do not
// expect pre-1.5.0 version on unix systems in the wild, we simply ignore those cases.
#[cfg(libpcap_1_5_0)]
unsafe { raw::pcap_set_immediate_mode(*self.handle, to as _) };
// In WinPcap we use `pcap_setmintocopy` as it does not have `pcap_set_immediate_mode`.
#[cfg(all(windows, not(libpcap_1_5_0)))]
unsafe { raw::pcap_setmintocopy(*self.handle, if to { 0 } else { raw::WINPCAP_MINTOCOPY_DEFAULT }) };
self
}
/// Set rfmon mode on or off. The default is maintained by pcap.
#[cfg(not(windows))]
pub fn rfmon(self, to: bool) -> Capture<Inactive> {
unsafe { raw::pcap_set_rfmon(*self.handle, to as _) };
self
}
/// Set the buffer size for incoming packet data.
///
/// The default is 1000000. This should always be larger than the snaplen.
pub fn buffer_size(self, to: i32) -> Capture<Inactive> {
unsafe { raw::pcap_set_buffer_size(*self.handle, to) };
self
}
/// Set the time stamp precision returned in captures.
#[cfg(libpcap_1_5_0)]
pub fn precision(self, precision: Precision) -> Capture<Inactive> {
unsafe { raw::pcap_set_tstamp_precision(*self.handle, precision as _) };
self
}
/// Set the snaplen size (the maximum length of a packet captured into the buffer).
/// Useful if you only want certain headers, but not the entire packet.
///
/// The default is 65535.
pub fn snaplen(self, to: i32) -> Capture<Inactive> {
unsafe { raw::pcap_set_snaplen(*self.handle, to) };
self
}
}
///# Activated captures include `Capture<Active>` and `Capture<Offline>`.
impl<T: Activated + ? Sized> Capture<T> {
/// List the datalink types that this captured device supports.
pub fn list_datalinks(&self) -> Result<Vec<Linktype>, Error> {
unsafe {
let mut links: *mut i32 = ptr::null_mut();
let num = raw::pcap_list_datalinks(*self.handle, &mut links);
let mut vec = vec![];
if num > 0 {
vec.extend(slice::from_raw_parts(links, num as _).iter().cloned().map(Linktype))
}
raw::pcap_free_datalinks(links);
self.check_err(num > 0).and(Ok(vec))
}
}
/// Set the datalink type for the current capture handle.
pub fn set_datalink(&mut self, linktype: Linktype) -> Result<(), Error> {
self.check_err(unsafe { raw::pcap_set_datalink(*self.handle, linktype.0) == 0 })
}
/// Get the current datalink type for this capture handle.
pub fn get_datalink(&self) -> Linktype {
unsafe { Linktype(raw::pcap_datalink(*self.handle)) }
}
/// Create a `Savefile` context for recording captured packets using this `Capture`'s
/// configurations.
pub fn savefile<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
let name = CString::new(path.as_ref().to_str().unwrap())?;
let handle = unsafe { raw::pcap_dump_open(*self.handle, name.as_ptr()) };
self.check_err(!handle.is_null()).map(|_| Savefile::new(handle))
}
/// Create a `Savefile` context for recording captured packets using this `Capture`'s
/// configurations. The output is written to a raw file descriptor which is opened
/// in `"w"` mode.
#[cfg(not(windows))]
pub fn savefile_raw_fd(&self, fd: RawFd) -> Result<Savefile, Error> {
open_raw_fd(fd, b'w')
.and_then(|file| {
let handle = unsafe { raw::pcap_dump_fopen(*self.handle, file) };
self.check_err(!handle.is_null()).map(|_| Savefile::new(handle))
})
}
/// Reopen a `Savefile` context for recording captured packets using this `Capture`'s
/// configurations. This is similar to `savefile()` but does not create the file if it
/// does not exist and, if it does already exist, and is a pcap file with the same
/// byte order as the host opening the file, and has the same time stamp precision,
/// link-layer header type, and snapshot length as p, it will write new packets
/// at the end of the file.
#[cfg(libpcap_1_7_2)]
pub fn savefile_append<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
let name = CString::new(path.as_ref().to_str().unwrap())?;
let handle = unsafe { raw::pcap_dump_open_append(*self.handle, name.as_ptr()) };
self.check_err(!handle.is_null()).map(|_| Savefile::new(handle))
}
/// Set the direction of the capture
pub fn direction(&self, direction: Direction) -> Result<(), Error> {
self.check_err(unsafe { raw::pcap_setdirection(*self.handle, direction as u32 as _) == 0 })
}
/// Blocks until a packet is returned from the capture handle or an error occurs.
///
/// pcap captures packets and places them into a buffer which this function reads
/// from. This buffer has a finite length, so if the buffer fills completely new
/// packets will be discarded temporarily. This means that in realtime situations,
/// you probably want to minimize the time between calls of this next() method.
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Packet, Error> {
unsafe {
let mut header: *mut raw::pcap_pkthdr = ptr::null_mut();
let mut packet: *const libc::c_uchar = ptr::null();
let retcode = raw::pcap_next_ex(*self.handle, &mut header, &mut packet);
self.check_err(retcode != -1)?; // -1 => an error occured while reading the packet
match retcode {
i if i >= 1 => {
// packet was read without issue
Ok(Packet::new(&*(&*header as *const raw::pcap_pkthdr as *const PacketHeader),
slice::from_raw_parts(packet, (*header).caplen as _)))
}
0 => {
// packets are being read from a live capture and the
// timeout expired
Err(TimeoutExpired)
}
-2 => {
// packets are being read from a "savefile" and there are no
// more packets to read
Err(NoMorePackets)
}
_ => {
// libpcap only defines codes >=1, 0, -1, and -2
unreachable!()
}
}
}
}
#[cfg(feature = "capture-stream")]
fn next_noblock<'a>(&'a mut self, cx: &mut core::task::Context, fd: &mut tokio::io::PollEvented<stream::SelectableFd>) -> Result<Packet<'a>, Error> {
if let futures::task::Poll::Pending = fd.poll_read_ready(cx, mio::Ready::readable()) {
Err(IoError(io::ErrorKind::WouldBlock))
} else {
match self.next() {
Ok(p) => Ok(p),
Err(TimeoutExpired) => {
fd.clear_read_ready(cx, mio::Ready::readable())?;
Err(IoError(io::ErrorKind::WouldBlock))
}
Err(e) => Err(e)
}
}
}
#[cfg(feature = "capture-stream")]
pub fn stream<C: stream::PacketCodec>(self, codec: C) -> Result<stream::PacketStream<T, C>, Error> {
if !self.nonblock {
return Err(NonNonBlock);
}
unsafe {
let fd = raw::pcap_get_selectable_fd(*self.handle);
stream::PacketStream::new(self, fd, codec)
}
}
/// Adds a filter to the capture using the given BPF program string. Internally
/// this is compiled using `pcap_compile()`.
///
/// See http://biot.com/capstats/bpf.html for more information about this syntax.
pub fn filter(&mut self, program: &str) -> Result<(), Error> {
let program = CString::new(program)?;
unsafe {
let mut bpf_program: raw::bpf_program = mem::zeroed();
let ret = raw::pcap_compile(*self.handle, &mut bpf_program, program.as_ptr(), 0, 0);
self.check_err(ret != -1)?;
let ret = raw::pcap_setfilter(*self.handle, &mut bpf_program);
raw::pcap_freecode(&mut bpf_program);
self.check_err(ret != -1)
}
}
pub fn stats(&mut self) -> Result<Stat, Error> {
unsafe {
let mut stats: raw::pcap_stat = mem::zeroed();
self.check_err(raw::pcap_stats(*self.handle, &mut stats) != -1)
.map(|_| Stat::new(stats.ps_recv, stats.ps_drop, stats.ps_ifdrop))
}
}
}
impl Capture<Active> {
/// Sends a packet over this capture handle's interface.
pub fn sendpacket<B: Borrow<[u8]>>(&mut self, buf: B) -> Result<(), Error> {
let buf = buf.borrow();
self.check_err(unsafe {
raw::pcap_sendpacket(*self.handle, buf.as_ptr() as _, buf.len() as _) == 0
})
}
pub fn setnonblock(mut self) -> Result<Capture<Active>, Error> {
with_errbuf(|err| unsafe {
if raw::pcap_setnonblock(*self.handle, 1, err) != 0 {
return Err(Error::new(err));
}
self.nonblock = true;
Ok(self)
})
}
}
impl Capture<Dead> {
/// Creates a "fake" capture handle for the given link type.
pub fn dead(linktype: Linktype) -> Result<Capture<Dead>, Error> {
unsafe { raw::pcap_open_dead(linktype.0, 65535).as_mut() }
.map(|h| Capture::new(h))
.ok_or(InsufficientMemory)
}
}
#[cfg(not(windows))]
impl AsRawFd for Capture<Active> {
fn as_raw_fd(&self) -> RawFd {
unsafe {
let fd = raw::pcap_fileno(*self.handle);
match fd {
-1 => {
panic!("Unable to get file descriptor for live capture");
}
fd => fd,
}
}
}
}
impl<T: State + ? Sized> Drop for Capture<T> {
fn drop(&mut self) {
unsafe { raw::pcap_close(*self.handle) }
}
}
impl<T: Activated> From<Capture<T>> for Capture<dyn Activated> {
fn from(cap: Capture<T>) -> Capture<dyn Activated> {
unsafe { mem::transmute(cap) }
}
}
/// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`.
pub struct Savefile {
handle: Unique<raw::pcap_dumper_t>,
}
impl Savefile {
pub fn write(&mut self, packet: &Packet) {
unsafe {
raw::pcap_dump(*self.handle as _,
&*(packet.header as *const PacketHeader as *const raw::pcap_pkthdr),
packet.data.as_ptr());
}
}
}
impl Savefile {
fn new(handle: *mut raw::pcap_dumper_t) -> Savefile {
unsafe { Savefile { handle: Unique::new(handle) } }
}
}
impl Drop for Savefile {
fn drop(&mut self) {
unsafe { raw::pcap_dump_close(*self.handle) }
}
}
#[cfg(not(windows))]
pub fn open_raw_fd(fd: RawFd, mode: u8) -> Result<*mut libc::FILE, Error> {
let mode = vec![mode, 0];
unsafe { libc::fdopen(fd, mode.as_ptr() as _).as_mut() }.map(|f| f as _).ok_or(InvalidRawFd)
}
#[inline]
fn cstr_to_string(ptr: *const libc::c_char) -> Result<Option<String>, Error> {
let string = if ptr.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(ptr as _) }.to_str()?.to_owned())
};
Ok(string)
}
#[inline]
fn with_errbuf<T, F>(func: F) -> Result<T, Error>
where F: FnOnce(*mut libc::c_char) -> Result<T, Error>
{
let mut errbuf = [0i8; 256];
func(errbuf.as_mut_ptr() as _)
}
#[test]
fn test_struct_size() {
use std::mem::size_of;
assert_eq!(size_of::<PacketHeader>(), size_of::<raw::pcap_pkthdr>());
}

221
__network/pcap/src/raw.rs Normal file
View File

@@ -0,0 +1,221 @@
#![allow(dead_code)]
#![allow(non_camel_case_types)]
use libc::{c_int, c_uint, c_char, c_uchar, c_ushort, sockaddr, timeval, FILE};
#[repr(C)]
#[derive(Copy, Clone)]
pub struct bpf_program {
pub bf_len: c_uint,
pub bf_insns: *mut bpf_insn,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct bpf_insn {
pub code: c_ushort,
pub jt: c_uchar,
pub jf: c_uchar,
pub k: c_uint,
}
pub enum pcap_t { }
pub enum pcap_dumper_t { }
#[repr(C)]
#[derive(Copy, Clone)]
pub struct pcap_file_header {
pub magic: c_uint,
pub version_major: c_ushort,
pub version_minor: c_ushort,
pub thiszone: c_int,
pub sigfigs: c_uint,
pub snaplen: c_uint,
pub linktype: c_uint,
}
pub type pcap_direction_t = c_uint;
pub const PCAP_D_INOUT: pcap_direction_t = 0;
pub const PCAP_D_IN: pcap_direction_t = 1;
pub const PCAP_D_OUT: pcap_direction_t = 2;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct pcap_pkthdr {
pub ts: timeval,
pub caplen: c_uint,
pub len: c_uint,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct pcap_stat {
pub ps_recv: c_uint,
pub ps_drop: c_uint,
pub ps_ifdrop: c_uint,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct pcap_if_t {
pub next: *mut pcap_if_t,
pub name: *mut c_char,
pub description: *mut c_char,
pub addresses: *mut pcap_addr_t,
pub flags: c_uint,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct pcap_addr_t {
pub next: *mut pcap_addr_t,
pub addr: *mut sockaddr,
pub netmask: *mut sockaddr,
pub broadaddr: *mut sockaddr,
pub dstaddr: *mut sockaddr,
}
pub type pcap_handler = Option<extern "C" fn(arg1: *mut c_uchar,
arg2: *const pcap_pkthdr,
arg3: *const c_uchar)
-> ()>;
extern "C" {
pub fn pcap_lookupdev(arg1: *mut c_char) -> *mut c_char;
// pub fn pcap_lookupnet(arg1: *const c_char, arg2: *mut c_uint, arg3: *mut c_uint,
// arg4: *mut c_char) -> c_int;
pub fn pcap_create(arg1: *const c_char, arg2: *mut c_char) -> *mut pcap_t;
pub fn pcap_set_snaplen(arg1: *mut pcap_t, arg2: c_int) -> c_int;
pub fn pcap_set_promisc(arg1: *mut pcap_t, arg2: c_int) -> c_int;
// pub fn pcap_can_set_rfmon(arg1: *mut pcap_t) -> c_int;
pub fn pcap_set_timeout(arg1: *mut pcap_t, arg2: c_int) -> c_int;
pub fn pcap_set_buffer_size(arg1: *mut pcap_t, arg2: c_int) -> c_int;
pub fn pcap_activate(arg1: *mut pcap_t) -> c_int;
// pub fn pcap_open_live(arg1: *const c_char, arg2: c_int, arg3: c_int, arg4: c_int,
// arg5: *mut c_char) -> *mut pcap_t;
pub fn pcap_open_dead(arg1: c_int, arg2: c_int) -> *mut pcap_t;
pub fn pcap_open_offline(arg1: *const c_char, arg2: *mut c_char) -> *mut pcap_t;
pub fn pcap_fopen_offline(arg1: *mut FILE, arg2: *mut c_char) -> *mut pcap_t;
pub fn pcap_close(arg1: *mut pcap_t);
// pub fn pcap_loop(arg1: *mut pcap_t, arg2: c_int,
// arg3: pcap_handler, arg4: *mut c_uchar) -> c_int;
// pub fn pcap_dispatch(arg1: *mut pcap_t, arg2: c_int, arg3: pcap_handler,
// arg4: *mut c_uchar)-> c_int;
// pub fn pcap_next(arg1: *mut pcap_t, arg2: *mut pcap_pkthdr) -> *const c_uchar;
pub fn pcap_next_ex(arg1: *mut pcap_t, arg2: *mut *mut pcap_pkthdr,
arg3: *mut *const c_uchar) -> c_int;
// pub fn pcap_breakloop(arg1: *mut pcap_t);
pub fn pcap_stats(arg1: *mut pcap_t, arg2: *mut pcap_stat) -> c_int;
pub fn pcap_setfilter(arg1: *mut pcap_t, arg2: *mut bpf_program) -> c_int;
pub fn pcap_setdirection(arg1: *mut pcap_t, arg2: pcap_direction_t) -> c_int;
// pub fn pcap_getnonblock(arg1: *mut pcap_t, arg2: *mut c_char) -> c_int;
pub fn pcap_setnonblock(arg1: *mut pcap_t, arg2: c_int, arg3: *mut c_char) -> c_int;
pub fn pcap_sendpacket(arg1: *mut pcap_t, arg2: *const c_uchar, arg3: c_int) -> c_int;
// pub fn pcap_statustostr(arg1: c_int) -> *const c_char;
// pub fn pcap_strerror(arg1: c_int) -> *const c_char;
pub fn pcap_geterr(arg1: *mut pcap_t) -> *mut c_char;
// pub fn pcap_perror(arg1: *mut pcap_t, arg2: *mut c_char);
pub fn pcap_compile(arg1: *mut pcap_t, arg2: *mut bpf_program, arg3: *const c_char,
arg4: c_int, arg5: c_uint) -> c_int;
// pub fn pcap_compile_nopcap(arg1: c_int, arg2: c_int, arg3: *mut bpf_program,
// arg4: *const c_char, arg5: c_int, arg6: c_uint) -> c_int;
pub fn pcap_freecode(arg1: *mut bpf_program);
// pub fn pcap_offline_filter(arg1: *const bpf_program, arg2: *const pcap_pkthdr,
// arg3: *const c_uchar) -> c_int;
pub fn pcap_datalink(arg1: *mut pcap_t) -> c_int;
// pub fn pcap_datalink_ext(arg1: *mut pcap_t) -> c_int;
pub fn pcap_list_datalinks(arg1: *mut pcap_t, arg2: *mut *mut c_int) -> c_int;
pub fn pcap_set_datalink(arg1: *mut pcap_t, arg2: c_int) -> c_int;
pub fn pcap_free_datalinks(arg1: *mut c_int);
// pub fn pcap_datalink_name_to_val(arg1: *const c_char) -> c_int;
pub fn pcap_datalink_val_to_name(arg1: c_int) -> *const c_char;
pub fn pcap_datalink_val_to_description(arg1: c_int) -> *const c_char;
// pub fn pcap_snapshot(arg1: *mut pcap_t) -> c_int;
// pub fn pcap_is_swapped(arg1: *mut pcap_t) -> c_int;
// pub fn pcap_major_version(arg1: *mut pcap_t) -> c_int;
// pub fn pcap_minor_version(arg1: *mut pcap_t) -> c_int;
// pub fn pcap_file(arg1: *mut pcap_t) -> *mut FILE;
pub fn pcap_fileno(arg1: *mut pcap_t) -> c_int;
pub fn pcap_dump_open(arg1: *mut pcap_t, arg2: *const c_char) -> *mut pcap_dumper_t;
pub fn pcap_dump_fopen(arg1: *mut pcap_t, fp: *mut FILE) -> *mut pcap_dumper_t;
// pub fn pcap_dump_file(arg1: *mut pcap_dumper_t) -> *mut FILE;
// pub fn pcap_dump_ftell(arg1: *mut pcap_dumper_t) -> c_long;
// pub fn pcap_dump_flush(arg1: *mut pcap_dumper_t) -> c_int;
pub fn pcap_dump_close(arg1: *mut pcap_dumper_t);
pub fn pcap_dump(arg1: *mut c_uchar, arg2: *const pcap_pkthdr, arg3: *const c_uchar);
pub fn pcap_findalldevs(arg1: *mut *mut pcap_if_t, arg2: *mut c_char) -> c_int;
pub fn pcap_freealldevs(arg1: *mut pcap_if_t);
// pub fn pcap_lib_version() -> *const c_char;
// pub fn bpf_image(arg1: *const bpf_insn, arg2: c_int) -> *mut c_char;
// pub fn bpf_dump(arg1: *const bpf_program, arg2: c_int);
pub fn pcap_get_selectable_fd(arg1: *mut pcap_t) -> c_int;
}
#[cfg(libpcap_1_2_1)]
extern "C" {
// pub fn pcap_free_tstamp_types(arg1: *mut c_int) -> ();
// pub fn pcap_list_tstamp_types(arg1: *mut pcap_t, arg2: *mut *mut c_int) -> c_int;
pub fn pcap_set_tstamp_type(arg1: *mut pcap_t, arg2: c_int) -> c_int;
// pub fn pcap_tstamp_type_name_to_val(arg1: *const c_char) -> c_int;
// pub fn pcap_tstamp_type_val_to_description(arg1: c_int) -> *const c_char;
// pub fn pcap_tstamp_type_val_to_name(arg1: c_int) -> *const c_char;
}
#[cfg(libpcap_1_5_0)]
extern "C" {
pub fn pcap_fopen_offline_with_tstamp_precision(arg1: *mut FILE, arg2: c_uint,
arg3: *mut c_char) -> *mut pcap_t;
// pub fn pcap_get_tstamp_precision(arg1: *mut pcap_t) -> c_int;
pub fn pcap_open_dead_with_tstamp_precision(arg1: c_int, arg2: c_int,
arg3: c_uint) -> *mut pcap_t;
pub fn pcap_open_offline_with_tstamp_precision(arg1: *const c_char, arg2: c_uint,
arg3: *mut c_char) -> *mut pcap_t;
pub fn pcap_set_immediate_mode(arg1: *mut pcap_t, arg2: c_int) -> c_int;
pub fn pcap_set_tstamp_precision(arg1: *mut pcap_t, arg2: c_int) -> c_int;
}
#[cfg(libpcap_1_7_2)]
extern "C" {
pub fn pcap_dump_open_append(arg1: *mut pcap_t, arg2: *const c_char) -> *mut pcap_dumper_t;
}
#[cfg(libpcap_1_9_0)]
extern "C" {
// pcap_bufsize
// pcap_createsrcstr
// pcap_dump_ftell64
// pcap_findalldevs_ex
// pcap_get_required_select_timeout
// pcap_open
// pcap_parsesrcstr
// pcap_remoteact_accept
// pcap_remoteact_cleanup
// pcap_remoteact_close
// pcap_remoteact_list
// pcap_set_protocol_linux
// pcap_setsampling
}
#[cfg(libpcap_1_9_1)]
extern "C" {
// pcap_datalink_val_to_description_or_dlt
}
#[cfg(windows)]
#[link(name = "wpcap")]
pub const WINPCAP_MINTOCOPY_DEFAULT: c_int = 16000;
#[cfg(windows)]
#[link(name = "wpcap")]
extern "C" {
pub fn pcap_setmintocopy(arg1: *mut pcap_t, arg2: c_int) -> c_int;
}
#[cfg(not(windows))]
#[link(name = "pcap")]
extern "C" {
// pub fn pcap_inject(arg1: *mut pcap_t, arg2: *const c_void, arg3: size_t) -> c_int;
pub fn pcap_set_rfmon(arg1: *mut pcap_t, arg2: c_int) -> c_int;
}

View File

@@ -0,0 +1,68 @@
use mio::{Ready, Poll, PollOpt, Token};
use mio::event::Evented;
use mio::unix::EventedFd;
use std::io;
use std::marker::Unpin;
#[cfg(not(windows))]
use std::os::unix::io::RawFd;
use std::pin::Pin;
use super::Activated;
use super::Packet;
use super::Error;
use super::State;
use super::Capture;
pub struct SelectableFd {
fd: RawFd
}
impl Evented for SelectableFd {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt)
-> io::Result<()>
{
EventedFd(&self.fd).register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt)
-> io::Result<()>
{
EventedFd(&self.fd).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
EventedFd(&self.fd).deregister(poll)
}
}
pub trait PacketCodec {
type Type;
fn decode<'a>(&mut self, packet: Packet<'a>) -> Result<Self::Type, Error>;
}
pub struct PacketStream<T: State + ? Sized, C> {
cap: Capture<T>,
fd: tokio::io::PollEvented<SelectableFd>,
codec: C,
}
impl<T: Activated + ? Sized, C: PacketCodec> PacketStream<T, C> {
pub fn new(cap: Capture<T>, fd: RawFd, codec: C) -> Result<PacketStream<T, C>, Error> {
Ok(PacketStream { cap, fd: tokio::io::PollEvented::new(SelectableFd { fd })?, codec })
}
}
impl<'a, T: Activated + ? Sized + Unpin, C: PacketCodec + Unpin> futures::Stream for PacketStream<T, C> {
type Item = Result<C::Type, Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut core::task::Context) -> futures::task::Poll<Option<Self::Item>> {
let stream = Pin::into_inner(self);
let p = match stream.cap.next_noblock(cx, &mut stream.fd) {
Ok(t) => t,
Err(Error::IoError(ref e)) if *e == ::std::io::ErrorKind::WouldBlock => {
return futures::task::Poll::Pending;
}
Err(e) => return futures::task::Poll::Ready(Some(Err(e))),
};
let frame_result = stream.codec.decode(p);
futures::task::Poll::Ready(Some(frame_result))
}
}

View File

@@ -0,0 +1,43 @@
#![allow(dead_code)]
use std::fmt;
use std::marker::PhantomData;
use std::ops::Deref;
pub struct Unique<T: ?Sized> {
pointer: *const T,
_marker: PhantomData<T>,
}
unsafe impl<T: Send + ?Sized> Send for Unique<T> {}
unsafe impl<T: Sync + ?Sized> Sync for Unique<T> {}
impl<T: ?Sized> Unique<T> {
pub unsafe fn new(ptr: *mut T) -> Unique<T> {
Unique {
pointer: ptr,
_marker: PhantomData,
}
}
pub unsafe fn get(&self) -> &T {
&*self.pointer
}
pub unsafe fn get_mut(&mut self) -> &mut T {
&mut ***self
}
}
impl<T: ?Sized> Deref for Unique<T> {
type Target = *mut T;
#[inline]
fn deref(&self) -> &*mut T {
unsafe { &*(&self.pointer as *const *const T as *const *mut T) }
}
}
impl<T> fmt::Pointer for Unique<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Pointer::fmt(&self.pointer, f)
}
}

Binary file not shown.

Binary file not shown.

290
__network/pcap/tests/lib.rs Normal file
View File

@@ -0,0 +1,290 @@
#[cfg(not(windows))]
use std::io;
use std::ops::Add;
use std::path::Path;
use tempdir::TempDir;
use pcap::{Active, Activated, Offline, Capture, Packet, PacketHeader, Linktype};
#[cfg(not(windows))]
use pcap::{Precision, Error};
#[cfg(not(windows))]
#[allow(non_camel_case_types)]
type time_t = libc::time_t;
#[cfg(windows)]
#[allow(non_camel_case_types)]
type time_t = libc::c_long;
#[cfg(not(windows))]
#[allow(non_camel_case_types)]
type suseconds_t = libc::suseconds_t;
#[cfg(windows)]
#[allow(non_camel_case_types)]
type suseconds_t = libc::c_long;
#[test]
fn read_packet_with_full_data() {
let mut capture = capture_from_test_file("packet_snaplen_65535.pcap");
assert_eq!(capture.next().unwrap().len(), 98);
}
#[test]
fn read_packet_with_truncated_data() {
let mut capture = capture_from_test_file("packet_snaplen_20.pcap");
assert_eq!(capture.next().unwrap().len(), 20);
}
fn capture_from_test_file(file_name: &str) -> Capture<Offline> {
let path = Path::new("tests/data/").join(file_name);
Capture::from_file(path).unwrap()
}
#[test]
fn unify_activated() {
#![allow(dead_code)]
fn test1() -> Capture<Active> {
loop {}
}
fn test2() -> Capture<Offline> {
loop {}
}
fn maybe(a: bool) -> Capture<dyn Activated> {
if a { test1().into() } else { test2().into() }
}
fn also_maybe(a: &mut Capture<dyn Activated>) {
a.filter("whatever filter string, this won't be run anyway").unwrap();
}
}
#[derive(Clone)]
pub struct Packets {
headers: Vec<PacketHeader>,
data: Vec<Vec<u8>>,
}
impl Packets {
pub fn new() -> Packets {
Packets {
headers: vec![],
data: vec![],
}
}
pub fn push(&mut self,
tv_sec: time_t,
tv_usec: suseconds_t,
caplen: u32,
len: u32,
data: &[u8]) {
self.headers.push(PacketHeader {
ts: libc::timeval { tv_sec, tv_usec },
caplen,
len,
});
self.data.push(data.to_vec());
}
pub fn foreach<F: FnMut(&Packet)>(&self, mut f: F) {
for (header, data) in self.headers.iter().zip(self.data.iter()) {
let packet = Packet::new(header, &data);
f(&packet);
}
}
pub fn verify<T: Activated + ?Sized>(&self, cap: &mut Capture<T>) {
for (header, data) in self.headers.iter().zip(self.data.iter()) {
assert_eq!(cap.next().unwrap(), Packet::new(header, &data));
}
assert!(cap.next().is_err());
}
}
impl<'a> Add for &'a Packets {
type Output = Packets;
fn add(self, rhs: &'a Packets) -> Packets {
let mut packets = self.clone();
packets.headers.extend(rhs.headers.iter());
packets.data.extend(rhs.data.iter().cloned());
packets
}
}
#[test]
fn capture_dead_savefile() {
let mut packets = Packets::new();
packets.push(1460408319, 1234, 1, 1, &[1]);
packets.push(1460408320, 4321, 1, 1, &[2]);
let dir = TempDir::new("pcap").unwrap();
let tmpfile = dir.path().join("test.pcap");
let cap = Capture::dead(Linktype(1)).unwrap();
let mut save = cap.savefile(&tmpfile).unwrap();
packets.foreach(|p| save.write(p));
drop(save);
let mut cap = Capture::from_file(&tmpfile).unwrap();
packets.verify(&mut cap);
}
#[test]
#[cfg(libpcap_1_7_2)]
fn capture_dead_savefile_append() {
let mut packets1 = Packets::new();
packets1.push(1460408319, 1234, 1, 1, &[1]);
packets1.push(1460408320, 4321, 1, 1, &[2]);
let mut packets2 = Packets::new();
packets2.push(1460408321, 2345, 1, 1, &[3]);
packets2.push(1460408322, 5432, 1, 1, &[4]);
let packets = &packets1 + &packets2;
let dir = TempDir::new("pcap").unwrap();
let tmpfile = dir.path().join("test.pcap");
let cap = Capture::dead(Linktype(1)).unwrap();
let mut save = cap.savefile(&tmpfile).unwrap();
packets1.foreach(|p| save.write(p));
drop(save);
let cap = Capture::dead(Linktype(1)).unwrap();
let mut save = cap.savefile_append(&tmpfile).unwrap();
packets2.foreach(|p| save.write(p));
drop(save);
let mut cap = Capture::from_file(&tmpfile).unwrap();
packets.verify(&mut cap);
}
#[test]
#[cfg(not(windows))]
fn test_raw_fd_api() {
use std::fs::File;
use std::thread;
use std::io::prelude::*;
#[cfg(not(windows))]
use std::os::unix::io::{RawFd, FromRawFd};
// Create a total of more than 64K data (> max pipe buf size)
const N_PACKETS: usize = 64;
let data: Vec<u8> = (0..191).cycle().take(N_PACKETS * 1024).collect();
let mut packets = Packets::new();
for i in 0..N_PACKETS {
packets.push(1460408319 + i as time_t,
1000 + i as suseconds_t,
1024,
1024,
&data[i * 1024..(i + 1) * 1024]);
}
let dir = TempDir::new("pcap").unwrap();
let tmpfile = dir.path().join("test.pcap");
// Write all packets to test.pcap savefile
let cap = Capture::dead(Linktype(1)).unwrap();
let mut save = cap.savefile(&tmpfile).unwrap();
packets.foreach(|p| save.write(p));
drop(save);
assert_eq!(Capture::from_raw_fd(-999).err().unwrap(),
Error::InvalidRawFd);
#[cfg(libpcap_1_5_0)]
{
assert_eq!(Capture::from_raw_fd_with_precision(-999, Precision::Micro).err().unwrap(),
Error::InvalidRawFd);
}
assert_eq!(cap.savefile_raw_fd(-999).err().unwrap(),
Error::InvalidRawFd);
// Create an unnamed pipe
let mut pipe = [0 as libc::c_int; 2];
assert_eq!(unsafe { libc::pipe(pipe.as_mut_ptr()) }, 0);
let (fd_in, fd_out) = (pipe[0], pipe[1]);
let filename = dir.path().join("test2.pcap");
let packets_c = packets.clone();
let pipe_thread = thread::spawn(move || {
// Write all packets to the pipe
let cap = Capture::dead(Linktype(1)).unwrap();
let mut save = cap.savefile_raw_fd(fd_out).unwrap();
packets_c.foreach(|p| save.write(p));
// fd_out will be closed by savefile destructor
});
// Save the pcap from pipe in a separate thread.
// Hypothetically, we could do any sort of processing here,
// like encoding to a gzip stream.
let mut file_in = unsafe { File::from_raw_fd(fd_in) };
let mut file_out = File::create(&filename).unwrap();
io::copy(&mut file_in, &mut file_out).unwrap();
// Verify that the contents match
let filename = dir.path().join("test2.pcap");
let (mut v1, mut v2) = (vec![], vec![]);
File::open(&tmpfile).unwrap().read_to_end(&mut v1).unwrap();
File::open(&filename).unwrap().read_to_end(&mut v2).unwrap();
assert_eq!(v1, v2);
// Join thread.
pipe_thread.join().unwrap();
#[cfg(libpcap_1_5_0)]
fn from_raw_fd_with_precision(fd: RawFd, precision: Precision) -> Capture<Offline> {
Capture::from_raw_fd_with_precision(fd, precision).unwrap()
}
#[cfg(not(libpcap_1_5_0))]
fn from_raw_fd_with_precision(fd: RawFd, _: Precision) -> Capture<Offline> {
Capture::from_raw_fd(fd).unwrap()
}
for with_tstamp in &[false, true] {
// Create an unnamed pipe
let mut pipe = [0 as libc::c_int; 2];
assert_eq!(unsafe { libc::pipe(pipe.as_mut_ptr()) }, 0);
let (fd_in, fd_out) = (pipe[0], pipe[1]);
let filename = tmpfile.clone();
let pipe_thread = thread::spawn(move || {
// Cat the pcap into the pipe in a separate thread.
// Hypothetically, we could do any sort of processing here,
// like decoding from a gzip stream.
let mut file_in = File::open(&filename).unwrap();
let mut file_out = unsafe { File::from_raw_fd(fd_out) };
io::copy(&mut file_in, &mut file_out).unwrap();
});
// Open the capture with pipe's file descriptor
let mut cap = if *with_tstamp {
from_raw_fd_with_precision(fd_in, Precision::Micro)
} else {
Capture::from_raw_fd(fd_in).unwrap()
};
// Verify that packets match
packets.verify(&mut cap);
// Join thread.
pipe_thread.join().unwrap();
}
}
#[test]
fn test_linktype() {
let capture = capture_from_test_file("packet_snaplen_65535.pcap");
let linktype = capture.get_datalink();
assert!(linktype.get_name().is_ok());
assert_eq!(linktype.get_name().unwrap(), String::from("EN10MB"));
assert!(linktype.get_description().is_ok());
}
#[test]
fn test_error() {
let mut capture = capture_from_test_file("packet_snaplen_65535.pcap");
// Trying to get stats from offline capture should error.
assert!(capture.stats().err().is_some());
}