From c8bb376d801d3c9e7f510c5d7c6c4868e4dad1b9 Mon Sep 17 00:00:00 2001 From: Jovi Hsu Date: Wed, 10 Nov 2021 22:41:11 +0800 Subject: [PATCH] dummy --- frontend/src/components/auth.rs | 20 +++---- frontend/src/components/input.rs | 68 +++++++++++++++++++++++ frontend/src/components/mod.rs | 1 + frontend/src/components/ws.rs | 1 + frontend/src/pages/page_remote.rs | 89 +++++++++++++++++++++++++++++-- frontend/src/protocal/common.rs | 20 ++++++- frontend/src/protocal/vnc.rs | 69 ++++++++++++++++++------ 7 files changed, 232 insertions(+), 36 deletions(-) create mode 100644 frontend/src/components/input.rs diff --git a/frontend/src/components/auth.rs b/frontend/src/components/auth.rs index c87da17..92402f4 100644 --- a/frontend/src/components/auth.rs +++ b/frontend/src/components/auth.rs @@ -1,3 +1,4 @@ +use super::input::Input; use anyhow; use serde_json::{json, Value}; use std::fmt::Debug; @@ -116,28 +117,19 @@ impl Component for AuthComponents { fn view(&self) -> Html { let link = &self.link; - let update_uname = link.callback(|e: ChangeData| match e { - ChangeData::Value(val) => AuthMsg::UpdateUsername(val), - _ => panic!("unexpected message"), - }); + let update_uname = link.callback(|v| AuthMsg::UpdateUsername(v)); - let update_pword = link.callback(|e: ChangeData| match e { - ChangeData::Value(val) => AuthMsg::UpdatePassword(val), - _ => panic!("unexpected message"), - }); + let update_pword = link.callback(|v| AuthMsg::UpdatePassword(v)); - let auth_post = link.callback(|_| { - // ConsoleService::log("Auth post"); - AuthMsg::AuthRequest - }); + let auth_post = link.callback(|_| AuthMsg::AuthRequest); html! {
- +
- +

diff --git a/frontend/src/components/input.rs b/frontend/src/components/input.rs new file mode 100644 index 0000000..34ef3ee --- /dev/null +++ b/frontend/src/components/input.rs @@ -0,0 +1,68 @@ +use yew::prelude::*; + +// message on update +pub enum InputMsg { + Update(String), +} + +// props on_change +#[derive(Clone, PartialEq, Properties)] +pub struct InputProps { + pub on_change: Callback, + pub id: String, + pub type_: String, + pub placeholder: String, +} + +// component input +pub struct Input { + link: ComponentLink, + on_change: Callback, + id: String, + type_: String, + placeholder: String, +} + +impl Component for Input { + type Message = InputMsg; + type Properties = InputProps; + + fn create(props: Self::Properties, link: ComponentLink) -> Self { + Input { + link, + on_change: props.on_change, + id: props.id, + type_: props.type_, + placeholder: props.placeholder, + } + } + + fn update(&mut self, msg: Self::Message) -> ShouldRender { + match msg { + InputMsg::Update(text) => { + self.on_change.emit(text); + true + } + } + } + + fn change(&mut self, _props: Self::Properties) -> ShouldRender { + false + } + + fn view(&self) -> Html { + let on_change = self.link.callback(|e: ChangeData| match e { + ChangeData::Value(v) => InputMsg::Update(v), + _ => panic!("unexpected message"), + }); + + html! { + + } + } +} diff --git a/frontend/src/components/mod.rs b/frontend/src/components/mod.rs index 8d25617..e90373a 100644 --- a/frontend/src/components/mod.rs +++ b/frontend/src/components/mod.rs @@ -1,3 +1,4 @@ pub mod auth; pub mod host; +pub mod input; pub mod ws; diff --git a/frontend/src/components/ws.rs b/frontend/src/components/ws.rs index 101b78b..21a98f4 100644 --- a/frontend/src/components/ws.rs +++ b/frontend/src/components/ws.rs @@ -104,6 +104,7 @@ impl Component for WebsocketCtx { fn rendered(&mut self, first_render: bool) { if first_render && self.ws.is_none() { + ConsoleService::log(&format!("Start websocket")); self.link.send_message(WebsocketMsg::Connect); } } diff --git a/frontend/src/pages/page_remote.rs b/frontend/src/pages/page_remote.rs index e0d4d31..28d6bad 100644 --- a/frontend/src/pages/page_remote.rs +++ b/frontend/src/pages/page_remote.rs @@ -9,9 +9,8 @@ use yew::{ }, }; -use crate::components::ws::WebsocketMsg; use crate::{ - components, + components::{self, input::Input, ws::WebsocketMsg}, protocal::{common::*, vnc::VncHandler}, utils::WeakComponentLink, }; @@ -24,6 +23,10 @@ pub struct PageRemote { connected: bool, handler: ProtocalHandler, ws_link: WeakComponentLink, + request_username: bool, + request_password: bool, + username: String, + password: String, } #[derive(Clone, PartialEq, Properties)] @@ -35,6 +38,9 @@ pub enum RemoteMsg { Connected, Recv(Vec), Send(Vec), + UpdateUsername(String), + UpdatePassword(String), + SendCredential, } impl Component for PageRemote { @@ -50,6 +56,10 @@ impl Component for PageRemote { connected: false, handler: ProtocalHandler::new(), ws_link: WeakComponentLink::default(), + request_username: false, + request_password: false, + username: String::from(""), + password: String::from(""), } } @@ -115,6 +125,11 @@ impl Component for PageRemote { self.link.send_message(RemoteMsg::Send(out)); false } + ProtocalHandlerOutput::RequirePassword => { + self.request_password = true; + true + } + _ => unimplemented!(), } } RemoteMsg::Send(v) => { @@ -125,6 +140,20 @@ impl Component for PageRemote { .send_message(WebsocketMsg::Send(Ok(v))); false } + RemoteMsg::UpdateUsername(username) => { + self.username = username; + true + } + RemoteMsg::UpdatePassword(password) => { + self.password = password; + true + } + RemoteMsg::SendCredential => { + self.request_username = false; + self.request_password = false; + self.handler.set_credential(&self.username, &self.password); + true + } } } @@ -146,11 +175,61 @@ impl Component for PageRemote { let ws_link = &self.ws_link; html! { <> - - {self.error_msg.clone()} +
+ {self.username_view()} + {self.password_view()} + {self.button_connect_view()} + + {self.error_msg.clone()} +
} } } } + +// impl PageRemote +impl PageRemote { + fn username_view(&self) -> Html { + if self.request_username { + let update_username = self.link.callback(|v| RemoteMsg::UpdateUsername(v)); + html! { + <> + +
+ + } + } else { + html! {} + } + } + + fn password_view(&self) -> Html { + if self.request_password { + let update_password = self.link.callback(|v| RemoteMsg::UpdatePassword(v)); + html! { + <> + +
+ + } + } else { + html! {} + } + } + + fn button_connect_view(&self) -> Html { + if self.request_username || self.request_password { + let send_credential = self.link.callback(|_| RemoteMsg::SendCredential); + html! { + <> + +
+ + } + } else { + html! {} + } + } +} diff --git a/frontend/src/protocal/common.rs b/frontend/src/protocal/common.rs index ff76c2d..b505696 100644 --- a/frontend/src/protocal/common.rs +++ b/frontend/src/protocal/common.rs @@ -3,6 +3,8 @@ pub enum ProtocalHandlerOutput { Ok, WsBuf(Vec), Err(String), + RequireUsername, + RequirePassword, } pub struct ProtocalHandler @@ -23,11 +25,16 @@ where pub fn handle(&mut self, input: &[u8]) -> ProtocalHandlerOutput { self.inner.handle(input) } + + pub fn set_credential(&mut self, username: &str, password: &str) -> ProtocalHandlerOutput { + self.inner.set_credential(username, password) + } } pub trait ProtocalImpl { fn new() -> Self; fn handle(&mut self, input: &[u8]) -> ProtocalHandlerOutput; + fn set_credential(&mut self, username: &str, password: &str) -> ProtocalHandlerOutput; } pub struct StreamReader<'a> { @@ -73,6 +80,12 @@ impl<'a> StreamReader<'a> { Some(Self::read_u32(self).map(|b| b as i32)?) } + pub fn extract_slice(&mut self, len: usize, buf: &mut [u8]) { + for x in self.inner.by_ref().take(len).enumerate() { + buf[x.0] = *x.1; + } + } + pub fn read_string_with_len(&mut self, len: usize) -> Option { let mut buf = vec![0u8; len as usize]; self.inner @@ -85,11 +98,16 @@ impl<'a> StreamReader<'a> { Some(String::from_utf8(buf).unwrap()) } - pub fn read_string(&mut self) -> Option { + pub fn read_string_l16(&mut self) -> Option { let len = self.read_u16()? as usize; Some(self.read_string_with_len(len)?) } + pub fn read_string_l32(&mut self) -> Option { + let len = self.read_u32()? as usize; + Some(self.read_string_with_len(len)?) + } + pub fn eof(&mut self) -> bool { self.inner.next().is_none() } diff --git a/frontend/src/protocal/vnc.rs b/frontend/src/protocal/vnc.rs index 83f7b70..2e0e5be 100644 --- a/frontend/src/protocal/vnc.rs +++ b/frontend/src/protocal/vnc.rs @@ -6,31 +6,42 @@ const VNC_RFB33: &[u8; 12] = b"RFB 003.003\n"; const VNC_RFB37: &[u8; 12] = b"RFB 003.007\n"; const VNC_RFB38: &[u8; 12] = b"RFB 003.008\n"; const VNC_VER_UNSUPPORTED: &str = "unsupported version"; +const VNC_FAILED: &str = "Connection failed with unknow reason"; enum VncState { Handshake, Authentication, + ClientInit, // auth done } #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] pub enum SecurityType { - Unknown(u8), - Invalid, - None, - VncAuth, - AppleRdp, + Invalid = 0, + None = 1, + VncAuth = 2, + RA2 = 5, + RA2ne = 6, + Tight = 16, + Ultra = 17, + TLS = 18, + VeNCrypt = 19, } pub struct VncHandler { state: VncState, - // output: Vec, + challenge: [u8; 16], + security_type: SecurityType, + password: String, } impl ProtocalImpl for VncHandler { fn new() -> Self { VncHandler { state: VncState::Handshake, - // output: Vec::new(), + challenge: [0u8; 16], + security_type: SecurityType::Invalid, + password: String::new(), } } @@ -47,11 +58,19 @@ impl ProtocalImpl for VncHandler { } VncState::Authentication => { ConsoleService::log(&format!("{:?}", input)); - return ProtocalHandlerOutput::Ok; + return self.start_authenticate(input); } _ => panic!("unsupported version"), } } + + fn set_credential(&mut self, _username: &str, password: &str) -> ProtocalHandlerOutput { + ConsoleService::log(&format!("{:?}", password)); + ConsoleService::log(&format!("{:?}", self.challenge)); + // since vnc do not require username, so we just ignore it + self.password = password.to_string(); + self.continue_authenticate() + } } // private methods @@ -59,17 +78,35 @@ impl VncHandler { fn handle_handshake(&self, rfbversion: &[u8]) -> Result<&'static [u8], &'static str> { match rfbversion { b"RFB 003.003\n" => Ok(VNC_RFB33), - b"RFB 003.007\n" => Ok(VNC_RFB37), - b"RFB 003.008\n" => Ok(VNC_RFB38), + b"RFB 003.007\n" => Ok(VNC_RFB33), + b"RFB 003.008\n" => Ok(VNC_RFB33), _ => Err(VNC_VER_UNSUPPORTED), } } - fn handle_authenticate(&mut self, auth: &[u8]) -> Result<&'static [u8], &'static str> { - match auth { - b"NONE\n" => Ok(b"\x00"), - b"VNCAUTH\n" => Ok(b"\x01"), - b"APPLETALK\n" => Ok(b"\x02"), - _ => Err(VNC_VER_UNSUPPORTED), + fn start_authenticate(&mut self, auth: &[u8]) -> ProtocalHandlerOutput { + let mut sr = StreamReader::new(auth); + match sr.read_u32() { + Some(0) => { + let err_msg = sr.read_string_l32().unwrap(); + ProtocalHandlerOutput::Err(err_msg) + } + Some(1) => { + self.state = VncState::ClientInit; + self.client_initialisation() + } + Some(2) => { + sr.extract_slice(16, &mut self.challenge); + ProtocalHandlerOutput::RequirePassword + } + _ => ProtocalHandlerOutput::Err(VNC_FAILED.to_string()), } } + + fn client_initialisation(&mut self) -> ProtocalHandlerOutput { + ProtocalHandlerOutput::Ok + } + + fn continue_authenticate(&mut self) -> ProtocalHandlerOutput { + ProtocalHandlerOutput::Ok + } }