Skip to main content

a7n_platform/
firewall.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3//! Windows Firewall diagnostics: reports why peers may not be able to reach
4//! this machine. Reading the firewall needs no elevation, and nothing here
5//! changes it; the fix is for the user to run elevated.
6
7use std::fmt;
8use std::path::PathBuf;
9
10#[cfg(any(windows, test))]
11mod rules;
12#[cfg(windows)]
13mod windows;
14
15/// A network profile of the Windows Firewall.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum Profile {
18    Domain,
19    Private,
20    Public,
21}
22
23impl Profile {
24    const ALL: [Self; 3] = [Self::Domain, Self::Private, Self::Public];
25
26    /// The value `netsh advfirewall` takes for `profile=`.
27    #[must_use]
28    pub fn name(self) -> &'static str {
29        match self {
30            Self::Domain => "domain",
31            Self::Private => "private",
32            Self::Public => "public",
33        }
34    }
35
36    /// The `NET_FW_PROFILE_TYPE2` bit of this profile.
37    fn bit(self) -> u32 {
38        match self {
39            Self::Domain => 1,
40            Self::Private => 2,
41            Self::Public => 4,
42        }
43    }
44}
45
46impl fmt::Display for Profile {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.write_str(self.name())
49    }
50}
51
52/// A set of [`Profile`]s.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
54pub struct Profiles(u32);
55
56impl Profiles {
57    pub fn iter(self) -> impl Iterator<Item = Profile> {
58        Profile::ALL
59            .into_iter()
60            .filter(move |&profile| self.has(profile))
61    }
62
63    fn has(self, profile: Profile) -> bool {
64        self.0 & profile.bit() != 0
65    }
66}
67
68/// Something that stops peers from reaching this machine.
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub enum Problem {
71    /// No enabled inbound allow rule covers UDP on `ports` for `profiles`.
72    MissingAllow {
73        program: PathBuf,
74        ports: Vec<u16>,
75        profiles: Profiles,
76    },
77    /// An enabled inbound block rule matches UDP on `ports`; blocks win over allows.
78    Blocked { rule: String, ports: Vec<u16> },
79}
80
81#[derive(Debug, thiserror::Error)]
82pub enum CheckError {
83    #[error("cannot find the path of this program: {0}")]
84    Program(#[from] std::io::Error),
85    #[error("the firewall could not be queried (HRESULT {0:#010x})")]
86    Query(i32),
87}
88
89/// Whether this platform has a firewall backend; [`check`] is pointless otherwise.
90pub const SUPPORTED: bool = cfg!(windows);
91
92/// Problems that block links on UDP `port` and mDNS, checked live on every
93/// call. Blocks on COM calls: run it off the async runtime. Always empty off
94/// Windows.
95///
96/// # Errors
97///
98/// [`CheckError`] when this program's path or the firewall cannot be read.
99pub fn check(port: u16) -> Result<Vec<Problem>, CheckError> {
100    #[cfg(windows)]
101    return windows::check(port);
102    #[cfg(not(windows))]
103    {
104        let _ = port;
105        Ok(Vec::new())
106    }
107}