1use nix::unistd::getuid;
4use std::io::{Read, Write};
5use std::os::unix::net::UnixStream;
6
7fn write_message(msg: &str, stream: &mut UnixStream) -> std::io::Result<()> {
8 let mut buf = Vec::new();
9 buf.extend(msg.bytes());
10 buf.push(b'\r');
11 buf.push(b'\n');
12 stream.write_all(&buf)?;
13 Ok(())
14}
15
16fn has_line_ending(buf: &[u8]) -> bool {
17 for idx in 1..buf.len() {
18 if buf[idx - 1] == b'\r' && buf[idx] == b'\n' {
19 return true;
20 }
21 }
22 false
23}
24
25fn find_line_ending(buf: &[u8]) -> Option<usize> {
26 for idx in 1..buf.len() {
27 if buf[idx - 1] == b'\r' && buf[idx] == b'\n' {
28 return Some(idx - 1);
29 }
30 }
31 None
32}
33
34fn read_message(stream: &mut UnixStream, buf: &mut Vec<u8>) -> std::io::Result<String> {
35 let mut tmpbuf = [0u8; 512];
36 while !has_line_ending(buf) {
37 let bytes = stream.read(&mut tmpbuf[..])?;
38 buf.extend(&tmpbuf[..bytes])
39 }
40 let idx = find_line_ending(buf).unwrap();
41 let line = buf.drain(0..idx).collect::<Vec<_>>();
42 Ok(String::from_utf8(line).unwrap())
43}
44
45fn get_uid_as_hex() -> String {
46 let uid = getuid();
47 let mut tmp = uid.as_raw();
48 let mut numbers = Vec::new();
49 if tmp == 0 {
50 return "30".to_owned();
51 }
52 while tmp > 0 {
53 numbers.push(tmp % 10);
54 tmp /= 10;
55 }
56 let mut hex = String::new();
57 for idx in 0..numbers.len() {
58 hex.push_str(match numbers[numbers.len() - 1 - idx] {
59 0 => "30",
60 1 => "31",
61 2 => "32",
62 3 => "33",
63 4 => "34",
64 5 => "35",
65 6 => "36",
66 7 => "37",
67 8 => "38",
68 9 => "39",
69 _ => unreachable!(),
70 })
71 }
72
73 hex
74}
75
76pub enum AuthResult {
77 Ok,
78 Rejected,
79}
80
81pub fn do_auth(stream: &mut UnixStream) -> std::io::Result<AuthResult> {
82 stream.write_all(&[0])?;
84 write_message(&format!("AUTH EXTERNAL {}", get_uid_as_hex()), stream)?;
85
86 let mut read_buf = Vec::new();
87 let msg = read_message(stream, &mut read_buf)?;
88 if msg.starts_with("OK") {
89 Ok(AuthResult::Ok)
90 } else {
91 Ok(AuthResult::Rejected)
92 }
93}
94
95pub fn negotiate_unix_fds(stream: &mut UnixStream) -> std::io::Result<AuthResult> {
96 write_message("NEGOTIATE_UNIX_FD", stream)?;
97
98 let mut read_buf = Vec::new();
99 let msg = read_message(stream, &mut read_buf)?;
100 if msg.starts_with("AGREE_UNIX_FD") {
101 Ok(AuthResult::Ok)
102 } else {
103 Ok(AuthResult::Rejected)
104 }
105}
106
107pub fn send_begin(stream: &mut UnixStream) -> std::io::Result<()> {
108 write_message("BEGIN", stream)?;
109 Ok(())
110}