use hex::encode;
use regex::Regex;
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult};
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::os::unix::net::UnixStream as StdUnixStream;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::UnixStream as TokioUnixStream;
async fn connect(path: &str) -> IoResult<TokioUnixStream> {
let mut stream = TokioUnixStream::connect(path).await?;
let zero: [u8; 1] = [0; 1];
stream.write_all(&zero[..]).await?;
let uid = unsafe { libc::getuid() };
let hex = encode(uid.to_string());
let cmd = "AUTH EXTERNAL ".to_owned() + &hex + "\r\n";
stream.write_all(&cmd.into_bytes()).await?;
let mut buf_reader = BufReader::new(&mut stream);
let mut resp = String::new();
buf_reader.read_line(&mut resp).await?;
if !resp.starts_with("OK ") {
return Err(IoError::new(IoErrorKind::Other, "Could not authenticate"));
}
let cmd = "NEGOTIATE_UNIX_FD\r\n";
stream.write_all(cmd.as_bytes()).await?;
resp.clear();
let mut buf_reader = BufReader::new(&mut stream);
buf_reader.read_line(&mut resp).await?;
if resp != "AGREE_UNIX_FD\r\n" {
return Err(IoError::new(IoErrorKind::Other, "Could not authenticate"));
}
stream.write_all(b"BEGIN\r\n").await?;
Ok(stream)
}
pub async fn get_unix_socket(path: &str) -> IoResult<TokioUnixStream> {
lazy_static! {
static ref UNIX_PATH_REGEX: Regex =
Regex::new("^unix:path=([^\\x00]+[^\\x00/])$").unwrap();
}
for p in path.split(';') {
if let Some(c) = UNIX_PATH_REGEX.captures(&p) {
if let Some(p) = c.get(1) {
match connect(p.as_str()).await {
Ok(stream) => return Ok(stream),
Err(e) => {
error!("Cannot connect to {}: {}", p.as_str(), e);
}
}
}
}
}
Err(IoError::new(
IoErrorKind::Other,
format!("Could not open any socket in: {}", path),
))
}
pub fn split(unix_stream: TokioUnixStream) -> IoResult<(TokioUnixStream, TokioUnixStream)> {
unsafe {
let fd = libc::dup(unix_stream.as_raw_fd());
let sink = StdUnixStream::from_raw_fd(fd);
let stream = unix_stream;
let sink = TokioUnixStream::from_std(sink)?;
Ok((stream, sink))
}
}