wireguard_uapi/xplatform/parser/
parse.rs

1use super::state::{ParsePeerState, ParseState};
2use crate::get;
3use crate::get::{DeviceBuilderError, ParseAllowedIpError, PeerBuilderError};
4use crate::xplatform::protocol::{GetKey, ParseKeyError};
5use std::net::AddrParseError;
6use std::num::ParseIntError;
7use std::{str::FromStr, time::Duration};
8use take_until::TakeUntilExt;
9use thiserror::Error;
10
11#[derive(Error, Debug)]
12pub enum ParseGetResponseError {
13    #[error("Failed to read line from socket: `{0}`")]
14    ReadLineIoError(#[source] std::io::Error),
15
16    #[error("Received non-zero error number in response: `{0}`")]
17    ServerError(String),
18    #[error("Received incomplete response")]
19    IncompleteResponse,
20    #[error("Received empty response")]
21    EmptyResponse,
22    #[error("Get responses require an empty line. None found.")]
23    MissingEndOfResponseNewline,
24
25    #[error("Encountered unknown key `{0}`")]
26    UnknownKey(String),
27    #[error("Missing value for key `{0}`")]
28    MissingValueForKey(GetKey),
29
30    #[error("{0}")]
31    InternalBuildGetDeviceError(DeviceBuilderError),
32    #[error("{0}")]
33    InternalBuildGetPeerError(PeerBuilderError),
34
35    #[error("Invalid private_key")]
36    InvalidPrivateKey,
37    #[error("Invalid public_key: `{0}`")]
38    InvalidPublicKey(String),
39    #[error("Invalid preshared_key: `{0}`")]
40    InvalidPresharedKey(String),
41    #[error("{0}")]
42    InvalidListenPort(#[source] ParseIntError),
43    #[error("{0}")]
44    InvalidFwmark(#[source] ParseIntError),
45    #[error("{0}")]
46    InvalidEndpoint(#[source] AddrParseError),
47    #[error("{0}")]
48    InvalidPersistentKeepaliveInterval(#[source] ParseIntError),
49    #[error(transparent)]
50    InvalidAllowedIp(#[from] ParseAllowedIpError),
51    #[error("{0}")]
52    InvalidRxBytes(#[source] ParseIntError),
53    #[error("{0}")]
54    InvalidTxBytes(#[source] ParseIntError),
55    #[error("{0}")]
56    InvalidLastHandhsakeTimeSec(#[source] ParseIntError),
57    #[error("{0}")]
58    InvalidLastHandhsakeTimeNsec(#[source] ParseIntError),
59    #[error("{0}")]
60    InvalidProtocolVersion(#[source] ParseIntError),
61
62    // Invalid parser state transition errors
63    #[error("Expected `private_key=...` or `listen_port=...`. Observed key: `{0}`")]
64    InvalidStartOfResponse(GetKey),
65    #[error("private_key was ambiguously specified twice in response.")]
66    AmbiguousPrivateKey,
67    #[error("Observed peer-level key `{0}` before public_key was specified")]
68    PeerLevelKeyBeforePublicKey(GetKey),
69    #[error("Observed interface-level key `{0}` after a peer-level key")]
70    InterfaceLevelKeyAfterPeerLevelKey(GetKey),
71    #[error("Observed key after end of response")]
72    DataAfterEndOfResponse(GetKey),
73}
74
75impl From<ParseKeyError> for ParseGetResponseError {
76    fn from(err: ParseKeyError) -> Self {
77        Self::UnknownKey(err.unknown_key)
78    }
79}
80
81pub fn parse(
82    lines: impl Iterator<Item = Result<String, std::io::Error>>,
83) -> Result<get::Device, ParseGetResponseError> {
84    let initial_state = {
85        let mut device_builder = get::DeviceBuilder::default();
86        device_builder.ifindex(0);
87        device_builder.ifname("".to_string());
88        device_builder.fwmark(0);
89        ParseState::Initial(device_builder)
90    };
91    let parse_state = lines
92        // WireGuard xplatform implementations signify the end of a response
93        // with an empty newline. Stop reading beyond this point to avoid
94        // hanging.
95        //
96        // The rust standard library may include take_until in the future.
97        // https://github.com/rust-lang/rust/issues/62208
98        .take_until(|result| matches!(result.as_ref().map(String::as_str), Ok("")))
99        .try_fold(initial_state, process_line)?;
100
101    match parse_state {
102        ParseState::Initial(_) => Err(ParseGetResponseError::EmptyResponse),
103        ParseState::InterfaceLevelKeys(_) | ParseState::PeerLevelKeys(_) => {
104            Err(ParseGetResponseError::MissingEndOfResponseNewline)
105        }
106        ParseState::Finish(device) => Ok(device),
107    }
108}
109
110fn process_line(
111    state: ParseState,
112    line: std::io::Result<String>,
113) -> Result<ParseState, ParseGetResponseError> {
114    type ParseErr = ParseGetResponseError;
115
116    let line = line.map_err(ParseErr::ReadLineIoError)?;
117
118    // An empty line signifies the end of a "get" response.
119    if line.is_empty() {
120        return match state {
121            ParseState::Initial(_) => Err(ParseGetResponseError::IncompleteResponse),
122            ParseState::InterfaceLevelKeys(device_builder) => device_builder
123                .build()
124                .map_err(ParseGetResponseError::InternalBuildGetDeviceError)
125                .map(ParseState::Finish),
126            ParseState::PeerLevelKeys(state) => Ok(ParseState::Finish(state.coalesce())),
127            ParseState::Finish(device) => Ok(ParseState::Finish(device)),
128        };
129    }
130
131    let (key, raw_val) = {
132        let mut tokens = line.trim().splitn(2, '=');
133
134        // The first token should always exist.
135        let raw_key = tokens.next().unwrap();
136        let key = GetKey::from_str(raw_key)?;
137
138        let raw_val = match tokens.next() {
139            Some(val) => val,
140            None => return Err(ParseErr::MissingValueForKey(key)),
141        };
142
143        (key, raw_val)
144    };
145
146    match state {
147        ParseState::Initial(mut device_builder) => match key {
148            GetKey::PrivateKey => {
149                let private_key: [u8; 32] = hex::decode(raw_val)
150                    .ok()
151                    .and_then(|buf| parse_device_key(&buf))
152                    .ok_or(ParseErr::InvalidPrivateKey)?;
153                device_builder.private_key(Some(private_key));
154                Ok(ParseState::InterfaceLevelKeys(device_builder))
155            }
156            GetKey::ListenPort => {
157                let listen_port = raw_val.parse().map_err(ParseErr::InvalidListenPort)?;
158                device_builder.listen_port(listen_port);
159                Ok(ParseState::InterfaceLevelKeys(device_builder))
160            }
161            GetKey::Errno => match raw_val {
162                "0" => Ok(ParseState::Initial(device_builder)),
163                _ => Err(ParseErr::ServerError(raw_val.to_string())),
164            },
165            _ => Err(ParseErr::InvalidStartOfResponse(key)),
166        },
167
168        ParseState::InterfaceLevelKeys(mut device_builder) => match key {
169            GetKey::PrivateKey => Err(ParseErr::AmbiguousPrivateKey),
170            GetKey::ListenPort => {
171                let listen_port = raw_val.parse().map_err(ParseErr::InvalidListenPort)?;
172                device_builder.listen_port(listen_port);
173                Ok(ParseState::InterfaceLevelKeys(device_builder))
174            }
175            GetKey::Fwmark => {
176                let fwmark = raw_val.parse().map_err(ParseErr::InvalidFwmark)?;
177                device_builder.fwmark(fwmark);
178                Ok(ParseState::InterfaceLevelKeys(device_builder))
179            }
180
181            // A public_key entry specifies the start of a new peer block.
182            // Transition the parser state to receive peer-level keys.
183            GetKey::PublicKey => {
184                let mut peer_builder = get::PeerBuilder::default();
185                let public_key = hex::decode(raw_val)
186                    .ok()
187                    .and_then(|buf| parse_device_key(&buf))
188                    .ok_or_else(|| ParseErr::InvalidPublicKey(raw_val.to_string()))?;
189                peer_builder.public_key(public_key);
190                peer_builder.preshared_key([0u8; 32]);
191                peer_builder.persistent_keepalive_interval(0);
192                peer_builder.tx_bytes(0);
193                peer_builder.rx_bytes(0);
194                peer_builder.protocol_version(1);
195                Ok(ParseState::PeerLevelKeys(Box::new(ParsePeerState {
196                    device_builder,
197                    peers: vec![],
198                    peer_builder,
199                    allowed_ips: vec![],
200                    last_handshake_time_sec: None,
201                    last_handshake_time_nsec: None,
202                })))
203            }
204
205            GetKey::PresharedKey
206            | GetKey::Endpoint
207            | GetKey::PersistentKeepaliveInterval
208            | GetKey::AllowedIp
209            | GetKey::RxBytes
210            | GetKey::TxBytes
211            | GetKey::LastHandshakeTimeSec
212            | GetKey::LastHandshakeTimeNsec
213            | GetKey::ProtocolVersion => Err(ParseErr::PeerLevelKeyBeforePublicKey(key)),
214
215            GetKey::Errno => match raw_val {
216                "0" => Ok(ParseState::InterfaceLevelKeys(device_builder)),
217                _ => Err(ParseErr::ServerError(raw_val.to_string())),
218            },
219        },
220
221        ParseState::PeerLevelKeys(mut state) => match key {
222            GetKey::PrivateKey | GetKey::ListenPort | GetKey::Fwmark => {
223                Err(ParseErr::InterfaceLevelKeyAfterPeerLevelKey(key))
224            }
225
226            GetKey::PublicKey => {
227                state.peer_builder.allowed_ips(state.allowed_ips);
228                let last_handshake_time = Duration::new(
229                    state.last_handshake_time_sec.unwrap_or(0),
230                    state.last_handshake_time_nsec.unwrap_or(0),
231                );
232                state.peer_builder.last_handshake_time(last_handshake_time);
233                let peer = state
234                    .peer_builder
235                    .build()
236                    .map_err(ParseErr::InternalBuildGetPeerError)?;
237                state.peers.push(peer);
238
239                state.peer_builder = get::PeerBuilder::default();
240                let public_key = hex::decode(raw_val)
241                    .ok()
242                    .and_then(|buf| parse_device_key(&buf))
243                    .ok_or_else(|| ParseErr::InvalidPublicKey(raw_val.to_string()))?;
244                state.peer_builder.public_key(public_key);
245                state.peer_builder.preshared_key([0u8; 32]);
246                state.peer_builder.persistent_keepalive_interval(0);
247                state.peer_builder.tx_bytes(0);
248                state.peer_builder.rx_bytes(0);
249                state.peer_builder.protocol_version(1);
250                state.allowed_ips = vec![];
251
252                Ok(ParseState::PeerLevelKeys(state))
253            }
254            GetKey::PresharedKey => {
255                let preshared_key = hex::decode(raw_val)
256                    .ok()
257                    .and_then(|buf| parse_device_key(&buf))
258                    .ok_or_else(|| ParseErr::InvalidPresharedKey(raw_val.to_string()))?;
259                state.peer_builder.preshared_key(preshared_key);
260                Ok(ParseState::PeerLevelKeys(state))
261            }
262            GetKey::Endpoint => {
263                let endpoint = raw_val.parse().map_err(ParseErr::InvalidEndpoint)?;
264                state.peer_builder.endpoint(Some(endpoint));
265                Ok(ParseState::PeerLevelKeys(state))
266            }
267            GetKey::PersistentKeepaliveInterval => {
268                let interval = raw_val
269                    .parse()
270                    .map_err(ParseErr::InvalidPersistentKeepaliveInterval)?;
271                state.peer_builder.persistent_keepalive_interval(interval);
272                Ok(ParseState::PeerLevelKeys(state))
273            }
274            GetKey::AllowedIp => {
275                let allowed_ip: get::AllowedIp = raw_val.parse()?;
276                state.allowed_ips.push(allowed_ip);
277                Ok(ParseState::PeerLevelKeys(state))
278            }
279            GetKey::RxBytes => {
280                let rx_bytes = raw_val.parse().map_err(ParseErr::InvalidRxBytes)?;
281                state.peer_builder.rx_bytes(rx_bytes);
282                Ok(ParseState::PeerLevelKeys(state))
283            }
284            GetKey::TxBytes => {
285                let tx_bytes = raw_val.parse().map_err(ParseErr::InvalidTxBytes)?;
286                state.peer_builder.tx_bytes(tx_bytes);
287                Ok(ParseState::PeerLevelKeys(state))
288            }
289            GetKey::LastHandshakeTimeSec => {
290                let sec = raw_val
291                    .parse()
292                    .map_err(ParseErr::InvalidLastHandhsakeTimeSec)?;
293                state.last_handshake_time_sec = Some(sec);
294                Ok(ParseState::PeerLevelKeys(state))
295            }
296            GetKey::LastHandshakeTimeNsec => {
297                let nsec = raw_val
298                    .parse()
299                    .map_err(ParseErr::InvalidLastHandhsakeTimeNsec)?;
300                state.last_handshake_time_nsec = Some(nsec);
301                Ok(ParseState::PeerLevelKeys(state))
302            }
303            GetKey::ProtocolVersion => {
304                let protocol_version = raw_val.parse().map_err(ParseErr::InvalidProtocolVersion)?;
305                state.peer_builder.protocol_version(protocol_version);
306                Ok(ParseState::PeerLevelKeys(state))
307            }
308            GetKey::Errno => match raw_val {
309                "0" => Ok(ParseState::PeerLevelKeys(state)),
310                _ => Err(ParseErr::ServerError(raw_val.to_string())),
311            },
312        },
313
314        ParseState::Finish(_) => Err(ParseErr::DataAfterEndOfResponse(key)),
315    }
316}
317
318// TODO: Get this from a shared util
319pub fn parse_device_key(buf: &[u8]) -> Option<[u8; 32]> {
320    if buf.len() != 32 {
321        return None;
322    }
323
324    let mut key = [0u8; 32];
325    key.copy_from_slice(buf);
326    Some(key)
327}
328
329#[cfg(test)]
330mod tests {
331    use super::{parse, parse_device_key};
332    use crate::get;
333    use std::time::Duration;
334
335    #[test]
336    fn parse_basic() -> anyhow::Result<()> {
337        let response = "\
338            private_key=18aa10c05a531f5c537a18426b376387fc2cbd701ae1b9b4271e327aaade9d4f\n\
339            listen_port=56137\n\
340            public_key=913ea0e20e28c12b5c5f5a858b93a05e686dc3ce524e16f3143bbb1023679751\n\
341            preshared_key=0000000000000000000000000000000000000000000000000000000000000000\n\
342            protocol_version=1\n\
343            endpoint=192.168.64.73:51820\n\
344            last_handshake_time_sec=1590459201\n\
345            last_handshake_time_nsec=283546000\n\
346            tx_bytes=824\n\
347            rx_bytes=696\n\
348            persistent_keepalive_interval=110\n\
349            allowed_ip=10.24.24.3/32\n\
350            errno=0\n\
351            \n";
352        let expected = get::Device {
353            ifindex: 0,
354            ifname: "".to_string(),
355            private_key: parse_device_key(&base64::decode(
356                "GKoQwFpTH1xTehhCazdjh/wsvXAa4bm0Jx4yeqrenU8=",
357            )?),
358            public_key: None,
359            listen_port: 56137,
360            fwmark: 0,
361            peers: vec![get::Peer {
362                public_key: parse_device_key(&base64::decode(
363                    "kT6g4g4owStcX1qFi5OgXmhtw85SThbzFDu7ECNnl1E=",
364                )?)
365                .unwrap(),
366                preshared_key: [0u8; 32],
367                endpoint: Some("192.168.64.73:51820".parse()?),
368                last_handshake_time: Duration::new(1_590_459_201, 283_546_000),
369                tx_bytes: 824,
370                rx_bytes: 696,
371                persistent_keepalive_interval: 110,
372                allowed_ips: vec![get::AllowedIp {
373                    family: 2,
374                    ipaddr: "10.24.24.3".parse()?,
375                    cidr_mask: 32,
376                }],
377                protocol_version: 1,
378            }],
379        };
380
381        let actual = parse(response.lines().map(String::from).map(Ok))?;
382        assert_eq!(actual, expected);
383
384        Ok(())
385    }
386
387    #[test]
388    fn parse_device_with_no_peers() -> anyhow::Result<()> {
389        let response = "\
390            private_key=18aa10c05a531f5c537a18426b376387fc2cbd701ae1b9b4271e327aaade9d4f\n\
391            listen_port=56137\n\
392            errno=0\n\
393            \n";
394        let expected = get::Device {
395            ifindex: 0,
396            ifname: "".to_string(),
397            private_key: parse_device_key(&base64::decode(
398                "GKoQwFpTH1xTehhCazdjh/wsvXAa4bm0Jx4yeqrenU8=",
399            )?),
400            public_key: None,
401            listen_port: 56137,
402            fwmark: 0,
403            peers: vec![],
404        };
405
406        let actual = parse(response.lines().map(String::from).map(Ok))?;
407        assert_eq!(actual, expected);
408
409        Ok(())
410    }
411
412    #[test]
413    fn parse_website_example() -> anyhow::Result<()> {
414        // The scope ID of an endpoint had to be removed since Rust's default SocketAddrV6 parser
415        // didn't recognize it. We'll have to see if any existing crates can parse this or write
416        // something manually.
417        let response = "\
418            private_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a\n\
419            listen_port=12912\n\
420            public_key=b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33\n\
421            preshared_key=188515093e952f5f22e865cef3012e72f8b5f0b598ac0309d5dacce3b70fcf52\n\
422            allowed_ip=192.168.4.4/32\n\
423            endpoint=[abcd:23::33]:51820\n\
424            public_key=58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376\n\
425            tx_bytes=38333\n\
426            rx_bytes=2224\n\
427            allowed_ip=192.168.4.6/32\n\
428            persistent_keepalive_interval=111\n\
429            endpoint=182.122.22.19:3233\n\
430            public_key=662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58\n\
431            endpoint=5.152.198.39:51820\n\
432            allowed_ip=192.168.4.10/32\n\
433            allowed_ip=192.168.4.11/32\n\
434            tx_bytes=1212111\n\
435            rx_bytes=1929999999\n\
436            protocol_version=1\n\
437            errno=0\n\
438            \n";
439
440        let expected = get::Device {
441            ifindex: 0,
442            ifname: "".to_string(),
443            private_key: parse_device_key(&base64::decode(
444                "6EtabScXwQA6E7QxVwNT26ypFGzxUMX4V1aA/rpSAno=",
445            )?),
446            public_key: None,
447            listen_port: 12912,
448            fwmark: 0,
449            peers: vec![
450                get::Peer {
451                    public_key: parse_device_key(&base64::decode(
452                        "uFmW/sycfx/G0lcqdu2hHVm80gvo5UOxXOS9hajnWjM=",
453                    )?)
454                    .unwrap(),
455                    preshared_key: parse_device_key(&base64::decode(
456                        "GIUVCT6VL18i6GXO8wEucvi18LWYrAMJ1drM47cPz1I=",
457                    )?)
458                    .unwrap(),
459                    endpoint: Some("[abcd:23::33]:51820".parse()?),
460                    last_handshake_time: Duration::new(0, 0),
461                    tx_bytes: 0,
462                    rx_bytes: 0,
463                    persistent_keepalive_interval: 0,
464                    allowed_ips: vec![get::AllowedIp {
465                        family: 2,
466                        ipaddr: "192.168.4.4".parse()?,
467                        cidr_mask: 32,
468                    }],
469                    protocol_version: 1,
470                },
471                get::Peer {
472                    public_key: parse_device_key(&base64::decode(
473                        "WEAuaVuhdyscyTCXVfBDJR6nf9zxD75jmJzrfhkyE3Y=",
474                    )?)
475                    .unwrap(),
476                    preshared_key: [0u8; 32],
477                    endpoint: Some("182.122.22.19:3233".parse()?),
478                    last_handshake_time: Duration::new(0, 0),
479                    tx_bytes: 38333,
480                    rx_bytes: 2224,
481                    persistent_keepalive_interval: 111,
482                    allowed_ips: vec![get::AllowedIp {
483                        family: 2,
484                        ipaddr: "192.168.4.6".parse()?,
485                        cidr_mask: 32,
486                    }],
487                    protocol_version: 1,
488                },
489                get::Peer {
490                    public_key: parse_device_key(&base64::decode(
491                        "Zi4U/VlFVvUiYEcDNANRJYkDtk81VTdj8ZQmqypRXFg=",
492                    )?)
493                    .unwrap(),
494                    preshared_key: [0u8; 32],
495                    endpoint: Some("5.152.198.39:51820".parse()?),
496                    last_handshake_time: Duration::new(0, 0),
497                    tx_bytes: 1_212_111,
498                    rx_bytes: 1_929_999_999,
499                    persistent_keepalive_interval: 0,
500                    allowed_ips: vec![
501                        get::AllowedIp {
502                            family: 2,
503                            ipaddr: "192.168.4.10".parse()?,
504                            cidr_mask: 32,
505                        },
506                        get::AllowedIp {
507                            family: 2,
508                            ipaddr: "192.168.4.11".parse()?,
509                            cidr_mask: 32,
510                        },
511                    ],
512                    protocol_version: 1,
513                },
514            ],
515        };
516
517        let actual = parse(response.lines().map(String::from).map(Ok))?;
518        assert_eq!(actual, expected);
519
520        Ok(())
521    }
522}