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
+ }
}