if_addrs/
lib.rs

1// Copyright 2018 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under the MIT license <LICENSE-MIT
4// http://opensource.org/licenses/MIT> or the Modified BSD license <LICENSE-BSD
5// https://opensource.org/licenses/BSD-3-Clause>, at your option. This file may not be copied,
6// modified, or distributed except according to those terms. Please review the Licences for the
7// specific language governing permissions and limitations relating to use of the SAFE Network
8// Software.
9#![cfg_attr(docsrs, feature(doc_auto_cfg))]
10#![cfg_attr(docsrs, feature(doc_cfg))]
11
12#[cfg(not(windows))]
13mod posix;
14#[cfg(all(
15    not(windows),
16    not(all(
17        target_vendor = "apple",
18        any(
19            target_os = "macos",
20            target_os = "ios",
21            target_os = "tvos",
22            target_os = "watchos",
23            target_os = "visionos"
24        )
25    )),
26    not(target_os = "freebsd"),
27    not(target_os = "netbsd"),
28    not(target_os = "openbsd"),
29    not(target_os = "illumos")
30))]
31mod posix_not_apple;
32mod sockaddr;
33#[cfg(windows)]
34mod windows;
35
36use std::io;
37use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
38
39/// Details about an interface on this host.
40#[derive(Debug, PartialEq, Eq, Hash, Clone)]
41pub struct Interface {
42    /// The name of the interface.
43    pub name: String,
44    /// The address details of the interface.
45    pub addr: IfAddr,
46    /// The index of the interface.
47    pub index: Option<u32>,
48    /// (Windows only) A permanent and unique identifier for the interface. It
49    /// cannot be modified by the user. It is typically a GUID string of the
50    /// form: "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}", but this is not
51    /// guaranteed by the Windows API.
52    #[cfg(windows)]
53    pub adapter_name: String,
54}
55
56impl Interface {
57    /// Check whether this is a loopback interface.
58    #[must_use]
59    pub const fn is_loopback(&self) -> bool {
60        self.addr.is_loopback()
61    }
62
63    /// Check whether this is a link local interface.
64    #[must_use]
65    pub const fn is_link_local(&self) -> bool {
66        self.addr.is_link_local()
67    }
68
69    /// Get the IP address of this interface.
70    #[must_use]
71    pub const fn ip(&self) -> IpAddr {
72        self.addr.ip()
73    }
74}
75
76/// Details about the address of an interface on this host.
77#[derive(Debug, PartialEq, Eq, Hash, Clone)]
78pub enum IfAddr {
79    /// This is an Ipv4 interface.
80    V4(Ifv4Addr),
81    /// This is an Ipv6 interface.
82    V6(Ifv6Addr),
83}
84
85impl IfAddr {
86    /// Check whether this is a loopback address.
87    #[must_use]
88    pub const fn is_loopback(&self) -> bool {
89        match *self {
90            IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_loopback(),
91            IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_loopback(),
92        }
93    }
94
95    /// Check whether this is a link local interface.
96    #[must_use]
97    pub const fn is_link_local(&self) -> bool {
98        match *self {
99            IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_link_local(),
100            IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_link_local(),
101        }
102    }
103
104    /// Get the IP address of this interface address.
105    #[must_use]
106    pub const fn ip(&self) -> IpAddr {
107        match *self {
108            IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.ip),
109            IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.ip),
110        }
111    }
112}
113
114/// Details about the ipv4 address of an interface on this host.
115#[derive(Debug, PartialEq, Eq, Hash, Clone)]
116pub struct Ifv4Addr {
117    /// The IP address of the interface.
118    pub ip: Ipv4Addr,
119    /// The netmask of the interface.
120    pub netmask: Ipv4Addr,
121    /// The CIDR prefix of the interface.
122    pub prefixlen: u8,
123    /// The broadcast address of the interface.
124    pub broadcast: Option<Ipv4Addr>,
125}
126
127impl Ifv4Addr {
128    /// Check whether this is a loopback address.
129    #[must_use]
130    pub const fn is_loopback(&self) -> bool {
131        self.ip.is_loopback()
132    }
133
134    /// Check whether this is a link local address.
135    #[must_use]
136    pub const fn is_link_local(&self) -> bool {
137        self.ip.is_link_local()
138    }
139}
140
141/// Details about the ipv6 address of an interface on this host.
142#[derive(Debug, PartialEq, Eq, Hash, Clone)]
143pub struct Ifv6Addr {
144    /// The IP address of the interface.
145    pub ip: Ipv6Addr,
146    /// The netmask of the interface.
147    pub netmask: Ipv6Addr,
148    /// The CIDR prefix of the interface.
149    pub prefixlen: u8,
150    /// The broadcast address of the interface.
151    pub broadcast: Option<Ipv6Addr>,
152}
153
154impl Ifv6Addr {
155    /// Check whether this is a loopback address.
156    #[must_use]
157    pub const fn is_loopback(&self) -> bool {
158        self.ip.is_loopback()
159    }
160
161    /// Check whether this is a link local address.
162    #[must_use]
163    pub const fn is_link_local(&self) -> bool {
164        let bytes = self.ip.octets();
165
166        bytes[0] == 0xfe && bytes[1] == 0x80
167    }
168}
169
170#[cfg(not(windows))]
171mod getifaddrs_posix {
172    use libc::if_nametoindex;
173
174    use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
175    use crate::posix::{self as ifaddrs, IfAddrs};
176    use crate::sockaddr;
177    use std::ffi::CStr;
178    use std::io;
179    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
180
181    /// Return a vector of IP details for all the valid interfaces on this host.
182    #[allow(unsafe_code)]
183    pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
184        let mut ret = Vec::<Interface>::new();
185        let ifaddrs = IfAddrs::new()?;
186
187        for ifaddr in ifaddrs.iter() {
188            let addr = match sockaddr::to_ipaddr(ifaddr.ifa_addr) {
189                None => continue,
190                Some(IpAddr::V4(ipv4_addr)) => {
191                    let netmask = match sockaddr::to_ipaddr(ifaddr.ifa_netmask) {
192                        Some(IpAddr::V4(netmask)) => netmask,
193                        _ => Ipv4Addr::new(0, 0, 0, 0),
194                    };
195                    let broadcast = if (ifaddr.ifa_flags & 2) != 0 {
196                        match ifaddrs::do_broadcast(&ifaddr) {
197                            Some(IpAddr::V4(broadcast)) => Some(broadcast),
198                            _ => None,
199                        }
200                    } else {
201                        None
202                    };
203                    let prefixlen = if cfg!(target_endian = "little") {
204                        u32::from_le_bytes(netmask.octets()).count_ones() as u8
205                    } else {
206                        u32::from_be_bytes(netmask.octets()).count_ones() as u8
207                    };
208                    IfAddr::V4(Ifv4Addr {
209                        ip: ipv4_addr,
210                        netmask,
211                        prefixlen,
212                        broadcast,
213                    })
214                }
215                Some(IpAddr::V6(ipv6_addr)) => {
216                    let netmask = match sockaddr::to_ipaddr(ifaddr.ifa_netmask) {
217                        Some(IpAddr::V6(netmask)) => netmask,
218                        _ => Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0),
219                    };
220                    let broadcast = if (ifaddr.ifa_flags & 2) != 0 {
221                        match ifaddrs::do_broadcast(&ifaddr) {
222                            Some(IpAddr::V6(broadcast)) => Some(broadcast),
223                            _ => None,
224                        }
225                    } else {
226                        None
227                    };
228                    let prefixlen = if cfg!(target_endian = "little") {
229                        u128::from_le_bytes(netmask.octets()).count_ones() as u8
230                    } else {
231                        u128::from_be_bytes(netmask.octets()).count_ones() as u8
232                    };
233                    IfAddr::V6(Ifv6Addr {
234                        ip: ipv6_addr,
235                        netmask,
236                        prefixlen,
237                        broadcast,
238                    })
239                }
240            };
241
242            let name = unsafe { CStr::from_ptr(ifaddr.ifa_name) }
243                .to_string_lossy()
244                .into_owned();
245            let index = {
246                let index = unsafe { if_nametoindex(ifaddr.ifa_name) };
247
248                // From `man if_nametoindex 3`:
249                // The if_nametoindex() function maps the interface name specified in ifname to its
250                // corresponding index. If the specified interface does not exist, it returns 0.
251                if index == 0 {
252                    None
253                } else {
254                    Some(index)
255                }
256            };
257            ret.push(Interface { name, addr, index });
258        }
259
260        Ok(ret)
261    }
262}
263
264/// Get a list of all the network interfaces on this machine along with their IP info.
265#[cfg(not(windows))]
266pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
267    getifaddrs_posix::get_if_addrs()
268}
269
270#[cfg(windows)]
271mod getifaddrs_windows {
272    use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
273    use crate::sockaddr;
274    use crate::windows::IfAddrs;
275    use std::io;
276    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
277    use windows_sys::Win32::Networking::WinSock::IpDadStatePreferred;
278
279    /// Return a vector of IP details for all the valid interfaces on this host.
280    pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
281        let mut ret = Vec::<Interface>::new();
282        let ifaddrs = IfAddrs::new()?;
283
284        for ifaddr in ifaddrs.iter() {
285            for addr in ifaddr.unicast_addresses() {
286                if addr.DadState != IpDadStatePreferred {
287                    continue;
288                }
289                let addr = match sockaddr::to_ipaddr(addr.Address.lpSockaddr) {
290                    None => continue,
291                    Some(IpAddr::V4(ipv4_addr)) => {
292                        let mut item_netmask = Ipv4Addr::new(0, 0, 0, 0);
293                        let mut item_broadcast = None;
294                        let item_prefix = addr.OnLinkPrefixLength;
295
296                        // Search prefixes for a prefix matching addr
297                        'prefixloopv4: for prefix in ifaddr.prefixes() {
298                            let ipprefix = sockaddr::to_ipaddr(prefix.Address.lpSockaddr);
299                            match ipprefix {
300                                Some(IpAddr::V4(ref a)) => {
301                                    let mut netmask: [u8; 4] = [0; 4];
302                                    for (n, netmask_elt) in netmask
303                                        .iter_mut()
304                                        .enumerate()
305                                        .take((prefix.PrefixLength as usize + 7) / 8)
306                                    {
307                                        let x_byte = ipv4_addr.octets()[n];
308                                        let y_byte = a.octets()[n];
309                                        for m in 0..8 {
310                                            if (n * 8) + m >= prefix.PrefixLength as usize {
311                                                break;
312                                            }
313                                            let bit = 1 << (7 - m);
314                                            if (x_byte & bit) == (y_byte & bit) {
315                                                *netmask_elt |= bit;
316                                            } else {
317                                                continue 'prefixloopv4;
318                                            }
319                                        }
320                                    }
321                                    item_netmask = Ipv4Addr::new(
322                                        netmask[0], netmask[1], netmask[2], netmask[3],
323                                    );
324                                    let mut broadcast: [u8; 4] = ipv4_addr.octets();
325                                    for n in 0..4 {
326                                        broadcast[n] |= !netmask[n];
327                                    }
328                                    item_broadcast = Some(Ipv4Addr::new(
329                                        broadcast[0],
330                                        broadcast[1],
331                                        broadcast[2],
332                                        broadcast[3],
333                                    ));
334                                    break 'prefixloopv4;
335                                }
336                                _ => continue,
337                            };
338                        }
339                        IfAddr::V4(Ifv4Addr {
340                            ip: ipv4_addr,
341                            netmask: item_netmask,
342                            prefixlen: item_prefix,
343                            broadcast: item_broadcast,
344                        })
345                    }
346                    Some(IpAddr::V6(ipv6_addr)) => {
347                        let mut item_netmask = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
348                        let item_prefix = addr.OnLinkPrefixLength;
349                        // Search prefixes for a prefix matching addr
350                        'prefixloopv6: for prefix in ifaddr.prefixes() {
351                            let ipprefix = sockaddr::to_ipaddr(prefix.Address.lpSockaddr);
352                            match ipprefix {
353                                Some(IpAddr::V6(ref a)) => {
354                                    // Iterate the bits in the prefix, if they all match this prefix
355                                    // is the right one, else try the next prefix
356                                    let mut netmask: [u16; 8] = [0; 8];
357                                    for (n, netmask_elt) in netmask
358                                        .iter_mut()
359                                        .enumerate()
360                                        .take((prefix.PrefixLength as usize + 15) / 16)
361                                    {
362                                        let x_word = ipv6_addr.segments()[n];
363                                        let y_word = a.segments()[n];
364                                        for m in 0..16 {
365                                            if (n * 16) + m >= prefix.PrefixLength as usize {
366                                                break;
367                                            }
368                                            let bit = 1 << (15 - m);
369                                            if (x_word & bit) == (y_word & bit) {
370                                                *netmask_elt |= bit;
371                                            } else {
372                                                continue 'prefixloopv6;
373                                            }
374                                        }
375                                    }
376                                    item_netmask = Ipv6Addr::new(
377                                        netmask[0], netmask[1], netmask[2], netmask[3], netmask[4],
378                                        netmask[5], netmask[6], netmask[7],
379                                    );
380                                    break 'prefixloopv6;
381                                }
382                                _ => continue,
383                            };
384                        }
385                        IfAddr::V6(Ifv6Addr {
386                            ip: ipv6_addr,
387                            netmask: item_netmask,
388                            prefixlen: item_prefix,
389                            broadcast: None,
390                        })
391                    }
392                };
393
394                let index = match addr {
395                    IfAddr::V4(_) => ifaddr.ipv4_index(),
396                    IfAddr::V6(_) => ifaddr.ipv6_index(),
397                };
398                ret.push(Interface {
399                    name: ifaddr.name(),
400                    addr,
401                    index,
402                    adapter_name: ifaddr.adapter_name(),
403                });
404            }
405        }
406
407        Ok(ret)
408    }
409}
410
411/// Get a list of all the network interfaces on this machine along with their IP info.
412#[cfg(windows)]
413pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
414    getifaddrs_windows::get_if_addrs()
415}
416
417#[cfg(not(any(
418    all(
419        target_vendor = "apple",
420        any(
421            target_os = "macos",
422            target_os = "ios",
423            target_os = "tvos",
424            target_os = "watchos",
425            target_os = "visionos"
426        )
427    ),
428    target_os = "freebsd",
429    target_os = "netbsd",
430    target_os = "openbsd",
431    target_os = "illumos"
432)))]
433#[cfg_attr(
434    docsrs,
435    doc(cfg(any(
436        not(target_vendor = "apple"),
437        not(target_os = "freebsd"),
438        not(target_os = "netbsd"),
439        not(target_os = "openbsd"),
440        not(target_os = "illumos")
441    )))
442)]
443mod if_change_notifier {
444    use super::Interface;
445    use std::collections::HashSet;
446    use std::io;
447    use std::time::{Duration, Instant};
448
449    #[derive(Debug, PartialEq, Eq, Hash, Clone)]
450    pub enum IfChangeType {
451        Added(Interface),
452        Removed(Interface),
453    }
454
455    #[cfg(windows)]
456    type InternalIfChangeNotifier = crate::windows::WindowsIfChangeNotifier;
457    #[cfg(not(windows))]
458    type InternalIfChangeNotifier = crate::posix_not_apple::PosixIfChangeNotifier;
459
460    /// (Not available on iOS/macOS) A utility to monitor for interface changes
461    /// and report them, so you can handle events such as WiFi
462    /// disconnection/flight mode/route changes
463    pub struct IfChangeNotifier {
464        inner: InternalIfChangeNotifier,
465        last_ifs: HashSet<Interface>,
466    }
467
468    impl IfChangeNotifier {
469        /// Create a new interface change notifier. Returns an OS specific error
470        /// if the network notifier could not be set up.
471        pub fn new() -> io::Result<Self> {
472            Ok(Self {
473                inner: InternalIfChangeNotifier::new()?,
474                last_ifs: HashSet::from_iter(super::get_if_addrs()?),
475            })
476        }
477
478        /// (Not available on iOS/macOS) Block until the OS reports that the
479        /// network interface list has changed, or until an optional timeout.
480        ///
481        /// For example, if an ethernet connector is plugged/unplugged, or a
482        /// WiFi network is connected to.
483        ///
484        /// The changed interfaces are returned. If an interface has both IPv4
485        /// and IPv6 addresses, you can expect both of them to be returned from
486        /// a single call to `wait`.
487        ///
488        /// Returns an [`io::ErrorKind::WouldBlock`] error on timeout, or
489        /// another error if the network notifier could not be read from.
490        pub fn wait(&mut self, timeout: Option<Duration>) -> io::Result<Vec<IfChangeType>> {
491            let start = Instant::now();
492            loop {
493                self.inner
494                    .wait(timeout.map(|t| t.saturating_sub(start.elapsed())))?;
495
496                // something has changed - now we find out what (or whether it was spurious)
497                let new_ifs = HashSet::from_iter(super::get_if_addrs()?);
498                let mut changes: Vec<IfChangeType> = new_ifs
499                    .difference(&self.last_ifs)
500                    .cloned()
501                    .map(IfChangeType::Added)
502                    .collect();
503                changes.extend(
504                    self.last_ifs
505                        .difference(&new_ifs)
506                        .cloned()
507                        .map(IfChangeType::Removed),
508                );
509                self.last_ifs = new_ifs;
510
511                if !changes.is_empty() {
512                    return Ok(changes);
513                }
514            }
515        }
516    }
517}
518
519#[cfg(not(any(
520    all(
521        target_vendor = "apple",
522        any(
523            target_os = "macos",
524            target_os = "ios",
525            target_os = "tvos",
526            target_os = "watchos",
527            target_os = "visionos"
528        )
529    ),
530    target_os = "freebsd",
531    target_os = "netbsd",
532    target_os = "openbsd",
533    target_os = "illumos"
534)))]
535#[cfg_attr(
536    docsrs,
537    doc(cfg(any(
538        not(target_vendor = "apple"),
539        not(target_os = "freebsd"),
540        not(target_os = "netbsd"),
541        not(target_os = "openbsd"),
542        not(target_os = "illumos")
543    )))
544)]
545pub use if_change_notifier::{IfChangeNotifier, IfChangeType};
546
547#[cfg(test)]
548mod tests {
549    use super::{get_if_addrs, Interface};
550    use std::io::Read;
551    use std::net::{IpAddr, Ipv4Addr};
552    use std::process::{Command, Stdio};
553    use std::str::FromStr;
554    use std::thread;
555    use std::time::Duration;
556
557    fn list_system_interfaces(cmd: &str, arg: &str) -> String {
558        let start_cmd = if arg.is_empty() {
559            Command::new(cmd).stdout(Stdio::piped()).spawn()
560        } else {
561            Command::new(cmd).arg(arg).stdout(Stdio::piped()).spawn()
562        };
563        let mut process = match start_cmd {
564            Err(why) => {
565                println!("couldn't start cmd {} : {}", cmd, why);
566                return String::new();
567            }
568            Ok(process) => process,
569        };
570        thread::sleep(Duration::from_millis(1000));
571        let _ = process.kill();
572        let result: Vec<u8> = process
573            .stdout
574            .unwrap()
575            .bytes()
576            .map(|x| x.unwrap())
577            .collect();
578        String::from_utf8(result).unwrap()
579    }
580
581    #[cfg(windows)]
582    fn list_system_addrs() -> Vec<IpAddr> {
583        use std::net::Ipv6Addr;
584        list_system_interfaces("ipconfig", "")
585            .lines()
586            .filter_map(|line| {
587                println!("{}", line);
588                if line.contains("Address") && !line.contains("Link-local") {
589                    let addr_s: Vec<&str> = line.split(" : ").collect();
590                    if line.contains("IPv6") {
591                        return Some(IpAddr::V6(Ipv6Addr::from_str(addr_s[1]).unwrap()));
592                    } else if line.contains("IPv4") {
593                        return Some(IpAddr::V4(Ipv4Addr::from_str(addr_s[1]).unwrap()));
594                    }
595                }
596                None
597            })
598            .collect()
599    }
600
601    #[cfg(any(target_os = "linux", target_os = "android"))]
602    fn list_system_addrs() -> Vec<IpAddr> {
603        list_system_interfaces("ip", "addr")
604            .lines()
605            .filter_map(|line| {
606                println!("{}", line);
607                if line.contains("inet ") {
608                    let addr_s: Vec<&str> = line.split_whitespace().collect();
609                    let addr: Vec<&str> = addr_s[1].split('/').collect();
610                    return Some(IpAddr::V4(Ipv4Addr::from_str(addr[0]).unwrap()));
611                }
612                None
613            })
614            .collect()
615    }
616
617    #[cfg(any(
618        target_os = "freebsd",
619        target_os = "netbsd",
620        target_os = "openbsd",
621        target_os = "illumos",
622        all(
623            target_vendor = "apple",
624            any(
625                target_os = "macos",
626                target_os = "ios",
627                target_os = "tvos",
628                target_os = "watchos",
629                target_os = "visionos"
630            )
631        )
632    ))]
633    fn list_system_addrs() -> Vec<IpAddr> {
634        list_system_interfaces("ifconfig", "")
635            .lines()
636            .filter_map(|line| {
637                println!("{}", line);
638                if line.contains("inet ") {
639                    let addr_s: Vec<&str> = line.split_whitespace().collect();
640                    return Some(IpAddr::V4(Ipv4Addr::from_str(addr_s[1]).unwrap()));
641                }
642                None
643            })
644            .collect()
645    }
646
647    #[test]
648    fn test_get_if_addrs() {
649        let ifaces = get_if_addrs().unwrap();
650        println!("Local interfaces:");
651        println!("{:#?}", ifaces);
652        // at least one loop back address
653        assert!(
654            1 <= ifaces
655                .iter()
656                .filter(|interface| interface.is_loopback())
657                .count()
658        );
659        // if index is set, it is non-zero
660        for interface in &ifaces {
661            if let Some(idx) = interface.index {
662                assert!(idx > 0);
663            }
664        }
665
666        // one address of IpV4(127.0.0.1)
667        let is_loopback =
668            |interface: &&Interface| interface.addr.ip() == IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
669        assert_eq!(1, ifaces.iter().filter(is_loopback).count());
670
671        // each system address shall be listed
672        let system_addrs = list_system_addrs();
673        assert!(!system_addrs.is_empty());
674        for addr in system_addrs {
675            let mut listed = false;
676            println!("\n checking whether {:?} has been properly listed \n", addr);
677            for interface in &ifaces {
678                if interface.addr.ip() == addr {
679                    listed = true;
680                }
681
682                assert!(interface.index.is_some());
683            }
684            assert!(listed);
685        }
686    }
687
688    #[cfg(not(any(
689        all(
690            target_vendor = "apple",
691            any(
692                target_os = "macos",
693                target_os = "ios",
694                target_os = "tvos",
695                target_os = "watchos",
696                target_os = "visionos"
697            )
698        ),
699        target_os = "freebsd",
700        target_os = "netbsd",
701        target_os = "openbsd",
702        target_os = "illumos"
703    )))]
704    #[test]
705    fn test_if_notifier() {
706        // Check that the interface notifier can start up and time out. No easy
707        // way to programmatically add/remove interfaces, so set a timeout of 0.
708        // Will cover a potential case of inadequate setup leading to an
709        // immediate change notification.
710        //
711        // There is a small race condition from creation -> check that an
712        // interface change *actually* occurs, so this test may spuriously fail
713        // extremely rarely.
714
715        let notifier = crate::IfChangeNotifier::new();
716        assert!(notifier.is_ok());
717        let mut notifier = notifier.unwrap();
718
719        assert!(notifier.wait(Some(Duration::ZERO)).is_err());
720    }
721}