From 940b71eba212fc15f66c85a92fee5d94bb5b05fb Mon Sep 17 00:00:00 2001 From: Jovi Hsu Date: Fri, 23 Sep 2022 04:30:56 +0000 Subject: [PATCH] Refactor, use websockify instead of self-backend server --- .gitignore | 3 +- .gitmodules | 3 + Cargo.toml | 6 - Makefile.toml | 29 +- README.md | 5 +- axum-websockify | 1 + backend/Cargo.toml | 55 -- backend/Makefile.toml | 21 - backend/src/agent/agent.rs | 213 -------- backend/src/agent/mod.rs | 4 - backend/src/agent/remote.rs | 86 ---- backend/src/agent/resolver.rs | 100 ---- backend/src/agent/ws.rs | 96 ---- backend/src/main.rs | 91 ---- backend/src/user/auth.rs | 83 --- backend/src/user/mod.rs | 1 - frontend/.gitignore | 10 - frontend/Cargo.toml | 56 --- frontend/Makefile.toml | 29 -- frontend/src/app.rs | 141 ------ frontend/src/components/auth.rs | 154 ------ frontend/src/components/clipboard.rs | 69 --- frontend/src/components/host.rs | 143 ------ frontend/src/components/input.rs | 68 --- frontend/src/components/mod.rs | 5 - frontend/src/components/ws.rs | 111 ---- frontend/src/lib.rs | 18 - frontend/src/pages/mod.rs | 3 - frontend/src/pages/page_home.rs | 28 -- frontend/src/pages/page_not_found.rs | 39 -- frontend/src/pages/page_vnc.rs | 473 ------------------ frontend/src/protocal/mod.rs | 2 - frontend/src/protocal/vnc/mod.rs | 4 - frontend/src/utils.rs | 49 -- frontend/static/index.html | 55 -- frontend/static/p404.html | 9 - run.sh | 2 +- webvnc/.appveyor.yml | 11 + webvnc/.gitignore | 6 + webvnc/Cargo.toml | 50 ++ webvnc/Makefile.toml | 25 + webvnc/asserts/vnc.html | 52 ++ webvnc/src/lib.rs | 310 ++++++++++++ webvnc/src/utils.rs | 10 + .../src/protocal => webvnc/src}/vnc/des.rs | 0 .../common.rs => webvnc/src/vnc/mod.rs | 77 +-- .../src/protocal => webvnc/src}/vnc/vnc.rs | 80 ++- .../protocal => webvnc/src}/vnc/x11cursor.rs | 2 +- .../src}/vnc/x11keyboard.rs | 0 webvnc/tests/web.rs | 13 + 50 files changed, 574 insertions(+), 2327 deletions(-) create mode 100644 .gitmodules delete mode 100644 Cargo.toml create mode 160000 axum-websockify delete mode 100644 backend/Cargo.toml delete mode 100644 backend/Makefile.toml delete mode 100644 backend/src/agent/agent.rs delete mode 100644 backend/src/agent/mod.rs delete mode 100644 backend/src/agent/remote.rs delete mode 100644 backend/src/agent/resolver.rs delete mode 100644 backend/src/agent/ws.rs delete mode 100644 backend/src/main.rs delete mode 100644 backend/src/user/auth.rs delete mode 100644 backend/src/user/mod.rs delete mode 100644 frontend/.gitignore delete mode 100644 frontend/Cargo.toml delete mode 100644 frontend/Makefile.toml delete mode 100644 frontend/src/app.rs delete mode 100644 frontend/src/components/auth.rs delete mode 100644 frontend/src/components/clipboard.rs delete mode 100644 frontend/src/components/host.rs delete mode 100644 frontend/src/components/input.rs delete mode 100644 frontend/src/components/mod.rs delete mode 100644 frontend/src/components/ws.rs delete mode 100644 frontend/src/lib.rs delete mode 100644 frontend/src/pages/mod.rs delete mode 100644 frontend/src/pages/page_home.rs delete mode 100644 frontend/src/pages/page_not_found.rs delete mode 100644 frontend/src/pages/page_vnc.rs delete mode 100644 frontend/src/protocal/mod.rs delete mode 100644 frontend/src/protocal/vnc/mod.rs delete mode 100644 frontend/src/utils.rs delete mode 100644 frontend/static/index.html delete mode 100644 frontend/static/p404.html create mode 100644 webvnc/.appveyor.yml create mode 100644 webvnc/.gitignore create mode 100644 webvnc/Cargo.toml create mode 100644 webvnc/Makefile.toml create mode 100644 webvnc/asserts/vnc.html create mode 100644 webvnc/src/lib.rs create mode 100644 webvnc/src/utils.rs rename {frontend/src/protocal => webvnc/src}/vnc/des.rs (100%) rename frontend/src/protocal/common.rs => webvnc/src/vnc/mod.rs (81%) rename {frontend/src/protocal => webvnc/src}/vnc/vnc.rs (91%) rename {frontend/src/protocal => webvnc/src}/vnc/x11cursor.rs (95%) rename {frontend/src/protocal => webvnc/src}/vnc/x11keyboard.rs (100%) create mode 100644 webvnc/tests/web.rs diff --git a/.gitignore b/.gitignore index fa7bac9..f3aac92 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target/ -/build/ -Cargo.lock \ No newline at end of file +/build/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6823c5d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "axum-websockify"] + path = axum-websockify + url = https://github.com/HsuJv/axum-websockify.git diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 9b7517e..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[workspace] - -members = [ - "backend", - "frontend", -] \ No newline at end of file diff --git a/Makefile.toml b/Makefile.toml index 21a3e9d..19f2e52 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -1,11 +1,32 @@ [tasks.install-debug] -dependencies = ["build-debug", "member_flow"] +dependencies = ["websockify", "wasm-debug"] [tasks.install-release] -dependencies = ["build-release", "member_flow"] +dependencies = ["websockify", "wasm-release"] -[tasks.member_flow] -run_task = { name = "member_flow", fork = true, parallel = true} +[tasks.wasm-debug] +script = ''' +cd ${VNC} && cargo make install-debug +''' + +[tasks.wasm-release] +script = ''' +cd ${VNC} && cargo make install-release +''' + + +[tasks.websockify] +dependencies = ["install-dir"] +script = ''' +cd ${WEBSOCKIFY} && cargo build --release && cp ./target/release/${WEBSOCKIFY} $INSTALL_PATH/ +''' + +[tasks.install-dir] +script = ''' +mkdir -p $INSTALL_PATH +''' [env] INSTALL_PATH= "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/build" +WEBSOCKIFY="axum-websockify" +VNC="webvnc" \ No newline at end of file diff --git a/README.md b/README.md index 80660ad..56de926 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # A Remote Access Gateway -* Full-stack project written with Rust / Yew + Actix +* Webassembly Terminal Services written with Rust / Yew ## Dependencies @@ -25,6 +25,3 @@ * RDP Clients: - WIP - -* Backend database - - WIP diff --git a/axum-websockify b/axum-websockify new file mode 160000 index 0000000..7422904 --- /dev/null +++ b/axum-websockify @@ -0,0 +1 @@ +Subproject commit 74229048831bc6c2d0227de68f5b1644f0d6c3f6 diff --git a/backend/Cargo.toml b/backend/Cargo.toml deleted file mode 100644 index 22af9f7..0000000 --- a/backend/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -authors = [ - "Jovi Hsu " -] -categories = ["wasm", "web-programming", "sslvpn"] -description = "" -edition = "2021" -keywords = ["yew", "wasm", "wasm-bindgen", "web", "sslvpn"] -license = "GPL3" -name = "webgateway-be" -readme = "README.md" -version = "0.1.0" -repository = "https://www.github.com/HsuJv/webgateway" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -tokio = {version="1.13.0", feature="io-util"} -tokio-io = "0.1.13" -tokio-core = "0.1.18" -tokio-codec = "0.1.2" -tokio-util = "0.6.9" - -actix = "0.12.0" -actix-session = "0.5.0-beta.3" -actix-web = "4.0.0-beta.10" -actix-files = "0.6.0-beta.8" -actix-web-actors = "4.0.0-beta.7" -actix-codec = "0.4" - -urlencoding = "2.1.0" -bytes = "1.1.0" -serde = "1.0" -serde_json = "1.0" -trust-dns-resolver = "0.20" -rand = "0.8" -rustls = "0.20.0" - -futures = "0.3.17" -futures-util= "0.3" - -# log systems -femme = "1.3" -log = "0.4" -async-log = "2.0.0" - -[profile.dev] -panic = "unwind" -opt-level = 0 - -[profile.release] -panic = 'abort' -codegen-units = 1 -opt-level = 's' -lto = true \ No newline at end of file diff --git a/backend/Makefile.toml b/backend/Makefile.toml deleted file mode 100644 index 2728eba..0000000 --- a/backend/Makefile.toml +++ /dev/null @@ -1,21 +0,0 @@ -[tasks.build-debug] -command="cargo" -args=["build"] - -[tasks.build-release] -command="cargo" -args=["build", "--release"] - -[tasks.install-debug] -dependencies = ["build-debug"] -script = ''' - mkdir -p $INSTALL_PATH - cp $CARGO_MAKE_CRATE_TARGET_DIRECTORY/debug/$CARGO_MAKE_CRATE_NAME $INSTALL_PATH - ''' - -[tasks.install-release] -dependencies = ["build-release"] -script = ''' - mkdir -p $INSTALL_PATH - cp $CARGO_MAKE_CRATE_TARGET_DIRECTORY/release/$CARGO_MAKE_CRATE_NAME $INSTALL_PATH - ''' \ No newline at end of file diff --git a/backend/src/agent/agent.rs b/backend/src/agent/agent.rs deleted file mode 100644 index 2b34002..0000000 --- a/backend/src/agent/agent.rs +++ /dev/null @@ -1,213 +0,0 @@ -use crate::agent::ws; -use actix::prelude::*; -use actix_codec::{Decoder, Encoder}; -use actix_web::web::Bytes; -use bytes::BytesMut; -use std::collections::HashMap; -use std::io; -use tokio::net::{tcp::OwnedWriteHalf, TcpStream}; -use tokio_util::codec::FramedRead; - -use log::*; - -struct TcpCodec; - -impl Encoder for TcpCodec { - type Error = io::Error; - - fn encode(&mut self, _item: Bytes, _dst: &mut BytesMut) -> Result<(), Self::Error> { - // info!("encoding: {:?}", item); - Ok(()) - } -} - -impl Decoder for TcpCodec { - type Item = Bytes; - type Error = io::Error; - - fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - // info!("recv from server: {:?}", src); - if src.is_empty() { - return Ok(None); - } - let web_bytes = Bytes::from(src.to_vec()); - src.clear(); - Ok(Some(web_bytes)) - } -} - -#[derive(Message)] -#[rtype(result = "()")] -pub enum AgentMsg { - Ready(Addr), - SendToServer(Bytes), - SendToClient(Bytes), - Shutdown, -} - -pub struct Agent { - id: u32, - server_info: String, - writer: OwnedWriteHalf, - ws_addr: Option>, - pending: Vec, -} - -impl Actor for Agent { - type Context = Context; - - fn started(&mut self, _ctx: &mut Self::Context) { - info!("Agent {} started", self.id); - // ctx.address().do_send(AgentMsg::ReadReady); - } -} - -impl Handler for Agent { - type Result = (); - - fn handle(&mut self, msg: AgentMsg, ctx: &mut Context) -> Self::Result { - match msg { - AgentMsg::Ready(ws_addr) => { - self.ws_addr = Some(ws_addr); - info!("Agent {} - Websocket connect ready", self.server_info); - for msg in self.pending.drain(..) { - self.ws_addr - .as_ref() - .unwrap() - .do_send(ws::WsMsg::SendToClient(msg)); - } - } - AgentMsg::SendToServer(data) => { - let to_send = data.to_vec(); - self.writer.try_write(&to_send).unwrap(); - } - AgentMsg::SendToClient(data) => { - if self.ws_addr.is_some() { - self.ws_addr - .as_ref() - .unwrap() - .do_send(ws::WsMsg::SendToClient(data)); - } - } - AgentMsg::Shutdown => { - info!("Agent {} - Shutdown", self.server_info); - ctx.stop(); - } - } - } -} - -impl StreamHandler> for Agent { - fn handle(&mut self, msg: Result, ctx: &mut Context) { - match msg { - Ok(data) => { - // info!("recv from server: {:?}", data); - if self.ws_addr.is_some() { - ctx.address().do_send(AgentMsg::SendToClient(data)); - } else { - info!("Websocket session not ready"); - self.pending.push(data); - } - } - Err(err) => { - error!("error: {:?}", err); - if self.ws_addr.is_some() { - self.ws_addr.as_ref().unwrap().do_send(ws::WsMsg::Close); - } - ctx.address().do_send(AgentMsg::Shutdown); - } - } - } -} - -impl Agent { - pub async fn new(id: u32, target: (String, u16)) -> Option> { - let (host, port) = target; - let server_info = format!("{}:{}", host, port); - info!("connect to server: {}", server_info); - let server_stream = TcpStream::connect(&server_info).await; - if server_stream.is_err() { - info!("connect to server failed: {}", server_info); - } - let server_stream = server_stream.unwrap(); - let addr = Agent::create(move |ctx| { - let (r, w) = server_stream.into_split(); - let r = FramedRead::new(r, TcpCodec {}); - Agent::add_stream(r, ctx); - Self { - id, - server_info, - writer: w, - ws_addr: None, - pending: vec![], - } - }); - Some(addr) - } -} - -#[derive(MessageResponse)] -pub enum AgentManagerResult { - Success(Addr), - Failed, - NoReturn, -} - -#[derive(Message)] -#[rtype(result = "AgentManagerResult")] -pub enum AgentManagerMsg { - Add((u32, Addr)), - Get(u32), - Del(u32), -} - -pub struct AgentManager { - agents: HashMap>, -} - -impl AgentManager { - pub fn new() -> Addr { - Self { - agents: HashMap::new(), - } - .start() - } -} - -impl Actor for AgentManager { - type Context = Context; - - fn started(&mut self, _ctx: &mut Context) { - info!("AgentManager started"); - } - - fn stopped(&mut self, _ctx: &mut Context) { - info!("AgentManager stopped"); - } -} - -impl Handler for AgentManager { - type Result = AgentManagerResult; - - fn handle(&mut self, msg: AgentManagerMsg, _ctx: &mut Context) -> Self::Result { - match msg { - AgentManagerMsg::Add(addr) => { - info!("add agent: {:?}", addr.0); - self.agents.insert(addr.0, addr.1); - AgentManagerResult::NoReturn - } - AgentManagerMsg::Get(aid) => { - info!("get agent: {}", aid); - if let Some(addr) = self.agents.get(&aid) { - AgentManagerResult::Success(addr.clone()) - } else { - AgentManagerResult::Failed - } - } - AgentManagerMsg::Del(id) => { - self.agents.remove(&id); - AgentManagerResult::NoReturn - } - } - } -} diff --git a/backend/src/agent/mod.rs b/backend/src/agent/mod.rs deleted file mode 100644 index 20548c9..0000000 --- a/backend/src/agent/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod agent; -pub mod remote; -pub mod resolver; -pub mod ws; diff --git a/backend/src/agent/remote.rs b/backend/src/agent/remote.rs deleted file mode 100644 index 6a40095..0000000 --- a/backend/src/agent/remote.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::sync::Arc; - -use actix_session::Session; -use actix_web::*; -use serde::{Deserialize, Serialize}; -use serde_json::json; - -use log::info; -use rand::Rng; - -use super::agent; - -#[derive(Debug, Serialize, Deserialize)] -pub struct RemoteInfo { - #[serde(default)] - host: String, - #[serde(default)] - ip: String, - #[serde(default)] - port: u16, -} - -#[post("/target/validate")] -pub async fn target_validate( - req: HttpRequest, - params: web::Json, -) -> Result { - let remote = params.into_inner(); - info!("{:?}", remote); - let app_data = req.app_data::>().unwrap(); - - match app_data.resolver.lockup(remote.host).await { - Some(ipaddr) => { - let json = json!({ - "status": "success", - "ip": ipaddr - }); - Ok(HttpResponse::Ok().json(json)) - } - _ => { - let json = json!({ - "status": "failed", - "message": "Failed to resolve the target name" - }); - Ok(HttpResponse::Ok().json(json)) - } - } -} - -#[post("/target/remote")] -pub async fn target_remote( - req: HttpRequest, - session: Session, - params: web::Json, -) -> Result { - let aid = rand::thread_rng().gen::(); - let app_data = req.app_data::>().unwrap(); - let remote = params.into_inner(); - let agent = agent::Agent::new(aid, (remote.ip, remote.port)).await; - - match agent { - Some(addr) => { - // add to agent list - let _ = app_data - .agents - .send(agent::AgentManagerMsg::Add((aid, addr))) - .await; - - // add session, so that the websocket can send message to the agent - let _ = session.insert("aid", aid); - - // send response - let json = json!({ - "status": "success", - }); - Ok(HttpResponse::Ok().json(json)) - } - _ => { - let json = json!({ - "status": "failed", - "message": "Failed to connect to the target" - }); - Ok(HttpResponse::Ok().json(json)) - } - } -} diff --git a/backend/src/agent/resolver.rs b/backend/src/agent/resolver.rs deleted file mode 100644 index a72519e..0000000 --- a/backend/src/agent/resolver.rs +++ /dev/null @@ -1,100 +0,0 @@ -// use actix::{Actor, Addr, Context, Handler, Message, MessageResponse}; - -// use std::net::*; -// use trust_dns_resolver::config::*; -// use trust_dns_resolver::Resolver; - -// use log::info; - -// #[derive(MessageResponse)] -// pub enum ResolveResp { -// Success(IpAddr), -// Failed, -// } - -// #[derive(Message)] -// #[rtype(result = "ResolveResp")] -// pub enum ResolveMsg { -// Resolve(String), -// } - -// pub struct DnsResolver { -// resolver: Resolver, -// } - -// impl Actor for DnsResolver { -// type Context = Context; -// } - -// impl Handler for DnsResolver { -// type Result = ResolveResp; - -// fn handle(&mut self, msg: ResolveMsg, _: &mut Context) -> Self::Result { -// match msg { -// ResolveMsg::Resolve(name) => { -// if let Ok(response) = self.resolver.lookup_ip(name.clone()) { -// if let Some(address) = response.iter().next() { -// info!("Resolved {} to {}", name, address); -// ResolveResp::Success(address) -// } else { -// ResolveResp::Failed -// } -// } else { -// info!("Failed to resolve {}", name); -// ResolveResp::Failed -// } -// } -// } -// } -// } - -// impl DnsResolver { -// pub fn new() -> Addr { -// let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap(); - -// DnsResolver { resolver }.start() -// } -// } -use std::net::IpAddr; - -use trust_dns_resolver::{ - config::*, - name_server::{GenericConnection, GenericConnectionProvider, TokioRuntime}, -}; -use trust_dns_resolver::{AsyncResolver, TokioHandle}; - -use log::*; - -pub struct DnsResolver { - resolver: AsyncResolver>, -} - -impl DnsResolver { - pub fn new() -> Self { - let resolver = AsyncResolver::new( - ResolverConfig::default(), - ResolverOpts::default(), - TokioHandle, - ) - .unwrap(); - - Self { resolver } - } - - pub async fn lockup(&self, name: String) -> Option { - let lookup = self.resolver.lookup_ip(name.clone()); - - if let Ok(response) = lookup.await { - if let Some(address) = response.iter().next() { - info!("Resolved {} to {}", name, address); - Some(address) - } else { - info!("Failed to resolve {}", name); - None - } - } else { - info!("Failed to resolve {}", name); - None - } - } -} diff --git a/backend/src/agent/ws.rs b/backend/src/agent/ws.rs deleted file mode 100644 index f979464..0000000 --- a/backend/src/agent/ws.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::sync::Arc; - -use actix::ActorContext; -use actix::{Actor, Addr, Message, StreamHandler}; -use actix::{AsyncContext, Handler}; -use actix_session::Session; -use actix_web::web::Bytes; -use actix_web::*; -use actix_web::{web, Error, HttpRequest, HttpResponse}; -use actix_web_actors::ws; -use log::*; - -use super::agent::*; - -#[derive(Message)] -#[rtype(result = "()")] -pub enum WsMsg { - SendToClient(Bytes), - Close, -} - -/// Define Websocket actor -pub struct WsSession { - agent: Addr, -} - -impl Actor for WsSession { - type Context = ws::WebsocketContext; - - fn started(&mut self, ctx: &mut Self::Context) { - // start heartbeats otherwise server will disconnect after 10 seconds - self.agent.do_send(AgentMsg::Ready(ctx.address())); - info!("Websocket connection is established."); - } -} - -impl Handler for WsSession { - type Result = (); - - fn handle(&mut self, msg: WsMsg, ctx: &mut Self::Context) { - match msg { - WsMsg::SendToClient(data) => { - ctx.binary(data); - } - WsMsg::Close => { - ctx.stop(); - } - }; - } -} - -/// Handler for ws::Message message -impl StreamHandler> for WsSession { - fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { - match msg { - Ok(ws::Message::Ping(msg)) => ctx.pong(&msg), - Ok(ws::Message::Text(text)) => ctx.text(text), - Ok(ws::Message::Binary(bin)) => { - self.agent.do_send(AgentMsg::SendToServer(bin)); - } - Ok(ws::Message::Close(_)) => { - self.agent.do_send(AgentMsg::Shutdown); - ctx.stop(); - } - _ => (), - } - } -} - -#[get("/ws")] -pub async fn ws_index( - req: HttpRequest, - session: Session, - stream: web::Payload, -) -> Result { - let aid = session.get::("aid").unwrap_or(Some(0)).unwrap(); - let app_data = req.app_data::>().unwrap(); - - let resp = match app_data - .agents - .send(AgentManagerMsg::Get(aid)) - .await - .unwrap() - { - AgentManagerResult::Success(agent) => ws::start(WsSession { agent }, &req, stream), - _ => Err(actix_web::error::ErrorInternalServerError( - "Agent not found", - )), - }; - - match &resp { - Ok(resp) => info!("{:?}", resp), - Err(e) => error!("{:?}", e), - } - resp -} diff --git a/backend/src/main.rs b/backend/src/main.rs deleted file mode 100644 index a2e95ee..0000000 --- a/backend/src/main.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::sync::Arc; - -use actix::Addr; -use actix_files as fs; -use actix_session::CookieSession; -use actix_web::http::{ContentEncoding, StatusCode}; -use actix_web::*; - -use agent::{agent::AgentManager, resolver::DnsResolver}; -use log::info; -use rand::Rng; -use user::auth::Authenticator; - -mod agent; -mod user; - -const STATIC_DIR: &str = "./static/"; -const PAGE_INDEX: &str = "./static/index.html"; -const PAGE_NOT_FOUND: &str = "./static/p404.html"; - -pub struct AppData { - // session: CookieSession, - resolver: DnsResolver, - authenticator: Addr, - agents: Addr, -} - -impl AppData { - pub fn new() -> Self { - Self { - resolver: DnsResolver::new(), - authenticator: Authenticator::new(), - agents: AgentManager::new(), - } - } -} - -impl Default for AppData { - fn default() -> Self { - Self::new() - } -} - -fn setup_logger() { - let logger = femme::pretty::Logger::new(); - async_log::Logger::wrap(logger, || 12) - .start(log::LevelFilter::Warn) - .unwrap(); -} - -#[get("/")] -async fn index(_: HttpRequest) -> Result { - Ok(fs::NamedFile::open(PAGE_INDEX)?) -} - -async fn p404() -> Result { - Ok(fs::NamedFile::open(PAGE_NOT_FOUND)?.set_status_code(StatusCode::NOT_FOUND)) -} - -#[actix_web::main] -async fn main() -> std::io::Result<()> { - setup_logger(); - - info!("Server starts at http://127.0.0.1:8080"); - let private_key = rand::thread_rng().gen::<[u8; 32]>(); - let app_data = Arc::new(AppData::new()); - HttpServer::new(move || { - App::new() - .app_data(app_data.clone()) - .wrap(CookieSession::signed(&private_key).secure(false)) - .wrap(middleware::Compress::new(ContentEncoding::Gzip)) - .service(index) - .service(user::auth::auth) - .service(agent::remote::target_validate) - .service(agent::remote::target_remote) - .service(agent::ws::ws_index) - .service( - fs::Files::new("/static", STATIC_DIR) - .prefer_utf8(true) - .index_file(PAGE_INDEX) - .use_etag(true) - .default_handler(web::route().to(p404)), - ) - .default_service(web::route().to(p404)) - }) - .bind("127.0.0.1:8080")? - .run() - .await?; - - Ok(()) -} diff --git a/backend/src/user/auth.rs b/backend/src/user/auth.rs deleted file mode 100644 index bfc29f1..0000000 --- a/backend/src/user/auth.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::sync::Arc; - -use actix::{Actor, Addr, Context, Handler, Message, MessageResponse}; -use actix_web::*; -use serde::{Deserialize, Serialize}; -use serde_json::json; - -use log::info; - -#[derive(MessageResponse)] -#[allow(dead_code)] -enum AuthResult { - AuthSuccess, - AuthFailure, -} - -#[derive(Message)] -#[rtype(result = "AuthResult")] -enum AuthMsg { - DoAuth(AuthInfo), -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct AuthInfo { - username: String, - password: String, -} - -pub struct Authenticator; - -impl Authenticator { - pub fn new() -> Addr { - Self {}.start() - } -} - -impl Actor for Authenticator { - type Context = Context; - - fn started(&mut self, _ctx: &mut Self::Context) { - info!("AuthInfo started"); - } - - fn stopped(&mut self, _ctx: &mut Self::Context) { - info!("AuthInfo stopped"); - } -} - -impl Handler for Authenticator { - type Result = AuthResult; - - fn handle(&mut self, msg: AuthMsg, _ctx: &mut Context) -> Self::Result { - match msg { - AuthMsg::DoAuth(_auth_info) => { - // if auth_info.username == "admin" && auth_info.password == "admin" { - // AuthResult::AuthSuccess - // } else { - // AuthResult::AuthFailure - // } - AuthResult::AuthSuccess - } - } - } -} - -#[post("/auth")] -pub async fn auth(params: web::Json, req: HttpRequest) -> Result { - let auth_info = params.into_inner(); - let app_data = req.app_data::>().unwrap(); - let res = app_data - .authenticator - .send(AuthMsg::DoAuth(auth_info)) - .await; - - match res { - Ok(AuthResult::AuthSuccess) => Ok(HttpResponse::Ok().json(json!({ - "status": "success", - }))), - _ => Ok(HttpResponse::Ok().json(json!({ - "status": "failure", - }))), - } -} diff --git a/backend/src/user/mod.rs b/backend/src/user/mod.rs deleted file mode 100644 index 0e4a05d..0000000 --- a/backend/src/user/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod auth; diff --git a/frontend/.gitignore b/frontend/.gitignore deleted file mode 100644 index 088ba6b..0000000 --- a/frontend/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - -# These are backup files generated by rustfmt -**/*.rs.bk diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml deleted file mode 100644 index 8671d8a..0000000 --- a/frontend/Cargo.toml +++ /dev/null @@ -1,56 +0,0 @@ -[package] -authors = [ - "Jovi Hsu " -] -categories = ["wasm", "web-programming", "sslvpn"] -description = "" -edition = "2021" -keywords = ["yew", "wasm", "wasm-bindgen", "web", "sslvpn"] -license = "GPL3" -name = "webgateway-fe" -readme = "README.md" -version = "0.1.0" -repository = "https://www.github.com/HsuJv/webgateway" - - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -wasm-bindgen = "^0.2" -yew = "0.18" -js-sys = "0.3.55" -web-sys = {version="0.3.55", features=["HtmlCanvasElement", "CanvasRenderingContext2d", "ImageData"]} -gloo = "0.4.0" -yew-router = "0.15" -# The `console_error_panic_hook` crate provides better debugging of panics by -# logging them with `console.error`. This is great for development, but requires -# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for -# code size when deploying. -console_error_panic_hook = { version = "0.1.6", optional = true } -# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size -# compared to the default allocator's ~10K. It is slower than the default -# allocator, however. -wee_alloc = { version = "0.4", optional = true } -serde_json = "1.0" -anyhow = "1.0" - -magic-crypt= "3" - -[features] -default = ["console_error_panic_hook", "wee_alloc"] - -[dev-dependencies] -wasm-bindgen-test = "0.3.13" - -[profile.dev] -panic = "unwind" -opt-level = 0 - - - -[profile.release] -panic = 'abort' -codegen-units = 1 -opt-level = 's' -lto = true \ No newline at end of file diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml deleted file mode 100644 index 3fad48a..0000000 --- a/frontend/Makefile.toml +++ /dev/null @@ -1,29 +0,0 @@ -[tasks.build-debug] -command = "wasm-pack" -args = ["build", "--target", "web", "--out-name", "wasm", "--out-dir", "./pkg", "--dev"] - -[tasks.build-release] -command = "wasm-pack" -args = ["build", "--target", "web", "--out-name", "wasm", "--out-dir", "./pkg"] - -[tasks.install-debug] -dependencies=["build-debug", "install_wasm", "install_html"] - -[tasks.install-release] -dependencies=["build-release", "install_wasm", "install_html"] - -[tasks.install_wasm] -script = ''' - mkdir -p $FE_STATIC_INSTALL_PATH - cp ./pkg/wasm.js $FE_STATIC_INSTALL_PATH - cp ./pkg/wasm_bg.wasm $FE_STATIC_INSTALL_PATH - ''' - -[tasks.install_html] -script = ''' - mkdir -p $FE_STATIC_INSTALL_PATH - cp static/* $FE_STATIC_INSTALL_PATH - ''' - -[env] -FE_STATIC_INSTALL_PATH="${INSTALL_PATH}/static" \ No newline at end of file diff --git a/frontend/src/app.rs b/frontend/src/app.rs deleted file mode 100644 index 830947d..0000000 --- a/frontend/src/app.rs +++ /dev/null @@ -1,141 +0,0 @@ -use std::borrow::Cow; - -use crate::components::auth; -use crate::pages::{page_home::PageHome, page_not_found::PageNotFound}; -use yew::html::IntoPropValue; -use yew::prelude::*; -use yew::services::ConsoleService; -use yew::Component; -use yew_router::prelude::*; -use yew_router::{router::Router, Switch}; - -#[derive(Switch, Clone, Debug)] -enum AppRoute { - // #[at("/ssh/:id")] - // Ssh(i32), - // #[to = "/ssh"] - // Ssh, - #[to = "/!"] - Home, - #[to = ""] - NotFound, -} - -impl From for &str { - fn from(route: AppRoute) -> Self { - match route { - // AppRoute::Ssh => "/ssh", - _ => "/", - } - } -} - -impl IntoPropValue>> for AppRoute { - fn into_prop_value(self: AppRoute) -> Option> { - Some(Cow::Borrowed(self.into())) - } -} - -pub struct App { - authdone: bool, - link: ComponentLink, -} - -pub enum AppMsg { - AuthDone, -} - -impl Component for App { - type Message = AppMsg; - type Properties = (); - - fn create(_: Self::Properties, link: ComponentLink) -> Self { - Self { - authdone: false, - link, - } - } - - fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - AppMsg::AuthDone => self.authdone = true, - } - true - } - - fn change(&mut self, _: Self::Properties) -> ShouldRender { - false - } - - fn view(&self) -> Html { - html! { - <> - { - - if self.authdone { - html! { - <> - {self.view_nav()} - -
- - render = Router::render(Self::switch) - redirect=Router::redirect(|route: Route| { - ConsoleService::log(&format!("{:?}", route)); - AppRoute::NotFound - }) - /> -
- - } - } - else { - let onauthdone = &self.link.callback(|_| AppMsg::AuthDone); - html!{ - - } - } - } - - - } - } -} - -impl App { - fn view_nav(&self) -> Html { - html! { - - } - } - - fn switch(switch: AppRoute) -> Html { - ConsoleService::log(&format!("{:?}", switch)); - match switch { - // Route::Ssh(ip) => { - // html! { } - // } - // AppRoute::Ssh => { - // html! {} - // } - AppRoute::Home => { - html! {} - } - AppRoute::NotFound => { - html! { } - } - } - } -} diff --git a/frontend/src/components/auth.rs b/frontend/src/components/auth.rs deleted file mode 100644 index 577199b..0000000 --- a/frontend/src/components/auth.rs +++ /dev/null @@ -1,154 +0,0 @@ -use super::input::Input; -use anyhow; -use serde_json::{json, Value}; -use std::fmt::Debug; -use yew::services::{ - fetch::{FetchTask, Request}, - FetchService, -}; -use yew::{format::Json, services::fetch::Response}; -use yew::{prelude::*, services::ConsoleService}; -pub enum AuthMsg { - UpdateUsername(String), - UpdatePassword(String), - AuthRequest, - AuthResponse(Result), -} - -pub struct AuthComponents { - username: String, - password: String, - link: ComponentLink, - auth_result: String, - fetch_task: Option, - onauthdone: Callback<()>, -} - -impl Debug for AuthComponents { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "AuthComponents {{ username: {}, password: {} }}", - self.username, self.password - ) - } -} - -#[derive(Clone, PartialEq, Properties)] -pub struct AuthProps { - #[prop_or_default] - pub onauthdone: Callback<()>, -} - -impl Component for AuthComponents { - type Message = AuthMsg; - type Properties = AuthProps; - - fn create(props: Self::Properties, link: ComponentLink) -> Self { - AuthComponents { - username: String::new(), - password: String::new(), - auth_result: String::new(), - link, - fetch_task: None, - onauthdone: props.onauthdone, - } - } - - fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - AuthMsg::UpdateUsername(username) => { - self.username = username; - self.auth_result.clear(); - } - AuthMsg::UpdatePassword(password) => { - self.password = password; - self.auth_result.clear(); - } - AuthMsg::AuthRequest => { - let auth_info = json!({ - "username": self.username, - "password": self.password, - }); - - // 1. build the request - let request = Request::post("/auth") - .header("Content-Type", "application/json") - .body(Json(&auth_info)) - .expect("Could not build auth request."); - // 2. construct a callback - let callback = - self.link - .callback(|response: Response>>| { - // ConsoleService::error(&format!("{:?}", response)); - let Json(data) = response.into_body(); - AuthMsg::AuthResponse(data) - }); - // 3. pass the request and callback to the fetch service - let task = FetchService::fetch(request, callback).expect("failed to start request"); - // 4. store the task so it isn't canceled immediately - self.fetch_task = Some(task); - } - AuthMsg::AuthResponse(response) => { - if let Ok(response) = response { - self.auth_result = response["status"].to_string(); - if "\"success\"" == self.auth_result { - self.onauthdone.emit(()); - } - } else { - self.auth_result = String::from("Auth failed with unknown reason"); - ConsoleService::error(&format!("{:?}", response.unwrap_err().to_string())); - } - // release resources - self.fetch_task = None; - } - } - // ConsoleService::log(&format!( - // "username: {}, password {}", - // self.username, self.password - // )); - true - } - - fn change(&mut self, _props: Self::Properties) -> ShouldRender { - false - } - - fn view(&self) -> Html { - let link = &self.link; - - let update_uname = link.callback(AuthMsg::UpdateUsername); - - let update_pword = link.callback(AuthMsg::UpdatePassword); - - let auth_post = link.callback(|_| AuthMsg::AuthRequest); - - html! { -
- - -
- - -
- -
- {self.auth_result_view()} -
- } - } -} - -impl AuthComponents { - fn auth_result_view(&self) -> Html { - if self.fetch_task.is_some() { - html! { -
{"Authing..."}
- } - } else { - html! { -
{self.auth_result.clone()}
- } - } - } -} diff --git a/frontend/src/components/clipboard.rs b/frontend/src/components/clipboard.rs deleted file mode 100644 index 3cb6110..0000000 --- a/frontend/src/components/clipboard.rs +++ /dev/null @@ -1,69 +0,0 @@ -use yew::prelude::*; - -use crate::utils::WeakComponentLink; - -pub enum ClipboardMsg { - UpdateClipboard(String), - SendClipboard, -} - -pub struct Clipboard { - link: ComponentLink, - onsubmit: Callback, - text: String, -} - -// Props -#[derive(Clone, PartialEq, Properties)] -pub struct ClipboardProps { - #[prop_or_default] - pub weak_link: WeakComponentLink, - pub onsubmit: Callback, -} - -impl Component for Clipboard { - type Message = ClipboardMsg; - type Properties = ClipboardProps; - - fn create(props: Self::Properties, link: ComponentLink) -> Self { - props.weak_link.borrow_mut().replace(link.clone()); - Clipboard { - link, - onsubmit: props.onsubmit, - text: String::new(), - } - } - - fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - ClipboardMsg::UpdateClipboard(text) => { - self.text = text; - } - ClipboardMsg::SendClipboard => { - self.onsubmit.emit(self.text.clone()); - self.text.clear(); - } - } - true - } - - fn change(&mut self, _props: Self::Properties) -> ShouldRender { - false - } - - fn view(&self) -> Html { - let update_clipboard = self.link.callback(|e: ChangeData| match e { - ChangeData::Value(v) => ClipboardMsg::UpdateClipboard(v), - _ => panic!("unexpected message"), - }); - let set_clipboard = self.link.callback(|_| ClipboardMsg::SendClipboard); - html! { - <> -