Compare commits
2 commits
main
...
debug-somm
Author | SHA1 | Date | |
---|---|---|---|
5a7c4aa781 | |||
1f4ca74cf9 |
7 changed files with 219 additions and 271 deletions
26
README.md
26
README.md
|
@ -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
|
||||
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
|
||||
---
|
||||
|
||||
|
|
|
@ -206,44 +206,33 @@ fn handle_interface(
|
|||
let mut event_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 {
|
||||
match reader.read_event().expect("Unable to parse XML file") {
|
||||
Event::Eof => panic!("Unexpected EOF"),
|
||||
Event::Start(e) => {
|
||||
add_msg(reader, e, false);
|
||||
}
|
||||
Event::Empty(e) => {
|
||||
add_msg(reader, e, true);
|
||||
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,
|
||||
));
|
||||
} 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,
|
||||
_ => continue,
|
||||
|
@ -262,7 +251,6 @@ fn handle_request_or_event(
|
|||
msg_type: WlMsgType,
|
||||
interface_name_snake: &str,
|
||||
start: quick_xml::events::BytesStart<'_>,
|
||||
is_empty: bool,
|
||||
) -> WlMsg {
|
||||
let name_attr = start
|
||||
.attributes()
|
||||
|
@ -290,80 +278,77 @@ fn handle_request_or_event(
|
|||
// Load arguments and their types from XML
|
||||
let mut args: Vec<(String, WlArgType)> = Vec::new();
|
||||
|
||||
if !is_empty {
|
||||
loop {
|
||||
match reader.read_event().expect("Unable to parse XML file") {
|
||||
Event::Eof => panic!("Unexpected EOF"),
|
||||
Event::Empty(e)
|
||||
if str::from_utf8(e.local_name().into_inner())
|
||||
.expect("utf8 encoding error")
|
||||
== "arg" =>
|
||||
{
|
||||
let mut name: Option<String> = None;
|
||||
let mut tt: Option<WlArgType> = None;
|
||||
let mut interface_name: Option<String> = None;
|
||||
loop {
|
||||
match reader.read_event().expect("Unable to parse XML file") {
|
||||
Event::Eof => panic!("Unexpected EOF"),
|
||||
Event::Empty(e)
|
||||
if str::from_utf8(e.local_name().into_inner()).expect("utf8 encoding error")
|
||||
== "arg" =>
|
||||
{
|
||||
let mut name: Option<String> = None;
|
||||
let mut tt: Option<WlArgType> = None;
|
||||
let mut interface_name: Option<String> = None;
|
||||
|
||||
for attr in e.attributes() {
|
||||
let attr = attr.expect("attr parsing error");
|
||||
let attr_name = str::from_utf8(attr.key.local_name().into_inner())
|
||||
.expect("utf8 encoding error");
|
||||
if attr_name == "name" {
|
||||
name = Some(
|
||||
str::from_utf8(&attr.value)
|
||||
.expect("utf8 encoding error")
|
||||
.to_string(),
|
||||
);
|
||||
} else if attr_name == "type" {
|
||||
tt = Some(WlArgType::parse(
|
||||
str::from_utf8(&attr.value).expect("utf8 encoding error"),
|
||||
));
|
||||
} else if attr_name == "interface" {
|
||||
interface_name = Some(
|
||||
str::from_utf8(&attr.value)
|
||||
.expect("utf8 encoding error")
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
for attr in e.attributes() {
|
||||
let attr = attr.expect("attr parsing error");
|
||||
let attr_name = str::from_utf8(attr.key.local_name().into_inner())
|
||||
.expect("utf8 encoding error");
|
||||
if attr_name == "name" {
|
||||
name = Some(
|
||||
str::from_utf8(&attr.value)
|
||||
.expect("utf8 encoding error")
|
||||
.to_string(),
|
||||
);
|
||||
} else if attr_name == "type" {
|
||||
tt = Some(WlArgType::parse(
|
||||
str::from_utf8(&attr.value).expect("utf8 encoding error"),
|
||||
));
|
||||
} else if attr_name == "interface" {
|
||||
interface_name = Some(
|
||||
str::from_utf8(&attr.value)
|
||||
.expect("utf8 encoding error")
|
||||
.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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -192,20 +192,28 @@ impl WlMsg {
|
|||
#opcode
|
||||
}
|
||||
|
||||
fn self_opcode(&self) -> u16 {
|
||||
#opcode
|
||||
}
|
||||
|
||||
fn object_type() -> crate::objects::WlObjectType {
|
||||
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
|
||||
}
|
||||
|
||||
fn is_destructor() -> bool {
|
||||
#is_destructor
|
||||
fn static_type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<#struct_name<'static>>()
|
||||
}
|
||||
|
||||
fn num_consumed_fds() -> usize {
|
||||
#num_consumed_fds
|
||||
fn self_static_type_id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<#struct_name<'static>>()
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn _to_json(&self) -> String {
|
||||
fn to_json(&self) -> String {
|
||||
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> {
|
||||
type Static = #struct_name<'static>;
|
||||
}
|
||||
unsafe impl<'a> crate::proto::AnyWlParsedMessage<'a> for #struct_name<'a> {}
|
||||
|
||||
pub struct #parser_fn_name;
|
||||
|
||||
|
@ -247,7 +261,7 @@ impl WlMsg {
|
|||
&self,
|
||||
objects: &'obj crate::proto::WlObjects,
|
||||
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<_>)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,6 +145,28 @@ impl<'a> WlMsgWriter<'a> {
|
|||
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.
|
||||
/// When this resolves, note that we might have only partially written. In that
|
||||
/// case the buffer is saved internally in this structure.
|
||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -141,7 +141,7 @@ impl<'a> ConnDuplex<'a> {
|
|||
|
||||
match verdict {
|
||||
WlMitmVerdict::Allowed => {
|
||||
self.downstream_write.queue_write(wl_raw_msg);
|
||||
self.downstream_write.write(wl_raw_msg).await?;
|
||||
}
|
||||
WlMitmVerdict::Terminate => {
|
||||
return Err(io::Error::new(
|
||||
|
@ -180,18 +180,20 @@ impl<'a> ConnDuplex<'a> {
|
|||
|
||||
match verdict {
|
||||
WlMitmVerdict::Allowed => {
|
||||
self.upstream_write.queue_write(wl_raw_msg);
|
||||
self.upstream_write.write(wl_raw_msg).await?;
|
||||
}
|
||||
WlMitmVerdict::Rejected(error_code) => {
|
||||
self.downstream_write.queue_write(
|
||||
WlDisplayErrorEvent::new(
|
||||
WL_DISPLAY_OBJECT_ID,
|
||||
wl_raw_msg.obj_id,
|
||||
error_code,
|
||||
"Rejected by wl-mitm",
|
||||
self.downstream_write
|
||||
.write(
|
||||
WlDisplayErrorEvent::new(
|
||||
WL_DISPLAY_OBJECT_ID,
|
||||
wl_raw_msg.obj_id,
|
||||
error_code,
|
||||
"Rejected by wl-mitm",
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
.build(),
|
||||
);
|
||||
.await?;
|
||||
}
|
||||
WlMitmVerdict::Terminate => {
|
||||
return Err(io::Error::new(
|
||||
|
@ -215,9 +217,6 @@ impl<'a> ConnDuplex<'a> {
|
|||
tokio::select! {
|
||||
biased;
|
||||
|
||||
res = self.downstream_write.dequeue_write() => res?,
|
||||
res = self.upstream_write.dequeue_write() => res?,
|
||||
|
||||
msg = self.upstream_read.read() => {
|
||||
control_flow!(self.handle_s2c_event(msg?).await?);
|
||||
}
|
||||
|
|
129
src/proto.rs
129
src/proto.rs
|
@ -35,27 +35,27 @@ impl<T> WaylandProtocolParsingOutcome<T> {
|
|||
|
||||
/// Internal module used to seal the [WlParsedMessage] trait
|
||||
mod __private {
|
||||
pub(super) trait WlParsedMessagePrivate: Sized {}
|
||||
pub(super) trait WlParsedMessagePrivate: Send {}
|
||||
pub(super) struct WlParsedMessagePrivateToken;
|
||||
}
|
||||
|
||||
#[allow(private_bounds, private_interfaces)]
|
||||
pub trait WlParsedMessage<'a>: __private::WlParsedMessagePrivate {
|
||||
fn opcode() -> u16;
|
||||
fn object_type() -> WlObjectType;
|
||||
fn msg_name() -> &'static str;
|
||||
/// Is this request / event a destructor? That is, does it destroy [Self::obj_id()]?
|
||||
fn is_destructor() -> bool;
|
||||
/// 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() -> usize;
|
||||
|
||||
fn opcode() -> u16
|
||||
where
|
||||
Self: Sized;
|
||||
fn object_type() -> WlObjectType
|
||||
where
|
||||
Self: Sized;
|
||||
fn static_type_id() -> TypeId
|
||||
where
|
||||
Self: Sized;
|
||||
fn try_from_msg<'obj>(
|
||||
objects: &'obj WlObjects,
|
||||
msg: &'a WlRawMsg,
|
||||
) -> WaylandProtocolParsingOutcome<Self>
|
||||
where
|
||||
Self: 'a,
|
||||
Self: Sized + 'a,
|
||||
{
|
||||
// Verify object type and opcode
|
||||
if objects.lookup_object(msg.obj_id) != Some(Self::object_type()) {
|
||||
|
@ -74,10 +74,19 @@ pub trait WlParsedMessage<'a>: __private::WlParsedMessagePrivate {
|
|||
_token: __private::WlParsedMessagePrivateToken,
|
||||
) -> WaylandProtocolParsingOutcome<Self>
|
||||
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
|
||||
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
|
||||
/// 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
|
||||
/// widely-used message with that capability is [WlRegistryBindRequest],
|
||||
/// 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
|
||||
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
|
||||
/// trait, you assert that the [DowncastableWlParsedMessage::Static] MUST correspond
|
||||
/// to a version of the implementor type with a static lifetime.
|
||||
/// trait, you assert that the [WlParsedMessage::static_type_id] and
|
||||
/// [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
|
||||
/// does not contain any lifetime other than 'a, and that the implenetor struct is
|
||||
/// _covariant_ with respect to lifetime 'a.
|
||||
///
|
||||
/// This is required for the soundness of the downcast_ref implementation.
|
||||
pub unsafe trait DowncastableWlParsedMessage<'a>: Send + WlParsedMessage<'a> {
|
||||
type Static: 'static;
|
||||
}
|
||||
pub unsafe trait AnyWlParsedMessage<'a>: WlParsedMessage<'a> {}
|
||||
|
||||
/// The implementation of dyn-available methods and downcasting for
|
||||
/// [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 {
|
||||
impl<'out, 'data: 'out> dyn AnyWlParsedMessage<'data> + 'data {
|
||||
/// Downcast the type-erased, borrowed Wayland message to a concrete type. Note that the
|
||||
/// safety of this relies on a few invariants:
|
||||
///
|
||||
/// 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).
|
||||
pub fn downcast_ref<
|
||||
T: AnyWlParsedMessage + DowncastableWlParsedMessage<'data> + 'data,
|
||||
>(
|
||||
&'data self,
|
||||
) -> Option<&'out T> {
|
||||
if self.static_type_id() != TypeId::of::<T::Static>() {
|
||||
pub fn downcast_ref<T: AnyWlParsedMessage<'data> + 'data>(&'out self) -> Option<&'out T> {
|
||||
if self.self_static_type_id() != T::static_type_id() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// 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
|
||||
// output type T cannot contain another lifetime that may be transmuted
|
||||
// by this unsafe block.
|
||||
|
@ -195,13 +148,13 @@ pub trait WlMsgParserFn: Send + Sync {
|
|||
&self,
|
||||
objects: &'obj WlObjects,
|
||||
msg: &'msg WlRawMsg,
|
||||
) -> WaylandProtocolParsingOutcome<Box<dyn AnyWlParsedMessage + 'msg>>;
|
||||
) -> WaylandProtocolParsingOutcome<Box<dyn AnyWlParsedMessage<'msg> + 'msg>>;
|
||||
}
|
||||
|
||||
/// Messages that can be converted back to [WlRawMsg]
|
||||
pub trait WlConstructableMessage<'a>: Sized + WlParsedMessage<'a> {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
@ -234,7 +187,7 @@ static WL_EVENT_REQUEST_PARSERS: LazyLock<(
|
|||
pub fn decode_event<'obj, 'msg>(
|
||||
objects: &'obj WlObjects,
|
||||
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 {
|
||||
return WaylandProtocolParsingOutcome::Unknown;
|
||||
};
|
||||
|
@ -254,7 +207,7 @@ pub fn decode_event<'obj, 'msg>(
|
|||
pub fn decode_request<'obj, 'msg>(
|
||||
objects: &'obj WlObjects,
|
||||
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 {
|
||||
return WaylandProtocolParsingOutcome::Unknown;
|
||||
};
|
||||
|
|
55
src/state.rs
55
src/state.rs
|
@ -8,9 +8,10 @@ use crate::{
|
|||
objects::WlObjects,
|
||||
proto::{
|
||||
AnyWlParsedMessage, WaylandProtocolParsingOutcome, WlDisplayDeleteIdEvent,
|
||||
WlKeyboardEnterEvent, WlPointerEnterEvent, WlRegistryBindRequest, WlRegistryGlobalEvent,
|
||||
WlRegistryGlobalRemoveEvent, WlTouchDownEvent, XdgSurfaceGetToplevelRequest,
|
||||
XdgToplevelSetAppIdRequest, XdgToplevelSetTitleRequest, XdgWmBaseGetXdgSurfaceRequest,
|
||||
WlKeyboardEnterEvent, WlParsedMessage, WlPointerEnterEvent, WlRegistryBindRequest,
|
||||
WlRegistryGlobalEvent, WlRegistryGlobalRemoveEvent, WlTouchDownEvent,
|
||||
XdgSurfaceGetToplevelRequest, XdgToplevelSetAppIdRequest, XdgToplevelSetTitleRequest,
|
||||
XdgWmBaseGetXdgSurfaceRequest,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -117,7 +118,7 @@ impl WlMitmState {
|
|||
/// is not handled here.
|
||||
fn handle_created_or_destroyed_objects(
|
||||
&mut self,
|
||||
msg: &dyn AnyWlParsedMessage,
|
||||
msg: &dyn AnyWlParsedMessage<'_>,
|
||||
from_client: bool,
|
||||
) -> bool {
|
||||
if let Some(created_objects) = msg.known_objects_created() {
|
||||
|
@ -132,7 +133,7 @@ impl WlMitmState {
|
|||
is_half_destroyed = self.objects.is_half_destroyed(id),
|
||||
"Trying to create object via message {}::{} but the object ID is already used!",
|
||||
parent_obj.interface(),
|
||||
msg.msg_name()
|
||||
msg.self_msg_name()
|
||||
);
|
||||
|
||||
return false;
|
||||
|
@ -144,7 +145,7 @@ impl WlMitmState {
|
|||
obj_id = id,
|
||||
"Created object via message {}::{}",
|
||||
parent_obj.interface(),
|
||||
msg.msg_name()
|
||||
msg.self_msg_name()
|
||||
);
|
||||
self.objects.record_object(tt, id);
|
||||
}
|
||||
|
@ -163,7 +164,7 @@ impl WlMitmState {
|
|||
obj_id = msg.obj_id(),
|
||||
"Object destructed via destructor {}::{}",
|
||||
obj_type.interface(),
|
||||
msg.msg_name()
|
||||
msg.self_msg_name()
|
||||
);
|
||||
|
||||
self.objects.remove_object(msg.obj_id(), from_client);
|
||||
|
@ -178,13 +179,13 @@ impl WlMitmState {
|
|||
|
||||
fn prepare_command(
|
||||
&self,
|
||||
msg: &dyn AnyWlParsedMessage,
|
||||
msg: &dyn AnyWlParsedMessage<'_>,
|
||||
cmd_str: &str,
|
||||
desc: &str,
|
||||
) -> tokio::process::Command {
|
||||
let mut cmd = tokio::process::Command::new(cmd_str);
|
||||
cmd.arg(msg.object_type().interface());
|
||||
cmd.arg(msg.msg_name());
|
||||
cmd.arg(msg.self_object_type().interface());
|
||||
cmd.arg(msg.self_msg_name());
|
||||
cmd.arg(desc);
|
||||
cmd.env("WL_MITM_MSG_JSON", msg.to_json());
|
||||
|
||||
|
@ -249,8 +250,8 @@ impl WlMitmState {
|
|||
num_fds = raw_msg.fds.len(),
|
||||
num_consumed_fds = msg.num_consumed_fds(),
|
||||
"{}::{}",
|
||||
msg.object_type().interface(),
|
||||
msg.msg_name(),
|
||||
msg.self_object_type().interface(),
|
||||
msg.self_msg_name(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -259,7 +260,7 @@ impl WlMitmState {
|
|||
if self.objects.is_half_destroyed(msg.obj_id()) {
|
||||
error!(
|
||||
obj_id = msg.obj_id(),
|
||||
opcode = msg.opcode(),
|
||||
opcode = msg.self_opcode(),
|
||||
"Client request detected on object already scheduled for destruction; aborting!"
|
||||
);
|
||||
return outcome.terminate();
|
||||
|
@ -333,11 +334,11 @@ impl WlMitmState {
|
|||
.config
|
||||
.filter
|
||||
.requests
|
||||
.get(msg.object_type().interface())
|
||||
.get(msg.self_object_type().interface())
|
||||
{
|
||||
if let Some(filtered) = filtered_requests
|
||||
.iter()
|
||||
.find(|f| f.requests.contains(msg.msg_name()))
|
||||
.find(|f| f.requests.contains(msg.self_msg_name()))
|
||||
{
|
||||
match filtered.action {
|
||||
WlFilterRequestAction::Ask => {
|
||||
|
@ -345,8 +346,8 @@ impl WlMitmState {
|
|||
info!(
|
||||
ask_cmd = ask_cmd,
|
||||
"Running ask command for {}::{}",
|
||||
msg.object_type().interface(),
|
||||
msg.msg_name()
|
||||
msg.self_object_type().interface(),
|
||||
msg.self_msg_name()
|
||||
);
|
||||
|
||||
let mut cmd = self.prepare_command(
|
||||
|
@ -359,8 +360,8 @@ impl WlMitmState {
|
|||
if !status.success() {
|
||||
warn!(
|
||||
"Blocked {}::{} because of return status {}",
|
||||
msg.object_type().interface(),
|
||||
msg.msg_name(),
|
||||
msg.self_object_type().interface(),
|
||||
msg.self_msg_name(),
|
||||
status
|
||||
);
|
||||
|
||||
|
@ -378,8 +379,8 @@ impl WlMitmState {
|
|||
|
||||
warn!(
|
||||
"Blocked {}::{} because of missing ask_cmd",
|
||||
msg.object_type().interface(),
|
||||
msg.msg_name()
|
||||
msg.self_object_type().interface(),
|
||||
msg.self_msg_name()
|
||||
);
|
||||
return match filtered.block_type {
|
||||
WlFilterRequestBlockType::Ignore => outcome.filtered(),
|
||||
|
@ -393,8 +394,8 @@ impl WlMitmState {
|
|||
info!(
|
||||
notify_cmd = notify_cmd,
|
||||
"Running notify command for {}::{}",
|
||||
msg.object_type().interface(),
|
||||
msg.msg_name()
|
||||
msg.self_object_type().interface(),
|
||||
msg.self_msg_name()
|
||||
);
|
||||
|
||||
let mut cmd = self.prepare_command(
|
||||
|
@ -409,8 +410,8 @@ impl WlMitmState {
|
|||
WlFilterRequestAction::Block => {
|
||||
warn!(
|
||||
"Blocked {}::{}",
|
||||
msg.object_type().interface(),
|
||||
msg.msg_name()
|
||||
msg.self_object_type().interface(),
|
||||
msg.self_msg_name()
|
||||
);
|
||||
return match filtered.block_type {
|
||||
WlFilterRequestBlockType::Ignore => outcome.filtered(),
|
||||
|
@ -457,8 +458,8 @@ impl WlMitmState {
|
|||
num_fds = raw_msg.fds.len(),
|
||||
num_consumed_fds = msg.num_consumed_fds(),
|
||||
"{}::{}",
|
||||
msg.object_type().interface(),
|
||||
msg.msg_name(),
|
||||
msg.self_object_type().interface(),
|
||||
msg.self_msg_name(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue