1use std::str;
17
18use super::{
19 addr::Addr,
20 parser::{ConnectionExprLanguage, Pairs, Parser, Rule},
21 port_set::PortSet,
22};
23
24#[derive(Clone, Debug, PartialEq)]
25pub enum ConnectionExpr {
26 RouteExpr(RouteExpr),
27 HostKey(String),
28}
29
30impl ConnectionExpr {
31 pub fn try_as_route_expr(&self) -> Option<&RouteExpr> {
32 if let ConnectionExpr::RouteExpr(ref e) = *self {
33 Some(e)
34 } else {
35 None
36 }
37 }
38}
39
40impl From<RouteExpr> for ConnectionExpr {
41 fn from(route_expr: RouteExpr) -> Self {
42 ConnectionExpr::RouteExpr(route_expr)
43 }
44}
45
46#[derive(Clone, Debug, PartialEq)]
47pub struct RouteExpr {
48 pub ports: PortSet,
49 pub addr: Option<Addr>,
50 pub tunnel: Option<TunnelExpr>,
51}
52
53#[derive(Clone, Debug, PartialEq)]
54pub struct TunnelExpr {
55 pub user: Option<String>,
56 pub addr: Addr,
57 pub ports: Option<PortSet>,
58}
59
60impl str::FromStr for ConnectionExpr {
61 type Err = ParseError;
62
63 fn from_str(s: &str) -> Result<Self, Self::Err> {
64 let top_pair = ConnectionExprLanguage::parse(Rule::connection_expr, s)
65 .map_err(|_| ParseError)?
66 .next()
67 .expect("grammar guarantees host expression")
68 .into_inner()
69 .next()
70 .expect("grammar guarantees inner specific host expression");
71 match top_pair.as_rule() {
72 Rule::local_connection_expr => {
73 connection_expr_from_local_connection_expr_pair(top_pair.into_inner())
74 .map(|e| e.into())
75 }
76 Rule::remote_connection_expr => {
77 connection_expr_from_remote_connection_expr_pair(top_pair.into_inner())
78 .map(|e| e.into())
79 }
80 Rule::tunneled_connection_expr => {
81 connection_expr_from_tunneled_connection_expr_pair(
82 top_pair.into_inner(),
83 )
84 .map(|e| e.into())
85 }
86 Rule::host_key_expr => {
87 Ok(ConnectionExpr::HostKey(top_pair.as_str().to_string()))
88 }
89 _ => unreachable!(
90 r#"grammar guarantees a local, remote, or tunneled route
91 expression to a remote host, or a host key reference"#
92 ),
93 }
94 }
95}
96
97fn connection_expr_from_local_connection_expr_pair(
98 mut pairs: Pairs<Rule>,
99) -> Result<RouteExpr, ParseError> {
100 Ok(RouteExpr {
101 ports: pairs
102 .next()
103 .expect("grammar guarantees a port set")
104 .try_into()
105 .map_err(|_| ParseError)?,
107 addr: None,
108 tunnel: None,
109 })
110}
111
112fn connection_expr_from_remote_connection_expr_pair(
113 mut pairs: Pairs<Rule>,
114) -> Result<RouteExpr, ParseError> {
115 let addr = pairs
116 .next()
117 .expect("grammar guarantees an address")
118 .try_into()
119 .expect("grammar guarantees the address is legal");
120 let mut connection_expr = connection_expr_from_local_connection_expr_pair(
121 pairs
122 .next()
123 .expect("grammar guarantees a local port expression")
124 .into_inner(),
125 )?;
126 connection_expr.addr = Some(addr);
127 Ok(connection_expr)
128}
129
130fn connection_expr_from_tunneled_connection_expr_pair(
131 pairs: Pairs<Rule>,
132) -> Result<RouteExpr, ParseError> {
133 let (tunnel, mut pairs) = tunnel_from_pairs(pairs)?;
134 let mut connection_expr = connection_expr_from_remote_connection_expr_pair(
135 pairs
136 .next()
137 .expect("grammar guarantees a remote port expression")
138 .into_inner(),
139 )?;
140 connection_expr.tunnel = Some(tunnel);
141 Ok(connection_expr)
142}
143
144fn tunnel_from_pairs(
145 mut pairs: Pairs<Rule>,
146) -> Result<(TunnelExpr, Pairs<Rule>), ParseError> {
147 let mut next = pairs.next().expect("grammar guarantees a user or address");
148 let user = if matches!(next.as_rule(), Rule::user) {
149 let s = next.as_str().to_owned();
150 next = pairs.next().expect("grammar guarantees an address");
151 Some(s)
152 } else {
153 None
154 };
155 match next.as_rule() {
156 Rule::addr => Ok((
157 TunnelExpr {
158 user,
159 addr: next.try_into().expect("grammar guarantess addr is legal"),
160 ports: None,
161 },
162 pairs,
163 )),
164 Rule::addr_and_port => {
165 let mut inner = next.into_inner();
166 let addr = inner
167 .next()
168 .expect("addr by grammar")
169 .try_into()
170 .expect("correct by gramma");
171 let ports = inner
172 .next()
173 .expect("port_set by grammar")
174 .try_into()
175 .map_err(|_| ParseError)?;
176 Ok((
177 TunnelExpr {
178 user,
179 addr,
180 ports: Some(ports),
181 },
182 pairs,
183 ))
184 }
185 _ => unreachable!("grammar guarantees addr or addr_and_port"),
186 }
187}
188
189#[derive(Debug, PartialEq, thiserror::Error)]
190#[error("parse error")]
191pub struct ParseError;
192
193#[cfg(test)]
194mod test {
195
196 use super::*;
197 use std::net;
198
199 fn ps(ports: &[u16]) -> PortSet {
200 maybe_ps(ports).unwrap()
201 }
202
203 fn maybe_ps(ports: &[u16]) -> Option<PortSet> {
204 PortSet::try_from_iter(ports.iter().cloned())
205 }
206
207 fn ip4(a: u8, b: u8, c: u8, d: u8) -> Addr {
208 Addr::IP(net::Ipv4Addr::new(a, b, c, d).into())
209 }
210
211 fn ip6(
212 a: u16,
213 b: u16,
214 c: u16,
215 d: u16,
216 e: u16,
217 f: u16,
218 g: u16,
219 h: u16,
220 ) -> Addr {
221 Addr::IP(net::Ipv6Addr::new(a, b, c, d, e, f, g, h).into())
222 }
223
224 fn dom(domain: &str) -> Addr {
225 Addr::Domain(domain.to_owned())
226 }
227
228 #[test]
229 fn local_connection_expr_parsing() {
230 let mk = |ports| {
231 Ok(ConnectionExpr::RouteExpr(RouteExpr {
232 ports: ps(ports),
233 addr: None,
234 tunnel: None,
235 }))
236 };
237 assert_eq!("1".parse(), mk(&[1]));
238 assert_eq!("1,2".parse(), mk(&[1, 2]));
239 assert_eq!("1-3".parse(), mk(&[1, 2, 3]));
240 assert_eq!("1,3,1-3".parse(), mk(&[1, 3, 2]));
241 }
242
243 #[test]
244 fn remote_connection_expr_parsing() {
245 let mk = |addr, ports| {
246 Ok(ConnectionExpr::RouteExpr(RouteExpr {
247 ports: ps(ports),
248 addr: Some(addr),
249 tunnel: None,
250 }))
251 };
252 assert_eq!("1.2.3.4:1,2-3".parse(), mk(ip4(1, 2, 3, 4), &[1, 2, 3]));
253 assert_eq!(
254 "[0:dead::beef:0]:1,2-3".parse(),
255 mk(ip6(0, 0xDEAD, 0, 0, 0, 0, 0xBEEF, 0), &[1, 2, 3])
256 );
257 assert_eq!("localhost:1,2-3".parse(), mk(dom("localhost"), &[1, 2, 3]));
258 }
259
260 #[test]
261 fn tunneled_connection_expr_parsing() {
262 let mk = |tunnel_user: Option<&str>,
263 tunnel_addr,
264 tunnel_ports: &[u16],
265 server_addr,
266 server_ports| {
267 Ok(ConnectionExpr::RouteExpr(RouteExpr {
268 ports: ps(server_ports),
269 addr: Some(server_addr),
270 tunnel: Some(TunnelExpr {
271 user: tunnel_user.map(|s| s.to_owned()),
272 addr: tunnel_addr,
273 ports: maybe_ps(tunnel_ports),
274 }),
275 }))
276 };
277 assert_eq!(
278 "1.2.3.4:5.6.7.8:9".parse(),
279 mk(None, ip4(1, 2, 3, 4), &[], ip4(5, 6, 7, 8), &[9])
280 );
281 assert_eq!(
282 "1.2.3.4:5:6.7.8.9:10".parse(),
283 mk(None, ip4(1, 2, 3, 4), &[5], ip4(6, 7, 8, 9), &[10])
284 );
285 assert_eq!(
286 "1.2.3.4:5,6-7:8.9.10.11:12-14,15-16".parse(),
287 mk(
288 None,
289 ip4(1, 2, 3, 4),
290 &[5, 6, 7],
291 ip4(8, 9, 10, 11),
292 &[12, 13, 14, 15, 16]
293 )
294 );
295 assert_eq!(
296 "a@1.2.3.4:5.6.7.8:9".parse(),
297 mk(Some("a"), ip4(1, 2, 3, 4), &[], ip4(5, 6, 7, 8), &[9])
298 );
299 assert_eq!(
300 "a@1.2.3.4:5:6.7.8.9:10".parse(),
301 mk(Some("a"), ip4(1, 2, 3, 4), &[5], ip4(6, 7, 8, 9), &[10])
302 );
303 assert_eq!(
304 "[::]:[::]:1".parse(),
305 mk(
306 None,
307 ip6(0, 0, 0, 0, 0, 0, 0, 0),
308 &[],
309 ip6(0, 0, 0, 0, 0, 0, 0, 0),
310 &[1]
311 )
312 );
313 assert_eq!(
314 "[::]:1:[::]:1".parse(),
315 mk(
316 None,
317 ip6(0, 0, 0, 0, 0, 0, 0, 0),
318 &[1],
319 ip6(0, 0, 0, 0, 0, 0, 0, 0),
320 &[1]
321 )
322 );
323 assert_eq!(
324 "a@[1:2:3:4:5:6:7:8]:9:[10:11:12:13:14:15:16:17]:18".parse(),
325 mk(
326 Some("a"),
327 ip6(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08),
328 &[9],
329 ip6(0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17),
330 &[18]
331 )
332 );
333 assert_eq!(
334 "a@b.c.d:e.f.g:1".parse(),
335 mk(Some("a"), dom("b.c.d"), &[], dom("e.f.g"), &[1])
336 );
337 assert_eq!(
338 "a@b.c.d.:1:e.f.g.:2".parse(),
339 mk(Some("a"), dom("b.c.d."), &[1], dom("e.f.g."), &[2])
340 );
341 }
342
343 #[test]
344 fn host_key_expr_parsing() {
345 let mk = |key: &str| Ok(ConnectionExpr::HostKey(key.to_owned()));
346 assert_eq!("x".parse(), mk("x"));
347 assert_eq!("my_prod_host_1".parse(), mk("my_prod_host_1"));
348 }
349}