Add admin program

This commit is contained in:
Denis Drakhnia 2023-10-17 15:16:28 +03:00
parent ce742f091d
commit 3cc3e5815e
6 changed files with 227 additions and 0 deletions

39
Cargo.lock generated
View File

@ -2,6 +2,16 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "admin"
version = "0.1.0"
dependencies = [
"blake2b_simd",
"getopts",
"thiserror",
"xash3d-protocol",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
@ -17,6 +27,18 @@ dependencies = [
"libc",
]
[[package]]
name = "arrayref"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "autocfg"
version = "1.1.0"
@ -29,6 +51,17 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "blake2b_simd"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "bumpalo"
version = "3.14.0"
@ -62,6 +95,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "core-foundation-sys"
version = "0.8.4"

View File

@ -3,4 +3,5 @@ resolver = "2"
members = [
"protocol",
"master",
"admin",
]

13
admin/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "admin"
version = "0.1.0"
license = "GPL-3.0-only"
authors = ["Denis Drakhnia <numas13@gmail.com>"]
edition = "2021"
rust-version = "1.56"
[dependencies]
thiserror = "1.0.49"
getopts = "0.2.21"
blake2b_simd = "<0.6"
xash3d-protocol = { path = "../protocol", version = "0.1.0" }

106
admin/src/cli.rs Normal file
View File

@ -0,0 +1,106 @@
// SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
use std::process;
use getopts::Options;
use xash3d_protocol::admin;
const BIN_NAME: &str = env!("CARGO_BIN_NAME");
const PKG_NAME: &str = env!("CARGO_PKG_NAME");
const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
const DEFAULT_HOST: &str = "mentality.rip";
const DEFAULT_PORT: u16 = 27010;
#[derive(Debug)]
pub struct Cli {
pub address: String,
pub command: String,
pub hash_len: usize,
pub hash_key: String,
pub hash_personal: String,
}
impl Default for Cli {
fn default() -> Cli {
Cli {
address: format!("{}:{}", DEFAULT_HOST, DEFAULT_PORT),
command: String::new(),
hash_len: admin::HASH_LEN,
hash_key: admin::HASH_KEY.to_owned(),
hash_personal: admin::HASH_PERSONAL.to_owned(),
}
}
}
fn print_usage(opts: Options) {
let brief = format!("Usage: {} [options]", BIN_NAME);
print!("{}", opts.usage(&brief));
}
fn print_version() {
println!("{} v{}", PKG_NAME, PKG_VERSION);
}
pub fn parse() -> Cli {
let mut cli = Cli::default();
let args: Vec<_> = std::env::args().collect();
let mut opts = Options::new();
opts.optflag("h", "help", "print usage help");
opts.optflag("v", "version", "print program version");
let help = format!("address to connect [default: {}]", cli.address);
opts.optopt("h", "host", &help, "ADDR");
let help = format!("hash length [default: {}]", cli.hash_len);
opts.optopt("l", "hash-length", &help, "N");
let help = format!("hash key [default: {}]", cli.hash_key);
opts.optopt("k", "hash-key", &help, "KEY");
let help = format!("hash personalization [default: {}]", cli.hash_personal);
opts.optopt("p", "hash-personal", &help, "PERSONAL");
let matches = match opts.parse(&args[1..]) {
Ok(m) => m,
Err(e) => {
eprintln!("{}", e);
process::exit(1);
}
};
if matches.opt_present("help") {
print_usage(opts);
process::exit(0);
}
if matches.opt_present("version") {
print_version();
process::exit(0);
}
if let Some(mut s) = matches.opt_str("host") {
if !s.contains(':') {
s.push_str(":27010");
}
cli.address = s;
}
match matches.opt_get("hash-length") {
Ok(Some(n)) => cli.hash_len = n,
Ok(None) => {}
Err(_) => {
eprintln!("Invalid key length");
process::exit(1);
}
}
if let Some(s) = matches.opt_str("hash-key") {
cli.hash_key = s;
}
if let Some(s) = matches.opt_str("hash-personal") {
cli.hash_personal = s;
}
cli.command = matches.free.join(" ");
cli
}

66
admin/src/main.rs Normal file
View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
mod cli;
use std::io;
use std::net::UdpSocket;
use blake2b_simd::Params;
use thiserror::Error;
use xash3d_protocol::{admin, master};
#[derive(Error, Debug)]
enum Error {
#[error("Unexpected response from master server")]
UnexpectedPacket,
#[error(transparent)]
Protocol(#[from] xash3d_protocol::Error),
#[error(transparent)]
Io(#[from] io::Error),
}
fn send_command(cli: &cli::Cli) -> Result<(), Error> {
let sock = UdpSocket::bind("0.0.0.0:0")?;
sock.connect(&cli.address)?;
let mut buf = [0; 512];
let n = admin::AdminChallenge.encode(&mut buf)?;
sock.send(&buf[..n])?;
let n = sock.recv(&mut buf)?;
let challenge = match master::Packet::decode(&buf[..n])? {
master::Packet::AdminChallengeResponse(p) => p.challenge,
_ => return Err(Error::UnexpectedPacket),
};
println!("Password:");
let mut password = String::new();
io::stdin().read_line(&mut password)?;
if password.ends_with('\n') {
password.pop();
}
let hash = Params::new()
.hash_length(cli.hash_len)
.key(cli.hash_key.as_bytes())
.personal(cli.hash_personal.as_bytes())
.to_state()
.update(password.as_bytes())
.update(&challenge.to_le_bytes())
.finalize();
let n = admin::AdminCommand::new(hash.as_bytes(), &cli.command).encode(&mut buf)?;
sock.send(&buf[..n])?;
Ok(())
}
fn main() {
let cli = cli::parse();
if let Err(e) = send_command(&cli) {
eprintln!("error: {}", e);
std::process::exit(1);
}
}

View File

@ -6,6 +6,8 @@ use crate::types::Str;
use crate::Error;
pub const HASH_LEN: usize = 64;
pub const HASH_KEY: &str = "Half-Life";
pub const HASH_PERSONAL: &str = "Freeman";
#[derive(Clone, Debug, PartialEq)]
pub struct AdminChallenge;