mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-07 11:59:59 +03:00
ask the user to accept unauthorized devices
This commit is contained in:
102
lan-mouse-gtk/resources/authorization_window.ui
Normal file
102
lan-mouse-gtk/resources/authorization_window.ui
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<requires lib="libadwaita" version="1.0"/>
|
||||
<template class="AuthorizationWindow" parent="AdwWindow">
|
||||
<property name="modal">True</property>
|
||||
<property name="width-request">180</property>
|
||||
<property name="default-width">180</property>
|
||||
<property name="height-request">180</property>
|
||||
<property name="default-height">180</property>
|
||||
<property name="title" translatable="yes">Unauthorized Device</property>
|
||||
<property name="content">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="vexpand">True</property>
|
||||
<child type="top">
|
||||
<object class="AdwHeaderBar">
|
||||
<style>
|
||||
<class name="flat"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">30</property>
|
||||
<property name="margin-start">30</property>
|
||||
<property name="margin-end">30</property>
|
||||
<property name="margin-top">30</property>
|
||||
<property name="margin-bottom">30</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">An unauthorized Device is trying to connect. Do you want to authorize this Device?</property>
|
||||
<property name="width-request">100</property>
|
||||
<property name="wrap">word-wrap</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title">sha256 fingerprint</property>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="child">
|
||||
<object class="GtkLabel" id="fingerprint">
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="hexpand">False</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap-mode">word-char</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="margin-top">10</property>
|
||||
<property name="margin-bottom">10</property>
|
||||
<property name="margin-start">10</property>
|
||||
<property name="margin-end">10</property>
|
||||
<property name="width-chars">64</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="margin-start">30</property>
|
||||
<property name="margin-end">30</property>
|
||||
<property name="margin-top">30</property>
|
||||
<property name="margin-bottom">30</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="spacing">30</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="valign">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<signal name="clicked" handler="handle_cancel" swapped="true"/>
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="can-shrink">True</property>
|
||||
<property name="height-request">50</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="confirm_button">
|
||||
<signal name="clicked" handler="handle_confirm" swapped="true"/>
|
||||
<property name="label" translatable="yes">Authorize</property>
|
||||
<property name="can-shrink">True</property>
|
||||
<property name="height-request">50</property>
|
||||
<property name="hexpand">True</property>
|
||||
<style>
|
||||
<class name="destructive-action"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
@@ -2,6 +2,7 @@
|
||||
<gresources>
|
||||
<gresource prefix="/de/feschber/LanMouse">
|
||||
<file compressed="true" preprocess="xml-stripblanks">window.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">authorization_window.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">fingerprint_window.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">client_row.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">key_row.ui</file>
|
||||
|
||||
19
lan-mouse-gtk/src/authorization_window.rs
Normal file
19
lan-mouse-gtk/src/authorization_window.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
mod imp;
|
||||
|
||||
use glib::Object;
|
||||
use gtk::{gio, glib, subclass::prelude::ObjectSubclassIsExt};
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct AuthorizationWindow(ObjectSubclass<imp::AuthorizationWindow>)
|
||||
@extends adw::Window, gtk::Window, gtk::Widget,
|
||||
@implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable,
|
||||
gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager;
|
||||
}
|
||||
|
||||
impl AuthorizationWindow {
|
||||
pub(crate) fn new(fingerprint: &str) -> Self {
|
||||
let window: Self = Object::builder().build();
|
||||
window.imp().set_fingerprint(fingerprint);
|
||||
window
|
||||
}
|
||||
}
|
||||
75
lan-mouse-gtk/src/authorization_window/imp.rs
Normal file
75
lan-mouse-gtk/src/authorization_window/imp.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use adw::prelude::*;
|
||||
use adw::subclass::prelude::*;
|
||||
use glib::subclass::InitializingObject;
|
||||
use gtk::{
|
||||
glib::{self, subclass::Signal},
|
||||
template_callbacks, Button, CompositeTemplate, Label,
|
||||
};
|
||||
|
||||
#[derive(CompositeTemplate, Default)]
|
||||
#[template(resource = "/de/feschber/LanMouse/authorization_window.ui")]
|
||||
pub struct AuthorizationWindow {
|
||||
#[template_child]
|
||||
pub fingerprint: TemplateChild<Label>,
|
||||
#[template_child]
|
||||
pub cancel_button: TemplateChild<Button>,
|
||||
#[template_child]
|
||||
pub confirm_button: TemplateChild<Button>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for AuthorizationWindow {
|
||||
const NAME: &'static str = "AuthorizationWindow";
|
||||
const ABSTRACT: bool = false;
|
||||
|
||||
type Type = super::AuthorizationWindow;
|
||||
type ParentType = adw::Window;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
#[template_callbacks]
|
||||
impl AuthorizationWindow {
|
||||
#[template_callback]
|
||||
fn handle_confirm(&self, _button: Button) {
|
||||
let fp = self.fingerprint.text().as_str().trim().to_owned();
|
||||
self.obj().emit_by_name("confirm-clicked", &[&fp])
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn handle_cancel(&self, _: Button) {
|
||||
self.obj().emit_by_name("cancel-clicked", &[])
|
||||
}
|
||||
|
||||
pub(super) fn set_fingerprint(&self, fingerprint: &str) {
|
||||
self.fingerprint.set_text(fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for AuthorizationWindow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
|
||||
SIGNALS.get_or_init(|| {
|
||||
vec![
|
||||
Signal::builder("confirm-clicked")
|
||||
.param_types([String::static_type()])
|
||||
.build(),
|
||||
Signal::builder("cancel-clicked").build(),
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for AuthorizationWindow {}
|
||||
impl WindowImpl for AuthorizationWindow {}
|
||||
impl ApplicationWindowImpl for AuthorizationWindow {}
|
||||
impl AdwWindowImpl for AuthorizationWindow {}
|
||||
@@ -1,7 +1,7 @@
|
||||
mod imp;
|
||||
|
||||
use glib::Object;
|
||||
use gtk::{gio, glib};
|
||||
use gtk::{gio, glib, prelude::ObjectExt, subclass::prelude::ObjectSubclassIsExt};
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct FingerprintWindow(ObjectSubclass<imp::FingerprintWindow>)
|
||||
@@ -11,8 +11,12 @@ glib::wrapper! {
|
||||
}
|
||||
|
||||
impl FingerprintWindow {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub(crate) fn new(fingerprint: Option<String>) -> Self {
|
||||
let window: Self = Object::builder().build();
|
||||
if let Some(fp) = fingerprint {
|
||||
window.imp().fingerprint.set_property("text", fp);
|
||||
window.imp().fingerprint.set_property("editable", false);
|
||||
}
|
||||
window
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod authorization_window;
|
||||
mod client_object;
|
||||
mod client_row;
|
||||
mod fingerprint_window;
|
||||
@@ -146,8 +147,21 @@ fn build_ui(app: &Application) {
|
||||
FrontendEvent::EmulationStatus(s) => window.set_emulation(s.into()),
|
||||
FrontendEvent::AuthorizedUpdated(keys) => window.set_authorized_keys(keys),
|
||||
FrontendEvent::PublicKeyFingerprint(fp) => window.set_pk_fp(&fp),
|
||||
FrontendEvent::IncomingConnected(_fingerprint, addr, pos) => {
|
||||
window.show_toast(format!("device connected: {addr} ({pos})").as_str());
|
||||
FrontendEvent::ConnectionAttempt { fingerprint } => {
|
||||
window.request_authorization(&fingerprint);
|
||||
}
|
||||
FrontendEvent::DeviceConnected {
|
||||
fingerprint: _,
|
||||
addr,
|
||||
} => {
|
||||
window.show_toast(format!("device connected: {addr}").as_str());
|
||||
}
|
||||
FrontendEvent::DeviceEntered {
|
||||
fingerprint: _,
|
||||
addr,
|
||||
pos,
|
||||
} => {
|
||||
window.show_toast(format!("device entered: {addr} ({pos})").as_str());
|
||||
}
|
||||
FrontendEvent::IncomingDisconnected(addr) => {
|
||||
window.show_toast(format!("{addr} disconnected").as_str());
|
||||
|
||||
@@ -16,7 +16,10 @@ use lan_mouse_ipc::{
|
||||
DEFAULT_PORT,
|
||||
};
|
||||
|
||||
use crate::{fingerprint_window::FingerprintWindow, key_object::KeyObject, key_row::KeyRow};
|
||||
use crate::{
|
||||
authorization_window::AuthorizationWindow, fingerprint_window::FingerprintWindow,
|
||||
key_object::KeyObject, key_row::KeyRow,
|
||||
};
|
||||
|
||||
use super::{client_object::ClientObject, client_row::ClientRow};
|
||||
|
||||
@@ -394,8 +397,8 @@ impl Window {
|
||||
self.request(FrontendRequest::Create);
|
||||
}
|
||||
|
||||
fn open_fingerprint_dialog(&self) {
|
||||
let window = FingerprintWindow::new();
|
||||
fn open_fingerprint_dialog(&self, fp: Option<String>) {
|
||||
let window = FingerprintWindow::new(fp);
|
||||
window.set_transient_for(Some(self));
|
||||
window.connect_closure(
|
||||
"confirm-clicked",
|
||||
@@ -469,4 +472,29 @@ impl Window {
|
||||
pub(super) fn set_pk_fp(&self, fingerprint: &str) {
|
||||
self.imp().fingerprint_row.set_subtitle(fingerprint);
|
||||
}
|
||||
|
||||
pub(super) fn request_authorization(&self, fingerprint: &str) {
|
||||
let window = AuthorizationWindow::new(fingerprint);
|
||||
window.set_transient_for(Some(self));
|
||||
window.connect_closure(
|
||||
"confirm-clicked",
|
||||
false,
|
||||
closure_local!(
|
||||
#[strong(rename_to = parent)]
|
||||
self,
|
||||
move |w: AuthorizationWindow, fp: String| {
|
||||
w.close();
|
||||
parent.open_fingerprint_dialog(Some(fp));
|
||||
}
|
||||
),
|
||||
);
|
||||
window.connect_closure(
|
||||
"cancel-clicked",
|
||||
false,
|
||||
closure_local!(move |w: AuthorizationWindow| {
|
||||
w.close();
|
||||
}),
|
||||
);
|
||||
window.present();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ impl Window {
|
||||
|
||||
#[template_callback]
|
||||
fn handle_add_cert_fingerprint(&self, _button: &Button) {
|
||||
self.obj().open_fingerprint_dialog();
|
||||
self.obj().open_fingerprint_dialog(None);
|
||||
}
|
||||
|
||||
pub fn set_port(&self, port: u16) {
|
||||
|
||||
@@ -202,10 +202,21 @@ pub enum FrontendEvent {
|
||||
AuthorizedUpdated(HashMap<String, String>),
|
||||
/// public key fingerprint of this device
|
||||
PublicKeyFingerprint(String),
|
||||
/// incoming connected
|
||||
IncomingConnected(String, SocketAddr, Position),
|
||||
/// new device connected
|
||||
DeviceConnected {
|
||||
addr: SocketAddr,
|
||||
fingerprint: String,
|
||||
},
|
||||
/// incoming device entered the screen
|
||||
DeviceEntered {
|
||||
fingerprint: String,
|
||||
addr: SocketAddr,
|
||||
pos: Position,
|
||||
},
|
||||
/// incoming disconnected
|
||||
IncomingDisconnected(SocketAddr),
|
||||
/// failed connection attempt (approval for fingerprint required)
|
||||
ConnectionAttempt { fingerprint: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::listen::{LanMouseListener, ListenerCreationError};
|
||||
use crate::listen::{LanMouseListener, ListenEvent, ListenerCreationError};
|
||||
use futures::StreamExt;
|
||||
use input_emulation::{EmulationHandle, InputEmulation, InputEmulationError};
|
||||
use input_event::Event;
|
||||
@@ -24,8 +24,15 @@ pub(crate) struct Emulation {
|
||||
}
|
||||
|
||||
pub(crate) enum EmulationEvent {
|
||||
/// new connection
|
||||
Connected {
|
||||
addr: SocketAddr,
|
||||
fingerprint: String,
|
||||
},
|
||||
ConnectionAttempt {
|
||||
fingerprint: String,
|
||||
},
|
||||
/// new connection
|
||||
Entered {
|
||||
/// address of the connection
|
||||
addr: SocketAddr,
|
||||
/// position of the connection
|
||||
@@ -34,7 +41,9 @@ pub(crate) enum EmulationEvent {
|
||||
fingerprint: String,
|
||||
},
|
||||
/// connection closed
|
||||
Disconnected { addr: SocketAddr },
|
||||
Disconnected {
|
||||
addr: SocketAddr,
|
||||
},
|
||||
/// the port of the listener has changed
|
||||
PortChanged(Result<u16, ListenerCreationError>),
|
||||
/// emulation was disabled
|
||||
@@ -121,31 +130,36 @@ impl ListenTask {
|
||||
let mut last_response = HashMap::new();
|
||||
loop {
|
||||
select! {
|
||||
e = self.listener.next() => {
|
||||
let (event, addr) = match e {
|
||||
Some(e) => e,
|
||||
None => break,
|
||||
};
|
||||
log::trace!("{event} <-<-<-<-<- {addr}");
|
||||
last_response.insert(addr, Instant::now());
|
||||
match event {
|
||||
ProtoEvent::Enter(pos) => {
|
||||
if let Some(fingerprint) = self.listener.get_certificate_fingerprint(addr).await {
|
||||
log::info!("releasing capture: {addr} entered this device");
|
||||
self.event_tx.send(EmulationEvent::ReleaseNotify).expect("channel closed");
|
||||
self.listener.reply(addr, ProtoEvent::Ack(0)).await;
|
||||
self.event_tx.send(EmulationEvent::Connected{addr, pos: to_ipc_pos(pos), fingerprint}).expect("channel closed");
|
||||
e = self.listener.next() => {match e {
|
||||
Some(ListenEvent::Msg { event, addr }) => {
|
||||
log::trace!("{event} <-<-<-<-<- {addr}");
|
||||
last_response.insert(addr, Instant::now());
|
||||
match event {
|
||||
ProtoEvent::Enter(pos) => {
|
||||
if let Some(fingerprint) = self.listener.get_certificate_fingerprint(addr).await {
|
||||
log::info!("releasing capture: {addr} entered this device");
|
||||
self.event_tx.send(EmulationEvent::ReleaseNotify).expect("channel closed");
|
||||
self.listener.reply(addr, ProtoEvent::Ack(0)).await;
|
||||
self.event_tx.send(EmulationEvent::Entered{addr, pos: to_ipc_pos(pos), fingerprint}).expect("channel closed");
|
||||
}
|
||||
}
|
||||
ProtoEvent::Leave(_) => {
|
||||
self.emulation_proxy.remove(addr);
|
||||
self.listener.reply(addr, ProtoEvent::Ack(0)).await;
|
||||
}
|
||||
ProtoEvent::Input(event) => self.emulation_proxy.consume(event, addr),
|
||||
ProtoEvent::Ping => self.listener.reply(addr, ProtoEvent::Pong(self.emulation_proxy.emulation_active.get())).await,
|
||||
_ => {}
|
||||
}
|
||||
ProtoEvent::Leave(_) => {
|
||||
self.emulation_proxy.remove(addr);
|
||||
self.listener.reply(addr, ProtoEvent::Ack(0)).await;
|
||||
}
|
||||
ProtoEvent::Input(event) => self.emulation_proxy.consume(event, addr),
|
||||
ProtoEvent::Ping => self.listener.reply(addr, ProtoEvent::Pong(self.emulation_proxy.emulation_active.get())).await,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Some(ListenEvent::Accept { addr, fingerprint }) => {
|
||||
self.event_tx.send(EmulationEvent::Connected { addr, fingerprint }).expect("channel closed");
|
||||
}
|
||||
Some(ListenEvent::Rejected { fingerprint }) => {
|
||||
self.event_tx.send(EmulationEvent::ConnectionAttempt { fingerprint }).expect("channel closed");
|
||||
}
|
||||
None => break
|
||||
}}
|
||||
event = self.emulation_proxy.event() => {
|
||||
self.event_tx.send(event).expect("channel closed");
|
||||
}
|
||||
|
||||
162
src/listen.rs
162
src/listen.rs
@@ -3,15 +3,15 @@ use lan_mouse_proto::{ProtoEvent, MAX_EVENT_SIZE};
|
||||
use local_channel::mpsc::{channel, Receiver, Sender};
|
||||
use rustls::pki_types::CertificateDer;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{HashMap, VecDeque},
|
||||
net::SocketAddr,
|
||||
rc::Rc,
|
||||
sync::{Arc, RwLock},
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
time::Duration,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use tokio::{
|
||||
sync::Mutex,
|
||||
sync::Mutex as AsyncMutex,
|
||||
task::{spawn_local, JoinHandle},
|
||||
};
|
||||
use webrtc_dtls::{
|
||||
@@ -34,11 +34,26 @@ pub enum ListenerCreationError {
|
||||
|
||||
type ArcConn = Arc<dyn Conn + Send + Sync>;
|
||||
|
||||
pub(crate) enum ListenEvent {
|
||||
Msg {
|
||||
event: ProtoEvent,
|
||||
addr: SocketAddr,
|
||||
},
|
||||
Accept {
|
||||
addr: SocketAddr,
|
||||
fingerprint: String,
|
||||
},
|
||||
Rejected {
|
||||
fingerprint: String,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) struct LanMouseListener {
|
||||
listen_rx: Receiver<(ProtoEvent, SocketAddr)>,
|
||||
listen_tx: Sender<(ProtoEvent, SocketAddr)>,
|
||||
listen_rx: Receiver<ListenEvent>,
|
||||
listen_tx: Sender<ListenEvent>,
|
||||
listen_task: JoinHandle<()>,
|
||||
conns: Rc<Mutex<Vec<(SocketAddr, ArcConn)>>>,
|
||||
connection_attempts: Arc<Mutex<VecDeque<String>>>,
|
||||
conns: Rc<AsyncMutex<Vec<(SocketAddr, ArcConn)>>>,
|
||||
request_port_change: Sender<u16>,
|
||||
port_changed: Receiver<Result<u16, ListenerCreationError>>,
|
||||
}
|
||||
@@ -58,26 +73,35 @@ impl LanMouseListener {
|
||||
let (listen_tx, listen_rx) = channel();
|
||||
let (request_port_change, mut request_port_change_rx) = channel();
|
||||
let (port_changed_tx, port_changed) = channel();
|
||||
let connection_attempts: Arc<Mutex<VecDeque<String>>> = Default::default();
|
||||
|
||||
let authorized = authorized_keys.clone();
|
||||
let verify_peer_certificate: Option<VerifyPeerCertificateFn> = Some(Arc::new(
|
||||
move |certs: &[Vec<u8>], _chains: &[CertificateDer<'static>]| {
|
||||
assert!(certs.len() == 1);
|
||||
let fingerprints = certs
|
||||
.iter()
|
||||
.map(|c| crypto::generate_fingerprint(c))
|
||||
.collect::<Vec<_>>();
|
||||
if authorized
|
||||
.read()
|
||||
.expect("lock")
|
||||
.contains_key(&fingerprints[0])
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(webrtc_dtls::Error::ErrVerifyDataMismatch)
|
||||
}
|
||||
},
|
||||
));
|
||||
let verify_peer_certificate: Option<VerifyPeerCertificateFn> = {
|
||||
let connection_attempts = connection_attempts.clone();
|
||||
Some(Arc::new(
|
||||
move |certs: &[Vec<u8>], _chains: &[CertificateDer<'static>]| {
|
||||
assert!(certs.len() == 1);
|
||||
let fingerprints = certs
|
||||
.iter()
|
||||
.map(|c| crypto::generate_fingerprint(c))
|
||||
.collect::<Vec<_>>();
|
||||
if authorized
|
||||
.read()
|
||||
.expect("lock")
|
||||
.contains_key(&fingerprints[0])
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
let fingerprint = fingerprints.into_iter().next().expect("fingerprint");
|
||||
connection_attempts
|
||||
.lock()
|
||||
.expect("lock")
|
||||
.push_back(fingerprint);
|
||||
Err(webrtc_dtls::Error::ErrVerifyDataMismatch)
|
||||
}
|
||||
},
|
||||
))
|
||||
};
|
||||
let cfg = Config {
|
||||
certificates: vec![cert.clone()],
|
||||
extended_master_secret: ExtendedMasterSecretType::Require,
|
||||
@@ -89,46 +113,60 @@ impl LanMouseListener {
|
||||
let listen_addr = SocketAddr::new("0.0.0.0".parse().expect("invalid ip"), port);
|
||||
let mut listener = listen(listen_addr, cfg.clone()).await?;
|
||||
|
||||
let conns: Rc<Mutex<Vec<(SocketAddr, ArcConn)>>> = Rc::new(Mutex::new(Vec::new()));
|
||||
let conns: Rc<AsyncMutex<Vec<(SocketAddr, ArcConn)>>> =
|
||||
Rc::new(AsyncMutex::new(Vec::new()));
|
||||
|
||||
let conns_clone = conns.clone();
|
||||
let tx = listen_tx.clone();
|
||||
let listen_task: JoinHandle<()> = spawn_local(async move {
|
||||
loop {
|
||||
let sleep = tokio::time::sleep(Duration::from_secs(2));
|
||||
tokio::select! {
|
||||
/* workaround for https://github.com/webrtc-rs/webrtc/issues/614 */
|
||||
_ = sleep => continue,
|
||||
c = listener.accept() => match c {
|
||||
Ok((conn, addr)) => {
|
||||
log::info!("dtls client connected, ip: {addr}");
|
||||
let mut conns = conns_clone.lock().await;
|
||||
conns.push((addr, conn.clone()));
|
||||
spawn_local(read_loop(conns_clone.clone(), addr, conn, tx.clone()));
|
||||
},
|
||||
Err(e) => log::warn!("accept: {e}"),
|
||||
},
|
||||
port = request_port_change_rx.recv() => {
|
||||
let port = port.expect("channel closed");
|
||||
let listen_addr = SocketAddr::new("0.0.0.0".parse().expect("invalid ip"), port);
|
||||
match listen(listen_addr, cfg.clone()).await {
|
||||
Ok(new_listener) => {
|
||||
let _ = listener.close().await;
|
||||
listener = new_listener;
|
||||
port_changed_tx.send(Ok(port)).expect("channel closed");
|
||||
}
|
||||
let listen_task: JoinHandle<()> = {
|
||||
let listen_tx = listen_tx.clone();
|
||||
let connection_attempts = connection_attempts.clone();
|
||||
spawn_local(async move {
|
||||
loop {
|
||||
let sleep = tokio::time::sleep(Duration::from_secs(2));
|
||||
tokio::select! {
|
||||
/* workaround for https://github.com/webrtc-rs/webrtc/issues/614 */
|
||||
_ = sleep => continue,
|
||||
c = listener.accept() => match c {
|
||||
Ok((conn, addr)) => {
|
||||
log::info!("dtls client connected, ip: {addr}");
|
||||
let mut conns = conns_clone.lock().await;
|
||||
conns.push((addr, conn.clone()));
|
||||
let dtls_conn: &DTLSConn = conn.as_any().downcast_ref().expect("dtls conn");
|
||||
let certs = dtls_conn.connection_state().await.peer_certificates;
|
||||
let cert = certs.first().expect("cert");
|
||||
let fingerprint = crypto::generate_fingerprint(cert);
|
||||
listen_tx.send(ListenEvent::Accept { addr, fingerprint }).expect("channel closed");
|
||||
spawn_local(read_loop(conns_clone.clone(), addr, conn, listen_tx.clone()));
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!("unable to change port: {e}");
|
||||
port_changed_tx.send(Err(e.into())).expect("channel closed");
|
||||
if let Some(fingerprint) = connection_attempts.lock().expect("lock").pop_front() {
|
||||
listen_tx.send(ListenEvent::Rejected { fingerprint }).expect("channel closed");
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
port = request_port_change_rx.recv() => {
|
||||
let port = port.expect("channel closed");
|
||||
let listen_addr = SocketAddr::new("0.0.0.0".parse().expect("invalid ip"), port);
|
||||
match listen(listen_addr, cfg.clone()).await {
|
||||
Ok(new_listener) => {
|
||||
let _ = listener.close().await;
|
||||
listener = new_listener;
|
||||
port_changed_tx.send(Ok(port)).expect("channel closed");
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("unable to change port: {e}");
|
||||
port_changed_tx.send(Err(e.into())).expect("channel closed");
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
conns,
|
||||
connection_attempts,
|
||||
listen_rx,
|
||||
listen_tx,
|
||||
listen_task,
|
||||
@@ -186,7 +224,7 @@ impl LanMouseListener {
|
||||
}
|
||||
|
||||
impl Stream for LanMouseListener {
|
||||
type Item = (ProtoEvent, SocketAddr);
|
||||
type Item = ListenEvent;
|
||||
|
||||
fn poll_next(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
@@ -197,16 +235,18 @@ impl Stream for LanMouseListener {
|
||||
}
|
||||
|
||||
async fn read_loop(
|
||||
conns: Rc<Mutex<Vec<(SocketAddr, ArcConn)>>>,
|
||||
conns: Rc<AsyncMutex<Vec<(SocketAddr, ArcConn)>>>,
|
||||
addr: SocketAddr,
|
||||
conn: ArcConn,
|
||||
dtls_tx: Sender<(ProtoEvent, SocketAddr)>,
|
||||
dtls_tx: Sender<ListenEvent>,
|
||||
) -> Result<(), Error> {
|
||||
let mut b = [0u8; MAX_EVENT_SIZE];
|
||||
|
||||
while conn.recv(&mut b).await.is_ok() {
|
||||
match b.try_into() {
|
||||
Ok(event) => dtls_tx.send((event, addr)).expect("channel closed"),
|
||||
Ok(event) => dtls_tx
|
||||
.send(ListenEvent::Msg { event, addr })
|
||||
.expect("channel closed"),
|
||||
Err(e) => {
|
||||
log::warn!("error receiving event: {e}");
|
||||
break;
|
||||
|
||||
@@ -211,7 +211,10 @@ impl Service {
|
||||
|
||||
fn handle_emulation_event(&mut self, event: EmulationEvent) {
|
||||
match event {
|
||||
EmulationEvent::Connected {
|
||||
EmulationEvent::ConnectionAttempt { fingerprint } => {
|
||||
self.notify_frontend(FrontendEvent::ConnectionAttempt { fingerprint });
|
||||
}
|
||||
EmulationEvent::Entered {
|
||||
addr,
|
||||
pos,
|
||||
fingerprint,
|
||||
@@ -219,7 +222,11 @@ impl Service {
|
||||
// check if already registered
|
||||
if !self.incoming_conns.contains(&addr) {
|
||||
self.add_incoming(addr, pos, fingerprint.clone());
|
||||
self.notify_frontend(FrontendEvent::IncomingConnected(fingerprint, addr, pos));
|
||||
self.notify_frontend(FrontendEvent::DeviceEntered {
|
||||
fingerprint,
|
||||
addr,
|
||||
pos,
|
||||
});
|
||||
} else {
|
||||
self.update_incoming(addr, pos, fingerprint);
|
||||
}
|
||||
@@ -246,6 +253,9 @@ impl Service {
|
||||
self.notify_frontend(FrontendEvent::EmulationStatus(self.emulation_status));
|
||||
}
|
||||
EmulationEvent::ReleaseNotify => self.capture.release(),
|
||||
EmulationEvent::Connected { addr, fingerprint } => {
|
||||
self.notify_frontend(FrontendEvent::DeviceConnected { addr, fingerprint });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +357,11 @@ impl Service {
|
||||
self.remove_incoming(addr);
|
||||
self.add_incoming(addr, pos, fingerprint.clone());
|
||||
self.notify_frontend(FrontendEvent::IncomingDisconnected(addr));
|
||||
self.notify_frontend(FrontendEvent::IncomingConnected(fingerprint, addr, pos));
|
||||
self.notify_frontend(FrontendEvent::DeviceEntered {
|
||||
fingerprint,
|
||||
addr,
|
||||
pos,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user