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"
|
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 = [
|
||||||
|
|
|
@ -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 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"
|
||||||
|
|
Loading…
Reference in New Issue