use std::io::{Error, ErrorKind, Read, Write}; use std::os::unix::net::UnixStream; const RESPONSE_NO: [u8; 4] = [0x0, 0x2, b'N', b'O']; const RESPONSE_OK: [u8; 4] = [0x0, 0x2, b'O', b'K']; pub struct Server { stream: UnixStream, handler: &Box, } pub struct Request { userid: String, password: String, service: String, realm: String, } pub trait Handler { fn check_auth(&self, request: &Request) -> bool; } pub type CheckFn = fn(&Request) -> bool; impl Server { pub fn new(stream: UnixStream, handler: &Box) -> Result { Ok( Server { stream, handler, } ) } /// The saslauthd protocol transmits strings as /// 16-bit unsigned network-byte-order lengths followed /// by the string itself. /// fn read_string(&mut self) -> Result { let mut length_buffer = [0u8, 0u8]; self.stream.read_exact(&mut length_buffer)?; let length = u16::from_be_bytes(length_buffer); let mut string_buffer = vec![0u8; length as usize]; self.stream.read_exact(&mut string_buffer)?; match String::from_utf8(string_buffer) { Ok(str) => Ok(str), Err(_err) => Err(Error::new(ErrorKind::Other, "Invalid UTF-8 sent")), } } fn write_string(&mut self, s: &str) -> Result<(), Error> { let length = s.len(); if length > 0xffff { return Err(Error::new(ErrorKind::Other, "String too long to write")); } let length_buffer = [(length >> 8) as u8, (length & 0xff) as u8]; self.stream.write_all(&length_buffer)?; self.stream.write_all(s.as_bytes())?; Ok(()) } fn communicate(&mut self) -> Result<(), Error> { let userid = self.read_string()?; let password = self.read_string()?; let service = self.read_string()?; let realm = self.read_string()?; if userid.is_empty() || password.is_empty() { self.stream.write_all(&RESPONSE_NO)?; return Ok(()); } let request = Request { userid, password, service, realm, }; if self.check_auth(&request) { self.stream.write_all(&RESPONSE_OK)? }else { self.stream.write_all(&RESPONSE_NO)? } Ok(()) } pub fn handle_client(&mut self) { if let Err(error) = self.communicate() { eprint!("{error}"); } } }