feat: c3 plus solution

This commit is contained in:
Christoph J. Scherr 2024-09-06 19:55:16 +02:00
parent 58e6637a70
commit f3129b1d50
4 changed files with 259 additions and 0 deletions

View File

@ -16,6 +16,7 @@ anyhow = "1.0.86"
async-trait = "0.1.82" async-trait = "0.1.82"
clap = { version = "4.5.17", features = ["derive"] } clap = { version = "4.5.17", features = ["derive"] }
libpt = { version = "0.7.1", features = ["cli", "log"] } libpt = { version = "0.7.1", features = ["cli", "log"] }
rand = "0.8.5"
serde = { version = "1.0.209", features = ["derive"] } serde = { version = "1.0.209", features = ["derive"] }
serde_json = "1.0.128" serde_json = "1.0.128"
tokio = { version = "1.40.0", features = [ tokio = { version = "1.40.0", features = [

73
solutions/3.py Normal file
View File

@ -0,0 +1,73 @@
from io import TextIOWrapper
import socket
REMOTE = "127.0.0.1"
PORT = 1337
MAX_GUARD = 300
def calc(a: int, b: int, op: str) -> int:
match op:
case '+': return a+b
case '-': return a-b
case '*': return a*b
case '/': return int(a/b)
case _: raise Exception(f"bad op: {op}")
def main() -> int:
response: str = ""
s = socket.socket()
s.connect((REMOTE, PORT))
sf: TextIOWrapper = s.makefile('rw')
guard = 0
res = 0
while guard < MAX_GUARD:
guard += 1
response = recv(sf)
if "What" in response:
parts = response.strip().replace('?',"").split(' ')
print(f"# {parts}")
a = int(parts[2])
op = parts[3]
b = int(parts[4])
print(f"# {a} {op} {b}")
res: int = calc(a,b,op)
_ = send(sf, str(res))
elif "correct" in response:
print(f"# we got one right: {response}")
elif "wrong" in response:
print(f"! our answer was not accepted: '{response}'")
s.close()
return 2
elif "win" in response:
print(f"# We won: '{response}'")
break
else:
print(f"! unknown response: '{response}'")
s.close()
return 1
s.close()
return 0
def send(sf: TextIOWrapper, msg: str) -> int:
print(f"> {msg}")
sent: int = sf.write(f"{msg}\n")
sf.flush()
return sent
def recv(sf: TextIOWrapper) -> str:
response = sf.readline().strip()
print(f"< {response}")
return response
if __name__ == "__main__":
exit(main())

183
src/challenge/c3.rs Normal file
View File

@ -0,0 +1,183 @@
use std::fmt::Display;
use std::net::SocketAddr;
use anyhow::Result;
use async_trait::async_trait;
use libpt::log::{debug, info, warn};
use rand::distributions::{Distribution, Standard};
use rand::{random, Rng};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::{TcpListener, TcpStream};
use super::Challenge;
use crate::config::Config;
use crate::has_won;
use crate::vault::VaultRef;
const NEEDED_CORRECT: usize = 16;
#[derive(Copy, Clone)]
enum Operation {
Add,
Sub,
Div,
Mul,
}
impl Operation {
pub(crate) fn calc(self, a: i32, b: i32) -> i32 {
match self {
Self::Add => a.overflowing_add(b).0,
Self::Sub => a.overflowing_sub(b).0,
Self::Div => a.overflowing_div(b).0,
Self::Mul => a.overflowing_mul(b).0,
}
}
}
impl Display for Operation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Add => "+",
Self::Sub => "-",
Self::Mul => "*",
Self::Div => "/",
}
)
}
}
impl Distribution<Operation> for Standard {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Operation {
match rng.gen_range(0..3) {
0 => Operation::Add,
1 => Operation::Sub,
2 => Operation::Mul,
3 => Operation::Div,
_ => unreachable!(),
}
}
}
pub struct C3 {
config: Config,
vault: VaultRef,
}
impl C3 {
fn mk_question() -> (String, String) {
let mut rng = rand::thread_rng();
let a: i32 = rng.gen_range(0i32..i16::MAX as i32);
let b: i32 = rng.gen_range(0i32..i16::MAX as i32);
let op: Operation = random();
let res = op.calc(a, b);
let question = format!("What is {a} {op} {b}?");
let solution = format!("{}", res);
(question, solution)
}
async fn handler(peer: (TcpStream, SocketAddr), vault: &VaultRef) -> Result<()> {
let mut stream = peer.0;
let addr = peer.1;
let mut correct: usize = 0;
let mut buf: Vec<u8> = Vec::new();
let (stream_read, mut stream_write) = stream.split();
let mut rdbuf = BufReader::new(stream_read);
loop {
if correct >= NEEDED_CORRECT {
Self::win(vault, &mut stream, &addr).await?;
break;
}
let (question, solution) = Self::mk_question();
debug!("sending question to {addr}: '{question}'");
stream_write.write_all((question + "\n").as_bytes()).await?;
rdbuf.read_until(0x0A, &mut buf).await?; // read until a newline comes
let answer = String::from_utf8_lossy(&buf).trim().to_string();
if answer == solution {
correct += 1;
debug!("{addr} got one correct");
stream_write
.write_all(format!("correct! {correct} / {NEEDED_CORRECT}\n").as_bytes())
.await?;
} else {
debug!("{addr} screwed up, {answer} != {solution}");
stream_write
.write_all("wrong!\n".to_string().as_bytes())
.await?;
correct = 0;
}
buf.clear()
}
Ok(())
}
async fn win(
vault: &VaultRef,
stream: &mut TcpStream,
addr: &std::net::SocketAddr,
) -> Result<()> {
has_won(addr);
if let Err(e) = stream
.write_all(("You win: ".to_owned() + vault.secret()).as_bytes())
.await
{
warn!("could not write to peer {addr}: {e}");
return Err(e.into());
};
if let Err(e) = stream.shutdown().await {
warn!("could end connection to peer {addr}: {e}");
return Err(e.into());
};
Ok(())
}
}
#[async_trait]
impl Challenge for C3 {
fn new(config: Config, vault: VaultRef) -> Self {
info!("Solution: {}", Self::solution());
Self { config, vault }
}
fn hints() -> Vec<String> {
vec![
"TCP connect to 1337 and answer the questions".to_string(),
"The questions keep chaning".to_string(),
"You should try to solve the questions in an automated way".to_string(),
]
}
fn solution() -> String {
String::from("Connect by TCP, send 1337 as bytes (not text).")
}
async fn serve(self) -> anyhow::Result<()> {
info!("serving challenge 3");
let listener = TcpListener::bind(self.config.addr).await?;
loop {
let vault = self.vault.clone();
let peer = match listener.accept().await {
Ok(s) => s,
Err(err) => {
warn!("could not accept tcp stream: {err:?}");
continue;
}
};
info!("new peer: {}", peer.1);
tokio::spawn(async move {
if let Err(e) = Self::handler(peer, &vault).await {
warn!("error while handling peer: {e}");
}
});
}
}
}

View File

@ -6,6 +6,7 @@ use crate::vault::VaultRef;
pub mod c1; pub mod c1;
pub mod c2; pub mod c2;
pub mod c3;
#[async_trait] #[async_trait]
pub trait Challenge pub trait Challenge
@ -22,6 +23,7 @@ pub async fn select_and_start(index: u16, config: Config, vault: VaultRef) -> an
match index { match index {
1 => c1::C1::new(config, vault).serve().await?, 1 => c1::C1::new(config, vault).serve().await?,
2 => c2::C2::new(config, vault).serve().await?, 2 => c2::C2::new(config, vault).serve().await?,
3 => c3::C3::new(config, vault).serve().await?,
_ => { _ => {
return Err(anyhow!( return Err(anyhow!(
"no challenge with index {index} does currently exist" "no challenge with index {index} does currently exist"