Start arg parsing.

Fortune for transfer's current commit: Blessing − 吉
basic
Avril 3 years ago
parent 192a7a6ed6
commit dca0c79c4a
Signed by: flanchan
GPG Key ID: 284488987C31F630

42
Cargo.lock generated

@ -472,6 +472,12 @@ version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "lazy_format"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "482170c31d2086032a851765674d916a49db14a808012ada930024cca23adf6b"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -800,6 +806,15 @@ version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.5"
@ -812,6 +827,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.130" version = "1.0.130"
@ -890,6 +920,16 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "stackalloc"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4f5c9dd3feb8a4adc8eae861e5f48862a92f9a9f38cf8fc99b92fc6ec016121"
dependencies = [
"cc",
"rustc_version",
]
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.4.1" version = "2.4.1"
@ -970,6 +1010,7 @@ dependencies = [
"cryptohelpers", "cryptohelpers",
"futures", "futures",
"getrandom 0.2.3", "getrandom 0.2.3",
"lazy_format",
"lazy_static", "lazy_static",
"log", "log",
"openssl", "openssl",
@ -978,6 +1019,7 @@ dependencies = [
"serde_cbor", "serde_cbor",
"serde_json", "serde_json",
"smallvec", "smallvec",
"stackalloc",
"tokio 1.12.0", "tokio 1.12.0",
] ]

@ -15,6 +15,7 @@ color-eyre = { version = "0.5.11", default-features = false }
cryptohelpers = { version = "1.8.2", features = ["sha256", "async", "rsa", "serde"] } cryptohelpers = { version = "1.8.2", features = ["sha256", "async", "rsa", "serde"] }
futures = "0.3.17" futures = "0.3.17"
getrandom = "0.2.3" getrandom = "0.2.3"
lazy_format = "1.9.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.14" log = "0.4.14"
openssl = "0.10.36" openssl = "0.10.36"
@ -23,4 +24,5 @@ serde = { version = "1.0.130", features = ["derive"] }
serde_cbor = "0.11.2" serde_cbor = "0.11.2"
serde_json = "1.0.68" serde_json = "1.0.68"
smallvec = { version = "1.6.1", features = ["serde", "const_generics", "write"] } smallvec = { version = "1.6.1", features = ["serde", "const_generics", "write"] }
stackalloc = "1.1.1"
tokio = { version = "1.12.0", features = ["full"] } tokio = { version = "1.12.0", features = ["full"] }

@ -41,15 +41,17 @@ impl fmt::Display for Usage
{ {
splash(f)?; splash(f)?;
writeln!(f, "Usage: {} --send <bind> [SEND OPTIONS] <file...>", program_name())?; writeln!(f, "Usage: {} S <bind> --send|--recv [OPTIONS] <file...>", program_name())?;
writeln!(f, "Usage: {} --recv <connect> [RECV OPTIONS] <output...>", program_name())?; writeln!(f, "Usage: {} C <connect> --send|--recv [OPTIONS] <output...>", program_name())?;
writeln!(f, "Usage: {} --help", program_name())?; writeln!(f, "Usage: {} --help", program_name())?;
writeln!(f, "\nNetworking mode:")?;
writeln!(f, " S: Server mode. Bind to an address/port")?;
writeln!(f, " C: Client mode. Connect to a listening address/port")?;
writeln!(f, "\nSEND OPTIONS:")?; writeln!(f, "\nSEND OPTIONS:")?;
writeln!(f, " -e\t\t\tEncrypt file(s)")?; writeln!(f, " -e\t\t\tEncrypt file(s)")?;
writeln!(f, " -c\t\t\tCompress files")?; writeln!(f, " -c\t\t\tCompress files")?;
writeln!(f, " --buffer-size <bytes>\tSize of file buffer")?; writeln!(f, " --buffer-size <bytes>\tSize of file buffer")?;
writeln!(f, " -a\t\t\tSend file names")?; writeln!(f, " -a\t\t\tSend file names")?;
writeln!(f, " -1\t\t\tExit after 1 client has been served")?;
writeln!(f, " -k\t\t\tSupport continuation of failed downloads")?; writeln!(f, " -k\t\t\tSupport continuation of failed downloads")?;
writeln!(f, "\nRECV OPTIONS:")?; writeln!(f, "\nRECV OPTIONS:")?;
@ -79,3 +81,5 @@ pub enum Op
Process(Box<Process>), Process(Box<Process>),
Help, Help,
} }
mod parse;

@ -0,0 +1,96 @@
//! Parsing args
use super::*;
use ext::*;
use std::iter;
/// Arg state
#[derive(Debug, Default)]
struct State
{
is_server: bool,
is_sending: bool,
enc: Option<bool>,
comp: Option<bool>,
bufsz: Option<usize>,
arc: Option<bool>,
contin: Option<bool>,
oneshot: Option<bool>,
inter: Option<bool>,
files: Vec<String>,
}
impl State
{
fn mode(&self) -> impl fmt::Display + 'static
{
let send = r#if!(self.is_sending, "send", "recv");
let serve = r#if!(self.is_server, "server", "client");
lazy_format!("{} ({})", send, serve)
}
}
fn parse_schain<I>(state: &mut State, single: I) -> eyre::Result<()>
where I: IntoIterator<Item=char>
{
for ch in single.into_iter().map(char::to_lowercase).flatten()
{
match ch {
'e' => state.enc = Some(true),
'c' => state.comp = Some(true),
'a' => state.arc = Some(true),
'k' => state.contin = Some(true),
'1' if state.is_server => state.oneshot = Some(true),
'i' if !state.is_sending => state.inter = Some(true),
x => return Err(eyre!("Unknown option for mode {}", state.mode()))
.with_section(move || x.header("Option was"))
.with_note(move || "Some options are only valid for certain modes"),
}
}
Ok(())
}
/// Try to parse an iterator of strings (usually the command-line arguments) into an `Op`.
pub fn parse_iter<I>(args: &mut I) -> eyre::Result<Op>
where I: Iterator<Item= String> + ?Sized
{
let state = parse_iter_raw(args)
.wrap_err(eyre!("Invalid arguments"))
.with_suggestion(|| "Try passing `--help`")?; // Send help message here, since it's unlikely to be helpful when returning from state's validation compared to here.
todo!("TODO: `impl TryFrom<State> for Op`, etc")
}
fn parse_iter_raw<I>(args: &mut I) -> eyre::Result<State>
where I: Iterator<Item= String> + ?Sized
{
let mut state = State::default();
//TODO: Parse modes before this.
while let Some(arg) = args.next()
{
let mut chars = arg.chars();
match (&mut chars).take(2).collect_array::<2>() {
['-', '-'] => {
// Long option
let opt = &arg[2..];
match opt {
"--" => break,
//TODO: Long options, pulling option param from `args` if needed, etc.
unknown => return Err(eyre!("Unknown option for mode {}", state.mode()))
.with_section(|| format!("--{}", unknown).header("Option was"))
.with_note(|| "Some options are only valid for certain modes"),
}
},
['-', n] => {
// Small option
parse_schain(&mut state, iter::once(n).chain(chars))?;
},
_ => {
// Not an option
state.files.push(arg);
},
}
}
Ok(state)
}

@ -1,5 +1,6 @@
//! Configuration //! Configuration
use super::*; use super::*;
use std::net::SocketAddr;
pub const DEFAULT_BUFFER_SIZE: usize = 4096; pub const DEFAULT_BUFFER_SIZE: usize = 4096;
@ -11,7 +12,7 @@ pub struct SendConfig
compress: bool, compress: bool,
buffer_size: usize, buffer_size: usize,
archive: bool, archive: bool,
oneshot: bool, //oneshot: bool, // Server specific
continuation: bool, continuation: bool,
} }
@ -25,7 +26,7 @@ impl Default for SendConfig
compress: false, compress: false,
buffer_size: DEFAULT_BUFFER_SIZE, buffer_size: DEFAULT_BUFFER_SIZE,
archive: false, archive: false,
oneshot: false, //oneshot: false,
continuation: false, continuation: false,
} }
} }
@ -51,11 +52,42 @@ impl Default for RecvConfig
} }
} }
/// Instructions for binding (server) mode
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Server
{
listen: SocketAddr, //TODO: Allow multiple?
}
/// Instructions for connecting (client) mode
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Client
{
connect: SocketAddr,
}
/// Program configuration /// A send or recv operation
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Config pub enum Operation
{ {
Send(SendConfig), Send(SendConfig),
Recv(RecvConfig), Recv(RecvConfig),
} }
/// Whether to serve (listen) or connect directly.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Mode
{
Server(Server),
Client(Client),
}
/// Program full configuration
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Config
{
/// Which operation (send/recv) are we performing?
pub op: Operation,
/// How are we performing it? (Bind/connect)
pub mode: Mode,
}

@ -20,6 +20,24 @@ use cryptohelpers::{
rsa, rsa,
}; };
mod ser; /// Size of buffer to use when copying a stream.
pub use ser::*; pub const STREAMING_BUFFER_SIZE: usize = 4096;
pub mod ser;
/// Copy `from` to `to`, transforming the data with the provided key and IV.
///
/// # Stream cipher usage
/// The copy is buffered by `STREAMING_BUFFER_SIZE` bytes, and the cipher applied to each read buffer.
/// If the buffer cannot be filled (because the stream reached EOF before filling it), then only the full portion of the buffer is transformed and written.
#[inline] pub async fn cc20_copy_stream<F, T, K>(from: &mut F, to: &mut T, keys: K, decrypt: bool) -> io::Result<(usize, usize)>
where K: key::CC20Key,
F: AsyncRead + Unpin + ?Sized,
T: AsyncWrite + Unpin + ?Sized
{
if decrypt {
ser::cha_copy::<F, T, STREAMING_BUFFER_SIZE, true>(from, to, keys.key(), keys.iv()).await
} else {
ser::cha_copy::<F, T, STREAMING_BUFFER_SIZE, false>(from, to, keys.key(), keys.iv()).await
}
}

@ -152,8 +152,84 @@ impl<I: Iterator<Item = u8> + Clone> fmt::Display for HexStringIter<I>
} }
} }
pub trait CollectArrayExt<T>: Sized
{
/// Collect an iterator into an array.
///
/// If the iterator has more elements than `N`, the rest are discarded.
///
/// # Panics
/// If the iterator has **less** elements than `N`.
fn collect_array<const N: usize>(self) -> [T; N];
}
impl<I> CollectArrayExt<I::Item> for I
where I: Iterator
{
fn collect_array<const N: usize>(self) -> [I::Item; N] {
use std::mem::MaybeUninit;
// SAFETY: This pattern is safe. The array elements are still maybeuninit.
let mut out = unsafe { MaybeUninit::<[MaybeUninit::<I::Item>; N]>::uninit().assume_init() };
let mut init_to = 0;
if N == 0 {
// SAFETY: This is valid, [I::Item; N] is 0 sized. (i uhh think...)
return unsafe { MaybeUninit::<[I::Item; N]>::uninit().assume_init() };
}
let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
#[cold]
#[inline(never)]
fn _panic_bad_size(exp: usize, got: usize) -> !
{
panic!("tried to collect into array of size {}, when iterator is only {} elements", exp, got)
}
init_to = out.iter_mut().zip(self)
.map(|(o, i)| *o = MaybeUninit::new(i)).count();
match init_to
{
n if n == N => (),
got => _panic_bad_size(N, got),
}
}));
match res {
Ok(()) => {
// SAFETY: Transmuting MaybeUninit<T> to T is fine.
// All elements are initialised by this point
unsafe {
#[inline(always)] unsafe fn assume_init_array<T, const N: usize>(array: [MaybeUninit<T>; N]) -> [T; N]
{
//std::intrinsics::assert_inhabited::<[T; N]>();
(&array as *const _ as *const [T; N]).read()
}
//MaybeUninit::array_assume_init(out)
assume_init_array(out)
}
},
Err(e) => {
// Drop all initialised elements before resuming unwind.
unsafe {
std::ptr::drop_in_place(&mut out[..init_to] as *mut [_]);
}
std::panic::resume_unwind(e)
},
}
}
}
#[macro_export] macro_rules! prog1 { #[macro_export] macro_rules! prog1 {
($first:expr, $($rest:expr);+ $(;)?) => { ($first:expr, $($rest:expr);+ $(;)?) => {
($first, $( $rest ),+).0 ($first, $( $rest ),+).0
} }
} }
#[macro_export] macro_rules! r#if {
($if:expr, $then:expr, $else:expr) => {
if $if { $then } else { $else }
}
}

@ -1,5 +1,4 @@
//! Key and IV structures for the cipher //! Key and IV structures for the cipher
use getrandom::getrandom; use getrandom::getrandom;
use std::{fmt, str}; use std::{fmt, str};
pub use crate::cha::{ pub use crate::cha::{
@ -8,6 +7,37 @@ pub use crate::cha::{
}; };
use crate::ext::*; use crate::ext::*;
/// A trait for objects that contain a key and IV.
pub trait CC20Key
{
fn key(&self) -> &Key;
fn iv(&self) -> &IV;
}
impl<'a, T: ?Sized> CC20Key for &'a T
where T: CC20Key {
#[inline] fn key(&self) -> &Key
{
T::key(self)
}
#[inline] fn iv(&self) -> &IV
{
T::iv(self)
}
}
impl<T, U> CC20Key for (T, U)
where T: AsRef<Key>, U: AsRef<IV>
{
fn key(&self) -> &Key
{
self.0.as_ref()
}
fn iv(&self) -> &IV
{
self.1.as_ref()
}
}
/// A 32 byte key for the chacha20_poly1305 cipher /// A 32 byte key for the chacha20_poly1305 cipher
/// ///
/// # Generation /// # Generation

@ -4,8 +4,11 @@
#[macro_use] extern crate log; #[macro_use] extern crate log;
#[macro_use] extern crate ad_hoc_iter; #[macro_use] extern crate ad_hoc_iter;
#[macro_use] extern crate lazy_static; #[macro_use] extern crate lazy_static;
//#[macro_use] extern crate lazy_format;
#[macro_use] extern crate serde; #[macro_use] extern crate serde;
use lazy_format::lazy_format;
use color_eyre::{ use color_eyre::{
eyre::{ eyre::{
self, self,

Loading…
Cancel
Save