external_ip/sources/
igd.rs

1/* use crate::sources::interfaces; */
2use crate::sources::interfaces::{Error, Family, IpFuture, IpResult, Source};
3use std::net::IpAddr;
4use std::pin::Pin;
5use std::sync::mpsc;
6use std::sync::{Arc, Mutex};
7use std::task::{Poll, Waker};
8use std::thread;
9
10use log::trace;
11
12/// IGD Source of the external ip
13///
14/// It will try to connect to the local router implementing the IGD interface to obtain the external
15/// IP directly from it.
16///
17/// The feature "igd" must be enabled to use this t(on by default)
18#[derive(Debug, Clone)]
19pub struct IGD {}
20
21impl IGD {
22    pub fn source() -> Box<dyn Source> {
23        Box::new(IGD {})
24    }
25}
26
27impl Source for IGD {
28    fn get_ip(&self, family: Family) -> IpFuture<'_> {
29        let (tx, rx) = mpsc::channel();
30        let future = IGDFuture {
31            rx,
32            waker: Arc::new(Mutex::from(None)),
33            family,
34        };
35        future.run(tx);
36        Box::pin(future)
37    }
38
39    fn box_clone(&self) -> Box<dyn Source> {
40        Box::new(self.clone())
41    }
42}
43
44struct IGDFuture {
45    rx: mpsc::Receiver<IpResult>,
46    waker: Arc<Mutex<Option<Waker>>>,
47    family: Family,
48}
49
50impl std::fmt::Display for IGD {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        write!(f, "IGD")
53    }
54}
55
56impl IGDFuture {
57    pub fn run(&self, tx: mpsc::Sender<IpResult>) {
58        let waker = self.waker.clone();
59        if matches!(self.family, Family::IPv4 | Family::Any) {
60            thread::spawn(move || {
61                trace!("IGD Future thread started");
62                fn inner() -> IpResult {
63                    let gateway = igd::search_gateway(Default::default())?;
64                    let ip = gateway.get_external_ip()?;
65                    Ok(IpAddr::from(ip))
66                }
67
68                let result = inner();
69                log::debug!("IGD task completed: {:?}", result);
70                let r = tx.send(IpResult::from(result));
71                log::debug!("Send result: {:?}", r);
72
73                if let Some(waker) = waker.lock().unwrap().take() {
74                    waker.wake();
75                }
76            });
77        }
78    }
79}
80
81impl std::future::Future for IGDFuture {
82    type Output = IpResult;
83
84    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context) -> Poll<Self::Output> {
85        if matches!(self.family, Family::IPv4 | Family::Any) {
86            let r = self.rx.try_recv();
87            match r {
88                Err(_) => {
89                    let mut waker = self.waker.lock().unwrap();
90                    *waker = Some(cx.waker().clone());
91                    Poll::Pending
92                }
93                Ok(x) => Poll::Ready(x),
94            }
95        } else {
96            Poll::Ready(std::result::Result::Err(Error::UnsupportedFamily))
97        }
98    }
99}