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(short = 'o', value_name = "KEY=VALUE",
125 help = "Pass an ssh option (repeatable)", action = ArgAction::Append)]
126 pub options: Vec<String>,
127
128 #[arg(short = 'A', help = "Enable agent forwarding", action = ArgAction::SetTrue)]
129 pub agent: bool,
130
131 #[arg(short = 'a', help = "Disable agent forwarding", action = ArgAction::SetTrue)]
132 pub no_agent: bool,
133
134 #[arg(short = 'X', help = "Enable X11 forwarding", action = ArgAction::SetTrue)]
135 pub x11: bool,
136
137 #[arg(short = 'Y', help = "Enable trusted X11 forwarding", action = ArgAction::SetTrue)]
138 pub x11_trusted: bool,
139
140 #[arg(short = 'N', help = "Do not execute remote command", action = ArgAction::SetTrue)]
141 pub no_cmd: bool,
142
143 #[arg(short = 't', help = "Force pseudo-terminal", action = ArgAction::SetTrue)]
144 pub force_tty: bool,
145
146 #[arg(short = 'T', help = "Disable pseudo-terminal", action = ArgAction::SetTrue)]
147 pub no_tty: bool,
148
149 #[arg(short = 'v', help = "Increase verbosity",
150 action = ArgAction::Count)]
151 pub verbose: u8,
152
153 #[arg(short = 'q', help = "Quiet mode", action = ArgAction::SetTrue)]
154 pub quiet: bool,
155}
156
157#[derive(Args, Clone, Debug)]
158pub struct ServerArgs {
159 #[arg(long, default_value = "22")]
160 pub ssh_port: u16,
161
162 #[arg(short, long, default_value_t = false)]
163 pub persist: bool,
164
165 #[arg(long, value_name = "DIR", help = KEY_DIR_HELP)]
166 pub key_dir: Option<PathBuf>,
167
168 #[arg(long, value_name = "URL", help = RELAY_URL_HELP, action = ArgAction::Append)]
169 pub relay_url: Vec<String>,
170
171 #[arg(long, value_name = "URL", help = EXTRA_RELAY_URL_HELP, action = ArgAction::Append)]
172 pub extra_relay_url: Vec<String>,
173}
174
175#[derive(Args, Clone, Debug)]
176pub struct InfoArgs {
177 #[arg(long, value_name = "DIR", help = KEY_DIR_HELP)]
178 pub key_dir: Option<PathBuf>,
179}
180
181#[derive(Subcommand, Clone, Debug)]
182pub enum ServiceCmd {
183 Install {
184 #[arg(long, default_value = "22")]
185 ssh_port: u16,
186
187 #[arg(long, value_name = "DIR", help = KEY_DIR_HELP)]
188 key_dir: Option<PathBuf>,
189
190 #[arg(long, value_name = "URL", help = RELAY_URL_HELP, action = ArgAction::Append)]
191 relay_url: Vec<String>,
192
193 #[arg(long, value_name = "URL", help = EXTRA_RELAY_URL_HELP, action = ArgAction::Append)]
194 extra_relay_url: Vec<String>,
195 },
196 Uninstall,
197}
198
199#[derive(Args, Clone, Debug)]
200pub struct ServiceArgs {
201 #[arg(long, default_value = "22")]
202 pub ssh_port: u16,
203
204 #[arg(long, value_name = "DIR", help = KEY_DIR_HELP)]
205 pub key_dir: Option<PathBuf>,
206
207 #[arg(long, value_name = "URL", help = RELAY_URL_HELP, action = ArgAction::Append)]
208 pub relay_url: Vec<String>,
209
210 #[arg(long, value_name = "URL", help = EXTRA_RELAY_URL_HELP, action = ArgAction::Append)]
211 pub extra_relay_url: Vec<String>,
212}