use std::{
collections::HashMap,
fs::OpenOptions,
io::{self, Read},
path::Path,
};
use log::trace;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct SSSocks5AuthPasswordUserConfig {
user_name: String,
password: String,
}
#[derive(Deserialize, Debug)]
struct SSSocks5AuthPasswordConfig {
users: Vec<SSSocks5AuthPasswordUserConfig>,
}
#[derive(Deserialize, Debug)]
struct SSSocks5AuthConfig {
#[serde(skip_serializing_if = "Option::is_none")]
password: Option<SSSocks5AuthPasswordConfig>,
}
#[derive(Debug, Clone)]
pub struct Socks5AuthConfig {
pub passwd: Socks5AuthPasswdConfig,
}
impl Socks5AuthConfig {
pub fn new() -> Self {
Self {
passwd: Socks5AuthPasswdConfig::new(),
}
}
pub fn load_from_file<P: AsRef<Path> + ?Sized>(filename: &P) -> io::Result<Self> {
let filename = filename.as_ref();
trace!(
"loading socks5 authentication configuration from {}",
filename.display()
);
let mut reader = OpenOptions::new().read(true).open(filename)?;
let mut content = String::new();
reader.read_to_string(&mut content)?;
let jconf: SSSocks5AuthConfig = match json5::from_str(&content) {
Ok(c) => c,
Err(err) => return Err(io::Error::other(err)),
};
let mut passwd = Socks5AuthPasswdConfig::new();
if let Some(p) = jconf.password {
for user in p.users {
passwd.add_user(user.user_name, user.password);
}
}
Ok(Self { passwd })
}
pub fn auth_required(&self) -> bool {
self.passwd.total_users() > 0
}
}
impl Default for Socks5AuthConfig {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Socks5AuthPasswdConfig {
passwd: HashMap<String, String>,
}
impl Socks5AuthPasswdConfig {
pub fn new() -> Self {
Self { passwd: HashMap::new() }
}
pub fn add_user<U, P>(&mut self, user_name: U, password: P)
where
U: Into<String>,
P: Into<String>,
{
self.passwd.insert(user_name.into(), password.into());
}
pub fn check_user<U, P>(&self, user_name: U, password: P) -> bool
where
U: AsRef<str>,
P: AsRef<str>,
{
match self.passwd.get(user_name.as_ref()) {
Some(pwd) => pwd == password.as_ref(),
None => false,
}
}
pub fn total_users(&self) -> usize {
self.passwd.len()
}
}
impl Default for Socks5AuthPasswdConfig {
fn default() -> Self {
Self::new()
}
}