generated from PlexSheep/rs-base
feat: c3 plus solution
This commit is contained in:
parent
58e6637a70
commit
f3129b1d50
|
@ -16,6 +16,7 @@ anyhow = "1.0.86"
|
|||
async-trait = "0.1.82"
|
||||
clap = { version = "4.5.17", features = ["derive"] }
|
||||
libpt = { version = "0.7.1", features = ["cli", "log"] }
|
||||
rand = "0.8.5"
|
||||
serde = { version = "1.0.209", features = ["derive"] }
|
||||
serde_json = "1.0.128"
|
||||
tokio = { version = "1.40.0", features = [
|
||||
|
|
|
@ -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())
|
|
@ -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}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use crate::vault::VaultRef;
|
|||
|
||||
pub mod c1;
|
||||
pub mod c2;
|
||||
pub mod c3;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Challenge
|
||||
|
@ -22,6 +23,7 @@ pub async fn select_and_start(index: u16, config: Config, vault: VaultRef) -> an
|
|||
match index {
|
||||
1 => c1::C1::new(config, vault).serve().await?,
|
||||
2 => c2::C2::new(config, vault).serve().await?,
|
||||
3 => c3::C3::new(config, vault).serve().await?,
|
||||
_ => {
|
||||
return Err(anyhow!(
|
||||
"no challenge with index {index} does currently exist"
|
||||
|
|
Loading…
Reference in New Issue