Reorganize and refactor, adding integration test for common library stuff
This commit is contained in:
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
mod options;
|
mod options;
|
||||||
|
|
||||||
use crate::options::OPTIONS;
|
use crate::options::OPTIONS;
|
||||||
use common::{read_string, Request};
|
use common::io::read_string;
|
||||||
|
use common::request::Request;
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
use std::io::{Error, ErrorKind, Read, Write};
|
||||||
|
|
||||||
|
/// The saslauthd protocol transmits strings as
|
||||||
|
/// 16-bit unsigned network-byte-order lengths followed
|
||||||
|
/// by the string itself.
|
||||||
|
///
|
||||||
|
pub fn read_string(stream: &mut impl Read) -> Result<String, Error> {
|
||||||
|
let mut length_buffer = [0u8, 0u8];
|
||||||
|
|
||||||
|
stream.read_exact(&mut length_buffer)?;
|
||||||
|
let length = u16::from_be_bytes(length_buffer);
|
||||||
|
|
||||||
|
let mut string_buffer = vec![0u8; length as usize];
|
||||||
|
|
||||||
|
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")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_string(stream: &mut impl Write, 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];
|
||||||
|
|
||||||
|
stream.write_all(&length_buffer)?;
|
||||||
|
stream.write_all(s.as_bytes())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
+2
-71
@@ -1,71 +1,2 @@
|
|||||||
use std::io::{Error, ErrorKind, Read, Write};
|
pub mod io;
|
||||||
|
pub mod request;
|
||||||
pub const RESPONSE_NO: [u8; 4] = [0x0, 0x2, b'N', b'O'];
|
|
||||||
pub const RESPONSE_OK: [u8; 4] = [0x0, 0x2, b'O', b'K'];
|
|
||||||
|
|
||||||
pub struct Request {
|
|
||||||
pub userid: String,
|
|
||||||
pub password: String,
|
|
||||||
pub service: String,
|
|
||||||
pub realm: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The saslauthd protocol transmits strings as
|
|
||||||
/// 16-bit unsigned network-byte-order lengths followed
|
|
||||||
/// by the string itself.
|
|
||||||
///
|
|
||||||
pub fn read_string(stream: &mut impl Read) -> Result<String, Error> {
|
|
||||||
let mut length_buffer = [0u8, 0u8];
|
|
||||||
|
|
||||||
stream.read_exact(&mut length_buffer)?;
|
|
||||||
let length = u16::from_be_bytes(length_buffer);
|
|
||||||
|
|
||||||
let mut string_buffer = vec![0u8; length as usize];
|
|
||||||
|
|
||||||
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(stream: &mut impl Write, 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];
|
|
||||||
|
|
||||||
stream.write_all(&length_buffer)?;
|
|
||||||
stream.write_all(s.as_bytes())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Request {
|
|
||||||
pub fn from_stream(stream: &mut impl Read) -> Result<Self, Error> {
|
|
||||||
let userid = read_string(stream)?;
|
|
||||||
let password = read_string(stream)?;
|
|
||||||
let service = read_string(stream)?;
|
|
||||||
let realm = read_string(stream)?;
|
|
||||||
|
|
||||||
Ok(Request {
|
|
||||||
userid,
|
|
||||||
password,
|
|
||||||
service,
|
|
||||||
realm,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_to_stream(&self, stream: &mut impl Write) -> Result<(), Error> {
|
|
||||||
write_string(stream, &self.userid)?;
|
|
||||||
write_string(stream, &self.password)?;
|
|
||||||
write_string(stream, &self.service)?;
|
|
||||||
write_string(stream, &self.realm)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
use crate::io::{read_string, write_string};
|
||||||
|
use std::io::{Error, Read, Write};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Request {
|
||||||
|
pub userid: String,
|
||||||
|
pub password: String,
|
||||||
|
pub service: String,
|
||||||
|
pub realm: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request {
|
||||||
|
pub fn new(userid: &str, password: &str, service: &str, realm: &str) -> Self {
|
||||||
|
Request {
|
||||||
|
userid: userid.to_string(),
|
||||||
|
password: password.to_string(),
|
||||||
|
service: service.to_string(),
|
||||||
|
realm: realm.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_stream(stream: &mut impl Read) -> Result<Self, Error> {
|
||||||
|
let userid = read_string(stream)?;
|
||||||
|
let password = read_string(stream)?;
|
||||||
|
let service = read_string(stream)?;
|
||||||
|
let realm = read_string(stream)?;
|
||||||
|
|
||||||
|
Ok(Request {
|
||||||
|
userid,
|
||||||
|
password,
|
||||||
|
service,
|
||||||
|
realm,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to_stream(&self, stream: &mut impl Write) -> Result<(), Error> {
|
||||||
|
write_string(stream, &self.userid)?;
|
||||||
|
write_string(stream, &self.password)?;
|
||||||
|
write_string(stream, &self.service)?;
|
||||||
|
write_string(stream, &self.realm)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
use crate::repository::PasswordDirectory;
|
use crate::repository::PasswordDirectory;
|
||||||
use common::{Request, RESPONSE_NO, RESPONSE_OK};
|
use common::request::Request;
|
||||||
use std::io::{Error, Write};
|
use std::io::{Error, Write};
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
|
|
||||||
|
pub const RESPONSE_NO: [u8; 4] = [0x0, 0x2, b'N', b'O'];
|
||||||
|
pub const RESPONSE_OK: [u8; 4] = [0x0, 0x2, b'O', b'K'];
|
||||||
|
|
||||||
pub struct Handler {
|
pub struct Handler {
|
||||||
stream: UnixStream,
|
stream: UnixStream,
|
||||||
handler: PasswordDirectory,
|
handler: PasswordDirectory,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler {
|
impl Handler {
|
||||||
pub fn new(stream: UnixStream, handler: PasswordDirectory) -> Result<Handler, Error> {
|
pub fn new(stream: UnixStream, handler: PasswordDirectory) -> Handler {
|
||||||
Ok(Handler { stream, handler })
|
Handler { stream, handler }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn communicate(&mut self) -> Result<(), Error> {
|
fn communicate(&mut self) -> Result<(), Error> {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use common::Request;
|
use common::request::Request;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -41,7 +41,7 @@ impl PasswordDirectory {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::repository::PasswordDirectory;
|
use crate::repository::PasswordDirectory;
|
||||||
use common::Request;
|
use common::request::Request;
|
||||||
|
|
||||||
const TEST_PATH: &str = "./test_passwords";
|
const TEST_PATH: &str = "./test_passwords";
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ use std::thread;
|
|||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
listener: UnixListener,
|
listener: UnixListener,
|
||||||
handler: PasswordDirectory,
|
repository: PasswordDirectory,
|
||||||
socket_name: PathBuf,
|
socket_name: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn new(socket_name: &str, handler: PasswordDirectory) -> Result<Server, Error> {
|
pub fn new(socket_name: &str, repository: PasswordDirectory) -> Result<Server, Error> {
|
||||||
let socket_name = PathBuf::from(socket_name);
|
let socket_name = PathBuf::from(socket_name);
|
||||||
|
|
||||||
if socket_name.exists() {
|
if socket_name.exists() {
|
||||||
@@ -28,7 +28,7 @@ impl Server {
|
|||||||
set_permissions(&socket_name, perms)?;
|
set_permissions(&socket_name, perms)?;
|
||||||
|
|
||||||
Ok(Server {
|
Ok(Server {
|
||||||
handler,
|
repository,
|
||||||
listener,
|
listener,
|
||||||
socket_name,
|
socket_name,
|
||||||
})
|
})
|
||||||
@@ -40,8 +40,8 @@ impl Server {
|
|||||||
match stream {
|
match stream {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
/* connection succeeded */
|
/* connection succeeded */
|
||||||
let handler = self.handler.clone();
|
let repository = self.repository.clone();
|
||||||
thread::spawn(|| Handler::new(stream, handler).unwrap().handle_client());
|
thread::spawn(|| Handler::new(stream, repository).handle_client());
|
||||||
}
|
}
|
||||||
Err(_err) => {
|
Err(_err) => {
|
||||||
/* connection failed */
|
/* connection failed */
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
use common::io::{read_string, write_string};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_reads_strings() {
|
||||||
|
let buffer = [0x0, 0x3, b'F', b'o', b'o'];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
read_string(&mut buffer.as_ref()).unwrap(),
|
||||||
|
"Foo".to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_writes_strings() {
|
||||||
|
let mut buffer = Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
write_string(&mut buffer, "Foo").unwrap();
|
||||||
|
|
||||||
|
// Should have written exactly 5 bytes
|
||||||
|
assert_eq!(buffer.get_ref().len(), 5usize);
|
||||||
|
|
||||||
|
assert_eq!(&buffer.get_ref()[0..5], [0x0, 0x3, b'F', b'o', b'o']);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
use common::request::Request;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
const SAMPLE: [u8; 26] = [
|
||||||
|
0x0, 0x3, b'F', b'o', b'o', 0x0, 0x4, b'B', b'a', b'z', b'0', 0x0, 0x5, b'x', b'x', b'x', b'y',
|
||||||
|
b'y', 0x0, 0x6, b'1', b'7', b'0', b'1', b'-', b'a',
|
||||||
|
];
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_reads_requests() {
|
||||||
|
let request = Request::from_stream(&mut Cursor::new(SAMPLE)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(request, Request::new("Foo", "Baz0", "xxxyy", "1701-a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_writes_requests() {
|
||||||
|
let request = Request::new("Foo", "Baz0", "xxxyy", "1701-a");
|
||||||
|
|
||||||
|
let mut buffer = Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
request.write_to_stream(&mut buffer).unwrap();
|
||||||
|
|
||||||
|
// Should have written exactly 5 bytes
|
||||||
|
assert_eq!(buffer.get_ref().len(), 26usize);
|
||||||
|
|
||||||
|
assert_eq!(&buffer.get_ref()[0..26], SAMPLE);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user