wireguard_uapi/xplatform/client/
unix.rs

1use crate::get;
2use crate::xplatform::error::GetDeviceError;
3use crate::xplatform::error::SetDeviceError;
4use crate::xplatform::parser::parse;
5use crate::xplatform::set;
6use std::io::BufRead;
7use std::io::Write;
8use std::os::unix::net::UnixStream;
9use std::path::Path;
10
11const GET_CMD: &str = "get=1\n\n";
12const SET_CMD: &str = "set=1\n";
13
14pub struct Client<P: AsRef<Path>> {
15    path: P,
16}
17
18impl<P: AsRef<Path>> Client<P> {
19    /// A path to the unix socket file. Ex: `/var/run/wireguard/utun0.sock`
20    pub fn create(path: P) -> Self {
21        Self { path }
22    }
23
24    pub fn get(&self) -> Result<get::Device, GetDeviceError> {
25        let mut stream = UnixStream::connect(&self.path)?;
26
27        stream.write_all(GET_CMD.as_bytes())?;
28
29        let reader = std::io::BufReader::new(stream);
30        let response_lines = reader.lines();
31
32        Ok(parse(response_lines)?)
33    }
34
35    pub fn set(&self, set_request: set::Device) -> Result<(), SetDeviceError> {
36        let mut stream = UnixStream::connect(&self.path)?;
37
38        stream.write_all(SET_CMD.as_bytes())?;
39        // TODO: This likely buffers the entire set_request in memory before
40        // sending it across the socket. We can probably do better.
41        stream.write_fmt(format_args!("{set_request}"))?;
42        stream.write_all(b"\n")?;
43
44        let reader = std::io::BufReader::new(stream);
45        let mut response_lines = reader.lines();
46
47        // The response for protocol_version=1 is expected to be a single
48        // "errno=N" line followed by an empty line.
49        let errno_line = response_lines
50            .next()
51            .ok_or(SetDeviceError::EmptyResponse)??;
52
53        let (raw_key, raw_value) = {
54            let mut tokens = errno_line.trim().splitn(2, '=');
55            let raw_key = tokens.next().unwrap();
56            let raw_value = match tokens.next() {
57                Some(val) => val,
58                None => return Err(SetDeviceError::InvalidResponse(errno_line)),
59            };
60
61            (raw_key, raw_value)
62        };
63
64        match (raw_key, raw_value) {
65            ("errno", "0") => {}
66            ("errno", val) => return Err(SetDeviceError::ServerError(val.to_string())),
67            (_, _) => return Err(SetDeviceError::InvalidResponse(errno_line)),
68        }
69
70        let empty_line = response_lines
71            .next()
72            .ok_or(SetDeviceError::EmptyResponse)??;
73        if !empty_line.is_empty() {
74            return Err(SetDeviceError::InvalidEndOfResponse(empty_line));
75        }
76
77        Ok(())
78    }
79}