1use std::error::Error;
2use std::net::IpAddr;
3use std::path::PathBuf;
4use std::str::FromStr;
5
6use structopt::{clap::arg_enum, StructOpt};
7use trust_dns_resolver::Resolver;
8
9#[derive(StructOpt, Debug)]
10pub enum TunnelerType {
11 Tcp,
12 Dns {
13 #[structopt(long, env)]
14 read_timeout_in_milliseconds: u64,
15 #[structopt(long, env)]
16 idle_client_timeout_in_milliseconds: u64,
17 #[structopt(default_value = "", long, env)]
18 client_suffix: String,
19 },
20 Tls {
21 #[structopt(long, env)]
22 ca_cert: PathBuf,
23 #[structopt(long, env)]
24 cert: PathBuf,
25 #[structopt(long, env)]
26 key: PathBuf,
27 #[structopt(long, env)]
28 server_hostname: String,
29 },
30}
31
32arg_enum! {
33 #[derive(Debug)]
34 pub enum TunneledType {
35 Tcp,
36 Udp,
37 }
38}
39
40fn parse_or_resolve_ip(src: &str) -> Result<IpAddr, Box<dyn Error>> {
41 IpAddr::from_str(src).or_else(|_| {
42 let resolver = Resolver::from_system_conf()?;
43 let response = resolver.lookup_ip(src)?;
44 response
45 .iter()
46 .next()
47 .ok_or_else(|| format!("failed to parse or resolve {} to an IP", src).into())
48 })
49}
50
51#[derive(StructOpt, Debug)]
52pub struct Cli {
53 #[structopt(subcommand)]
54 pub tunneler_type: TunnelerType,
55
56 #[structopt(long, env, possible_values = &TunneledType::variants(), case_insensitive = true)]
57 pub tunneled_type: TunneledType,
58
59 #[structopt(long, env, parse(try_from_str = parse_or_resolve_ip))]
60 pub remote_address: IpAddr,
61
62 #[structopt(long, env)]
63 pub remote_port: u16,
64
65 #[structopt(default_value = "0.0.0.0", long, env)]
66 pub local_address: IpAddr,
67
68 #[structopt(default_value = "8888", long, env)]
69 pub local_port: u16,
70
71 #[structopt(default_value = "info", long, env)]
72 pub log_level: log::LevelFilter,
73}
74
75#[cfg(test)]
76mod tests {
77 use std::net::{Ipv4Addr, Ipv6Addr};
78
79 use super::*;
80
81 #[test]
82 fn parse_or_resolve_ip_ipv4() -> Result<(), Box<dyn Error>> {
83 let res = parse_or_resolve_ip("127.0.0.1")?;
84 assert_eq!(res, IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
85 Ok(())
86 }
87
88 #[test]
89 fn parse_or_resolve_ip_ipv6() -> Result<(), Box<dyn Error>> {
90 let res = parse_or_resolve_ip("::1")?;
91 assert_eq!(res, IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
92 Ok(())
93 }
94
95 #[test]
96 fn parse_or_resolve_ip_lookup_success() -> Result<(), Box<dyn Error>> {
97 let res = parse_or_resolve_ip("localhost")?;
98 assert_eq!(res, IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
99 Ok(())
100 }
101
102 #[test]
103 fn parse_or_resolve_ip_lookup_fail() -> Result<(), Box<dyn Error>> {
104 let res = parse_or_resolve_ip("blablabla");
105 assert!(res.is_err());
106 Ok(())
107 }
108}