1use std::net::SocketAddr;
2use std::path::PathBuf;
3
4use clap::Parser;
5use clap::builder::Styles;
6use clap::builder::styling::{AnsiColor, Style};
7
8use ombrac_transport::quic::Congestion;
9
10use crate::config::{EndpointConfig, TlsMode, TransportConfig};
11
12#[derive(Parser, Debug)]
14#[command(version, about, long_about = None, styles = styles())]
15pub struct Args {
16 #[clap(long, short = 'c', value_name = "FILE")]
18 pub config: Option<PathBuf>,
19
20 #[clap(
22 long,
23 short = 'k',
24 help_heading = "Required",
25 value_name = "STR",
26 required_unless_present = "config"
27 )]
28 pub secret: Option<String>,
29
30 #[clap(
32 long,
33 short = 's',
34 help_heading = "Required",
35 value_name = "ADDR",
36 required_unless_present = "config"
37 )]
38 pub server: Option<String>,
39
40 #[clap(
42 long,
43 help_heading = "Protocol",
44 value_name = "STR",
45 alias = "handshake-option"
46 )]
47 pub auth_option: Option<String>,
48
49 #[clap(flatten)]
50 pub endpoint: CliEndpointConfig,
51
52 #[clap(flatten)]
53 pub transport: CliTransportConfig,
54
55 #[cfg(feature = "tracing")]
56 #[clap(flatten)]
57 pub logging: CliLoggingConfig,
58}
59
60#[derive(Parser, Debug, Clone, Default)]
62pub struct CliEndpointConfig {
63 #[cfg(feature = "endpoint-http")]
65 #[clap(long, value_name = "ADDR", help_heading = "Endpoint")]
66 pub http: Option<SocketAddr>,
67
68 #[cfg(feature = "endpoint-socks")]
70 #[clap(long, value_name = "ADDR", help_heading = "Endpoint")]
71 pub socks: Option<SocketAddr>,
72
73 #[cfg(feature = "endpoint-tun")]
74 #[clap(flatten)]
75 pub tun: Option<CliTunConfig>,
76}
77
78impl CliEndpointConfig {
79 pub fn into_endpoint_config(self) -> EndpointConfig {
81 EndpointConfig {
82 #[cfg(feature = "endpoint-http")]
83 http: self.http,
84 #[cfg(feature = "endpoint-socks")]
85 socks: self.socks,
86 #[cfg(feature = "endpoint-tun")]
87 tun: self.tun.map(|t| t.into_tun_config()),
88 }
89 }
90}
91
92#[cfg(feature = "endpoint-tun")]
94#[derive(Parser, Debug, Clone, Default)]
95pub struct CliTunConfig {
96 #[clap(long, help_heading = "Endpoint", value_name = "FD")]
99 pub tun_fd: Option<i32>,
100
101 #[clap(long, help_heading = "Endpoint", value_name = "CIDR")]
103 pub tun_ipv4: Option<String>,
104
105 #[clap(long, help_heading = "Endpoint", value_name = "CIDR")]
107 pub tun_ipv6: Option<String>,
108
109 #[clap(long, help_heading = "Endpoint", value_name = "MTU")]
111 pub tun_mtu: Option<u16>,
112
113 #[clap(long, help_heading = "Endpoint", value_name = "CIDR")]
115 pub fake_dns: Option<String>,
116
117 #[clap(long, help_heading = "Endpoint", value_name = "BOOL")]
119 pub disable_udp_443: Option<bool>,
120}
121
122#[cfg(feature = "endpoint-tun")]
123impl CliTunConfig {
124 pub fn into_tun_config(self) -> crate::config::TunConfig {
126 crate::config::TunConfig {
127 tun_fd: self.tun_fd,
128 tun_ipv4: self.tun_ipv4,
129 tun_ipv6: self.tun_ipv6,
130 tun_mtu: self.tun_mtu,
131 fake_dns: self.fake_dns,
132 disable_udp_443: self.disable_udp_443,
133 }
134 }
135}
136
137#[derive(Parser, Debug, Clone)]
139pub struct CliTransportConfig {
140 #[clap(long, help_heading = "Transport", value_name = "ADDR")]
142 pub bind: Option<SocketAddr>,
143
144 #[clap(long, help_heading = "Transport", value_name = "STR")]
146 pub server_name: Option<String>,
147
148 #[clap(long, value_enum, help_heading = "Transport")]
150 pub tls_mode: Option<TlsMode>,
151
152 #[clap(long, help_heading = "Transport", value_name = "FILE")]
155 pub ca_cert: Option<PathBuf>,
156
157 #[clap(long, help_heading = "Transport", value_name = "FILE")]
159 pub client_cert: Option<PathBuf>,
160
161 #[clap(long, help_heading = "Transport", value_name = "FILE")]
163 pub client_key: Option<PathBuf>,
164
165 #[clap(long, help_heading = "Transport", value_name = "BOOL")]
167 pub zero_rtt: Option<bool>,
168
169 #[clap(
171 long,
172 help_heading = "Transport",
173 value_name = "PROTOCOLS",
174 value_delimiter = ','
175 )]
176 pub alpn_protocols: Option<Vec<Vec<u8>>>,
177
178 #[clap(long, help_heading = "Transport", value_name = "ALGORITHM")]
180 pub congestion: Option<Congestion>,
181
182 #[clap(long, help_heading = "Transport", value_name = "NUM")]
184 pub cwnd_init: Option<u64>,
185
186 #[clap(long, help_heading = "Transport", value_name = "TIME")]
188 pub idle_timeout: Option<u64>,
189
190 #[clap(long, help_heading = "Transport", value_name = "TIME")]
192 pub keep_alive: Option<u64>,
193
194 #[clap(long, help_heading = "Transport", value_name = "NUM")]
196 pub max_streams: Option<u64>,
197}
198
199#[cfg(feature = "tracing")]
201#[derive(Parser, Debug, Clone)]
202pub struct CliLoggingConfig {
203 #[clap(long, help_heading = "Logging", value_name = "LEVEL")]
205 pub log_level: Option<String>,
206}
207
208impl CliTransportConfig {
209 pub fn into_transport_config(self) -> TransportConfig {
211 TransportConfig {
212 bind: self.bind,
213 server_name: self.server_name,
214 tls_mode: self.tls_mode,
215 ca_cert: self.ca_cert,
216 client_cert: self.client_cert,
217 client_key: self.client_key,
218 zero_rtt: self.zero_rtt,
219 alpn_protocols: self.alpn_protocols,
220 congestion: self.congestion,
221 cwnd_init: self.cwnd_init,
222 idle_timeout: self.idle_timeout,
223 keep_alive: self.keep_alive,
224 max_streams: self.max_streams,
225 }
226 }
227}
228
229#[cfg(feature = "tracing")]
230impl CliLoggingConfig {
231 pub fn into_logging_config(self) -> crate::config::LoggingConfig {
233 crate::config::LoggingConfig {
234 log_level: self.log_level,
235 }
236 }
237}
238
239#[derive(Debug)]
241pub struct CliConfig {
242 pub secret: Option<String>,
243 pub server: Option<String>,
244 pub auth_option: Option<String>,
245 pub endpoint: EndpointConfig,
246 pub transport: TransportConfig,
247 #[cfg(feature = "tracing")]
248 pub logging: crate::config::LoggingConfig,
249}
250
251impl Args {
252 pub fn parse_to_config() -> CliConfig {
254 let args = Self::parse();
255 CliConfig {
256 secret: args.secret,
257 server: args.server,
258 auth_option: args.auth_option,
259 endpoint: args.endpoint.into_endpoint_config(),
260 transport: args.transport.into_transport_config(),
261 #[cfg(feature = "tracing")]
262 logging: args.logging.into_logging_config(),
263 }
264 }
265
266 pub fn config_path() -> Option<PathBuf> {
268 Self::parse().config
269 }
270}
271
272fn styles() -> Styles {
273 Styles::styled()
274 .header(Style::new().bold().fg_color(Some(AnsiColor::Green.into())))
275 .usage(Style::new().bold().fg_color(Some(AnsiColor::Green.into())))
276 .literal(Style::new().bold().fg_color(Some(AnsiColor::Cyan.into())))
277 .placeholder(Style::new().fg_color(Some(AnsiColor::Cyan.into())))
278 .valid(Style::new().bold().fg_color(Some(AnsiColor::Cyan.into())))
279 .invalid(Style::new().bold().fg_color(Some(AnsiColor::Yellow.into())))
280 .error(Style::new().bold().fg_color(Some(AnsiColor::Red.into())))
281}