Compare commits

..

2 commits

Author SHA1 Message Date
5a7c4aa781 Enable biased again 2025-03-09 16:51:03 -04:00
1f4ca74cf9 [DNM] Write to sockets in order, instead of out of order 2025-03-09 16:39:46 -04:00
7 changed files with 219 additions and 271 deletions

View file

@ -52,32 +52,6 @@ This repo contains an example configuration at `config.toml` that allows a few b
desktop apps to function. It also demonstrates the use of `ask_cmd` and `notify_cmd` by defining filters on clipboard-related desktop apps to function. It also demonstrates the use of `ask_cmd` and `notify_cmd` by defining filters on clipboard-related
requests. Detailed explanation of the configuration format is also contained in the example. requests. Detailed explanation of the configuration format is also contained in the example.
To launch a program under `wl-mitm`, set its `WAYLAND_DISPLAY` env variable to whatever `listen` is under `[socket]` in `config.toml`.
Note that you may want to use another container and pass _only_ the `wl-mitm`'d socket through for proper isolation.
A Word on Filtering
---
`wl-mitm` gives you a ton of flexibility on filtering. In particular, you are allowed to filter _any_ request on _any_ object
in `config.toml`.
However, please keep in mind that while most requests can be filtered, Wayland (and most of its protocols) is not designed to handle
this type of filtering. While a lot of requests can be filtered without consequences, there are a significant number of them that
will result in irrecoverable de-sync between the client and the server. For example, _any_ message that creates a new object ID
(of the type `new_id`) _will_ result in undefined behavior if filtered.
`wl-mitm` does provide the ability to _reject_ a request with an explicit error. Unfortunately, errors in Wayland are usually
fatal, and clients are not designed to recover from them.
The most reliable (and the only protocol-compliant) way to prevent a client from using certain features is to block the
entire global object where that feature resides. These are usually "manager" objects that live in the global registry,
and can be blocked in `allowed_globals` inside `config.toml`. Each extension protocol has to expose _some_ manager object
in order for clients to access their functionalities, and thus blocking them there will prevent clients from even knowing
their existence. However, globals can't be selectively blocked using `ask_cmd`, because clients usually bind to them as soon
as they start.
Since `notify_cmd` never blocks a request, it is safe to use on _any_ request filter.
XWayland XWayland
--- ---

View file

@ -206,44 +206,33 @@ fn handle_interface(
let mut event_opcode = 0; let mut event_opcode = 0;
let mut request_opcode = 0; let mut request_opcode = 0;
let mut add_msg = |reader: &mut quick_xml::Reader<&[u8]>,
e: quick_xml::events::BytesStart<'_>,
is_empty: bool| {
let start_tag =
str::from_utf8(e.local_name().into_inner()).expect("Unable to parse start tag");
if start_tag == "event" {
// An event! Increment our opcode tracker for it!
event_opcode += 1;
msgs.push(handle_request_or_event(
reader,
event_opcode - 1,
WlMsgType::Event,
interface_name_snake,
e,
is_empty,
));
} else if start_tag == "request" {
// A request! Increment our opcode tracker for it!
request_opcode += 1;
msgs.push(handle_request_or_event(
reader,
request_opcode - 1,
WlMsgType::Request,
interface_name_snake,
e,
is_empty,
));
};
};
loop { loop {
match reader.read_event().expect("Unable to parse XML file") { match reader.read_event().expect("Unable to parse XML file") {
Event::Eof => panic!("Unexpected EOF"), Event::Eof => panic!("Unexpected EOF"),
Event::Start(e) => { Event::Start(e) => {
add_msg(reader, e, false); let start_tag =
} str::from_utf8(e.local_name().into_inner()).expect("Unable to parse start tag");
Event::Empty(e) => { if start_tag == "event" {
add_msg(reader, e, true); // An event! Increment our opcode tracker for it!
event_opcode += 1;
msgs.push(handle_request_or_event(
reader,
event_opcode - 1,
WlMsgType::Event,
interface_name_snake,
e,
));
} else if start_tag == "request" {
// A request! Increment our opcode tracker for it!
request_opcode += 1;
msgs.push(handle_request_or_event(
reader,
request_opcode - 1,
WlMsgType::Request,
interface_name_snake,
e,
));
};
} }
Event::End(e) if e.local_name() == start.local_name() => break, Event::End(e) if e.local_name() == start.local_name() => break,
_ => continue, _ => continue,
@ -262,7 +251,6 @@ fn handle_request_or_event(
msg_type: WlMsgType, msg_type: WlMsgType,
interface_name_snake: &str, interface_name_snake: &str,
start: quick_xml::events::BytesStart<'_>, start: quick_xml::events::BytesStart<'_>,
is_empty: bool,
) -> WlMsg { ) -> WlMsg {
let name_attr = start let name_attr = start
.attributes() .attributes()
@ -290,80 +278,77 @@ fn handle_request_or_event(
// Load arguments and their types from XML // Load arguments and their types from XML
let mut args: Vec<(String, WlArgType)> = Vec::new(); let mut args: Vec<(String, WlArgType)> = Vec::new();
if !is_empty { loop {
loop { match reader.read_event().expect("Unable to parse XML file") {
match reader.read_event().expect("Unable to parse XML file") { Event::Eof => panic!("Unexpected EOF"),
Event::Eof => panic!("Unexpected EOF"), Event::Empty(e)
Event::Empty(e) if str::from_utf8(e.local_name().into_inner()).expect("utf8 encoding error")
if str::from_utf8(e.local_name().into_inner()) == "arg" =>
.expect("utf8 encoding error") {
== "arg" => let mut name: Option<String> = None;
{ let mut tt: Option<WlArgType> = None;
let mut name: Option<String> = None; let mut interface_name: Option<String> = None;
let mut tt: Option<WlArgType> = None;
let mut interface_name: Option<String> = None;
for attr in e.attributes() { for attr in e.attributes() {
let attr = attr.expect("attr parsing error"); let attr = attr.expect("attr parsing error");
let attr_name = str::from_utf8(attr.key.local_name().into_inner()) let attr_name = str::from_utf8(attr.key.local_name().into_inner())
.expect("utf8 encoding error"); .expect("utf8 encoding error");
if attr_name == "name" { if attr_name == "name" {
name = Some( name = Some(
str::from_utf8(&attr.value) str::from_utf8(&attr.value)
.expect("utf8 encoding error") .expect("utf8 encoding error")
.to_string(), .to_string(),
); );
} else if attr_name == "type" { } else if attr_name == "type" {
tt = Some(WlArgType::parse( tt = Some(WlArgType::parse(
str::from_utf8(&attr.value).expect("utf8 encoding error"), str::from_utf8(&attr.value).expect("utf8 encoding error"),
)); ));
} else if attr_name == "interface" { } else if attr_name == "interface" {
interface_name = Some( interface_name = Some(
str::from_utf8(&attr.value) str::from_utf8(&attr.value)
.expect("utf8 encoding error") .expect("utf8 encoding error")
.to_string(), .to_string(),
); );
}
} }
if let Some(ref mut name) = name {
if name == "type" {
*name = "_type".to_string();
} else if name == "msg" {
*name = "_msg".to_string();
}
}
if let Some(WlArgType::NewId(_)) = tt {
if let Some(interface_name) = interface_name {
tt.as_mut().unwrap().set_interface_name(interface_name);
} else {
// Unspecified interface for new_id; special serialization format!
args.push((
format!(
"{}_interface_name",
name.as_ref().expect("needs an arg name!")
),
WlArgType::String,
));
args.push((
format!(
"{}_interface_version",
name.as_ref().expect("needs an arg name!")
),
WlArgType::Uint,
))
}
}
args.push((
name.expect("args must have a name"),
tt.expect("args must have a type"),
));
} }
Event::End(e) if e.local_name() == start.local_name() => break,
_ => continue, if let Some(ref mut name) = name {
if name == "type" {
*name = "_type".to_string();
} else if name == "msg" {
*name = "_msg".to_string();
}
}
if let Some(WlArgType::NewId(_)) = tt {
if let Some(interface_name) = interface_name {
tt.as_mut().unwrap().set_interface_name(interface_name);
} else {
// Unspecified interface for new_id; special serialization format!
args.push((
format!(
"{}_interface_name",
name.as_ref().expect("needs an arg name!")
),
WlArgType::String,
));
args.push((
format!(
"{}_interface_version",
name.as_ref().expect("needs an arg name!")
),
WlArgType::Uint,
))
}
}
args.push((
name.expect("args must have a name"),
tt.expect("args must have a type"),
));
} }
Event::End(e) if e.local_name() == start.local_name() => break,
_ => continue,
} }
} }

View file

@ -192,20 +192,28 @@ impl WlMsg {
#opcode #opcode
} }
fn self_opcode(&self) -> u16 {
#opcode
}
fn object_type() -> crate::objects::WlObjectType { fn object_type() -> crate::objects::WlObjectType {
crate::proto::#interface_name_snake_upper crate::proto::#interface_name_snake_upper
} }
fn msg_name() -> &'static str { fn self_object_type(&self) -> crate::objects::WlObjectType {
crate::proto::#interface_name_snake_upper
}
fn self_msg_name(&self) -> &'static str {
#msg_name_snake #msg_name_snake
} }
fn is_destructor() -> bool { fn static_type_id() -> std::any::TypeId {
#is_destructor std::any::TypeId::of::<#struct_name<'static>>()
} }
fn num_consumed_fds() -> usize { fn self_static_type_id(&self) -> std::any::TypeId {
#num_consumed_fds std::any::TypeId::of::<#struct_name<'static>>()
} }
#[allow(unused, private_interfaces, non_snake_case)] #[allow(unused, private_interfaces, non_snake_case)]
@ -223,22 +231,28 @@ impl WlMsg {
}) })
} }
fn _obj_id(&self) -> u32 { fn obj_id(&self) -> u32 {
self.obj_id self.obj_id
} }
fn _known_objects_created(&self) -> Option<Vec<(u32, crate::objects::WlObjectType)>> { fn is_destructor(&self) -> bool {
#is_destructor
}
fn known_objects_created(&self) -> Option<Vec<(u32, crate::objects::WlObjectType)>> {
#known_objects_created #known_objects_created
} }
fn _to_json(&self) -> String { fn to_json(&self) -> String {
serde_json::to_string(self).unwrap() serde_json::to_string(self).unwrap()
} }
fn num_consumed_fds(&self) -> usize {
#num_consumed_fds
}
} }
unsafe impl<'a> crate::proto::DowncastableWlParsedMessage<'a> for #struct_name<'a> { unsafe impl<'a> crate::proto::AnyWlParsedMessage<'a> for #struct_name<'a> {}
type Static = #struct_name<'static>;
}
pub struct #parser_fn_name; pub struct #parser_fn_name;
@ -247,7 +261,7 @@ impl WlMsg {
&self, &self,
objects: &'obj crate::proto::WlObjects, objects: &'obj crate::proto::WlObjects,
msg: &'msg crate::codec::WlRawMsg, msg: &'msg crate::codec::WlRawMsg,
) -> crate::proto::WaylandProtocolParsingOutcome<Box<dyn crate::proto::AnyWlParsedMessage + 'msg>> { ) -> crate::proto::WaylandProtocolParsingOutcome<Box<dyn crate::proto::AnyWlParsedMessage<'msg> + 'msg>> {
#struct_name::try_from_msg(objects, msg).map(|r| Box::new(r) as Box<_>) #struct_name::try_from_msg(objects, msg).map(|r| Box::new(r) as Box<_>)
} }
} }

View file

@ -145,6 +145,28 @@ impl<'a> WlMsgWriter<'a> {
self.write_queue.push(msg); self.write_queue.push(msg);
} }
pub async fn write(&mut self, msg: WlRawMsg) -> io::Result<()> {
let (buf, fds) = msg.into_parts();
let mut written = 0usize;
while written < buf.len() {
self.egress.writable().await?;
let res = self
.egress
.send_with_fd(&buf[written..], unsafe { std::mem::transmute(fds.deref()) });
match res {
Ok(new_written) => written += new_written,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => continue,
Err(e) => return Err(e),
}
}
Ok(())
}
/// Try to make progress by flushing some of the queued up messages into the stream. /// Try to make progress by flushing some of the queued up messages into the stream.
/// When this resolves, note that we might have only partially written. In that /// When this resolves, note that we might have only partially written. In that
/// case the buffer is saved internally in this structure. /// case the buffer is saved internally in this structure.

View file

@ -141,7 +141,7 @@ impl<'a> ConnDuplex<'a> {
match verdict { match verdict {
WlMitmVerdict::Allowed => { WlMitmVerdict::Allowed => {
self.downstream_write.queue_write(wl_raw_msg); self.downstream_write.write(wl_raw_msg).await?;
} }
WlMitmVerdict::Terminate => { WlMitmVerdict::Terminate => {
return Err(io::Error::new( return Err(io::Error::new(
@ -180,18 +180,20 @@ impl<'a> ConnDuplex<'a> {
match verdict { match verdict {
WlMitmVerdict::Allowed => { WlMitmVerdict::Allowed => {
self.upstream_write.queue_write(wl_raw_msg); self.upstream_write.write(wl_raw_msg).await?;
} }
WlMitmVerdict::Rejected(error_code) => { WlMitmVerdict::Rejected(error_code) => {
self.downstream_write.queue_write( self.downstream_write
WlDisplayErrorEvent::new( .write(
WL_DISPLAY_OBJECT_ID, WlDisplayErrorEvent::new(
wl_raw_msg.obj_id, WL_DISPLAY_OBJECT_ID,
error_code, wl_raw_msg.obj_id,
"Rejected by wl-mitm", error_code,
"Rejected by wl-mitm",
)
.build(),
) )
.build(), .await?;
);
} }
WlMitmVerdict::Terminate => { WlMitmVerdict::Terminate => {
return Err(io::Error::new( return Err(io::Error::new(
@ -215,9 +217,6 @@ impl<'a> ConnDuplex<'a> {
tokio::select! { tokio::select! {
biased; biased;
res = self.downstream_write.dequeue_write() => res?,
res = self.upstream_write.dequeue_write() => res?,
msg = self.upstream_read.read() => { msg = self.upstream_read.read() => {
control_flow!(self.handle_s2c_event(msg?).await?); control_flow!(self.handle_s2c_event(msg?).await?);
} }

View file

@ -35,27 +35,27 @@ impl<T> WaylandProtocolParsingOutcome<T> {
/// Internal module used to seal the [WlParsedMessage] trait /// Internal module used to seal the [WlParsedMessage] trait
mod __private { mod __private {
pub(super) trait WlParsedMessagePrivate: Sized {} pub(super) trait WlParsedMessagePrivate: Send {}
pub(super) struct WlParsedMessagePrivateToken; pub(super) struct WlParsedMessagePrivateToken;
} }
#[allow(private_bounds, private_interfaces)] #[allow(private_bounds, private_interfaces)]
pub trait WlParsedMessage<'a>: __private::WlParsedMessagePrivate { pub trait WlParsedMessage<'a>: __private::WlParsedMessagePrivate {
fn opcode() -> u16; fn opcode() -> u16
fn object_type() -> WlObjectType; where
fn msg_name() -> &'static str; Self: Sized;
/// Is this request / event a destructor? That is, does it destroy [Self::obj_id()]? fn object_type() -> WlObjectType
fn is_destructor() -> bool; where
/// How many fds have been consumed in parsing this message? Self: Sized;
/// This is used to return any unused fds to the decoder. fn static_type_id() -> TypeId
fn num_consumed_fds() -> usize; where
Self: Sized;
fn try_from_msg<'obj>( fn try_from_msg<'obj>(
objects: &'obj WlObjects, objects: &'obj WlObjects,
msg: &'a WlRawMsg, msg: &'a WlRawMsg,
) -> WaylandProtocolParsingOutcome<Self> ) -> WaylandProtocolParsingOutcome<Self>
where where
Self: 'a, Self: Sized + 'a,
{ {
// Verify object type and opcode // Verify object type and opcode
if objects.lookup_object(msg.obj_id) != Some(Self::object_type()) { if objects.lookup_object(msg.obj_id) != Some(Self::object_type()) {
@ -74,10 +74,19 @@ pub trait WlParsedMessage<'a>: __private::WlParsedMessagePrivate {
_token: __private::WlParsedMessagePrivateToken, _token: __private::WlParsedMessagePrivateToken,
) -> WaylandProtocolParsingOutcome<Self> ) -> WaylandProtocolParsingOutcome<Self>
where where
Self: 'a; Self: Sized + 'a;
// dyn-available methods
fn self_opcode(&self) -> u16;
fn self_object_type(&self) -> WlObjectType;
fn self_msg_name(&self) -> &'static str;
fn self_static_type_id(&self) -> TypeId;
/// The object ID which this message acts upon /// The object ID which this message acts upon
fn _obj_id(&self) -> u32; fn obj_id(&self) -> u32;
/// Is this request / event a destructor? That is, does it destroy [Self::obj_id()]?
fn is_destructor(&self) -> bool;
/// List of (object id, object type) pairs created by this message /// List of (object id, object type) pairs created by this message
/// Note that this only includes objects created with a fixed, known interface /// Note that this only includes objects created with a fixed, known interface
@ -85,99 +94,43 @@ pub trait WlParsedMessage<'a>: __private::WlParsedMessagePrivate {
/// serialized differently, and are not included here. However, the only /// serialized differently, and are not included here. However, the only
/// widely-used message with that capability is [WlRegistryBindRequest], /// widely-used message with that capability is [WlRegistryBindRequest],
/// which is already handled separately on its own. /// which is already handled separately on its own.
fn _known_objects_created(&self) -> Option<Vec<(u32, WlObjectType)>>; fn known_objects_created(&self) -> Option<Vec<(u32, WlObjectType)>>;
/// Serialize this message into a JSON string, for use with ask scripts /// Serialize this message into a JSON string, for use with ask scripts
fn _to_json(&self) -> String; fn to_json(&self) -> String;
/// How many fds have been consumed in parsing this message?
/// This is used to return any unused fds to the decoder.
fn num_consumed_fds(&self) -> usize;
} }
/// A version of [WlParsedMessage] that supports downcasting. By implementing this /// A version of [WlParsedMessage] that supports downcasting. By implementing this
/// trait, you assert that the [DowncastableWlParsedMessage::Static] MUST correspond /// trait, you assert that the [WlParsedMessage::static_type_id] and
/// to a version of the implementor type with a static lifetime. /// [WlParsedMessage::self_static_type_id] implementations are sound: they MUST
/// return the [TypeId] of the implementing struct, assuming it had static lifetime.
/// ///
/// Any implementor also asserts that the type implementing this trait /// Any implementor also asserts that the type implementing this trait
/// does not contain any lifetime other than 'a, and that the implenetor struct is /// does not contain any lifetime other than 'a, and that the implenetor struct is
/// _covariant_ with respect to lifetime 'a. /// _covariant_ with respect to lifetime 'a.
/// ///
/// This is required for the soundness of the downcast_ref implementation. /// This is required for the soundness of the downcast_ref implementation.
pub unsafe trait DowncastableWlParsedMessage<'a>: Send + WlParsedMessage<'a> { pub unsafe trait AnyWlParsedMessage<'a>: WlParsedMessage<'a> {}
type Static: 'static;
}
/// The implementation of dyn-available methods and downcasting for impl<'out, 'data: 'out> dyn AnyWlParsedMessage<'data> + 'data {
/// [DowncastableWlParsedMessage]
pub trait AnyWlParsedMessage: Send {
fn static_type_id(&self) -> TypeId;
fn opcode(&self) -> u16;
fn object_type(&self) -> WlObjectType;
fn msg_name(&self) -> &'static str;
fn is_destructor(&self) -> bool;
fn obj_id(&self) -> u32;
fn known_objects_created(&self) -> Option<Vec<(u32, WlObjectType)>>;
fn to_json(&self) -> String;
fn num_consumed_fds(&self) -> usize;
}
impl<'a, T> AnyWlParsedMessage for T
where
T: DowncastableWlParsedMessage<'a> + 'a,
{
fn static_type_id(&self) -> TypeId {
TypeId::of::<T::Static>()
}
fn opcode(&self) -> u16 {
T::opcode()
}
fn object_type(&self) -> WlObjectType {
T::object_type()
}
fn msg_name(&self) -> &'static str {
T::msg_name()
}
fn is_destructor(&self) -> bool {
T::is_destructor()
}
fn obj_id(&self) -> u32 {
T::_obj_id(self)
}
fn known_objects_created(&self) -> Option<Vec<(u32, WlObjectType)>> {
T::_known_objects_created(self)
}
fn to_json(&self) -> String {
T::_to_json(self)
}
fn num_consumed_fds(&self) -> usize {
T::num_consumed_fds()
}
}
impl<'out, 'data: 'out> dyn AnyWlParsedMessage + 'data {
/// Downcast the type-erased, borrowed Wayland message to a concrete type. Note that the /// Downcast the type-erased, borrowed Wayland message to a concrete type. Note that the
/// safety of this relies on a few invariants: /// safety of this relies on a few invariants:
/// ///
/// 1. 'data outlives 'out (guaranteed by the trait bound above) /// 1. 'data outlives 'out (guaranteed by the trait bound above)
/// 2. The type implementing [DowncastableWlParsedMessage] does not contain any lifetime other than /// 2. The type implementing [AnyWlParsedMessage] does not contain any lifetime other than
/// 'data (or 'a in the trait's definition). /// 'data (or 'a in the trait's definition).
pub fn downcast_ref< pub fn downcast_ref<T: AnyWlParsedMessage<'data> + 'data>(&'out self) -> Option<&'out T> {
T: AnyWlParsedMessage + DowncastableWlParsedMessage<'data> + 'data, if self.self_static_type_id() != T::static_type_id() {
>(
&'data self,
) -> Option<&'out T> {
if self.static_type_id() != TypeId::of::<T::Static>() {
return None; return None;
} }
// SAFETY: We have verified the type IDs match up. // SAFETY: We have verified the type IDs match up.
// //
// In addition, because [DowncastableWlParsedMessage]'s contract requires that no // In addition, because [AnyWlParsedMessage]'s contract requires that no
// lifetime other than 'a ('data) is contained in the implemetor, the // lifetime other than 'a ('data) is contained in the implemetor, the
// output type T cannot contain another lifetime that may be transmuted // output type T cannot contain another lifetime that may be transmuted
// by this unsafe block. // by this unsafe block.
@ -195,13 +148,13 @@ pub trait WlMsgParserFn: Send + Sync {
&self, &self,
objects: &'obj WlObjects, objects: &'obj WlObjects,
msg: &'msg WlRawMsg, msg: &'msg WlRawMsg,
) -> WaylandProtocolParsingOutcome<Box<dyn AnyWlParsedMessage + 'msg>>; ) -> WaylandProtocolParsingOutcome<Box<dyn AnyWlParsedMessage<'msg> + 'msg>>;
} }
/// Messages that can be converted back to [WlRawMsg] /// Messages that can be converted back to [WlRawMsg]
pub trait WlConstructableMessage<'a>: Sized + WlParsedMessage<'a> { pub trait WlConstructableMessage<'a>: Sized + WlParsedMessage<'a> {
fn build(&self) -> WlRawMsg { fn build(&self) -> WlRawMsg {
WlRawMsg::build(self._obj_id(), Self::opcode(), |buf, fds| { WlRawMsg::build(self.obj_id(), Self::opcode(), |buf, fds| {
self.build_inner(buf, fds) self.build_inner(buf, fds)
}) })
} }
@ -234,7 +187,7 @@ static WL_EVENT_REQUEST_PARSERS: LazyLock<(
pub fn decode_event<'obj, 'msg>( pub fn decode_event<'obj, 'msg>(
objects: &'obj WlObjects, objects: &'obj WlObjects,
msg: &'msg WlRawMsg, msg: &'msg WlRawMsg,
) -> WaylandProtocolParsingOutcome<Box<dyn AnyWlParsedMessage + 'msg>> { ) -> WaylandProtocolParsingOutcome<Box<dyn AnyWlParsedMessage<'msg> + 'msg>> {
let Some(obj_type) = objects.lookup_object(msg.obj_id) else { let Some(obj_type) = objects.lookup_object(msg.obj_id) else {
return WaylandProtocolParsingOutcome::Unknown; return WaylandProtocolParsingOutcome::Unknown;
}; };
@ -254,7 +207,7 @@ pub fn decode_event<'obj, 'msg>(
pub fn decode_request<'obj, 'msg>( pub fn decode_request<'obj, 'msg>(
objects: &'obj WlObjects, objects: &'obj WlObjects,
msg: &'msg WlRawMsg, msg: &'msg WlRawMsg,
) -> WaylandProtocolParsingOutcome<Box<dyn AnyWlParsedMessage + 'msg>> { ) -> WaylandProtocolParsingOutcome<Box<dyn AnyWlParsedMessage<'msg> + 'msg>> {
let Some(obj_type) = objects.lookup_object(msg.obj_id) else { let Some(obj_type) = objects.lookup_object(msg.obj_id) else {
return WaylandProtocolParsingOutcome::Unknown; return WaylandProtocolParsingOutcome::Unknown;
}; };

View file

@ -8,9 +8,10 @@ use crate::{
objects::WlObjects, objects::WlObjects,
proto::{ proto::{
AnyWlParsedMessage, WaylandProtocolParsingOutcome, WlDisplayDeleteIdEvent, AnyWlParsedMessage, WaylandProtocolParsingOutcome, WlDisplayDeleteIdEvent,
WlKeyboardEnterEvent, WlPointerEnterEvent, WlRegistryBindRequest, WlRegistryGlobalEvent, WlKeyboardEnterEvent, WlParsedMessage, WlPointerEnterEvent, WlRegistryBindRequest,
WlRegistryGlobalRemoveEvent, WlTouchDownEvent, XdgSurfaceGetToplevelRequest, WlRegistryGlobalEvent, WlRegistryGlobalRemoveEvent, WlTouchDownEvent,
XdgToplevelSetAppIdRequest, XdgToplevelSetTitleRequest, XdgWmBaseGetXdgSurfaceRequest, XdgSurfaceGetToplevelRequest, XdgToplevelSetAppIdRequest, XdgToplevelSetTitleRequest,
XdgWmBaseGetXdgSurfaceRequest,
}, },
}; };
@ -117,7 +118,7 @@ impl WlMitmState {
/// is not handled here. /// is not handled here.
fn handle_created_or_destroyed_objects( fn handle_created_or_destroyed_objects(
&mut self, &mut self,
msg: &dyn AnyWlParsedMessage, msg: &dyn AnyWlParsedMessage<'_>,
from_client: bool, from_client: bool,
) -> bool { ) -> bool {
if let Some(created_objects) = msg.known_objects_created() { if let Some(created_objects) = msg.known_objects_created() {
@ -132,7 +133,7 @@ impl WlMitmState {
is_half_destroyed = self.objects.is_half_destroyed(id), is_half_destroyed = self.objects.is_half_destroyed(id),
"Trying to create object via message {}::{} but the object ID is already used!", "Trying to create object via message {}::{} but the object ID is already used!",
parent_obj.interface(), parent_obj.interface(),
msg.msg_name() msg.self_msg_name()
); );
return false; return false;
@ -144,7 +145,7 @@ impl WlMitmState {
obj_id = id, obj_id = id,
"Created object via message {}::{}", "Created object via message {}::{}",
parent_obj.interface(), parent_obj.interface(),
msg.msg_name() msg.self_msg_name()
); );
self.objects.record_object(tt, id); self.objects.record_object(tt, id);
} }
@ -163,7 +164,7 @@ impl WlMitmState {
obj_id = msg.obj_id(), obj_id = msg.obj_id(),
"Object destructed via destructor {}::{}", "Object destructed via destructor {}::{}",
obj_type.interface(), obj_type.interface(),
msg.msg_name() msg.self_msg_name()
); );
self.objects.remove_object(msg.obj_id(), from_client); self.objects.remove_object(msg.obj_id(), from_client);
@ -178,13 +179,13 @@ impl WlMitmState {
fn prepare_command( fn prepare_command(
&self, &self,
msg: &dyn AnyWlParsedMessage, msg: &dyn AnyWlParsedMessage<'_>,
cmd_str: &str, cmd_str: &str,
desc: &str, desc: &str,
) -> tokio::process::Command { ) -> tokio::process::Command {
let mut cmd = tokio::process::Command::new(cmd_str); let mut cmd = tokio::process::Command::new(cmd_str);
cmd.arg(msg.object_type().interface()); cmd.arg(msg.self_object_type().interface());
cmd.arg(msg.msg_name()); cmd.arg(msg.self_msg_name());
cmd.arg(desc); cmd.arg(desc);
cmd.env("WL_MITM_MSG_JSON", msg.to_json()); cmd.env("WL_MITM_MSG_JSON", msg.to_json());
@ -249,8 +250,8 @@ impl WlMitmState {
num_fds = raw_msg.fds.len(), num_fds = raw_msg.fds.len(),
num_consumed_fds = msg.num_consumed_fds(), num_consumed_fds = msg.num_consumed_fds(),
"{}::{}", "{}::{}",
msg.object_type().interface(), msg.self_object_type().interface(),
msg.msg_name(), msg.self_msg_name(),
) )
} }
@ -259,7 +260,7 @@ impl WlMitmState {
if self.objects.is_half_destroyed(msg.obj_id()) { if self.objects.is_half_destroyed(msg.obj_id()) {
error!( error!(
obj_id = msg.obj_id(), obj_id = msg.obj_id(),
opcode = msg.opcode(), opcode = msg.self_opcode(),
"Client request detected on object already scheduled for destruction; aborting!" "Client request detected on object already scheduled for destruction; aborting!"
); );
return outcome.terminate(); return outcome.terminate();
@ -333,11 +334,11 @@ impl WlMitmState {
.config .config
.filter .filter
.requests .requests
.get(msg.object_type().interface()) .get(msg.self_object_type().interface())
{ {
if let Some(filtered) = filtered_requests if let Some(filtered) = filtered_requests
.iter() .iter()
.find(|f| f.requests.contains(msg.msg_name())) .find(|f| f.requests.contains(msg.self_msg_name()))
{ {
match filtered.action { match filtered.action {
WlFilterRequestAction::Ask => { WlFilterRequestAction::Ask => {
@ -345,8 +346,8 @@ impl WlMitmState {
info!( info!(
ask_cmd = ask_cmd, ask_cmd = ask_cmd,
"Running ask command for {}::{}", "Running ask command for {}::{}",
msg.object_type().interface(), msg.self_object_type().interface(),
msg.msg_name() msg.self_msg_name()
); );
let mut cmd = self.prepare_command( let mut cmd = self.prepare_command(
@ -359,8 +360,8 @@ impl WlMitmState {
if !status.success() { if !status.success() {
warn!( warn!(
"Blocked {}::{} because of return status {}", "Blocked {}::{} because of return status {}",
msg.object_type().interface(), msg.self_object_type().interface(),
msg.msg_name(), msg.self_msg_name(),
status status
); );
@ -378,8 +379,8 @@ impl WlMitmState {
warn!( warn!(
"Blocked {}::{} because of missing ask_cmd", "Blocked {}::{} because of missing ask_cmd",
msg.object_type().interface(), msg.self_object_type().interface(),
msg.msg_name() msg.self_msg_name()
); );
return match filtered.block_type { return match filtered.block_type {
WlFilterRequestBlockType::Ignore => outcome.filtered(), WlFilterRequestBlockType::Ignore => outcome.filtered(),
@ -393,8 +394,8 @@ impl WlMitmState {
info!( info!(
notify_cmd = notify_cmd, notify_cmd = notify_cmd,
"Running notify command for {}::{}", "Running notify command for {}::{}",
msg.object_type().interface(), msg.self_object_type().interface(),
msg.msg_name() msg.self_msg_name()
); );
let mut cmd = self.prepare_command( let mut cmd = self.prepare_command(
@ -409,8 +410,8 @@ impl WlMitmState {
WlFilterRequestAction::Block => { WlFilterRequestAction::Block => {
warn!( warn!(
"Blocked {}::{}", "Blocked {}::{}",
msg.object_type().interface(), msg.self_object_type().interface(),
msg.msg_name() msg.self_msg_name()
); );
return match filtered.block_type { return match filtered.block_type {
WlFilterRequestBlockType::Ignore => outcome.filtered(), WlFilterRequestBlockType::Ignore => outcome.filtered(),
@ -457,8 +458,8 @@ impl WlMitmState {
num_fds = raw_msg.fds.len(), num_fds = raw_msg.fds.len(),
num_consumed_fds = msg.num_consumed_fds(), num_consumed_fds = msg.num_consumed_fds(),
"{}::{}", "{}::{}",
msg.object_type().interface(), msg.self_object_type().interface(),
msg.msg_name(), msg.self_msg_name(),
) )
} }