1use std::{ffi::OsString, path::PathBuf};
2
3use clap::{ArgAction, Args, Parser, Subcommand};
4
5const TARGET_HELP: &str = "Target in the form user@ENDPOINT_ID";
6const RELAY_URL_HELP: &str = "Use only these relay servers, replacing the defaults (repeatable)";
7const EXTRA_RELAY_URL_HELP: &str = "Add relay servers alongside the defaults (repeatable)";
8const KEY_DIR_HELP: &str = "Directory for iroh-ssh identity keys (default: ~/.ssh)";
9
10#[derive(Parser, Debug)]
11#[command(name = "iroh-ssh", about = "ssh without ip")]
12pub struct Cli {
13 #[command(subcommand)]
14 pub cmd: Option<Cmd>,
15
16 #[arg(help = TARGET_HELP)]
17 pub target: Option<String>,
18
19 #[arg(long, value_name = "URL", help = RELAY_URL_HELP, action = ArgAction::Append)]
20 pub relay_url: Vec<String>,
21
22 #[arg(long, value_name = "URL", help = EXTRA_RELAY_URL_HELP, action = ArgAction::Append)]
23 pub extra_relay_url: Vec<String>,
24
25 #[command(flatten)]
26 pub ssh: SshOpts,
27
28 #[arg(trailing_var_arg = true)]
29 pub remote_cmd: Option<Vec<OsString>>,
30}
31
32#[derive(Subcommand, Debug)]
33pub enum Cmd {
34 Connect(ConnectArgs),
35 #[command(hide = true)]
36 Exec(ExecArgs),
37 Server(ServerArgs),
38 Service {
39 #[command(subcommand)]
40 op: ServiceCmd,
41 },
42 Info(InfoArgs),
43 #[command(hide = true)]
44 Proxy(ProxyArgs),
45 #[command(hide = true)]
46 RunService(ServiceArgs),
47 Version,
48}
49
50#[derive(Args, Clone, Debug)]
51pub struct ProxyArgs {
52 #[arg(help = "Proxy Endpoint ID")]
53 pub endpoint_id: String,
54
55 #[arg(long, value_name = "URL", help = RELAY_URL_HELP, action = ArgAction::Append)]
56 pub relay_url: Vec<String>,
57
58 #[arg(long, value_name = "URL", help = EXTRA_RELAY_URL_HELP, action = ArgAction::Append)]
59 pub extra_relay_url: Vec<String>,
60}
61
62#[derive(Args, Clone, Debug)]
63pub struct ConnectArgs {
64 #[arg(help = TARGET_HELP)]
65 pub target: String,
66
67 #[arg(long, value_name = "URL", help = RELAY_URL_HELP, action = ArgAction::Append)]
68 pub relay_url: Vec<String>,
69
70 #[arg(long, value_name = "URL", help = EXTRA_RELAY_URL_HELP, action = ArgAction::Append)]
71 pub extra_relay_url: Vec<String>,
72
73 #[command(flatten)]
74 pub ssh: SshOpts,
75
76 #[arg(trailing_var_arg = true)]
77 pub remote_cmd: Vec<OsString>,
78}
79
80#[derive(Args, Clone, Debug)]
81pub struct ExecArgs {
82 #[arg(help = TARGET_HELP)]
83 pub target: String,
84
85 #[arg(long, value_name = "URL", help = RELAY_URL_HELP, action = ArgAction::Append)]
86 pub relay_url: Vec<String>,
87
88 #[arg(long, value_name = "URL", help = EXTRA_RELAY_URL_HELP, action = ArgAction::Append)]
89 pub extra_relay_url: Vec<String>,
90
91 #[command(flatten)]
92 pub ssh: SshOpts,
93
94 #[arg(trailing_var_arg = true, required = true)]
95 pub remote_cmd: Vec<OsString>,
96}
97
98#[derive(Args, Clone, Default, Debug)]
99pub struct SshOpts {
100 #[arg(
101 short = 'i',
102 long,
103 value_name = "PATH",
104 help = "Identity file for publickey auth"
105 )]
106 pub identity_file: Option<PathBuf>,
107
108 #[arg(short = 'L', value_name = "LPORT:HOST:RPORT",
109 help = "Local forward [bind_addr:]lport:host:rport (host can't be endpoint_id yet)", action = ArgAction::Append)]
110 pub local_forward: Vec<String>,
111
112 #[arg(short = 'R', value_name = "RPORT:HOST:LPORT",
113 help = "Remote forward [bind_addr:]rport:host:lport (host can't be endpoint_id yet)", action = ArgAction::Append)]
114 pub remote_forward: Vec<String>,
115
116 #[arg(
117 short = 'p',
118 long,
119 value_name = "PORT",
120 help = "Remote sshd port (default 22)"
121 )]
122 pub port: Option<u16>,
123
124 #[arg(
125 short = 'l',
126 value_name = "USER",
127 help = "Login user (lets rsync's `-l user host cmd` invocation work)"
128 )]
129 pub login_user: Option<String>,
130
131 #[arg(short = 'o', value_name = "KEY=VALUE",
132 help = "Pass an ssh option (repeatable)", action = ArgAction::Append)]
133 pub options: Vec<String>,
134
135 #[arg(short = 'A', help = "Enable agent forwarding", action = ArgAction::SetTrue)]
136 pub agent: bool,
137
138 #[arg(short = 'a', help = "Disable agent forwarding", action = ArgAction::SetTrue)]
139 pub no_agent: bool,
140
141 #[arg(short = 'X', help = "Enable X11 forwarding", action = ArgAction::SetTrue)]
142 pub x11: bool,
143
144 #[arg(short = 'Y', help = "Enable trusted X11 forwarding", action = ArgAction::SetTrue)]
145 pub x11_trusted: bool,
146
147 #[arg(short = 'N', help = "Do not execute remote command", action = ArgAction::SetTrue)]
148 pub no_cmd: bool,
149
150 #[arg(short = 't', help = "Force pseudo-terminal", action = ArgAction::SetTrue)]
151 pub force_tty: bool,
152
153 #[arg(short = 'T', help = "Disable pseudo-terminal", action = ArgAction::SetTrue)]
154 pub no_tty: bool,
155
156 #[arg(short = 'v', help = "Increase verbosity",
157 action = ArgAction::Count)]
158 pub verbose: u8,
159
160 #[arg(short = 'q', help = "Quiet mode", action = ArgAction::SetTrue)]
161 pub quiet: bool,
162}
163
164#[derive(Args, Clone, Debug)]
165pub struct ServerArgs {
166 #[arg(long, default_value = "22")]
167 pub ssh_port: u16,
168
169 #[arg(short, long, default_value_t = false)]
170 pub persist: bool,
171
172 #[arg(long, value_name = "DIR", help = KEY_DIR_HELP)]
173 pub key_dir: Option<PathBuf>,
174
175 #[arg(long, value_name = "URL", help = RELAY_URL_HELP, action = ArgAction::Append)]
176 pub relay_url: Vec<String>,
177
178 #[arg(long, value_name = "URL", help = EXTRA_RELAY_URL_HELP, action = ArgAction::Append)]
179 pub extra_relay_url: Vec<String>,
180}
181
182#[derive(Args, Clone, Debug)]
183pub struct InfoArgs {
184 #[arg(long, value_name = "DIR", help = KEY_DIR_HELP)]
185 pub key_dir: Option<PathBuf>,
186}
187
188#[derive(Subcommand, Clone, Debug)]
189pub enum ServiceCmd {
190 Install {
191 #[arg(long, default_value = "22")]
192 ssh_port: u16,
193
194 #[arg(long, value_name = "DIR", help = KEY_DIR_HELP)]
195 key_dir: Option<PathBuf>,
196
197 #[arg(long, value_name = "URL", help = RELAY_URL_HELP, action = ArgAction::Append)]
198 relay_url: Vec<String>,
199
200 #[arg(long, value_name = "URL", help = EXTRA_RELAY_URL_HELP, action = ArgAction::Append)]
201 extra_relay_url: Vec<String>,
202 },
203 Uninstall,
204}
205
206#[derive(Args, Clone, Debug)]
207pub struct ServiceArgs {
208 #[arg(long, default_value = "22")]
209 pub ssh_port: u16,
210
211 #[arg(long, value_name = "DIR", help = KEY_DIR_HELP)]
212 pub key_dir: Option<PathBuf>,
213
214 #[arg(long, value_name = "URL", help = RELAY_URL_HELP, action = ArgAction::Append)]
215 pub relay_url: Vec<String>,
216
217 #[arg(long, value_name = "URL", help = EXTRA_RELAY_URL_HELP, action = ArgAction::Append)]
218 pub extra_relay_url: Vec<String>,
219}