feat: add pcap
This commit is contained in:
29
__network/pcap/CHANGELOG.md
Normal file
29
__network/pcap/CHANGELOG.md
Normal 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
65
__network/pcap/Cargo.toml
Normal 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"]
|
||||||
202
__network/pcap/LICENSE-APACHE
Normal file
202
__network/pcap/LICENSE-APACHE
Normal 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.
|
||||||
|
|
||||||
19
__network/pcap/LICENSE-MIT
Normal file
19
__network/pcap/LICENSE-MIT
Normal 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
76
__network/pcap/README.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# pcap [](https://travis-ci.org/ebfull/pcap) [](https://crates.io/crates/pcap) [](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
122
__network/pcap/build.rs
Normal 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);
|
||||||
|
}
|
||||||
15
__network/pcap/examples/easylisten.rs
Normal file
15
__network/pcap/examples/easylisten.rs
Normal 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());
|
||||||
|
}
|
||||||
18
__network/pcap/examples/getdevices.rs
Normal file
18
__network/pcap/examples/getdevices.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
__network/pcap/examples/getstatistics.rs
Normal file
22
__network/pcap/examples/getstatistics.rs
Normal 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
|
||||||
|
);
|
||||||
|
}
|
||||||
16
__network/pcap/examples/listenlocalhost.rs
Normal file
16
__network/pcap/examples/listenlocalhost.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
__network/pcap/examples/savefile.rs
Normal file
37
__network/pcap/examples/savefile.rs
Normal 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);
|
||||||
|
}
|
||||||
47
__network/pcap/examples/streamlisten.rs
Normal file
47
__network/pcap/examples/streamlisten.rs
Normal 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
870
__network/pcap/src/lib.rs
Normal 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
221
__network/pcap/src/raw.rs
Normal 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;
|
||||||
|
}
|
||||||
68
__network/pcap/src/stream.rs
Normal file
68
__network/pcap/src/stream.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
43
__network/pcap/src/unique.rs
Normal file
43
__network/pcap/src/unique.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
__network/pcap/tests/data/packet_snaplen_20.pcap
Normal file
BIN
__network/pcap/tests/data/packet_snaplen_20.pcap
Normal file
Binary file not shown.
BIN
__network/pcap/tests/data/packet_snaplen_65535.pcap
Normal file
BIN
__network/pcap/tests/data/packet_snaplen_65535.pcap
Normal file
Binary file not shown.
290
__network/pcap/tests/lib.rs
Normal file
290
__network/pcap/tests/lib.rs
Normal 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());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user