external_ip/sources/
igd.rs1use 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#[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}