rdp-rs worked poc
This commit is contained in:
parent
f79b272ffd
commit
c6aa71107c
3257
Cargo.lock
generated
Normal file
3257
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -13,11 +13,21 @@ default = ["console_error_panic_hook"]
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2.63"
|
||||
js-sys = "0.3"
|
||||
untrusted = "0.9"
|
||||
md4 = "0.10.2"
|
||||
hmac = "0.12.1"
|
||||
md-5 = "0.10.5"
|
||||
x509-parser = "0.14.0"
|
||||
rdp-rs = { path = "./rdp-rs", default-features = false }
|
||||
ws_stream_wasm = { version = "^0.7", features = ["tokio_io"] }
|
||||
async_io_stream = { version = "^0.3", features = ["tokio_io"] }
|
||||
pharos = "0.5.3"
|
||||
wasm-bindgen-futures = "0.4.33"
|
||||
futures = "0.3.25"
|
||||
async-trait = "0.1.58"
|
||||
tokio = { version = "^1", features = [
|
||||
"sync",
|
||||
"macros",
|
||||
"io-util",
|
||||
"rt",
|
||||
"time"
|
||||
]}
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit e7a7693b46c7ba56483e464a2a2e478d08c2a9ea
|
||||
Subproject commit c340a749868f70fde328bf15c751f843da6e119e
|
@ -1,12 +1,9 @@
|
||||
mod canvas;
|
||||
mod rdp_client;
|
||||
mod rdp_ws;
|
||||
mod utils;
|
||||
|
||||
use canvas::CanvasUtils;
|
||||
use rdp_client::Rdp;
|
||||
use rdp_ws::Rdp;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{ErrorEvent, HtmlButtonElement, MessageEvent, WebSocket};
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! console_log {
|
||||
@ -22,81 +19,8 @@ extern "C" {
|
||||
pub fn getClipBoard() -> String;
|
||||
}
|
||||
|
||||
fn rdp_close_handle(rdp: &Rdp, canvas: &CanvasUtils, msg: &str) {
|
||||
rdp.close();
|
||||
// unsafe {
|
||||
// REFRESHER.take();
|
||||
// }
|
||||
canvas.close();
|
||||
let status = web_sys::window()
|
||||
.unwrap()
|
||||
.document()
|
||||
.unwrap()
|
||||
.get_element_by_id("rdp_status")
|
||||
.unwrap();
|
||||
status.set_text_content(Some(msg));
|
||||
}
|
||||
|
||||
fn rdp_out_handler(ws: &WebSocket, rdp: &Rdp, canvas: &CanvasUtils) {
|
||||
let out = rdp.get_output();
|
||||
if let Some(out) = out {
|
||||
for ref o in out {
|
||||
match o {
|
||||
rdp_client::RdpOutput::Err(err) => {
|
||||
console_log!("Err {}", err);
|
||||
rdp_close_handle(rdp, canvas, err);
|
||||
}
|
||||
rdp_client::RdpOutput::WsBuf(buf) => match ws.send_with_u8_array(buf) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
let err = format!("error sending message: {:?}", err);
|
||||
rdp_close_handle(rdp, canvas, &err);
|
||||
}
|
||||
},
|
||||
rdp_client::RdpOutput::RequireSSL => match ws.send_with_str("SSL") {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
let err = format!("error launching ssl: {:?}", err);
|
||||
rdp_close_handle(rdp, canvas, &err);
|
||||
}
|
||||
},
|
||||
rdp_client::RdpOutput::RequirePassword => {
|
||||
// let pwd = prompt("Please input the password");
|
||||
// rdp.set_credential(&pwd);
|
||||
// rdp_out_handler(ws, rdp, canvas);
|
||||
}
|
||||
rdp_client::RdpOutput::RenderImage(ri) => {
|
||||
canvas.draw(ri);
|
||||
}
|
||||
rdp_client::RdpOutput::SetResolution(x, y) => {
|
||||
canvas.init(*x as u32, *y as u32);
|
||||
canvas.bind(rdp);
|
||||
// rdp.require_frame(0);
|
||||
rdp_out_handler(ws, rdp, canvas);
|
||||
|
||||
// let vnc_cloned = rdp.clone();
|
||||
// let ws_cloned = ws.clone();
|
||||
// let canvas_cloned = canvas.clone();
|
||||
|
||||
// set a interval for fps enhance
|
||||
// let refresh = move || {
|
||||
// vnc_cloned.require_frame(1);
|
||||
// rdp_out_handler(&ws_cloned, &vnc_cloned, &canvas_cloned);
|
||||
// };
|
||||
|
||||
// let refersher = Interval::new(20, refresh);
|
||||
|
||||
// unsafe {
|
||||
// REFRESHER = Some(refersher);
|
||||
// }
|
||||
}
|
||||
rdp_client::RdpOutput::SetClipboard(text) => {
|
||||
setClipBoard(text.to_owned());
|
||||
// ConsoleService::log(&self.error_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn read_credentials() {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn start_websocket() -> Result<(), JsValue> {
|
||||
@ -115,72 +39,11 @@ fn start_websocket() -> Result<(), JsValue> {
|
||||
},
|
||||
host = web_sys::window().unwrap().location().host()?
|
||||
);
|
||||
let ws = WebSocket::new_with_str(&url, "binary")?;
|
||||
let canvas = CanvasUtils::new();
|
||||
let rdp = Rdp::new();
|
||||
|
||||
let clipboard = web_sys::window()
|
||||
.unwrap()
|
||||
.document()
|
||||
.unwrap()
|
||||
.get_element_by_id("clipboardsend")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlButtonElement>()
|
||||
.map_err(|_| ())
|
||||
.unwrap();
|
||||
let rdp_cloned = rdp.clone();
|
||||
let onclickcb = Closure::<dyn FnMut()>::new(move || {
|
||||
console_log!("Send {:?}", getClipBoard());
|
||||
rdp_cloned.set_clipboard(&getClipBoard());
|
||||
spawn_local(async move {
|
||||
let mut rdp = Rdp::new(&url, "", "", "");
|
||||
while !rdp.start().await {}
|
||||
});
|
||||
clipboard.set_onclick(Some(onclickcb.as_ref().unchecked_ref()));
|
||||
onclickcb.forget();
|
||||
|
||||
ws.set_binary_type(web_sys::BinaryType::Arraybuffer);
|
||||
// on message
|
||||
let cloned_ws = ws.clone();
|
||||
let rdp_cloned = rdp.clone();
|
||||
let canvas_cloned = canvas.clone();
|
||||
|
||||
let onmessage_callback = Closure::<dyn FnMut(_)>::new(move |e: MessageEvent| {
|
||||
if let Ok(abuf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
|
||||
let array = js_sys::Uint8Array::new(&abuf);
|
||||
// let mut canvas_ctx = None;
|
||||
rdp_cloned.do_input(array.to_vec());
|
||||
rdp_out_handler(&cloned_ws, &rdp_cloned, &canvas_cloned);
|
||||
} else {
|
||||
console_log!("message event, received Unknown: {:?}", e.data());
|
||||
}
|
||||
});
|
||||
ws.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
|
||||
// forget the callback to keep it alive
|
||||
onmessage_callback.forget();
|
||||
|
||||
// onerror
|
||||
let onerror_callback = Closure::<dyn FnMut(_)>::new(move |e: ErrorEvent| {
|
||||
console_log!("error event: {:?}", e);
|
||||
});
|
||||
ws.set_onerror(Some(onerror_callback.as_ref().unchecked_ref()));
|
||||
onerror_callback.forget();
|
||||
|
||||
// onopen
|
||||
let rdp_cloned = rdp.clone();
|
||||
let ws_cloned = ws.clone();
|
||||
let canvas_cloned = canvas.clone();
|
||||
let onopen_callback = Closure::<dyn FnMut()>::new(move || {
|
||||
console_log!("socket opened");
|
||||
rdp_cloned.init();
|
||||
rdp_out_handler(&ws_cloned, &rdp_cloned, &canvas_cloned);
|
||||
});
|
||||
ws.set_onopen(Some(onopen_callback.as_ref().unchecked_ref()));
|
||||
onopen_callback.forget();
|
||||
|
||||
let onclose_callback = Closure::<dyn FnMut()>::new(move || {
|
||||
console_log!("socket close");
|
||||
rdp_close_handle(&rdp, &canvas, "Disconnected");
|
||||
});
|
||||
ws.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
|
||||
onclose_callback.forget();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
3
webrdp/src/rdp_ws/mod.rs
Normal file
3
webrdp/src/rdp_ws/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod rdp_client;
|
||||
mod ws_bio;
|
||||
pub use rdp_client::Rdp;
|
100
webrdp/src/rdp_ws/rdp_client.rs
Normal file
100
webrdp/src/rdp_ws/rdp_client.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use rdp::{
|
||||
core::client::{Connector, RdpClient},
|
||||
model::{
|
||||
error::{RdpError, RdpErrorKind, RdpResult},
|
||||
link::{self, AsyncSecureBio},
|
||||
},
|
||||
};
|
||||
use web_sys::Element;
|
||||
|
||||
use crate::{console_log, log};
|
||||
|
||||
use super::ws_bio::WsSecureBio;
|
||||
|
||||
const RDP_HOSTNAME: &str = "webrdp";
|
||||
|
||||
pub struct Rdp {
|
||||
url: String,
|
||||
status_bar: Element,
|
||||
username: String,
|
||||
password: String,
|
||||
domain: String,
|
||||
}
|
||||
|
||||
impl Rdp {
|
||||
pub fn new(url: &str, username: &str, password: &str, domain: &str) -> Self {
|
||||
let status_bar = web_sys::window()
|
||||
.unwrap()
|
||||
.document()
|
||||
.unwrap()
|
||||
.get_element_by_id("rdp_status")
|
||||
.unwrap();
|
||||
Self {
|
||||
url: url.to_owned(),
|
||||
status_bar,
|
||||
username: username.to_owned(),
|
||||
password: password.to_owned(),
|
||||
domain: domain.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_user(&mut self, username: &str) {
|
||||
self.username = username.to_owned();
|
||||
}
|
||||
|
||||
pub fn set_password(&mut self, password: &str) {
|
||||
self.password = password.to_owned();
|
||||
}
|
||||
|
||||
pub fn set_domain(&mut self, domain: &str) {
|
||||
self.domain = domain.to_owned();
|
||||
}
|
||||
|
||||
pub async fn start(&mut self) -> bool {
|
||||
let ws_stream = WsSecureBio::new(&self.url).await;
|
||||
|
||||
let body = web_sys::window()
|
||||
.unwrap()
|
||||
.document()
|
||||
.unwrap()
|
||||
.body()
|
||||
.unwrap();
|
||||
|
||||
let mut rdp_connector = Connector::new()
|
||||
.screen(body.client_width() as u16, body.client_height() as u16)
|
||||
.credentials(
|
||||
self.domain.clone(),
|
||||
self.username.clone(),
|
||||
self.password.clone(),
|
||||
)
|
||||
.set_restricted_admin_mode(false)
|
||||
.auto_logon(false)
|
||||
.blank_creds(false)
|
||||
.check_certificate(false)
|
||||
.name(RDP_HOSTNAME.to_string())
|
||||
.use_nla(true);
|
||||
|
||||
let mut rdp_client = rdp_connector.connect(Box::new(ws_stream)).await.unwrap();
|
||||
console_log!("Rdp Started");
|
||||
loop {
|
||||
if let Err(rdp::model::error::Error::RdpError(e)) = rdp_client
|
||||
.read(|event| match event {
|
||||
_ => console_log!("ignore event"),
|
||||
})
|
||||
.await
|
||||
{
|
||||
match e.kind() {
|
||||
RdpErrorKind::Disconnect => {
|
||||
console_log!("Server ask for disconnect");
|
||||
}
|
||||
_ => console_log!("{:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn disconnect_with_msg(&self, msg: &str) {
|
||||
self.status_bar.set_text_content(Some(msg));
|
||||
}
|
||||
}
|
47
webrdp/src/rdp_ws/ws_bio.rs
Normal file
47
webrdp/src/rdp_ws/ws_bio.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use async_io_stream::IoStream;
|
||||
use async_trait::async_trait;
|
||||
use rdp::model::{error::RdpResult, link::AsyncSecureBio};
|
||||
use tokio::io::AsyncReadExt;
|
||||
use ws_stream_wasm::*;
|
||||
|
||||
use crate::{console_log, log};
|
||||
use {futures::stream::StreamExt, pharos::*, wasm_bindgen::UnwrapThrowExt, ws_stream_wasm::*};
|
||||
|
||||
pub struct WsSecureBio {
|
||||
peer_cert: Vec<u8>,
|
||||
ws_stream: IoStream<WsStreamIo, Vec<u8>>,
|
||||
ws_meta: WsMeta,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AsyncSecureBio<IoStream<WsStreamIo, Vec<u8>>> for WsSecureBio {
|
||||
async fn start_ssl(&mut self, _check_certificate: bool) -> RdpResult<()> {
|
||||
let _ = self.ws_meta.wrapped().send_with_str("SSL");
|
||||
let mut ber_cert = [0; 1500];
|
||||
let size = self.ws_stream.read(&mut ber_cert).await.unwrap();
|
||||
console_log!("Read {} byte public cert", size);
|
||||
self.peer_cert = ber_cert.to_vec();
|
||||
Ok(())
|
||||
}
|
||||
fn get_peer_certificate_der(&self) -> RdpResult<Option<Vec<u8>>> {
|
||||
Ok(Some(self.peer_cert.clone()))
|
||||
}
|
||||
async fn shutdown(&mut self) -> std::io::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_io(&mut self) -> &mut IoStream<WsStreamIo, Vec<u8>> {
|
||||
&mut self.ws_stream
|
||||
}
|
||||
}
|
||||
|
||||
impl WsSecureBio {
|
||||
pub async fn new(url: &str) -> Self {
|
||||
let (ws, wsio) = WsMeta::connect(url, vec!["binary"]).await.unwrap();
|
||||
Self {
|
||||
peer_cert: vec![],
|
||||
ws_stream: wsio.into_io(),
|
||||
ws_meta: ws,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user