mirror of
https://gitlab.com/marcandre.lureau/qemu-display.git
synced 2025-04-14 08:44:46 +00:00
qemu-rdp: initial commit
This commit is contained in:
parent
77de690912
commit
52ab1e7d31
@ -5,6 +5,7 @@ members = [
|
||||
"qemu-rdw",
|
||||
"qemu-vnc",
|
||||
"qemu-vte",
|
||||
"qemu-rdp",
|
||||
"xtask",
|
||||
]
|
||||
default-members = ["qemu-rdw"]
|
||||
|
22
qemu-rdp/Cargo.toml
Normal file
22
qemu-rdp/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "qemu-rdp"
|
||||
version = "0.1.0"
|
||||
authors = ["Mihnea Buzatu <mihneabuzatu88@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
qemu-display = { path = "../qemu-display" }
|
||||
keycodemap = { path ="../keycodemap" }
|
||||
bytes = "1.4"
|
||||
rustls = { version = "0.21" }
|
||||
rustls-pemfile = "1.0"
|
||||
tokio = { version = "1.28", features = ["full"] }
|
||||
tokio-rustls = "0.24"
|
||||
anyhow = "1.0"
|
||||
zbus = { version = "3.0" }
|
||||
clap = { version = "3.2", features = ["derive"] }
|
||||
async-trait = "0.1"
|
||||
ironrdp = { path = "../../IronRDP/crates/ironrdp" }
|
||||
ironrdp-server = { path = "../../IronRDP/crates/ironrdp-server" }
|
56
qemu-rdp/src/args.rs
Normal file
56
qemu-rdp/src/args.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use clap::clap_derive::ValueEnum;
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||
pub enum SecurityProtocol {
|
||||
Ssl,
|
||||
Hybrid,
|
||||
HybridEx,
|
||||
}
|
||||
|
||||
impl From<SecurityProtocol> for ironrdp::pdu::nego::SecurityProtocol {
|
||||
fn from(value: SecurityProtocol) -> Self {
|
||||
match value {
|
||||
SecurityProtocol::Ssl => ironrdp::pdu::nego::SecurityProtocol::SSL,
|
||||
SecurityProtocol::Hybrid => ironrdp::pdu::nego::SecurityProtocol::HYBRID,
|
||||
SecurityProtocol::HybridEx => ironrdp::pdu::nego::SecurityProtocol::HYBRID_EX,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct ServerArgs {
|
||||
/// IP address
|
||||
#[clap(short, long, default_value = "127.0.0.1")]
|
||||
pub address: std::net::IpAddr,
|
||||
|
||||
/// IP port
|
||||
#[clap(short, long, default_value = "3389")]
|
||||
pub port: u16,
|
||||
|
||||
/// Specify the security protocols to use
|
||||
#[clap(long, value_enum, value_parser, default_value_t = SecurityProtocol::Ssl)]
|
||||
pub security_protocol: SecurityProtocol,
|
||||
|
||||
/// Path to tls certificate
|
||||
#[clap(short, long, value_parser)]
|
||||
pub cert: Option<String>,
|
||||
|
||||
/// Path to private key
|
||||
#[clap(short, long, value_parser)]
|
||||
pub key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct Args {
|
||||
#[clap(flatten)]
|
||||
pub server: ServerArgs,
|
||||
|
||||
/// DBUS address
|
||||
#[clap(short, long)]
|
||||
pub dbus_address: Option<String>,
|
||||
}
|
||||
|
||||
pub fn parse() -> Args {
|
||||
Args::parse()
|
||||
}
|
19
qemu-rdp/src/main.rs
Normal file
19
qemu-rdp/src/main.rs
Normal file
@ -0,0 +1,19 @@
|
||||
mod args;
|
||||
mod server;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), anyhow::Error> {
|
||||
let mut args = args::parse();
|
||||
|
||||
let dbus = match args.dbus_address.take() {
|
||||
None => zbus::Connection::session().await,
|
||||
Some(addr) => zbus::ConnectionBuilder::address(addr.as_str())?
|
||||
.build()
|
||||
.await
|
||||
}
|
||||
.expect("Failed to connect to DBus");
|
||||
|
||||
server::Server::new(dbus, args.server).run().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
118
qemu-rdp/src/server/display.rs
Normal file
118
qemu-rdp/src/server/display.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use anyhow::Result;
|
||||
use ironrdp::connector::DesktopSize;
|
||||
use qemu_display::{Console, ConsoleListenerHandler, Cursor, MouseSet, Scanout, Update};
|
||||
|
||||
use ironrdp_server::{BitmapUpdate, DisplayUpdate, PixelFormat, PixelOrder, RdpServerDisplay};
|
||||
|
||||
pub struct DisplayHandler {
|
||||
dbus: zbus::Connection,
|
||||
console: Console,
|
||||
receiver: tokio::sync::mpsc::Receiver<DisplayUpdate>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl RdpServerDisplay for DisplayHandler {
|
||||
async fn size(&mut self) -> DesktopSize {
|
||||
let width = self.console.proxy.width().await.unwrap() as u16;
|
||||
let height = self.console.proxy.height().await.unwrap() as u16;
|
||||
DesktopSize { height, width }
|
||||
}
|
||||
|
||||
async fn get_update(&mut self) -> Option<DisplayUpdate> {
|
||||
self.receiver.recv().await
|
||||
}
|
||||
}
|
||||
|
||||
struct Listener {
|
||||
sender: tokio::sync::mpsc::Sender<DisplayUpdate>,
|
||||
}
|
||||
|
||||
impl Listener {
|
||||
fn new(sender: tokio::sync::mpsc::Sender<DisplayUpdate>) -> Self {
|
||||
Self { sender }
|
||||
}
|
||||
|
||||
async fn send(&mut self, update: DisplayUpdate) {
|
||||
if let Err(e) = self.sender.send(update).await {
|
||||
println!("{:?}", e);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ConsoleListenerHandler for Listener {
|
||||
async fn scanout(&mut self, scanout: Scanout) {
|
||||
let bitmap = DisplayUpdate::Bitmap(BitmapUpdate {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: scanout.width,
|
||||
height: scanout.height,
|
||||
format: PixelFormat::RgbA32,
|
||||
order: PixelOrder::TopToBottom,
|
||||
data: scanout.data,
|
||||
});
|
||||
|
||||
self.send(bitmap).await;
|
||||
}
|
||||
|
||||
async fn update(&mut self, update: Update) {
|
||||
let bitmap = DisplayUpdate::Bitmap(BitmapUpdate {
|
||||
// TODO: fix scary conversion
|
||||
top: update.x as u32,
|
||||
left: update.y as u32,
|
||||
width: update.w as u32,
|
||||
height: update.h as u32,
|
||||
format: PixelFormat::RgbA32,
|
||||
order: PixelOrder::TopToBottom,
|
||||
data: update.data,
|
||||
});
|
||||
|
||||
self.send(bitmap).await;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn scanout_dmabuf(&mut self, _scanout: qemu_display::ScanoutDMABUF) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn update_dmabuf(&mut self, _update: qemu_display::UpdateDMABUF) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn disable(&mut self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn mouse_set(&mut self, _set: MouseSet) {
|
||||
println!("MOUSE SET");
|
||||
}
|
||||
|
||||
async fn cursor_define(&mut self, _cursor: Cursor) {
|
||||
println!("CURSOR DEFINE");
|
||||
}
|
||||
|
||||
fn disconnected(&mut self) {
|
||||
println!("DISCONNECTED!");
|
||||
}
|
||||
|
||||
fn interfaces(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayHandler {
|
||||
pub async fn connect(dbus: zbus::Connection) -> Result<Self> {
|
||||
let (sender, receiver) = tokio::sync::mpsc::channel::<DisplayUpdate>(32);
|
||||
let listener = Listener::new(sender);
|
||||
|
||||
let console = Console::new(&dbus, 0).await?;
|
||||
console.register_listener(listener).await?;
|
||||
|
||||
Ok(Self {
|
||||
dbus,
|
||||
console,
|
||||
receiver,
|
||||
})
|
||||
}
|
||||
}
|
68
qemu-rdp/src/server/input.rs
Normal file
68
qemu-rdp/src/server/input.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use qemu_display::{Console, KeyboardProxy, MouseButton, MouseProxy};
|
||||
|
||||
use ironrdp_server::{KeyboardEvent, MouseEvent, RdpServerInputHandler};
|
||||
|
||||
pub struct InputHandler<'a> {
|
||||
dbus: zbus::Connection,
|
||||
mouse: MouseProxy<'a>,
|
||||
keyboard: KeyboardProxy<'a>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> RdpServerInputHandler for InputHandler<'a> {
|
||||
async fn keyboard(&mut self, event: KeyboardEvent) {
|
||||
let result = match event {
|
||||
KeyboardEvent::Pressed(code) => {
|
||||
self.keyboard.press(code as u32).await
|
||||
}
|
||||
|
||||
KeyboardEvent::Released(code) => {
|
||||
self.keyboard.release(code as u32).await
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = result {
|
||||
eprintln!("keyboard error: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
async fn mouse(&mut self, event: MouseEvent) {
|
||||
let result = match event {
|
||||
MouseEvent::Move { x, y } => {
|
||||
self.mouse.set_abs_position(x as u32, y as u32).await
|
||||
}
|
||||
MouseEvent::RightPressed => {
|
||||
self.mouse.press(MouseButton::Right).await
|
||||
}
|
||||
MouseEvent::RightReleased => {
|
||||
self.mouse.release(MouseButton::Right).await
|
||||
}
|
||||
MouseEvent::LeftPressed => {
|
||||
self.mouse.press(MouseButton::Left).await
|
||||
}
|
||||
MouseEvent::LeftReleased => {
|
||||
self.mouse.release(MouseButton::Left).await
|
||||
}
|
||||
MouseEvent::Scroll => {
|
||||
unimplemented!()
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = result {
|
||||
eprintln!("keyboard error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> InputHandler<'a> {
|
||||
pub async fn connect(dbus: zbus::Connection) -> anyhow::Result<InputHandler<'a>> {
|
||||
let console = Console::new(&dbus, 0).await?;
|
||||
|
||||
Ok(Self {
|
||||
dbus,
|
||||
mouse: console.mouse,
|
||||
keyboard: console.keyboard,
|
||||
})
|
||||
}
|
||||
}
|
60
qemu-rdp/src/server/mod.rs
Normal file
60
qemu-rdp/src/server/mod.rs
Normal file
@ -0,0 +1,60 @@
|
||||
mod display;
|
||||
mod input;
|
||||
|
||||
use std::{fs::File, io::BufReader, sync::Arc};
|
||||
|
||||
use anyhow::Error;
|
||||
use ironrdp_server::RdpServer;
|
||||
use rustls::ServerConfig;
|
||||
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
|
||||
use crate::args::ServerArgs;
|
||||
|
||||
use display::DisplayHandler;
|
||||
use input::InputHandler;
|
||||
|
||||
pub struct Server {
|
||||
dbus: zbus::Connection,
|
||||
args: ServerArgs,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(dbus: zbus::Connection, args: ServerArgs) -> Self {
|
||||
Self { dbus, args }
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> Result<(), Error> {
|
||||
let tls = self
|
||||
.args
|
||||
.cert
|
||||
.as_ref()
|
||||
.zip(self.args.key.as_ref())
|
||||
.map(|(cert, key)| acceptor(cert, key).unwrap());
|
||||
|
||||
let handler = InputHandler::connect(self.dbus.clone()).await?;
|
||||
let display = DisplayHandler::connect(self.dbus.clone()).await?;
|
||||
|
||||
let mut server = RdpServer::builder()
|
||||
.with_addr((self.args.address, self.args.port))
|
||||
.with_ssl(tls.unwrap())
|
||||
.with_io_handler(handler)
|
||||
.with_display_handler(display)
|
||||
.build();
|
||||
|
||||
server.run().await
|
||||
}
|
||||
}
|
||||
|
||||
fn acceptor(cert_path: &str, key_path: &str) -> Result<TlsAcceptor, Error> {
|
||||
let cert = certs(&mut BufReader::new(File::open(cert_path)?))?[0].clone();
|
||||
let key = pkcs8_private_keys(&mut BufReader::new(File::open(key_path)?))?[0].clone();
|
||||
|
||||
let server_config = ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(vec![rustls::Certificate(cert)], rustls::PrivateKey(key))
|
||||
.expect("bad certificate/key");
|
||||
|
||||
Ok(TlsAcceptor::from(Arc::new(server_config)))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user