extcap/
lib.rs

1//!
2//! This crate helps writing [extcap][wireshark-extcap] plugins for [Wireshark][wireshark].
3//!
4//! See [Extcap: Developer Guide][wireshark-extcap-dev] also.
5//!
6//! [wireshark]: https://www.wireshark.org/
7//! [wireshark-extcap]: https://www.wireshark.org/docs/man-pages/extcap.html
8//! [wireshark-extcap-dev]:https://www.wireshark.org/docs/wsdg_html_chunked/ChCaptureExtcap.html
9//!
10//! ## Quick Example
11//! ```
12//! use extcap::{Extcap, ExtcapListener, ExtcapResult, ExtcapWriter, IFace};
13//! use pcap_file::{pcap::PcapHeader, DataLink, PcapWriter};
14//!
15//! struct HelloDump {}
16//!
17//! impl ExtcapListener for HelloDump {
18//!     fn capture_header(&mut self, extcap: &Extcap, ifc: &IFace) -> PcapHeader {
19//!         PcapHeader { datalink: DataLink::USER10, ..Default::default() }
20//!     }
21//!
22//!     fn capture(&mut self, extcap: &Extcap, ifc: &IFace, mut pcap_writer: PcapWriter<ExtcapWriter>) -> ExtcapResult<()> {
23//!         let pkt = b"Hello Extcap!";
24//!         pcap_writer.write(0, 0, pkt, pkt.len() as u32);
25//!         Ok(())
26//!     }
27//! }
28//!
29//! fn main() -> Result<(), Box<dyn std::error::Error>> {
30//!     let mut ex = Extcap::new("hellodump");
31//!     ex.add_interface(IFace::new("helloif"));
32//!     ex.run(HelloDump {});
33//!     Ok(())
34//! }
35//! ```
36//! More examples can be found in the `examples` directory
37//!
38
39#![deny(missing_docs)]
40#![deny(warnings)]
41
42use std::collections::HashSet;
43use std::fmt::Display;
44use std::fs::File;
45use std::io::{self, Stdout, Write};
46
47use clap::{Arg, ArgGroup, ArgMatches, Command};
48#[cfg(feature = "ctrl-pipe")]
49use futures::future;
50#[cfg(feature = "async-api")]
51use futures::{channel::mpsc::Receiver, stream::StreamExt};
52use log::{debug, warn};
53#[cfg(feature = "async-api")]
54use pcap_file::pcap::Packet;
55use pcap_file::pcap::{PcapHeader, PcapWriter};
56
57mod error;
58pub use crate::error::ExtcapError;
59
60mod iface;
61pub use crate::iface::IFace;
62
63mod arg;
64pub use crate::arg::{IfArg, IfArgType, IfArgVal};
65
66mod control;
67pub use crate::control::{ButtonRole, Control, ControlType, ControlVal};
68
69#[cfg(feature = "ctrl-pipe")]
70mod control_pipe;
71#[cfg(feature = "ctrl-pipe")]
72use crate::control_pipe::ControlPipe;
73#[cfg(feature = "ctrl-pipe")]
74pub use crate::control_pipe::{ControlCmd, ControlMsg, CtrlPipes};
75
76#[cfg(feature = "ctrl-pipe")]
77mod control_pipe_runtime;
78
79const OPT_EXTCAP_VERSION: &str = "extcap-version";
80const OPT_EXTCAP_INTERFACES: &str = "extcap-interfaces";
81const OPT_EXTCAP_INTERFACE: &str = "extcap-interface";
82const OPT_EXTCAP_DTLS: &str = "extcap-dlts";
83const OPT_EXTCAP_CONFIG: &str = "extcap-config";
84const OPT_EXTCAP_RELOAD_OPTION: &str = "extcap-reload-option";
85const OPT_CAPTURE: &str = "capture";
86const OPT_EXTCAP_CAPTURE_FILTER: &str = "extcap-capture-filter";
87const OPT_FIFO: &str = "fifo";
88const OPT_EXTCAP_CONTROL_IN: &str = "extcap-control-in";
89const OPT_EXTCAP_CONTROL_OUT: &str = "extcap-control-out";
90const OPT_DEBUG: &str = "debug";
91const OPT_DEBUG_FILE: &str = "debug-file";
92
93fn print_opt_value<T: Display>(name: &str, value: &Option<T>) {
94    if let Some(val) = value {
95        print!("{{{}={}}}", name, val);
96    }
97}
98
99/// Possible writers for `PcapWriter`
100pub enum ExtcapWriter {
101    /// Writer to stdout
102    EWStdout(Stdout),
103    /// Writer to file
104    EWFile(File),
105}
106
107impl Write for ExtcapWriter {
108    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
109        match self {
110            ExtcapWriter::EWStdout(sout) => sout.write(buf),
111            ExtcapWriter::EWFile(file) => file.write(buf),
112        }
113    }
114
115    fn flush(&mut self) -> io::Result<()> {
116        match self {
117            ExtcapWriter::EWStdout(sout) => sout.flush(),
118            ExtcapWriter::EWFile(file) => file.flush(),
119        }
120    }
121}
122
123#[cfg(feature = "ctrl-pipe")]
124fn create_control_pipe(ctrl_in: &str, ctrl_out: &str) -> io::Result<ControlPipe> {
125    Ok(ControlPipe::new(
126        File::open(ctrl_in)?,
127        File::create(ctrl_out)?,
128    ))
129}
130
131fn create_pcap_writer(fifo: &str, pcap_header: PcapHeader) -> io::Result<PcapWriter<ExtcapWriter>> {
132    let writer = if fifo == "-" {
133        ExtcapWriter::EWStdout(io::stdout())
134    } else {
135        ExtcapWriter::EWFile(File::create(fifo)?)
136    };
137    PcapWriter::with_header(pcap_header, writer)
138        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
139}
140
141/// Extcap specific result
142pub type ExtcapResult<T> = Result<T, ExtcapError>;
143
144/// Packet receiver for async-api
145#[cfg(feature = "async-api")]
146pub type ExtcapReceiver = Receiver<Packet<'static>>;
147
148/// A trait for Extcap callbacks
149pub trait ExtcapListener {
150    /// Log initialization
151    fn init_log(&mut self, _extcap: &Extcap, _debug: bool, _debug_file: Option<&str>) {}
152
153    /// Interfaces update if it depends on passed options
154    fn update_interfaces(&mut self, _extcap: &mut Extcap) {}
155
156    /// Interface config reload required for some argument(s)
157    fn reload_option(
158        &mut self,
159        _extcap: &Extcap,
160        _ifc: &IFace,
161        _arg: &IfArg,
162    ) -> Option<Vec<IfArgVal>> {
163        None
164    }
165
166    /// Get capture header from listener
167    fn capture_header(&mut self, extcap: &Extcap, ifc: &IFace) -> PcapHeader;
168
169    /// Main capture loop
170    fn capture(
171        &mut self,
172        _extcap: &Extcap,
173        _ifc: &IFace,
174        _pcap_writer: PcapWriter<ExtcapWriter>,
175    ) -> ExtcapResult<()> {
176        unimplemented!()
177    }
178
179    /// Main async capture loop
180    #[cfg(feature = "async-api")]
181    fn capture_async(&mut self, _extcap: &Extcap, _ifc: &IFace) -> ExtcapResult<ExtcapReceiver> {
182        unimplemented!()
183    }
184
185    /// Main async capture loop with optional `CtrlPipes`
186    #[cfg(feature = "ctrl-pipe")]
187    fn capture_async_with_ctrl(
188        &mut self,
189        extcap: &Extcap,
190        ifc: &IFace,
191        _ctrl_pipes: Option<CtrlPipes>,
192    ) -> ExtcapResult<ExtcapReceiver> {
193        self.capture_async(extcap, ifc)
194    }
195}
196
197/// Extcap steps
198#[derive(Debug)]
199pub enum ExtcapStep {
200    /// Not determined
201    None,
202    /// Query for available interfaces
203    QueryIfaces,
204    /// Ask for DLT’s to each interface
205    QueryDlts,
206    /// The extcap configuration interface
207    ConfigIface {
208        /// Reload of some argument invoked
209        reload: bool,
210    },
211    /// The capture process
212    Capture {
213        /// Control pipes available
214        ctrl_pipe: bool,
215    },
216}
217
218impl Default for ExtcapStep {
219    fn default() -> Self {
220        ExtcapStep::None
221    }
222}
223
224enum TillCaptureOutcome<T> {
225    Finish(T),
226    Capture { ifidx: usize },
227}
228
229type TillCaptureResult<T> = Result<TillCaptureOutcome<T>, ExtcapError>;
230
231/// Exctcap representation
232#[derive(Default)]
233pub struct Extcap<'a> {
234    step: ExtcapStep,
235    app: Option<Command<'a>>,
236    app_args: HashSet<String>, // optional user arguments added from interfaces
237    matches: Option<ArgMatches>,
238    version: Option<String>,
239    helppage: Option<String>,
240    ws_version: Option<String>,
241    interfaces: Vec<IFace<'a>>,
242    reload_opt: bool,
243    ifc_debug: bool,
244    control: bool,
245    controls: Vec<Control>,
246}
247
248impl<'a> Extcap<'a> {
249    /// Creates a new instance of an `Extcap` requiring a name.
250    pub fn new(name: &'a str) -> Self {
251        let app = Command::new(name)
252            .allow_negative_numbers(true)
253            //.template(HELP_TEMPLATE)
254            .arg(
255                Arg::new(OPT_EXTCAP_VERSION)
256                    .long(OPT_EXTCAP_VERSION)
257                    .help("Wireshark version")
258                    .takes_value(true)
259                    .value_name("ver"),
260            )
261            .arg(
262                Arg::new(OPT_EXTCAP_INTERFACES)
263                    .long(OPT_EXTCAP_INTERFACES)
264                    .help("List the extcap Interfaces"),
265            )
266            .arg(
267                Arg::new(OPT_EXTCAP_INTERFACE)
268                    .long(OPT_EXTCAP_INTERFACE)
269                    .help("Specify the extcap interface")
270                    .takes_value(true)
271                    .value_name("iface")
272                    .conflicts_with(OPT_EXTCAP_INTERFACES),
273            )
274            .arg(
275                Arg::new(OPT_EXTCAP_DTLS)
276                    .long(OPT_EXTCAP_DTLS)
277                    .help("List the DLTs"),
278            )
279            .arg(
280                Arg::new(OPT_EXTCAP_CONFIG)
281                    .long(OPT_EXTCAP_CONFIG)
282                    .help("List the additional configuration for an interface"),
283            )
284            .arg(
285                Arg::new(OPT_CAPTURE)
286                    .long(OPT_CAPTURE)
287                    .help("Run the capture")
288                    .requires(OPT_FIFO),
289            )
290            .group(
291                ArgGroup::new("if_action")
292                    .args(&[OPT_EXTCAP_DTLS, OPT_EXTCAP_CONFIG, OPT_CAPTURE])
293                    .multiple(false)
294                    .requires(OPT_EXTCAP_INTERFACE),
295            )
296            .arg(
297                Arg::new(OPT_EXTCAP_CAPTURE_FILTER)
298                    .long(OPT_EXTCAP_CAPTURE_FILTER)
299                    .help("The capture filter")
300                    .takes_value(true)
301                    .value_name("filter")
302                    .requires(OPT_CAPTURE),
303            )
304            .arg(
305                Arg::new(OPT_FIFO)
306                    .long(OPT_FIFO)
307                    .help("Dump data to file or fifo")
308                    .takes_value(true)
309                    .value_name("file")
310                    .requires(OPT_CAPTURE),
311            );
312
313        Self {
314            app: Some(app),
315            ..Default::default()
316        }
317    }
318
319    /// Get current step for which it has been invoked from Wireshark
320    pub fn get_step(&self) -> &ExtcapStep {
321        &self.step
322    }
323
324    fn take_app(&mut self) -> Command<'a> {
325        self.app.take().expect("Extcap invalid state: already run")
326    }
327
328    fn update_app<F>(&mut self, f: F)
329    where
330        F: FnOnce(Command<'a>) -> Command<'a>,
331    {
332        self.app = Some(f(self.take_app()));
333    }
334
335    fn app_arg(&mut self, arg: Arg<'a>) {
336        self.app = Some(self.take_app().arg(arg));
337    }
338
339    /// Get parsed command line arguments. Provided by `clap::App`.
340    pub fn get_matches(&self) -> &ArgMatches {
341        self.matches
342            .as_ref()
343            .expect("Extcap invalid state: not run yet")
344    }
345
346    /// Sets the version string
347    pub fn version(&mut self, ver: &'a str) {
348        self.version = Some(ver.to_owned());
349        self.update_app(|a| a.version(ver));
350    }
351
352    /// Sets the help URI
353    pub fn help(&mut self, helppage: &'a str) {
354        self.helppage = Some(String::from(helppage));
355    }
356
357    /// Sets the author string
358    pub fn author(&mut self, author: &'a str) {
359        self.update_app(|a| a.author(author));
360    }
361
362    /// Sets the about string
363    pub fn about(&mut self, about: &'a str) {
364        self.update_app(|a| a.about(about));
365    }
366
367    /// Sets the usage string
368    pub fn usage(&mut self, usage: &'a str) {
369        self.update_app(|a| a.override_usage(usage));
370    }
371
372    /// Sets the after-help string
373    pub fn after_help(&mut self, help: &'a str) {
374        self.update_app(|a| a.after_help(help));
375    }
376
377    /// Adds an interface
378    pub fn add_interface(&mut self, ifc: IFace<'a>) {
379        if ifc.has_reloadable_arg() && !self.reload_opt {
380            self.config_reload_opt();
381        }
382        if ifc.has_debug() && !self.ifc_debug {
383            self.config_debug();
384        }
385        for ifa in ifc.get_args() {
386            self.config_arg(ifa)
387        }
388        self.interfaces.push(ifc);
389    }
390
391    fn get_if_idx(&self, ifc: &str) -> Option<usize> {
392        self.interfaces
393            .iter()
394            .position(|x| x.get_interface() == ifc)
395    }
396
397    fn get_if(&self, ifidx: usize) -> &IFace {
398        &self.interfaces[ifidx]
399    }
400
401    fn get_if_mut(&mut self, ifidx: usize) -> &mut IFace<'a> {
402        &mut self.interfaces[ifidx]
403    }
404
405    pub(crate) fn config_arg(&mut self, ifa: &IfArg<'a>) {
406        if self.app_args.contains(ifa.get_name()) {
407            return;
408        }
409
410        let mut arg = Arg::new(ifa.get_name()).long(ifa.get_name());
411        if let Some(hlp) = ifa.get_display() {
412            arg = arg.help(hlp);
413        }
414        arg = arg.takes_value(!matches!(ifa.get_type(), IfArgType::Boolflag));
415        self.app_arg(arg);
416
417        self.app_args.insert(ifa.get_name().to_owned());
418    }
419
420    pub(crate) fn config_reload_opt(&mut self) {
421        if self.reload_opt {
422            return;
423        }
424        self.reload_opt = true;
425        self.app_arg(
426            Arg::new(OPT_EXTCAP_RELOAD_OPTION)
427                .long(OPT_EXTCAP_RELOAD_OPTION)
428                .help("Reload values for the given argument")
429                .takes_value(true)
430                .value_name("option")
431                .requires(OPT_EXTCAP_INTERFACE)
432                .requires(OPT_EXTCAP_CONFIG),
433        );
434        self.app_args.insert(OPT_EXTCAP_RELOAD_OPTION.to_owned());
435    }
436
437    pub(crate) fn config_control(&mut self) {
438        if self.control {
439            return;
440        }
441        self.control = true;
442        self.app_arg(
443            Arg::new(OPT_EXTCAP_CONTROL_IN)
444                .long(OPT_EXTCAP_CONTROL_IN)
445                .help("The pipe for control messages from toolbar")
446                .takes_value(true)
447                .value_name("in-pipe")
448                .requires(OPT_CAPTURE),
449        );
450        self.app_arg(
451            Arg::new(OPT_EXTCAP_CONTROL_OUT)
452                .long(OPT_EXTCAP_CONTROL_OUT)
453                .help("The pipe for control messages to toolbar")
454                .takes_value(true)
455                .value_name("out-pipe")
456                .requires(OPT_CAPTURE),
457        );
458        self.app_args.insert(OPT_EXTCAP_CONTROL_IN.to_owned());
459        self.app_args.insert(OPT_EXTCAP_CONTROL_OUT.to_owned());
460    }
461
462    fn config_debug(&mut self) {
463        if self.ifc_debug {
464            return;
465        }
466        self.ifc_debug = true;
467        self.app_arg(
468            Arg::new(OPT_DEBUG)
469                .long(OPT_DEBUG)
470                .help("Print additional messages"),
471        );
472        self.app_arg(
473            Arg::new(OPT_DEBUG_FILE)
474                .long(OPT_DEBUG_FILE)
475                .help("Print debug messages to file")
476                .takes_value(true)
477                .value_name("file"),
478        );
479        self.app_args.insert(OPT_DEBUG.to_owned());
480        self.app_args.insert(OPT_DEBUG_FILE.to_owned());
481    }
482
483    /// Adds a control
484    pub fn add_control(&mut self, mut control: Control) {
485        if !self.control {
486            self.config_control();
487        }
488        control.set_number(self.controls.len());
489        self.controls.push(control);
490    }
491
492    fn print_version(&self) {
493        print!(
494            "extcap {{version={}}}",
495            self.version.as_ref().map_or("unknown", String::as_ref)
496        );
497        print_opt_value("help", &self.helppage);
498        println!();
499    }
500
501    fn print_iface_list(&self) {
502        self.interfaces.iter().for_each(IFace::print_iface);
503    }
504
505    fn print_control_list(&self) {
506        self.controls.iter().for_each(Control::print_control);
507    }
508
509    /// Starts main capture loop
510    pub fn run<T: ExtcapListener>(mut self, mut listener: T) -> ExtcapResult<()> {
511        match self.run_till_capture(&mut listener)? {
512            TillCaptureOutcome::Finish(_) => Ok(()),
513            TillCaptureOutcome::Capture { ifidx } => {
514                self.capture(&mut listener, self.get_if(ifidx))
515            }
516        }
517    }
518
519    /// Main async capture loop
520    #[cfg(feature = "async-api")]
521    pub async fn run_async<T: ExtcapListener>(mut self, mut listener: T) -> ExtcapResult<()> {
522        match self.run_till_capture(&mut listener)? {
523            TillCaptureOutcome::Finish(_) => Ok(()),
524            TillCaptureOutcome::Capture { ifidx } => {
525                self.capture_async(&mut listener, self.get_if(ifidx)).await
526            }
527        }
528    }
529
530    fn run_till_capture<T: ExtcapListener>(&mut self, listener: &mut T) -> TillCaptureResult<()> {
531        // Save matches for listener
532        self.matches = match self.take_app().try_get_matches() {
533            Ok(m) => Some(m),
534            Err(cerr) => match cerr.kind() {
535                clap::ErrorKind::DisplayHelp | clap::ErrorKind::DisplayVersion => {
536                    print!("{}", cerr);
537                    return Ok(TillCaptureOutcome::Finish(()));
538                }
539                _ => return Err(cerr.into()),
540            },
541        };
542
543        // Determine the step
544        self.step = if self.get_matches().is_present(OPT_EXTCAP_INTERFACES) {
545            ExtcapStep::QueryIfaces
546        } else if self.get_matches().is_present(OPT_EXTCAP_DTLS) {
547            ExtcapStep::QueryDlts
548        } else if self.get_matches().is_present(OPT_EXTCAP_CONFIG) {
549            let reload = self.get_matches().is_present(OPT_EXTCAP_RELOAD_OPTION);
550            ExtcapStep::ConfigIface { reload }
551        } else if self.get_matches().is_present(OPT_CAPTURE) {
552            let ctrl_pipe = self.get_matches().is_present(OPT_EXTCAP_CONTROL_IN)
553                && self.get_matches().is_present(OPT_EXTCAP_CONTROL_OUT);
554            ExtcapStep::Capture { ctrl_pipe }
555        } else {
556            ExtcapStep::None
557        };
558
559        // Log initialization
560        let debug = self.get_matches().is_present(OPT_DEBUG);
561        let debug_file = self.get_matches().value_of(OPT_DEBUG_FILE).and_then(|s| {
562            if s.trim().is_empty() {
563                None
564            } else {
565                Some(s)
566            }
567        });
568        listener.init_log(self, debug, debug_file);
569        debug!("=======================");
570        debug!(
571            "Log initialized debug={} debug_file={}",
572            debug,
573            debug_file.unwrap_or_default()
574        );
575        debug!("step = {:?}", self.step);
576        debug!("env::args = {:?}", std::env::args());
577
578        // Save version for listener
579        self.ws_version = self
580            .get_matches()
581            .value_of(OPT_EXTCAP_VERSION)
582            .map(String::from);
583        debug!(
584            "Wireshark version {}",
585            self.ws_version
586                .as_ref()
587                .map_or("-not provided-", String::as_str)
588        );
589
590        // Call listener interfaces update if it depends on passed options
591        listener.update_interfaces(self);
592
593        if let ExtcapStep::QueryIfaces = self.get_step() {
594            debug!("list of interfaces required");
595            self.print_version();
596            self.print_iface_list();
597            self.print_control_list();
598            return Ok(TillCaptureOutcome::Finish(()));
599        }
600
601        let ifidx = self
602            .get_matches()
603            .value_of(OPT_EXTCAP_INTERFACE)
604            .map_or_else(
605                || Err(ExtcapError::missing_interface()),
606                |ifnm| {
607                    self.get_if_idx(ifnm)
608                        .ok_or_else(|| ExtcapError::invalid_interface(ifnm))
609                },
610            )?;
611
612        debug!("interface = {}", self.get_if(ifidx).get_interface());
613        match self.get_step() {
614            ExtcapStep::QueryDlts => {
615                debug!("interface DLTs required");
616                self.get_if(ifidx).print_dlt_list();
617                Ok(TillCaptureOutcome::Finish(()))
618            }
619            ExtcapStep::ConfigIface { .. } => {
620                if let Some(arg) = self.get_matches().value_of(OPT_EXTCAP_RELOAD_OPTION) {
621                    let arg = arg.to_owned(); // ends self immutable borrow
622                    debug!("interface config reload required for '{}' argument", arg);
623                    self.reload_option(listener, ifidx, &arg);
624                } else {
625                    debug!("interface config required");
626                    self.get_if(ifidx).print_arg_list();
627                }
628                Ok(TillCaptureOutcome::Finish(()))
629            }
630            ExtcapStep::Capture { .. } => Ok(TillCaptureOutcome::Capture { ifidx }),
631            _ => Err(ExtcapError::unknown_step()),
632        }
633    }
634
635    fn reload_option<T: ExtcapListener>(&mut self, listener: &mut T, ifidx: usize, arg: &str) {
636        let ifc = self.get_if(ifidx);
637        let aidx = if let Some(aidx) = ifc.get_arg_idx(arg) {
638            aidx
639        } else {
640            warn!(
641                "reload_option() arg '{}' not available for interface '{}'",
642                arg,
643                ifc.get_interface()
644            );
645            return;
646        };
647
648        if let Some(nargs) = listener.reload_option(self, ifc, ifc.get_arg(aidx)) {
649            debug!(
650                "reload_option() arg '{}' for interface '{}' has got {} values",
651                arg,
652                ifc.get_interface(),
653                nargs.len()
654            );
655            let arg = self.get_if_mut(ifidx).get_arg_mut(aidx);
656            arg.reload_option(nargs);
657        } else {
658            debug!(
659                "reload_option() arg '{}' for interface '{}' nothing has changed",
660                arg,
661                ifc.get_interface()
662            );
663        };
664
665        self.get_if(ifidx).get_arg(aidx).print_arg();
666    }
667
668    fn capture<T: ExtcapListener>(&self, listener: &mut T, ifc: &IFace) -> ExtcapResult<()> {
669        let fifo = self.get_matches().value_of(OPT_FIFO).unwrap();
670        let capture_filter = self.get_matches().value_of(OPT_EXTCAP_CAPTURE_FILTER);
671        debug!(
672            "capture required fifo={} capture_filter={}",
673            fifo,
674            capture_filter.unwrap_or_default()
675        );
676
677        let ph = listener.capture_header(self, ifc);
678        debug!("capture pcap header: {:?}", ph);
679        let pw = create_pcap_writer(fifo, ph)?;
680
681        let res = {
682            debug!("capture starting");
683            listener.capture(self, ifc, pw)
684        };
685        debug!("capture finished: {:?}", res);
686
687        res
688    }
689
690    #[cfg(feature = "async-api")]
691    async fn capture_async<T: ExtcapListener>(
692        &self,
693        listener: &mut T,
694        ifc: &IFace<'_>,
695    ) -> ExtcapResult<()> {
696        let fifo = self.get_matches().value_of(OPT_FIFO).unwrap();
697        let capture_filter = self.get_matches().value_of(OPT_EXTCAP_CAPTURE_FILTER);
698        debug!(
699            "async capture required fifo={} capture_filter={}",
700            fifo,
701            capture_filter.unwrap_or_default()
702        );
703        #[cfg(feature = "ctrl-pipe")]
704        let mut control_pipe = {
705            let control_in = self.get_matches().value_of(OPT_EXTCAP_CONTROL_IN);
706            let control_out = self.get_matches().value_of(OPT_EXTCAP_CONTROL_OUT);
707            if let (Some(ctrl_in), Some(ctrl_out)) = (control_in, control_out) {
708                debug!("async capture with control in={} out={}", ctrl_in, ctrl_out);
709                match create_control_pipe(ctrl_in, ctrl_out) {
710                    Ok(ctrl_pipe) => Some(ctrl_pipe),
711                    Err(e) => {
712                        warn!(
713                            "create_control_pipe(ctrl_in={}, ctrl_out={}), failed with error {}",
714                            ctrl_in, ctrl_out, e
715                        );
716                        None
717                    }
718                }
719            } else {
720                None
721            }
722        };
723
724        let ph = listener.capture_header(self, ifc);
725        debug!("async capture pcap header: {:?}", ph);
726        let pw = create_pcap_writer(fifo, ph)?;
727
728        #[cfg(feature = "ctrl-pipe")]
729        let res = {
730            let ctrl_pipe = control_pipe.as_mut().map(control_pipe::ControlPipe::start);
731            debug!(
732                "async capture starting {} ctrl pipes",
733                if ctrl_pipe.is_some() {
734                    "with"
735                } else {
736                    "without"
737                }
738            );
739            let receiver = listener.capture_async_with_ctrl(self, ifc, ctrl_pipe)?;
740            let tsk_ctrl_opt = control_pipe
741                .as_mut()
742                .map(control_pipe::ControlPipe::run_task);
743            let tsk_capture = async {
744                let res = capture_async_loop(receiver, pw).await;
745                if let Some(cp) = control_pipe {
746                    cp.stop();
747                }
748                res
749            };
750            if let Some(tsk_ctrl) = tsk_ctrl_opt {
751                future::join(tsk_ctrl, tsk_capture).await.1
752            } else {
753                tsk_capture.await
754            }
755        };
756
757        #[cfg(not(feature = "ctrl-pipe"))]
758        let res = {
759            debug!("async capture starting");
760            let receiver = listener.capture_async(self, ifc)?;
761            capture_async_loop(receiver, pw).await
762        };
763
764        debug!("async capture finished: {:?}", res);
765
766        res
767    }
768}
769
770#[cfg(feature = "async-api")]
771async fn capture_async_loop(
772    mut receiver: ExtcapReceiver,
773    mut pw: PcapWriter<ExtcapWriter>,
774) -> ExtcapResult<()> {
775    debug!("async capture started");
776    while let Some(pkt) = receiver.next().await {
777        debug!("async packet received {:?}", pkt);
778        pw.write_packet(&pkt)?;
779    }
780    Ok(())
781}