diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 490f35b..0000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "pcap-sys/libpcap"] - path = pcap-sys/libpcap - url = https://github.com/the-tcpdump-group/libpcap -[submodule "nl-sys/libnl"] - path = nl-sys/libnl - url = https://github.com/thom311/libnl diff --git a/flake.lock b/flake.lock index a8c5c72..5932a7d 100644 --- a/flake.lock +++ b/flake.lock @@ -20,6 +20,27 @@ "type": "github" } }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1705991032, + "narHash": "sha256-C+ePPXLMOxkJfJMqihUC1XbNr9Xy2F6BpWrmGDsgzXk=", + "owner": "nix-community", + "repo": "fenix", + "rev": "2189a3d994aaee6f83d3fc92deb13c458dd03dbd", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -107,11 +128,30 @@ "root": { "inputs": { "crane": "crane", + "fenix": "fenix", "flake-utils": "flake-utils", "libnl": "libnl", "libpcap-src": "libpcap-src", "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" + "rust-overlay": "rust-overlay", + "winpcap": "winpcap" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1705864945, + "narHash": "sha256-ZATChFWHToTZQFLlzrzDUX8fjEbMHHBIyPaZU1JGmjI=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "d410d4a2baf9e99b37b03dd42f06238b14374bf7", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" } }, "rust-overlay": { @@ -122,11 +162,11 @@ ] }, "locked": { - "lastModified": 1705976279, - "narHash": "sha256-Zx97bJ3+O8IP70uJPD//rRsr8bcxICISMTZUT/L9eFk=", + "lastModified": 1706062676, + "narHash": "sha256-aIgYdyQyKRHZ8gSmke3DE09D5ypK4tP+XYqrKPAd/3M=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "f889dc31ef97835834bdc3662394ebdb3c96b974", + "rev": "81eb4bdb219d97d749f152eb4de6a081b088b08d", "type": "github" }, "original": { @@ -164,6 +204,19 @@ "repo": "default", "type": "github" } + }, + "winpcap": { + "flake": false, + "locked": { + "lastModified": 1277475016, + "narHash": "sha256-lea+6c9obTpxWosi2xRXUxV4+whsCTWMJ7wxZNASRz8=", + "type": "tarball", + "url": "https://www.winpcap.org/install/bin/WpdPack_4_1_2.zip" + }, + "original": { + "type": "tarball", + "url": "https://www.winpcap.org/install/bin/WpdPack_4_1_2.zip" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index c22f43b..6416ba5 100644 --- a/flake.nix +++ b/flake.nix @@ -12,6 +12,10 @@ inputs.nixpkgs.follows = "nixpkgs"; }; flake-utils.url = "github:numtide/flake-utils"; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; libpcap-src = { url = "git+https://github.com/the-tcpdump-group/libpcap"; @@ -21,15 +25,19 @@ url = "git+https://github.com/thom311/libnl"; flake = false; }; + winpcap = { + url = "https://www.winpcap.org/install/bin/WpdPack_4_1_2.zip"; + flake = false; + }; }; - outputs = - { self, nixpkgs, flake-utils, crane, rust-overlay, libpcap-src, libnl }: + outputs = { self, nixpkgs, flake-utils, crane, fenix, rust-overlay + , libpcap-src, libnl, winpcap }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; - overlays = [ (import rust-overlay) ]; + overlays = [ (import rust-overlay) fenix.overlays.default ]; }; buildTools = with pkgs; [ @@ -104,33 +112,20 @@ cargoExtraArgs = "-p sparse-c2-beacon --locked"; }); in with pkgs; { - devShells.default = craneLib.devShell rec { + devShells.default = craneLib.devShell { name = "sparse"; - buildInputs = buildTools; + buildInputs = windowsBuildTools ++ [ rust-analyzer ]; SPARSE_BUILD_LIBPCAP = "${libpcap-src}"; SPARSE_BUILD_LIBNL = "${libnl}"; }; - packages = rec { + packages = { inherit sparse-05-linux-server sparse-05-windows-server; inherit sparse-c2-linux-beacon; default = sparse-c2-linux-beacon; - - windows = craneLib.buildPackage (commonArgs // { - doCheck = false; - - CARGO_BUILD_TARGET = "x86_64-pc-windows-gnu"; - - depsBuildBuild = [ - pkgsCross.mingwW64.stdenv.cc - pkgsCross.mingwW64.windows.pthreads - ]; - - artifacts = windowsArtifacts; - }); }; }); } diff --git a/nl-sys/build.rs b/nl-sys/build.rs index 103da75..c682f07 100644 --- a/nl-sys/build.rs +++ b/nl-sys/build.rs @@ -22,7 +22,8 @@ fn main() -> std::io::Result<()> { let mut options = CopyOptions::new(); options.copy_inside = true; - copy(std::env!("SPARSE_BUILD_LIBNL"), &libnl_src, &options); + options.skip_exist = true; + copy(std::env!("SPARSE_BUILD_LIBNL"), &libnl_src, &options).expect("could not copy nl"); let dst = autotools::Config::new(libnl_src).reconf("-vi").build(); diff --git a/nl-sys/libnl b/nl-sys/libnl deleted file mode 160000 index cbafad9..0000000 --- a/nl-sys/libnl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cbafad9ddf24caef5230fef715d34f0539603be0 diff --git a/pcap-sys/build.rs b/pcap-sys/build.rs index 744ef76..a01b364 100644 --- a/pcap-sys/build.rs +++ b/pcap-sys/build.rs @@ -23,7 +23,8 @@ fn main() { let mut options = CopyOptions::new(); options.copy_inside = true; - copy(std::env!("SPARSE_BUILD_LIBPCAP"), &libpcap_src, &options); + options.skip_exist = true; + copy(std::env!("SPARSE_BUILD_LIBPCAP"), &libpcap_src, &options).expect("could not copy libpcap source code to build"); let dst = cmake::Config::new(&libpcap_src) .define("BUILD_SHARED_LIBS", "OFF") diff --git a/pcap-sys/libpcap b/pcap-sys/libpcap deleted file mode 160000 index ab3f6a6..0000000 --- a/pcap-sys/libpcap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ab3f6a677ba66a9679c6f3412f0320a5776842d0 diff --git a/pcap-sys/src/lib.rs b/pcap-sys/src/lib.rs index 02b5c1e..c41403e 100644 --- a/pcap-sys/src/lib.rs +++ b/pcap-sys/src/lib.rs @@ -14,17 +14,15 @@ // along with this program. If not, see . use std::{ - collections::HashMap, ffi::{CStr, CString}, - os::fd::{AsRawFd, RawFd}, - pin::Pin, - ptr, slice, - task::{self, Poll}, + ptr, slice }; pub mod error; mod ffi; pub use packets; +#[cfg(target_os = "linux")] +pub mod stream; pub mod consts { pub use super::ffi::{ @@ -51,9 +49,6 @@ pub mod consts { } use ffi::PcapDevIf; -use futures::{ready, StreamExt}; -use tokio::io::unix::AsyncFd; -use tokio_stream::StreamMap; pub struct PcapDevIterator { dev_if: *const PcapDevIf, @@ -409,327 +404,15 @@ impl Interface { (info.fail_error, count) } - pub fn stream(mut self) -> error::Result> { + #[cfg(target_os = "linux")] + pub fn stream(mut self) -> error::Result> { self.set_non_blocking(true)?; - Ok(InterfaceStream { - inner: AsyncFd::with_interest( - InternalInterfaceStream::::new(unsafe { std::mem::transmute(self) })?, + Ok(stream::InterfaceStream { + inner: tokio::io::unix::AsyncFd::with_interest( + stream::InternalInterfaceStream::::new(unsafe { std::mem::transmute(self) })?, tokio::io::Interest::READABLE, )?, }) } } - -struct InternalInterfaceStream { - interface: Interface, - fd: RawFd, -} - -impl InternalInterfaceStream { - fn new(interface: Interface) -> error::Result> { - let fd = unsafe { ffi::pcap_get_selectable_fd(interface.dev) }; - if fd == -1 { - return Err(error::Error::InvalidPcapFd); - } - Ok(Self { interface, fd }) - } -} - -impl AsRawFd for InternalInterfaceStream { - fn as_raw_fd(&self) -> RawFd { - self.fd - } -} - -pub struct InterfaceStream { - inner: AsyncFd>, -} - -impl InterfaceStream { - pub fn sendpacket(&mut self, packet: packets::EthernetPkt) -> error::Result<()> { - self.inner.get_mut().interface.sendpacket(packet) - } - - pub fn set_filter( - &mut self, - filter: &str, - optimize: bool, - mask: Option, - ) -> error::Result> { - self.inner - .get_mut() - .interface - .set_filter(filter, optimize, mask) - } -} - -impl Unpin for InterfaceStream {} - -impl futures::Stream for InterfaceStream { - type Item = error::Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { - let stream = Pin::into_inner(self); - - loop { - let mut guard = ready!(stream.inner.poll_read_ready_mut(cx))?; - - match guard.try_io(|inner| match inner.get_mut().interface.next_packet() { - Ok(p) => Ok(Ok(p)), - Err(e) => Ok(Err(e)), - }) { - Ok(result) => { - return Poll::Ready(Some(result?)); - } - Err(_would_block) => continue, - } - } - } -} - -pub fn new_aggregate_interface_filtered( - crash: bool, - mut f: F, -) -> error::Result> -where - F: FnMut(&str) -> bool, -{ - let interfaces = if crash { - PcapDevIterator::new()? - .filter(|s| (f)(s)) - .map(|if_name| { - let new_name = if_name.clone(); - Interface::::new(&if_name) - .map(|interface| (if_name, interface)) - .map_err(|e| e.add_ifname(&new_name)) - }) - .collect::>>()? - } else { - PcapDevIterator::new()? - .filter(|s| (f)(s)) - .filter_map(|if_name| { - let new_name = if_name.clone(); - Interface::::new(&if_name) - .map(|interface| (if_name, interface)) - .ok() - .or_else(|| { - println!("{} failed to create device", new_name); - None - }) - }) - .collect::>() - }; - - Ok(AggregateInterface { interfaces, crash }) -} - -pub fn new_aggregate_interface(crash: bool) -> error::Result> { - new_aggregate_interface_filtered(crash, |_| true) -} - -pub struct AggregateInterface { - interfaces: HashMap>, - crash: bool, -} - -impl AggregateInterface { - pub fn set_non_blocking(&mut self, nonblocking: bool) -> error::Result<()> { - for (n, i) in self.interfaces.iter_mut() { - i.set_non_blocking(nonblocking) - .map_err(|e| e.add_ifname(n))?; - } - - Ok(()) - } - - pub fn lookupnets(&self) -> error::Result> { - self.interfaces - .iter() - .map(|(name, interface)| { - interface - .lookupnet() - .map(|net| (&**name, net)) - .map_err(|e| e.add_ifname(&name)) - }) - .collect::>() - } - - pub fn get_ifnames(&self) -> Vec<&str> { - self.interfaces.keys().map(|n| &**n).collect::<_>() - } -} - -impl AggregateInterface { - pub fn set_promisc(&mut self, promisc: bool) -> error::Result<()> { - for (n, i) in self.interfaces.iter_mut() { - i.set_promisc(promisc).map_err(|e| e.add_ifname(n))?; - } - - Ok(()) - } - - pub fn set_buffer_size(&mut self, bufsize: i32) -> error::Result<()> { - for (n, i) in self.interfaces.iter_mut() { - i.set_buffer_size(bufsize).map_err(|e| e.add_ifname(n))?; - } - - Ok(()) - } - - pub fn set_timeout(&mut self, timeout: i32) -> error::Result<()> { - for (n, i) in self.interfaces.iter_mut() { - i.set_timeout(timeout).map_err(|e| e.add_ifname(n))?; - } - - Ok(()) - } - - pub fn activate(self) -> error::Result> { - Ok(AggregateInterface { - interfaces: if self.crash { - self.interfaces - .into_iter() - .map(|(name, interface)| { - let new_name = name.clone(); - interface - .activate() - .map(|interface| (name, interface)) - .map_err(|e| e.add_ifname(&new_name)) - }) - .collect::>()? - } else { - self.interfaces - .into_iter() - .filter_map(|(name, interface)| { - let name_clone = name.clone(); - interface - .activate() - .map(|interface| (name, interface)) - .ok() - .or_else(|| { - println!("{} failed to activate", name_clone); - None - }) - }) - .collect::<_>() - }, - crash: self.crash, - }) - } -} - -impl AggregateInterface { - pub fn datalinks(&self) -> HashMap<&str, i32> { - self.interfaces - .iter() - .map(|(name, interface)| (&**name, interface.datalink())) - .collect::<_>() - } - - pub fn prune(&mut self, mut f: F) - where - F: FnMut(&str, &mut Interface) -> bool, - { - let to_prune = self - .interfaces - .iter_mut() - .filter_map(|(k, v)| if (f)(k, v) { Some(k.clone()) } else { None }) - .collect::>(); - - for name in to_prune { - self.interfaces.remove(&name); - } - } - - pub fn set_filter( - &mut self, - filter: &str, - optimize: bool, - mask: Option, - ) -> error::Result>> { - if self.crash { - self.interfaces - .iter_mut() - .map(|(name, interface)| { - interface - .set_filter(filter, optimize, mask) - .map(|bpf| (&**name, bpf)) - .map_err(|e| e.add_ifname(&name)) - }) - .collect::>() - } else { - Ok(self - .interfaces - .iter_mut() - .filter_map(|(name, interface)| { - let name_clone = name.clone(); - interface - .set_filter(filter, optimize, mask) - .map(|bpf| (&**name, bpf)) - .ok() - .or_else(|| { - println!("{} failed to set filter", name_clone); - None - }) - }) - .collect::<_>()) - } - } - - pub fn sendpacket(&self, ifname: &str, packet: packets::EthernetPkt) -> error::Result<()> { - if let Some(interface) = self.interfaces.get(ifname) { - interface - .sendpacket(packet) - .map_err(|e| e.add_ifname(ifname))?; - } - - Ok(()) - } -} - -impl AggregateInterface { - pub fn stream(self) -> error::Result> { - Ok(AggregateInterfaceStream { - streams: self - .interfaces - .into_iter() - .map(|(ifname, interface)| { - let new_name = ifname.clone(); - interface - .stream() - .map(|stream| (ifname, stream)) - .map_err(|e| e.add_ifname(&new_name)) - }) - .collect::>()?, - }) - } -} - -pub struct AggregateInterfaceStream { - streams: StreamMap>, -} - -impl AggregateInterfaceStream { - pub fn get_ifnames(&self) -> Vec<&str> { - self.streams.keys().map(|n| &**n).collect::<_>() - } - - pub fn sendpacket(&mut self, ifname: &str, packet: packets::EthernetPkt) -> error::Result<()> { - if let Some(interface) = self.streams.values_mut().find(|interface| { - interface.inner.get_ref().interface.dev_name.as_bytes() == ifname.as_bytes() - }) { - interface.sendpacket(packet)?; - } - - Ok(()) - } -} - -impl futures::Stream for AggregateInterfaceStream { - type Item = (String, error::Result); - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { - self.streams.poll_next_unpin(cx) - } -} diff --git a/pcap-sys/src/stream.rs b/pcap-sys/src/stream.rs new file mode 100644 index 0000000..2554161 --- /dev/null +++ b/pcap-sys/src/stream.rs @@ -0,0 +1,340 @@ +// Copyright (C) 2023 Andrew Rioux +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use std::{ + collections::HashMap, + os::fd::{AsRawFd, RawFd}, + pin::Pin, + task::{self, Poll}, +}; + +use futures::{ready, StreamExt}; +use tokio::io::unix::AsyncFd; +use tokio_stream::StreamMap; + +use super::{error, ffi, packets, Activated, NotListening, Disabled, State, Interface, DevActivated, DevDisabled, PcapDevIterator}; + +pub(crate) struct InternalInterfaceStream { + interface: Interface, + fd: RawFd, +} + +impl InternalInterfaceStream { + pub(crate) fn new(interface: Interface) -> error::Result> { + let fd = unsafe { ffi::pcap_get_selectable_fd(interface.dev) }; + if fd == -1 { + return Err(error::Error::InvalidPcapFd); + } + Ok(Self { interface, fd }) + } +} + +impl AsRawFd for InternalInterfaceStream { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +pub struct InterfaceStream { + pub(crate) inner: AsyncFd>, +} + +impl InterfaceStream { + pub fn sendpacket(&mut self, packet: packets::EthernetPkt) -> error::Result<()> { + self.inner.get_mut().interface.sendpacket(packet) + } + + pub fn set_filter( + &mut self, + filter: &str, + optimize: bool, + mask: Option, + ) -> error::Result> { + self.inner + .get_mut() + .interface + .set_filter(filter, optimize, mask) + } +} + +impl Unpin for InterfaceStream {} + +impl futures::Stream for InterfaceStream { + type Item = error::Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { + let stream = Pin::into_inner(self); + + loop { + let mut guard = ready!(stream.inner.poll_read_ready_mut(cx))?; + + match guard.try_io(|inner| match inner.get_mut().interface.next_packet() { + Ok(p) => Ok(Ok(p)), + Err(e) => Ok(Err(e)), + }) { + Ok(result) => { + return Poll::Ready(Some(result?)); + } + Err(_would_block) => continue, + } + } + } +} + +pub fn new_aggregate_interface_filtered( + crash: bool, + mut f: F, +) -> error::Result> +where + F: FnMut(&str) -> bool, +{ + let interfaces = if crash { + PcapDevIterator::new()? + .filter(|s| (f)(s)) + .map(|if_name| { + let new_name = if_name.clone(); + Interface::::new(&if_name) + .map(|interface| (if_name, interface)) + .map_err(|e| e.add_ifname(&new_name)) + }) + .collect::>>()? + } else { + PcapDevIterator::new()? + .filter(|s| (f)(s)) + .filter_map(|if_name| { + let new_name = if_name.clone(); + Interface::::new(&if_name) + .map(|interface| (if_name, interface)) + .ok() + .or_else(|| { + println!("{} failed to create device", new_name); + None + }) + }) + .collect::>() + }; + + Ok(AggregateInterface { interfaces, crash }) +} + +pub fn new_aggregate_interface(crash: bool) -> error::Result> { + new_aggregate_interface_filtered(crash, |_| true) +} + +pub struct AggregateInterface { + interfaces: HashMap>, + crash: bool, +} + +impl AggregateInterface { + pub fn set_non_blocking(&mut self, nonblocking: bool) -> error::Result<()> { + for (n, i) in self.interfaces.iter_mut() { + i.set_non_blocking(nonblocking) + .map_err(|e| e.add_ifname(n))?; + } + + Ok(()) + } + + pub fn lookupnets(&self) -> error::Result> { + self.interfaces + .iter() + .map(|(name, interface)| { + interface + .lookupnet() + .map(|net| (&**name, net)) + .map_err(|e| e.add_ifname(&name)) + }) + .collect::>() + } + + pub fn get_ifnames(&self) -> Vec<&str> { + self.interfaces.keys().map(|n| &**n).collect::<_>() + } +} + +impl AggregateInterface { + pub fn set_promisc(&mut self, promisc: bool) -> error::Result<()> { + for (n, i) in self.interfaces.iter_mut() { + i.set_promisc(promisc).map_err(|e| e.add_ifname(n))?; + } + + Ok(()) + } + + pub fn set_buffer_size(&mut self, bufsize: i32) -> error::Result<()> { + for (n, i) in self.interfaces.iter_mut() { + i.set_buffer_size(bufsize).map_err(|e| e.add_ifname(n))?; + } + + Ok(()) + } + + pub fn set_timeout(&mut self, timeout: i32) -> error::Result<()> { + for (n, i) in self.interfaces.iter_mut() { + i.set_timeout(timeout).map_err(|e| e.add_ifname(n))?; + } + + Ok(()) + } + + pub fn activate(self) -> error::Result> { + Ok(AggregateInterface { + interfaces: if self.crash { + self.interfaces + .into_iter() + .map(|(name, interface)| { + let new_name = name.clone(); + interface + .activate() + .map(|interface| (name, interface)) + .map_err(|e| e.add_ifname(&new_name)) + }) + .collect::>()? + } else { + self.interfaces + .into_iter() + .filter_map(|(name, interface)| { + let name_clone = name.clone(); + interface + .activate() + .map(|interface| (name, interface)) + .ok() + .or_else(|| { + println!("{} failed to activate", name_clone); + None + }) + }) + .collect::<_>() + }, + crash: self.crash, + }) + } +} + +impl AggregateInterface { + pub fn datalinks(&self) -> HashMap<&str, i32> { + self.interfaces + .iter() + .map(|(name, interface)| (&**name, interface.datalink())) + .collect::<_>() + } + + pub fn prune(&mut self, mut f: F) + where + F: FnMut(&str, &mut Interface) -> bool, + { + let to_prune = self + .interfaces + .iter_mut() + .filter_map(|(k, v)| if (f)(k, v) { Some(k.clone()) } else { None }) + .collect::>(); + + for name in to_prune { + self.interfaces.remove(&name); + } + } + + pub fn set_filter( + &mut self, + filter: &str, + optimize: bool, + mask: Option, + ) -> error::Result>> { + if self.crash { + self.interfaces + .iter_mut() + .map(|(name, interface)| { + interface + .set_filter(filter, optimize, mask) + .map(|bpf| (&**name, bpf)) + .map_err(|e| e.add_ifname(&name)) + }) + .collect::>() + } else { + Ok(self + .interfaces + .iter_mut() + .filter_map(|(name, interface)| { + let name_clone = name.clone(); + interface + .set_filter(filter, optimize, mask) + .map(|bpf| (&**name, bpf)) + .ok() + .or_else(|| { + println!("{} failed to set filter", name_clone); + None + }) + }) + .collect::<_>()) + } + } + + pub fn sendpacket(&self, ifname: &str, packet: packets::EthernetPkt) -> error::Result<()> { + if let Some(interface) = self.interfaces.get(ifname) { + interface + .sendpacket(packet) + .map_err(|e| e.add_ifname(ifname))?; + } + + Ok(()) + } +} + +impl AggregateInterface { + pub fn stream(self) -> error::Result> { + Ok(AggregateInterfaceStream { + streams: self + .interfaces + .into_iter() + .map(|(ifname, interface)| { + let new_name = ifname.clone(); + interface + .stream() + .map(|stream| (ifname, stream)) + .map_err(|e| e.add_ifname(&new_name)) + }) + .collect::>()?, + }) + } +} + +pub struct AggregateInterfaceStream { + streams: StreamMap>, +} + +impl AggregateInterfaceStream { + pub fn get_ifnames(&self) -> Vec<&str> { + self.streams.keys().map(|n| &**n).collect::<_>() + } + + pub fn sendpacket(&mut self, ifname: &str, packet: packets::EthernetPkt) -> error::Result<()> { + if let Some(interface) = self.streams.values_mut().find(|interface| { + interface.inner.get_ref().interface.dev_name.as_bytes() == ifname.as_bytes() + }) { + interface.sendpacket(packet)?; + } + + Ok(()) + } +} + +impl futures::Stream for AggregateInterfaceStream { + type Item = (String, error::Result); + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { + self.streams.poll_next_unpin(cx) + } +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f148683..eaad48a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] channel = "stable" -components = [ "rustfmt", "rust-std", "rustc-dev" ] +components = [] targets = [ "x86_64-pc-windows-gnu", "x86_64-unknown-linux-musl" ] profile = "minimal" diff --git a/sparse-05/sparse-05-server/src/interface.rs b/sparse-05/sparse-05-server/src/interface.rs index 100b209..e50d808 100644 --- a/sparse-05/sparse-05-server/src/interface.rs +++ b/sparse-05/sparse-05-server/src/interface.rs @@ -1,8 +1,8 @@ use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket}; -#[cfg(target_os = "linux")] +#[cfg(feature = "pcap")] use std::{sync::Arc, thread}; -#[cfg(target_os = "linux")] +#[cfg(feature = "pcap")] use anyhow::{anyhow, bail}; use packets::{self, EthernetPkt}; @@ -17,7 +17,7 @@ pub enum Interface { impl Interface { pub fn new(ttype: TransportType, port: u16) -> anyhow::Result { match ttype { - #[cfg(target_os = "linux")] + #[cfg(feature = "pcap")] TransportType::RawUdp => { let mut interfaces = pcap_sys::PcapDevIterator::new()?; let interface_name = interfaces @@ -52,7 +52,7 @@ impl Interface { retry!(interface.set_promisc(false)); retry!(interface.set_timeout(10)); - let mut interface = retry!(interface.activate()); + let interface = retry!(interface.activate()); retry!(interface.set_filter(&format!("inbound and port {port}"), true, None)); @@ -65,7 +65,7 @@ impl Interface { Ok(Interface::RawUdp(interface, port)) } - #[cfg(target_os = "windows")] + #[cfg(not(feature = "pcap"))] TransportType::RawUdp => { panic!("transport not available!"); } @@ -78,7 +78,7 @@ impl Interface { pub fn split(self) -> anyhow::Result<(InterfaceSender, InterfaceReceiver)> { match self { - #[cfg(target_os = "linux")] + #[cfg(feature = "pcap")] Self::RawUdp(interface, _) => { let arc = Arc::new(interface); Ok(( @@ -98,7 +98,7 @@ impl Interface { } pub enum InterfaceSender { - #[cfg(target_os = "linux")] + #[cfg(feature = "pcap")] RawUdp(Arc>), Udp(UdpSocket), } @@ -106,7 +106,7 @@ pub enum InterfaceSender { impl InterfaceSender { pub fn sendpacket(&self, packet: EthernetPkt) -> anyhow::Result<()> { match self { - #[cfg(target_os = "linux")] + #[cfg(feature = "pcap")] Self::RawUdp(interf) => Ok(interf.sendpacket(packet)?), Self::Udp(interf) => { use packets::*; @@ -128,7 +128,7 @@ impl InterfaceSender { } pub enum InterfaceReceiver { - #[cfg(target_os = "linux")] + #[cfg(feature = "pcap")] RawUdp(Arc>), Udp(UdpSocket, u16), } @@ -139,7 +139,7 @@ impl InterfaceReceiver { F: FnMut(packets::EthernetPacket) -> anyhow::Result<()>, { match self { - #[cfg(target_os = "linux")] + #[cfg(feature = "pcap")] Self::RawUdp(interf) => interf.listen( move |_, packet| { let _ = (f)(packet.to_owned()); @@ -176,7 +176,7 @@ impl InterfaceReceiver { }, }; - #[cfg(target_os = "linux")] + #[cfg(feature = "pcap")] Ok(()) } }