From a244e714eac70c325318b0d887faa4ec0aaab392 Mon Sep 17 00:00:00 2001 From: Barry Pederson Date: Tue, 1 Mar 2022 09:07:29 -0800 Subject: [PATCH] Refactor Listener and Server into separate files. --- src/listener.rs | 54 +++++++++++++++++ src/main.rs | 153 ++---------------------------------------------- src/server.rs | 101 ++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 148 deletions(-) create mode 100644 src/listener.rs create mode 100644 src/server.rs diff --git a/src/listener.rs b/src/listener.rs new file mode 100644 index 0000000..851ab17 --- /dev/null +++ b/src/listener.rs @@ -0,0 +1,54 @@ +use std::fs::{metadata, set_permissions}; +use std::io::Error; +use std::os::unix::fs::PermissionsExt; +use std::os::unix::net::UnixListener; +use std::thread; + +use crate::options::Opt; +use crate::server::Server; + +pub struct Listener { + options: Opt, + listener: UnixListener, +} + +impl Listener { + pub fn new(options: Opt) -> Result { + if options.socket.exists() { + std::fs::remove_file(&options.socket)?; + } + + let listener = UnixListener::bind(&options.socket)?; + + let mut perms = metadata(&options.socket)?.permissions(); + perms.set_mode(0o0777); + set_permissions(&options.socket, perms)?; + + Ok(Listener { options, listener }) + } + + pub fn run(&mut self) -> Result<(), Error> { + // accept connections and process them, spawning a new thread for each one + for stream in self.listener.incoming() { + match stream { + Ok(stream) => { + /* connection succeeded */ + let options = self.options.clone(); + thread::spawn(|| Server::new(options, stream).unwrap().handle_client()); + } + Err(_err) => { + /* connection failed */ + break; + } + } + } + Ok(()) + } +} + +impl Drop for Listener { + fn drop(&mut self) { + let _result = std::fs::remove_file(&self.options.socket); + println!("Shutting down"); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 78bd9ab..03868bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,155 +1,12 @@ -use std::fs::{metadata, set_permissions, File}; -use std::io::{BufRead, BufReader, Error, ErrorKind, Read, Write}; -use std::net::Shutdown; -use std::os::unix::fs::PermissionsExt; -use std::os::unix::net::{UnixListener, UnixStream}; -use std::thread; +mod listener; +mod options; +mod server; + use structopt::StructOpt; +use crate::listener::Listener; use crate::options::Opt; -mod options; - -const RESPONSE_NO: [u8; 4] = [0x0, 0x2, b'N', b'O']; -const RESPONSE_OK: [u8; 4] = [0x0, 0x2, b'O', b'K']; - -struct Listener { - options: Opt, - listener: UnixListener, -} - -struct Server { - options: Opt, - stream: UnixStream, -} - -impl Listener { - pub fn new(options: Opt) -> Result { - if options.socket.exists() { - std::fs::remove_file(&options.socket)?; - } - - let listener = UnixListener::bind(&options.socket)?; - - let mut perms = metadata(&options.socket)?.permissions(); - perms.set_mode(0o0777); - set_permissions(&options.socket, perms)?; - - Ok(Listener { options, listener }) - } - - pub fn run(&mut self) -> Result<(), Error> { - // accept connections and process them, spawning a new thread for each one - for stream in self.listener.incoming() { - match stream { - Ok(stream) => { - /* connection succeeded */ - let options = self.options.clone(); - thread::spawn(|| Server::new(options, stream).unwrap().handle_client()); - } - Err(_err) => { - /* connection failed */ - break; - } - } - } - Ok(()) - } -} - -impl Drop for Listener { - fn drop(&mut self) { - let _result = std::fs::remove_file(&self.options.socket); - println!("Shutting down"); - } -} - -impl Server { - pub fn new(options: Opt, stream: UnixStream) -> Result { - Ok(Server { options, stream }) - } - - /// 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 check_auth(&mut self, userid: &str, password: &str) -> Result { - // Don't allow empty userids or passwords - // - if userid.is_empty() || password.is_empty() { - return Ok(false); - } - - let mut password_file = self.options.passwords.clone(); - password_file.push(&userid); - - let file = File::open(password_file)?; - - for line in BufReader::new(file).lines().flatten() { - if !line.starts_with('#') && line.trim() == password { - return Ok(true); - } - } - - Ok(false) - } - - 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()?; - - match self.check_auth(&userid, &password) { - Ok(true) => self.stream.write_all(&RESPONSE_OK)?, - Ok(false) => self.stream.write_all(&RESPONSE_NO)?, - Err(e) => { - self.stream.write_all(&RESPONSE_NO)?; - self.stream.shutdown(Shutdown::Both)?; - return Err(e); - } - } - - Ok(()) - } - - fn handle_client(&mut self) { - if let Err(error) = self.communicate() { - eprint!("{}", error); - } - } -} fn main() -> std::io::Result<()> { let mut server = Listener::new(Opt::from_args())?; diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..02d391c --- /dev/null +++ b/src/server.rs @@ -0,0 +1,101 @@ +use std::fs::File; +use std::io::{BufRead, BufReader, Error, ErrorKind, Read, Write}; +use std::net::Shutdown; +use std::os::unix::net::UnixStream; + +use crate::options::Opt; + +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 { + options: Opt, + stream: UnixStream, +} + +impl Server { + pub fn new(options: Opt, stream: UnixStream) -> Result { + Ok(Server { options, stream }) + } + + /// 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 check_auth(&mut self, userid: &str, password: &str) -> Result { + // Don't allow empty userids or passwords + // + if userid.is_empty() || password.is_empty() { + return Ok(false); + } + + let mut password_file = self.options.passwords.clone(); + password_file.push(&userid); + + let file = File::open(password_file)?; + + for line in BufReader::new(file).lines().flatten() { + if !line.starts_with('#') && line.trim() == password { + return Ok(true); + } + } + + Ok(false) + } + + 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()?; + + match self.check_auth(&userid, &password) { + Ok(true) => self.stream.write_all(&RESPONSE_OK)?, + Ok(false) => self.stream.write_all(&RESPONSE_NO)?, + Err(e) => { + self.stream.write_all(&RESPONSE_NO)?; + self.stream.shutdown(Shutdown::Both)?; + return Err(e); + } + } + + Ok(()) + } + + pub fn handle_client(&mut self) { + if let Err(error) = self.communicate() { + eprint!("{}", error); + } + } +}