diff --git a/.gitignore b/.gitignore index cca6749..16f43ab 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ /ptth_server_build_BIHWLQXQ/ /scraper-secret.txt /target + +# TLS certs used for QUIC experiments +*.crt diff --git a/Cargo.lock b/Cargo.lock index 4acfd38..5dd14f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1201,6 +1201,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "ptth_quic_client_gui" +version = "0.1.0" +dependencies = [ + "anyhow", + "fltk", + "quic_demo", + "quinn", + "structopt", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "ptth_relay" version = "2.0.0" @@ -1313,7 +1327,6 @@ version = "0.1.0" dependencies = [ "anyhow", "base64", - "fltk", "futures-util", "quinn", "rcgen", diff --git a/Dockerfile b/Dockerfile index 70b6d7b..6ce3c98 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,8 @@ cargo new --bin crates/ptth_relay && \ cargo new --bin crates/ptth_server && \ cargo new --bin crates/ptth_file_server_bin && \ cargo new --bin tools/ptth_tail && \ -cargo new --bin crates/debug_proxy +cargo new --bin crates/debug_proxy && \ +cargo new --bin prototypes/quic_demo # copy over your manifests COPY ./Cargo.lock ./ @@ -27,6 +28,7 @@ COPY ./Cargo.toml ./ COPY ./crates/always_equal/Cargo.toml ./crates/always_equal/ COPY ./crates/ptth_core/Cargo.toml ./crates/ptth_core/ COPY ./crates/ptth_relay/Cargo.toml ./crates/ptth_relay/ +COPY ./prototypes/quic_demo/Cargo.toml ./prototypes/quic_demo/ # this build step will cache your dependencies RUN cargo build --release -p ptth_relay @@ -36,7 +38,8 @@ rm \ src/*.rs \ crates/always_equal/src/*.rs \ crates/ptth_core/src/*.rs \ -crates/ptth_relay/src/*.rs +crates/ptth_relay/src/*.rs \ +prototypes/quic_demo/src/*.rs # Copy source tree # Yes, I tried a few variations on the syntax. Dockerfiles are just rough. @@ -46,6 +49,7 @@ COPY ./crates/always_equal ./crates/always_equal COPY ./crates/ptth_core ./crates/ptth_core COPY ./crates/ptth_relay ./crates/ptth_relay COPY ./handlebars/ ./handlebars +COPY ./prototypes/quic_demo ./prototypes/quic_demo # Bug in cargo's incremental build logic, triggered by # Docker doing something funny with mtimes? Maybe? diff --git a/crates/ptth_relay/src/config.rs b/crates/ptth_relay/src/config.rs index 7536170..f28d172 100644 --- a/crates/ptth_relay/src/config.rs +++ b/crates/ptth_relay/src/config.rs @@ -15,7 +15,6 @@ use crate::{ errors::ConfigError, key_validity::{ ScraperKey, - Valid30Days, }, }; @@ -99,7 +98,6 @@ pub mod file { use crate::key_validity::{ BlakeHashWrapper, ScraperKey, - Valid30Days, }; #[derive (Clone, Debug, Deserialize, Serialize)] @@ -142,7 +140,7 @@ pub mod file { pub servers: Option >, // Adding a DB will take a while, so I'm moving these out of dev mode. - pub scraper_keys: Option >>, + pub scraper_keys: Option >, pub news_url: Option , } @@ -156,7 +154,7 @@ pub struct Config { pub address: IpAddr, pub port: Option , pub servers: HashMap , - pub scraper_keys: HashMap >, + pub scraper_keys: HashMap , pub news_url: Option , } diff --git a/crates/ptth_relay/src/key_validity.rs b/crates/ptth_relay/src/key_validity.rs index dbe1c23..67dc43a 100644 --- a/crates/ptth_relay/src/key_validity.rs +++ b/crates/ptth_relay/src/key_validity.rs @@ -78,36 +78,17 @@ impl Serialize for BlakeHashWrapper { } } -pub struct Valid7Days; -pub struct Valid30Days; -//pub struct Valid90Days; - pub trait MaxValidDuration { fn dur () -> Duration; } -impl MaxValidDuration for Valid7Days { - fn dur () -> Duration { - Duration::days (7) - } -} - -impl MaxValidDuration for Valid30Days { - fn dur () -> Duration { - Duration::days (30) - } -} - #[derive (Deserialize)] -pub struct ScraperKey { - name: String, +pub struct ScraperKey { + pub name: String, not_before: DateTime , not_after: DateTime , pub hash: BlakeHashWrapper, - - #[serde (default)] - _phantom: std::marker::PhantomData , } #[derive (Copy, Clone, Debug, PartialEq)] @@ -121,21 +102,20 @@ pub enum KeyValidity { DurationNegative, } -impl ScraperKey { +impl ScraperKey { pub fn new_30_day > (name: S, input: &[u8]) -> Self { let now = Utc::now (); Self { name: name.into (), not_before: now, - not_after: now + V::dur (), + not_after: now + Duration::days (30), hash: BlakeHashWrapper::from_key (input), - _phantom: Default::default (), } } } -impl ScraperKey { +impl ScraperKey { #[must_use] pub fn is_valid (&self, now: DateTime , input: &[u8]) -> KeyValidity { use KeyValidity::*; @@ -152,13 +132,6 @@ impl ScraperKey { return DurationNegative; } - let max_dur = V::dur (); - let actual_dur = self.not_after - self.not_before; - - if actual_dur > max_dur { - return DurationTooLong (max_dur); - } - if now >= self.not_after { return Expired; } @@ -196,12 +169,11 @@ mod tests { fn duration_negative () { let zero_time = Utc::now (); - let key = ScraperKey:: { + let key = ScraperKey { name: "automated testing".to_string (), not_before: zero_time + Duration::days (1 + 2), not_after: zero_time + Duration::days (1), hash: BlakeHashWrapper::from_key ("bad_password".as_bytes ()), - _phantom: Default::default (), }; let err = DurationNegative; @@ -215,46 +187,22 @@ mod tests { } } - #[test] - fn key_valid_too_long () { - let zero_time = Utc::now (); - - let key = ScraperKey:: { - name: "automated testing".to_string (), - not_before: zero_time + Duration::days (1), - not_after: zero_time + Duration::days (1 + 31), - hash: BlakeHashWrapper::from_key ("bad_password".as_bytes ()), - _phantom: Default::default (), - }; - - let err = DurationTooLong (Duration::days (30)); - - for (input, expected) in &[ - (zero_time + Duration::days (0), err), - (zero_time + Duration::days (2), err), - (zero_time + Duration::days (100), err), - ] { - assert_eq! (key.is_valid (*input, "bad_password".as_bytes ()), *expected); - } - } - #[test] fn normal_key () { let zero_time = Utc::now (); - let key = ScraperKey:: { + let key = ScraperKey { name: "automated testing".to_string (), not_before: zero_time + Duration::days (1), - not_after: zero_time + Duration::days (1 + 30), + not_after: zero_time + Duration::days (1 + 60), hash: BlakeHashWrapper::from_key ("bad_password".as_bytes ()), - _phantom: Default::default (), }; for (input, expected) in &[ (zero_time + Duration::days (0), ClockIsBehind), (zero_time + Duration::days (2), Valid), - (zero_time + Duration::days (29), Valid), - (zero_time + Duration::days (1 + 30), Expired), + (zero_time + Duration::days (60 - 1), Valid), + (zero_time + Duration::days (60 + 1), Expired), (zero_time + Duration::days (100), Expired), ] { assert_eq! (key.is_valid (*input, "bad_password".as_bytes ()), *expected); @@ -265,12 +213,11 @@ mod tests { fn wrong_key () { let zero_time = Utc::now (); - let key = ScraperKey:: { + let key = ScraperKey { name: "automated testing".to_string (), not_before: zero_time + Duration::days (1), not_after: zero_time + Duration::days (1 + 30), hash: BlakeHashWrapper::from_key ("bad_password".as_bytes ()), - _phantom: Default::default (), }; for input in &[ diff --git a/crates/ptth_relay/src/lib.rs b/crates/ptth_relay/src/lib.rs index e5e8a73..72c5f9d 100644 --- a/crates/ptth_relay/src/lib.rs +++ b/crates/ptth_relay/src/lib.rs @@ -127,8 +127,6 @@ async fn handle_http_request ( return Err (UnknownServer); } - let user = get_user_name (&req); - let req = http_serde::RequestParts::from_hyper (req.method, uri.clone (), req.headers) .map_err (|_| BadRequest)?; @@ -136,11 +134,6 @@ async fn handle_http_request ( let req_id = rusty_ulid::generate_ulid_string (); - state.audit_log.push (AuditEvent::new (AuditData::WebClientGet { - user, - server_name: server_name.to_string (), - uri, - })).await; trace! ("Created request {}", req_id); { @@ -610,6 +603,13 @@ async fn handle_all ( } => { let (parts, _) = req.into_parts (); + let user = get_user_name (&parts); + state.audit_log.push (AuditEvent::new (AuditData::WebClientGet { + user, + server_name: listen_code.to_string (), + uri: path.to_string (), + })).await; + handle_http_request (parts, path.to_string (), &state, listen_code).await? }, ClientServerList => handle_server_list (state, handlebars).await?, diff --git a/crates/ptth_relay/src/relay_state.rs b/crates/ptth_relay/src/relay_state.rs index c08cfbf..9f66ffa 100644 --- a/crates/ptth_relay/src/relay_state.rs +++ b/crates/ptth_relay/src/relay_state.rs @@ -121,6 +121,10 @@ pub enum AuditData { server: crate::config::file::Server, }, RelayStart, + ScraperGet { + key_name: String, + path: String, + }, WebClientGet { user: Option , server_name: String, @@ -312,7 +316,7 @@ impl Builder { self } - pub fn scraper_key (mut self, key: crate::key_validity::ScraperKey ) + pub fn scraper_key (mut self, key: crate::key_validity::ScraperKey) -> Self { self.config.scraper_keys.insert (key.hash.encode_base64 (), key); diff --git a/crates/ptth_relay/src/routing.rs b/crates/ptth_relay/src/routing.rs index 94c8572..89dda71 100644 --- a/crates/ptth_relay/src/routing.rs +++ b/crates/ptth_relay/src/routing.rs @@ -125,6 +125,7 @@ pub fn route_url <'a> (method: &Method, path: &'a str) -> Result , Er }) } else { + tracing::error! ("URL routing failed for `{}`", path); Err (Error::NotFound) } } diff --git a/crates/ptth_relay/src/scraper_api.rs b/crates/ptth_relay/src/scraper_api.rs index 67e0688..ddb2e93 100644 --- a/crates/ptth_relay/src/scraper_api.rs +++ b/crates/ptth_relay/src/scraper_api.rs @@ -127,6 +127,11 @@ async fn api_v1 ( ) -> Result , RequestError> { + use crate::{ + AuditData, + AuditEvent, + }; + let api_key = req.headers ().get ("X-ApiKey"); let api_key = match api_key { @@ -138,6 +143,8 @@ async fn api_v1 ( let bad_key = || error_reply (StatusCode::FORBIDDEN, strings::FORBIDDEN); + let key_name; + { let config = state.config.read ().await; @@ -160,8 +167,15 @@ async fn api_v1 ( return Ok (bad_key ()?); }, } + + key_name = expected_key.name.to_string (); } + state.audit_log.push (AuditEvent::new (AuditData::ScraperGet { + key_name, + path: path_rest.to_string (), + })).await; + if path_rest == "test" { Ok (error_reply (StatusCode::OK, "You're valid!")?) } @@ -224,7 +238,6 @@ mod tests { use tokio::runtime::Runtime; use crate::{ - config, key_validity, }; use super::*; diff --git a/docs/reference/scraper-keys.md b/docs/reference/scraper-keys.md new file mode 100644 index 0000000..8e2b155 --- /dev/null +++ b/docs/reference/scraper-keys.md @@ -0,0 +1,31 @@ +# How scraper keys work + +Come up with a random passphrase: + +`not this, this is a bogus passphrase for documentation` + +Run that through the `hash-api-key` subcommand of any `ptth_relay` instance: + +`ptth_relay hash-api-key` + +You'll get a hash like this: + +`RUWt1hQQuHIRjftOdgeZf0PG/DtAmIaMqot/nwBAZXQ=` + +Make sure that gets into the relay's config file, `ptth_relay.toml`: + +``` +[[scraper_keys]] +name = "shudder_mummy" +not_before = "2021-08-27T19:20:25-05:00" +not_after = "2031-08-27T19:20:25-05:00" +hash = "RUWt1hQQuHIRjftOdgeZf0PG/DtAmIaMqot/nwBAZXQ=" +``` + +Use curl to like, try it out: + +``` +curl \ +--header "X-ApiKey: not this, this is a bogus passphrase for documentation" \ +http://localhost:4000/scraper/v1/test +``` diff --git a/eff_short_wordlist_1.txt b/eff_short_wordlist_1.txt new file mode 100644 index 0000000..9dfc2ea --- /dev/null +++ b/eff_short_wordlist_1.txt @@ -0,0 +1,1253 @@ +acid +acorn +acre +acts +afar +affix +aged +agent +agile +aging +agony +ahead +aide +aids +aim +ajar +alarm +alias +alibi +alien +alike +alive +aloe +aloft +alone +amend +amino +ample +amuse +anger +angle +ankle +apple +april +apron +aqua +area +arena +argue +arise +armed +armor +army +aroma +array +arson +art +ashen +ashes +atlas +atom +attic +audio +avert +avoid +awake +award +awoke +axis +bacon +badge +bagel +baggy +baked +baker +balmy +banjo +barge +barn +bash +basil +bask +batch +bath +baton +bats +blade +blank +blast +blaze +bleak +blend +bless +blimp +blink +bloat +blob +blog +blot +blunt +blurt +blush +boast +boat +boil +bok +bolt +boney +bonus +bony +book +booth +boots +boss +botch +both +boxer +bribe +brick +bride +brim +bring +brink +brisk +broad +broil +broke +brook +broom +brush +bud +buggy +bulk +bully +bunch +bunny +bunt +busy +buzz +cable +cache +cadet +cage +cake +calm +cameo +canal +candy +cane +canon +cape +card +cargo +carol +carry +carve +case +cash +cause +cedar +chain +chair +chant +chaos +charm +chase +cheer +chef +chess +chest +chew +chief +chili +chill +chip +chomp +chop +chow +chuck +chump +chunk +churn +chute +cider +cinch +city +civic +civil +clad +claim +clamp +clap +clash +clasp +class +claw +clay +clean +clear +cleat +cleft +clerk +click +cling +clink +clip +cloak +clock +clone +cloth +cloud +clump +coach +coast +coat +cod +coil +coke +cola +cold +colt +coma +come +comic +comma +cone +cope +copy +coral +cork +cost +cot +couch +cough +cover +cozy +craft +cramp +crane +crank +crate +crave +crawl +crazy +creme +crepe +crept +crib +cried +crisp +crook +crop +cross +crowd +crown +crumb +crush +crust +cub +cult +cupid +cure +curl +curry +curse +curve +curvy +cushy +cut +cycle +dab +dad +daily +dairy +daisy +dance +darn +dart +dash +data +date +dawn +deaf +deal +dean +debit +debt +debug +decaf +decal +decay +deck +decor +decoy +deed +delay +denim +dense +dent +depth +derby +desk +dial +diary +dice +dig +dill +dime +dimly +diner +dingy +disco +dish +disk +ditch +ditzy +dizzy +dock +dodge +doing +doll +dome +donor +donut +dose +dot +dove +down +doze +drab +drama +drank +draw +dress +dried +drift +drill +drive +drone +droop +drove +drown +drum +dry +duck +duct +dude +dug +duke +duo +dusk +dust +duty +dwell +eagle +early +earth +easel +east +eaten +eats +ebay +ebony +ebook +echo +edge +eel +eject +elbow +elder +elf +elk +elm +elope +elude +elves +email +emit +empty +emu +enter +entry +envoy +equal +erase +error +erupt +essay +etch +evade +even +evict +evil +evoke +exact +exit +fable +faced +fact +fade +fall +false +fancy +fang +fax +feast +feed +femur +fence +fend +ferry +fetch +fever +fiber +fifth +fifty +film +filth +final +finch +fit +five +flag +flaky +flame +flap +flask +fled +flick +fling +flint +flip +flirt +float +flock +flop +floss +flyer +foam +foe +fog +foil +folk +food +fool +found +fox +foyer +frail +frame +fray +fresh +fried +frill +frisk +from +front +frost +froth +frown +froze +fruit +gag +gains +gala +game +gap +gave +gear +gecko +geek +gem +genre +gift +gig +gills +given +giver +glad +glass +glide +gloss +glove +glow +glue +goal +going +golf +gong +good +gooey +goofy +gore +gown +grab +grain +grant +grape +graph +grasp +grass +grave +gravy +gray +green +greet +grew +grid +grief +grill +grip +grit +growl +grub +grunt +guide +gulf +gulp +gummy +guru +gush +gut +guy +habit +half +halo +halt +happy +harm +hash +hasty +hatch +hate +haven +hazel +hazy +heap +heat +heave +hedge +hefty +help +herbs +hers +hub +hug +hula +hull +human +humid +hunk +hunt +hurry +hurt +hush +hut +ice +icing +icon +icy +igloo +image +ion +iron +islam +issue +item +ivory +ivy +jab +jam +jaws +jazz +jeep +jelly +jet +jiffy +job +jog +jolly +jolt +jot +joy +judge +juice +july +jumbo +jump +junky +juror +jury +keep +keg +kept +kick +kilt +king +kite +kitty +kiwi +knee +knelt +koala +ladle +lady +lair +lake +lance +land +lapel +large +lash +lasso +last +latch +late +lazy +left +legal +lemon +lend +lens +lent +level +lever +lid +life +lift +lilac +lily +limb +limes +line +lint +lion +lip +list +lived +liver +lunar +lunch +lung +lurch +lure +lurk +lying +lyric +mace +maker +malt +mango +manor +many +map +march +mardi +marry +mash +match +mate +math +mocha +mold +mom +moody +mop +morse +most +motor +motto +mount +mouse +mousy +mouth +move +movie +mower +mud +mug +mulch +mule +mull +mumbo +mummy +mural +muse +music +musky +mute +nacho +nail +name +nanny +nap +navy +near +neat +neon +nerd +nest +net +next +niece +ninth +nutty +oak +oasis +oat +ocean +oil +old +olive +omen +onion +only +ooze +opal +open +opera +opt +otter +ouch +ounce +outer +oval +oven +owl +ozone +pace +pager +palm +panda +panic +pants +panty +paper +park +party +pasta +patch +path +patio +payer +pecan +penny +pep +perch +perky +perm +pest +petal +petri +petty +photo +plank +plant +plaza +plead +plot +plow +pluck +plug +plus +poach +pod +poem +poet +pogo +point +poise +poker +polar +polio +polka +polo +pond +pony +poppy +poser +pout +power +prank +press +print +prior +prism +prize +prong +proof +props +prude +prune +pry +pug +pull +pulp +pulse +puma +punch +punk +pupil +puppy +purr +purse +push +putt +quack +quake +query +quiet +quill +quilt +quit +quota +quote +rabid +race +radar +radio +raft +rage +raid +rail +rake +rally +ramp +ranch +range +rank +rant +rash +raven +reach +react +ream +rebel +recap +relax +relay +relic +remix +repay +repel +reply +rerun +reset +rhyme +rice +rich +ride +rigid +rigor +rinse +riot +ripen +rise +risk +ritzy +rival +river +roast +robe +robin +rock +rogue +roman +romp +rope +rover +royal +ruby +rug +ruin +rule +runny +rush +rust +rut +sadly +sage +said +saint +salad +salon +salsa +salt +same +sandy +santa +satin +sauna +saved +savor +sax +say +scale +scan +scare +scarf +scary +scoff +scold +scoop +scoot +scope +score +scorn +scout +scowl +scrap +scrub +scuba +scuff +sect +sedan +self +send +sepia +serve +set +seven +shack +shade +shady +shaft +shaky +sham +shape +share +sharp +shed +sheep +sheet +shelf +shell +shine +shiny +ship +shirt +shock +shop +shore +shout +shove +shown +showy +shred +shrug +shun +shush +shut +shy +sift +silk +silly +silo +sip +siren +sixth +size +skate +skew +skid +skier +skies +skip +skirt +skit +sky +slab +slack +slain +slam +slang +slash +slate +slaw +sled +sleek +sleep +sleet +slept +slice +slick +slimy +sling +slip +slit +slob +slot +slug +slum +slush +small +smash +smell +smile +smirk +smog +snack +snap +snare +snarl +sneak +sneer +sniff +snore +snort +snout +snowy +snub +speak +speed +spend +spent +spew +spied +spill +spiny +spoil +spoke +spoof +spool +spoon +sport +spot +spout +spray +spree +spur +squad +squat +squid +stack +staff +stage +stain +stall +stamp +stand +stank +stark +start +stash +state +stays +steam +steep +stem +step +stew +stick +sting +stir +stock +stole +stomp +stony +stood +stool +stoop +stop +storm +stout +stove +straw +stray +strut +stuck +stud +stuff +stump +stung +stunt +suds +sugar +sulk +surf +sushi +swab +swan +swarm +sway +swear +sweat +sweep +swell +swept +swim +swing +swipe +swirl +swoop +swore +syrup +tacky +taco +tag +take +tall +talon +tamer +tank +taper +taps +tarot +tart +task +taste +tasty +taunt +thank +thaw +theft +theme +thing +think +thong +thorn +those +thud +thumb +thump +thus +tiara +tidal +tidy +tiger +tile +tilt +tint +tiny +trace +track +trade +train +trait +trap +trash +tray +treat +tree +trek +trend +trial +tribe +trick +trio +trout +truce +truck +trunk +try +tug +tulip +turf +tusk +tutor +tutu +tux +tweak +tweet +twice +twine +twins +twirl +twist +uncle +uncut +undo +unify +union +unit +untie +upon +upper +urban +used +user +usher +utter +value +vapor +vegan +venue +verse +vest +veto +vice +video +view +viral +virus +visa +visor +vocal +voice +void +volt +voter +vowel +wad +wafer +wager +wages +wagon +wake +walk +wand +wasp +watch +water +wavy +wheat +whiff +whole +whoop +wick +widen +widow +width +wife +wifi +wilt +wind +wing +wink +wipe +wired +wiry +wise +wish +wispy +wok +wolf +wool +woozy +word +work +worry +wound +woven +wrath +wreck +wrist +xerox +yahoo +yam +yard +year +yeast +yelp +yield +yo-yo +yodel +yoga +yoyo +zebra +zero +zesty +zippy +zone +zoom diff --git a/prototypes/ptth_quic_client_gui/Cargo.toml b/prototypes/ptth_quic_client_gui/Cargo.toml new file mode 100644 index 0000000..7281233 --- /dev/null +++ b/prototypes/ptth_quic_client_gui/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ptth_quic_client_gui" +version = "0.1.0" +authors = ["Trish"] +edition = "2018" +license = "AGPL-3.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.38" +fltk = "1.1.1" +quic_demo = { path = "../quic_demo" } +quinn = "0.7.2" +structopt = "0.3.20" +tokio = { version = "1.8.1", features = ["full"] } +tracing-subscriber = "0.2.16" +tracing = "0.1.25" diff --git a/prototypes/quic_demo/src/bin/client_gui.rs b/prototypes/ptth_quic_client_gui/src/main.rs similarity index 59% rename from prototypes/quic_demo/src/bin/client_gui.rs rename to prototypes/ptth_quic_client_gui/src/main.rs index 1273420..4cfdfda 100644 --- a/prototypes/quic_demo/src/bin/client_gui.rs +++ b/prototypes/ptth_quic_client_gui/src/main.rs @@ -10,10 +10,12 @@ use fltk::{ window::Window }; use structopt::StructOpt; -use tokio::net::TcpListener; -use quic_demo::prelude::*; -use protocol::PeerId; +use quic_demo::{ + client_proxy::*, + prelude::*, + protocol::PeerId, +}; #[derive (Debug, StructOpt)] struct Opt { @@ -122,19 +124,13 @@ fn main () -> anyhow::Result <()> { Some (Message::OpenPort (port_idx)) => { if let Ok (params) = gui_ports [port_idx].get_params () { let connection_p2_p3 = connection_p2_p3.clone (); - let (shutdown_flag, shutdown_flag_rx) = tokio::sync::watch::channel (true); - let task = rt.spawn (forward_port ( + let _guard = rt.enter (); + forwarding_instances [port_idx].replace (ForwardingInstance::new ( connection_p2_p3, params, - shutdown_flag_rx )); - forwarding_instances [port_idx].replace (ForwardingInstance { - task, - shutdown_flag, - }); - gui_ports [port_idx].set_forwarding (true); frame_status.set_label ("Forwarding 1 port"); @@ -142,11 +138,7 @@ fn main () -> anyhow::Result <()> { }, Some (Message::ClosePort (port_idx)) => { if let Some (old_instance) = forwarding_instances [port_idx].take () { - rt.block_on (async { - old_instance.shutdown_flag.send (false)?; - old_instance.task.await??; - Ok::<_, anyhow::Error> (()) - })?; + rt.block_on (old_instance.close ())?; } gui_ports [port_idx].set_forwarding (false); @@ -168,125 +160,6 @@ fn set_active (w: &mut W, b: bool) { } } -struct ForwardingInstance { - task: tokio::task::JoinHandle >, - shutdown_flag: tokio::sync::watch::Sender , -} - -async fn forward_port ( - connection_p2_p3: quinn::Connection, - params: ForwardingParams, - shutdown_flag_rx: tokio::sync::watch::Receiver , -) -> anyhow::Result <()> -{ - let ForwardingParams { - client_tcp_port, - server_id, - server_tcp_port, - } = params; - - let listener = TcpListener::bind (("127.0.0.1", client_tcp_port)).await?; - - trace! ("Accepting local TCP connections from P1 on {}", client_tcp_port); - - while *shutdown_flag_rx.borrow () { - let mut shutdown_flag_rx_2 = shutdown_flag_rx.clone (); - - tokio::select! { - x = listener.accept () => { - let (tcp_socket, _) = x?; - let connection = connection_p2_p3.clone (); - let server_id = server_id.clone (); - let shutdown_flag_rx = shutdown_flag_rx.clone (); - - tokio::spawn (handle_p1 (connection, server_id, server_tcp_port, tcp_socket, shutdown_flag_rx)); - }, - _ = shutdown_flag_rx_2.changed () => (), - }; - } - - Ok::<_, anyhow::Error> (()) -} - -async fn handle_p1 ( - connection: quinn::Connection, - server_id: String, - server_tcp_port: u16, - tcp_socket: tokio::net::TcpStream, - shutdown_flag_rx: tokio::sync::watch::Receiver , -) -> anyhow::Result <()> -{ - let (mut local_recv, mut local_send) = tcp_socket.into_split (); - - debug! ("Starting PTTH connection"); - - let (mut relay_send, mut relay_recv) = protocol::p2_connect_to_p5 (&connection, &server_id, server_tcp_port).await?; - - trace! ("Relaying bytes..."); - - let task_blue = { - let mut shutdown_flag_rx = shutdown_flag_rx.clone (); - - tokio::spawn (async move { - let mut buf = vec! [0u8; 65_536]; - while *shutdown_flag_rx.borrow () { - trace! ("Blue reading from QUIC..."); - tokio::select! { - x = relay_recv.read (&mut buf) => { - let bytes_read = match x? { - None => break, - Some (0) => break, - Some (x) => x, - }; - let buf_slice = &buf [0..bytes_read]; - trace! ("Uplink relaying {} bytes", bytes_read); - local_send.write_all (buf_slice).await?; - }, - _ = shutdown_flag_rx.changed () => (), - }; - } - - debug! ("Blue QUIC --> TCP closed"); - - Ok::<_, anyhow::Error> (()) - }) - }; - - let task_green = { - let mut shutdown_flag_rx = shutdown_flag_rx.clone (); - - tokio::spawn (async move { - let mut buf = vec! [0u8; 65_536]; - while *shutdown_flag_rx.borrow () { - trace! ("Green reading from TCP..."); - tokio::select! { - x = local_recv.read (&mut buf) => { - let bytes_read = match x? { - 0 => break, - x => x, - }; - let buf_slice = &buf [0..bytes_read]; - trace! ("Downlink relaying {} bytes", bytes_read); - relay_send.write_all (buf_slice).await?; - }, - _ = shutdown_flag_rx.changed () => (), - }; - } - - debug! ("Green TCP --> QUIC closed"); - - Ok::<_, anyhow::Error> (()) - }) - }; - - task_blue.await??; - task_green.await??; - - debug! ("Ended PTTH connection"); - - Ok (()) -} - struct GuiPort { input_client_port: Input, input_server_id: Input, @@ -295,12 +168,6 @@ struct GuiPort { but_close: Button, } -struct ForwardingParams { - client_tcp_port: u16, - server_id: String, - server_tcp_port: u16, -} - impl GuiPort { fn new (fltk_tx: fltk::app::Sender , x: &mut i32, y: i32, port_idx: usize) -> Self { let margin = 10; diff --git a/prototypes/quic_demo/.gitignore b/prototypes/quic_demo/.gitignore index 1892ca7..69734c1 100644 --- a/prototypes/quic_demo/.gitignore +++ b/prototypes/quic_demo/.gitignore @@ -1,4 +1 @@ -# TLS certs used for QUIC experiments -*.crt - /app_packages diff --git a/prototypes/quic_demo/TODO.md b/prototypes/quic_demo/TODO.md new file mode 100644 index 0000000..8d86232 --- /dev/null +++ b/prototypes/quic_demo/TODO.md @@ -0,0 +1,8 @@ +# Top 10 TODO items by priority + +- Allow lazy P2 --> P3 connections +- Allow multiple relays in P2 GUI +- Integrate relay server into `ptth_relay` +- Integrate server proxy into `ptth_server` +- Auth for client proxies +- Auth for server proxies diff --git a/prototypes/quic_demo/sky-pie.md b/prototypes/quic_demo/sky-pie.md new file mode 100644 index 0000000..2b4dbc5 --- /dev/null +++ b/prototypes/quic_demo/sky-pie.md @@ -0,0 +1,15 @@ +# Pie in the sky ideas + +These aren't good enough for the main TODO list, but I think they would be +cool. Most of them are a combo of "Too much work" and "Too little demand". +They're not weekend projects with obvious payoff, they're month-long projects +with a high risk of failure. + +- Custom VNC protocol that uses datagrams (Would need to forward datagrams +within PTTH too) +- Cross-platform pull-style backups (Doing backups well is its own field of +study. But I think convenient pull backups are worth it, even if they're +not as perfect as Borg) +- Remote shell for Windows (Just admitting that PTTH is basically malware +minus the mal) +- Generic file send / receive with a decent GUI (This is actually feasible) diff --git a/prototypes/quic_demo/src/bin/quic_demo_client.rs b/prototypes/quic_demo/src/bin/quic_demo_client.rs index 1581952..6dbb1f6 100644 --- a/prototypes/quic_demo/src/bin/quic_demo_client.rs +++ b/prototypes/quic_demo/src/bin/quic_demo_client.rs @@ -28,7 +28,7 @@ async fn main () -> anyhow::Result <()> { let relay_addr = opt.relay_addr.unwrap_or_else (|| String::from ("127.0.0.1:30380")).parse ()?; let endpoint = make_client_endpoint ("0.0.0.0:0".parse ()?, &[&server_cert])?; - trace! ("Connecting to relay server"); + debug! ("Connecting to relay server"); let client_id = opt.client_id.unwrap_or_else (|| "bogus_client".to_string ()); @@ -46,7 +46,7 @@ async fn main () -> anyhow::Result <()> { let server_tcp_port = opt.server_tcp_port.unwrap_or (30382); let listener = TcpListener::bind (("127.0.0.1", client_tcp_port)).await?; - trace! ("Accepting local TCP connections from P1"); + debug! ("Accepting local TCP connections from P1"); // End of per-port stuff // Beginning of per-connection stuff diff --git a/prototypes/quic_demo/src/client_proxy.rs b/prototypes/quic_demo/src/client_proxy.rs new file mode 100644 index 0000000..0701ffc --- /dev/null +++ b/prototypes/quic_demo/src/client_proxy.rs @@ -0,0 +1,159 @@ +use tokio::{ + net::TcpListener, + sync::watch, + task::JoinHandle, +}; + +use crate::prelude::*; + +pub struct ForwardingInstance { + task: JoinHandle >, + shutdown_flag: watch::Sender , +} + +impl ForwardingInstance { + pub fn new ( + connection_p2_p3: quinn::Connection, + params: ForwardingParams, + ) -> Self + { + let (shutdown_flag, shutdown_flag_rx) = tokio::sync::watch::channel (true); + + let task = tokio::spawn (forward_port ( + connection_p2_p3, + params, + shutdown_flag_rx + )); + + Self { + task, + shutdown_flag, + } + } + + pub async fn close (self) -> anyhow::Result <()> { + self.shutdown_flag.send (false)?; + self.task.await??; + Ok (()) + } +} + +pub struct ForwardingParams { + pub client_tcp_port: u16, + pub server_id: String, + pub server_tcp_port: u16, +} + +async fn forward_port ( + connection_p2_p3: quinn::Connection, + params: ForwardingParams, + shutdown_flag_rx: tokio::sync::watch::Receiver , +) -> anyhow::Result <()> +{ + let ForwardingParams { + client_tcp_port, + server_id, + server_tcp_port, + } = params; + + let listener = TcpListener::bind (("127.0.0.1", client_tcp_port)).await?; + + trace! ("Accepting local TCP connections from P1 on {}", client_tcp_port); + + while *shutdown_flag_rx.borrow () { + let mut shutdown_flag_rx_2 = shutdown_flag_rx.clone (); + + tokio::select! { + x = listener.accept () => { + let (tcp_socket, _) = x?; + let connection = connection_p2_p3.clone (); + let server_id = server_id.clone (); + let shutdown_flag_rx = shutdown_flag_rx.clone (); + + tokio::spawn (handle_p1 (connection, server_id, server_tcp_port, tcp_socket, shutdown_flag_rx)); + }, + _ = shutdown_flag_rx_2.changed () => (), + }; + } + + Ok::<_, anyhow::Error> (()) +} + +async fn handle_p1 ( + connection: quinn::Connection, + server_id: String, + server_tcp_port: u16, + tcp_socket: tokio::net::TcpStream, + shutdown_flag_rx: tokio::sync::watch::Receiver , +) -> anyhow::Result <()> +{ + let (mut local_recv, mut local_send) = tcp_socket.into_split (); + + debug! ("Starting PTTH connection"); + + let (mut relay_send, mut relay_recv) = protocol::p2_connect_to_p5 (&connection, &server_id, server_tcp_port).await?; + + trace! ("Relaying bytes..."); + + let task_blue = { + let mut shutdown_flag_rx = shutdown_flag_rx.clone (); + + tokio::spawn (async move { + let mut buf = vec! [0u8; 65_536]; + while *shutdown_flag_rx.borrow () { + trace! ("Blue reading from QUIC..."); + tokio::select! { + x = relay_recv.read (&mut buf) => { + let bytes_read = match x? { + None => break, + Some (0) => break, + Some (x) => x, + }; + let buf_slice = &buf [0..bytes_read]; + trace! ("Uplink relaying {} bytes", bytes_read); + local_send.write_all (buf_slice).await?; + }, + _ = shutdown_flag_rx.changed () => (), + }; + } + + debug! ("Blue QUIC --> TCP closed"); + + Ok::<_, anyhow::Error> (()) + }) + }; + + let task_green = { + let mut shutdown_flag_rx = shutdown_flag_rx.clone (); + + tokio::spawn (async move { + let mut buf = vec! [0u8; 65_536]; + while *shutdown_flag_rx.borrow () { + trace! ("Green reading from TCP..."); + tokio::select! { + x = local_recv.read (&mut buf) => { + let bytes_read = match x? { + 0 => break, + x => x, + }; + let buf_slice = &buf [0..bytes_read]; + trace! ("Downlink relaying {} bytes", bytes_read); + relay_send.write_all (buf_slice).await?; + }, + _ = shutdown_flag_rx.changed () => (), + }; + } + + debug! ("Green TCP --> QUIC closed"); + + Ok::<_, anyhow::Error> (()) + }) + }; + + task_blue.await??; + task_green.await??; + + debug! ("Ended PTTH connection"); + + Ok (()) +} diff --git a/prototypes/quic_demo/src/lib.rs b/prototypes/quic_demo/src/lib.rs index 83c96a2..dcbd50a 100644 --- a/prototypes/quic_demo/src/lib.rs +++ b/prototypes/quic_demo/src/lib.rs @@ -1,3 +1,4 @@ +pub mod client_proxy; pub mod connection; pub mod prelude; pub mod protocol; diff --git a/todone.md b/todone.md new file mode 100644 index 0000000..f4c3744 --- /dev/null +++ b/todone.md @@ -0,0 +1,3 @@ +# Completed todos + +- Split client proxy into its own crate to isolate FLTK dep