add problem 08
This commit is contained in:
Generated
+225
@@ -0,0 +1,225 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "problem08"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.119"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "problem08"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
anyhow = "1"
|
||||
@@ -0,0 +1,242 @@
|
||||
use anyhow::{Context, Result, bail, ensure};
|
||||
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::{TcpListener, TcpStream, tcp::ReadHalf},
|
||||
};
|
||||
|
||||
const LOREM: &str = "Lorem ipsum dolor sit amet.";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let listener = TcpListener::bind("0.0.0.0:10000").await?;
|
||||
|
||||
loop {
|
||||
let (socket, _) = listener.accept().await?;
|
||||
tokio::spawn(async move {
|
||||
match handle_connection(socket).await {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
dbg!(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_connection(mut socket: TcpStream) -> Result<()> {
|
||||
let (mut reader, mut writer) = socket.split();
|
||||
|
||||
let cipher_spec = receive_cipher_spec(&mut reader).await?;
|
||||
|
||||
if is_no_op(&cipher_spec) {
|
||||
socket.shutdown().await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut client_stream_position = 0;
|
||||
let mut server_stream_position = 0;
|
||||
|
||||
let mut encoded_read_buffer = Vec::with_capacity(5_000);
|
||||
let mut decoded_read_buffer = String::with_capacity(5_000);
|
||||
|
||||
loop {
|
||||
read_to_buffer_and_decode(
|
||||
&mut reader,
|
||||
&mut encoded_read_buffer,
|
||||
&mut decoded_read_buffer,
|
||||
&mut client_stream_position,
|
||||
&cipher_spec,
|
||||
)
|
||||
.await?;
|
||||
|
||||
ensure!(
|
||||
decoded_read_buffer.is_ascii(),
|
||||
"error on decryption, non-ascii detected"
|
||||
);
|
||||
|
||||
'inner: loop {
|
||||
let reply = match generate_reply_and_encode(
|
||||
&mut decoded_read_buffer,
|
||||
&mut server_stream_position,
|
||||
&cipher_spec,
|
||||
) {
|
||||
Ok(reply) => reply,
|
||||
Err(_) => {
|
||||
break 'inner;
|
||||
}
|
||||
};
|
||||
writer.write_all(&reply).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_reply_and_encode(
|
||||
read_buffer: &mut String,
|
||||
server_stream_position: &mut usize,
|
||||
ciphers: &[Cipher],
|
||||
) -> Result<Vec<u8>> {
|
||||
let newline = match find_next_newline(read_buffer) {
|
||||
Some(idx) => idx,
|
||||
None => bail!("breaking inner"),
|
||||
};
|
||||
|
||||
let binding = read_buffer.drain(0..=newline);
|
||||
let this_line = binding.as_str();
|
||||
|
||||
let reply = generate_cleartext_reply(this_line)?;
|
||||
|
||||
let encoded = encode(&reply, server_stream_position, ciphers);
|
||||
|
||||
Ok(encoded)
|
||||
}
|
||||
|
||||
fn generate_cleartext_reply(request: &str) -> Result<String> {
|
||||
let toys = request.split(',');
|
||||
|
||||
let mut itemized_toys = vec![];
|
||||
|
||||
for toy in toys {
|
||||
let (amount, item) = toy.split_once('x').expect("no empty items");
|
||||
let amount: u32 = amount.parse().expect("only valid numbers");
|
||||
itemized_toys.push((amount, item));
|
||||
}
|
||||
|
||||
itemized_toys.sort_by_key(|(amount, _)| *amount);
|
||||
|
||||
let &(amount_requested, item_requested) =
|
||||
itemized_toys.last().context("empty requests are invalid")?;
|
||||
|
||||
let mut reply = String::new();
|
||||
reply.push_str(&amount_requested.to_string());
|
||||
reply.push_str("x ");
|
||||
reply.push_str(item_requested.trim());
|
||||
reply.push('\n');
|
||||
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
fn encode(reply: &str, server_stream_position: &mut usize, ciphers: &[Cipher]) -> Vec<u8> {
|
||||
let mut result = vec![];
|
||||
|
||||
for ch in reply.chars() {
|
||||
let mut b = ch as u8;
|
||||
for cipher in ciphers {
|
||||
b = cipher.encode_byte(b, *server_stream_position);
|
||||
}
|
||||
result.push(b);
|
||||
*server_stream_position += 1;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn decode(
|
||||
incoming_message: &mut Vec<u8>,
|
||||
decoded_read_buffer: &mut String,
|
||||
client_stream_position: &mut usize,
|
||||
ciphers: &[Cipher],
|
||||
) {
|
||||
for byte in incoming_message.iter() {
|
||||
let mut b = *byte;
|
||||
for cipher in ciphers.iter().rev() {
|
||||
b = cipher.decode_byte(b, *client_stream_position);
|
||||
}
|
||||
decoded_read_buffer.push(b as char);
|
||||
*client_stream_position += 1;
|
||||
}
|
||||
incoming_message.clear();
|
||||
}
|
||||
|
||||
fn find_next_newline(read_buffer: &str) -> Option<usize> {
|
||||
read_buffer.chars().position(|c| c == '\n')
|
||||
}
|
||||
|
||||
async fn read_to_buffer_and_decode(
|
||||
reader: &mut ReadHalf<'_>,
|
||||
encoded_read_buffer: &mut Vec<u8>,
|
||||
decoded_read_buffer: &mut String,
|
||||
client_stream_position: &mut usize,
|
||||
ciphers: &[Cipher],
|
||||
) -> Result<usize> {
|
||||
let mut read_amount = 0;
|
||||
|
||||
while !decoded_read_buffer.contains('\n') {
|
||||
read_amount += reader.read_buf(encoded_read_buffer).await?;
|
||||
if read_amount == 0 {
|
||||
bail!("client disconnected");
|
||||
}
|
||||
decode(
|
||||
encoded_read_buffer,
|
||||
decoded_read_buffer,
|
||||
client_stream_position,
|
||||
ciphers,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(read_amount)
|
||||
}
|
||||
|
||||
async fn receive_cipher_spec(reader: &mut ReadHalf<'_>) -> Result<Vec<Cipher>> {
|
||||
let mut cipher_spec = vec![];
|
||||
|
||||
loop {
|
||||
let byte = reader.read_u8().await?;
|
||||
|
||||
match byte {
|
||||
0x00 => {
|
||||
break;
|
||||
}
|
||||
0x01 => cipher_spec.push(Cipher::ReverseBits),
|
||||
0x02 => {
|
||||
let byte = reader.read_u8().await?;
|
||||
cipher_spec.push(Cipher::Xor(byte));
|
||||
}
|
||||
0x03 => cipher_spec.push(Cipher::XorPos),
|
||||
0x04 => {
|
||||
let byte = reader.read_u8().await?;
|
||||
cipher_spec.push(Cipher::Add(byte));
|
||||
}
|
||||
0x05 => cipher_spec.push(Cipher::AddPos),
|
||||
byte => bail!("illegal cipher request: {byte:x}"),
|
||||
}
|
||||
}
|
||||
Ok(cipher_spec)
|
||||
}
|
||||
|
||||
fn is_no_op(ciphers: &[Cipher]) -> bool {
|
||||
let clear = LOREM.as_bytes();
|
||||
let encoded = encode(LOREM, &mut 0, ciphers);
|
||||
clear == encoded
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Cipher {
|
||||
ReverseBits,
|
||||
Xor(u8),
|
||||
XorPos,
|
||||
Add(u8),
|
||||
AddPos,
|
||||
}
|
||||
|
||||
impl Cipher {
|
||||
fn decode_byte(&self, byte: u8, stream_position: usize) -> u8 {
|
||||
match self {
|
||||
Cipher::ReverseBits => byte.reverse_bits(),
|
||||
Cipher::Xor(bits) => byte ^ bits,
|
||||
Cipher::XorPos => byte ^ stream_position as u8,
|
||||
Cipher::Add(summand) => byte.wrapping_sub(*summand),
|
||||
Cipher::AddPos => byte.wrapping_sub(stream_position as u8),
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_byte(&self, byte: u8, stream_position: usize) -> u8 {
|
||||
match self {
|
||||
Cipher::ReverseBits => byte.reverse_bits(),
|
||||
Cipher::Xor(bits) => byte ^ bits,
|
||||
Cipher::XorPos => byte ^ stream_position as u8,
|
||||
Cipher::Add(summand) => byte.wrapping_add(*summand),
|
||||
Cipher::AddPos => byte.wrapping_add(stream_position as u8),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user