add problem 08

This commit is contained in:
timeshifter
2026-07-16 16:29:58 +02:00
parent e98de342ff
commit 0b4767e13a
3 changed files with 475 additions and 0 deletions
+242
View File
@@ -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),
}
}
}