dns_over_https/
args.rs

1use crate::upstream::Upstream;
2use std::net::SocketAddr;
3
4#[repr(C)]
5#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
6pub enum ArgVerbosity {
7    Off = 0,
8    Error,
9    Warn,
10    #[default]
11    Info,
12    Debug,
13    Trace,
14}
15
16#[cfg(target_os = "android")]
17impl TryFrom<jni::sys::jint> for ArgVerbosity {
18    type Error = std::io::Error;
19    fn try_from(value: jni::sys::jint) -> Result<Self, <Self as TryFrom<jni::sys::jint>>::Error> {
20        match value {
21            0 => Ok(ArgVerbosity::Off),
22            1 => Ok(ArgVerbosity::Error),
23            2 => Ok(ArgVerbosity::Warn),
24            3 => Ok(ArgVerbosity::Info),
25            4 => Ok(ArgVerbosity::Debug),
26            5 => Ok(ArgVerbosity::Trace),
27            _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid verbosity level")),
28        }
29    }
30}
31
32impl From<ArgVerbosity> for log::LevelFilter {
33    fn from(verbosity: ArgVerbosity) -> Self {
34        match verbosity {
35            ArgVerbosity::Off => log::LevelFilter::Off,
36            ArgVerbosity::Error => log::LevelFilter::Error,
37            ArgVerbosity::Warn => log::LevelFilter::Warn,
38            ArgVerbosity::Info => log::LevelFilter::Info,
39            ArgVerbosity::Debug => log::LevelFilter::Debug,
40            ArgVerbosity::Trace => log::LevelFilter::Trace,
41        }
42    }
43}
44
45impl From<log::Level> for ArgVerbosity {
46    fn from(level: log::Level) -> Self {
47        match level {
48            log::Level::Error => ArgVerbosity::Error,
49            log::Level::Warn => ArgVerbosity::Warn,
50            log::Level::Info => ArgVerbosity::Info,
51            log::Level::Debug => ArgVerbosity::Debug,
52            log::Level::Trace => ArgVerbosity::Trace,
53        }
54    }
55}
56
57impl std::fmt::Display for ArgVerbosity {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        match self {
60            ArgVerbosity::Off => write!(f, "off"),
61            ArgVerbosity::Error => write!(f, "error"),
62            ArgVerbosity::Warn => write!(f, "warn"),
63            ArgVerbosity::Info => write!(f, "info"),
64            ArgVerbosity::Debug => write!(f, "debug"),
65            ArgVerbosity::Trace => write!(f, "trace"),
66        }
67    }
68}
69
70/// A lightweight DNS-over-HTTPS proxy
71#[derive(clap::Parser, Debug, Clone, PartialEq, Eq, Default)]
72#[command(author, version, about = "A lightweight DNS-over-HTTPS proxy", long_about = None)]
73pub struct Args {
74    /// Listen for DNS requests on the addresses and ports
75    #[arg(short, long, value_name = "IP:port", default_value = "127.0.0.1:53")]
76    pub bind: Vec<SocketAddr>,
77
78    /// URL(s) of upstream DNS-over-HTTPS service
79    #[arg(short, long, value_name = "URL", default_value = "https://1.1.1.1/dns-query")]
80    pub upstream_urls: Vec<String>,
81
82    /// Verbosity level
83    #[arg(short, long, value_name = "level", value_enum, default_value = "info")]
84    pub verbosity: ArgVerbosity,
85
86    /// Windows only: Run as a service
87    #[cfg(target_os = "windows")]
88    #[arg(long)]
89    pub service: bool,
90}
91
92impl Args {
93    pub fn bind<T: Into<SocketAddr>>(&mut self, bind: T) -> &mut Self {
94        self.bind.push(bind.into());
95        self
96    }
97
98    pub fn upstream_url<T: Into<String>>(&mut self, url: T) -> &mut Self {
99        self.upstream_urls.push(url.into());
100        self
101    }
102
103    pub fn verbosity(&mut self, verbosity: ArgVerbosity) -> &mut Self {
104        self.verbosity = verbosity;
105        self
106    }
107
108    /// Returns the Args for the current run.
109    pub fn parse() -> Args {
110        <Args as clap::Parser>::parse()
111    }
112
113    /// Return a vector of Upstreams with the given Client.
114    pub fn upstreams<'a>(&'a self, client: &'a reqwest::Client) -> Vec<Upstream<'a>> {
115        self.upstream_urls.iter().map(|url| Upstream::new(client, url)).collect()
116    }
117}