feat(cfg): turn groupnames into gids at parse time
This commit is contained in:
7
assets/config.toml
Normal file
7
assets/config.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
# example file to show/test configuration
|
||||
|
||||
[unix]
|
||||
groups = [ "wheel", 0 ]
|
||||
|
||||
# [server]
|
||||
# listen = "127.0.0.1:8080"
|
||||
40
src/conf.rs
40
src/conf.rs
@@ -1,40 +1,40 @@
|
||||
//! Configuration file structure. (TODO, docs)
|
||||
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
fs::File,
|
||||
io::{self, Read, Seek},
|
||||
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
|
||||
};
|
||||
|
||||
use libc::gid_t;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Either<T, U> {
|
||||
Left(T),
|
||||
Right(U),
|
||||
}
|
||||
|
||||
impl<T: Debug, U: Debug> Debug for Either<T, U> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Left(l) => l.fmt(f),
|
||||
Self::Right(r) => r.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Group = Either<String, gid_t>;
|
||||
use crate::serdes::Group;
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Unix {
|
||||
groups: Vec<Group>,
|
||||
pub groups: Vec<Group>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Server {
|
||||
pub listen: SocketAddr,
|
||||
}
|
||||
impl Default for Server {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
listen: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8080)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Config {
|
||||
unix: Unix,
|
||||
pub unix: Unix,
|
||||
pub server: Server,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
||||
@@ -10,6 +10,7 @@ pub mod args;
|
||||
pub mod auth;
|
||||
pub mod conf;
|
||||
pub mod ext;
|
||||
pub mod serdes;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
|
||||
103
src/serdes/inner_helpers.rs
Normal file
103
src/serdes/inner_helpers.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use std::ffi::CString;
|
||||
|
||||
use libc::gid_t;
|
||||
use serde::{Deserialize, Deserializer, Serialize, de::Error};
|
||||
|
||||
/// Private module until its worth it to promote from size to a crate module.
|
||||
mod __libc_wrappers {
|
||||
use std::{ffi::CStr, mem::MaybeUninit};
|
||||
|
||||
use libc::c_char;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum GetgrnamError {
|
||||
#[error("no matching group record found")]
|
||||
NoMatchingRecord,
|
||||
/// ace variant for unexpected errors, error handling is shallow here
|
||||
#[error("unexpected getgrnam_r error")]
|
||||
Unexpected,
|
||||
}
|
||||
|
||||
pub fn get_group_by_name(cname: &CStr) -> Result<libc::gid_t, GetgrnamError> {
|
||||
use GetgrnamError as E;
|
||||
|
||||
let name = cname.as_ptr();
|
||||
let mut grp = MaybeUninit::uninit();
|
||||
let mut buf = [c_char::from(0); 1024];
|
||||
let mut result: MaybeUninit<*mut libc::group> = MaybeUninit::uninit();
|
||||
let ret = unsafe {
|
||||
libc::getgrnam_r(
|
||||
name,
|
||||
grp.as_mut_ptr(),
|
||||
buf.as_mut_ptr(),
|
||||
buf.len(),
|
||||
result.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
// SAFETY: Pointers inside `grp` point may point to the buffer `buf` so they cannot be
|
||||
// moved outside of this function.
|
||||
|
||||
// > On success, getgrnam_r() [..] return zero, and set `*result` to `grp`.
|
||||
//
|
||||
// > If no matching group record was found, these functions return 0 and store NULL in
|
||||
// `*result`.
|
||||
//
|
||||
// > In case of error, an error number is returned, and NULL stored in `*result`.
|
||||
//
|
||||
// SAFETY: Either way, `result` is initialized to a nullable pointer.
|
||||
let result_ptr = unsafe { result.assume_init() };
|
||||
if ret == 0 && result_ptr == grp.as_mut_ptr() {
|
||||
let grp_gid = unsafe { grp.assume_init() }.gr_gid;
|
||||
Ok(grp_gid)
|
||||
} else if ret == 0 && result_ptr.is_null() {
|
||||
Err(E::NoMatchingRecord)
|
||||
} else {
|
||||
// ret should be != 0 and result NULL, but any other case is also unexpected and I'm
|
||||
// not doing much FFI error handling rn
|
||||
Err(E::Unexpected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum EitherTyp {
|
||||
Gid(gid_t),
|
||||
Groupname(String),
|
||||
}
|
||||
|
||||
/// Deserialize either a gid number or a groupname into a [`gid_t`]
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
pub fn deserialize_group<'de, D>(d: D) -> Result<gid_t, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let either = EitherTyp::deserialize(d)?;
|
||||
|
||||
match either {
|
||||
EitherTyp::Gid(gid) => Ok(gid),
|
||||
EitherTyp::Groupname(groupname) => {
|
||||
use __libc_wrappers::GetgrnamError as E;
|
||||
use serde::de::Unexpected;
|
||||
|
||||
let cname = CString::new(groupname).map_err(|nul_err| {
|
||||
D::Error::invalid_value(
|
||||
Unexpected::Bytes(&nul_err.into_vec()),
|
||||
&"a string without null bytes",
|
||||
)
|
||||
})?;
|
||||
|
||||
let gid = __libc_wrappers::get_group_by_name(&cname).map_err(|err| match err {
|
||||
E::NoMatchingRecord => D::Error::invalid_value(
|
||||
Unexpected::Other("non-existent group"),
|
||||
&"existent group",
|
||||
),
|
||||
E::Unexpected => D::Error::custom("caught an unexpected getgrnam error"),
|
||||
})?;
|
||||
|
||||
Ok(gid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
src/serdes/mod.rs
Normal file
16
src/serdes/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use libc::gid_t;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod inner_helpers;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Group(#[serde(deserialize_with = "inner_helpers::deserialize_group")] pub gid_t);
|
||||
|
||||
impl Debug for Group {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user