intial version
This commit is contained in:
126
src/lib.rs
Normal file
126
src/lib.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
pub mod prot {
|
||||
//! # Protocol
|
||||
//!
|
||||
//! The authentication protocol is dead simple for now, it is a client-server model in which
|
||||
//! both parts send/receive data in the same format.
|
||||
//!
|
||||
//! ## Communication
|
||||
//!
|
||||
//! It simply works by expecting a set of data, and if, anything is unexpected, the communication aborts.
|
||||
//!
|
||||
//! ### Data De/Serialization
|
||||
//!
|
||||
//! Integer primitives are sent as bytes in Big Endian order.
|
||||
//!
|
||||
//! Strings are sent in 2 steps, first it sends the byte lenth. The byte length is flexible and
|
||||
//! depends on the protocol, is sent first, then, that numbers of bytes is expected, and to be
|
||||
//! in utf-8 format.
|
||||
//!
|
||||
//! The byte lenth type associated still be annotated by `String<N>` where N is the data type
|
||||
//! that holds the length.
|
||||
//!
|
||||
//! ### Protocol
|
||||
//!
|
||||
//! The communication lacks versioning in the current state, as soon as the connection opens,
|
||||
//! the client sends the user as a `String<u8>` and the password as a `String<u8>`. Numbers are
|
||||
//! this small because higher users or passwords are rare or even impossible and this prevent
|
||||
//! memory allocation abuse.
|
||||
//!
|
||||
//! Then the server responds with a single [`u8`], the given value is then mapped to the enum
|
||||
//! [`ServerResponse`].
|
||||
|
||||
/// Represents the status after a login attempt
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum ServerResponse {
|
||||
/// Something went wrong in the server, e.g. the server failed pam client initialization
|
||||
ServerError = 0,
|
||||
|
||||
/// The user is locked due to too many failed locin attempts. One must not rely on these,
|
||||
/// it's common for lockers to not report the lock status and just report as a [`Failed`]
|
||||
/// state.
|
||||
///
|
||||
/// [`Failed`]: #variant.Failed
|
||||
Locked = 1,
|
||||
|
||||
/// The authentication likely went through but failed
|
||||
Failed = 2,
|
||||
|
||||
/// The authentication succeeded
|
||||
Succeeded = 3,
|
||||
}
|
||||
|
||||
impl ServerResponse {
|
||||
#[must_use]
|
||||
pub const fn as_u8(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn from_u8(b: u8) -> Option<Self> {
|
||||
match b {
|
||||
0 => Some(Self::ServerError),
|
||||
1 => Some(Self::Locked),
|
||||
2 => Some(Self::Failed),
|
||||
3 => Some(Self::Succeeded),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn attempt_login(
|
||||
mut stream: std::os::unix::net::UnixStream,
|
||||
user: &str,
|
||||
passwd: &str,
|
||||
) -> Option<ServerResponse> {
|
||||
ucom::write_str(&mut stream, user)?;
|
||||
ucom::write_str(&mut stream, passwd)?;
|
||||
|
||||
ServerResponse::from_u8(ucom::read_u8(&mut stream)?)
|
||||
}
|
||||
|
||||
pub mod ucom {
|
||||
//! # µcom
|
||||
//!
|
||||
//! Small module to serialize basic primitives in a very basic form.
|
||||
|
||||
use std::io::{Read, Write};
|
||||
|
||||
pub fn read_duple(from: &mut impl Read) -> Option<(String, String)> {
|
||||
let user = read_str(from)?;
|
||||
let pass = read_str(from)?;
|
||||
Some((user, pass))
|
||||
}
|
||||
|
||||
pub fn read_str(from: &mut impl Read) -> Option<String> {
|
||||
let size = read_u8(from)? as usize;
|
||||
let mut buf = vec![0; size];
|
||||
from.read_exact(&mut buf).ok()?;
|
||||
String::from_utf8(buf).ok()
|
||||
}
|
||||
|
||||
pub fn read_u8(from: &mut impl Read) -> Option<u8> {
|
||||
let mut size_buf = [0; 1];
|
||||
from.read_exact(&mut size_buf).ok()?;
|
||||
Some(u8::from_be_bytes(size_buf))
|
||||
}
|
||||
|
||||
pub fn write_duple(into: &mut impl Write, user: &str, pass: &str) -> Option<()> {
|
||||
write_str(into, user)?;
|
||||
write_str(into, pass)?;
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn write_str(into: &mut impl Write, str: &str) -> Option<()> {
|
||||
let buf = str.as_bytes();
|
||||
write_u8(into, buf.len().try_into().ok()?)?;
|
||||
into.write_all(buf).ok()
|
||||
}
|
||||
|
||||
pub fn write_u8(into: &mut impl Write, u8: u8) -> Option<()> {
|
||||
let size_buf = u8.to_be_bytes();
|
||||
into.write_all(&size_buf).ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user